3911 Marko Mäkelä 2012-05-30
WL#6255 MVCC bug fix: Ensure that older transactions are denied access
to a table that has been rebuilt, whether or not it happened in-place
(ALGORITHM=INPLACE) or in the old way (ALGORITHM=COPY).
When a table is created, we record the trx_id of the creating data
dictionary transaction in each index of the table in the data dictionary
cache. In this way, transactions that were started before the table
creation (or copying) can be denied access to the new table.
To be exact, there are two transactions involved in DDL. One is the
user transaction associated with the MySQL client connection. For
InnoDB data dictionary operations, we create a data dictionary transaction.
Theoretically, other transactions could start between the prebuilt->trx
and the dictionary trx. However, the table cannot be modified during this
time frame, thanks to a meta-data lock (MDL). In CREATE TABLE, the table
does not exist before InnoDB returns, and in ALTER TABLE, other transactions
are blocked from accessing the table while the table copy is being set up.
dict_create_index_step(): Initialize index->trx_id, so that older
transactions will be denied access to a newly created table.
ha_innobase::commit_inplace_alter_table(): Remove the adjustment of
index->trx_id for ALGORITHM=INPLACE.
modified:
mysql-test/suite/innodb/r/innodb-index.result
mysql-test/suite/innodb/t/innodb-index.test
storage/innobase/dict/dict0crea.cc
storage/innobase/handler/handler0alter.cc
3910 Marko Mäkelä 2012-05-30
WL#6255 bug fix: Relax a bogus debug assertion.
btr_page_split_and_insert(): This function can be invoked during
online secondary index creation when loading the created index.
modified:
storage/innobase/btr/btr0btr.cc
3909 Marko Mäkelä 2012-05-30
WL#6255: Fix the online rebuild logging of ROW_FORMAT=REDUNDANT tables.
Move some accessor function prototypes and #defines from rem0rec.ic
to rem0rec.h.
rec_2_is_field_extern(): Determine if a field is stored externally.
row_log_table_low_redundant(): Like row_log_table_low(), but for
ROW_FORMAT=REDUNDANT records. Convert the rec_t to a dtuple_t and
then to the rebuild log format. Add some assertions.
modified:
storage/innobase/include/rem0rec.h
storage/innobase/include/rem0rec.ic
storage/innobase/row/row0log.cc
=== modified file 'mysql-test/suite/innodb/r/innodb-index.result'
--- a/mysql-test/suite/innodb/r/innodb-index.result revid:marko.makela@stripped530125444-m89sg202qswa44h4
+++ b/mysql-test/suite/innodb/r/innodb-index.result revid:marko.makela@stripped530194837-18ttnm2mooe7dl2p
@@ -925,7 +925,11 @@ DROP TABLE t1;
CREATE TABLE t1 (a INT, b CHAR(1)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (3,'a'),(3,'b'),(1,'c'),(0,'d'),(1,'e');
CREATE TABLE t2 (a INT, b CHAR(1)) ENGINE=InnoDB;
+CREATE TABLE t2i (a INT NOT NULL, b CHAR(1) NOT NULL) ENGINE=InnoDB;
+CREATE TABLE t2c (a INT NOT NULL, b CHAR(1) NOT NULL) ENGINE=InnoDB;
INSERT INTO t2 SELECT * FROM t1;
+INSERT INTO t2i SELECT * FROM t1;
+INSERT INTO t2c SELECT * FROM t1;
BEGIN;
SELECT * FROM t1;
a b
@@ -937,7 +941,19 @@ a b
SET lock_wait_timeout=1;
CREATE INDEX t1a ON t1(a);
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+ALTER TABLE t2 ADD PRIMARY KEY(a,b), ADD INDEX t2a(a), ALGORITHM=INPLACE;
+ERROR 42000: This version of MySQL doesn't yet support 'ALTER TABLE t2 ADD PRIMARY KEY(a,b), ADD INDEX t2a(a), ALGORITHM=INPLACE'
CREATE INDEX t2a ON t2(a);
+ALTER TABLE t2i ADD PRIMARY KEY(a,b), ADD INDEX t2a(a), ALGORITHM=INPLACE;
+ALTER TABLE t2c ADD PRIMARY KEY(a,b), ADD INDEX t2a(a), ALGORITHM=COPY;
+SELECT * FROM t2i;
+ERROR HY000: Table definition has changed, please retry transaction
+SELECT * FROM t2i FORCE INDEX(t2a) ORDER BY a;
+ERROR HY000: Table definition has changed, please retry transaction
+SELECT * FROM t2c;
+ERROR HY000: Table definition has changed, please retry transaction
+SELECT * FROM t2c FORCE INDEX(t2a) ORDER BY a;
+ERROR HY000: Table definition has changed, please retry transaction
SELECT * FROM t2;
a b
3 a
@@ -955,6 +971,13 @@ a b
0 d
1 e
COMMIT;
+SELECT * FROM t2;
+a b
+3 a
+3 b
+1 c
+0 d
+1 e
SELECT * FROM t2 FORCE INDEX(t2a) ORDER BY a;
a b
0 d
@@ -962,6 +985,34 @@ a b
1 e
3 a
3 b
+SELECT * FROM t2i;
+a b
+0 d
+1 c
+1 e
+3 a
+3 b
+SELECT * FROM t2i FORCE INDEX(t2a) ORDER BY a;
+a b
+0 d
+1 c
+1 e
+3 a
+3 b
+SELECT * FROM t2c;
+a b
+0 d
+1 c
+1 e
+3 a
+3 b
+SELECT * FROM t2c FORCE INDEX(t2a) ORDER BY a;
+a b
+0 d
+1 c
+1 e
+3 a
+3 b
alter table t2 add index t2a(b);
ERROR 42000: Duplicate key name 't2a'
alter table t2 drop index t2a, add index t2a(b);
@@ -972,4 +1023,20 @@ t2 CREATE TABLE `t2` (
`b` char(1) DEFAULT NULL,
KEY `t2a` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
-DROP TABLE t1,t2;
+show create table t2i;
+Table Create Table
+t2i CREATE TABLE `t2i` (
+ `a` int(11) NOT NULL,
+ `b` char(1) NOT NULL,
+ PRIMARY KEY (`a`,`b`),
+ KEY `t2a` (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+show create table t2c;
+Table Create Table
+t2c CREATE TABLE `t2c` (
+ `a` int(11) NOT NULL,
+ `b` char(1) NOT NULL,
+ PRIMARY KEY (`a`,`b`),
+ KEY `t2a` (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+DROP TABLE t1,t2,t2c,t2i;
=== modified file 'mysql-test/suite/innodb/t/innodb-index.test'
--- a/mysql-test/suite/innodb/t/innodb-index.test revid:marko.makela@stripped4-m89sg202qswa44h4
+++ b/mysql-test/suite/innodb/t/innodb-index.test revid:marko.makela@stripped18ttnm2mooe7dl2p
@@ -348,7 +348,11 @@ connection a;
CREATE TABLE t1 (a INT, b CHAR(1)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (3,'a'),(3,'b'),(1,'c'),(0,'d'),(1,'e');
CREATE TABLE t2 (a INT, b CHAR(1)) ENGINE=InnoDB;
+CREATE TABLE t2i (a INT NOT NULL, b CHAR(1) NOT NULL) ENGINE=InnoDB;
+CREATE TABLE t2c (a INT NOT NULL, b CHAR(1) NOT NULL) ENGINE=InnoDB;
INSERT INTO t2 SELECT * FROM t1;
+INSERT INTO t2i SELECT * FROM t1;
+INSERT INTO t2c SELECT * FROM t1;
connection b;
BEGIN;
# This acquires a MDL lock on t1 until commit.
@@ -358,14 +362,35 @@ connection a;
SET lock_wait_timeout=1;
--error ER_LOCK_WAIT_TIMEOUT
CREATE INDEX t1a ON t1(a);
+# InnoDB cannot change a column to NOT NULL yet
+--error ER_NOT_SUPPORTED_YET
+ALTER TABLE t2 ADD PRIMARY KEY(a,b), ADD INDEX t2a(a), ALGORITHM=INPLACE;
CREATE INDEX t2a ON t2(a);
+ALTER TABLE t2i ADD PRIMARY KEY(a,b), ADD INDEX t2a(a), ALGORITHM=INPLACE;
+ALTER TABLE t2c ADD PRIMARY KEY(a,b), ADD INDEX t2a(a), ALGORITHM=COPY;
connection b;
+# t2i and t2c are too new for this transaction, because they were rebuilt
+--error ER_TABLE_DEF_CHANGED
+SELECT * FROM t2i;
+--error ER_TABLE_DEF_CHANGED
+SELECT * FROM t2i FORCE INDEX(t2a) ORDER BY a;
+--error ER_TABLE_DEF_CHANGED
+SELECT * FROM t2c;
+--error ER_TABLE_DEF_CHANGED
+SELECT * FROM t2c FORCE INDEX(t2a) ORDER BY a;
+# In t2, only the new index t2a is too new for this transaction.
SELECT * FROM t2;
--error ER_TABLE_DEF_CHANGED
SELECT * FROM t2 FORCE INDEX(t2a) ORDER BY a;
SELECT * FROM t2;
COMMIT;
+# For a new transaction, all of t2, t2i, t2c are accessible.
+SELECT * FROM t2;
SELECT * FROM t2 FORCE INDEX(t2a) ORDER BY a;
+SELECT * FROM t2i;
+SELECT * FROM t2i FORCE INDEX(t2a) ORDER BY a;
+SELECT * FROM t2c;
+SELECT * FROM t2c FORCE INDEX(t2a) ORDER BY a;
connection default;
disconnect a;
disconnect b;
@@ -374,5 +399,7 @@ disconnect b;
alter table t2 add index t2a(b);
alter table t2 drop index t2a, add index t2a(b);
show create table t2;
+show create table t2i;
+show create table t2c;
-DROP TABLE t1,t2;
+DROP TABLE t1,t2,t2c,t2i;
=== modified file 'storage/innobase/btr/btr0btr.cc'
--- a/storage/innobase/btr/btr0btr.cc revid:marko.makela@stripped4
+++ b/storage/innobase/btr/btr0btr.cc revid:marko.makela@stripped
@@ -2599,6 +2599,7 @@ func_start:
ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(cursor->index),
MTR_MEMO_X_LOCK));
ut_ad(!dict_index_is_online_ddl(cursor->index)
+ || (flags & BTR_CREATE_FLAG)
|| dict_index_is_clust(cursor->index));
#ifdef UNIV_SYNC_DEBUG
ut_ad(rw_lock_own(dict_index_get_lock(cursor->index), RW_LOCK_EX));
=== modified file 'storage/innobase/dict/dict0crea.cc'
--- a/storage/innobase/dict/dict0crea.cc revid:marko.makela@oracle.com-20120530125444-m89sg202qswa44h4
+++ b/storage/innobase/dict/dict0crea.cc revid:marko.makela@stripped-20120530194837-18ttnm2mooe7dl2p
@@ -1167,6 +1167,7 @@ dict_create_index_step(
}
node->index->page = node->page_no;
+ node->index->trx_id = trx->id;
node->state = INDEX_COMMIT_WORK;
}
=== modified file 'storage/innobase/handler/handler0alter.cc'
--- a/storage/innobase/handler/handler0alter.cc revid:marko.makela@stripped0120530125444-m89sg202qswa44h4
+++ b/storage/innobase/handler/handler0alter.cc revid:marko.makela@stripped530194837-18ttnm2mooe7dl2p
@@ -3039,22 +3039,13 @@ ha_innobase::commit_inplace_alter_table(
switch (error) {
dict_table_t* old_table;
- trx_id_t trx_id;
case DB_SUCCESS:
old_table = prebuilt->table;
- trx_id = prebuilt->trx->id;
trx_commit_for_mysql(prebuilt->trx);
row_prebuilt_free(prebuilt, TRUE);
error = row_merge_drop_table(trx, old_table);
prebuilt = row_create_prebuilt(
ctx->indexed_table, table->s->reclength);
- /* Prevent old transactions from accessing the
- rebuilt table, because the history is missing. */
- for (dict_index_t* index = dict_table_get_first_index(
- ctx->indexed_table);
- index; index = dict_table_get_next_index(index)) {
- index->trx_id = trx_id;
- }
err = 0;
break;
case DB_TABLESPACE_ALREADY_EXISTS:
No bundle (reason: useless for push emails).| Thread |
|---|
| • bzr push into mysql-trunk-wl6255 branch (marko.makela:3909 to 3911) WL#6255 | marko.makela | 30 May |