mirror of
https://github.com/MariaDB/server.git
synced 2025-08-09 22:24:09 +03:00
- InnoDB DDL results in `Duplicate entry' if concurrent DML throws duplicate key error. The following scenario explains the problem connection con1: ALTER TABLE t1 FORCE; connection con2: INSERT INTO t1(pk, uk) VALUES (2, 2), (3, 2); In connection con2, InnoDB throws the 'DUPLICATE KEY' error because of unique index. Alter operation will throw the error when applying the concurrent DML log. - Inserting the duplicate key for unique index logs the insert operation for online ALTER TABLE. When insertion fails, transaction does rollback and it leads to logging of delete operation for online ALTER TABLE. While applying the insert log entries, alter operation encounters 'DUPLICATE KEY' error. - To avoid the above fake duplicate scenario, InnoDB should not write any log for online ALTER TABLE before DML transaction commit. - User thread which does DML can apply the online log if InnoDB ran out of online log and index is marked as completed. Set online log error if apply phase encountered any error. It can also clear all other indexes log, marks the newly added indexes as corrupted. - Removed the old online code which was a part of DML operations commit_inplace_alter_table() : Does apply the online log for the last batch of secondary index log and does frees the log for the completed index. trx_t::apply_online_log: Set to true while writing the undo log if the modified table has active DDL trx_t::apply_log(): Apply the DML changes to online DDL tables dict_table_t::is_active_ddl(): Returns true if the table has an active DDL dict_index_t::online_log_make_dummy(): Assign dummy value for clustered index online log to indicate the secondary indexes are being rebuild. dict_index_t::online_log_is_dummy(): Check whether the online log has dummy value ha_innobase_inplace_ctx::log_failure(): Handle the apply log failure for online DDL transaction row_log_mark_other_online_index_abort(): Clear out all other online index log after encountering the error during row_log_apply() row_log_get_error(): Get the error happened during row_log_apply() row_log_online_op(): Does apply the online log if index is completed and ran out of memory. Returns false if apply log fails UndorecApplier: Introduced a class to maintain the undo log record, latched undo buffer page, parse the undo log record, maintain the undo record type, info bits and update vector UndorecApplier::get_old_rec(): Get the correct version of the clustered index record that was modified by the current undo log record UndorecApplier::clear_undo_rec(): Clear the undo log related information after applying the undo log record UndorecApplier::log_update(): Handle the update, delete undo log and apply it on online indexes UndorecApplier::log_insert(): Handle the insert undo log and apply it on online indexes UndorecApplier::is_same(): Check whether the given roll pointer is generated by the current undo log record information trx_t::rollback_low(): Set apply_online_log for the transaction after partially rollbacked transaction has any active DDL prepare_inplace_alter_table_dict(): After allocating the online log, InnoDB does create fulltext common tables. Fulltext index doesn't allow the index to be online. So removed the dead code of online log removal Thanks to Marko Mäkelä for providing the initial prototype and Matthias Leich for testing the issue patiently.
316 lines
8.3 KiB
Plaintext
316 lines
8.3 KiB
Plaintext
--source include/have_innodb.inc
|
|
--source include/have_debug_sync.inc
|
|
--source include/have_debug.inc
|
|
--source include/count_sessions.inc
|
|
|
|
set default_storage_engine=innodb;
|
|
CREATE TABLE `t` (
|
|
`a` VARCHAR(100),
|
|
`b` VARCHAR(100),
|
|
`c` VARCHAR(200) GENERATED ALWAYS AS (CONCAT(a,b)) VIRTUAL,
|
|
`h` VARCHAR(10) DEFAULT NULL,
|
|
`i` int
|
|
) ENGINE=InnoDB;
|
|
|
|
INSERT INTO t VALUES (REPEAT('g', 100), REPEAT('x', 10), DEFAULT, "kk", 1);
|
|
INSERT INTO t VALUES (REPEAT('a', 100), REPEAT('b', 100), DEFAULT, "mm", 2);
|
|
|
|
CREATE INDEX idx ON t(c(100));
|
|
|
|
SET session debug_dbug="+d,ib_alter_add_virtual_fail";
|
|
--error ER_WRONG_KEY_COLUMN
|
|
ALTER TABLE t ADD COLUMN x VARCHAR(200) GENERATED ALWAYS AS (a) VIRTUAL,
|
|
ALGORITHM = INPLACE;
|
|
--error ER_WRONG_KEY_COLUMN
|
|
ALTER TABLE t DROP COLUMN c, ALGORITHM = INPLACE;
|
|
SET session debug_dbug="";
|
|
DROP TABLE t;
|
|
|
|
#online test
|
|
CREATE TABLE t (a INT, b INT, c INT GENERATED ALWAYS AS(a+b), h VARCHAR(10));
|
|
|
|
INSERT INTO t VALUES (11, 3, DEFAULT, 'mm');
|
|
INSERT INTO t VALUES (18, 1, DEFAULT, 'mm');
|
|
INSERT INTO t VALUES (28, 1, DEFAULT, 'mm');
|
|
INSERT INTO t VALUES (null, null, DEFAULT, "mx");
|
|
|
|
SET DEBUG_SYNC = 'innodb_inplace_alter_table_enter SIGNAL start_create WAIT_FOR go_ahead';
|
|
--send CREATE INDEX idx ON t(c)
|
|
|
|
connect (con1,localhost,root,,);
|
|
|
|
SET DEBUG_SYNC = 'now WAIT_FOR start_create';
|
|
update t set a=0 where a = 11;
|
|
start transaction;
|
|
update t set a=1 where a = 0;
|
|
ROLLBACK;
|
|
SET DEBUG_SYNC = 'now SIGNAL go_ahead';
|
|
|
|
connection default;
|
|
reap;
|
|
|
|
SELECT c FROM t;
|
|
SHOW CREATE TABLE t;
|
|
SELECT * FROM t;
|
|
|
|
--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON
|
|
ALTER TABLE t FORCE, LOCK=NONE;
|
|
if (0) {# MDEV-14341 TODO: re-enable this
|
|
SET DEBUG_SYNC = 'innodb_inplace_alter_table_enter SIGNAL start_create WAIT_FOR go_ahead';
|
|
--send ALTER TABLE t FORCE
|
|
|
|
connection con1;
|
|
SET DEBUG_SYNC = 'now WAIT_FOR start_create';
|
|
start transaction;
|
|
update t set a=1 where a = 0;
|
|
rollback;
|
|
start transaction;
|
|
delete from t;
|
|
insert into t values(1,null,default,null);
|
|
rollback;
|
|
start transaction;
|
|
update t set b=b+1;
|
|
rollback;
|
|
SET DEBUG_SYNC = 'now SIGNAL go_ahead';
|
|
|
|
connection default;
|
|
reap;
|
|
|
|
check table t;
|
|
SELECT c FROM t;
|
|
|
|
SET DEBUG_SYNC = 'innodb_inplace_alter_table_enter SIGNAL start_create WAIT_FOR go_ahead';
|
|
--send ALTER TABLE t FORCE
|
|
|
|
connection con1;
|
|
|
|
SET DEBUG_SYNC = 'now WAIT_FOR start_create';
|
|
start transaction;
|
|
DELETE FROM t WHERE a = 0;
|
|
ROLLBACK;
|
|
DELETE FROM t WHERE a = 0;
|
|
SET DEBUG_SYNC = 'now SIGNAL go_ahead';
|
|
|
|
connection default;
|
|
reap;
|
|
|
|
SELECT c FROM t;
|
|
}
|
|
|
|
disconnect con1;
|
|
DROP TABLE t;
|
|
|
|
SET DEBUG_SYNC = 'RESET';
|
|
|
|
|
|
# Test add virtual column and add index at the same time
|
|
# introduce some error
|
|
|
|
CREATE TABLE t (a INT, b INT, c INT GENERATED ALWAYS AS(a+b), h VARCHAR(10));
|
|
|
|
INSERT INTO t VALUES (11, 3, DEFAULT, 'mm');
|
|
|
|
INSERT INTO t VALUES (18, 1, DEFAULT, 'mm');
|
|
|
|
INSERT INTO t VALUES (28, 1, DEFAULT, 'mm');
|
|
|
|
INSERT INTO t VALUES (null, null, DEFAULT, 'mm');
|
|
|
|
CREATE INDEX idx_1 on t(c);
|
|
|
|
SET @saved_dbug = @@SESSION.debug_dbug;
|
|
SET debug_dbug = '+d,create_index_fail';
|
|
|
|
--enable_info
|
|
--error ER_DUP_ENTRY
|
|
ALTER TABLE t ADD COLUMN x INT GENERATED ALWAYS AS(a+b), ADD INDEX idx (x),
|
|
ADD INDEX idcx (c,x);
|
|
|
|
UPDATE t SET a=a+1;
|
|
|
|
--error ER_DUP_ENTRY
|
|
ALTER TABLE t ADD INDEX idc(c);
|
|
SET debug_dbug = @saved_dbug;
|
|
--disable_info
|
|
|
|
UPDATE t SET b=b-1;
|
|
|
|
SHOW CREATE TABLE t;
|
|
|
|
SELECT c FROM t;
|
|
|
|
DROP TABLE t;
|
|
|
|
if (0) {# MDEV-14341 TODO: re-enable LOCK=NONE and these tests
|
|
--echo #
|
|
--echo # Bug#22018532 ASSERTION WHEN ONLINE REAPPLY REBUILD LOG ON
|
|
--echo # MULTIPLE INDEXED VIRTUAL COLUMNS
|
|
--echo #
|
|
|
|
create table t (
|
|
a int as (1) virtual,
|
|
b int,
|
|
c int as (1) virtual,
|
|
unique(b),
|
|
unique(c),
|
|
key(a)
|
|
) engine=innodb;
|
|
|
|
insert ignore into t values();
|
|
|
|
SET DEBUG_SYNC = 'innodb_inplace_alter_table_enter SIGNAL start_create WAIT_FOR go_ahead';
|
|
--send optimize table t
|
|
|
|
connect (con1,localhost,root,,);
|
|
|
|
SET DEBUG_SYNC = 'now WAIT_FOR start_create';
|
|
insert ignore into t values();
|
|
SET DEBUG_SYNC = 'now SIGNAL go_ahead';
|
|
|
|
connection default;
|
|
--echo /* connection default */ optimize table t;
|
|
reap;
|
|
SELECT c FROM t;
|
|
SHOW CREATE TABLE t;
|
|
SELECT * FROM t;
|
|
DROP TABLE t;
|
|
|
|
# Do another test without duplicate error
|
|
|
|
CREATE TABLE t (a INT, b INT, c INT GENERATED ALWAYS AS(a+b), h VARCHAR(10));
|
|
|
|
INSERT INTO t VALUES (11, 3, DEFAULT, 'mm');
|
|
INSERT INTO t VALUES (18, 1, DEFAULT, 'mm');
|
|
INSERT INTO t VALUES (28, 1, DEFAULT, 'mm');
|
|
INSERT INTO t VALUES (null, null, DEFAULT, 'mm');
|
|
|
|
CREATE INDEX idx ON t(c);
|
|
|
|
SET DEBUG_SYNC = 'innodb_inplace_alter_table_enter SIGNAL start_rebuild WAIT_FOR go_ahead';
|
|
--send optimize table t
|
|
|
|
connection con1;
|
|
SET DEBUG_SYNC = 'now WAIT_FOR start_rebuild';
|
|
INSERT INTO t VALUES (48, 2, DEFAULT, 'xx');
|
|
INSERT INTO t VALUES (68, 3, DEFAULT, 'sx');
|
|
SET DEBUG_SYNC = 'now SIGNAL go_ahead';
|
|
|
|
connection default;
|
|
--echo /* connection default */ optimize table t;
|
|
reap;
|
|
|
|
SELECT c FROM t;
|
|
|
|
disconnect con1;
|
|
|
|
DROP TABLE t;
|
|
|
|
--echo #
|
|
--echo # Bug#22951879 - ASSERTS RELATED TO ONLINE DDL AND GCOL
|
|
--echo #
|
|
|
|
# Create a table with 2 virtual column, one (vbidxcol) is indexed and
|
|
# the other one (vbcol) is not
|
|
create table ibstd_14 (a int not null, d int not null, b varchar(198) not null, c char(181), vadcol int as (a+length(d)) stored, vbcol char(2) as (substr(b,2,2)) virtual, vbidxcol char(3) as (substr(b,1,3)) virtual , index(d), index(a), index(vbidxcol), index(a,vbidxcol), index(vbidxcol,d), unique key (b(10), a, d), index(c(99), b(31)), index(b(5), c(10), a) , index(a,d)) engine=InnoDB stats_persistent=1 row_format=dynamic;
|
|
|
|
# Do an alter table rebuild table and also create a new index on this
|
|
# non-indexed virtual column
|
|
SET DEBUG_SYNC = 'innodb_inplace_alter_table_enter SIGNAL start_create WAIT_FOR go_ahead';
|
|
--send alter table ibstd_14 row_format=compressed key_block_size=4,add key kn3 (d,c,vbcol,b)
|
|
|
|
# Do a concurrent insert, and make sure this newly indexed virtual column
|
|
# is also logged
|
|
connect (con1,localhost,root);
|
|
SET DEBUG_SYNC = 'now WAIT_FOR start_create';
|
|
insert into ibstd_14 (a,d,b,c, vbidxcol, vbcol) values ('118','6',repeat('oacolaarlruoacuroauurloraarucoooarcooauoolacalllaulrruarrrucruuooclacuoouccarrcoocloccorrrrarourcooalloocooccouruolaorlcaocualolc','1'),repeat('lolrrlalcocroraaulauclaaucolcorcuooaolruaooooluooooouaoorlarucorullalcrrloccououaooaorluorraclrcooouuolocoaolcocaaculruoocucoocoooauuolarcoraraocaoolulolarru','1'),default,default);
|
|
|
|
insert into ibstd_14 (a,d,b,c, vbidxcol, vbcol) values ('118','6', 'aaaa', 'lll', default, default);
|
|
|
|
# Also do an concurrent update, make sure this is performed
|
|
update ibstd_14 set b='11111' where b='aaaa';
|
|
|
|
SET DEBUG_SYNC = 'now SIGNAL go_ahead';
|
|
|
|
connection default;
|
|
reap;
|
|
|
|
select * from ibstd_14;
|
|
|
|
# This will use the newly added "kn3" index, to check materialized vbcol
|
|
# after log reapply
|
|
select d,c,vbcol,b from ibstd_14;
|
|
|
|
# check the value is inserted into the index
|
|
select vbcol from ibstd_14;
|
|
|
|
drop table ibstd_14;
|
|
|
|
--echo #
|
|
--echo # Bug#22018745 CORRUPTION IN ONLINE TABLE REBUILD
|
|
--echo # (ROW_FORMAT=REDUNDANT, INDEXED VIRTUAL COLUMN)
|
|
--echo #
|
|
|
|
CREATE TABLE t (
|
|
b char(5) PRIMARY KEY,
|
|
v char(3) GENERATED ALWAYS AS (substr(b,1,3)) VIRTUAL, KEY(v)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=REDUNDANT;
|
|
|
|
connection con1;
|
|
SET DEBUG_SYNC='row_log_table_apply1_before SIGNAL prepared WAIT_FOR apply';
|
|
--send OPTIMIZE TABLE t
|
|
connection default;
|
|
|
|
SET DEBUG_SYNC='now WAIT_FOR prepared';
|
|
INSERT INTO t SET b='fubar';
|
|
BEGIN;
|
|
DELETE FROM t;
|
|
ROLLBACK;
|
|
SET DEBUG_SYNC='now SIGNAL apply';
|
|
|
|
connection con1;
|
|
reap;
|
|
|
|
connection default;
|
|
CHECK TABLE t;
|
|
SELECT * FROM t;
|
|
DROP TABLE t;
|
|
|
|
disconnect con1;
|
|
}
|
|
SET DEBUG_SYNC = 'RESET';
|
|
|
|
|
|
--echo #
|
|
--echo # Bug#28825718 - ASSERTION FAILURE: TRX0REC.CC:NNN:N_IDX > 0 WHILE DOING REPLACE/INSERT
|
|
--echo #
|
|
|
|
CREATE TABLE t1(a INT PRIMARY KEY, b INT, c INT GENERATED ALWAYS AS(b+1) VIRTUAL) ENGINE=InnoDB;
|
|
|
|
INSERT INTO t1(a, b) VALUES(1, 1);
|
|
|
|
connect (con1,localhost,root,,);
|
|
SET DEBUG_SYNC = 'row_log_apply_after SIGNAL s1 WAIT_FOR s2';
|
|
SET lock_wait_timeout = 1;
|
|
--send ALTER TABLE t1 ADD UNIQUE INDEX(c, b)
|
|
|
|
connection default;
|
|
SET DEBUG_SYNC = 'now WAIT_FOR s1';
|
|
SET DEBUG_SYNC = 'row_log_insert_handle SIGNAL s2 WAIT_FOR s3';
|
|
--send INSERT INTO t1(a, b) VALUES(2, 2)
|
|
|
|
connection con1;
|
|
--error ER_LOCK_WAIT_TIMEOUT
|
|
reap;
|
|
SET DEBUG_SYNC = 'now SIGNAL s3';
|
|
disconnect con1;
|
|
connection default;
|
|
reap;
|
|
SET DEBUG_SYNC = 'RESET';
|
|
ALTER TABLE t1 ADD KEY(b);
|
|
INSERT INTO t1(a, b) VALUES(3, 3);
|
|
SELECT * FROM t1;
|
|
CHECK TABLE t1;
|
|
DROP TABLE t1;
|
|
|
|
--source include/wait_until_count_sessions.inc
|