summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/suite/gcol/r/innodb_virtual_debug.result2
-rw-r--r--mysql-test/suite/gcol/t/innodb_virtual_debug.test2
-rw-r--r--mysql-test/suite/innodb/r/alter_candidate_key.result2
-rw-r--r--mysql-test/suite/innodb/r/alter_crash.result5
-rw-r--r--mysql-test/suite/innodb/r/alter_mdl_timeout.result2
-rw-r--r--mysql-test/suite/innodb/r/alter_not_null_debug,STRICT.rdiff27
-rw-r--r--mysql-test/suite/innodb/r/innodb-alter-debug.result5
-rw-r--r--mysql-test/suite/innodb/r/innodb-index-debug.result8
-rw-r--r--mysql-test/suite/innodb/r/innodb-index-online.result86
-rw-r--r--mysql-test/suite/innodb/r/innodb-table-online.result66
-rw-r--r--mysql-test/suite/innodb/r/instant_alter_debug,dynamic.rdiff6
-rw-r--r--mysql-test/suite/innodb/r/instant_alter_debug,redundant.rdiff2
-rw-r--r--mysql-test/suite/innodb/r/instant_alter_debug.result1
-rw-r--r--mysql-test/suite/innodb/r/table_definition_cache_debug.result1
-rw-r--r--mysql-test/suite/innodb/t/alter_candidate_key.test2
-rw-r--r--mysql-test/suite/innodb/t/alter_crash.test1
-rw-r--r--mysql-test/suite/innodb/t/alter_mdl_timeout.test2
-rw-r--r--mysql-test/suite/innodb/t/alter_not_null_debug.test2
-rw-r--r--mysql-test/suite/innodb/t/innodb-alter-debug.test5
-rw-r--r--mysql-test/suite/innodb/t/innodb-index-debug.test8
-rw-r--r--mysql-test/suite/innodb/t/innodb-index-online.test23
-rw-r--r--mysql-test/suite/innodb/t/innodb-table-online.test27
-rw-r--r--mysql-test/suite/innodb/t/instant_alter_debug.test1
-rw-r--r--mysql-test/suite/innodb/t/table_definition_cache_debug.test1
-rw-r--r--storage/innobase/CMakeLists.txt3
-rw-r--r--storage/innobase/btr/btr0cur.cc12
-rw-r--r--storage/innobase/dict/dict0dict.cc1
-rw-r--r--storage/innobase/handler/ha_innodb.cc74
-rw-r--r--storage/innobase/handler/handler0alter.cc152
-rw-r--r--storage/innobase/include/dict0mem.h27
-rw-r--r--storage/innobase/include/row0log.h98
-rw-r--r--storage/innobase/include/row0log.inl80
-rw-r--r--storage/innobase/include/trx0rec.h70
-rw-r--r--storage/innobase/include/trx0trx.h7
-rw-r--r--storage/innobase/include/trx0undo.h28
-rw-r--r--storage/innobase/row/row0ins.cc110
-rw-r--r--storage/innobase/row/row0log.cc812
-rw-r--r--storage/innobase/row/row0merge.cc10
-rw-r--r--storage/innobase/row/row0purge.cc63
-rw-r--r--storage/innobase/row/row0uins.cc171
-rw-r--r--storage/innobase/row/row0umod.cc128
-rw-r--r--storage/innobase/row/row0upd.cc148
-rw-r--r--storage/innobase/trx/trx0rec.cc115
-rw-r--r--storage/innobase/trx/trx0trx.cc11
-rw-r--r--storage/innobase/trx/trx0undo.cc143
45 files changed, 1231 insertions, 1319 deletions
diff --git a/mysql-test/suite/gcol/r/innodb_virtual_debug.result b/mysql-test/suite/gcol/r/innodb_virtual_debug.result
index 806cf1a98c8..80b2bde6ca5 100644
--- a/mysql-test/suite/gcol/r/innodb_virtual_debug.result
+++ b/mysql-test/suite/gcol/r/innodb_virtual_debug.result
@@ -105,7 +105,7 @@ SET lock_wait_timeout = 1;
ALTER TABLE t1 ADD UNIQUE INDEX(c, b);
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR s1';
-SET DEBUG_SYNC = 'row_ins_sec_index_enter SIGNAL s2 WAIT_FOR s3';
+SET DEBUG_SYNC = 'row_log_insert_handle SIGNAL s2 WAIT_FOR s3';
INSERT INTO t1(a, b) VALUES(2, 2);
connection con1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
diff --git a/mysql-test/suite/gcol/t/innodb_virtual_debug.test b/mysql-test/suite/gcol/t/innodb_virtual_debug.test
index 40446b991cd..5ebc90dac19 100644
--- a/mysql-test/suite/gcol/t/innodb_virtual_debug.test
+++ b/mysql-test/suite/gcol/t/innodb_virtual_debug.test
@@ -295,7 +295,7 @@ SET lock_wait_timeout = 1;
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR s1';
-SET DEBUG_SYNC = 'row_ins_sec_index_enter SIGNAL s2 WAIT_FOR s3';
+SET DEBUG_SYNC = 'row_log_insert_handle SIGNAL s2 WAIT_FOR s3';
--send INSERT INTO t1(a, b) VALUES(2, 2)
connection con1;
diff --git a/mysql-test/suite/innodb/r/alter_candidate_key.result b/mysql-test/suite/innodb/r/alter_candidate_key.result
index 79cb225e3b5..2ada5a499a8 100644
--- a/mysql-test/suite/innodb/r/alter_candidate_key.result
+++ b/mysql-test/suite/innodb/r/alter_candidate_key.result
@@ -74,7 +74,7 @@ connection con1;
SET DEBUG_SYNC='now WAIT_FOR dml';
BEGIN;
INSERT INTO t1 SET a=NULL;
-ROLLBACK;
+COMMIT;
set DEBUG_SYNC='now SIGNAL dml_done';
connection default;
ERROR 22004: Invalid use of NULL value
diff --git a/mysql-test/suite/innodb/r/alter_crash.result b/mysql-test/suite/innodb/r/alter_crash.result
index 46ea85d3e1e..a98aeb70a15 100644
--- a/mysql-test/suite/innodb/r/alter_crash.result
+++ b/mysql-test/suite/innodb/r/alter_crash.result
@@ -169,9 +169,6 @@ INSERT INTO t1(f1, f2) VALUES(2, "This is column2 value");
ROLLBACK;
set DEBUG_SYNC = 'now SIGNAL insert_done';
connection default;
-Warnings:
-Warning 1265 Data truncated for column 'f3' at row 3
-Warning 1265 Data truncated for column 'f4' at row 3
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
@@ -202,6 +199,7 @@ connection default;
SET DEBUG_SYNC = 'now WAIT_FOR scanned';
BEGIN;
INSERT INTO t1 VALUES(2,1);
+COMMIT;
SET DEBUG_SYNC = 'now SIGNAL commit';
SET DEBUG_SYNC = 'now WAIT_FOR c';
SET GLOBAL innodb_fil_make_page_dirty_debug=0;
@@ -221,4 +219,5 @@ t1 CREATE TABLE `t1` (
SELECT * FROM t1;
a b
1 1
+2 1
DROP TABLE t1;
diff --git a/mysql-test/suite/innodb/r/alter_mdl_timeout.result b/mysql-test/suite/innodb/r/alter_mdl_timeout.result
index 7af1362c69e..e4fba8e260f 100644
--- a/mysql-test/suite/innodb/r/alter_mdl_timeout.result
+++ b/mysql-test/suite/innodb/r/alter_mdl_timeout.result
@@ -10,7 +10,7 @@ begin;
INSERT INTO t1 VALUES('e','e',5, 5);
SET DEBUG_SYNC="now SIGNAL con1_insert";
SET DEBUG_SYNC="now WAIT_FOR con1_wait";
-SET DEBUG_SYNC="before_row_upd_sec_new_index_entry SIGNAL con1_update WAIT_FOR alter_rollback";
+SET DEBUG_SYNC="after_row_upd_clust SIGNAL con1_update WAIT_FOR alter_rollback";
UPDATE t1 set f4 = 10 order by f1 desc limit 2;
connection default;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
diff --git a/mysql-test/suite/innodb/r/alter_not_null_debug,STRICT.rdiff b/mysql-test/suite/innodb/r/alter_not_null_debug,STRICT.rdiff
index 09c717c44b0..81466d791ac 100644
--- a/mysql-test/suite/innodb/r/alter_not_null_debug,STRICT.rdiff
+++ b/mysql-test/suite/innodb/r/alter_not_null_debug,STRICT.rdiff
@@ -1,11 +1,16 @@
-18,21c18
-< affected rows: 0
-< info: Records: 0 Duplicates: 0 Warnings: 1
-< Warnings:
-< Warning 1265 Data truncated for column 'c2' at row 3
----
-> ERROR 01000: Data truncated for column 'c2' at row 3
-24c21
-< 2 0
----
-> 2 NULL
+@@ -15,13 +15,10 @@
+ SET DEBUG_SYNC= 'now SIGNAL flushed';
+ affected rows: 0
+ connection default;
+-affected rows: 0
+-info: Records: 0 Duplicates: 0 Warnings: 1
+-Warnings:
+-Warning 1265 Data truncated for column 'c2' at row 3
++ERROR 22004: Invalid use of NULL value
+ SELECT * FROM t1;
+ c1 c2
+-2 0
++2 NULL
+ 3 1
+ DROP TABLE t1;
+ CREATE TABLE t1(c1 INT NOT NULL, c2 INT, PRIMARY KEY(c1))ENGINE=INNODB;
diff --git a/mysql-test/suite/innodb/r/innodb-alter-debug.result b/mysql-test/suite/innodb/r/innodb-alter-debug.result
index 81fc67e55c7..aae9432fc35 100644
--- a/mysql-test/suite/innodb/r/innodb-alter-debug.result
+++ b/mysql-test/suite/innodb/r/innodb-alter-debug.result
@@ -43,9 +43,8 @@ SET DEBUG_SYNC = 'now SIGNAL s2';
/* connection default */
connection default;
/* reap */ alter table t1 force, add b int, ALGORITHM=inplace;
-ERROR 23000: Duplicate entry '1' for key 'uk'
SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL s1 WAIT_FOR s2';
-alter table t1 force, add b int, ALGORITHM=inplace;;
+alter table t1 force, add c int, ALGORITHM=inplace;;
/* connection con1 */
connection con1;
set DEBUG_SYNC = 'now WAIT_FOR s1';
@@ -55,7 +54,6 @@ SET DEBUG_SYNC = 'now SIGNAL s2';
/* connection default */
connection default;
/* reap */ alter table t1 force, add b int, ALGORITHM=inplace;
-ERROR 23000: Duplicate entry '1' for key 'uk'
SET DEBUG_SYNC = 'RESET';
drop table t1;
#
@@ -72,7 +70,6 @@ ERROR 23000: Duplicate entry '1' for key 'a'
SET DEBUG_SYNC = 'now SIGNAL S2';
disconnect con1;
connection default;
-ERROR 23000: Duplicate entry '1' for key 'a'
SET DEBUG_SYNC='RESET';
DROP TABLE t1;
#
diff --git a/mysql-test/suite/innodb/r/innodb-index-debug.result b/mysql-test/suite/innodb/r/innodb-index-debug.result
index f6b23eea41a..c36a0531b95 100644
--- a/mysql-test/suite/innodb/r/innodb-index-debug.result
+++ b/mysql-test/suite/innodb/r/innodb-index-debug.result
@@ -118,20 +118,21 @@ drop table t480;
# MDEV-12827 Assertion failure when reporting duplicate key error
# in online table rebuild
#
-CREATE TABLE t1 (j INT UNIQUE, i INT UNIQUE) ENGINE=InnoDB;
+CREATE TABLE t1 (j INT UNIQUE, i INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES(2, 2);
connect con1,localhost,root,,test;
SET DEBUG_SYNC='row_log_table_apply1_before SIGNAL built WAIT_FOR log';
-ALTER TABLE t1 DROP j, FORCE;
+ALTER TABLE t1 DROP j, ADD UNIQUE INDEX(i), FORCE;
connection default;
SET DEBUG_SYNC='now WAIT_FOR built';
SET DEBUG_DBUG='+d,row_ins_row_level';
INSERT INTO t1 (i) VALUES (0),(0);
-ERROR 23000: Duplicate entry '0' for key 'i'
SET DEBUG_SYNC='now SIGNAL log';
SET DEBUG_DBUG=@saved_debug_dbug;
connection con1;
ERROR 23000: Duplicate entry '0' for key 'i'
+DELETE FROM t1;
+ALTER TABLE t1 ADD UNIQUE INDEX(i);
SET DEBUG_SYNC='row_log_table_apply1_before SIGNAL built2 WAIT_FOR log2';
ALTER TABLE t1 DROP j, FORCE;
connection default;
@@ -141,7 +142,6 @@ UPDATE t1 SET i=0;
ERROR 23000: Duplicate entry '0' for key 'i'
SET DEBUG_SYNC='now SIGNAL log2';
connection con1;
-ERROR 23000: Duplicate entry '0' for key 'i'
disconnect con1;
connection default;
SET DEBUG_SYNC='RESET';
diff --git a/mysql-test/suite/innodb/r/innodb-index-online.result b/mysql-test/suite/innodb/r/innodb-index-online.result
index 1ee352cd402..be86cf7565e 100644
--- a/mysql-test/suite/innodb/r/innodb-index-online.result
+++ b/mysql-test/suite/innodb/r/innodb-index-online.result
@@ -85,7 +85,8 @@ ddl_sort_file_alter_table 0
ddl_log_file_alter_table 0
BEGIN;
INSERT INTO t1 VALUES(7,4,2);
-ROLLBACK;
+COMMIT;
+DELETE FROM t1 where c1 = 7;
SET DEBUG_SYNC = 'now SIGNAL rollback_done';
connection con1;
ERROR 23000: Duplicate entry '4' for key 'c2'
@@ -96,14 +97,14 @@ SET DEBUG_SYNC = 'now WAIT_FOR created';
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count
ddl_background_drop_indexes 0
-ddl_online_create_index 0
+ddl_online_create_index 1
ddl_pending_alter_table 1
ddl_sort_file_alter_table 0
ddl_log_file_alter_table 0
INSERT INTO t1 VALUES(6,3,1);
SET DEBUG_SYNC = 'now SIGNAL dml_done';
connection con1;
-ERROR 23000: Duplicate entry for key 'c2'
+ERROR 23000: Duplicate entry '' for key '*UNKNOWN*'
DELETE FROM t1 WHERE c1=6;
ALTER TABLE t1 ADD UNIQUE INDEX(c2);
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
@@ -243,38 +244,22 @@ ddl_online_create_index 1
ddl_pending_alter_table 1
ddl_sort_file_alter_table 0
ddl_log_file_alter_table 0
-BEGIN;
-DELETE FROM t1;
-ROLLBACK;
UPDATE t1 SET c2 = c2 + 1;
-BEGIN;
+UPDATE t1 SET c2 = c2 + 2;
UPDATE t1 SET c2 = c2 + 1;
-DELETE FROM t1;
-ROLLBACK;
-BEGIN;
-DELETE FROM t1;
-ROLLBACK;
+UPDATE t1 SET c2 = c2 + 2;
UPDATE t1 SET c2 = c2 + 1;
-BEGIN;
+UPDATE t1 SET c2 = c2 + 2;
UPDATE t1 SET c2 = c2 + 1;
-DELETE FROM t1;
-ROLLBACK;
-BEGIN;
-DELETE FROM t1;
-ROLLBACK;
+UPDATE t1 SET c2 = c2 + 2;
UPDATE t1 SET c2 = c2 + 1;
-BEGIN;
+UPDATE t1 SET c2 = c2 + 2;
UPDATE t1 SET c2 = c2 + 1;
-DELETE FROM t1;
-ROLLBACK;
-BEGIN;
-DELETE FROM t1;
-ROLLBACK;
+UPDATE t1 SET c2 = c2 + 2;
UPDATE t1 SET c2 = c2 + 1;
-BEGIN;
+UPDATE t1 SET c2 = c2 + 2;
UPDATE t1 SET c2 = c2 + 1;
-DELETE FROM t1;
-ROLLBACK;
+UPDATE t1 SET c2 = c2 + 2;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count
ddl_background_drop_indexes 0
@@ -310,7 +295,7 @@ ERROR HY000: Creating index 'c2e' required more than 'innodb_online_alter_log_ma
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count
ddl_background_drop_indexes 1
-ddl_online_create_index 0
+ddl_online_create_index 1
ddl_pending_alter_table 0
ddl_sort_file_alter_table 0
ddl_log_file_alter_table 1
@@ -321,7 +306,7 @@ name pos
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count
ddl_background_drop_indexes 1
-ddl_online_create_index 0
+ddl_online_create_index 1
ddl_pending_alter_table 0
ddl_sort_file_alter_table 0
ddl_log_file_alter_table 1
@@ -361,19 +346,45 @@ ddl_log_file_alter_table 1
BEGIN;
INSERT INTO t1 SELECT 320 + c1, c2, c3 FROM t1 WHERE c1 > 160;
DELETE FROM t1 WHERE c1 > 320;
-ROLLBACK;
+COMMIT;
BEGIN;
UPDATE t1 SET c2 = c2 + 1;
-DELETE FROM t1;
-ROLLBACK;
+COMMIT;
BEGIN;
INSERT INTO t1 SELECT 320 + c1, c2, c3 FROM t1 WHERE c1 > 160;
DELETE FROM t1 WHERE c1 > 320;
-ROLLBACK;
+COMMIT;
BEGIN;
UPDATE t1 SET c2 = c2 + 1;
-DELETE FROM t1;
-ROLLBACK;
+COMMIT;
+BEGIN;
+INSERT INTO t1 SELECT 320 + c1, c2, c3 FROM t1 WHERE c1 > 160;
+DELETE FROM t1 WHERE c1 > 320;
+COMMIT;
+BEGIN;
+UPDATE t1 SET c2 = c2 + 1;
+COMMIT;
+BEGIN;
+INSERT INTO t1 SELECT 320 + c1, c2, c3 FROM t1 WHERE c1 > 160;
+DELETE FROM t1 WHERE c1 > 320;
+COMMIT;
+BEGIN;
+UPDATE t1 SET c2 = c2 + 1;
+COMMIT;
+BEGIN;
+INSERT INTO t1 SELECT 320 + c1, c2, c3 FROM t1 WHERE c1 > 160;
+DELETE FROM t1 WHERE c1 > 320;
+COMMIT;
+BEGIN;
+UPDATE t1 SET c2 = c2 + 1;
+COMMIT;
+BEGIN;
+INSERT INTO t1 SELECT 320 + c1, c2, c3 FROM t1 WHERE c1 > 160;
+DELETE FROM t1 WHERE c1 > 320;
+COMMIT;
+BEGIN;
+UPDATE t1 SET c2 = c2 + 1;
+COMMIT;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count
ddl_background_drop_indexes 0
@@ -452,7 +463,7 @@ name pos
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count
ddl_background_drop_indexes 1
-ddl_online_create_index 0
+ddl_online_create_index 1
ddl_pending_alter_table 0
ddl_sort_file_alter_table 0
ddl_log_file_alter_table 2
@@ -460,7 +471,7 @@ connection default;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count
ddl_background_drop_indexes 1
-ddl_online_create_index 0
+ddl_online_create_index 1
ddl_pending_alter_table 0
ddl_sort_file_alter_table 0
ddl_log_file_alter_table 2
@@ -498,7 +509,6 @@ SET DEBUG_SYNC = 'row_log_apply_before SIGNAL t1u_created WAIT_FOR dup_done';
ALTER TABLE t1 ADD UNIQUE(c);
connection con1;
SET DEBUG_SYNC = 'now WAIT_FOR t1u_created';
-BEGIN;
INSERT INTO t1 VALUES('bar'),('bar');
SET DEBUG_SYNC = 'now SIGNAL dup_done';
connection default;
diff --git a/mysql-test/suite/innodb/r/innodb-table-online.result b/mysql-test/suite/innodb/r/innodb-table-online.result
index 91d9b355125..659d645b5cc 100644
--- a/mysql-test/suite/innodb/r/innodb-table-online.result
+++ b/mysql-test/suite/innodb/r/innodb-table-online.result
@@ -97,11 +97,11 @@ ddl_online_create_index 1
ddl_pending_alter_table 1
ddl_sort_file_alter_table 0
ddl_log_file_alter_table 0
-BEGIN;
INSERT INTO t1 VALUES(4,7,2);
SET DEBUG_SYNC = 'now SIGNAL insert_done';
connection con1;
ERROR 23000: Duplicate entry '4' for key 'PRIMARY'
+DELETE FROM t1 WHERE c1=4 and c2=7;
connection default;
ROLLBACK;
connection con1;
@@ -213,30 +213,22 @@ ddl_online_create_index 1
ddl_pending_alter_table 1
ddl_sort_file_alter_table 0
ddl_log_file_alter_table 1
-BEGIN;
-DELETE FROM t1;
-ROLLBACK;
UPDATE t1 SET c2 = c2 + 1;
-BEGIN;
+UPDATE t1 SET c2 = c2 + 2;
UPDATE t1 SET c2 = c2 + 1;
-DELETE FROM t1;
-ROLLBACK;
-BEGIN;
-DELETE FROM t1;
-ROLLBACK;
+UPDATE t1 SET c2 = c2 + 2;
UPDATE t1 SET c2 = c2 + 1;
-BEGIN;
+UPDATE t1 SET c2 = c2 + 2;
UPDATE t1 SET c2 = c2 + 1;
-DELETE FROM t1;
-ROLLBACK;
-BEGIN;
-DELETE FROM t1;
-ROLLBACK;
+UPDATE t1 SET c2 = c2 + 2;
UPDATE t1 SET c2 = c2 + 1;
-BEGIN;
+UPDATE t1 SET c2 = c2 + 2;
UPDATE t1 SET c2 = c2 + 1;
-DELETE FROM t1;
-ROLLBACK;
+UPDATE t1 SET c2 = c2 + 2;
+UPDATE t1 SET c2 = c2 + 1;
+UPDATE t1 SET c2 = c2 + 2;
+UPDATE t1 SET c2 = c2 + 1;
+UPDATE t1 SET c2 = c2 + 2;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count
ddl_background_drop_indexes 0
@@ -286,7 +278,7 @@ SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL rebuilt3 WAIT_FOR dml3_done
ALTER TABLE t1 ADD PRIMARY KEY(c22f), CHANGE c2 c22f INT;
ERROR 42000: Multiple primary key defined
ALTER TABLE t1 DROP PRIMARY KEY, ADD PRIMARY KEY(c22f), CHANGE c2 c22f INT;
-ERROR 23000: Duplicate entry '5' for key 'PRIMARY'
+ERROR 23000: Duplicate entry '26' for key 'PRIMARY'
ALTER TABLE t1 DROP PRIMARY KEY, ADD PRIMARY KEY(c22f,c1,c4(5)),
CHANGE c2 c22f INT, CHANGE c3 c3 CHAR(255) NULL, CHANGE c1 c1 INT AFTER c22f,
ADD COLUMN c4 VARCHAR(6) DEFAULT 'Online', LOCK=NONE;
@@ -302,11 +294,8 @@ ddl_log_file_alter_table 1
BEGIN;
INSERT INTO t1 SELECT 320 + c1, c2, c3 FROM t1 WHERE c1 > 240;
DELETE FROM t1 WHERE c1 > 320;
-ROLLBACK;
-BEGIN;
UPDATE t1 SET c2 = c2 + 1;
-DELETE FROM t1;
-ROLLBACK;
+COMMIT;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count
ddl_background_drop_indexes 0
@@ -374,12 +363,11 @@ SET DEBUG_SYNC = 'now WAIT_FOR c3p5_created0';
BEGIN;
INSERT INTO t1 VALUES(347,33101,'Pikku kakkosen posti','YLETV2');
INSERT INTO t1 VALUES(33101,347,NULL,'');
+COMMIT;
SET DEBUG_SYNC = 'now SIGNAL ins_done0';
connection con1;
-ERROR 01000: Data truncated for column 'c3' at row 323
-connection default;
-ROLLBACK;
-connection con1;
+ERROR 22004: Invalid use of NULL value
+DELETE FROM t1 WHERE c1= 347 and c22f = 33101;
ALTER TABLE t1 MODIFY c3 CHAR(255) NOT NULL;
SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL c3p5_created WAIT_FOR ins_done';
ALTER TABLE t1 DROP PRIMARY KEY, DROP COLUMN c22f,
@@ -405,20 +393,20 @@ ddl_log_file_alter_table 2
connection default;
SELECT COUNT(*) FROM t1;
COUNT(*)
-321
+322
ALTER TABLE t1 ROW_FORMAT=REDUNDANT;
SELECT * FROM t1 LIMIT 10;
c22f c1 c3 c4
-5 1 1foo Online
-5 6 6foofoofoofoofoofoo Online
-5 11 11foofoofoofoofoofoofoofoofoofoofoo Online
-5 16 16foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
-5 21 21foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
-5 26 26foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
-5 31 31foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
-5 36 36foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
-5 41 41foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
-5 46 46foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
+27 1 1foo Online
+27 6 6foofoofoofoofoofoo Online
+27 11 11foofoofoofoofoofoofoofoofoofoofoo Online
+27 16 16foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
+27 21 21foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
+27 26 26foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
+27 31 31foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
+27 36 36foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
+27 41 41foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
+27 46 46foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
connection con1;
ALTER TABLE t1 DISCARD TABLESPACE;
connection default;
diff --git a/mysql-test/suite/innodb/r/instant_alter_debug,dynamic.rdiff b/mysql-test/suite/innodb/r/instant_alter_debug,dynamic.rdiff
deleted file mode 100644
index 379514edad9..00000000000
--- a/mysql-test/suite/innodb/r/instant_alter_debug,dynamic.rdiff
+++ /dev/null
@@ -1,6 +0,0 @@
-@@ -470,4 +470,4 @@
- FROM information_schema.global_status
- WHERE variable_name = 'innodb_instant_alter_column';
- instants
--33
-+32
diff --git a/mysql-test/suite/innodb/r/instant_alter_debug,redundant.rdiff b/mysql-test/suite/innodb/r/instant_alter_debug,redundant.rdiff
index eafa8e5725d..cff4ff18c70 100644
--- a/mysql-test/suite/innodb/r/instant_alter_debug,redundant.rdiff
+++ b/mysql-test/suite/innodb/r/instant_alter_debug,redundant.rdiff
@@ -1,4 +1,4 @@
-@@ -509,4 +509,4 @@
+@@ -527,4 +527,4 @@
FROM information_schema.global_status
WHERE variable_name = 'innodb_instant_alter_column';
instants
diff --git a/mysql-test/suite/innodb/r/instant_alter_debug.result b/mysql-test/suite/innodb/r/instant_alter_debug.result
index 82230573c44..5f74c234260 100644
--- a/mysql-test/suite/innodb/r/instant_alter_debug.result
+++ b/mysql-test/suite/innodb/r/instant_alter_debug.result
@@ -262,7 +262,6 @@ INSERT INTO t1 SET a=3;
ROLLBACK;
SET DEBUG_SYNC = 'now SIGNAL logged';
connection ddl;
-ERROR 22004: Invalid use of NULL value
disconnect ddl;
connection default;
SET DEBUG_SYNC = RESET;
diff --git a/mysql-test/suite/innodb/r/table_definition_cache_debug.result b/mysql-test/suite/innodb/r/table_definition_cache_debug.result
index df171c89cd4..6bd754aaca3 100644
--- a/mysql-test/suite/innodb/r/table_definition_cache_debug.result
+++ b/mysql-test/suite/innodb/r/table_definition_cache_debug.result
@@ -12,6 +12,7 @@ connection default;
SET DEBUG_SYNC = 'now WAIT_FOR scanned';
BEGIN;
INSERT INTO to_be_evicted VALUES(3, 2);
+COMMIT;
SET DEBUG_SYNC = 'now SIGNAL got_duplicate';
connection ddl;
ERROR 23000: Duplicate entry '2' for key 'b'
diff --git a/mysql-test/suite/innodb/t/alter_candidate_key.test b/mysql-test/suite/innodb/t/alter_candidate_key.test
index 7c8f5e30993..824ad1ea799 100644
--- a/mysql-test/suite/innodb/t/alter_candidate_key.test
+++ b/mysql-test/suite/innodb/t/alter_candidate_key.test
@@ -50,7 +50,7 @@ connection con1;
SET DEBUG_SYNC='now WAIT_FOR dml';
BEGIN;
INSERT INTO t1 SET a=NULL;
-ROLLBACK;
+COMMIT;
set DEBUG_SYNC='now SIGNAL dml_done';
connection default;
--error ER_INVALID_USE_OF_NULL
diff --git a/mysql-test/suite/innodb/t/alter_crash.test b/mysql-test/suite/innodb/t/alter_crash.test
index 1049efd3e12..e0e294ae4f0 100644
--- a/mysql-test/suite/innodb/t/alter_crash.test
+++ b/mysql-test/suite/innodb/t/alter_crash.test
@@ -213,6 +213,7 @@ connection default;
SET DEBUG_SYNC = 'now WAIT_FOR scanned';
BEGIN;
INSERT INTO t1 VALUES(2,1);
+COMMIT;
SET DEBUG_SYNC = 'now SIGNAL commit';
SET DEBUG_SYNC = 'now WAIT_FOR c';
# Make all pending changes durable for recovery.
diff --git a/mysql-test/suite/innodb/t/alter_mdl_timeout.test b/mysql-test/suite/innodb/t/alter_mdl_timeout.test
index 15e7f524fd0..ff77921b2d2 100644
--- a/mysql-test/suite/innodb/t/alter_mdl_timeout.test
+++ b/mysql-test/suite/innodb/t/alter_mdl_timeout.test
@@ -14,7 +14,7 @@ begin;
INSERT INTO t1 VALUES('e','e',5, 5);
SET DEBUG_SYNC="now SIGNAL con1_insert";
SET DEBUG_SYNC="now WAIT_FOR con1_wait";
-SET DEBUG_SYNC="before_row_upd_sec_new_index_entry SIGNAL con1_update WAIT_FOR alter_rollback";
+SET DEBUG_SYNC="after_row_upd_clust SIGNAL con1_update WAIT_FOR alter_rollback";
SEND UPDATE t1 set f4 = 10 order by f1 desc limit 2;
connection default;
diff --git a/mysql-test/suite/innodb/t/alter_not_null_debug.test b/mysql-test/suite/innodb/t/alter_not_null_debug.test
index 9c5ba0faff0..87113b2b3f8 100644
--- a/mysql-test/suite/innodb/t/alter_not_null_debug.test
+++ b/mysql-test/suite/innodb/t/alter_not_null_debug.test
@@ -7,7 +7,7 @@ let $sql_mode = `SELECT @@SQL_MODE`;
let $error_code = 0;
if ($sql_mode == "STRICT_TRANS_TABLES") {
- let $error_code = WARN_DATA_TRUNCATED;
+ let $error_code = ER_INVALID_USE_OF_NULL;
}
diff --git a/mysql-test/suite/innodb/t/innodb-alter-debug.test b/mysql-test/suite/innodb/t/innodb-alter-debug.test
index 1789ec294e4..2241ef5d295 100644
--- a/mysql-test/suite/innodb/t/innodb-alter-debug.test
+++ b/mysql-test/suite/innodb/t/innodb-alter-debug.test
@@ -52,11 +52,10 @@ SET DEBUG_SYNC = 'now SIGNAL s2';
--echo /* connection default */
connection default;
--echo /* reap */ alter table t1 force, add b int, ALGORITHM=inplace;
---error ER_DUP_ENTRY
--reap
SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL s1 WAIT_FOR s2';
---send alter table t1 force, add b int, ALGORITHM=inplace;
+--send alter table t1 force, add c int, ALGORITHM=inplace;
--echo /* connection con1 */
connection con1;
@@ -68,7 +67,6 @@ SET DEBUG_SYNC = 'now SIGNAL s2';
--echo /* connection default */
connection default;
--echo /* reap */ alter table t1 force, add b int, ALGORITHM=inplace;
---error ER_DUP_ENTRY
--reap
SET DEBUG_SYNC = 'RESET';
@@ -92,7 +90,6 @@ SET DEBUG_SYNC = 'now SIGNAL S2';
disconnect con1;
CONNECTION default;
---error ER_DUP_ENTRY
reap;
SET DEBUG_SYNC='RESET';
diff --git a/mysql-test/suite/innodb/t/innodb-index-debug.test b/mysql-test/suite/innodb/t/innodb-index-debug.test
index 204bdfe5540..f03ef061769 100644
--- a/mysql-test/suite/innodb/t/innodb-index-debug.test
+++ b/mysql-test/suite/innodb/t/innodb-index-debug.test
@@ -122,17 +122,16 @@ drop table t480;
--echo # in online table rebuild
--echo #
-CREATE TABLE t1 (j INT UNIQUE, i INT UNIQUE) ENGINE=InnoDB;
+CREATE TABLE t1 (j INT UNIQUE, i INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES(2, 2);
--connect (con1,localhost,root,,test)
SET DEBUG_SYNC='row_log_table_apply1_before SIGNAL built WAIT_FOR log';
--send
-ALTER TABLE t1 DROP j, FORCE;
+ALTER TABLE t1 DROP j, ADD UNIQUE INDEX(i), FORCE;
--connection default
SET DEBUG_SYNC='now WAIT_FOR built';
SET DEBUG_DBUG='+d,row_ins_row_level';
---error ER_DUP_ENTRY
INSERT INTO t1 (i) VALUES (0),(0);
SET DEBUG_SYNC='now SIGNAL log';
SET DEBUG_DBUG=@saved_debug_dbug;
@@ -140,6 +139,8 @@ SET DEBUG_DBUG=@saved_debug_dbug;
--connection con1
--error ER_DUP_ENTRY
reap;
+DELETE FROM t1;
+ALTER TABLE t1 ADD UNIQUE INDEX(i);
SET DEBUG_SYNC='row_log_table_apply1_before SIGNAL built2 WAIT_FOR log2';
--send
ALTER TABLE t1 DROP j, FORCE;
@@ -152,7 +153,6 @@ UPDATE t1 SET i=0;
SET DEBUG_SYNC='now SIGNAL log2';
--connection con1
---error ER_DUP_ENTRY
reap;
--disconnect con1
--connection default
diff --git a/mysql-test/suite/innodb/t/innodb-index-online.test b/mysql-test/suite/innodb/t/innodb-index-online.test
index 6f30dad531d..2cb84b18402 100644
--- a/mysql-test/suite/innodb/t/innodb-index-online.test
+++ b/mysql-test/suite/innodb/t/innodb-index-online.test
@@ -98,7 +98,8 @@ eval $innodb_metrics_select;
# Insert a duplicate entry (4) for the already started UNIQUE INDEX(c2).
BEGIN;
INSERT INTO t1 VALUES(7,4,2);
-ROLLBACK;
+COMMIT;
+DELETE FROM t1 where c1 = 7;
SET DEBUG_SYNC = 'now SIGNAL rollback_done';
connection con1;
@@ -121,7 +122,7 @@ INSERT INTO t1 VALUES(6,3,1);
SET DEBUG_SYNC = 'now SIGNAL dml_done';
connection con1;
# This is due to the duplicate entry (6,3,1).
---error ER_DUP_UNKNOWN_IN_INDEX
+--error ER_DUP_ENTRY
reap;
DELETE FROM t1 WHERE c1=6;
ALTER TABLE t1 ADD UNIQUE INDEX(c2);
@@ -237,17 +238,11 @@ SET DEBUG_SYNC = 'now WAIT_FOR c2e_created';
# At this point, the clustered index scan must have completed,
# but the modification log keeps accumulating due to the DEBUG_SYNC.
eval $innodb_metrics_select;
-let $c= 4;
+let $c= 8;
while ($c)
{
- BEGIN;
- DELETE FROM t1;
- ROLLBACK;
UPDATE t1 SET c2 = c2 + 1;
- BEGIN;
- UPDATE t1 SET c2 = c2 + 1;
- DELETE FROM t1;
- ROLLBACK;
+ UPDATE t1 SET c2 = c2 + 2;
dec $c;
}
# Incomplete index c2e should exist until the DDL thread notices the overflow.
@@ -325,17 +320,16 @@ connection default;
SET DEBUG_SYNC = 'now WAIT_FOR c2f_created';
# Generate some log (delete-mark, delete-unmark, insert etc.)
eval $innodb_metrics_select;
-let $c= 2;
+let $c= 6;
while ($c)
{
BEGIN;
INSERT INTO t1 SELECT 320 + c1, c2, c3 FROM t1 WHERE c1 > 160;
DELETE FROM t1 WHERE c1 > 320;
-ROLLBACK;
+COMMIT;
BEGIN;
UPDATE t1 SET c2 = c2 + 1;
-DELETE FROM t1;
-ROLLBACK;
+COMMIT;
dec $c;
}
eval $innodb_metrics_select;
@@ -481,7 +475,6 @@ send ALTER TABLE t1 ADD UNIQUE(c);
connection con1;
SET DEBUG_SYNC = 'now WAIT_FOR t1u_created';
-BEGIN;
INSERT INTO t1 VALUES('bar'),('bar');
SET DEBUG_SYNC = 'now SIGNAL dup_done';
diff --git a/mysql-test/suite/innodb/t/innodb-table-online.test b/mysql-test/suite/innodb/t/innodb-table-online.test
index 7ed87fcc26b..a1cf50f541d 100644
--- a/mysql-test/suite/innodb/t/innodb-table-online.test
+++ b/mysql-test/suite/innodb/t/innodb-table-online.test
@@ -101,7 +101,6 @@ SET DEBUG_SYNC = 'now WAIT_FOR scanned';
eval $innodb_metrics_select;
# Insert a duplicate entry (4) for the already started UNIQUE INDEX(c1).
-BEGIN;
INSERT INTO t1 VALUES(4,7,2);
SET DEBUG_SYNC = 'now SIGNAL insert_done';
@@ -111,7 +110,7 @@ connection con1;
# error on the (4,7,2).
--error ER_DUP_ENTRY
reap;
-
+DELETE FROM t1 WHERE c1=4 and c2=7;
connection default;
ROLLBACK;
@@ -204,17 +203,11 @@ UPDATE t1 SET c2 = c2 + 1;
# At this point, the clustered index scan must have completed,
# but the modification log keeps accumulating due to the DEBUG_SYNC.
eval $innodb_metrics_select;
-let $c= 3;
+let $c= 8;
while ($c)
{
- BEGIN;
- DELETE FROM t1;
- ROLLBACK;
UPDATE t1 SET c2 = c2 + 1;
- BEGIN;
- UPDATE t1 SET c2 = c2 + 1;
- DELETE FROM t1;
- ROLLBACK;
+ UPDATE t1 SET c2 = c2 + 2;
dec $c;
}
# Temporary table should exist until the DDL thread notices the overflow.
@@ -279,11 +272,8 @@ eval $innodb_metrics_select;
BEGIN;
INSERT INTO t1 SELECT 320 + c1, c2, c3 FROM t1 WHERE c1 > 240;
DELETE FROM t1 WHERE c1 > 320;
-ROLLBACK;
-BEGIN;
UPDATE t1 SET c2 = c2 + 1;
-DELETE FROM t1;
-ROLLBACK;
+COMMIT;
eval $innodb_metrics_select;
# Release con1.
SET DEBUG_SYNC = 'now SIGNAL dml3_done';
@@ -346,16 +336,13 @@ SET DEBUG_SYNC = 'now WAIT_FOR c3p5_created0';
BEGIN;
INSERT INTO t1 VALUES(347,33101,'Pikku kakkosen posti','YLETV2');
INSERT INTO t1 VALUES(33101,347,NULL,'');
+COMMIT;
SET DEBUG_SYNC = 'now SIGNAL ins_done0';
connection con1;
---error WARN_DATA_TRUNCATED
+--error ER_INVALID_USE_OF_NULL
reap;
-
-connection default;
-ROLLBACK;
-
-connection con1;
+DELETE FROM t1 WHERE c1= 347 and c22f = 33101;
ALTER TABLE t1 MODIFY c3 CHAR(255) NOT NULL;
SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL c3p5_created WAIT_FOR ins_done';
diff --git a/mysql-test/suite/innodb/t/instant_alter_debug.test b/mysql-test/suite/innodb/t/instant_alter_debug.test
index f102185c27f..c6eca884907 100644
--- a/mysql-test/suite/innodb/t/instant_alter_debug.test
+++ b/mysql-test/suite/innodb/t/instant_alter_debug.test
@@ -303,7 +303,6 @@ ROLLBACK;
SET DEBUG_SYNC = 'now SIGNAL logged';
connection ddl;
---error ER_INVALID_USE_OF_NULL
reap;
disconnect ddl;
diff --git a/mysql-test/suite/innodb/t/table_definition_cache_debug.test b/mysql-test/suite/innodb/t/table_definition_cache_debug.test
index 6a466af4cc5..8950691e05c 100644
--- a/mysql-test/suite/innodb/t/table_definition_cache_debug.test
+++ b/mysql-test/suite/innodb/t/table_definition_cache_debug.test
@@ -29,6 +29,7 @@ SET DEBUG_SYNC = 'now WAIT_FOR scanned';
# and then hogs the table lock, so that the unique index cannot be dropped.
BEGIN;
INSERT INTO to_be_evicted VALUES(3, 2);
+COMMIT;
SET DEBUG_SYNC = 'now SIGNAL got_duplicate';
connection ddl;
diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt
index aee68b8460d..d05a3cb8ae6 100644
--- a/storage/innobase/CMakeLists.txt
+++ b/storage/innobase/CMakeLists.txt
@@ -1,6 +1,6 @@
# Copyright (c) 2006, 2017, Oracle and/or its affiliates. All rights reserved.
-# Copyright (c) 2014, 2021, MariaDB Corporation.
+# Copyright (c) 2014, 2022, MariaDB Corporation.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -214,7 +214,6 @@ SET(INNOBASE_SOURCES
include/row0import.h
include/row0ins.h
include/row0log.h
- include/row0log.inl
include/row0merge.h
include/row0mysql.h
include/row0purge.h
diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc
index 05f3128d819..f12561c3126 100644
--- a/storage/innobase/btr/btr0cur.cc
+++ b/storage/innobase/btr/btr0cur.cc
@@ -5401,10 +5401,6 @@ btr_cur_del_mark_set_clust_rec(
<< ib::hex(trx->id) << ": "
<< rec_printer(rec, offsets).str());
- if (dict_index_is_online_ddl(index)) {
- row_log_table_delete(rec, index, offsets, NULL);
- }
-
btr_cur_upd_rec_sys(block, rec, index, offsets, trx, roll_ptr, mtr);
return(err);
}
@@ -7061,8 +7057,6 @@ btr_store_big_rec_extern_fields(
+ prev_block->page.frame,
page_no);
}
- } else if (dict_index_is_online_ddl(index)) {
- row_log_table_blob_alloc(index, page_no);
}
ut_ad(!page_has_siblings(block->page.frame));
@@ -7312,8 +7306,6 @@ btr_free_externally_stored_field(
page_t* page;
const uint32_t space_id = mach_read_from_4(
field_ref + BTR_EXTERN_SPACE_ID);
- const uint32_t start_page = mach_read_from_4(
- field_ref + BTR_EXTERN_PAGE_NO);
uint32_t page_no;
uint32_t next_page_no;
mtr_t mtr;
@@ -7383,10 +7375,6 @@ btr_free_externally_stored_field(
return;
}
- if (page_no == start_page && dict_index_is_online_ddl(index)) {
- row_log_table_blob_free(index, start_page);
- }
-
ext_block = buf_page_get(
page_id_t(space_id, page_no), ext_zip_size,
RW_X_LATCH, &mtr);
diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc
index f64fd6f04c9..196e674396b 100644
--- a/storage/innobase/dict/dict0dict.cc
+++ b/storage/innobase/dict/dict0dict.cc
@@ -2114,7 +2114,6 @@ dict_index_remove_from_cache_low(
there can't be any active operations on this index (or table). */
if (index->online_log) {
- ut_ad(index->online_status == ONLINE_INDEX_CREATION);
row_log_free(index->online_log);
index->online_log = NULL;
}
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 8151352d3ed..1ced55be0e8 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -5184,33 +5184,25 @@ ha_innobase::keys_to_use_for_scanning()
return(&key_map_full);
}
-/****************************************************************//**
-Ensures that if there's a concurrent inplace ADD INDEX, being-indexed virtual
-columns are computed. They are not marked as indexed in the old table, so the
-server won't add them to the read_set automatically */
-void
-ha_innobase::column_bitmaps_signal()
-/*================================*/
+/** Ensure that indexed virtual columns will be computed. */
+void ha_innobase::column_bitmaps_signal()
{
- if (!table->vfield || table->current_lock != F_WRLCK) {
- return;
- }
+ if (!table->vfield)
+ return;
- dict_index_t* clust_index = dict_table_get_first_index(m_prebuilt->table);
- uint num_v = 0;
- for (uint j = 0; j < table->s->virtual_fields; j++) {
- if (table->vfield[j]->stored_in_db()) {
- continue;
- }
+ dict_index_t* clust_index= dict_table_get_first_index(m_prebuilt->table);
+ uint num_v= 0;
+ for (uint j = 0; j < table->s->virtual_fields; j++)
+ {
+ if (table->vfield[j]->stored_in_db())
+ continue;
- dict_col_t* col = &m_prebuilt->table->v_cols[num_v].m_col;
- if (col->ord_part ||
- (dict_index_is_online_ddl(clust_index) &&
- row_log_col_is_indexed(clust_index, num_v))) {
- table->mark_virtual_column_with_deps(table->vfield[j]);
- }
- num_v++;
- }
+ dict_col_t *col= &m_prebuilt->table->v_cols[num_v].m_col;
+ if (col->ord_part ||
+ (dict_index_is_online_ddl(clust_index) &&
+ row_log_col_is_indexed(clust_index, num_v)))
+ table->mark_virtual_column_with_deps(table->vfield[j]);
+ }
}
@@ -8195,34 +8187,13 @@ calc_row_difference(
}
}
-#ifdef UNIV_DEBUG
- bool online_ord_part = false;
-#endif
-
if (is_virtual) {
/* If the virtual column is not indexed,
we shall ignore it for update */
if (!col->ord_part) {
- /* Check whether there is a table-rebuilding
- online ALTER TABLE in progress, and this
- virtual column could be newly indexed, thus
- it will be materialized. Then we will have
- to log its update.
- Note, we do not support online dropping virtual
- column while adding new index, nor with
- online alter column order while adding index,
- so the virtual column sequence must not change
- if it is online operation */
- if (dict_index_is_online_ddl(clust_index)
- && row_log_col_is_indexed(clust_index,
- num_v)) {
-#ifdef UNIV_DEBUG
- online_ord_part = true;
-#endif
- } else {
- num_v++;
- continue;
- }
+ next:
+ num_v++;
+ continue;
}
if (!uvect->old_vrow) {
@@ -8248,8 +8219,7 @@ calc_row_difference(
prebuilt, vfield, o_len,
col, old_mysql_row_col,
col_pack_len, buf);
- num_v++;
- continue;
+ goto next;
}
}
@@ -8298,7 +8268,7 @@ calc_row_difference(
upd_fld_set_virtual_col(ufield);
ufield->field_no = num_v;
- ut_ad(col->ord_part || online_ord_part);
+ ut_ad(col->ord_part);
ufield->old_v_val = static_cast<dfield_t*>(
mem_heap_alloc(
uvect->heap,
@@ -8383,7 +8353,7 @@ calc_row_difference(
prebuilt, vfield, o_len,
col, old_mysql_row_col,
col_pack_len, buf);
- ut_ad(col->ord_part || online_ord_part);
+ ut_ad(col->ord_part);
num_v++;
}
}
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index f8167b86582..cd83d7af974 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -1173,6 +1173,14 @@ struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx
return true;
return false;
}
+
+ /** Handle the apply log failure for online DDL operation.
+ @param ha_alter_info handler alter inplace info
+ @param altered_table MySQL table that is being altered
+ @param error error code */
+ void log_handle_failure(Alter_inplace_info* ha_alter_info,
+ TABLE* altered_table,
+ dberr_t error);
};
/********************************************************************//**
@@ -7011,6 +7019,7 @@ error_handling_drop_uncached:
row_log_free(
index->online_log);
index->online_log = NULL;
+ ctx->old_table->indexes.start->clear_dummy_log();
ok = false;
});
@@ -8344,6 +8353,45 @@ get_error_key_name(
}
}
+/** Handle the apply log failure for online DDL operation.
+@param ha_alter_info handler alter inplace info
+@param altered_table MySQL table that is being altered
+@param error error code */
+void ha_innobase_inplace_ctx::log_handle_failure(
+ Alter_inplace_info* ha_alter_info,
+ TABLE* altered_table,
+ dberr_t error)
+{
+ ulint err_key= thr_get_trx(thr)->error_key_num;
+ switch (error)
+ {
+ KEY* dup_key;
+ case DB_SUCCESS:
+ break;
+ case DB_DUPLICATE_KEY:
+ if (err_key == ULINT_UNDEFINED)
+ /* This should be the hidden index on FTS_DOC_ID */
+ dup_key= NULL;
+ else
+ {
+ DBUG_ASSERT(err_key < ha_alter_info->key_count);
+ dup_key= &ha_alter_info->key_info_buffer[err_key];
+ }
+ print_keydup_error(altered_table, dup_key, MYF(0));
+ break;
+ case DB_ONLINE_LOG_TOO_BIG:
+ my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
+ get_error_key_name(err_key, ha_alter_info, new_table));
+ break;
+ case DB_INDEX_CORRUPT:
+ my_error(ER_INDEX_CORRUPT, MYF(0),
+ get_error_key_name(err_key, ha_alter_info, new_table));
+ break;
+ default:
+ my_error_innodb(error, old_table->name.m_name, old_table->flags);
+ }
+}
+
/** Alter the table structure in-place with operations
specified using Alter_inplace_info.
The level of concurrency allowed during this operation depends
@@ -8771,7 +8819,7 @@ inline bool rollback_inplace_alter_table(Alter_inplace_info *ha_alter_info,
thd_lock_wait_timeout(ctx->trx->mysql_thd);
const uint save_timeout= innodb_lock_wait_timeout;
innodb_lock_wait_timeout= ~0U; /* infinite */
-
+ ctx->old_table->indexes.start->clear_dummy_log();
if (fts_exist)
{
const dict_index_t *fts_index= nullptr;
@@ -10734,7 +10782,6 @@ static bool alter_rebuild_apply_log(
dropped were not created in the copy of the table. Apply any
last bit of the rebuild log and then rename the tables. */
dict_table_t* user_table = ctx->old_table;
- dict_table_t* rebuilt_table = ctx->new_table;
DEBUG_SYNC_C("row_log_table_apply2_before");
@@ -10763,41 +10810,12 @@ static bool alter_rebuild_apply_log(
ctx->new_table->vc_templ = NULL;
}
- ulint err_key = thr_get_trx(ctx->thr)->error_key_num;
-
- switch (error) {
- KEY* dup_key;
- case DB_SUCCESS:
- break;
- case DB_DUPLICATE_KEY:
- if (err_key == ULINT_UNDEFINED) {
- /* This should be the hidden index on
- FTS_DOC_ID. */
- dup_key = NULL;
- } else {
- DBUG_ASSERT(err_key < ha_alter_info->key_count);
- dup_key = &ha_alter_info->key_info_buffer[err_key];
- }
-
- print_keydup_error(altered_table, dup_key, MYF(0));
- DBUG_RETURN(true);
- case DB_ONLINE_LOG_TOO_BIG:
- my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
- get_error_key_name(err_key, ha_alter_info,
- rebuilt_table));
- DBUG_RETURN(true);
- case DB_INDEX_CORRUPT:
- my_error(ER_INDEX_CORRUPT, MYF(0),
- get_error_key_name(err_key, ha_alter_info,
- rebuilt_table));
- DBUG_RETURN(true);
- default:
- my_error_innodb(error, ctx->old_table->name.m_name,
- user_table->flags);
- DBUG_RETURN(true);
+ if (error == DB_SUCCESS) {
+ DBUG_RETURN(false);
}
- DBUG_RETURN(false);
+ ctx->log_handle_failure(ha_alter_info, altered_table, error);
+ DBUG_RETURN(true);
}
/** Commit or rollback the changes made during
@@ -10988,6 +11006,70 @@ lock_fail:
DBUG_RETURN(true);
}
}
+ } else {
+ dberr_t error= DB_SUCCESS;
+ for (inplace_alter_handler_ctx** pctx= ctx_array; *pctx;
+ pctx++) {
+ auto ctx= static_cast<ha_innobase_inplace_ctx*>(*pctx);
+
+ if (!ctx->online || !ctx->old_table->space
+ || !ctx->old_table->is_readable()) {
+ continue;
+ }
+
+ for (ulint i = 0; i < ctx->num_to_add_index; i++) {
+ dict_index_t *index= ctx->add_index[i];
+
+ if (index->type & (DICT_FTS | DICT_SPATIAL))
+ continue;
+
+ index->lock.x_lock(SRW_LOCK_CALL);
+ if (index->online_log == nullptr) {
+ /* online log would've cleared
+ when we detect the error in
+ other index */
+ index->lock.x_unlock();
+ continue;
+ }
+
+ if (index->is_corrupted()) {
+ /* Online index log has been
+ preserved to show the error
+ when it happened via
+ row_log_apply() by DML thread */
+ error= row_log_get_error(index);
+err_index:
+ ctx->log_handle_failure(
+ ha_alter_info,
+ altered_table, error);
+ row_log_free(index->online_log);
+ index->online_log= nullptr;
+ index->lock.x_unlock();
+
+ ctx->old_table->indexes.start->clear_dummy_log();
+ if (fts_exist) {
+ purge_sys.resume_FTS();
+ }
+ MONITOR_ATOMIC_INC(
+ MONITOR_BACKGROUND_DROP_INDEX);
+ DBUG_RETURN(true);
+ }
+
+ error = row_log_apply(
+ m_prebuilt->trx, index, altered_table,
+ ctx->m_stage);
+
+ if (error != DB_SUCCESS) {
+ goto err_index;
+ }
+
+ row_log_free(index->online_log);
+ index->online_log= nullptr;
+ index->lock.x_unlock();
+ }
+
+ ctx->old_table->indexes.start->clear_dummy_log();
+ }
}
dict_table_t *table_stats = nullptr, *index_stats = nullptr;
diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h
index dac738fbc68..4ea96601afd 100644
--- a/storage/innobase/include/dict0mem.h
+++ b/storage/innobase/include/dict0mem.h
@@ -2,7 +2,7 @@
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
-Copyright (c) 2013, 2021, MariaDB Corporation.
+Copyright (c) 2013, 2022, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -1399,6 +1399,25 @@ public:
rollback of TRX_UNDO_EMPTY. The BTR_SEG_LEAF is freed and reinitialized.
@param thr query thread */
void clear(que_thr_t *thr);
+
+ /** Assign the clustered index online log to table.
+ It can be used by concurrent DML to identify whether
+ the table has any active DDL */
+ void assign_dummy_log()
+ {
+ ut_ad(this->is_clust());
+ lock.s_lock(SRW_LOCK_CALL);
+ online_log= reinterpret_cast<row_log_t*>(table);
+ lock.s_unlock();
+ }
+
+ /** Clear the online log for the clustered index */
+ void clear_dummy_log()
+ {
+ lock.s_lock(SRW_LOCK_CALL);
+ online_log= nullptr;
+ lock.s_unlock();
+ }
};
/** Detach a virtual column from an index.
@@ -2360,6 +2379,12 @@ public:
return false;
}
+ /** @return whether a DDL operation is in progress on this table */
+ bool is_active_ddl() const
+ {
+ return UT_LIST_GET_FIRST(indexes)->online_log;
+ }
+
/** @return whether the name is
mysql.innodb_index_stats or mysql.innodb_table_stats */
bool is_stats_table() const;
diff --git a/storage/innobase/include/row0log.h b/storage/innobase/include/row0log.h
index 732ef494326..1327759a58c 100644
--- a/storage/innobase/include/row0log.h
+++ b/storage/innobase/include/row0log.h
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2011, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, 2021, MariaDB Corporation.
+Copyright (c) 2017, 2022, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -24,15 +24,15 @@ Modification log for online index creation and online table rebuild
Created 2011-05-26 Marko Makela
*******************************************************/
-#ifndef row0log_h
-#define row0log_h
+#pragma once
#include "que0types.h"
#include "mtr0types.h"
#include "row0types.h"
#include "rem0types.h"
-#include "data0types.h"
+#include "dict0dict.h"
#include "trx0types.h"
+#include "trx0undo.h"
class ut_stage_alter_t;
@@ -74,37 +74,24 @@ row_log_free(
/******************************************************//**
Free the row log for an index on which online creation was aborted. */
-UNIV_INLINE
-void
-row_log_abort_sec(
-/*==============*/
- dict_index_t* index) /*!< in/out: index (x-latched) */
- MY_ATTRIBUTE((nonnull));
+inline void row_log_abort_sec(dict_index_t *index)
+{
+ ut_ad(index->lock.have_u_or_x());
+ ut_ad(!index->is_clust());
+ dict_index_set_online_status(index, ONLINE_INDEX_ABORTED);
+ row_log_free(index->online_log);
+ index->online_log= nullptr;
+}
-/******************************************************//**
-Try to log an operation to a secondary index that is
-(or was) being created.
-@retval true if the operation was logged or can be ignored
-@retval false if online index creation is not taking place */
-UNIV_INLINE
+/** Logs an operation to a secondary index that is (or was) being created.
+@param index index, S or X latched
+@param tuple index tuple (NULL= empty the index)
+@param trx_id transaction ID for insert, or 0 for delete
+@retval false if row_log_apply() failure happens
+or true otherwise */
bool
-row_log_online_op_try(
-/*==================*/
- dict_index_t* index, /*!< in/out: index, S or X latched */
- const dtuple_t* tuple, /*!< in: index tuple */
- trx_id_t trx_id) /*!< in: transaction ID for insert,
- or 0 for delete */
- MY_ATTRIBUTE((nonnull, warn_unused_result));
-/******************************************************//**
-Logs an operation to a secondary index that is (or was) being created. */
-void
-row_log_online_op(
-/*==============*/
- dict_index_t* index, /*!< in/out: index, S or X latched */
- const dtuple_t* tuple, /*!< in: index tuple (NULL=empty the index) */
- trx_id_t trx_id) /*!< in: transaction ID for insert,
- or 0 for delete */
- ATTRIBUTE_COLD;
+row_log_online_op(dict_index_t *index, const dtuple_t *tuple,
+ trx_id_t trx_id) ATTRIBUTE_COLD;
/******************************************************//**
Gets the error status of the online index rebuild log.
@@ -118,7 +105,7 @@ row_log_table_get_error(
/** Check whether a virtual column is indexed in the new table being
created during alter table
-@param[in] index cluster index
+@param[in] index clustered index
@param[in] v_no virtual column number
@return true if it is indexed, else false */
bool
@@ -185,22 +172,6 @@ row_log_table_insert(
dict_index_t* index, /*!< in/out: clustered index, S-latched
or X-latched */
const rec_offs* offsets);/*!< in: rec_get_offsets(rec,index) */
-/******************************************************//**
-Notes that a BLOB is being freed during online ALTER TABLE. */
-void
-row_log_table_blob_free(
-/*====================*/
- dict_index_t* index, /*!< in/out: clustered index, X-latched */
- ulint page_no)/*!< in: starting page number of the BLOB */
- ATTRIBUTE_COLD __attribute__((nonnull));
-/******************************************************//**
-Notes that a BLOB is being allocated during online ALTER TABLE. */
-void
-row_log_table_blob_alloc(
-/*=====================*/
- dict_index_t* index, /*!< in/out: clustered index, X-latched */
- ulint page_no)/*!< in: starting page number of the BLOB */
- ATTRIBUTE_COLD __attribute__((nonnull));
/** Apply the row_log_table log to a table upon completing rebuild.
@param[in] thr query graph
@@ -252,6 +223,11 @@ row_log_apply(
@return number of n_core_fields */
unsigned row_log_get_n_core_fields(const dict_index_t *index);
+/** Get the error code of online log for the index
+@param index online index
+@return error code present in online log */
+dberr_t row_log_get_error(const dict_index_t *index);
+
#ifdef HAVE_PSI_STAGE_INTERFACE
/** Estimate how much work is to be done by the log apply phase
of an ALTER TABLE for this index.
@@ -261,8 +237,26 @@ of an ALTER TABLE for this index.
ulint
row_log_estimate_work(
const dict_index_t* index);
+
#endif /* HAVE_PSI_STAGE_INTERFACE */
-#include "row0log.inl"
+/** Handle the insert undo log and apply it on online indexes
+@param tuple row reference from undo log record
+@param rec_info undo log record info
+@param clust_index clustered index
+@param heap memory heap */
+void row_log_insert_handle(const dtuple_t *tuple,
+ const trx_undo_rec_info *rec_info,
+ dict_index_t *clust_index,
+ mem_heap_t *heap);
-#endif /* row0log.h */
+/** Handle the update, delete undo log and apply it on online
+indexes
+@param tuple row reference from undo log record
+@param rec_info undo log record info
+@param clust_index clustered index
+@param heap memory heap */
+void row_log_update_handle(const dtuple_t *tuple,
+ const trx_undo_rec_info *rec_info,
+ dict_index_t *clust_index,
+ mem_heap_t *heap);
diff --git a/storage/innobase/include/row0log.inl b/storage/innobase/include/row0log.inl
deleted file mode 100644
index f9f3dd006bf..00000000000
--- a/storage/innobase/include/row0log.inl
+++ /dev/null
@@ -1,80 +0,0 @@
-/*****************************************************************************
-
-Copyright (c) 2011, 2015, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2020, MariaDB Corporation.
-
-This program is free software; you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free Software
-Foundation; version 2 of the License.
-
-This program is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along with
-this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
-
-*****************************************************************************/
-
-/**************************************************//**
-@file include/row0log.ic
-Modification log for online index creation and online table rebuild
-
-Created 2012-10-18 Marko Makela
-*******************************************************/
-
-#include "dict0dict.h"
-
-/******************************************************//**
-Free the row log for an index on which online creation was aborted. */
-UNIV_INLINE
-void
-row_log_abort_sec(
-/*===============*/
- dict_index_t* index) /*!< in/out: index (x-latched) */
-{
- ut_ad(index->lock.have_u_or_x());
- ut_ad(!dict_index_is_clust(index));
- dict_index_set_online_status(index, ONLINE_INDEX_ABORTED);
- row_log_free(index->online_log);
- index->online_log = NULL;
-}
-
-/******************************************************//**
-Try to log an operation to a secondary index that is
-(or was) being created.
-@retval true if the operation was logged or can be ignored
-@retval false if online index creation is not taking place */
-UNIV_INLINE
-bool
-row_log_online_op_try(
-/*==================*/
- dict_index_t* index, /*!< in/out: index, S or X latched */
- const dtuple_t* tuple, /*!< in: index tuple */
- trx_id_t trx_id) /*!< in: transaction ID for insert,
- or 0 for delete */
-{
- ut_ad(index->lock.have_any());
-
- switch (dict_index_get_online_status(index)) {
- case ONLINE_INDEX_COMPLETE:
- /* This is a normal index. Do not log anything.
- The caller must perform the operation on the
- index tree directly. */
- return(false);
- case ONLINE_INDEX_CREATION:
- /* The index is being created online. Log the
- operation. */
- row_log_online_op(index, tuple, trx_id);
- break;
- case ONLINE_INDEX_ABORTED:
- case ONLINE_INDEX_ABORTED_DROPPED:
- /* The index was created online, but the operation was
- aborted. Do not log the operation and tell the caller
- to skip the operation. */
- break;
- }
-
- return(true);
-}
diff --git a/storage/innobase/include/trx0rec.h b/storage/innobase/include/trx0rec.h
index 0afae491c78..186d9410dfc 100644
--- a/storage/innobase/include/trx0rec.h
+++ b/storage/innobase/include/trx0rec.h
@@ -82,6 +82,7 @@ trx_undo_rec_get_pars(
undo_no_t* undo_no, /*!< out: undo log record number */
table_id_t* table_id) /*!< out: table id */
MY_ATTRIBUTE((nonnull));
+
/*******************************************************************//**
Builds a row reference from an undo log record.
@return pointer to remaining part of undo record */
@@ -208,37 +209,47 @@ fetching the purge record */
the undo log (which is the after image for an update) */
#define TRX_UNDO_GET_OLD_V_VALUE 0x2
-/*******************************************************************//**
-Build a previous version of a clustered index record. The caller must
-hold a latch on the index page of the clustered index record.
+/** Build a previous version of a clustered index record. The caller
+must hold a latch on the index page of the clustered index record.
+@param index_rec clustered index record in the index tree
+@param index_mtr mtr which contains the latch to index_rec page
+ and purge_view
+@param rec version of a clustered index record
+@param index clustered index
+@param offsets rec_get_offsets(rec, index)
+@param heap memory heap from which the memory needed is
+ allocated
+@param old_vers previous version or NULL if rec is the
+ first inserted version, or if history data
+ has been deleted (an error), or if the purge
+ could have removed the version
+ though it has not yet done so
+@param v_heap memory heap used to create vrow
+ dtuple if it is not yet created. This heap
+ diffs from "heap" above in that it could be
+ prebuilt->old_vers_heap for selection
+@param vrow virtual column info, if any
+@param v_status status determine if it is going into this
+ function by purge thread or not.
+ And if we read "after image" of undo log
+@param undo_rec_info undo record info or NULL
@retval true if previous version was built, or if it was an insert
or the table has been rebuilt
@retval false if the previous version is earlier than purge_view,
-which means that it may have been removed */
+or being purged, which means that it may have been removed */
bool
trx_undo_prev_version_build(
-/*========================*/
- const rec_t* index_rec,/*!< in: clustered index record in the
- index tree */
- mtr_t* index_mtr,/*!< in: mtr which contains the latch to
- index_rec page and purge_view */
- const rec_t* rec, /*!< in: version of a clustered index record */
- dict_index_t* index, /*!< in: clustered index */
- rec_offs* offsets,/*!< in/out: rec_get_offsets(rec, index) */
- mem_heap_t* heap, /*!< in: memory heap from which the memory
- needed is allocated */
- rec_t** old_vers,/*!< out, own: previous version, or NULL if
- rec is the first inserted version, or if
- history data has been deleted */
- mem_heap_t* v_heap, /* !< in: memory heap used to create vrow
- dtuple if it is not yet created. This heap
- diffs from "heap" above in that it could be
- prebuilt->old_vers_heap for selection */
- dtuple_t** vrow, /*!< out: virtual column info, if any */
- ulint v_status);
- /*!< in: status determine if it is going
- into this function by purge thread or not.
- And if we read "after image" of undo log */
+ const rec_t *index_rec,
+ mtr_t *index_mtr,
+ const rec_t *rec,
+ dict_index_t *index,
+ rec_offs *offsets,
+ mem_heap_t *heap,
+ rec_t **old_vers,
+ mem_heap_t *v_heap,
+ dtuple_t **vrow,
+ ulint v_status,
+ const trx_undo_rec_info *undo_rec_info= nullptr);
/** Read from an undo log record a non-virtual column value.
@param[in,out] ptr pointer to remaining part of the undo record
@@ -281,6 +292,13 @@ trx_undo_read_v_idx(
bool* is_undo_log,
uint32_t* field_no);
+/** Check whether the given roll ptr matches with the undo log
+@param roll_ptr rollback pointer of the record
+@param undo_info undo log record information
+@return true if the undo record matches with rollback pointer */
+bool trx_undo_rec_is_equal(roll_ptr_t roll_ptr,
+ const trx_undo_rec_info *undo_info);
+
/* Types of an undo log record: these have to be smaller than 16, as the
compilation info multiplied by 16 is ORed to this value in an undo log
record */
diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h
index 6b9ffdc6374..c42579166dd 100644
--- a/storage/innobase/include/trx0trx.h
+++ b/storage/innobase/include/trx0trx.h
@@ -860,6 +860,10 @@ public:
bool auto_commit; /*!< true if it is an autocommit */
bool will_lock; /*!< set to inform trx_start_low() that
the transaction may acquire locks */
+ /* True if transaction has to read the undo log and
+ log the DML changes for online DDL table */
+ bool apply_online_log = false;
+
/*------------------------------*/
fts_trx_t* fts_trx; /*!< FTS information, or NULL if
transaction hasn't modified tables
@@ -885,6 +889,7 @@ public:
error, or empty. */
rw_trx_hash_element_t *rw_trx_hash_element;
LF_PINS *rw_trx_hash_pins;
+
ulint magic_n;
/** @return whether any persistent undo log has been generated */
@@ -933,6 +938,8 @@ public:
@retval false if the rollback was aborted by shutdown */
inline bool rollback_finish();
private:
+ /** Apply any changes to tables for which online DDL is in progress. */
+ ATTRIBUTE_COLD void apply_log() const;
/** Process tables that were modified by the committing transaction. */
inline void commit_tables();
/** Mark a transaction committed in the main memory data structures. */
diff --git a/storage/innobase/include/trx0undo.h b/storage/innobase/include/trx0undo.h
index 62662ffe221..3de8640606a 100644
--- a/storage/innobase/include/trx0undo.h
+++ b/storage/innobase/include/trx0undo.h
@@ -326,6 +326,34 @@ struct trx_undo_t {
/*!< undo log objects in the rollback
segment are chained into lists */
};
+
+/** Undo record information like rollback segment id, page_id, offset */
+struct trx_undo_rec_info
+{
+ trx_id_t trx_id;
+ buf_block_t *block;
+ ulint offset;
+ ulint type;
+ ulint cmpl_info;
+ bool updated_extern;
+ undo_no_t undo_no;
+ trx_undo_rec_t *undo_rec;
+ upd_t *update= nullptr;
+
+ trx_undo_rec_info(trx_id_t trx_id_, buf_block_t *block_,
+ ulint offset_):
+ trx_id(trx_id_), block(block_), offset(offset_) {}
+
+ void assign_value(ulint type_, ulint cmpl_info_, bool updated_ext,
+ undo_no_t undo_no_)
+ {
+ type= type_;
+ cmpl_info= cmpl_info_;
+ updated_extern= updated_ext;
+ undo_no= undo_no_;
+ }
+};
+
#endif /* !UNIV_INNOCHECKSUM */
/** The offset of the undo log page header on pages of the undo log */
diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc
index 911e33875a1..8daa4f7e8b7 100644
--- a/storage/innobase/row/row0ins.cc
+++ b/storage/innobase/row/row0ins.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2016, 2021, MariaDB Corporation.
+Copyright (c) 2016, 2022, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -35,7 +35,6 @@ Created 4/20/1996 Heikki Tuuri
#include "que0que.h"
#include "row0upd.h"
#include "row0sel.h"
-#include "row0log.h"
#include "rem0cmp.h"
#include "lock0lock.h"
#include "log0log.h"
@@ -2025,7 +2024,6 @@ row_ins_scan_sec_index_for_duplicate(
dict_index_t* index, /*!< in: non-clustered unique index */
dtuple_t* entry, /*!< in: index entry */
que_thr_t* thr, /*!< in: query thread */
- bool s_latch,/*!< in: whether index->lock is being held */
mtr_t* mtr, /*!< in/out: mini-transaction */
mem_heap_t* offsets_heap)
/*!< in/out: memory heap that can be emptied */
@@ -2042,7 +2040,7 @@ row_ins_scan_sec_index_for_duplicate(
rec_offs_init(offsets_);
- ut_ad(s_latch == (index->lock.have_u_not_x() || index->lock.have_s()));
+ ut_ad(!index->lock.have_any());
n_unique = dict_index_get_n_unique(index);
@@ -2066,11 +2064,7 @@ row_ins_scan_sec_index_for_duplicate(
dtuple_set_n_fields_cmp(entry, n_unique);
- btr_pcur_open(index, entry, PAGE_CUR_GE,
- s_latch
- ? BTR_SEARCH_LEAF_ALREADY_S_LATCHED
- : BTR_SEARCH_LEAF,
- &pcur, mtr);
+ btr_pcur_open(index, entry, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, mtr);
allow_duplicates = thr_get_trx(thr)->duplicates;
@@ -2484,11 +2478,6 @@ row_ins_index_entry_big_rec(
&pcur, offsets, big_rec, &mtr, BTR_STORE_INSERT);
DEBUG_SYNC_C_IF_THD(thd, "after_row_ins_extern");
- if (error == DB_SUCCESS
- && dict_index_is_online_ddl(index)) {
- row_log_table_insert(btr_pcur_get_rec(&pcur), index, offsets);
- }
-
mtr.commit();
btr_pcur_close(&pcur);
@@ -2742,11 +2731,6 @@ err_exit:
&pcur, flags, mode, &offsets, &offsets_heap,
entry_heap, entry, thr, &mtr);
- if (err == DB_SUCCESS && dict_index_is_online_ddl(index)) {
- row_log_table_insert(btr_cur_get_rec(cursor),
- index, offsets);
- }
-
mtr_commit(&mtr);
mem_heap_free(entry_heap);
} else {
@@ -2784,9 +2768,9 @@ do_insert:
}
}
- if (big_rec != NULL) {
- mtr_commit(&mtr);
+ mtr.commit();
+ if (big_rec) {
/* Online table rebuild could read (and
ignore) the incomplete record at this point.
If online rebuild is in progress, the
@@ -2799,14 +2783,6 @@ do_insert:
entry, big_rec, offsets, &offsets_heap, index,
trx->mysql_thd);
dtuple_convert_back_big_rec(index, entry, big_rec);
- } else {
- if (err == DB_SUCCESS
- && dict_index_is_online_ddl(index)) {
- row_log_table_insert(
- insert_rec, index, offsets);
- }
-
- mtr_commit(&mtr);
}
}
@@ -2820,19 +2796,10 @@ func_exit:
DBUG_RETURN(err);
}
-/** Start a mini-transaction and check if the index will be dropped.
+/** Start a mini-transaction.
@param[in,out] mtr mini-transaction
-@param[in,out] index secondary index
-@param[in] check whether to check
-@param[in] search_mode flags
-@return true if the index is to be dropped */
-static MY_ATTRIBUTE((warn_unused_result))
-bool
-row_ins_sec_mtr_start_and_check_if_aborted(
- mtr_t* mtr,
- dict_index_t* index,
- bool check,
- ulint search_mode)
+@param[in,out] index secondary index */
+static void row_ins_sec_mtr_start(mtr_t *mtr, dict_index_t *index)
{
ut_ad(!dict_index_is_clust(index));
ut_ad(mtr->is_named_space(index->table->space));
@@ -2842,30 +2809,6 @@ row_ins_sec_mtr_start_and_check_if_aborted(
mtr->start();
index->set_modified(*mtr);
mtr->set_log_mode(log_mode);
-
- if (!check) {
- return(false);
- }
-
- if (search_mode & BTR_ALREADY_S_LATCHED) {
- mtr_s_lock_index(index, mtr);
- } else {
- mtr_sx_lock_index(index, mtr);
- }
-
- switch (index->online_status) {
- case ONLINE_INDEX_ABORTED:
- case ONLINE_INDEX_ABORTED_DROPPED:
- ut_ad(!index->is_committed());
- return(true);
- case ONLINE_INDEX_COMPLETE:
- return(false);
- case ONLINE_INDEX_CREATION:
- break;
- }
-
- ut_error;
- return(true);
}
/***************************************************************//**
@@ -2925,28 +2868,6 @@ row_ins_sec_index_entry_low(
}
}
- /* Ensure that we acquire index->lock when inserting into an
- index with index->online_status == ONLINE_INDEX_COMPLETE, but
- could still be subject to rollback_inplace_alter_table().
- This prevents a concurrent change of index->online_status.
- The memory object cannot be freed as long as we have an open
- reference to the table, or index->table->n_ref_count > 0. */
- const bool check = !index->is_committed();
- if (check) {
- DEBUG_SYNC_C("row_ins_sec_index_enter");
- if (mode == BTR_MODIFY_LEAF) {
- search_mode |= BTR_ALREADY_S_LATCHED;
- mtr_s_lock_index(index, &mtr);
- } else {
- mtr_sx_lock_index(index, &mtr);
- }
-
- if (row_log_online_op_try(
- index, entry, thr_get_trx(thr)->id)) {
- goto func_exit;
- }
- }
-
/* Note that we use PAGE_CUR_LE as the search mode, because then
the function will return in both low_match and up_match of the
cursor sensible values */
@@ -3031,13 +2952,10 @@ row_ins_sec_index_entry_low(
DEBUG_SYNC_C("row_ins_sec_index_unique");
- if (row_ins_sec_mtr_start_and_check_if_aborted(
- &mtr, index, check, search_mode)) {
- goto func_exit;
- }
+ row_ins_sec_mtr_start(&mtr, index);
err = row_ins_scan_sec_index_for_duplicate(
- flags, index, entry, thr, check, &mtr, offsets_heap);
+ flags, index, entry, thr, &mtr, offsets_heap);
mtr_commit(&mtr);
@@ -3066,10 +2984,7 @@ row_ins_sec_index_entry_low(
DBUG_RETURN(err);
}
- if (row_ins_sec_mtr_start_and_check_if_aborted(
- &mtr, index, check, search_mode)) {
- goto func_exit;
- }
+ row_ins_sec_mtr_start(&mtr, index);
DEBUG_SYNC_C("row_ins_sec_index_entry_dup_locks_created");
@@ -3664,7 +3579,8 @@ row_ins(
FTS_DOC_ID for history is enough.
*/
const unsigned type = index->type;
- if (index->type & DICT_FTS) {
+ if (index->type & DICT_FTS
+ || !index->is_committed()) {
} else if (!(type & DICT_UNIQUE) || index->n_uniq > 1
|| !node->vers_history_row()) {
diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc
index 610ac1ad2ae..e3b597d83af 100644
--- a/storage/innobase/row/row0log.cc
+++ b/storage/innobase/row/row0log.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2011, 2018, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, 2021, MariaDB Corporation.
+Copyright (c) 2017, 2022, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -89,79 +89,6 @@ struct row_log_buf_t {
row_log_apply(). */
};
-/** Tracks BLOB allocation during online ALTER TABLE */
-class row_log_table_blob_t {
-public:
- /** Constructor (declaring a BLOB freed)
- @param offset_arg row_log_t::tail::total */
-#ifdef UNIV_DEBUG
- row_log_table_blob_t(ulonglong offset_arg) :
- old_offset (0), free_offset (offset_arg),
- offset (BLOB_FREED) {}
-#else /* UNIV_DEBUG */
- row_log_table_blob_t() :
- offset (BLOB_FREED) {}
-#endif /* UNIV_DEBUG */
-
- /** Declare a BLOB freed again.
- @param offset_arg row_log_t::tail::total */
-#ifdef UNIV_DEBUG
- void blob_free(ulonglong offset_arg)
-#else /* UNIV_DEBUG */
- void blob_free()
-#endif /* UNIV_DEBUG */
- {
- ut_ad(offset < offset_arg);
- ut_ad(offset != BLOB_FREED);
- ut_d(old_offset = offset);
- ut_d(free_offset = offset_arg);
- offset = BLOB_FREED;
- }
- /** Declare a freed BLOB reused.
- @param offset_arg row_log_t::tail::total */
- void blob_alloc(ulonglong offset_arg) {
- ut_ad(free_offset <= offset_arg);
- ut_d(old_offset = offset);
- offset = offset_arg;
- }
- /** Determine if a BLOB was freed at a given log position
- @param offset_arg row_log_t::head::total after the log record
- @return true if freed */
- bool is_freed(ulonglong offset_arg) const {
- /* This is supposed to be the offset at the end of the
- current log record. */
- ut_ad(offset_arg > 0);
- /* We should never get anywhere close the magic value. */
- ut_ad(offset_arg < BLOB_FREED);
- return(offset_arg < offset);
- }
-private:
- /** Magic value for a freed BLOB */
- static const ulonglong BLOB_FREED = ~0ULL;
-#ifdef UNIV_DEBUG
- /** Old offset, in case a page was freed, reused, freed, ... */
- ulonglong old_offset;
- /** Offset of last blob_free() */
- ulonglong free_offset;
-#endif /* UNIV_DEBUG */
- /** Byte offset to the log file */
- ulonglong offset;
-};
-
-/** @brief Map of off-page column page numbers to 0 or log byte offsets.
-
-If there is no mapping for a page number, it is safe to access.
-If a page number maps to 0, it is an off-page column that has been freed.
-If a page number maps to a nonzero number, the number is a byte offset
-into the index->online_log, indicating that the page is safe to access
-when applying log records starting from that offset. */
-typedef std::map<
- ulint,
- row_log_table_blob_t,
- std::less<ulint>,
- ut_allocator<std::pair<const ulint, row_log_table_blob_t> > >
- page_no_map;
-
/** @brief Buffer for logging modifications during online index creation
All modifications to an index that is being created will be logged by
@@ -178,10 +105,6 @@ struct row_log_t {
pfs_os_file_t fd; /*!< file descriptor */
mysql_mutex_t mutex; /*!< mutex protecting error,
max_trx and tail */
- page_no_map* blobs; /*!< map of page numbers of off-page columns
- that have been freed during table-rebuilding
- ALTER TABLE (row_log_table_*); protected by
- index->lock X-latch only */
dict_table_t* table; /*!< table that is being rebuilt,
or NULL when this is a secondary
index that is being created online */
@@ -241,6 +164,11 @@ struct row_log_t {
const TABLE* old_table; /*< Use old table in case of error. */
uint64_t n_rows; /*< Number of rows read from the table */
+
+ /** Alter table transaction. It can be used to apply the DML logs
+ into the table */
+ const trx_t* alter_trx;
+
/** Determine whether the log should be in the 'instant ADD' format
@param[in] index the clustered index of the source table
@return whether to use the 'instant ADD COLUMN' format */
@@ -334,8 +262,6 @@ static void row_log_empty(dict_index_t *index)
row_log_t *log= index->online_log;
mysql_mutex_lock(&log->mutex);
- UT_DELETE(log->blobs);
- log->blobs= nullptr;
row_log_block_free(log->tail);
row_log_block_free(log->head);
row_merge_file_destroy_low(log->fd);
@@ -345,15 +271,15 @@ static void row_log_empty(dict_index_t *index)
mysql_mutex_unlock(&log->mutex);
}
-/******************************************************//**
-Logs an operation to a secondary index that is (or was) being created. */
-void
-row_log_online_op(
-/*==============*/
- dict_index_t* index, /*!< in/out: index, S or X latched */
- const dtuple_t* tuple, /*!< in: index tuple (NULL=empty the index) */
- trx_id_t trx_id) /*!< in: transaction ID for insert,
- or 0 for delete */
+/** Logs an operation to a secondary index that is (or was) being created.
+@param index index, S or X latched
+@param tuple index tuple (NULL= empty the index)
+@param trx_id transaction ID for insert, or 0 for delete
+@retval false if row_log_apply() failure happens
+or true otherwise */
+bool
+row_log_online_op(dict_index_t *index, const dtuple_t *tuple,
+ trx_id_t trx_id)
{
byte* b;
ulint extra_size;
@@ -361,16 +287,19 @@ row_log_online_op(
ulint mrec_size;
ulint avail_size;
row_log_t* log;
+ bool success= true;
ut_ad(!tuple || dtuple_validate(tuple));
ut_ad(!tuple || dtuple_get_n_fields(tuple) == dict_index_get_n_fields(index));
ut_ad(index->lock.have_x() || index->lock.have_s());
if (index->is_corrupted()) {
- return;
+ return success;
}
- ut_ad(dict_index_is_online_ddl(index));
+ ut_ad(dict_index_is_online_ddl(index)
+ || (index->online_log
+ && index->online_status == ONLINE_INDEX_COMPLETE));
/* Compute the size of the record. This differs from
row_merge_buf_encode(), because here we do not encode
@@ -450,7 +379,26 @@ row_log_online_op(
byte* buf = log->tail.block;
if (byte_offset + srv_sort_buf_size >= srv_online_max_size) {
- goto write_failed;
+ if (index->online_status != ONLINE_INDEX_COMPLETE)
+ goto write_failed;
+ /* About to run out of log, InnoDB has to
+ apply the online log for the completed index */
+ index->lock.s_unlock();
+ dberr_t error= row_log_apply(
+ log->alter_trx, index, nullptr, nullptr);
+ index->lock.s_lock(SRW_LOCK_CALL);
+ if (error != DB_SUCCESS) {
+ /** Mark all newly added indexes
+ as corrupted */
+ log->error = error;
+ success = false;
+ goto err_exit;
+ }
+
+ /** Recheck whether the index online log */
+ if (!index->online_log) {
+ goto err_exit;
+ }
}
if (mrec_size == avail_size) {
@@ -510,6 +458,7 @@ write_failed:
MEM_UNDEFINED(log->tail.buf, sizeof log->tail.buf);
err_exit:
mysql_mutex_unlock(&log->mutex);
+ return success;
}
/******************************************************//**
@@ -660,16 +609,16 @@ err_exit:
/** Check whether a virtual column is indexed in the new table being
created during alter table
-@param[in] index cluster index
-@param[in] v_no virtual column number
+@param[in] index cluster index
+@param[in] v_no virtual column number
@return true if it is indexed, else false */
bool
row_log_col_is_indexed(
- const dict_index_t* index,
- ulint v_no)
+ const dict_index_t* index,
+ ulint v_no)
{
- return(dict_table_get_nth_v_col(
- index->online_log->table, v_no)->m_col.ord_part);
+ return(dict_table_get_nth_v_col(
+ index->online_log->table, v_no)->m_col.ord_part);
}
/******************************************************//**
@@ -833,7 +782,6 @@ row_log_table_low_redundant(
dtuple_t* tuple;
const ulint n_fields = rec_get_n_fields_old(rec);
- ut_ad(!page_is_comp(page_align(rec)));
ut_ad(index->n_fields >= n_fields);
ut_ad(index->n_fields == n_fields || index->is_instant());
ut_ad(dict_tf2_is_valid(index->table->flags, index->table->flags2));
@@ -994,22 +942,6 @@ row_log_table_low(
ut_ad(rec_offs_size(offsets) <= sizeof log->tail.buf);
ut_ad(index->lock.have_any());
-#ifdef UNIV_DEBUG
- switch (fil_page_get_type(page_align(rec))) {
- case FIL_PAGE_INDEX:
- break;
- case FIL_PAGE_TYPE_INSTANT:
- ut_ad(index->is_instant());
- ut_ad(!page_has_siblings(page_align(rec)));
- ut_ad(page_get_page_no(page_align(rec)) == index->page);
- break;
- default:
- ut_ad("wrong page type" == 0);
- }
-#endif /* UNIV_DEBUG */
- ut_ad(!rec_is_metadata(rec, *index));
- ut_ad(page_rec_is_leaf(rec));
- ut_ad(!page_is_comp(page_align(rec)) == !rec_offs_comp(offsets));
/* old_pk=row_log_table_get_pk() [not needed in INSERT] is a prefix
of the clustered index record (PRIMARY KEY,DB_TRX_ID,DB_ROLL_PTR),
with no information on virtual columns */
@@ -1028,7 +960,6 @@ row_log_table_low(
return;
}
- ut_ad(page_is_comp(page_align(rec)));
ut_ad(rec_get_status(rec) == REC_STATUS_ORDINARY
|| rec_get_status(rec) == REC_STATUS_INSTANT);
@@ -1471,78 +1402,6 @@ row_log_table_insert(
}
/******************************************************//**
-Notes that a BLOB is being freed during online ALTER TABLE. */
-void
-row_log_table_blob_free(
-/*====================*/
- dict_index_t* index, /*!< in/out: clustered index, X-latched */
- ulint page_no)/*!< in: starting page number of the BLOB */
-{
- ut_ad(dict_index_is_clust(index));
- ut_ad(dict_index_is_online_ddl(index));
- ut_ad(index->lock.have_u_or_x());
- ut_ad(page_no != FIL_NULL);
-
- if (index->online_log->error != DB_SUCCESS) {
- return;
- }
-
- page_no_map* blobs = index->online_log->blobs;
-
- if (blobs == NULL) {
- index->online_log->blobs = blobs = UT_NEW_NOKEY(page_no_map());
- }
-
-#ifdef UNIV_DEBUG
- const ulonglong log_pos = index->online_log->tail.total;
-#else
-# define log_pos /* empty */
-#endif /* UNIV_DEBUG */
-
- const page_no_map::value_type v(page_no,
- row_log_table_blob_t(log_pos));
-
- std::pair<page_no_map::iterator,bool> p = blobs->insert(v);
-
- if (!p.second) {
- /* Update the existing mapping. */
- ut_ad(p.first->first == page_no);
- p.first->second.blob_free(log_pos);
- }
-#undef log_pos
-}
-
-/******************************************************//**
-Notes that a BLOB is being allocated during online ALTER TABLE. */
-void
-row_log_table_blob_alloc(
-/*=====================*/
- dict_index_t* index, /*!< in/out: clustered index, X-latched */
- ulint page_no)/*!< in: starting page number of the BLOB */
-{
- ut_ad(dict_index_is_clust(index));
- ut_ad(dict_index_is_online_ddl(index));
- ut_ad(index->lock.have_u_or_x());
-
- ut_ad(page_no != FIL_NULL);
-
- if (index->online_log->error != DB_SUCCESS) {
- return;
- }
-
- /* Only track allocations if the same page has been freed
- earlier. Double allocation without a free is not allowed. */
- if (page_no_map* blobs = index->online_log->blobs) {
- page_no_map::iterator p = blobs->find(page_no);
-
- if (p != blobs->end()) {
- ut_ad(p->first == page_no);
- p->second.blob_alloc(index->online_log->tail.total);
- }
- }
-}
-
-/******************************************************//**
Converts a log record to a table row.
@return converted row, or NULL if the conversion fails */
static MY_ATTRIBUTE((nonnull, warn_unused_result))
@@ -1618,34 +1477,13 @@ row_log_table_apply_convert_mrec(
ut_ad(rec_offs_any_extern(offsets));
index->lock.x_lock(SRW_LOCK_CALL);
- if (const page_no_map* blobs = log->blobs) {
- data = rec_get_nth_field(
- mrec, offsets, i, &len);
- ut_ad(len >= BTR_EXTERN_FIELD_REF_SIZE);
-
- ulint page_no = mach_read_from_4(
- data + len - (BTR_EXTERN_FIELD_REF_SIZE
- - BTR_EXTERN_PAGE_NO));
- page_no_map::const_iterator p = blobs->find(
- page_no);
- if (p != blobs->end()
- && p->second.is_freed(log->head.total)) {
- /* This BLOB has been freed.
- We must not access the row. */
- *error = DB_MISSING_HISTORY;
- dfield_set_data(dfield, data, len);
- dfield_set_ext(dfield);
- goto blob_done;
- }
- }
-
data = btr_rec_copy_externally_stored_field(
mrec, offsets,
index->table->space->zip_size(),
i, &len, heap);
ut_a(data);
dfield_set_data(dfield, data, len);
-blob_done:
+
index->lock.x_unlock();
} else {
data = rec_get_nth_field(mrec, offsets, i, &len);
@@ -1693,6 +1531,12 @@ blob_done:
if ((new_col->prtype & DATA_NOT_NULL)
&& dfield_is_null(dfield)) {
+ if (!log->allow_not_null) {
+ /* We got a NULL value for a NOT NULL column. */
+ *error = DB_INVALID_NULL;
+ return NULL;
+ }
+
const dfield_t& default_field
= log->defaults->fields[col_no];
@@ -1702,12 +1546,6 @@ blob_done:
WARN_DATA_TRUNCATED, 1,
ulong(log->n_rows));
- if (!log->allow_not_null) {
- /* We got a NULL value for a NOT NULL column. */
- *error = DB_INVALID_NULL;
- return NULL;
- }
-
*dfield = default_field;
}
@@ -1818,15 +1656,6 @@ row_log_table_apply_insert(
mrec, dup->index, offsets, log, heap, &error);
switch (error) {
- case DB_MISSING_HISTORY:
- ut_ad(log->blobs);
- /* Because some BLOBs are missing, we know that the
- transaction was rolled back later (a rollback of
- an insert can free BLOBs).
- We can simply skip the insert: the subsequent
- ROW_T_DELETE will be ignored, or a ROW_T_UPDATE will
- be interpreted as ROW_T_INSERT. */
- return(DB_SUCCESS);
case DB_SUCCESS:
ut_ad(row != NULL);
break;
@@ -2101,20 +1930,6 @@ row_log_table_apply_update(
mrec, dup->index, offsets, log, heap, &error);
switch (error) {
- case DB_MISSING_HISTORY:
- /* The record contained BLOBs that are now missing. */
- ut_ad(log->blobs);
- /* Whether or not we are updating the PRIMARY KEY, we
- know that there should be a subsequent
- ROW_T_DELETE for rolling back a preceding ROW_T_INSERT,
- overriding this ROW_T_UPDATE record. (*1)
-
- This allows us to interpret this ROW_T_UPDATE
- as ROW_T_DELETE.
-
- When applying the subsequent ROW_T_DELETE, no matching
- record will be found. */
- /* fall through */
case DB_SUCCESS:
ut_ad(row != NULL);
break;
@@ -2144,79 +1959,16 @@ row_log_table_apply_update(
}
#endif /* UNIV_DEBUG */
- if (page_rec_is_infimum(btr_pcur_get_rec(&pcur))
- || btr_pcur_get_low_match(&pcur) < index->n_uniq) {
- /* The record was not found. This should only happen
- when an earlier ROW_T_INSERT or ROW_T_UPDATE was
- diverted because BLOBs were freed when the insert was
- later rolled back. */
-
- ut_ad(log->blobs);
-
- if (error == DB_SUCCESS) {
- /* An earlier ROW_T_INSERT could have been
- skipped because of a missing BLOB, like this:
-
- BEGIN;
- INSERT INTO t SET blob_col='blob value';
- UPDATE t SET blob_col='';
- ROLLBACK;
-
- This would generate the following records:
- ROW_T_INSERT (referring to 'blob value')
- ROW_T_UPDATE
- ROW_T_UPDATE (referring to 'blob value')
- ROW_T_DELETE
- [ROLLBACK removes the 'blob value']
-
- The ROW_T_INSERT would have been skipped
- because of a missing BLOB. Now we are
- executing the first ROW_T_UPDATE.
- The second ROW_T_UPDATE (for the ROLLBACK)
- would be interpreted as ROW_T_DELETE, because
- the BLOB would be missing.
-
- We could probably assume that the transaction
- has been rolled back and simply skip the
- 'insert' part of this ROW_T_UPDATE record.
- However, there might be some complex scenario
- that could interfere with such a shortcut.
- So, we will insert the row (and risk
- introducing a bogus duplicate key error
- for the ALTER TABLE), and a subsequent
- ROW_T_UPDATE or ROW_T_DELETE will delete it. */
- mtr_commit(&mtr);
- error = row_log_table_apply_insert_low(
- thr, row, offsets_heap, heap, dup);
- } else {
- /* Some BLOBs are missing, so we are interpreting
- this ROW_T_UPDATE as ROW_T_DELETE (see *1).
- Because the record was not found, we do nothing. */
- ut_ad(error == DB_MISSING_HISTORY);
- error = DB_SUCCESS;
-func_exit:
- mtr_commit(&mtr);
- }
-func_exit_committed:
- ut_ad(mtr.has_committed());
-
- if (error != DB_SUCCESS) {
- /* Report the erroneous row using the new
- version of the table. */
- innobase_row_to_mysql(dup->table, log->table, row);
- }
-
- return(error);
- }
+ ut_ad(!page_rec_is_infimum(btr_pcur_get_rec(&pcur))
+ && btr_pcur_get_low_match(&pcur) >= index->n_uniq);
/* Prepare to update (or delete) the record. */
rec_offs* cur_offsets = rec_get_offsets(
btr_pcur_get_rec(&pcur), index, nullptr, index->n_core_fields,
ULINT_UNDEFINED, &offsets_heap);
+#ifdef UNIV_DEBUG
if (!log->same_pk) {
- /* Only update the record if DB_TRX_ID,DB_ROLL_PTR match what
- was buffered. */
ulint len;
const byte* rec_trx_id
= rec_get_nth_field(btr_pcur_get_rec(&pcur),
@@ -2231,60 +1983,29 @@ func_exit_committed:
+ static_cast<const char*>(old_pk_trx_id->data)
== old_pk_trx_id[1].data);
ut_d(trx_id_check(old_pk_trx_id->data, log->min_trx));
-
- if (memcmp(rec_trx_id, old_pk_trx_id->data,
- DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN)) {
- /* The ROW_T_UPDATE was logged for a different
- DB_TRX_ID,DB_ROLL_PTR. This is possible if an
- earlier ROW_T_INSERT or ROW_T_UPDATE was diverted
- because some BLOBs were missing due to rolling
- back the initial insert or due to purging
- the old BLOB values of an update. */
- ut_ad(log->blobs);
- if (error != DB_SUCCESS) {
- ut_ad(error == DB_MISSING_HISTORY);
- /* Some BLOBs are missing, so we are
- interpreting this ROW_T_UPDATE as
- ROW_T_DELETE (see *1).
- Because this is a different row,
- we will do nothing. */
- error = DB_SUCCESS;
- } else {
- /* Because the user record is missing due to
- BLOBs that were missing when processing
- an earlier log record, we should
- interpret the ROW_T_UPDATE as ROW_T_INSERT.
- However, there is a different user record
- with the same PRIMARY KEY value already. */
- error = DB_DUPLICATE_KEY;
- }
-
- goto func_exit;
- }
- }
-
- if (error != DB_SUCCESS) {
- ut_ad(error == DB_MISSING_HISTORY);
- ut_ad(log->blobs);
- /* Some BLOBs are missing, so we are interpreting
- this ROW_T_UPDATE as ROW_T_DELETE (see *1). */
- error = row_log_table_apply_delete_low(
- &pcur, cur_offsets, heap, &mtr);
- goto func_exit_committed;
+ ut_ad(!memcmp(rec_trx_id, old_pk_trx_id->data,
+ DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN));
}
+#endif
dtuple_t* entry = row_build_index_entry_low(
row, NULL, index, heap, ROW_BUILD_NORMAL);
upd_t* update = row_upd_build_difference_binary(
index, entry, btr_pcur_get_rec(&pcur), cur_offsets,
false, NULL, heap, dup->table, &error);
- if (error != DB_SUCCESS) {
- goto func_exit;
- }
+ if (error != DB_SUCCESS || !update->n_fields) {
+func_exit:
+ mtr.commit();
+func_exit_committed:
+ ut_ad(mtr.has_committed());
- if (!update->n_fields) {
- /* Nothing to do. */
- goto func_exit;
+ if (error != DB_SUCCESS) {
+ /* Report the erroneous row using the new
+ version of the table. */
+ innobase_row_to_mysql(dup->table, log->table, row);
+ }
+
+ return error;
}
const bool pk_updated
@@ -2739,7 +2460,8 @@ ulint
row_log_estimate_work(
const dict_index_t* index)
{
- if (index == NULL || index->online_log == NULL) {
+ if (index == NULL || index->online_log == NULL
+ || index->online_log == reinterpret_cast<row_log_t*>(index->table)) {
return(0);
}
@@ -3230,7 +2952,6 @@ row_log_allocate(
log->fd = OS_FILE_CLOSED;
mysql_mutex_init(index_online_log_key, &log->mutex, nullptr);
- log->blobs = NULL;
log->table = table;
log->same_pk = same_pk;
log->defaults = defaults;
@@ -3280,6 +3001,15 @@ row_log_allocate(
}
index->online_log = log;
+
+ if (!table) {
+ /* Assign the clustered index online log to table.
+ It can be used by concurrent DML to identify whether
+ the table has any online DDL */
+ index->table->indexes.start->assign_dummy_log();
+ log->alter_trx= trx;
+ }
+
/* While we might be holding an exclusive data dictionary lock
here, in row_log_abort_sec() we will not always be holding it. Use
atomic operations in both cases. */
@@ -3297,7 +3027,6 @@ row_log_free(
{
MONITOR_ATOMIC_DEC(MONITOR_ONLINE_CREATE_INDEX);
- UT_DELETE(log->blobs);
UT_DELETE_ARRAY(log->non_core_fields);
row_log_block_free(log->tail);
row_log_block_free(log->head);
@@ -3720,7 +3449,9 @@ row_log_apply_ops(
const ulint i = 1 + REC_OFFS_HEADER_SIZE
+ dict_index_get_n_fields(index);
- ut_ad(dict_index_is_online_ddl(index));
+ ut_ad(dict_index_is_online_ddl(index)
+ || (index->online_log
+ && index->online_status == ONLINE_INDEX_COMPLETE));
ut_ad(!index->is_committed());
ut_ad(index->lock.have_x());
ut_ad(index->online_log);
@@ -3740,7 +3471,8 @@ next_block:
ut_ad(index->lock.have_x());
ut_ad(index->online_log->head.bytes == 0);
- stage->inc(row_log_progress_inc_per_block());
+ if (stage)
+ stage->inc(row_log_progress_inc_per_block());
if (trx_is_interrupted(trx)) {
goto interrupted;
@@ -3792,6 +3524,8 @@ corruption:
/* End of log reached. */
all_done:
ut_ad(has_index_lock);
+ index->online_log->tail.bytes = 0;
+ index->online_log->head.bytes = 0;
ut_ad(index->online_log->head.blocks == 0);
ut_ad(index->online_log->tail.blocks == 0);
error = DB_SUCCESS;
@@ -4039,20 +3773,24 @@ row_log_apply(
ut_stage_alter_t* stage)
{
dberr_t error;
- row_log_t* log;
row_merge_dup_t dup = { index, table, NULL, 0 };
DBUG_ENTER("row_log_apply");
- ut_ad(dict_index_is_online_ddl(index));
+ ut_ad(dict_index_is_online_ddl(index)
+ || (index->online_log
+ && index->online_status == ONLINE_INDEX_COMPLETE));
ut_ad(!dict_index_is_clust(index));
- stage->begin_phase_log_index();
+ if (stage) {
+ stage->begin_phase_log_index();
+ }
log_free_check();
index->lock.x_lock(SRW_LOCK_CALL);
- if (!dict_table_is_corrupted(index->table)) {
+ if (!dict_table_is_corrupted(index->table)
+ && index->online_log) {
error = row_log_apply_ops(trx, index, &dup, stage);
} else {
error = DB_SUCCESS;
@@ -4067,17 +3805,15 @@ row_log_apply(
index->table->drop_aborted = TRUE;
dict_index_set_online_status(index, ONLINE_INDEX_ABORTED);
- } else {
+ } else if (stage) {
+ /* Mark the index as completed only when it is
+ being called by DDL thread */
ut_ad(dup.n_dup == 0);
dict_index_set_online_status(index, ONLINE_INDEX_COMPLETE);
}
- log = index->online_log;
- index->online_log = NULL;
index->lock.x_unlock();
- row_log_free(log);
-
DBUG_RETURN(error);
}
@@ -4102,6 +3838,12 @@ static void row_log_table_empty(dict_index_t *index)
}
}
+dberr_t row_log_get_error(const dict_index_t *index)
+{
+ ut_ad(index->online_log);
+ return index->online_log->error;
+}
+
void dict_table_t::clear(que_thr_t *thr)
{
bool rebuild= false;
@@ -4138,3 +3880,343 @@ void dict_table_t::clear(que_thr_t *thr)
index->clear(thr);
}
}
+
+/** Get the version of clustered index record which was modified
+by undo log record
+@param rec clustered index record
+@param index clustered index
+@param offsets rec_get_offsets(rec, index)
+@param rec_info undo log record information
+@param mtr mtr which contains the latch to rec page
+@param heap memory heap
+@return version of clustered index record */
+static rec_t *row_log_vers_undo_match(
+ rec_t *rec, dict_index_t *index,
+ rec_offs **offsets, const trx_undo_rec_info *rec_info,
+ mtr_t *mtr, mem_heap_t *heap)
+{
+ ut_ad(dict_index_is_clust(index));
+ ulint len= 0;
+ rec_t *prev_version;
+ rec_t *version= rec;
+ bool match= false;
+ while (!match && version != nullptr)
+ {
+ *offsets= rec_get_offsets(version, index, *offsets,
+ index->n_core_fields, ULINT_UNDEFINED,
+ &heap);
+ trx_id_t trx_id= trx_read_trx_id(
+ rec_get_nth_field(version, *offsets, index->db_trx_id(), &len));
+
+ ut_ad(len == DATA_TRX_ID_LEN);
+ ut_ad(trx_id == rec_info->trx_id);
+
+ roll_ptr_t roll_ptr= trx_read_roll_ptr(
+ rec_get_nth_field(version, *offsets, index->db_roll_ptr(), &len));
+ ut_ad(len == DATA_ROLL_PTR_LEN);
+ match= trx_undo_rec_is_equal(roll_ptr, rec_info);
+ if (!match)
+ {
+ trx_undo_prev_version_build(rec, mtr, version, index,
+ *offsets, heap, &prev_version, NULL,
+ NULL, 0, rec_info);
+ version= prev_version;
+ }
+ }
+
+ return version;
+}
+
+/** Get the clustered index record version which was modified by
+undo log record
+@param tuple tuple contains primary key value
+@param index clustered index
+@param[out] clust_rec clustered index record
+@param offsets offsets points to the record
+@param rec_info undo log record info
+@param mtr mtr
+@param heap memory heap
+@return clustered index record */
+static rec_t* row_log_undo_vers_record(const dtuple_t *tuple,
+ dict_index_t *index,
+ rec_t **clust_rec,
+ rec_offs **offsets,
+ const trx_undo_rec_info *rec_info,
+ mtr_t *mtr, mem_heap_t *heap)
+{
+ ut_ad(dict_index_is_clust(index));
+ btr_pcur_t pcur;
+ bool found= row_search_on_row_ref(&pcur, BTR_MODIFY_LEAF,
+ index->table, tuple, mtr);
+ ut_ad(found);
+ *clust_rec= btr_pcur_get_rec(&pcur);
+
+ rec_t *match_rec= row_log_vers_undo_match(*clust_rec, index, offsets,
+ rec_info, mtr, heap);
+ return match_rec;
+}
+
+/** Clear out all online log of other online indexes after
+encountering the error during row_log_apply() in DML thread
+@param table table which does online DDL */
+static void row_log_mark_other_online_index_abort(dict_table_t *table)
+{
+ dict_index_t *clust_index= dict_table_get_first_index(table);
+ dict_index_t *index= dict_table_get_next_index(clust_index);
+ while (index)
+ {
+ if (index->online_log
+ && index->online_status <= ONLINE_INDEX_CREATION
+ && !index->is_corrupted())
+ {
+ index->lock.x_lock(SRW_LOCK_CALL);
+ row_log_abort_sec(index);
+ index->type |= DICT_CORRUPT;
+ index->lock.x_unlock();
+ }
+ index= dict_table_get_next_index(index);
+ }
+ clust_index->clear_dummy_log();
+ table->drop_aborted= TRUE;
+ MONITOR_ATOMIC_INC(MONITOR_BACKGROUND_DROP_INDEX);
+}
+
+void row_log_insert_handle(const dtuple_t *tuple,
+ const trx_undo_rec_info *rec_info,
+ dict_index_t *clust_index,
+ mem_heap_t *heap)
+{
+ DEBUG_SYNC_C("row_log_insert_handle");
+ ut_ad(dict_index_is_clust(clust_index));
+ rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
+ rec_offs *offsets= offsets_;
+ mtr_t mtr;
+
+ rec_offs_init(offsets_);
+ mtr.start();
+ rec_t *rec;
+ rec_t *match_rec= row_log_undo_vers_record(tuple, clust_index, &rec,
+ &offsets,
+ rec_info, &mtr, heap);
+ if (!match_rec)
+ {
+ mtr.commit();
+ return;
+ }
+
+ if (match_rec != rec)
+ offsets= rec_get_offsets(match_rec, clust_index, offsets,
+ clust_index->n_core_fields,
+ ULINT_UNDEFINED, &heap);
+
+ rec_t *copy_rec= rec_copy(mem_heap_alloc(
+ heap, rec_offs_size(offsets)), match_rec, offsets);
+ rec_offs_make_valid(copy_rec, clust_index, true, offsets);
+ mtr.commit();
+
+ dict_table_t *table= clust_index->table;
+ clust_index->lock.s_lock(SRW_LOCK_CALL);
+ if (clust_index->online_log
+ && clust_index->online_log != reinterpret_cast<row_log_t*>(table)
+ && clust_index->online_status <= ONLINE_INDEX_CREATION)
+ {
+ row_log_table_insert(copy_rec, clust_index, offsets);
+ clust_index->lock.s_unlock();
+ }
+ else
+ {
+ clust_index->lock.s_unlock();
+ ulint len= 0;
+ trx_id_t trx_id= trx_read_trx_id(
+ rec_get_nth_field(copy_rec, offsets, clust_index->db_trx_id(), &len));
+ ut_ad(len == DATA_TRX_ID_LEN);
+ row_ext_t *ext;
+ dtuple_t *row= row_build(ROW_COPY_POINTERS, clust_index,
+ copy_rec, offsets, table, NULL, NULL, &ext, heap);
+
+ if (table->n_v_cols)
+ {
+ if (rec_info->type == TRX_UNDO_UPD_DEL_REC)
+ row_upd_replace_vcol(row, table, rec_info->update, false,
+ nullptr,
+ (rec_info->cmpl_info & UPD_NODE_NO_ORD_CHANGE)
+ ? nullptr : rec_info->undo_rec);
+ else trx_undo_read_v_cols(table, rec_info->undo_rec, row, false);
+ }
+
+ bool success= true;
+ dict_index_t *index= dict_table_get_next_index(clust_index);
+ while (index)
+ {
+ index->lock.s_lock(SRW_LOCK_CALL);
+ if (index->online_log
+ && index->online_status <= ONLINE_INDEX_CREATION
+ && !index->is_corrupted())
+ {
+ dtuple_t *entry= row_build_index_entry_low(row, ext, index,
+ heap, ROW_BUILD_NORMAL);
+ success= row_log_online_op(index, entry, trx_id);
+ }
+
+ index->lock.s_unlock();
+ if (!success)
+ {
+ row_log_mark_other_online_index_abort(index->table);
+ return;
+ }
+ index= dict_table_get_next_index(index);
+ }
+ }
+}
+
+void row_log_update_handle(const dtuple_t *tuple,
+ const trx_undo_rec_info *rec_info,
+ dict_index_t *clust_index,
+ mem_heap_t *heap)
+{
+ rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
+ rec_offs offsets2_[REC_OFFS_NORMAL_SIZE];
+ rec_offs *offsets= offsets_;
+ rec_offs *prev_offsets= offsets2_;
+ mtr_t mtr;
+
+ rec_offs_init(offsets_);
+ rec_offs_init(offsets2_);
+
+ dict_table_t *table= clust_index->table;
+
+ clust_index->lock.s_lock(SRW_LOCK_CALL);
+ bool table_rebuild=
+ (clust_index->online_log
+ && clust_index->online_log != reinterpret_cast<row_log_t*>(table)
+ && clust_index->online_status <= ONLINE_INDEX_CREATION);
+ clust_index->lock.s_unlock();
+
+ mtr.start();
+ rec_t *rec;
+ rec_t *prev_version;
+ bool is_update= (rec_info->type == TRX_UNDO_UPD_EXIST_REC);
+ rec_t *match_rec= row_log_undo_vers_record(
+ tuple, clust_index, &rec, &offsets, rec_info, &mtr, heap);
+ if (!match_rec)
+ {
+ mtr.commit();
+ return;
+ }
+
+ rec_t *copy_rec= rec_copy(mem_heap_alloc(
+ heap, rec_offs_size(offsets)), match_rec, offsets);
+
+ if (table_rebuild)
+ {
+ trx_undo_prev_version_build(rec, &mtr, match_rec, clust_index,
+ offsets, heap, &prev_version, NULL,
+ NULL, 0, rec_info);
+
+ prev_offsets= rec_get_offsets(prev_version, clust_index, prev_offsets,
+ clust_index->n_core_fields,
+ ULINT_UNDEFINED, &heap);
+
+ offsets= rec_get_offsets(match_rec, clust_index, offsets,
+ clust_index->n_core_fields,
+ ULINT_UNDEFINED, &heap);
+
+ rec_t *prev_copy_rec= rec_copy(mem_heap_alloc(
+ heap, rec_offs_size(prev_offsets)), prev_version, prev_offsets);
+ rec_offs_make_valid(prev_copy_rec, clust_index, true, prev_offsets);
+ rec_offs_make_valid(copy_rec, clust_index, true, offsets);
+ mtr.commit();
+
+ clust_index->lock.s_lock(SRW_LOCK_CALL);
+ /* Recheck whether clustered index online log has been cleared */
+ if (!clust_index->online_log)
+ goto clust_exit;
+ if (is_update)
+ {
+ const dtuple_t *rebuilt_old_pk= row_log_table_get_pk(
+ prev_copy_rec, clust_index, prev_offsets, NULL, &heap);
+ row_log_table_update(copy_rec, clust_index, offsets, rebuilt_old_pk);
+ }
+ else
+ row_log_table_delete(prev_copy_rec, clust_index, prev_offsets, nullptr);
+clust_exit:
+ clust_index->lock.s_unlock();
+ return;
+ }
+
+ rec_offs_make_valid(copy_rec, clust_index, true, offsets);
+ mtr.commit();
+ dtuple_t *row= nullptr;
+ trx_id_t trx_id;
+ row_ext_t *new_ext;
+ row_ext_t *old_ext;
+ dtuple_t *old_row= nullptr;
+ ulint len;
+
+ trx_id= trx_read_trx_id(
+ rec_get_nth_field(
+ copy_rec, offsets, clust_index->db_trx_id(), &len));
+ ut_ad(len == DATA_TRX_ID_LEN);
+ row= row_build(ROW_COPY_POINTERS, clust_index,
+ copy_rec, offsets, clust_index->table, NULL, NULL,
+ &new_ext, heap);
+ if (table->n_v_cols
+ && !(rec_info->cmpl_info & UPD_NODE_NO_ORD_CHANGE))
+ {
+ for (ulint i = 0; i < dict_table_get_n_v_cols(table); i++)
+ dfield_get_type(
+ dtuple_get_nth_v_field(row, i))->mtype = DATA_MISSING;
+ }
+
+ if (is_update)
+ {
+ old_row= dtuple_copy(row, heap);
+ row_upd_replace(old_row, &old_ext, clust_index, rec_info->update,
+ heap);
+ }
+
+ if (table->n_v_cols)
+ row_upd_replace_vcol(row, table, rec_info->update, false,
+ nullptr,
+ (rec_info->cmpl_info & UPD_NODE_NO_ORD_CHANGE)
+ ? nullptr : rec_info->undo_rec);
+
+ bool success= true;
+ dict_index_t *index= dict_table_get_next_index(clust_index);
+ while (index)
+ {
+ index->lock.s_lock(SRW_LOCK_CALL);
+ if (index->online_log
+ && index->online_status <= ONLINE_INDEX_CREATION
+ && !index->is_corrupted())
+ {
+ if (is_update)
+ {
+ dtuple_t *old_entry= row_build_index_entry_low(
+ old_row, old_ext, index, heap, ROW_BUILD_NORMAL);
+
+ success= row_log_online_op(index, old_entry, 0);
+
+ dtuple_t *new_entry= row_build_index_entry_low(
+ row, new_ext, index, heap, ROW_BUILD_NORMAL);
+
+ if (success)
+ success= row_log_online_op(index, new_entry, trx_id);
+ }
+ else
+ {
+ dtuple_t *old_entry= row_build_index_entry_low(
+ row, new_ext, index, heap, ROW_BUILD_NORMAL);
+
+ success= row_log_online_op(index, old_entry, 0);
+ }
+ }
+ index->lock.s_unlock();
+ if (!success)
+ {
+ row_log_mark_other_online_index_abort(index->table);
+ return;
+ }
+ index= dict_table_get_next_index(index);
+ }
+}
diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc
index a515d7d7e56..18b21fb34c2 100644
--- a/storage/innobase/row/row0merge.cc
+++ b/storage/innobase/row/row0merge.cc
@@ -853,7 +853,7 @@ row_merge_dup_report(
row_merge_dup_t* dup, /*!< in/out: for reporting duplicates */
const dfield_t* entry) /*!< in: duplicate index entry */
{
- if (!dup->n_dup++) {
+ if (!dup->n_dup++ && dup->table) {
/* Only report the first duplicate record,
but count all duplicate records. */
innobase_fields_to_mysql(dup->table, dup->index, entry);
@@ -3629,11 +3629,7 @@ row_merge_insert_index_tuples(
Any modifications after the
row_merge_read_clustered_index() scan
- will go through row_log_table_apply().
- Any modifications to off-page columns
- will be tracked by
- row_log_table_blob_alloc() and
- row_log_table_blob_free(). */
+ will go through row_log_table_apply(). */
row_merge_copy_blobs(
mrec, offsets, old_table->space->zip_size(),
dtuple, tuple_heap);
@@ -4814,6 +4810,8 @@ func_exit:
MONITOR_BACKGROUND_DROP_INDEX);
}
}
+
+ new_table->indexes.start->clear_dummy_log();
}
DBUG_EXECUTE_IF("ib_index_crash_after_bulk_load", DBUG_SUICIDE(););
diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc
index e6267a2023a..3ba2e964689 100644
--- a/storage/innobase/row/row0purge.cc
+++ b/storage/innobase/row/row0purge.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, 2021, MariaDB Corporation.
+Copyright (c) 2017, 2022, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -41,7 +41,6 @@ Created 3/14/1997 Heikki Tuuri
#include "row0upd.h"
#include "row0vers.h"
#include "row0mysql.h"
-#include "row0log.h"
#include "log0log.h"
#include "srv0mon.h"
#include "srv0start.h"
@@ -359,27 +358,6 @@ row_purge_remove_sec_if_poss_tree(
mtr.start();
index->set_modified(mtr);
- if (!index->is_committed()) {
- /* The index->online_status may change if the index is
- or was being created online, but not committed yet. It
- is protected by index->lock. */
- mtr_sx_lock_index(index, &mtr);
-
- if (dict_index_is_online_ddl(index)) {
- /* Online secondary index creation will not
- copy any delete-marked records. Therefore
- there is nothing to be purged. We must also
- skip the purge when a completed index is
- dropped by rollback_inplace_alter_table(). */
- goto func_exit_no_pcur;
- }
- } else {
- /* For secondary indexes,
- index->online_status==ONLINE_INDEX_COMPLETE if
- index->is_committed(). */
- ut_ad(!dict_index_is_online_ddl(index));
- }
-
search_result = row_search_index_entry(
index, entry,
BTR_MODIFY_TREE | BTR_LATCH_FOR_DELETE,
@@ -452,7 +430,6 @@ row_purge_remove_sec_if_poss_tree(
func_exit:
btr_pcur_close(&pcur); // FIXME: need this?
-func_exit_no_pcur:
mtr.commit();
return(success);
@@ -483,40 +460,10 @@ row_purge_remove_sec_if_poss_leaf(
mtr.start();
index->set_modified(mtr);
- if (!index->is_committed()) {
- /* For uncommitted spatial index, we also skip the purge. */
- if (dict_index_is_spatial(index)) {
- goto func_exit_no_pcur;
- }
-
- /* The index->online_status may change if the the
- index is or was being created online, but not
- committed yet. It is protected by index->lock. */
- mtr_s_lock_index(index, &mtr);
-
- if (dict_index_is_online_ddl(index)) {
- /* Online secondary index creation will not
- copy any delete-marked records. Therefore
- there is nothing to be purged. We must also
- skip the purge when a completed index is
- dropped by rollback_inplace_alter_table(). */
- goto func_exit_no_pcur;
- }
-
- mode = BTR_PURGE_LEAF_ALREADY_S_LATCHED;
- } else {
- /* For secondary indexes,
- index->online_status==ONLINE_INDEX_COMPLETE if
- index->is_committed(). */
- ut_ad(!dict_index_is_online_ddl(index));
-
- /* Change buffering is disabled for spatial index and
- virtual index. */
- mode = (dict_index_is_spatial(index)
- || dict_index_has_virtual(index))
- ? BTR_MODIFY_LEAF
- : BTR_PURGE_LEAF;
- }
+ /* Change buffering is disabled for spatial index and
+ virtual index. */
+ mode = (index->type & (DICT_SPATIAL | DICT_VIRTUAL))
+ ? BTR_MODIFY_LEAF : BTR_PURGE_LEAF;
/* Set the purge node for the call to row_purge_poss_sec(). */
pcur.btr_cur.purge_node = node;
diff --git a/storage/innobase/row/row0uins.cc b/storage/innobase/row/row0uins.cc
index 173ae9a98f9..446d56f8daa 100644
--- a/storage/innobase/row/row0uins.cc
+++ b/storage/innobase/row/row0uins.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, 2021, MariaDB Corporation.
+Copyright (c) 2017, 2022, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -35,7 +35,6 @@ Created 2/25/1997 Heikki Tuuri
#include "mach0data.h"
#include "row0undo.h"
#include "row0vers.h"
-#include "row0log.h"
#include "trx0trx.h"
#include "trx0rec.h"
#include "row0row.h"
@@ -70,7 +69,6 @@ row_undo_ins_remove_clust_rec(
ulint n_tries = 0;
mtr_t mtr;
dict_index_t* index = node->pcur.btr_cur.index;
- bool online;
table_id_t table_id = 0;
const bool dict_locked = node->trx->dict_operation_lock_mode;
restart:
@@ -90,20 +88,10 @@ restart:
if (index->table->is_temporary()) {
ut_ad(node->rec_type == TRX_UNDO_INSERT_REC);
mtr.set_log_mode(MTR_LOG_NO_REDO);
- ut_ad(!dict_index_is_online_ddl(index));
ut_ad(index->table->id >= DICT_HDR_FIRST_ID);
- online = false;
} else {
index->set_modified(mtr);
ut_ad(lock_table_has_locks(index->table));
- online = dict_index_is_online_ddl(index);
- if (online) {
- ut_ad(node->rec_type == TRX_UNDO_INSERT_REC);
- ut_ad(!node->trx->dict_operation_lock_mode);
- ut_ad(node->table->id != DICT_INDEXES_ID);
- ut_ad(node->table->id != DICT_COLUMNS_ID);
- mtr_s_lock_index(index, &mtr);
- }
}
/* This is similar to row_undo_mod_clust(). The DDL thread may
@@ -112,8 +100,7 @@ restart:
purged. However, we can log the removal out of sync with the
B-tree modification. */
ut_a(node->pcur.restore_position(
- online ? BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED
- : (node->rec_type == TRX_UNDO_INSERT_METADATA)
+ (node->rec_type == TRX_UNDO_INSERT_METADATA)
? BTR_MODIFY_TREE
: BTR_MODIFY_LEAF,
&mtr) == btr_pcur_t::SAME_ALL);
@@ -126,94 +113,83 @@ restart:
ut_ad(rec_is_metadata(rec, index->table->not_redundant())
== (node->rec_type == TRX_UNDO_INSERT_METADATA));
- if (online && dict_index_is_online_ddl(index)) {
- mem_heap_t* heap = NULL;
- const rec_offs* offsets = rec_get_offsets(
- rec, index, NULL, index->n_core_fields,
- ULINT_UNDEFINED, &heap);
- row_log_table_delete(rec, index, offsets, NULL);
- mem_heap_free(heap);
- } else {
- switch (node->table->id) {
- case DICT_COLUMNS_ID:
- /* This is rolling back an INSERT into SYS_COLUMNS.
- If it was part of an instant ALTER TABLE operation, we
- must evict the table definition, so that it can be
- reloaded after the dictionary operation has been
- completed. At this point, any corresponding operation
- to the metadata record will have been rolled back. */
- ut_ad(!online);
- ut_ad(node->trx->dict_operation_lock_mode);
- ut_ad(node->rec_type == TRX_UNDO_INSERT_REC);
- if (rec_get_n_fields_old(rec)
- != DICT_NUM_FIELDS__SYS_COLUMNS
- || (rec_get_1byte_offs_flag(rec)
- ? rec_1_get_field_end_info(rec, 0) != 8
- : rec_2_get_field_end_info(rec, 0) != 8)) {
- break;
- }
- static_assert(!DICT_FLD__SYS_COLUMNS__TABLE_ID, "");
- node->trx->evict_table(mach_read_from_8(rec));
+ switch (node->table->id) {
+ case DICT_COLUMNS_ID:
+ /* This is rolling back an INSERT into SYS_COLUMNS.
+ If it was part of an instant ALTER TABLE operation, we
+ must evict the table definition, so that it can be
+ reloaded after the dictionary operation has been
+ completed. At this point, any corresponding operation
+ to the metadata record will have been rolled back. */
+ ut_ad(node->trx->dict_operation_lock_mode);
+ ut_ad(node->rec_type == TRX_UNDO_INSERT_REC);
+ if (rec_get_n_fields_old(rec)
+ != DICT_NUM_FIELDS__SYS_COLUMNS
+ || (rec_get_1byte_offs_flag(rec)
+ ? rec_1_get_field_end_info(rec, 0) != 8
+ : rec_2_get_field_end_info(rec, 0) != 8)) {
break;
- case DICT_INDEXES_ID:
- ut_ad(!online);
- ut_ad(node->trx->dict_operation_lock_mode);
- ut_ad(node->rec_type == TRX_UNDO_INSERT_REC);
- if (!table_id) {
- table_id = mach_read_from_8(rec);
- if (table_id) {
- mtr.commit();
- goto restart;
- }
- ut_ad("corrupted SYS_INDEXES record" == 0);
+ }
+ static_assert(!DICT_FLD__SYS_COLUMNS__TABLE_ID, "");
+ node->trx->evict_table(mach_read_from_8(rec));
+ break;
+ case DICT_INDEXES_ID:
+ ut_ad(node->trx->dict_operation_lock_mode);
+ ut_ad(node->rec_type == TRX_UNDO_INSERT_REC);
+ if (!table_id) {
+ table_id = mach_read_from_8(rec);
+ if (table_id) {
+ mtr.commit();
+ goto restart;
}
+ ut_ad("corrupted SYS_INDEXES record" == 0);
+ }
- pfs_os_file_t d = OS_FILE_CLOSED;
-
- if (const uint32_t space_id = dict_drop_index_tree(
- &node->pcur, node->trx, &mtr)) {
- if (table) {
- lock_release_on_rollback(node->trx,
- table);
- if (!dict_locked) {
- dict_sys.lock(SRW_LOCK_CALL);
- }
- if (table->release()) {
- dict_sys.remove(table);
- } else if (table->space_id
- == space_id) {
- table->space = nullptr;
- table->file_unreadable = true;
- }
- if (!dict_locked) {
- dict_sys.unlock();
- }
- table = nullptr;
- if (!mdl_ticket);
- else if (MDL_context* mdl_context =
- static_cast<MDL_context*>(
- thd_mdl_context(
- node->trx->
- mysql_thd))) {
- mdl_context->release_lock(
- mdl_ticket);
- mdl_ticket = nullptr;
- }
- }
+ pfs_os_file_t d = OS_FILE_CLOSED;
- d = fil_delete_tablespace(space_id);
+ if (const uint32_t space_id = dict_drop_index_tree(
+ &node->pcur, node->trx, &mtr)) {
+ if (table) {
+ lock_release_on_rollback(node->trx,
+ table);
+ if (!dict_locked) {
+ dict_sys.lock(SRW_LOCK_CALL);
+ }
+ if (table->release()) {
+ dict_sys.remove(table);
+ } else if (table->space_id
+ == space_id) {
+ table->space = nullptr;
+ table->file_unreadable = true;
+ }
+ if (!dict_locked) {
+ dict_sys.unlock();
+ }
+ table = nullptr;
+ if (!mdl_ticket);
+ else if (MDL_context* mdl_context =
+ static_cast<MDL_context*>(
+ thd_mdl_context(
+ node->trx->
+ mysql_thd))) {
+ mdl_context->release_lock(
+ mdl_ticket);
+ mdl_ticket = nullptr;
+ }
}
- mtr.commit();
+ d = fil_delete_tablespace(space_id);
+ }
- if (d != OS_FILE_CLOSED) {
- os_file_close(d);
- }
+ mtr.commit();
- mtr.start();
- ut_a(node->pcur.restore_position(
- BTR_MODIFY_LEAF, &mtr) == btr_pcur_t::SAME_ALL);
+ if (d != OS_FILE_CLOSED) {
+ os_file_close(d);
}
+
+ mtr.start();
+ ut_a(node->pcur.restore_position(
+ BTR_MODIFY_LEAF, &mtr) == btr_pcur_t::SAME_ALL);
}
if (btr_cur_optimistic_delete(&node->pcur.btr_cur, 0, &mtr)) {
@@ -299,10 +275,6 @@ row_undo_ins_remove_sec_low(
mtr_sx_lock_index(index, &mtr);
}
- if (row_log_online_op_try(index, entry, 0)) {
- goto func_exit_no_pcur;
- }
-
if (dict_index_is_spatial(index)) {
if (modify_leaf) {
mode |= BTR_RTREE_DELETE_MARK;
@@ -346,7 +318,6 @@ row_undo_ins_remove_sec_low(
}
btr_pcur_close(&pcur);
-func_exit_no_pcur:
mtr_commit(&mtr);
return(err);
@@ -546,7 +517,7 @@ row_undo_ins_remove_sec_rec(
while (index != NULL) {
dtuple_t* entry;
- if (index->type & DICT_FTS) {
+ if (index->type & DICT_FTS || !index->is_committed()) {
dict_table_next_uncorrupted_index(index);
continue;
}
diff --git a/storage/innobase/row/row0umod.cc b/storage/innobase/row/row0umod.cc
index 494a23eaf0a..f18d7ab1be1 100644
--- a/storage/innobase/row/row0umod.cc
+++ b/storage/innobase/row/row0umod.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, 2021, MariaDB Corporation.
+Copyright (c) 2017, 2022, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -36,7 +36,6 @@ Created 2/27/1997 Heikki Tuuri
#include "ibuf0ibuf.h"
#include "row0undo.h"
#include "row0vers.h"
-#include "row0log.h"
#include "trx0trx.h"
#include "trx0rec.h"
#include "row0row.h"
@@ -80,11 +79,6 @@ row_undo_mod_clust_low(
mem_heap_t** offsets_heap,
/*!< in/out: memory heap that can be emptied */
mem_heap_t* heap, /*!< in/out: memory heap */
- const dtuple_t**rebuilt_old_pk,
- /*!< out: row_log_table_get_pk()
- before the update, or NULL if
- the table is not being rebuilt online or
- the PRIMARY KEY definition does not change */
byte* sys, /*!< out: DB_TRX_ID, DB_ROLL_PTR
for row_log_table_delete() */
que_thr_t* thr, /*!< in: query thread */
@@ -111,15 +105,6 @@ row_undo_mod_clust_low(
|| node->update->info_bits == REC_INFO_METADATA_ADD
|| node->update->info_bits == REC_INFO_METADATA_ALTER);
- if (mode != BTR_MODIFY_LEAF
- && dict_index_is_online_ddl(btr_cur_get_index(btr_cur))) {
- *rebuilt_old_pk = row_log_table_get_pk(
- btr_cur_get_rec(btr_cur),
- btr_cur_get_index(btr_cur), NULL, sys, &heap);
- } else {
- *rebuilt_old_pk = NULL;
- }
-
if (mode != BTR_MODIFY_TREE) {
ut_ad((mode & ulint(~BTR_ALREADY_S_LATCHED))
== BTR_MODIFY_LEAF);
@@ -269,7 +254,6 @@ row_undo_mod_clust(
bool have_latch = false;
dberr_t err;
dict_index_t* index;
- bool online;
ut_ad(thr_get_trx(thr) == node->trx);
ut_ad(node->trx->in_rollback);
@@ -287,26 +271,16 @@ row_undo_mod_clust(
ut_ad(lock_table_has_locks(index->table));
}
- online = dict_index_is_online_ddl(index);
- if (online) {
- ut_ad(!node->trx->dict_operation_lock_mode);
- mtr_s_lock_index(index, &mtr);
- }
-
mem_heap_t* heap = mem_heap_create(1024);
mem_heap_t* offsets_heap = NULL;
rec_offs* offsets = NULL;
- const dtuple_t* rebuilt_old_pk;
byte sys[DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN];
/* Try optimistic processing of the record, keeping changes within
the index page */
err = row_undo_mod_clust_low(node, &offsets, &offsets_heap,
- heap, &rebuilt_old_pk, sys,
- thr, &mtr, online
- ? BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED
- : BTR_MODIFY_LEAF);
+ heap, sys, thr, &mtr, BTR_MODIFY_LEAF);
if (err != DB_SUCCESS) {
btr_pcur_commit_specify_mtr(pcur, &mtr);
@@ -321,34 +295,12 @@ row_undo_mod_clust(
index->set_modified(mtr);
}
- err = row_undo_mod_clust_low(
- node, &offsets, &offsets_heap,
- heap, &rebuilt_old_pk, sys,
- thr, &mtr, BTR_MODIFY_TREE);
+ err = row_undo_mod_clust_low(node, &offsets, &offsets_heap,
+ heap, sys, thr, &mtr,
+ BTR_MODIFY_TREE);
ut_ad(err == DB_SUCCESS || err == DB_OUT_OF_FILE_SPACE);
}
- if (err == DB_SUCCESS && online && dict_index_is_online_ddl(index)) {
- switch (node->rec_type) {
- case TRX_UNDO_DEL_MARK_REC:
- row_log_table_insert(
- btr_pcur_get_rec(pcur), index, offsets);
- break;
- case TRX_UNDO_UPD_EXIST_REC:
- row_log_table_update(
- btr_pcur_get_rec(pcur), index, offsets,
- rebuilt_old_pk);
- break;
- case TRX_UNDO_UPD_DEL_REC:
- row_log_table_delete(
- btr_pcur_get_rec(pcur), index, offsets, sys);
- break;
- default:
- ut_ad(0);
- break;
- }
- }
-
/**
* when scrubbing, and records gets cleared,
* the transaction id is not present afterwards.
@@ -570,10 +522,6 @@ row_undo_mod_del_mark_or_remove_sec_low(
ut_ad(mode == (BTR_MODIFY_TREE | BTR_LATCH_FOR_DELETE));
mtr_sx_lock_index(index, &mtr);
}
-
- if (row_log_online_op_try(index, entry, 0)) {
- goto func_exit_no_pcur;
- }
} else {
/* For secondary indexes,
index->online_status==ONLINE_INDEX_COMPLETE if
@@ -669,7 +617,6 @@ row_undo_mod_del_mark_or_remove_sec_low(
func_exit:
btr_pcur_close(&pcur);
-func_exit_no_pcur:
mtr_commit(&mtr);
return(err);
@@ -753,28 +700,6 @@ row_undo_mod_del_unmark_sec_and_undo_update(
try_again:
row_mtr_start(&mtr, index, !(mode & BTR_MODIFY_LEAF));
- if (!index->is_committed()) {
- /* The index->online_status may change if the index is
- or was being created online, but not committed yet. It
- is protected by index->lock. */
- if (mode == BTR_MODIFY_LEAF) {
- mode = BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED;
- mtr_s_lock_index(index, &mtr);
- } else {
- ut_ad(mode == BTR_MODIFY_TREE);
- mtr_sx_lock_index(index, &mtr);
- }
-
- if (row_log_online_op_try(index, entry, trx->id)) {
- goto func_exit_no_pcur;
- }
- } else {
- /* For secondary indexes,
- index->online_status==ONLINE_INDEX_COMPLETE if
- index->is_committed(). */
- ut_ad(!dict_index_is_online_ddl(index));
- }
-
btr_cur->thr = thr;
search_result = row_search_index_entry(index, entry, mode,
@@ -802,34 +727,26 @@ try_again:
}
}
- if (index->is_committed()) {
- /* During online secondary index creation, it
- is possible that MySQL is waiting for a
- meta-data lock upgrade before invoking
- ha_innobase::commit_inplace_alter_table()
- while this ROLLBACK is executing. InnoDB has
- finished building the index, but it does not
- yet exist in MySQL. In this case, we suppress
- the printout to the error log. */
+ if (btr_cur->up_match >= dict_index_get_n_unique(index)
+ || btr_cur->low_match >= dict_index_get_n_unique(index)) {
ib::warn() << "Record in index " << index->name
<< " of table " << index->table->name
- << " was not found on rollback, trying to"
- " insert: " << *entry
+ << " was not found on rollback, and"
+ " a duplicate exists: "
+ << *entry
<< " at: " << rec_index_print(
btr_cur_get_rec(btr_cur), index);
- }
-
- if (btr_cur->up_match >= dict_index_get_n_unique(index)
- || btr_cur->low_match >= dict_index_get_n_unique(index)) {
- if (index->is_committed()) {
- ib::warn() << "Record in index " << index->name
- << " was not found on rollback, and"
- " a duplicate exists";
- }
err = DB_DUPLICATE_KEY;
break;
}
+ ib::warn() << "Record in index " << index->name
+ << " of table " << index->table->name
+ << " was not found on rollback, trying to insert: "
+ << *entry
+ << " at: " << rec_index_print(
+ btr_cur_get_rec(btr_cur), index);
+
/* Insert the missing record that we were trying to
delete-unmark. */
big_rec_t* big_rec;
@@ -912,7 +829,6 @@ try_again:
}
btr_pcur_close(&pcur);
-func_exit_no_pcur:
mtr_commit(&mtr);
return(err);
@@ -940,7 +856,7 @@ row_undo_mod_upd_del_sec(
dict_index_t* index = node->index;
dtuple_t* entry;
- if (index->type & DICT_FTS) {
+ if (index->type & DICT_FTS || !index->is_committed()) {
dict_table_next_uncorrupted_index(node->index);
continue;
}
@@ -1006,7 +922,7 @@ row_undo_mod_del_mark_sec(
dict_index_t* index = node->index;
dtuple_t* entry;
- if (index->type == DICT_FTS) {
+ if (index->type == DICT_FTS || !index->is_committed()) {
dict_table_next_uncorrupted_index(node->index);
continue;
}
@@ -1076,6 +992,12 @@ row_undo_mod_upd_exist_sec(
while (node->index != NULL) {
+
+ if (!node->index->is_committed()) {
+ dict_table_next_uncorrupted_index(node->index);
+ continue;
+ }
+
dict_index_t* index = node->index;
dtuple_t* entry;
diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc
index 4d364c19540..0dd0266ac45 100644
--- a/storage/innobase/row/row0upd.cc
+++ b/storage/innobase/row/row0upd.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2015, 2021, MariaDB Corporation.
+Copyright (c) 2015, 2022, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -1081,16 +1081,7 @@ row_upd_replace_vcol(
/* If there is no index on the column, do not bother for
value update */
if (!col->m_col.ord_part) {
- dict_index_t* clust_index
- = dict_table_get_first_index(table);
-
- /* Skip the column if there is no online alter
- table in progress or it is not being indexed
- in new table */
- if (!dict_index_is_online_ddl(clust_index)
- || !row_log_col_is_indexed(clust_index, col_no)) {
- continue;
- }
+ continue;
}
dfield = dtuple_get_nth_v_field(row, col_no);
@@ -1906,6 +1897,13 @@ row_upd_sec_index_entry(
ut_ad(trx->id != 0);
index = node->index;
+ if (!index->is_committed()) {
+ return DB_SUCCESS;
+ }
+
+ /* For secondary indexes, index->online_status==ONLINE_INDEX_COMPLETE
+ if index->is_committed(). */
+ ut_ad(!dict_index_is_online_ddl(index));
const bool referenced = row_upd_index_is_referenced(index, trx);
#ifdef WITH_WSREP
@@ -1929,75 +1927,22 @@ row_upd_sec_index_entry(
case SRV_TMP_SPACE_ID:
mtr.set_log_mode(MTR_LOG_NO_REDO);
flags = BTR_NO_LOCKING_FLAG;
+ mode = index->is_spatial()
+ ? ulint(BTR_MODIFY_LEAF | BTR_RTREE_DELETE_MARK)
+ : ulint(BTR_MODIFY_LEAF);
break;
default:
index->set_modified(mtr);
/* fall through */
case IBUF_SPACE_ID:
flags = index->table->no_rollback() ? BTR_NO_ROLLBACK : 0;
- break;
- }
-
- bool uncommitted = !index->is_committed();
-
- if (uncommitted) {
- /* The index->online_status may change if the index is
- or was being created online, but not committed yet. It
- is protected by index->lock. */
-
- mtr_s_lock_index(index, &mtr);
-
- switch (dict_index_get_online_status(index)) {
- case ONLINE_INDEX_COMPLETE:
- /* This is a normal index. Do not log anything.
- Perform the update on the index tree directly. */
- break;
- case ONLINE_INDEX_CREATION:
- /* Log a DELETE and optionally INSERT. */
- row_log_online_op(index, entry, 0);
-
- if (!node->is_delete) {
- mem_heap_empty(heap);
- entry = row_build_index_entry(
- node->upd_row, node->upd_ext,
- index, heap);
- ut_a(entry);
- row_log_online_op(index, entry, trx->id);
- }
- /* fall through */
- case ONLINE_INDEX_ABORTED:
- case ONLINE_INDEX_ABORTED_DROPPED:
- mtr_commit(&mtr);
- goto func_exit;
- }
-
/* We can only buffer delete-mark operations if there
- are no foreign key constraints referring to the index.
- Change buffering is disabled for temporary tables and
- spatial index. */
- mode = (referenced || index->table->is_temporary()
- || dict_index_is_spatial(index))
- ? BTR_MODIFY_LEAF_ALREADY_S_LATCHED
- : BTR_DELETE_MARK_LEAF_ALREADY_S_LATCHED;
- } else {
- /* For secondary indexes,
- index->online_status==ONLINE_INDEX_COMPLETE if
- index->is_committed(). */
- ut_ad(!dict_index_is_online_ddl(index));
-
- /* We can only buffer delete-mark operations if there
- are no foreign key constraints referring to the index.
- Change buffering is disabled for temporary tables and
- spatial index. */
- mode = (referenced || index->table->is_temporary()
- || dict_index_is_spatial(index))
- ? BTR_MODIFY_LEAF
- : BTR_DELETE_MARK_LEAF;
- }
-
- if (dict_index_is_spatial(index)) {
- ut_ad(mode & BTR_MODIFY_LEAF);
- mode |= BTR_RTREE_DELETE_MARK;
+ are no foreign key constraints referring to the index. */
+ mode = index->is_spatial()
+ ? ulint(BTR_MODIFY_LEAF | BTR_RTREE_DELETE_MARK)
+ : referenced
+ ? ulint(BTR_MODIFY_LEAF) : ulint(BTR_DELETE_MARK_LEAF);
+ break;
}
/* Set the query thread, so that ibuf_insert_low() will be
@@ -2020,19 +1965,6 @@ row_upd_sec_index_entry(
break;
case ROW_NOT_FOUND:
- if (!index->is_committed()) {
- /* When online CREATE INDEX copied the update
- that we already made to the clustered index,
- and completed the secondary index creation
- before we got here, the old secondary index
- record would not exist. The CREATE INDEX
- should be waiting for a MySQL meta-data lock
- upgrade at least until this UPDATE returns.
- After that point, set_committed(true) would be
- invoked by commit_inplace_alter_table(). */
- break;
- }
-
if (dict_index_is_spatial(index) && btr_cur->rtr_info->fd_del) {
/* We found the record, but a delete marked */
break;
@@ -2139,35 +2071,11 @@ row_upd_sec_index_entry(
DEBUG_SYNC_C_IF_THD(trx->mysql_thd,
"before_row_upd_sec_new_index_entry");
- uncommitted = !index->is_committed();
- if (uncommitted) {
- mtr.start();
- /* The index->online_status may change if the index is
- being rollbacked. It is protected by index->lock. */
-
- mtr_s_lock_index(index, &mtr);
-
- switch (dict_index_get_online_status(index)) {
- case ONLINE_INDEX_COMPLETE:
- case ONLINE_INDEX_CREATION:
- break;
- case ONLINE_INDEX_ABORTED:
- case ONLINE_INDEX_ABORTED_DROPPED:
- mtr_commit(&mtr);
- goto func_exit;
- }
-
- }
-
/* Build a new index entry */
entry = row_build_index_entry(node->upd_row, node->upd_ext,
index, heap);
ut_a(entry);
- if (uncommitted) {
- mtr_commit(&mtr);
- }
-
/* Insert new index entry */
err = row_ins_sec_index_entry(index, entry, thr, !node->is_delete);
@@ -2488,7 +2396,6 @@ row_upd_clust_rec(
btr_pcur_t* pcur;
btr_cur_t* btr_cur;
dberr_t err;
- const dtuple_t* rebuilt_old_pk = NULL;
ut_ad(dict_index_is_clust(index));
ut_ad(!thr_get_trx(thr)->in_rollback);
@@ -2502,11 +2409,6 @@ row_upd_clust_rec(
dict_table_is_comp(index->table)));
ut_ad(rec_offs_validate(btr_cur_get_rec(btr_cur), index, offsets));
- if (dict_index_is_online_ddl(index)) {
- rebuilt_old_pk = row_log_table_get_pk(
- btr_cur_get_rec(btr_cur), index, offsets, NULL, &heap);
- }
-
/* Try optimistic updating of the record, keeping changes within
the page; we do not check locks because we assume the x-lock on the
record to update */
@@ -2524,7 +2426,7 @@ row_upd_clust_rec(
}
if (err == DB_SUCCESS) {
- goto success;
+ goto func_exit;
}
if (buf_pool.running_out()) {
@@ -2577,15 +2479,6 @@ row_upd_clust_rec(
DEBUG_SYNC_C("after_row_upd_extern");
}
- if (err == DB_SUCCESS) {
-success:
- if (dict_index_is_online_ddl(index)) {
- row_log_table_update(
- btr_cur_get_rec(btr_cur),
- index, offsets, rebuilt_old_pk);
- }
- }
-
func_exit:
if (heap) {
mem_heap_free(heap);
@@ -2932,7 +2825,8 @@ row_upd(
break;
}
- if (node->index->type != DICT_FTS) {
+ if (node->index->type != DICT_FTS
+ && node->index->is_committed()) {
err = row_upd_sec_step(node, thr);
if (err != DB_SUCCESS) {
diff --git a/storage/innobase/trx/trx0rec.cc b/storage/innobase/trx/trx0rec.cc
index a78212d3359..4ba526bdc19 100644
--- a/storage/innobase/trx/trx0rec.cc
+++ b/storage/innobase/trx/trx0rec.cc
@@ -2037,6 +2037,10 @@ trx_undo_report_row_operation(
auto m = trx->mod_tables.emplace(index->table, trx->undo_no);
ut_ad(m.first->second.valid(trx->undo_no));
+ if (m.second && index->table->is_active_ddl()) {
+ trx->apply_online_log= true;
+ }
+
bool bulk = !rec;
if (!bulk) {
@@ -2226,12 +2230,14 @@ err_exit:
/** Copy an undo record to heap.
@param[in] roll_ptr roll pointer to a record that exists
-@param[in,out] heap memory heap where copied */
+@param[in,out] heap memory heap where copied
+@param[in] undo_rec_info undo log record information */
static
trx_undo_rec_t*
trx_undo_get_undo_rec_low(
- roll_ptr_t roll_ptr,
- mem_heap_t* heap)
+ roll_ptr_t roll_ptr,
+ mem_heap_t* heap,
+ const trx_undo_rec_info*undo_rec_info= nullptr)
{
trx_undo_rec_t* undo_rec;
ulint rseg_id;
@@ -2247,14 +2253,21 @@ trx_undo_get_undo_rec_low(
trx_rseg_t* rseg = &trx_sys.rseg_array[rseg_id];
ut_ad(rseg->is_persistent());
- mtr.start();
+ if (undo_rec_info
+ && undo_rec_info->block->page.id().page_no() == page_no) {
+ undo_rec = trx_undo_rec_copy(
+ undo_rec_info->block->page.frame + offset, heap);
+ } else {
+ mtr.start();
- buf_block_t* undo_page = trx_undo_page_get_s_latched(
- page_id_t(rseg->space->id, page_no), &mtr);
+ buf_block_t *undo_page = trx_undo_page_get_s_latched(
+ page_id_t(rseg->space->id, page_no), &mtr);
- undo_rec = trx_undo_rec_copy(undo_page->page.frame + offset, heap);
+ undo_rec = trx_undo_rec_copy(
+ undo_page->page.frame + offset, heap);
- mtr.commit();
+ mtr.commit();
+ }
return(undo_rec);
}
@@ -2278,13 +2291,15 @@ trx_undo_get_undo_rec(
mem_heap_t* heap,
trx_id_t trx_id,
const table_name_t& name,
- trx_undo_rec_t** undo_rec)
+ trx_undo_rec_t** undo_rec,
+ const trx_undo_rec_info*undo_rec_info= nullptr)
{
purge_sys.latch.rd_lock(SRW_LOCK_CALL);
bool missing_history = purge_sys.changes_visible(trx_id, name);
if (!missing_history) {
- *undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
+ *undo_rec = trx_undo_get_undo_rec_low(
+ roll_ptr, heap, undo_rec_info);
}
purge_sys.latch.rd_unlock();
@@ -2298,41 +2313,47 @@ trx_undo_get_undo_rec(
#define ATTRIB_USED_ONLY_IN_DEBUG MY_ATTRIBUTE((unused))
#endif /* UNIV_DEBUG */
-/*******************************************************************//**
-Build a previous version of a clustered index record. The caller must
-hold a latch on the index page of the clustered index record.
+/** Build a previous version of a clustered index record. The caller
+must hold a latch on the index page of the clustered index record.
+@param index_rec clustered index record in the index tree
+@param index_mtr mtr which contains the latch to index_rec page
+ and purge_view
+@param rec version of a clustered index record
+@param index clustered index
+@param offsets rec_get_offsets(rec, index)
+@param heap memory heap from which the memory needed is
+ allocated
+@param old_vers previous version or NULL if rec is the
+ first inserted version, or if history data
+ has been deleted (an error), or if the purge
+ could have removed the version
+ though it has not yet done so
+@param v_heap memory heap used to create vrow
+ dtuple if it is not yet created. This heap
+ diffs from "heap" above in that it could be
+ prebuilt->old_vers_heap for selection
+@param v_row virtual column info, if any
+@param v_status status determine if it is going into this
+ function by purge thread or not.
+ And if we read "after image" of undo log
+@param undo_rec_info undo record info or NULL
@retval true if previous version was built, or if it was an insert
or the table has been rebuilt
@retval false if the previous version is earlier than purge_view,
or being purged, which means that it may have been removed */
bool
trx_undo_prev_version_build(
-/*========================*/
- const rec_t* index_rec ATTRIB_USED_ONLY_IN_DEBUG,
- /*!< in: clustered index record in the
- index tree */
- mtr_t* index_mtr ATTRIB_USED_ONLY_IN_DEBUG,
- /*!< in: mtr which contains the latch to
- index_rec page and purge_view */
- const rec_t* rec, /*!< in: version of a clustered index record */
- dict_index_t* index, /*!< in: clustered index */
- rec_offs* offsets,/*!< in/out: rec_get_offsets(rec, index) */
- mem_heap_t* heap, /*!< in: memory heap from which the memory
- needed is allocated */
- rec_t** old_vers,/*!< out, own: previous version, or NULL if
- rec is the first inserted version, or if
- history data has been deleted (an error),
- or if the purge COULD have removed the version
- though it has not yet done so */
- mem_heap_t* v_heap, /* !< in: memory heap used to create vrow
- dtuple if it is not yet created. This heap
- diffs from "heap" above in that it could be
- prebuilt->old_vers_heap for selection */
- dtuple_t** vrow, /*!< out: virtual column info, if any */
- ulint v_status)
- /*!< in: status determine if it is going
- into this function by purge thread or not.
- And if we read "after image" of undo log */
+ const rec_t *index_rec ATTRIB_USED_ONLY_IN_DEBUG,
+ mtr_t *index_mtr ATTRIB_USED_ONLY_IN_DEBUG,
+ const rec_t *rec,
+ dict_index_t *index,
+ rec_offs *offsets,
+ mem_heap_t *heap,
+ rec_t **old_vers,
+ mem_heap_t *v_heap,
+ dtuple_t **vrow,
+ ulint v_status,
+ const trx_undo_rec_info *undo_rec_info)
{
trx_undo_rec_t* undo_rec = NULL;
dtuple_t* entry;
@@ -2371,7 +2392,7 @@ trx_undo_prev_version_build(
if (trx_undo_get_undo_rec(
roll_ptr, heap, rec_trx_id, index->table->name,
- &undo_rec)) {
+ &undo_rec, undo_rec_info)) {
if (v_status & TRX_UNDO_PREV_IN_PURGE) {
/* We are fetching the record being purged */
undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
@@ -2634,3 +2655,17 @@ trx_undo_read_v_cols(
ut_ad(ptr == end_ptr);
}
+
+bool trx_undo_rec_is_equal(roll_ptr_t roll_ptr,
+ const trx_undo_rec_info *undo_info)
+{
+ bool is_insert;
+ ulint rseg_id;
+ uint16_t offset;
+ uint32_t page_no;
+
+ trx_undo_decode_roll_ptr(roll_ptr, &is_insert, &rseg_id,
+ &page_no, &offset);
+ return page_no == undo_info->block->page.id().page_no()
+ && offset == undo_info->offset;
+}
diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc
index 7b070a605f4..ceaf7699028 100644
--- a/storage/innobase/trx/trx0trx.cc
+++ b/storage/innobase/trx/trx0trx.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2015, 2021, MariaDB Corporation.
+Copyright (c) 2015, 2022, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -133,6 +133,8 @@ trx_init(
trx->bulk_insert = false;
+ trx->apply_online_log = false;
+
ut_d(trx->start_file = 0);
ut_d(trx->start_line = 0);
@@ -1396,7 +1398,7 @@ void trx_t::commit_cleanup()
TRANSACTIONAL_TARGET void trx_t::commit_low(mtr_t *mtr)
{
ut_ad(!mtr || mtr->is_active());
- ut_d(bool aborted = in_rollback && error_state == DB_DEADLOCK);
+ ut_d(bool aborted= in_rollback && error_state == DB_DEADLOCK);
ut_ad(!mtr == (aborted || !has_logged()));
ut_ad(!mtr || !aborted);
@@ -1415,12 +1417,13 @@ TRANSACTIONAL_TARGET void trx_t::commit_low(mtr_t *mtr)
ut_ad(error == DB_DUPLICATE_KEY || error == DB_LOCK_WAIT_TIMEOUT);
}
-#ifndef DBUG_OFF
+#ifdef ENABLED_DEBUG_SYNC
const bool debug_sync= mysql_thd && has_logged_persistent();
#endif
if (mtr)
{
+ apply_log();
trx_write_serialisation_history(this, mtr);
/* The following call commits the mini-transaction, making the
@@ -1440,7 +1443,7 @@ TRANSACTIONAL_TARGET void trx_t::commit_low(mtr_t *mtr)
mtr->commit();
}
-#ifndef DBUG_OFF
+#ifdef ENABLED_DEBUG_SYNC
if (debug_sync)
DEBUG_SYNC_C("before_trx_state_committed_in_memory");
#endif
diff --git a/storage/innobase/trx/trx0undo.cc b/storage/innobase/trx/trx0undo.cc
index 86f2d467219..959086f9689 100644
--- a/storage/innobase/trx/trx0undo.cc
+++ b/storage/innobase/trx/trx0undo.cc
@@ -290,6 +290,149 @@ trx_undo_get_first_rec(const fil_space_t &space, uint32_t page_no,
mtr);
}
+typedef std::map<table_id_t, dict_table_t*> table_id_map;
+
+/** Apply the TRX_UNDO_INSERT_REC undo log record
+@param rec undo log record
+@param rec_info undo log record info
+@param table table to be logged
+@param heap memory heap */
+static void trx_undo_rec_apply_insert(trx_undo_rec_t *rec,
+ trx_undo_rec_info *rec_info,
+ dict_table_t *table,
+ mem_heap_t *heap)
+{
+ dict_index_t *index= dict_table_get_first_index(table);
+ const dtuple_t *undo_tuple;
+ rec= trx_undo_rec_get_row_ref(rec, index, &undo_tuple, heap);
+ rec_info->undo_rec= rec;
+ row_log_insert_handle(undo_tuple, rec_info, index, heap);
+}
+
+/** Apply the TRX_UNDO_UPD & TRX_UNDO_DEL undo log record
+@param rec undo log record
+@param undo_info undo log record info
+@param table table which does online ddl
+@param heap memory heap */
+static void trx_undo_rec_apply_update(trx_undo_rec_t *rec,
+ trx_undo_rec_info *rec_info,
+ dict_table_t *table,
+ mem_heap_t *heap)
+{
+ dict_index_t *index= dict_table_get_first_index(table);
+ const dtuple_t *undo_tuple;
+ trx_id_t trx_id;
+ roll_ptr_t roll_ptr;
+ byte info_bits;
+ rec= trx_undo_update_rec_get_sys_cols(rec, &trx_id, &roll_ptr,
+ &info_bits);
+ rec= trx_undo_rec_get_row_ref(rec, index, &undo_tuple, heap);
+
+ rec= trx_undo_update_rec_get_update(rec, index, rec_info->type,
+ trx_id, roll_ptr, info_bits,
+ heap, &rec_info->update);
+ rec_info->undo_rec= rec;
+ if (rec_info->type == TRX_UNDO_UPD_DEL_REC)
+ row_log_insert_handle(undo_tuple, rec_info, index, heap);
+ else
+ row_log_update_handle(undo_tuple, rec_info, index, heap);
+}
+
+/** Apply all DML undo log records to the online DDL tables
+@param rec undo log record
+@param online_log_tables table list which does online DDL
+@param rec_info undo log record info
+@param heap heap which used to create tuple
+ for the undo log record */
+static void trx_undo_rec_apply_log(trx_undo_rec_t *rec,
+ const table_id_map &online_log_tables,
+ trx_undo_rec_info *undo_info,
+ mem_heap_t *heap)
+{
+ ulint type, cmpl_info= 0;
+ bool updated_extern= false;
+ undo_no_t undo_no= 0;
+ table_id_t table_id= 0;
+ rec_t *ptr= trx_undo_rec_get_pars(rec, &type, &cmpl_info,
+ &updated_extern, &undo_no, &table_id);
+ auto it= online_log_tables.find(table_id);
+ if (it == online_log_tables.end())
+ return;
+
+
+ if (!it->second->is_active_ddl())
+ return;
+ undo_info->assign_value(type, cmpl_info, updated_extern,
+ undo_no);
+ switch(type)
+ {
+ case TRX_UNDO_INSERT_REC:
+ trx_undo_rec_apply_insert(ptr, undo_info, it->second, heap);
+ break;
+ case TRX_UNDO_UPD_EXIST_REC:
+ case TRX_UNDO_UPD_DEL_REC:
+ case TRX_UNDO_DEL_MARK_REC:
+ trx_undo_rec_apply_update(ptr, undo_info, it->second, heap);
+ break;
+ default:
+ ut_ad(0);
+ }
+
+ mem_heap_empty(heap);
+}
+
+
+/** Apply any changes to tables for which online DDL is in progress. */
+ATTRIBUTE_COLD void trx_t::apply_log() const
+{
+ if (undo_no == 0 || apply_online_log == false)
+ return;
+ const trx_undo_t *undo= rsegs.m_redo.undo;
+ if (!undo)
+ return;
+ table_id_map online_log_tables;
+ for (auto& t : mod_tables)
+ {
+ if (t.first->is_active_ddl())
+ online_log_tables[t.first->id]= t.first;
+ }
+ page_id_t page_id{rsegs.m_redo.rseg->space->id, undo->hdr_page_no};
+ page_id_t next_page_id(page_id);
+ mtr_t mtr;
+ mem_heap_t *heap= mem_heap_create(100);
+ mtr.start();
+ buf_block_t *block= buf_page_get(page_id, 0, RW_S_LATCH, &mtr);
+ ut_ad(block);
+
+ while (block)
+ {
+ trx_undo_rec_t *rec= trx_undo_page_get_first_rec(block, page_id.page_no(),
+ undo->hdr_offset);
+ while (rec)
+ {
+ trx_undo_rec_info undo_rec_info(id, block, page_offset(rec));
+ trx_undo_rec_apply_log(rec, online_log_tables, &undo_rec_info,
+ heap);
+ rec= trx_undo_page_get_next_rec(block, page_offset(rec),
+ page_id.page_no(),
+ undo->hdr_offset);
+ }
+
+ uint32_t next= mach_read_from_4(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE +
+ FLST_NEXT + FIL_ADDR_PAGE +
+ block->page.frame);
+ if (next == FIL_NULL)
+ break;
+ next_page_id.set_page_no(next);
+ mtr.commit();
+ mtr.start();
+ block= buf_page_get(next_page_id, 0, RW_S_LATCH, &mtr);
+ ut_ad(block);
+ }
+ mtr.commit();
+ mem_heap_free(heap);
+}
+
/*============== UNDO LOG FILE COPY CREATION AND FREEING ==================*/
/** Initialize an undo log page.