summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/my_base.h6
-rw-r--r--mysql-test/main/commit.result5
-rw-r--r--mysql-test/main/commit.test5
-rw-r--r--mysql-test/main/flush_read_lock.result1
-rw-r--r--mysql-test/main/flush_read_lock.test3
-rw-r--r--mysql-test/main/innodb_mysql_lock.result2
-rw-r--r--mysql-test/main/innodb_mysql_lock.test5
-rw-r--r--mysql-test/main/xa_prepared_binlog_off.result11
-rw-r--r--mysql-test/suite/binlog/r/binlog_mdev342.result7
-rw-r--r--mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result14
-rw-r--r--mysql-test/suite/binlog/t/binlog_mdev342.test4
-rw-r--r--mysql-test/suite/binlog/t/binlog_xa_prepared.inc4
-rw-r--r--mysql-test/suite/binlog_encryption/rpl_parallel_gco_wait_kill.result6
-rw-r--r--mysql-test/suite/binlog_encryption/rpl_parallel_ignored_errors.result1
-rw-r--r--mysql-test/suite/binlog_encryption/rpl_parallel_slave_bgc_kill.result10
-rw-r--r--mysql-test/suite/binlog_encryption/rpl_parallel_stop_slave.result6
-rw-r--r--mysql-test/suite/innodb/r/alter_candidate_key.result1
-rw-r--r--mysql-test/suite/innodb/r/binlog_consistent.result6
-rw-r--r--mysql-test/suite/innodb/r/ddl_purge.result1
-rw-r--r--mysql-test/suite/innodb/r/group_commit.result6
-rw-r--r--mysql-test/suite/innodb/r/group_commit_no_optimize_thread.result6
-rw-r--r--mysql-test/suite/innodb/r/innodb-lock.result2
-rw-r--r--mysql-test/suite/innodb/r/innodb-table-online.result53
-rw-r--r--mysql-test/suite/innodb/r/lock_insert_into_empty.result24
-rw-r--r--mysql-test/suite/innodb/t/alter_candidate_key.test3
-rw-r--r--mysql-test/suite/innodb/t/autoinc_persist.test11
-rw-r--r--mysql-test/suite/innodb/t/binlog_consistent.test11
-rw-r--r--mysql-test/suite/innodb/t/ddl_purge.test4
-rw-r--r--mysql-test/suite/innodb/t/group_commit.test3
-rw-r--r--mysql-test/suite/innodb/t/group_commit_no_optimize_thread.test3
-rw-r--r--mysql-test/suite/innodb/t/innodb-lock.test4
-rw-r--r--mysql-test/suite/innodb/t/innodb-table-online.test58
-rw-r--r--mysql-test/suite/innodb/t/innodb_trx_weight.test2
-rw-r--r--mysql-test/suite/innodb/t/lock_insert_into_empty.test26
-rw-r--r--mysql-test/suite/innodb_fts/r/innodb_fts_plugin.result20
-rw-r--r--mysql-test/suite/innodb_gis/r/rtree_concurrent_srch.result3
-rw-r--r--mysql-test/suite/innodb_gis/t/rtree_concurrent_srch.test1
-rw-r--r--mysql-test/suite/mariabackup/incremental_backup.result3
-rw-r--r--mysql-test/suite/mariabackup/incremental_backup.test3
-rw-r--r--mysql-test/suite/parts/inc/partition_auto_increment.inc13
-rw-r--r--mysql-test/suite/parts/r/partition_auto_increment_innodb.result87
-rw-r--r--mysql-test/suite/rpl/include/rpl_parallel_gco_wait_kill.inc4
-rw-r--r--mysql-test/suite/rpl/include/rpl_parallel_ignored_errors.inc3
-rw-r--r--mysql-test/suite/rpl/include/rpl_parallel_slave_bgc_kill.inc4
-rw-r--r--mysql-test/suite/rpl/include/rpl_parallel_stop_slave.inc4
-rw-r--r--mysql-test/suite/rpl/r/parallel_backup.result1
-rw-r--r--mysql-test/suite/rpl/r/rpl_circular_for_4_hosts.result1
-rw-r--r--mysql-test/suite/rpl/r/rpl_mariadb_slave_capability.result2
-rw-r--r--mysql-test/suite/rpl/r/rpl_parallel_gco_wait_kill.result6
-rw-r--r--mysql-test/suite/rpl/r/rpl_parallel_ignored_errors.result1
-rw-r--r--mysql-test/suite/rpl/r/rpl_parallel_no_log_slave_updates.result3
-rw-r--r--mysql-test/suite/rpl/r/rpl_parallel_retry.result2
-rw-r--r--mysql-test/suite/rpl/r/rpl_parallel_slave_bgc_kill.result10
-rw-r--r--mysql-test/suite/rpl/r/rpl_parallel_stop_slave.result6
-rw-r--r--mysql-test/suite/rpl/r/rpl_semisync_ali_issues.result4
-rw-r--r--mysql-test/suite/rpl/r/rpl_xa_survive_disconnect.result5
-rw-r--r--mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_lsu_off.result5
-rw-r--r--mysql-test/suite/rpl/t/parallel_backup.test3
-rw-r--r--mysql-test/suite/rpl/t/rpl_circular_for_4_hosts.test3
-rw-r--r--mysql-test/suite/rpl/t/rpl_mariadb_slave_capability.test3
-rw-r--r--mysql-test/suite/rpl/t/rpl_parallel_no_log_slave_updates.test4
-rw-r--r--mysql-test/suite/rpl/t/rpl_parallel_retry.test7
-rw-r--r--mysql-test/suite/rpl/t/rpl_semisync_ali_issues.test4
-rw-r--r--mysql-test/suite/rpl/t/rpl_xa_survive_disconnect.test4
-rw-r--r--mysql-test/suite/sys_vars/r/identity_func.result68
-rw-r--r--mysql-test/suite/sys_vars/t/identity_func.test5
-rw-r--r--mysql-test/suite/versioning/r/trx_id.result7
-rw-r--r--mysql-test/suite/versioning/t/trx_id.test4
-rw-r--r--sql/ha_partition.cc3
-rw-r--r--sql/sql_class.cc8
-rw-r--r--sql/sql_insert.cc9
-rw-r--r--sql/sql_table.cc5
-rw-r--r--storage/innobase/btr/btr0btr.cc95
-rw-r--r--storage/innobase/btr/btr0cur.cc90
-rw-r--r--storage/innobase/handler/ha_innodb.cc28
-rw-r--r--storage/innobase/include/dict0mem.h16
-rw-r--r--storage/innobase/include/lock0lock.h44
-rw-r--r--storage/innobase/include/lock0prdt.h4
-rw-r--r--storage/innobase/include/read0types.h19
-rw-r--r--storage/innobase/include/row0ins.h5
-rw-r--r--storage/innobase/include/row0log.h6
-rw-r--r--storage/innobase/include/trx0rec.h9
-rw-r--r--storage/innobase/include/trx0trx.h132
-rw-r--r--storage/innobase/lock/lock0lock.cc138
-rw-r--r--storage/innobase/lock/lock0prdt.cc12
-rw-r--r--storage/innobase/row/row0ins.cc67
-rw-r--r--storage/innobase/row/row0log.cc125
-rw-r--r--storage/innobase/row/row0merge.cc37
-rw-r--r--storage/innobase/row/row0mysql.cc10
-rw-r--r--storage/innobase/row/row0purge.cc8
-rw-r--r--storage/innobase/row/row0sel.cc197
-rw-r--r--storage/innobase/row/row0uins.cc17
-rw-r--r--storage/innobase/row/row0undo.cc9
-rw-r--r--storage/innobase/trx/trx0rec.cc105
-rw-r--r--storage/innobase/trx/trx0roll.cc8
-rw-r--r--storage/innobase/trx/trx0trx.cc44
-rw-r--r--storage/mroonga/ha_mroonga.cpp3
97 files changed, 1281 insertions, 589 deletions
diff --git a/include/my_base.h b/include/my_base.h
index dc5b135628f..0fff63ca1f8 100644
--- a/include/my_base.h
+++ b/include/my_base.h
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 1995, 2018, MariaDB Corporation.
+ Copyright (c) 1995, 2021, 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
@@ -219,7 +219,9 @@ enum ha_extra_function {
/** Finish writing rows during ALTER TABLE...ALGORITHM=COPY. */
HA_EXTRA_END_ALTER_COPY,
/** Fake the start of a statement after wsrep_load_data_splitting hack */
- HA_EXTRA_FAKE_START_STMT
+ HA_EXTRA_FAKE_START_STMT,
+ /** IGNORE is being used for the insert statement */
+ HA_EXTRA_IGNORE_INSERT
};
/* Compatible option, to be deleted in 6.0 */
diff --git a/mysql-test/main/commit.result b/mysql-test/main/commit.result
index f2e012d4782..44d46900d38 100644
--- a/mysql-test/main/commit.result
+++ b/mysql-test/main/commit.result
@@ -247,11 +247,13 @@ COMMIT;
SET @@completion_type=1;
COMMIT AND NO CHAIN;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-START TRANSACTION;
TRUNCATE TABLE t1;
+INSERT INTO t1 VALUES(100);
+START TRANSACTION;
INSERT INTO t1 VALUES (1000);
SELECT * FROM t1;
s1
+100
1000
Should read '1000'
connection con1;
@@ -260,6 +262,7 @@ COMMIT;
connection default;
SELECT * FROM t1;
s1
+100
1000
Should only read the '1000' as this transaction is now in REP READ
COMMIT AND NO CHAIN;
diff --git a/mysql-test/main/commit.test b/mysql-test/main/commit.test
index 762397dfa23..9f5e077fd2d 100644
--- a/mysql-test/main/commit.test
+++ b/mysql-test/main/commit.test
@@ -284,8 +284,11 @@ COMMIT;
SET @@completion_type=1;
COMMIT AND NO CHAIN;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-START TRANSACTION;
TRUNCATE TABLE t1;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t1 VALUES(100);
+START TRANSACTION;
INSERT INTO t1 VALUES (1000);
SELECT * FROM t1;
--echo Should read '1000'
diff --git a/mysql-test/main/flush_read_lock.result b/mysql-test/main/flush_read_lock.result
index 9a81cae1724..ae57788c7d4 100644
--- a/mysql-test/main/flush_read_lock.result
+++ b/mysql-test/main/flush_read_lock.result
@@ -1372,6 +1372,7 @@ unlock tables;
connection default;
# Reap XA COMMIT.
delete from t3_trans;
+INSERT INTO t3_trans VALUES(100);
#
# Check that XA COMMIT / ROLLBACK for prepared transaction from a
# disconnected session is blocked by active FTWRL in another connection.
diff --git a/mysql-test/main/flush_read_lock.test b/mysql-test/main/flush_read_lock.test
index 205b8b302ea..40f8b2aec3b 100644
--- a/mysql-test/main/flush_read_lock.test
+++ b/mysql-test/main/flush_read_lock.test
@@ -1679,6 +1679,9 @@ connection default;
--echo # Reap XA COMMIT.
--reap
delete from t3_trans;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t3_trans VALUES(100);
--echo #
--echo # Check that XA COMMIT / ROLLBACK for prepared transaction from a
--echo # disconnected session is blocked by active FTWRL in another connection.
diff --git a/mysql-test/main/innodb_mysql_lock.result b/mysql-test/main/innodb_mysql_lock.result
index 25cf2882cd0..6ca332018ca 100644
--- a/mysql-test/main/innodb_mysql_lock.result
+++ b/mysql-test/main/innodb_mysql_lock.result
@@ -12,6 +12,8 @@ connect con3,localhost,root,,;
connection con1;
set @@autocommit=0;
CREATE TABLE t1(s1 INT UNIQUE) ENGINE=innodb;
+INSERT INTO t1 VALUES (100);
+COMMIT;
INSERT INTO t1 VALUES (1);
connection con2;
set @@autocommit=0;
diff --git a/mysql-test/main/innodb_mysql_lock.test b/mysql-test/main/innodb_mysql_lock.test
index 6e746468dba..5ee688b1d6e 100644
--- a/mysql-test/main/innodb_mysql_lock.test
+++ b/mysql-test/main/innodb_mysql_lock.test
@@ -25,6 +25,11 @@ connect (con3,localhost,root,,);
connection con1;
set @@autocommit=0;
CREATE TABLE t1(s1 INT UNIQUE) ENGINE=innodb;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent DML won't happen on the table
+INSERT INTO t1 VALUES (100);
+COMMIT;
+
INSERT INTO t1 VALUES (1);
connection con2;
diff --git a/mysql-test/main/xa_prepared_binlog_off.result b/mysql-test/main/xa_prepared_binlog_off.result
index ca19f6cdfaf..ebcb24d3af3 100644
--- a/mysql-test/main/xa_prepared_binlog_off.result
+++ b/mysql-test/main/xa_prepared_binlog_off.result
@@ -3,6 +3,7 @@ connection default;
CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
call mtr.add_suppression("Found 10 prepared XA transactions");
CREATE TABLE t (a INT) ENGINE=innodb;
+INSERT INTO t VALUES(100);
connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
SET @@sql_log_bin = OFF;
CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
@@ -48,18 +49,21 @@ connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
XA START 'trx1ro';
SELECT * from t ORDER BY a;
a
+100
XA END 'trx1ro';
XA PREPARE 'trx1ro';
connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
XA START 'trx2ro';
SELECT * from t ORDER BY a;
a
+100
XA END 'trx2ro';
XA PREPARE 'trx2ro';
connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
XA START 'trx3ro';
SELECT * from t ORDER BY a;
a
+100
XA END 'trx3ro';
XA PREPARE 'trx3ro';
connection default;
@@ -402,6 +406,7 @@ ERROR XAE08: XAER_DUPID: The XID already exists
XA ROLLBACK 'trx_19';
SELECT * FROM t;
a
+100
5
6
7
@@ -533,6 +538,7 @@ a
12
13
14
+100
XA END 'trx1ro';
XA PREPARE 'trx1ro';
connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
@@ -559,6 +565,7 @@ a
12
13
14
+100
XA END 'trx2ro';
XA PREPARE 'trx2ro';
connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
@@ -585,6 +592,7 @@ a
12
13
14
+100
XA END 'trx3ro';
XA PREPARE 'trx3ro';
connection default;
@@ -927,6 +935,7 @@ ERROR XAE08: XAER_DUPID: The XID already exists
XA ROLLBACK 'trx_19';
SELECT * FROM t;
a
+100
5
6
7
@@ -1036,7 +1045,7 @@ XA END 'one_phase_trx_4';
XA COMMIT 'one_phase_trx_4' ONE PHASE;
SELECT SUM(a) FROM t;
SUM(a)
-290
+390
DROP TABLE t;
DROP VIEW v_processlist;
All transactions must be completed, to empty-list the following:
diff --git a/mysql-test/suite/binlog/r/binlog_mdev342.result b/mysql-test/suite/binlog/r/binlog_mdev342.result
index 7513e855497..6b3ba9d187a 100644
--- a/mysql-test/suite/binlog/r/binlog_mdev342.result
+++ b/mysql-test/suite/binlog/r/binlog_mdev342.result
@@ -2,6 +2,7 @@ SET GLOBAL max_binlog_size= 4096;
SET GLOBAL innodb_flush_log_at_trx_commit= 1;
RESET MASTER;
CREATE TABLE t1 (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=Innodb;
+INSERT INTO t1 VALUES(100, "MDEV-515");
connect con1,localhost,root,,;
SET DEBUG_SYNC= "binlog_open_before_update_index SIGNAL con1_ready WAIT_FOR con1_cont";
SET SESSION debug_dbug="+d,crash_create_critical_before_update_index";
@@ -20,6 +21,7 @@ connection default;
SELECT a FROM t1 ORDER BY a;
a
1
+100
show binary logs;
Log_name File_size
master-bin.000001 #
@@ -32,6 +34,11 @@ master-bin.000001 # Binlog_checkpoint # # master-bin.000001
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=Innodb
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Annotate_rows # # INSERT INTO t1 VALUES(100, "MDEV-515")
+master-bin.000001 # Table_map # # table_id: # (test.t1)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Xid # # COMMIT /* XID */
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Annotate_rows # # INSERT INTO t1 VALUES (1, REPEAT("x", 4100))
master-bin.000001 # Table_map # # table_id: # (test.t1)
master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
diff --git a/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result b/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result
index 9fda8ab3143..10a0fc39266 100644
--- a/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result
+++ b/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result
@@ -3,6 +3,7 @@ RESET MASTER;
CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
call mtr.add_suppression("Found 10 prepared XA transactions");
CREATE TABLE t (a INT) ENGINE=innodb;
+INSERT INTO t VALUES(100);
connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
SET @@sql_log_bin = OFF;
CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
@@ -48,18 +49,21 @@ connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
XA START 'trx1ro';
SELECT * from t ORDER BY a;
a
+100
XA END 'trx1ro';
XA PREPARE 'trx1ro';
connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
XA START 'trx2ro';
SELECT * from t ORDER BY a;
a
+100
XA END 'trx2ro';
XA PREPARE 'trx2ro';
connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
XA START 'trx3ro';
SELECT * from t ORDER BY a;
a
+100
XA END 'trx3ro';
XA PREPARE 'trx3ro';
connection default;
@@ -402,6 +406,7 @@ ERROR XAE08: XAER_DUPID: The XID already exists
XA ROLLBACK 'trx_19';
SELECT * FROM t;
a
+100
5
6
7
@@ -533,6 +538,7 @@ a
12
13
14
+100
XA END 'trx1ro';
XA PREPARE 'trx1ro';
connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
@@ -559,6 +565,7 @@ a
12
13
14
+100
XA END 'trx2ro';
XA PREPARE 'trx2ro';
connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
@@ -585,6 +592,7 @@ a
12
13
14
+100
XA END 'trx3ro';
XA PREPARE 'trx3ro';
connection default;
@@ -927,6 +935,7 @@ ERROR XAE08: XAER_DUPID: The XID already exists
XA ROLLBACK 'trx_19';
SELECT * FROM t;
a
+100
5
6
7
@@ -1036,7 +1045,7 @@ XA END 'one_phase_trx_4';
XA COMMIT 'one_phase_trx_4' ONE PHASE;
SELECT SUM(a) FROM t;
SUM(a)
-290
+390
DROP TABLE t;
DROP VIEW v_processlist;
include/show_binlog_events.inc
@@ -1048,6 +1057,9 @@ master-bin.000001 # Query # # use `mtr`; INSERT INTO test_suppressions (pattern)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE t (a INT) ENGINE=innodb
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t VALUES(100)
+master-bin.000001 # Xid # # COMMIT /* XID */
master-bin.000001 # Gtid # # XA START X'7472785f30',X'',1 GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=0
master-bin.000001 # Query # # XA END X'7472785f30',X'',1
diff --git a/mysql-test/suite/binlog/t/binlog_mdev342.test b/mysql-test/suite/binlog/t/binlog_mdev342.test
index 024fa884f91..6d48ea129ee 100644
--- a/mysql-test/suite/binlog/t/binlog_mdev342.test
+++ b/mysql-test/suite/binlog/t/binlog_mdev342.test
@@ -11,7 +11,9 @@ SET GLOBAL innodb_flush_log_at_trx_commit= 1;
RESET MASTER;
CREATE TABLE t1 (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=Innodb;
-
+# MDEV-515 takes X-lock on the table for the first insert
+# In that case, Concurrent DML will get blocked
+INSERT INTO t1 VALUES(100, "MDEV-515");
# One connection does an insert that causes a binlog rotate.
# The rotate is paused after writing new file but before updating index.
connect(con1,localhost,root,,);
diff --git a/mysql-test/suite/binlog/t/binlog_xa_prepared.inc b/mysql-test/suite/binlog/t/binlog_xa_prepared.inc
index b6306791cf4..e93832dbf08 100644
--- a/mysql-test/suite/binlog/t/binlog_xa_prepared.inc
+++ b/mysql-test/suite/binlog/t/binlog_xa_prepared.inc
@@ -54,6 +54,10 @@ CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where typ
CREATE TABLE t (a INT) ENGINE=innodb;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t VALUES(100);
+
# Counter is incremented at the end of post restart to
# reflect number of loops done in correctness computation.
--let $restart_number = 0
diff --git a/mysql-test/suite/binlog_encryption/rpl_parallel_gco_wait_kill.result b/mysql-test/suite/binlog_encryption/rpl_parallel_gco_wait_kill.result
index 4472550c4f2..f12d19442f1 100644
--- a/mysql-test/suite/binlog_encryption/rpl_parallel_gco_wait_kill.result
+++ b/mysql-test/suite/binlog_encryption/rpl_parallel_gco_wait_kill.result
@@ -20,6 +20,7 @@ connect con_temp4,127.0.0.1,root,,test,$SERVER_MYPORT_1,;
connect con_temp5,127.0.0.1,root,,test,$SERVER_MYPORT_1,;
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
+INSERT INTO t3 VALUES(100, 100);
connection server_2;
connection server_1;
SET sql_log_bin=0;
@@ -136,6 +137,7 @@ a b
68 68
69 69
70 70
+100 100
SET debug_sync='RESET';
connection server_2;
SET debug_sync='now SIGNAL d0_cont';
@@ -161,6 +163,7 @@ a b
68 68
69 69
70 70
+100 100
SET debug_sync='RESET';
SET GLOBAL slave_parallel_threads=0;
SET GLOBAL slave_parallel_threads=10;
@@ -190,6 +193,7 @@ a b
68 68
69 69
70 70
+100 100
SET sql_log_bin=0;
DROP FUNCTION foo;
CREATE FUNCTION foo(x INT, d1 VARCHAR(500), d2 VARCHAR(500))
@@ -225,6 +229,7 @@ SELECT * FROM t3 WHERE a >= 80 ORDER BY a;
a b
80 0
81 10000
+100 100
connection server_2;
SET debug_sync='now WAIT_FOR wait_queue_ready';
KILL THD_ID;
@@ -244,6 +249,7 @@ a b
80 0
81 10000
82 0
+100 100
connection server_2;
include/stop_slave.inc
SET GLOBAL slave_parallel_mode=@old_parallel_mode;
diff --git a/mysql-test/suite/binlog_encryption/rpl_parallel_ignored_errors.result b/mysql-test/suite/binlog_encryption/rpl_parallel_ignored_errors.result
index 3dd5a3ea83c..ce11b814d44 100644
--- a/mysql-test/suite/binlog_encryption/rpl_parallel_ignored_errors.result
+++ b/mysql-test/suite/binlog_encryption/rpl_parallel_ignored_errors.result
@@ -13,6 +13,7 @@ include/start_slave.inc
connection server_1;
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t1 (a int PRIMARY KEY) ENGINE=InnoDB;
+INSERT INTO t1 VALUES(1);
include/save_master_gtid.inc
connection server_2;
include/sync_with_master_gtid.inc
diff --git a/mysql-test/suite/binlog_encryption/rpl_parallel_slave_bgc_kill.result b/mysql-test/suite/binlog_encryption/rpl_parallel_slave_bgc_kill.result
index 320bf0e49f8..ba131ea094f 100644
--- a/mysql-test/suite/binlog_encryption/rpl_parallel_slave_bgc_kill.result
+++ b/mysql-test/suite/binlog_encryption/rpl_parallel_slave_bgc_kill.result
@@ -17,6 +17,8 @@ ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t1 (a int PRIMARY KEY) ENGINE=MyISAM;
CREATE TABLE t2 (a int PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
+INSERT INTO t2 VALUES(100);
+INSERT INTO t3 VALUES(100, 100);
connection server_2;
connection server_1;
SET sql_log_bin=0;
@@ -80,6 +82,7 @@ a b
32 32
33 33
34 34
+100 100
SET debug_sync='RESET';
connection server_2;
SET sql_log_bin=0;
@@ -98,6 +101,7 @@ STOP SLAVE IO_THREAD;
SELECT * FROM t3 WHERE a >= 30 ORDER BY a;
a b
31 31
+100 100
SET debug_sync='RESET';
SET GLOBAL slave_parallel_threads=0;
SET GLOBAL slave_parallel_threads=10;
@@ -121,6 +125,7 @@ a b
33 33
34 34
39 0
+100 100
SET sql_log_bin=0;
DROP FUNCTION foo;
CREATE FUNCTION foo(x INT, d1 VARCHAR(500), d2 VARCHAR(500))
@@ -179,6 +184,7 @@ a b
42 42
43 43
44 44
+100 100
SET debug_sync='RESET';
connection server_2;
SET debug_sync='now WAIT_FOR t2_query';
@@ -211,6 +217,7 @@ a b
43 43
44 44
49 0
+100 100
SET sql_log_bin=0;
DROP FUNCTION foo;
CREATE FUNCTION foo(x INT, d1 VARCHAR(500), d2 VARCHAR(500))
@@ -274,6 +281,7 @@ a b
52 52
53 53
54 54
+100 100
SET debug_sync='RESET';
connection server_2;
SET debug_sync='now WAIT_FOR t2_query';
@@ -286,6 +294,7 @@ include/wait_for_slave_sql_error.inc [errno=1317,1927,1964]
SELECT * FROM t3 WHERE a >= 50 ORDER BY a;
a b
51 51
+100 100
SET debug_sync='RESET';
SET GLOBAL slave_parallel_threads=0;
SET GLOBAL slave_parallel_threads=10;
@@ -309,6 +318,7 @@ a b
53 53
54 54
59 0
+100 100
connection server_2;
include/stop_slave.inc
CHANGE MASTER TO master_use_gtid=slave_pos;
diff --git a/mysql-test/suite/binlog_encryption/rpl_parallel_stop_slave.result b/mysql-test/suite/binlog_encryption/rpl_parallel_stop_slave.result
index 6c9fd168e73..0c810d2a3f4 100644
--- a/mysql-test/suite/binlog_encryption/rpl_parallel_stop_slave.result
+++ b/mysql-test/suite/binlog_encryption/rpl_parallel_stop_slave.result
@@ -16,6 +16,8 @@ ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t1 (a int PRIMARY KEY) ENGINE=MyISAM;
CREATE TABLE t2 (a int PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
+INSERT INTO t2 VALUES(100);
+INSERT INTO t3 VALUES(100, 100);
connection server_2;
include/stop_slave.inc
connection server_1;
@@ -55,9 +57,11 @@ SELECT * FROM t2 WHERE a >= 20 ORDER BY a;
a
20
21
+100
SELECT * FROM t3 WHERE a >= 20 ORDER BY a;
a b
20 20
+100 100
include/start_slave.inc
SELECT * FROM t1 WHERE a >= 20 ORDER BY a;
a
@@ -66,11 +70,13 @@ SELECT * FROM t2 WHERE a >= 20 ORDER BY a;
a
20
21
+100
SELECT * FROM t3 WHERE a >= 20 ORDER BY a;
a b
20 20
21 21
22 22
+100 100
connection server_2;
include/stop_slave.inc
SET GLOBAL slave_parallel_mode=@old_parallel_mode;
diff --git a/mysql-test/suite/innodb/r/alter_candidate_key.result b/mysql-test/suite/innodb/r/alter_candidate_key.result
index 23989e0da5f..b0b56047abc 100644
--- a/mysql-test/suite/innodb/r/alter_candidate_key.result
+++ b/mysql-test/suite/innodb/r/alter_candidate_key.result
@@ -66,6 +66,7 @@ test.t1 check status OK
DROP TABLE t1;
SET SQL_MODE= strict_trans_tables;
CREATE TABLE t1(a INT UNIQUE) ENGINE=InnoDB;
+INSERT INTO t1 VALUES(3);
SET DEBUG_SYNC='row_log_table_apply1_before SIGNAL dml WAIT_FOR dml_done';
ALTER TABLE t1 MODIFY COLUMN a INT NOT NULL;
connection con1;
diff --git a/mysql-test/suite/innodb/r/binlog_consistent.result b/mysql-test/suite/innodb/r/binlog_consistent.result
index 0562a92ef68..cceca394166 100644
--- a/mysql-test/suite/innodb/r/binlog_consistent.result
+++ b/mysql-test/suite/innodb/r/binlog_consistent.result
@@ -5,6 +5,7 @@ connect con3,localhost,root,,;
connect con4,localhost,root,,;
connection default;
CREATE TABLE t1 (a INT, b VARCHAR(100), PRIMARY KEY (a,b)) ENGINE=innodb;
+INSERT INTO t1 VALUES(9, "");
SHOW MASTER STATUS;
File Position Binlog_Do_DB Binlog_Ignore_DB
master-bin.000001 <pos>
@@ -39,6 +40,7 @@ connection default;
SELECT * FROM t1 ORDER BY a,b;
a b
0
+9
SHOW STATUS LIKE 'binlog_snapshot_%';
Variable_name Value
Binlog_snapshot_file master-bin.000001
@@ -61,6 +63,7 @@ connection default;
SELECT * FROM t1 ORDER BY a,b;
a b
0
+9
SHOW STATUS LIKE 'binlog_snapshot_%';
Variable_name Value
Binlog_snapshot_file master-bin.000001
@@ -80,6 +83,9 @@ include/show_binlog_events.inc
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT, b VARCHAR(100), PRIMARY KEY (a,b)) ENGINE=innodb
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES(9, "")
+master-bin.000001 # Xid # # COMMIT /* XID */
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=myisam
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
diff --git a/mysql-test/suite/innodb/r/ddl_purge.result b/mysql-test/suite/innodb/r/ddl_purge.result
index 45f4c99e97b..a1d96de24ca 100644
--- a/mysql-test/suite/innodb/r/ddl_purge.result
+++ b/mysql-test/suite/innodb/r/ddl_purge.result
@@ -1,5 +1,6 @@
CREATE TABLE t0 (pk INT PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t1 (pk INT PRIMARY KEY, b INT) ENGINE=InnoDB;
+INSERT INTO t0 VALUES(100);
connect con1,localhost,root,,test;
BEGIN;
INSERT INTO t0 SET pk=1;
diff --git a/mysql-test/suite/innodb/r/group_commit.result b/mysql-test/suite/innodb/r/group_commit.result
index e2fb89767d4..5fd7f9c9155 100644
--- a/mysql-test/suite/innodb/r/group_commit.result
+++ b/mysql-test/suite/innodb/r/group_commit.result
@@ -1,4 +1,5 @@
CREATE TABLE t1 (a VARCHAR(10) PRIMARY KEY) ENGINE=innodb;
+INSERT INTO t1 VALUES(100);
SELECT variable_value INTO @commits FROM information_schema.global_status
WHERE variable_name = 'binlog_commits';
SELECT variable_value INTO @group_commits FROM information_schema.global_status
@@ -32,11 +33,13 @@ SET DEBUG_SYNC= "now WAIT_FOR group2_con4";
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT * FROM t1 ORDER BY a;
a
+100
SET DEBUG_SYNC= "now SIGNAL group2_queued";
connection con1;
connection default;
SELECT * FROM t1 ORDER BY a;
a
+100
con1
connection con5;
SET DEBUG_SYNC= "commit_before_get_LOCK_after_binlog_sync SIGNAL group3_con5";
@@ -51,11 +54,13 @@ connection default;
SET DEBUG_SYNC= "now WAIT_FOR group3_con5";
SELECT * FROM t1 ORDER BY a;
a
+100
con1
SET DEBUG_SYNC= "now SIGNAL group3_committed";
SET DEBUG_SYNC= "now WAIT_FOR group2_visible";
SELECT * FROM t1 ORDER BY a;
a
+100
con1
con2
con3
@@ -69,6 +74,7 @@ connection con6;
connection default;
SELECT * FROM t1 ORDER BY a;
a
+100
con1
con2
con3
diff --git a/mysql-test/suite/innodb/r/group_commit_no_optimize_thread.result b/mysql-test/suite/innodb/r/group_commit_no_optimize_thread.result
index 41e9f6a9d08..e22589ed255 100644
--- a/mysql-test/suite/innodb/r/group_commit_no_optimize_thread.result
+++ b/mysql-test/suite/innodb/r/group_commit_no_optimize_thread.result
@@ -1,4 +1,5 @@
CREATE TABLE t1 (a VARCHAR(10) PRIMARY KEY) ENGINE=innodb;
+INSERT INTO t1 VALUES("default");
SELECT variable_value INTO @commits FROM information_schema.global_status
WHERE variable_name = 'binlog_commits';
SELECT variable_value INTO @group_commits FROM information_schema.global_status
@@ -32,12 +33,14 @@ SET DEBUG_SYNC= "now WAIT_FOR group2_con4";
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT * FROM t1 ORDER BY a;
a
+default
SET DEBUG_SYNC= "now SIGNAL group2_queued";
connection con1;
connection default;
SELECT * FROM t1 ORDER BY a;
a
con1
+default
connection con5;
SET DEBUG_SYNC= "commit_before_get_LOCK_after_binlog_sync SIGNAL group3_con5";
SET DEBUG_SYNC= "commit_after_get_LOCK_log SIGNAL con5_leader WAIT_FOR con6_queued";
@@ -52,6 +55,7 @@ SET DEBUG_SYNC= "now WAIT_FOR group3_con5";
SELECT * FROM t1 ORDER BY a;
a
con1
+default
SET DEBUG_SYNC= "now SIGNAL group3_committed";
SET DEBUG_SYNC= "now WAIT_FOR group2_visible";
SELECT * FROM t1 ORDER BY a;
@@ -60,6 +64,7 @@ con1
con2
con3
con4
+default
SET DEBUG_SYNC= "now SIGNAL group2_checked";
connection con2;
connection con3;
@@ -75,6 +80,7 @@ con3
con4
con5
con6
+default
SELECT variable_value - @commits FROM information_schema.global_status
WHERE variable_name = 'binlog_commits';
variable_value - @commits
diff --git a/mysql-test/suite/innodb/r/innodb-lock.result b/mysql-test/suite/innodb/r/innodb-lock.result
index 1fe0d263fef..83bed139da8 100644
--- a/mysql-test/suite/innodb/r/innodb-lock.result
+++ b/mysql-test/suite/innodb/r/innodb-lock.result
@@ -147,6 +147,8 @@ DROP TABLE t1, t2;
#
CREATE TABLE t1 (pk INT AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB
PARTITION BY key (pk) PARTITIONS 2;
+INSERT INTO t1 VALUES(100);
+INSERT INTO t1 VALUES(101);
CREATE TABLE t2 (a INT) ENGINE=InnoDB;
INSERT INTO t2 VALUES (1),(2),(3),(4),(5),(6);
CREATE TABLE t3 (b INT) ENGINE=InnoDB;
diff --git a/mysql-test/suite/innodb/r/innodb-table-online.result b/mysql-test/suite/innodb/r/innodb-table-online.result
index 7872332a4a8..70c19eefd0d 100644
--- a/mysql-test/suite/innodb/r/innodb-table-online.result
+++ b/mysql-test/suite/innodb/r/innodb-table-online.result
@@ -18,15 +18,12 @@ ddl_log_file_alter_table 0
SET DEBUG_SYNC = 'RESET';
SET DEBUG_SYNC = 'write_row_noreplace SIGNAL have_handle WAIT_FOR go_ahead';
INSERT INTO t1 VALUES(1,2,3);
-# Establish session con1 (user=root)
connect con1,localhost,root,,;
-connection con1;
SET DEBUG_SYNC = 'now WAIT_FOR have_handle';
SET lock_wait_timeout = 1;
ALTER TABLE t1 ROW_FORMAT=REDUNDANT;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SET DEBUG_SYNC = 'now SIGNAL go_ahead';
-# session default
connection default;
ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
@@ -37,7 +34,6 @@ ddl_online_create_index 0
ddl_pending_alter_table 0
ddl_sort_file_alter_table 0
ddl_log_file_alter_table 0
-# session con1
connection con1;
SET @saved_debug_dbug = @@SESSION.debug_dbug;
SET DEBUG_DBUG = '+d,innodb_OOM_prepare_inplace_alter';
@@ -55,7 +51,6 @@ SET SESSION DEBUG = @saved_debug_dbug;
Warnings:
Warning 1287 '@@debug' is deprecated and will be removed in a future release. Please use '@@debug_dbug' instead
ALTER TABLE t1 ROW_FORMAT=REDUNDANT, ALGORITHM=INPLACE, LOCK=NONE;
-# session default
connection default;
SHOW CREATE TABLE t1;
Table Create Table
@@ -67,22 +62,17 @@ t1 CREATE TABLE `t1` (
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=REDUNDANT
BEGIN;
INSERT INTO t1 VALUES(7,4,2);
-# session con1
connection con1;
SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL scanned WAIT_FOR insert_done';
ALTER TABLE t1 DROP PRIMARY KEY, ADD UNIQUE INDEX(c2);
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
-# session default
connection default;
COMMIT;
-# session con1
connection con1;
ALTER TABLE t1 DROP PRIMARY KEY, ADD UNIQUE INDEX(c2);
ERROR 23000: Duplicate entry '4' for key 'c2'
-# session default
connection default;
DELETE FROM t1 WHERE c1 = 7;
-# session con1
connection con1;
ALTER TABLE t1 DROP PRIMARY KEY, ADD UNIQUE INDEX(c2), ROW_FORMAT=COMPACT,
LOCK = SHARED, ALGORITHM = INPLACE;
@@ -100,7 +90,6 @@ t1 CREATE TABLE `t1` (
UNIQUE KEY `c2_2` (`c2`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT
ALTER TABLE t1 DROP INDEX c2, ADD PRIMARY KEY(c1);
-# session default
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR scanned';
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
@@ -114,13 +103,10 @@ ddl_log_file_alter_table 0
BEGIN;
INSERT INTO t1 VALUES(4,7,2);
SET DEBUG_SYNC = 'now SIGNAL insert_done';
-# session con1
connection con1;
ERROR 23000: Duplicate entry '4' for key 'PRIMARY'
-# session default
connection default;
ROLLBACK;
-# session con1
connection con1;
SHOW CREATE TABLE t1;
Table Create Table
@@ -142,7 +128,6 @@ ddl_online_create_index 0
ddl_pending_alter_table 0
ddl_sort_file_alter_table 0
ddl_log_file_alter_table 0
-# session default
connection default;
INSERT INTO t1 VALUES(6,3,1);
ERROR 23000: Duplicate entry '3' for key 'c2_2'
@@ -152,14 +137,12 @@ DROP INDEX c2_2 ON t1;
BEGIN;
INSERT INTO t1 VALUES(7,4,2);
ROLLBACK;
-# session con1
connection con1;
KILL QUERY @id;
ERROR 70100: Query execution was interrupted
SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL rebuilt WAIT_FOR dml_done';
SET DEBUG_SYNC = 'row_log_table_apply2_before SIGNAL applied WAIT_FOR kill_done';
ALTER TABLE t1 ROW_FORMAT=REDUNDANT;
-# session default
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR rebuilt';
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
@@ -176,7 +159,6 @@ ROLLBACK;
SET DEBUG_SYNC = 'now SIGNAL dml_done WAIT_FOR applied';
KILL QUERY @id;
SET DEBUG_SYNC = 'now SIGNAL kill_done';
-# session con1
connection con1;
ERROR 70100: Query execution was interrupted
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
@@ -187,7 +169,6 @@ ddl_online_create_index 0
ddl_pending_alter_table 0
ddl_sort_file_alter_table 0
ddl_log_file_alter_table 0
-# session default
connection default;
CHECK TABLE t1;
Table Op Msg_type Msg_text
@@ -212,7 +193,6 @@ WHERE variable_name = 'innodb_encryption_n_merge_blocks_decrypted');
SET @rowlog_encrypt_0=
(SELECT variable_value FROM information_schema.global_status
WHERE variable_name = 'innodb_encryption_n_rowlog_blocks_encrypted');
-# session con1
connection con1;
SHOW CREATE TABLE t1;
Table Create Table
@@ -227,7 +207,6 @@ SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL rebuilt2 WAIT_FOR dml2_done
SET lock_wait_timeout = 10;
ALTER TABLE t1 ROW_FORMAT=COMPACT
PAGE_COMPRESSED = YES PAGE_COMPRESSION_LEVEL = 1, ALGORITHM = INPLACE;
-# session default
connection default;
INSERT INTO t1 SELECT 80 + c1, c2, c3 FROM t1;
INSERT INTO t1 SELECT 160 + c1, c2, c3 FROM t1;
@@ -290,7 +269,6 @@ SELECT
sort_balance @merge_encrypt_1>@merge_encrypt_0 @merge_decrypt_1>@merge_decrypt_0 @rowlog_encrypt_1>@rowlog_encrypt_0
0 0 0 0
SET DEBUG_SYNC = 'now SIGNAL dml2_done';
-# session con1
connection con1;
ERROR HY000: Creating index 'PRIMARY' required more than 'innodb_online_alter_log_max_size' bytes of modification log. Please try again
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
@@ -321,7 +299,6 @@ ERROR 23000: Duplicate entry '5' 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;
-# session default
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR rebuilt3';
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
@@ -349,7 +326,6 @@ ddl_pending_alter_table 1
ddl_sort_file_alter_table 2
ddl_log_file_alter_table 2
SET DEBUG_SYNC = 'now SIGNAL dml3_done';
-# session con1
connection con1;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count
@@ -405,20 +381,16 @@ SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL c3p5_created0 WAIT_FOR ins_
ALTER TABLE t1 MODIFY c3 CHAR(255) NOT NULL, DROP COLUMN c22f,
DROP PRIMARY KEY, ADD PRIMARY KEY(c1,c4(5)),
ADD COLUMN c5 CHAR(5) DEFAULT 'tired' FIRST;
-# session default
connection default;
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,'');
SET DEBUG_SYNC = 'now SIGNAL ins_done0';
-# session con1
connection con1;
ERROR 01000: Data truncated for column 'c3' at row 323
-# session default
connection default;
ROLLBACK;
-# session con1
connection con1;
ALTER TABLE t1 MODIFY c3 CHAR(255) NOT NULL;
SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL c3p5_created WAIT_FOR ins_done';
@@ -426,14 +398,12 @@ ALTER TABLE t1 DROP PRIMARY KEY, DROP COLUMN c22f,
ADD COLUMN c6 VARCHAR(1000) DEFAULT
'I love tracking down hard-to-reproduce bugs.',
ADD PRIMARY KEY c3p5(c3(5), c6(2));
-# session default
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR c3p5_created';
SET DEBUG_SYNC = 'ib_after_row_insert SIGNAL ins_done WAIT_FOR ddl_timed_out';
INSERT INTO t1 VALUES(347,33101,NULL,'');
ERROR 23000: Column 'c3' cannot be null
INSERT INTO t1 VALUES(347,33101,'Pikku kakkosen posti','');
-# session con1
connection con1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SET DEBUG_SYNC = 'now SIGNAL ddl_timed_out';
@@ -445,7 +415,6 @@ ddl_online_create_index 0
ddl_pending_alter_table 0
ddl_sort_file_alter_table 6
ddl_log_file_alter_table 2
-# session default
connection default;
SELECT COUNT(*) FROM t1;
COUNT(*)
@@ -463,12 +432,8 @@ c22f c1 c3 c4
5 36 36foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
5 41 41foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
5 46 46foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo Online
-# session con1
connection con1;
ALTER TABLE t1 DISCARD TABLESPACE;
-# Disconnect session con1
-disconnect con1;
-# session default
connection default;
SHOW CREATE TABLE t1;
Table Create Table
@@ -479,9 +444,25 @@ t1 CREATE TABLE `t1` (
`c4` varchar(6) NOT NULL DEFAULT 'Online',
PRIMARY KEY (`c22f`,`c1`,`c4`(5))
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=REDUNDANT
-SET DEBUG_SYNC = 'RESET';
SET GLOBAL innodb_monitor_disable = module_ddl;
DROP TABLE t1;
+CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
+connection con1;
+SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL created WAIT_FOR ins';
+ALTER TABLE t1 FORCE;
+connection default;
+SET DEBUG_SYNC = 'now WAIT_FOR created';
+BEGIN;
+INSERT INTO t1 VALUES(1);
+ROLLBACK;
+SET DEBUG_SYNC = 'now SIGNAL ins';
+connection con1;
+disconnect con1;
+connection default;
+SELECT * FROM t1;
+a
+DROP TABLE t1;
+SET DEBUG_SYNC = 'RESET';
SET GLOBAL innodb_file_per_table = @global_innodb_file_per_table_orig;
SET GLOBAL innodb_monitor_enable = default;
SET GLOBAL innodb_monitor_disable = default;
diff --git a/mysql-test/suite/innodb/r/lock_insert_into_empty.result b/mysql-test/suite/innodb/r/lock_insert_into_empty.result
new file mode 100644
index 00000000000..434edffe729
--- /dev/null
+++ b/mysql-test/suite/innodb/r/lock_insert_into_empty.result
@@ -0,0 +1,24 @@
+CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
+CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB;
+INSERT INTO t1 VALUES(0);
+BEGIN;
+DELETE FROM t1;
+INSERT INTO t2 VALUES(1),(1);
+ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
+connect con1,localhost,root,,;
+BEGIN;
+SELECT * FROM t2 LOCK IN SHARE MODE;
+a
+connection default;
+SET innodb_lock_wait_timeout=1;
+INSERT INTO t2 VALUES(2);
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+disconnect con1;
+INSERT INTO t2 VALUES(3);
+COMMIT;
+SELECT * FROM t1;
+a
+SELECT * FROM t2;
+a
+3
+DROP TABLE t1, t2;
diff --git a/mysql-test/suite/innodb/t/alter_candidate_key.test b/mysql-test/suite/innodb/t/alter_candidate_key.test
index 7429cd89a1a..979d8fa4fee 100644
--- a/mysql-test/suite/innodb/t/alter_candidate_key.test
+++ b/mysql-test/suite/innodb/t/alter_candidate_key.test
@@ -42,6 +42,9 @@ DROP TABLE t1;
SET SQL_MODE= strict_trans_tables;
CREATE TABLE t1(a INT UNIQUE) ENGINE=InnoDB;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t1 VALUES(3);
SET DEBUG_SYNC='row_log_table_apply1_before SIGNAL dml WAIT_FOR dml_done';
--send ALTER TABLE t1 MODIFY COLUMN a INT NOT NULL
connection con1;
diff --git a/mysql-test/suite/innodb/t/autoinc_persist.test b/mysql-test/suite/innodb/t/autoinc_persist.test
index fd85b45fbfa..6e094b40e02 100644
--- a/mysql-test/suite/innodb/t/autoinc_persist.test
+++ b/mysql-test/suite/innodb/t/autoinc_persist.test
@@ -410,6 +410,17 @@ INSERT INTO mdev6076a SET b=NULL;
SELECT * FROM mdev6076a;
INSERT INTO mdev6076b SET b=NULL;
SELECT * FROM mdev6076b;
+# MDEV-515 resets the PAGE_ROOT_AUTOINC field in
+# root page during rollback.
+--disable_query_log
+BEGIN;
+let $i = 55;
+WHILE ($i) {
+ eval INSERT INTO mdev6076empty SET b=$i;
+ dec $i;
+}
+ROLLBACK;
+--enable_query_log
INSERT INTO mdev6076empty SET b=NULL;
SELECT * FROM mdev6076empty;
DROP TABLE mdev6076a, mdev6076b, mdev6076empty;
diff --git a/mysql-test/suite/innodb/t/binlog_consistent.test b/mysql-test/suite/innodb/t/binlog_consistent.test
index 2a735a30a0e..6a1935f80c4 100644
--- a/mysql-test/suite/innodb/t/binlog_consistent.test
+++ b/mysql-test/suite/innodb/t/binlog_consistent.test
@@ -16,7 +16,10 @@ connect(con4,localhost,root,,);
connection default;
CREATE TABLE t1 (a INT, b VARCHAR(100), PRIMARY KEY (a,b)) ENGINE=innodb;
-let pos=`select $binlog_start_pos + 254`;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t1 VALUES(9, "");
+let pos=`select $binlog_start_pos + 422`;
--replace_result $pos <pos>
SHOW MASTER STATUS;
--replace_result $pos <pos>
@@ -53,10 +56,10 @@ COMMIT;
connection default;
SELECT * FROM t1 ORDER BY a,b;
-let pos=`select $binlog_start_pos + 788`;
+let pos=`select $binlog_start_pos + 956`;
--replace_result $pos <pos>
SHOW STATUS LIKE 'binlog_snapshot_%';
-let pos=`select $binlog_start_pos + 1164`;
+let pos=`select $binlog_start_pos + 1332`;
--replace_result $pos <pos>
SHOW MASTER STATUS;
SELECT * FROM t2 ORDER BY a;
@@ -74,7 +77,7 @@ FLUSH LOGS;
connection default;
SELECT * FROM t1 ORDER BY a,b;
-let pos=`select $binlog_start_pos + 788`;
+let pos=`select $binlog_start_pos + 956`;
--replace_result $pos <pos>
SHOW STATUS LIKE 'binlog_snapshot_%';
let pos=`select $binlog_start_pos + 131`;
diff --git a/mysql-test/suite/innodb/t/ddl_purge.test b/mysql-test/suite/innodb/t/ddl_purge.test
index 60d17acead8..678cb597c03 100644
--- a/mysql-test/suite/innodb/t/ddl_purge.test
+++ b/mysql-test/suite/innodb/t/ddl_purge.test
@@ -4,7 +4,9 @@
CREATE TABLE t0 (pk INT PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t1 (pk INT PRIMARY KEY, b INT) ENGINE=InnoDB;
-
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t0 VALUES(100);
--connect (con1,localhost,root,,test)
BEGIN;
INSERT INTO t0 SET pk=1;
diff --git a/mysql-test/suite/innodb/t/group_commit.test b/mysql-test/suite/innodb/t/group_commit.test
index 692e06f38b8..3a2018bec5d 100644
--- a/mysql-test/suite/innodb/t/group_commit.test
+++ b/mysql-test/suite/innodb/t/group_commit.test
@@ -10,6 +10,9 @@
# to check some edge case for concurrency control.
CREATE TABLE t1 (a VARCHAR(10) PRIMARY KEY) ENGINE=innodb;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t1 VALUES(100);
SELECT variable_value INTO @commits FROM information_schema.global_status
WHERE variable_name = 'binlog_commits';
diff --git a/mysql-test/suite/innodb/t/group_commit_no_optimize_thread.test b/mysql-test/suite/innodb/t/group_commit_no_optimize_thread.test
index 85c0e295424..107fc6f4056 100644
--- a/mysql-test/suite/innodb/t/group_commit_no_optimize_thread.test
+++ b/mysql-test/suite/innodb/t/group_commit_no_optimize_thread.test
@@ -10,6 +10,9 @@
# to check some edge case for concurrency control.
CREATE TABLE t1 (a VARCHAR(10) PRIMARY KEY) ENGINE=innodb;
+# MDEV-515 takes X-LOCK on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t1 VALUES("default");
SELECT variable_value INTO @commits FROM information_schema.global_status
WHERE variable_name = 'binlog_commits';
diff --git a/mysql-test/suite/innodb/t/innodb-lock.test b/mysql-test/suite/innodb/t/innodb-lock.test
index 9e5505270be..84219db111e 100644
--- a/mysql-test/suite/innodb/t/innodb-lock.test
+++ b/mysql-test/suite/innodb/t/innodb-lock.test
@@ -199,6 +199,10 @@ DROP TABLE t1, t2;
CREATE TABLE t1 (pk INT AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB
PARTITION BY key (pk) PARTITIONS 2;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t1 VALUES(100);
+INSERT INTO t1 VALUES(101);
CREATE TABLE t2 (a INT) ENGINE=InnoDB;
INSERT INTO t2 VALUES (1),(2),(3),(4),(5),(6);
diff --git a/mysql-test/suite/innodb/t/innodb-table-online.test b/mysql-test/suite/innodb/t/innodb-table-online.test
index ce014b0aa00..be346001953 100644
--- a/mysql-test/suite/innodb/t/innodb-table-online.test
+++ b/mysql-test/suite/innodb/t/innodb-table-online.test
@@ -30,9 +30,7 @@ SET DEBUG_SYNC = 'write_row_noreplace SIGNAL have_handle WAIT_FOR go_ahead';
--send
INSERT INTO t1 VALUES(1,2,3);
---echo # Establish session con1 (user=root)
connect (con1,localhost,root,,);
-connection con1;
# This should block at the end because of the INSERT in connection default
# is holding a metadata lock.
@@ -42,13 +40,11 @@ SET lock_wait_timeout = 1;
ALTER TABLE t1 ROW_FORMAT=REDUNDANT;
SET DEBUG_SYNC = 'now SIGNAL go_ahead';
---echo # session default
connection default;
--error ER_DUP_ENTRY
reap;
eval $innodb_metrics_select;
---echo # session con1
connection con1;
SET @saved_debug_dbug = @@SESSION.debug_dbug;
SET DEBUG_DBUG = '+d,innodb_OOM_prepare_inplace_alter';
@@ -61,14 +57,12 @@ ALTER TABLE t1 ROW_FORMAT=REDUNDANT, ALGORITHM=INPLACE, LOCK=NONE;
SET SESSION DEBUG = @saved_debug_dbug;
ALTER TABLE t1 ROW_FORMAT=REDUNDANT, ALGORITHM=INPLACE, LOCK=NONE;
---echo # session default
connection default;
SHOW CREATE TABLE t1;
# Insert a duplicate entry (4) for the upcoming UNIQUE INDEX(c2).
BEGIN;
INSERT INTO t1 VALUES(7,4,2);
---echo # session con1
connection con1;
# This DEBUG_SYNC should not kick in yet, because the duplicate key will be
# detected before we get a chance to apply the online log.
@@ -78,20 +72,16 @@ SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL scanned WAIT_FOR insert_don
--error ER_LOCK_WAIT_TIMEOUT
ALTER TABLE t1 DROP PRIMARY KEY, ADD UNIQUE INDEX(c2);
---echo # session default
connection default;
COMMIT;
---echo # session con1
connection con1;
--error ER_DUP_ENTRY
ALTER TABLE t1 DROP PRIMARY KEY, ADD UNIQUE INDEX(c2);
---echo # session default
connection default;
DELETE FROM t1 WHERE c1 = 7;
---echo # session con1
connection con1;
ALTER TABLE t1 DROP PRIMARY KEY, ADD UNIQUE INDEX(c2), ROW_FORMAT=COMPACT,
LOCK = SHARED, ALGORITHM = INPLACE;
@@ -106,7 +96,6 @@ SHOW CREATE TABLE t1;
--send
ALTER TABLE t1 DROP INDEX c2, ADD PRIMARY KEY(c1);
---echo # session default
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR scanned';
eval $innodb_metrics_select;
@@ -116,7 +105,6 @@ BEGIN;
INSERT INTO t1 VALUES(4,7,2);
SET DEBUG_SYNC = 'now SIGNAL insert_done';
---echo # session con1
connection con1;
# Because the modification log will be applied in order and we did
# not roll back before the log apply, there will be a duplicate key
@@ -124,11 +112,9 @@ connection con1;
--error ER_DUP_ENTRY
reap;
---echo # session default
connection default;
ROLLBACK;
---echo # session con1
connection con1;
SHOW CREATE TABLE t1;
# Now, rebuild the table without any concurrent DML, while no duplicate exists.
@@ -137,7 +123,6 @@ ALTER TABLE t1 DROP PRIMARY KEY, ADD UNIQUE INDEX(c2), ALGORITHM = INPLACE;
ALTER TABLE t1 DROP INDEX c2, ADD PRIMARY KEY(c1), ALGORITHM = INPLACE;
eval $innodb_metrics_select;
---echo # session default
connection default;
--error ER_DUP_ENTRY
INSERT INTO t1 VALUES(6,3,1);
@@ -148,7 +133,6 @@ BEGIN;
INSERT INTO t1 VALUES(7,4,2);
ROLLBACK;
---echo # session con1
connection con1;
let $ID= `SELECT @id := CONNECTION_ID()`;
--error ER_QUERY_INTERRUPTED
@@ -159,7 +143,6 @@ SET DEBUG_SYNC = 'row_log_table_apply2_before SIGNAL applied WAIT_FOR kill_done'
--send
ALTER TABLE t1 ROW_FORMAT=REDUNDANT;
---echo # session default
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR rebuilt';
eval $innodb_metrics_select;
@@ -171,13 +154,11 @@ let $ignore= `SELECT @id := $ID`;
KILL QUERY @id;
SET DEBUG_SYNC = 'now SIGNAL kill_done';
---echo # session con1
connection con1;
--error ER_QUERY_INTERRUPTED
reap;
eval $innodb_metrics_select;
---echo # session default
connection default;
CHECK TABLE t1;
INSERT INTO t1 SELECT 5 + c1, c2, c3 FROM t1;
@@ -199,7 +180,6 @@ SET @rowlog_encrypt_0=
(SELECT variable_value FROM information_schema.global_status
WHERE variable_name = 'innodb_encryption_n_rowlog_blocks_encrypted');
---echo # session con1
connection con1;
SHOW CREATE TABLE t1;
ALTER TABLE t1 ROW_FORMAT=REDUNDANT;
@@ -217,7 +197,6 @@ PAGE_COMPRESSED = YES PAGE_COMPRESSION_LEVEL = 1, ALGORITHM = INPLACE;
# Generate some log (delete-mark, delete-unmark, insert etc.)
# while the index creation is blocked. Some of this may run
# in parallel with the clustered index scan.
---echo # session default
connection default;
INSERT INTO t1 SELECT 80 + c1, c2, c3 FROM t1;
INSERT INTO t1 SELECT 160 + c1, c2, c3 FROM t1;
@@ -261,7 +240,6 @@ SELECT
# Release con1.
SET DEBUG_SYNC = 'now SIGNAL dml2_done';
---echo # session con1
connection con1;
# If the following fails with the wrong error, it probably means that
# you should rerun with a larger mtr --debug-sync-timeout.
@@ -295,7 +273,6 @@ 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;
---echo # session default
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR rebuilt3';
# Generate some log (delete-mark, delete-unmark, insert etc.)
@@ -312,7 +289,6 @@ eval $innodb_metrics_select;
# Release con1.
SET DEBUG_SYNC = 'now SIGNAL dml3_done';
---echo # session con1
connection con1;
reap;
eval $innodb_metrics_select;
@@ -365,7 +341,6 @@ ALTER TABLE t1 MODIFY c3 CHAR(255) NOT NULL, DROP COLUMN c22f,
DROP PRIMARY KEY, ADD PRIMARY KEY(c1,c4(5)),
ADD COLUMN c5 CHAR(5) DEFAULT 'tired' FIRST;
---echo # session default
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR c3p5_created0';
@@ -374,16 +349,13 @@ INSERT INTO t1 VALUES(347,33101,'Pikku kakkosen posti','YLETV2');
INSERT INTO t1 VALUES(33101,347,NULL,'');
SET DEBUG_SYNC = 'now SIGNAL ins_done0';
---echo # session con1
connection con1;
--error WARN_DATA_TRUNCATED
reap;
---echo # session default
connection default;
ROLLBACK;
---echo # session con1
connection con1;
ALTER TABLE t1 MODIFY c3 CHAR(255) NOT NULL;
@@ -394,7 +366,6 @@ ADD COLUMN c6 VARCHAR(1000) DEFAULT
'I love tracking down hard-to-reproduce bugs.',
ADD PRIMARY KEY c3p5(c3(5), c6(2));
---echo # session default
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR c3p5_created';
SET DEBUG_SYNC = 'ib_after_row_insert SIGNAL ins_done WAIT_FOR ddl_timed_out';
@@ -403,33 +374,48 @@ INSERT INTO t1 VALUES(347,33101,NULL,'');
--send
INSERT INTO t1 VALUES(347,33101,'Pikku kakkosen posti','');
---echo # session con1
connection con1;
--error ER_LOCK_WAIT_TIMEOUT
reap;
SET DEBUG_SYNC = 'now SIGNAL ddl_timed_out';
eval $innodb_metrics_select;
---echo # session default
connection default;
reap;
SELECT COUNT(*) FROM t1;
ALTER TABLE t1 ROW_FORMAT=REDUNDANT;
SELECT * FROM t1 LIMIT 10;
---echo # session con1
connection con1;
ALTER TABLE t1 DISCARD TABLESPACE;
---echo # Disconnect session con1
-disconnect con1;
---echo # session default
connection default;
SHOW CREATE TABLE t1;
-SET DEBUG_SYNC = 'RESET';
SET GLOBAL innodb_monitor_disable = module_ddl;
DROP TABLE t1;
+CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
+
+connection con1;
+SET DEBUG_SYNC = 'row_log_table_apply1_before SIGNAL created WAIT_FOR ins';
+send ALTER TABLE t1 FORCE;
+
+connection default;
+SET DEBUG_SYNC = 'now WAIT_FOR created';
+BEGIN;
+INSERT INTO t1 VALUES(1);
+ROLLBACK;
+SET DEBUG_SYNC = 'now SIGNAL ins';
+
+connection con1;
+reap;
+disconnect con1;
+
+connection default;
+SELECT * FROM t1;
+DROP TABLE t1;
+SET DEBUG_SYNC = 'RESET';
+
# Check that all connections opened by test cases in this file are really
# gone so execution of other tests won't be affected by their presence.
--source include/wait_until_count_sessions.inc
diff --git a/mysql-test/suite/innodb/t/innodb_trx_weight.test b/mysql-test/suite/innodb/t/innodb_trx_weight.test
index 8dc23e99585..e33885dac8b 100644
--- a/mysql-test/suite/innodb/t/innodb_trx_weight.test
+++ b/mysql-test/suite/innodb/t/innodb_trx_weight.test
@@ -78,7 +78,7 @@ INSERT INTO t3 SELECT * FROM t3;
-- let $con1_extra_sql_present = 1
-- let $con2_extra_sql = SELECT * FROM t3 FOR UPDATE
-- let $con2_extra_sql_present = 1
--- let $con1_should_be_rolledback = 0
+-- let $con1_should_be_rolledback = 1
-- source include/innodb_trx_weight.inc
# test weight when non-transactional tables are edited
diff --git a/mysql-test/suite/innodb/t/lock_insert_into_empty.test b/mysql-test/suite/innodb/t/lock_insert_into_empty.test
new file mode 100644
index 00000000000..c6a53db79cb
--- /dev/null
+++ b/mysql-test/suite/innodb/t/lock_insert_into_empty.test
@@ -0,0 +1,26 @@
+--source include/have_innodb.inc
+
+CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
+CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB;
+
+INSERT INTO t1 VALUES(0);
+BEGIN;
+DELETE FROM t1;
+--error ER_DUP_ENTRY
+INSERT INTO t2 VALUES(1),(1);
+
+connect (con1,localhost,root,,);
+BEGIN;
+SELECT * FROM t2 LOCK IN SHARE MODE;
+
+connection default;
+SET innodb_lock_wait_timeout=1;
+--error ER_LOCK_WAIT_TIMEOUT
+INSERT INTO t2 VALUES(2);
+disconnect con1;
+INSERT INTO t2 VALUES(3);
+COMMIT;
+
+SELECT * FROM t1;
+SELECT * FROM t2;
+DROP TABLE t1, t2;
diff --git a/mysql-test/suite/innodb_fts/r/innodb_fts_plugin.result b/mysql-test/suite/innodb_fts/r/innodb_fts_plugin.result
index ec417af97c5..b1426ece222 100644
--- a/mysql-test/suite/innodb_fts/r/innodb_fts_plugin.result
+++ b/mysql-test/suite/innodb_fts/r/innodb_fts_plugin.result
@@ -139,25 +139,25 @@ INSERT INTO articles (title, body) VALUES
SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('MySQL');
id title body
-6 MySQL Tutorial DBMS stands for MySQL DataBase ...
-7 How To Use MySQL Well After you went through a ...
-8 Optimizing MySQL In this tutorial we will show ...
-9 1001 MySQL Tricks How to use full-text search engine
+1 MySQL Tutorial DBMS stands for MySQL DataBase ...
+2 How To Use MySQL Well After you went through a ...
+3 Optimizing MySQL In this tutorial we will show ...
+4 1001 MySQL Tricks How to use full-text search engine
SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('tutorial');
id title body
-6 MySQL Tutorial DBMS stands for MySQL DataBase ...
-8 Optimizing MySQL In this tutorial we will show ...
+1 MySQL Tutorial DBMS stands for MySQL DataBase ...
+3 Optimizing MySQL In this tutorial we will show ...
SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('Tricks');
id title body
-9 1001 MySQL Tricks How to use full-text search engine
-10 Go MariaDB Tricks How to use full text search engine
+4 1001 MySQL Tricks How to use full-text search engine
+5 Go MariaDB Tricks How to use full text search engine
SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('full text search');
id title body
-10 Go MariaDB Tricks How to use full text search engine
-9 1001 MySQL Tricks How to use full-text search engine
+5 Go MariaDB Tricks How to use full text search engine
+4 1001 MySQL Tricks How to use full-text search engine
SELECT COUNT(*) FROM articles;
COUNT(*)
5
diff --git a/mysql-test/suite/innodb_gis/r/rtree_concurrent_srch.result b/mysql-test/suite/innodb_gis/r/rtree_concurrent_srch.result
index f5a337ecd03..81b82691876 100644
--- a/mysql-test/suite/innodb_gis/r/rtree_concurrent_srch.result
+++ b/mysql-test/suite/innodb_gis/r/rtree_concurrent_srch.result
@@ -328,6 +328,7 @@ connection default;
DROP TABLE t1;
SET DEBUG_SYNC = 'RESET';
create table t1 (c1 int, c2 geometry not null, spatial index (c2))engine=innodb;
+insert into t1 values(100, point(100, 100));
create procedure insert_t1(IN total int)
begin
declare i int default 1;
@@ -348,7 +349,7 @@ rollback;
SET DEBUG_SYNC= 'now SIGNAL sigb';
connection a;
count(*)
-0
+1
connection default;
drop procedure insert_t1;
DROP TABLE t1;
diff --git a/mysql-test/suite/innodb_gis/t/rtree_concurrent_srch.test b/mysql-test/suite/innodb_gis/t/rtree_concurrent_srch.test
index 43119415d47..c4a6448f387 100644
--- a/mysql-test/suite/innodb_gis/t/rtree_concurrent_srch.test
+++ b/mysql-test/suite/innodb_gis/t/rtree_concurrent_srch.test
@@ -388,6 +388,7 @@ DROP TABLE t1;
SET DEBUG_SYNC = 'RESET';
create table t1 (c1 int, c2 geometry not null, spatial index (c2))engine=innodb;
+insert into t1 values(100, point(100, 100));
delimiter |;
create procedure insert_t1(IN total int)
diff --git a/mysql-test/suite/mariabackup/incremental_backup.result b/mysql-test/suite/mariabackup/incremental_backup.result
index d6a78655a0c..ed67ceee8e2 100644
--- a/mysql-test/suite/mariabackup/incremental_backup.result
+++ b/mysql-test/suite/mariabackup/incremental_backup.result
@@ -1,6 +1,7 @@
call mtr.add_suppression("InnoDB: New log files created");
CREATE TABLE t_aria(i INT) ENGINE ARIA;
CREATE TABLE t(i INT PRIMARY KEY) ENGINE INNODB;
+INSERT INTO t VALUES(100);
BEGIN;
INSERT INTO t VALUES(2);
connect con1,localhost,root,,;
@@ -17,6 +18,7 @@ SELECT * FROM t;
i
1
2
+100
# Prepare full backup, apply incremental one
# Aria log file was updated during applying incremental backup
disconnect con1;
@@ -29,5 +31,6 @@ SELECT * FROM t;
i
1
2
+100
DROP TABLE t;
DROP TABLE t_aria;
diff --git a/mysql-test/suite/mariabackup/incremental_backup.test b/mysql-test/suite/mariabackup/incremental_backup.test
index 88e277fd95a..62cdf9e6cb3 100644
--- a/mysql-test/suite/mariabackup/incremental_backup.test
+++ b/mysql-test/suite/mariabackup/incremental_backup.test
@@ -13,6 +13,9 @@ let incremental_dir=$MYSQLTEST_VARDIR/tmp/backup_inc1;
CREATE TABLE t_aria(i INT) ENGINE ARIA;
CREATE TABLE t(i INT PRIMARY KEY) ENGINE INNODB;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t VALUES(100);
BEGIN;
INSERT INTO t VALUES(2);
connect (con1,localhost,root,,);
diff --git a/mysql-test/suite/parts/inc/partition_auto_increment.inc b/mysql-test/suite/parts/inc/partition_auto_increment.inc
index 4392d04db8a..3721caeb465 100644
--- a/mysql-test/suite/parts/inc/partition_auto_increment.inc
+++ b/mysql-test/suite/parts/inc/partition_auto_increment.inc
@@ -393,6 +393,12 @@ connect(con1, localhost, root,,);
connection default;
eval CREATE TABLE t1 (c1 INT NOT NULL AUTO_INCREMENT, PRIMARY KEY (c1))
ENGINE = $engine;
+if ($engine == "'InnoDB'")
+{
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t1(c1) VALUES(100);
+}
START TRANSACTION;
INSERT INTO t1 (c1) VALUES (2);
INSERT INTO t1 (c1) VALUES (4);
@@ -434,6 +440,13 @@ eval CREATE TABLE t1 (c1 INT NOT NULL AUTO_INCREMENT, PRIMARY KEY (c1))
ENGINE = $engine
PARTITION BY HASH(c1)
PARTITIONS 2;
+IF ($engine == "'InnoDB'")
+{
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t1 (c1) VALUES (100);
+INSERT INTO t1 (c1) VALUES (101);
+}
START TRANSACTION;
INSERT INTO t1 (c1) VALUES (2);
INSERT INTO t1 (c1) VALUES (4);
diff --git a/mysql-test/suite/parts/r/partition_auto_increment_innodb.result b/mysql-test/suite/parts/r/partition_auto_increment_innodb.result
index 76f1ddfceae..7b25d4858ff 100644
--- a/mysql-test/suite/parts/r/partition_auto_increment_innodb.result
+++ b/mysql-test/suite/parts/r/partition_auto_increment_innodb.result
@@ -490,6 +490,7 @@ connect con1, localhost, root,,;
connection default;
CREATE TABLE t1 (c1 INT NOT NULL AUTO_INCREMENT, PRIMARY KEY (c1))
ENGINE = 'InnoDB';
+INSERT INTO t1(c1) VALUES(100);
START TRANSACTION;
INSERT INTO t1 (c1) VALUES (2);
INSERT INTO t1 (c1) VALUES (4);
@@ -510,17 +511,19 @@ connection con1;
INSERT INTO t1 (c1) VALUES (NULL);
SELECT * FROM t1 ORDER BY c1;
c1
-5
10
-22
-23
+100
+101
+104
+105
COMMIT;
SELECT * FROM t1 ORDER BY c1;
c1
-5
10
-22
-23
+100
+101
+104
+105
disconnect con1;
connection default;
INSERT INTO t1 (c1) VALUES (NULL);
@@ -528,31 +531,33 @@ SELECT * FROM t1 ORDER BY c1;
c1
2
4
-5
10
-11
-12
16
19
21
-22
-23
-24
+100
+101
+102
+103
+104
+105
+106
COMMIT;
SELECT * FROM t1 ORDER BY c1;
c1
2
4
-5
10
-11
-12
16
19
21
-22
-23
-24
+100
+101
+102
+103
+104
+105
+106
DROP TABLE t1;
# Test with two threads + start transaction
connect con1, localhost, root,,;
@@ -561,6 +566,8 @@ CREATE TABLE t1 (c1 INT NOT NULL AUTO_INCREMENT, PRIMARY KEY (c1))
ENGINE = 'InnoDB'
PARTITION BY HASH(c1)
PARTITIONS 2;
+INSERT INTO t1 (c1) VALUES (100);
+INSERT INTO t1 (c1) VALUES (101);
START TRANSACTION;
INSERT INTO t1 (c1) VALUES (2);
INSERT INTO t1 (c1) VALUES (4);
@@ -578,17 +585,21 @@ connection con1;
INSERT INTO t1 (c1) VALUES (NULL);
SELECT * FROM t1 ORDER BY c1;
c1
-5
10
-22
-23
+100
+101
+102
+105
+106
COMMIT;
SELECT * FROM t1 ORDER BY c1;
c1
-5
10
-22
-23
+100
+101
+102
+105
+106
disconnect con1;
connection default;
INSERT INTO t1 (c1) VALUES (NULL);
@@ -596,31 +607,35 @@ SELECT * FROM t1 ORDER BY c1;
c1
2
4
-5
10
-11
-12
16
19
21
-22
-23
-24
+100
+101
+102
+103
+104
+105
+106
+107
COMMIT;
SELECT * FROM t1 ORDER BY c1;
c1
2
4
-5
10
-11
-12
16
19
21
-22
-23
-24
+100
+101
+102
+103
+104
+105
+106
+107
DROP TABLE t1;
# Test with another column after
CREATE TABLE t1 (
diff --git a/mysql-test/suite/rpl/include/rpl_parallel_gco_wait_kill.inc b/mysql-test/suite/rpl/include/rpl_parallel_gco_wait_kill.inc
index d918b2ea692..415960e563e 100644
--- a/mysql-test/suite/rpl/include/rpl_parallel_gco_wait_kill.inc
+++ b/mysql-test/suite/rpl/include/rpl_parallel_gco_wait_kill.inc
@@ -26,6 +26,10 @@ CHANGE MASTER TO master_use_gtid=slave_pos;
--connect (con_temp5,127.0.0.1,root,,test,$SERVER_MYPORT_1,)
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t3 VALUES(100, 100);
+
--save_master_pos
--connection server_2
diff --git a/mysql-test/suite/rpl/include/rpl_parallel_ignored_errors.inc b/mysql-test/suite/rpl/include/rpl_parallel_ignored_errors.inc
index 7a6a758a508..493385f1ae3 100644
--- a/mysql-test/suite/rpl/include/rpl_parallel_ignored_errors.inc
+++ b/mysql-test/suite/rpl/include/rpl_parallel_ignored_errors.inc
@@ -57,6 +57,9 @@ CALL mtr.add_suppression("Commit failed due to failure of an earlier commit on w
--connection server_1
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t1 (a int PRIMARY KEY) ENGINE=InnoDB;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t1 VALUES(1);
--source include/save_master_gtid.inc
--connection server_2
diff --git a/mysql-test/suite/rpl/include/rpl_parallel_slave_bgc_kill.inc b/mysql-test/suite/rpl/include/rpl_parallel_slave_bgc_kill.inc
index a78dbad052f..efb998b0443 100644
--- a/mysql-test/suite/rpl/include/rpl_parallel_slave_bgc_kill.inc
+++ b/mysql-test/suite/rpl/include/rpl_parallel_slave_bgc_kill.inc
@@ -25,6 +25,10 @@ ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t1 (a int PRIMARY KEY) ENGINE=MyISAM;
CREATE TABLE t2 (a int PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t2 VALUES(100);
+INSERT INTO t3 VALUES(100, 100);
--save_master_pos
--connection server_2
diff --git a/mysql-test/suite/rpl/include/rpl_parallel_stop_slave.inc b/mysql-test/suite/rpl/include/rpl_parallel_stop_slave.inc
index 4eeddc927e0..35879e98e66 100644
--- a/mysql-test/suite/rpl/include/rpl_parallel_stop_slave.inc
+++ b/mysql-test/suite/rpl/include/rpl_parallel_stop_slave.inc
@@ -23,6 +23,10 @@ ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t1 (a int PRIMARY KEY) ENGINE=MyISAM;
CREATE TABLE t2 (a int PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t2 VALUES(100);
+INSERT INTO t3 VALUES(100, 100);
--save_master_pos
--connection server_2
diff --git a/mysql-test/suite/rpl/r/parallel_backup.result b/mysql-test/suite/rpl/r/parallel_backup.result
index d87c61f2d0f..9d394a220d5 100644
--- a/mysql-test/suite/rpl/r/parallel_backup.result
+++ b/mysql-test/suite/rpl/r/parallel_backup.result
@@ -6,6 +6,7 @@ include/master-slave.inc
#
connection master;
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE = innodb;
+INSERT INTO t1 VALUES(100);
connection slave;
include/stop_slave.inc
SET @old_parallel_threads= @@GLOBAL.slave_parallel_threads;
diff --git a/mysql-test/suite/rpl/r/rpl_circular_for_4_hosts.result b/mysql-test/suite/rpl/r/rpl_circular_for_4_hosts.result
index f50f6993bdf..4ec1e2512d2 100644
--- a/mysql-test/suite/rpl/r/rpl_circular_for_4_hosts.result
+++ b/mysql-test/suite/rpl/r/rpl_circular_for_4_hosts.result
@@ -17,6 +17,7 @@ SET auto_increment_offset= 1;
connection server_1;
CREATE TABLE t1 (a INT NOT NULL AUTO_INCREMENT, b VARCHAR(100), c INT NOT NULL, PRIMARY KEY(a)) ENGINE=MyISAM;
CREATE TABLE t2 (a INT NOT NULL AUTO_INCREMENT, b VARCHAR(100), c INT NOT NULL, PRIMARY KEY(a)) ENGINE=InnoDB;
+INSERT INTO t2 (b,c) VALUES('MDEV-515', 100);
include/rpl_sync.inc
connection server_4;
call mtr.add_suppression("Slave SQL.*Request to stop slave SQL Thread received while applying a group that has non-transactional changes; waiting for completion of the group");
diff --git a/mysql-test/suite/rpl/r/rpl_mariadb_slave_capability.result b/mysql-test/suite/rpl/r/rpl_mariadb_slave_capability.result
index d384422f88a..ac846ac6c00 100644
--- a/mysql-test/suite/rpl/r/rpl_mariadb_slave_capability.result
+++ b/mysql-test/suite/rpl/r/rpl_mariadb_slave_capability.result
@@ -78,6 +78,7 @@ slave-relay-bin.000007 # Query # # COMMIT
*** MDEV-5754: MySQL 5.5 slaves cannot replicate from MariaDB 10.0 ***
connection master;
CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB;
+INSERT INTO t2 VALUES(100);
connect con1,127.0.0.1,root,,test,$SERVER_MYPORT_1,;
SET debug_sync='commit_after_release_LOCK_prepare_ordered SIGNAL master_queued1 WAIT_FOR master_cont1';
INSERT INTO t2 VALUES (1);
@@ -112,6 +113,7 @@ SELECT * FROM t2 ORDER BY a;
a
1
2
+100
# Test that slave which cannot tolerate holes in binlog stream but
# knows the event does not get dummy event
include/stop_slave.inc
diff --git a/mysql-test/suite/rpl/r/rpl_parallel_gco_wait_kill.result b/mysql-test/suite/rpl/r/rpl_parallel_gco_wait_kill.result
index 4472550c4f2..f12d19442f1 100644
--- a/mysql-test/suite/rpl/r/rpl_parallel_gco_wait_kill.result
+++ b/mysql-test/suite/rpl/r/rpl_parallel_gco_wait_kill.result
@@ -20,6 +20,7 @@ connect con_temp4,127.0.0.1,root,,test,$SERVER_MYPORT_1,;
connect con_temp5,127.0.0.1,root,,test,$SERVER_MYPORT_1,;
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
+INSERT INTO t3 VALUES(100, 100);
connection server_2;
connection server_1;
SET sql_log_bin=0;
@@ -136,6 +137,7 @@ a b
68 68
69 69
70 70
+100 100
SET debug_sync='RESET';
connection server_2;
SET debug_sync='now SIGNAL d0_cont';
@@ -161,6 +163,7 @@ a b
68 68
69 69
70 70
+100 100
SET debug_sync='RESET';
SET GLOBAL slave_parallel_threads=0;
SET GLOBAL slave_parallel_threads=10;
@@ -190,6 +193,7 @@ a b
68 68
69 69
70 70
+100 100
SET sql_log_bin=0;
DROP FUNCTION foo;
CREATE FUNCTION foo(x INT, d1 VARCHAR(500), d2 VARCHAR(500))
@@ -225,6 +229,7 @@ SELECT * FROM t3 WHERE a >= 80 ORDER BY a;
a b
80 0
81 10000
+100 100
connection server_2;
SET debug_sync='now WAIT_FOR wait_queue_ready';
KILL THD_ID;
@@ -244,6 +249,7 @@ a b
80 0
81 10000
82 0
+100 100
connection server_2;
include/stop_slave.inc
SET GLOBAL slave_parallel_mode=@old_parallel_mode;
diff --git a/mysql-test/suite/rpl/r/rpl_parallel_ignored_errors.result b/mysql-test/suite/rpl/r/rpl_parallel_ignored_errors.result
index 3dd5a3ea83c..ce11b814d44 100644
--- a/mysql-test/suite/rpl/r/rpl_parallel_ignored_errors.result
+++ b/mysql-test/suite/rpl/r/rpl_parallel_ignored_errors.result
@@ -13,6 +13,7 @@ include/start_slave.inc
connection server_1;
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t1 (a int PRIMARY KEY) ENGINE=InnoDB;
+INSERT INTO t1 VALUES(1);
include/save_master_gtid.inc
connection server_2;
include/sync_with_master_gtid.inc
diff --git a/mysql-test/suite/rpl/r/rpl_parallel_no_log_slave_updates.result b/mysql-test/suite/rpl/r/rpl_parallel_no_log_slave_updates.result
index e966023c30f..6e324be9ebb 100644
--- a/mysql-test/suite/rpl/r/rpl_parallel_no_log_slave_updates.result
+++ b/mysql-test/suite/rpl/r/rpl_parallel_no_log_slave_updates.result
@@ -35,6 +35,7 @@ SET sql_log_bin=1;
connect con_temp3,127.0.0.1,root,,test,$SERVER_MYPORT_1,;
SET debug_sync='commit_after_release_LOCK_prepare_ordered SIGNAL master_queued1 WAIT_FOR master_cont1';
SET binlog_format=statement;
+SET debug_dbug='+d,row_ins_row_level';
INSERT INTO t3 VALUES (31, foo(31,
'ha_commit_one_phase WAIT_FOR t2_waiting',
'commit_one_phase_2 SIGNAL t1_ready WAIT_FOR t1_cont'));
@@ -43,6 +44,7 @@ SET debug_sync='now WAIT_FOR master_queued1';
connect con_temp4,127.0.0.1,root,,test,$SERVER_MYPORT_1,;
SET debug_sync='commit_after_release_LOCK_prepare_ordered SIGNAL master_queued2';
SET binlog_format=statement;
+SET debug_dbug='+d,row_ins_row_level';
BEGIN;
INSERT INTO t3 VALUES (32, foo(32,
'ha_write_row_end SIGNAL t2_query WAIT_FOR t2_cont',
@@ -56,6 +58,7 @@ SET debug_sync='now WAIT_FOR master_queued2';
connect con_temp5,127.0.0.1,root,,test,$SERVER_MYPORT_1,;
SET debug_sync='commit_after_release_LOCK_prepare_ordered SIGNAL master_queued3';
SET binlog_format=statement;
+SET debug_dbug='+d,row_ins_row_level';
INSERT INTO t3 VALUES (34, foo(34,
'',
''));
diff --git a/mysql-test/suite/rpl/r/rpl_parallel_retry.result b/mysql-test/suite/rpl/r/rpl_parallel_retry.result
index b43556815d1..381fec649ff 100644
--- a/mysql-test/suite/rpl/r/rpl_parallel_retry.result
+++ b/mysql-test/suite/rpl/r/rpl_parallel_retry.result
@@ -259,6 +259,7 @@ connection server_1;
CREATE TABLE t3 (a INT PRIMARY KEY, b INT, KEY b_idx(b)) ENGINE=InnoDB;
INSERT INTO t3 VALUES (1,NULL), (2,2), (3,NULL), (4,4), (5, NULL), (6, 6);
CREATE TABLE t4 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
+INSERT INTO t4 VALUES(100, 100);
SET @old_format= @@SESSION.binlog_format;
SET binlog_format='statement';
connection server_2;
@@ -344,6 +345,7 @@ DROP function foo;
connection server_2;
connection server_1;
CREATE TABLE t1 (a int PRIMARY KEY, b INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES(100, 100);
connection server_2;
include/stop_slave.inc
SET @old_parallel_threads=@@GLOBAL.slave_parallel_threads;
diff --git a/mysql-test/suite/rpl/r/rpl_parallel_slave_bgc_kill.result b/mysql-test/suite/rpl/r/rpl_parallel_slave_bgc_kill.result
index 320bf0e49f8..ba131ea094f 100644
--- a/mysql-test/suite/rpl/r/rpl_parallel_slave_bgc_kill.result
+++ b/mysql-test/suite/rpl/r/rpl_parallel_slave_bgc_kill.result
@@ -17,6 +17,8 @@ ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t1 (a int PRIMARY KEY) ENGINE=MyISAM;
CREATE TABLE t2 (a int PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
+INSERT INTO t2 VALUES(100);
+INSERT INTO t3 VALUES(100, 100);
connection server_2;
connection server_1;
SET sql_log_bin=0;
@@ -80,6 +82,7 @@ a b
32 32
33 33
34 34
+100 100
SET debug_sync='RESET';
connection server_2;
SET sql_log_bin=0;
@@ -98,6 +101,7 @@ STOP SLAVE IO_THREAD;
SELECT * FROM t3 WHERE a >= 30 ORDER BY a;
a b
31 31
+100 100
SET debug_sync='RESET';
SET GLOBAL slave_parallel_threads=0;
SET GLOBAL slave_parallel_threads=10;
@@ -121,6 +125,7 @@ a b
33 33
34 34
39 0
+100 100
SET sql_log_bin=0;
DROP FUNCTION foo;
CREATE FUNCTION foo(x INT, d1 VARCHAR(500), d2 VARCHAR(500))
@@ -179,6 +184,7 @@ a b
42 42
43 43
44 44
+100 100
SET debug_sync='RESET';
connection server_2;
SET debug_sync='now WAIT_FOR t2_query';
@@ -211,6 +217,7 @@ a b
43 43
44 44
49 0
+100 100
SET sql_log_bin=0;
DROP FUNCTION foo;
CREATE FUNCTION foo(x INT, d1 VARCHAR(500), d2 VARCHAR(500))
@@ -274,6 +281,7 @@ a b
52 52
53 53
54 54
+100 100
SET debug_sync='RESET';
connection server_2;
SET debug_sync='now WAIT_FOR t2_query';
@@ -286,6 +294,7 @@ include/wait_for_slave_sql_error.inc [errno=1317,1927,1964]
SELECT * FROM t3 WHERE a >= 50 ORDER BY a;
a b
51 51
+100 100
SET debug_sync='RESET';
SET GLOBAL slave_parallel_threads=0;
SET GLOBAL slave_parallel_threads=10;
@@ -309,6 +318,7 @@ a b
53 53
54 54
59 0
+100 100
connection server_2;
include/stop_slave.inc
CHANGE MASTER TO master_use_gtid=slave_pos;
diff --git a/mysql-test/suite/rpl/r/rpl_parallel_stop_slave.result b/mysql-test/suite/rpl/r/rpl_parallel_stop_slave.result
index 6c9fd168e73..0c810d2a3f4 100644
--- a/mysql-test/suite/rpl/r/rpl_parallel_stop_slave.result
+++ b/mysql-test/suite/rpl/r/rpl_parallel_stop_slave.result
@@ -16,6 +16,8 @@ ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t1 (a int PRIMARY KEY) ENGINE=MyISAM;
CREATE TABLE t2 (a int PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
+INSERT INTO t2 VALUES(100);
+INSERT INTO t3 VALUES(100, 100);
connection server_2;
include/stop_slave.inc
connection server_1;
@@ -55,9 +57,11 @@ SELECT * FROM t2 WHERE a >= 20 ORDER BY a;
a
20
21
+100
SELECT * FROM t3 WHERE a >= 20 ORDER BY a;
a b
20 20
+100 100
include/start_slave.inc
SELECT * FROM t1 WHERE a >= 20 ORDER BY a;
a
@@ -66,11 +70,13 @@ SELECT * FROM t2 WHERE a >= 20 ORDER BY a;
a
20
21
+100
SELECT * FROM t3 WHERE a >= 20 ORDER BY a;
a b
20 20
21 21
22 22
+100 100
connection server_2;
include/stop_slave.inc
SET GLOBAL slave_parallel_mode=@old_parallel_mode;
diff --git a/mysql-test/suite/rpl/r/rpl_semisync_ali_issues.result b/mysql-test/suite/rpl/r/rpl_semisync_ali_issues.result
index 9607e8a7998..36a2bac5838 100644
--- a/mysql-test/suite/rpl/r/rpl_semisync_ali_issues.result
+++ b/mysql-test/suite/rpl/r/rpl_semisync_ali_issues.result
@@ -74,6 +74,7 @@ select * from t1;
a
1
truncate table t1;
+INSERT INTO t1 VALUES (100);
connection slave;
connection con1;
SET DEBUG_SYNC= 'reset';
@@ -107,6 +108,7 @@ SET DEBUG_SYNC= "now WAIT_FOR after_commit_done";
connection slave;
select * from t1;
a
+100
1
2
3
@@ -114,6 +116,7 @@ a
connection con2;
select * from t1;
a
+100
1
2
3
@@ -123,6 +126,7 @@ connection con1;
connection con1;
select * from t1;
a
+100
1
2
3
diff --git a/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect.result b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect.result
index 0ee7b497077..3ab263b7f35 100644
--- a/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect.result
+++ b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect.result
@@ -7,6 +7,7 @@ CREATE DATABASE d1;
CREATE DATABASE d2;
CREATE TABLE d1.t (a INT) ENGINE=innodb;
CREATE TABLE d2.t (a INT) ENGINE=innodb;
+INSERT INTO d1.t VALUES(100);
connect master_conn1, 127.0.0.1,root,,test,$MASTER_MYPORT,;
SET @@session.binlog_format= statement;
XA START '1-stmt';
@@ -45,6 +46,9 @@ master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE d1.t (a INT) ENGINE=innodb
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE d2.t (a INT) ENGINE=innodb
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO d1.t VALUES(100)
+master-bin.000001 # Xid # # COMMIT /* XID */
master-bin.000001 # Gtid # # XA START X'312d73746d74',X'',1 GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO d1.t VALUES (1)
master-bin.000001 # Query # # XA END X'312d73746d74',X'',1
@@ -90,6 +94,7 @@ connection master2;
XA START '4';
SELECT * FROM d1.t;
a
+100
1
2
XA END '4';
diff --git a/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_lsu_off.result b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_lsu_off.result
index 0ee7b497077..3ab263b7f35 100644
--- a/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_lsu_off.result
+++ b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_lsu_off.result
@@ -7,6 +7,7 @@ CREATE DATABASE d1;
CREATE DATABASE d2;
CREATE TABLE d1.t (a INT) ENGINE=innodb;
CREATE TABLE d2.t (a INT) ENGINE=innodb;
+INSERT INTO d1.t VALUES(100);
connect master_conn1, 127.0.0.1,root,,test,$MASTER_MYPORT,;
SET @@session.binlog_format= statement;
XA START '1-stmt';
@@ -45,6 +46,9 @@ master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE d1.t (a INT) ENGINE=innodb
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE d2.t (a INT) ENGINE=innodb
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO d1.t VALUES(100)
+master-bin.000001 # Xid # # COMMIT /* XID */
master-bin.000001 # Gtid # # XA START X'312d73746d74',X'',1 GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO d1.t VALUES (1)
master-bin.000001 # Query # # XA END X'312d73746d74',X'',1
@@ -90,6 +94,7 @@ connection master2;
XA START '4';
SELECT * FROM d1.t;
a
+100
1
2
XA END '4';
diff --git a/mysql-test/suite/rpl/t/parallel_backup.test b/mysql-test/suite/rpl/t/parallel_backup.test
index 6ed182c024b..964e2a30309 100644
--- a/mysql-test/suite/rpl/t/parallel_backup.test
+++ b/mysql-test/suite/rpl/t/parallel_backup.test
@@ -10,6 +10,9 @@
--connection master
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE = innodb;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t1 VALUES(100);
--sync_slave_with_master
--source include/stop_slave.inc
diff --git a/mysql-test/suite/rpl/t/rpl_circular_for_4_hosts.test b/mysql-test/suite/rpl/t/rpl_circular_for_4_hosts.test
index 8d610a87b89..aeb849b893e 100644
--- a/mysql-test/suite/rpl/t/rpl_circular_for_4_hosts.test
+++ b/mysql-test/suite/rpl/t/rpl_circular_for_4_hosts.test
@@ -39,6 +39,9 @@ while ($_rpl_server)
--connection server_1
CREATE TABLE t1 (a INT NOT NULL AUTO_INCREMENT, b VARCHAR(100), c INT NOT NULL, PRIMARY KEY(a)) ENGINE=MyISAM;
CREATE TABLE t2 (a INT NOT NULL AUTO_INCREMENT, b VARCHAR(100), c INT NOT NULL, PRIMARY KEY(a)) ENGINE=InnoDB;
+# MDEV-515 takes X-lock on the table. So it won't allow
+# concurrent DML to happen at the same time
+INSERT INTO t2 (b,c) VALUES('MDEV-515', 100);
--source include/rpl_sync.inc
--connection server_4
call mtr.add_suppression("Slave SQL.*Request to stop slave SQL Thread received while applying a group that has non-transactional changes; waiting for completion of the group");
diff --git a/mysql-test/suite/rpl/t/rpl_mariadb_slave_capability.test b/mysql-test/suite/rpl/t/rpl_mariadb_slave_capability.test
index 046a65f77db..19f2db32cb7 100644
--- a/mysql-test/suite/rpl/t/rpl_mariadb_slave_capability.test
+++ b/mysql-test/suite/rpl/t/rpl_mariadb_slave_capability.test
@@ -84,6 +84,9 @@ let $binlog_limit=7,5;
--connection master
CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t2 VALUES(100);
let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1);
let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1);
diff --git a/mysql-test/suite/rpl/t/rpl_parallel_no_log_slave_updates.test b/mysql-test/suite/rpl/t/rpl_parallel_no_log_slave_updates.test
index 75db619d225..2e4c57953fe 100644
--- a/mysql-test/suite/rpl/t/rpl_parallel_no_log_slave_updates.test
+++ b/mysql-test/suite/rpl/t/rpl_parallel_no_log_slave_updates.test
@@ -57,6 +57,8 @@ SET sql_log_bin=1;
--connect (con_temp3,127.0.0.1,root,,test,$SERVER_MYPORT_1,)
SET debug_sync='commit_after_release_LOCK_prepare_ordered SIGNAL master_queued1 WAIT_FOR master_cont1';
SET binlog_format=statement;
+
+SET debug_dbug='+d,row_ins_row_level';
send INSERT INTO t3 VALUES (31, foo(31,
'ha_commit_one_phase WAIT_FOR t2_waiting',
'commit_one_phase_2 SIGNAL t1_ready WAIT_FOR t1_cont'));
@@ -67,6 +69,7 @@ SET debug_sync='now WAIT_FOR master_queued1';
--connect (con_temp4,127.0.0.1,root,,test,$SERVER_MYPORT_1,)
SET debug_sync='commit_after_release_LOCK_prepare_ordered SIGNAL master_queued2';
SET binlog_format=statement;
+SET debug_dbug='+d,row_ins_row_level';
BEGIN;
# This insert is just so we can get T2 to wait while a query is running that we
# can see in SHOW PROCESSLIST so we can get its thread_id to kill later.
@@ -86,6 +89,7 @@ SET debug_sync='now WAIT_FOR master_queued2';
--connect (con_temp5,127.0.0.1,root,,test,$SERVER_MYPORT_1,)
SET debug_sync='commit_after_release_LOCK_prepare_ordered SIGNAL master_queued3';
SET binlog_format=statement;
+SET debug_dbug='+d,row_ins_row_level';
send INSERT INTO t3 VALUES (34, foo(34,
'',
''));
diff --git a/mysql-test/suite/rpl/t/rpl_parallel_retry.test b/mysql-test/suite/rpl/t/rpl_parallel_retry.test
index 97a3a709eb5..0449995621a 100644
--- a/mysql-test/suite/rpl/t/rpl_parallel_retry.test
+++ b/mysql-test/suite/rpl/t/rpl_parallel_retry.test
@@ -266,6 +266,9 @@ SELECT * FROM t1 WHERE a >= 100 ORDER BY a;
CREATE TABLE t3 (a INT PRIMARY KEY, b INT, KEY b_idx(b)) ENGINE=InnoDB;
INSERT INTO t3 VALUES (1,NULL), (2,2), (3,NULL), (4,4), (5, NULL), (6, 6);
CREATE TABLE t4 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t4 VALUES(100, 100);
# We need statement binlog format to be able to inject debug_sync statements
# on the slave with calls to foo().
@@ -390,7 +393,9 @@ DROP function foo;
--connection server_1
CREATE TABLE t1 (a int PRIMARY KEY, b INT) ENGINE=InnoDB;
-
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent insert won't happen on the table
+INSERT INTO t1 VALUES(100, 100);
# Replicate create-t1 and prepare to re-start slave in optimistic mode
--sync_slave_with_master server_2
diff --git a/mysql-test/suite/rpl/t/rpl_semisync_ali_issues.test b/mysql-test/suite/rpl/t/rpl_semisync_ali_issues.test
index f67c6e2ac0a..5e6f350b191 100644
--- a/mysql-test/suite/rpl/t/rpl_semisync_ali_issues.test
+++ b/mysql-test/suite/rpl/t/rpl_semisync_ali_issues.test
@@ -81,6 +81,10 @@ connection con1;
select * from t1;
truncate table t1;
+# MDEV-515 takes X-lock on the table
+# So concurrent DML won't happen on the table
+INSERT INTO t1 VALUES (100);
+
sync_slave_with_master;
# Test more threads in one semisync queue
diff --git a/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect.test b/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect.test
index 28d189364d6..e4193a35583 100644
--- a/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect.test
+++ b/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect.test
@@ -30,6 +30,10 @@ CREATE DATABASE d2;
CREATE TABLE d1.t (a INT) ENGINE=innodb;
CREATE TABLE d2.t (a INT) ENGINE=innodb;
+# MDEV-515 takes X-lock on the table for the first insert.
+# So concurrent DML won't happen on the table
+INSERT INTO d1.t VALUES(100);
+
connect (master_conn1, 127.0.0.1,root,,test,$MASTER_MYPORT,);
--let $conn_id=`SELECT connection_id()`
SET @@session.binlog_format= statement;
diff --git a/mysql-test/suite/sys_vars/r/identity_func.result b/mysql-test/suite/sys_vars/r/identity_func.result
index 47a6fa856c5..2e064532228 100644
--- a/mysql-test/suite/sys_vars/r/identity_func.result
+++ b/mysql-test/suite/sys_vars/r/identity_func.result
@@ -14,6 +14,8 @@ id INT NOT NULL auto_increment,
PRIMARY KEY (id),
name VARCHAR(30)
) ENGINE = INNODB;
+INSERT INTO t1 VALUES(100, "MDEV-515");
+INSERT INTO t2 VALUES(100, "MDEV-515");
'#--------------------FN_DYNVARS_035_01-------------------------#'
## It should be zero ##
SELECT @@identity = 0;
@@ -29,40 +31,47 @@ INSERT into t1(name) values('Record_3');
## Verifying total values in t1 ##
SELECT @@identity from t1;
@@identity
-3
-3
-3
+103
+103
+103
+103
## Now inserting some data in table t2 ##
INSERT into t2(name) values('Record_1');
## Verifying total values in t2 ##
SELECT @@identity from t2;
@@identity
-1
+101
+101
'#--------------------FN_DYNVARS_035_02-------------------------#'
connect test_con2, localhost, root,,;
connection test_con2;
SELECT * from t1;
id name
+100 MDEV-515
## Verifying total values in t1 ##
SELECT @@identity from t1;
@@identity
+0
## Verifying total values in t2 ##
SELECT @@identity from t2;
@@identity
+0
## Inserting some more records in table t1 ##
INSERT into t1(name) values('Record_1_1');
INSERT into t1(name) values('Record_1_2');
## Verifying total values in t1 ##
SELECT @@identity from t1;
@@identity
-5
-5
+105
+105
+105
## Inserting row in table t2 ##
INSERT into t2(name) values('Record_1_3');
## Verifying total values in t2 ##
SELECT @@identity from t2;
@@identity
-2
+102
+102
'#--------------------FN_DYNVARS_035_03-------------------------#'
connection test_con1;
## Commiting rows added in test_con1 ##
@@ -70,38 +79,43 @@ COMMIT;
## Verifying records in both tables ##
SELECT * from t1;
id name
-1 Record_1
-2 Record_2
-3 Record_3
-4 Record_1_1
-5 Record_1_2
+100 MDEV-515
+101 Record_1
+102 Record_2
+103 Record_3
+104 Record_1_1
+105 Record_1_2
SELECT * from t2;
id name
-1 Record_1
-2 Record_1_3
+100 MDEV-515
+101 Record_1
+102 Record_1_3
## Verifying total values in t1 after commiting data ##
SELECT @@identity from t1;
@@identity
-1
-1
-1
-1
-1
+101
+101
+101
+101
+101
+101
## Verifying total values in t2 after commiting data ##
SELECT @@identity from t2;
@@identity
-1
-1
+101
+101
+101
INSERT into t1(name) values('Record_4');
## Now verifying value of variable after inserting 1 row in this connection ##
SELECT @@identity from t1;
@@identity
-6
-6
-6
-6
-6
-6
+106
+106
+106
+106
+106
+106
+106
## Dropping tables t1 & t2 ##
drop table t1, t2;
disconnect test_con1;
diff --git a/mysql-test/suite/sys_vars/t/identity_func.test b/mysql-test/suite/sys_vars/t/identity_func.test
index e3bbaa1d3a2..7398b5d0b63 100644
--- a/mysql-test/suite/sys_vars/t/identity_func.test
+++ b/mysql-test/suite/sys_vars/t/identity_func.test
@@ -46,6 +46,11 @@ PRIMARY KEY (id),
name VARCHAR(30)
) ENGINE = INNODB;
+# MDEV-515 takes X-lock on the table for the first insert
+# So concurrent insert won't happen on the table
+INSERT INTO t1 VALUES(100, "MDEV-515");
+INSERT INTO t2 VALUES(100, "MDEV-515");
+
--echo '#--------------------FN_DYNVARS_035_01-------------------------#'
###############################################
# Verifying initial value of identity. #
diff --git a/mysql-test/suite/versioning/r/trx_id.result b/mysql-test/suite/versioning/r/trx_id.result
index b2bb59a4150..3ddd1bf16fe 100644
--- a/mysql-test/suite/versioning/r/trx_id.result
+++ b/mysql-test/suite/versioning/r/trx_id.result
@@ -89,6 +89,7 @@ sys_start bigint(20) unsigned as row start invisible,
sys_end bigint(20) unsigned as row end invisible,
period for system_time (sys_start, sys_end)
) with system versioning;
+INSERT INTO t1 VALUES(100);
set transaction isolation level read committed;
start transaction;
insert into t1 values (1);
@@ -129,25 +130,31 @@ select @ts1 < @ts2, @ts2 < @ts3;
# MVCC is resolved
select * from t1 for system_time as of transaction @trx_id1;
x
+100
1
2
3
select * from t1 for system_time as of timestamp @ts1;
x
+100
3
select * from t1 for system_time as of transaction @trx_id2;
x
+100
2
3
select * from t1 for system_time as of timestamp @ts2;
x
+100
2
3
select * from t1 for system_time as of transaction @trx_id3;
x
+100
3
select * from t1 for system_time as of timestamp @ts3;
x
+100
1
2
3
diff --git a/mysql-test/suite/versioning/t/trx_id.test b/mysql-test/suite/versioning/t/trx_id.test
index db691a7ec19..08282f8b02f 100644
--- a/mysql-test/suite/versioning/t/trx_id.test
+++ b/mysql-test/suite/versioning/t/trx_id.test
@@ -87,6 +87,10 @@ create or replace table t1 (
period for system_time (sys_start, sys_end)
) with system versioning;
+# MDEV-515 takes X-lock on the table for the first insert
+# So concurrent DML won't happen on the table
+INSERT INTO t1 VALUES(100);
+
set transaction isolation level read committed;
start transaction;
insert into t1 values (1);
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 470f59fe15f..1924d55cfa0 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2005, 2019, Oracle and/or its affiliates.
- Copyright (c) 2009, 2020, MariaDB
+ Copyright (c) 2009, 2021, MariaDB
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
@@ -9145,6 +9145,7 @@ int ha_partition::extra(enum ha_extra_function operation)
case HA_EXTRA_BEGIN_ALTER_COPY:
case HA_EXTRA_END_ALTER_COPY:
case HA_EXTRA_FAKE_START_STMT:
+ case HA_EXTRA_IGNORE_INSERT:
DBUG_RETURN(loop_partitions(extra_cb, &operation));
default:
{
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index d8ceefb5238..3b8414f0c71 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -5062,6 +5062,14 @@ extern "C" enum enum_server_command thd_current_command(MYSQL_THD thd)
return thd->get_command();
}
+#ifdef HAVE_REPLICATION /* Working around MDEV-24622 */
+/** @return whether the current thread is for applying binlog in a replica */
+extern "C" int thd_is_slave(const MYSQL_THD thd)
+{
+ return thd && thd->slave_thread;
+}
+#endif /* HAVE_REPLICATION */
+
/* Returns high resolution timestamp for the start
of the current query. */
extern "C" unsigned long long thd_start_utime(const MYSQL_THD thd)
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index be91e23ef58..05af99ede79 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2016, Oracle and/or its affiliates.
- Copyright (c) 2010, 2019, MariaDB Corporation
+ Copyright (c) 2010, 2021, MariaDB
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
@@ -2116,6 +2116,9 @@ int write_record(THD *thd, TABLE *table, COPY_INFO *info, select_result *sink)
goto after_trg_or_ignored_err;
}
+ /* Notify the engine about insert ignore operation */
+ if (info->handle_duplicates == DUP_ERROR && info->ignore)
+ table->file->extra(HA_EXTRA_IGNORE_INSERT);
after_trg_n_copied_inc:
info->copied++;
thd->record_first_successful_insert_id_in_cur_stmt(table->file->insert_id_for_cur_row);
@@ -4148,6 +4151,7 @@ bool select_insert::prepare_eof()
if (info.ignore || info.handle_duplicates != DUP_ERROR)
if (table->file->ha_table_flags() & HA_DUPLICATE_POS)
table->file->ha_rnd_end();
+ table->file->extra(HA_EXTRA_END_ALTER_COPY);
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
@@ -4740,7 +4744,10 @@ select_create::prepare(List<Item> &_values, SELECT_LEX_UNIT *u)
if (info.handle_duplicates == DUP_UPDATE)
table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE);
if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
+ {
table->file->ha_start_bulk_insert((ha_rows) 0);
+ table->file->extra(HA_EXTRA_BEGIN_ALTER_COPY);
+ }
thd->abort_on_warning= !info.ignore && thd->is_strict_mode();
if (check_that_all_fields_are_given_values(thd, table, table_list))
DBUG_RETURN(1);
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 6825fbc8e24..5321ffd11bb 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2019, Oracle and/or its affiliates.
- Copyright (c) 2010, 2020, MariaDB
+ Copyright (c) 2010, 2021, MariaDB
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
@@ -11533,6 +11533,9 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
}
else
{
+ /* In case of alter ignore, notify the engine about it. */
+ if (ignore)
+ to->file->extra(HA_EXTRA_IGNORE_INSERT);
DEBUG_SYNC(thd, "copy_data_between_tables_before");
found_count++;
mysql_stage_set_work_completed(thd->m_stage_progress_psi, found_count);
diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc
index 8f9186fb923..4d85ce16df1 100644
--- a/storage/innobase/btr/btr0btr.cc
+++ b/storage/innobase/btr/btr0btr.cc
@@ -977,6 +977,43 @@ btr_free_root_check(
return(block);
}
+/** Initialize the root page of the b-tree
+@param[in,out] block root block
+@param[in] index_id index id
+@param[in] index index of root page
+@param[in,out] mtr mini-transaction */
+static void btr_root_page_init(buf_block_t *block, index_id_t index_id,
+ dict_index_t *index, mtr_t *mtr)
+{
+ constexpr uint16_t field= PAGE_HEADER + PAGE_INDEX_ID;
+ byte *page_index_id= my_assume_aligned<2>(field + block->frame);
+
+ /* Create a new index page on the allocated segment page */
+ if (UNIV_LIKELY_NULL(block->page.zip.data))
+ {
+ mach_write_to_8(page_index_id, index_id);
+ ut_ad(!page_has_siblings(block->page.zip.data));
+ page_create_zip(block, index, 0, 0, mtr);
+ }
+ else
+ {
+ page_create(block, mtr, index && index->table->not_redundant());
+ if (index && index->is_spatial())
+ {
+ static_assert(((FIL_PAGE_INDEX & 0xff00) | byte(FIL_PAGE_RTREE)) ==
+ FIL_PAGE_RTREE, "compatibility");
+ mtr->write<1>(*block, FIL_PAGE_TYPE + 1 + block->frame,
+ byte(FIL_PAGE_RTREE));
+ if (mach_read_from_8(block->frame + FIL_RTREE_SPLIT_SEQ_NUM))
+ mtr->memset(block, FIL_RTREE_SPLIT_SEQ_NUM, 8, 0);
+ }
+ /* Set the level of the new index page */
+ mtr->write<2,mtr_t::MAYBE_NOP>(
+ *block, PAGE_HEADER + PAGE_LEVEL + block->frame, 0U);
+ mtr->write<8,mtr_t::MAYBE_NOP>(*block, page_index_id, index_id);
+ }
+}
+
/** Create the root node for a new index tree.
@param[in] type type of the index
@param[in] index_id index id
@@ -1049,36 +1086,7 @@ btr_create(
ut_ad(!page_has_siblings(block->frame));
- constexpr uint16_t field = PAGE_HEADER + PAGE_INDEX_ID;
-
- byte* page_index_id = my_assume_aligned<2>(field + block->frame);
-
- /* Create a new index page on the allocated segment page */
- if (UNIV_LIKELY_NULL(block->page.zip.data)) {
- mach_write_to_8(page_index_id, index_id);
- ut_ad(!page_has_siblings(block->page.zip.data));
- page_create_zip(block, index, 0, 0, mtr);
- } else {
- page_create(block, mtr,
- index && index->table->not_redundant());
- if (index && index->is_spatial()) {
- static_assert(((FIL_PAGE_INDEX & 0xff00)
- | byte(FIL_PAGE_RTREE))
- == FIL_PAGE_RTREE, "compatibility");
- mtr->write<1>(*block, FIL_PAGE_TYPE + 1 + block->frame,
- byte(FIL_PAGE_RTREE));
- if (mach_read_from_8(block->frame
- + FIL_RTREE_SPLIT_SEQ_NUM)) {
- mtr->memset(block, FIL_RTREE_SPLIT_SEQ_NUM,
- 8, 0);
- }
- }
- /* Set the level of the new index page */
- mtr->write<2,mtr_t::MAYBE_NOP>(*block, PAGE_HEADER + PAGE_LEVEL
- + block->frame, 0U);
- mtr->write<8,mtr_t::MAYBE_NOP>(*block, page_index_id,
- index_id);
- }
+ btr_root_page_init(block, index_id, index, mtr);
/* We reset the free bits for the page in a separate
mini-transaction to allow creation of several trees in the
@@ -1167,6 +1175,33 @@ top_loop:
}
}
+/** Clear the index tree and reinitialize the root page, in the
+rollback of TRX_UNDO_EMPTY. The BTR_SEG_LEAF is freed and reinitialized.
+@param thr query thread */
+void dict_index_t::clear(que_thr_t *thr)
+{
+ mtr_t mtr;
+ mtr.start();
+ if (table->is_temporary())
+ mtr.set_log_mode(MTR_LOG_NO_REDO);
+ else
+ set_modified(mtr);
+
+ if (buf_block_t *root_block= buf_page_get(page_id_t(table->space->id, page),
+ table->space->zip_size(),
+ RW_X_LATCH, &mtr))
+ {
+ btr_free_but_not_root(root_block, mtr.get_log_mode());
+ mtr.memset(root_block, PAGE_HEADER + PAGE_BTR_SEG_LEAF,
+ FSEG_HEADER_SIZE, 0);
+ if (fseg_create(table->space, PAGE_HEADER + PAGE_BTR_SEG_LEAF, &mtr, false,
+ root_block))
+ btr_root_page_init(root_block, id, this, &mtr);
+ }
+
+ mtr.commit();
+}
+
/** Free a persistent index tree if it exists.
@param[in] page_id root page id
@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0
diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc
index 2f4db18ca50..6add764279a 100644
--- a/storage/innobase/btr/btr0cur.cc
+++ b/storage/innobase/btr/btr0cur.cc
@@ -3,7 +3,7 @@
Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc.
Copyright (c) 2012, Facebook Inc.
-Copyright (c) 2015, 2020, MariaDB Corporation.
+Copyright (c) 2015, 2021, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -67,6 +67,7 @@ Created 10/16/1994 Heikki Tuuri
#include "srv0start.h"
#include "mysql_com.h"
#include "dict0stats.h"
+#include "row0ins.h"
/** Buffered B-tree operation types, introduced as part of delete buffering. */
enum btr_op_t {
@@ -3210,25 +3211,27 @@ btr_cur_ins_lock_and_undo(
should inherit LOCK_GAP type locks from the
successor record */
{
- dict_index_t* index;
- dberr_t err = DB_SUCCESS;
- rec_t* rec;
- roll_ptr_t roll_ptr;
+ if (!(~flags | (BTR_NO_UNDO_LOG_FLAG | BTR_KEEP_SYS_FLAG))) {
+ return DB_SUCCESS;
+ }
/* Check if we have to wait for a lock: enqueue an explicit lock
request if yes */
- rec = btr_cur_get_rec(cursor);
- index = cursor->index;
+ rec_t* rec = btr_cur_get_rec(cursor);
+ dict_index_t* index = cursor->index;
ut_ad(!dict_index_is_online_ddl(index)
|| dict_index_is_clust(index)
|| (flags & BTR_CREATE_FLAG));
+ ut_ad((flags & BTR_NO_UNDO_LOG_FLAG)
+ || !index->table->skip_alter_undo);
+
ut_ad(mtr->is_named_space(index->table->space));
/* Check if there is predicate or GAP lock preventing the insertion */
if (!(flags & BTR_NO_LOCKING_FLAG)) {
- if (dict_index_is_spatial(index)) {
+ if (index->is_spatial()) {
lock_prdt_t prdt;
rtr_mbr_t mbr;
@@ -3240,44 +3243,55 @@ btr_cur_ins_lock_and_undo(
lock_init_prdt_from_mbr(
&prdt, &mbr, 0, NULL);
- err = lock_prdt_insert_check_and_lock(
- flags, rec, btr_cur_get_block(cursor),
- index, thr, mtr, &prdt);
+ if (dberr_t err = lock_prdt_insert_check_and_lock(
+ rec, btr_cur_get_block(cursor),
+ index, thr, mtr, &prdt)) {
+ return err;
+ }
*inherit = false;
} else {
- err = lock_rec_insert_check_and_lock(
- flags, rec, btr_cur_get_block(cursor),
- index, thr, mtr, inherit);
+ ut_ad(!dict_index_is_online_ddl(index)
+ || index->is_primary()
+ || (flags & BTR_CREATE_FLAG));
+ if (dberr_t err = lock_rec_insert_check_and_lock(
+ rec, btr_cur_get_block(cursor),
+ index, thr, mtr, inherit)) {
+ return err;
+ }
}
}
- if (err != DB_SUCCESS
- || !(~flags | (BTR_NO_UNDO_LOG_FLAG | BTR_KEEP_SYS_FLAG))
- || !dict_index_is_clust(index) || dict_index_is_ibuf(index)) {
-
- return(err);
+ if (!index->is_primary() || !page_is_leaf(page_align(rec))) {
+ return DB_SUCCESS;
}
- if (flags & BTR_NO_UNDO_LOG_FLAG) {
- roll_ptr = roll_ptr_t(1) << ROLL_PTR_INSERT_FLAG_POS;
- if (!(flags & BTR_KEEP_SYS_FLAG)) {
-upd_sys:
- dfield_t* r = dtuple_get_nth_field(
- entry, index->db_roll_ptr());
- ut_ad(r->len == DATA_ROLL_PTR_LEN);
- trx_write_roll_ptr(static_cast<byte*>(r->data),
- roll_ptr);
+ constexpr roll_ptr_t dummy_roll_ptr = roll_ptr_t{1}
+ << ROLL_PTR_INSERT_FLAG_POS;
+ roll_ptr_t roll_ptr = dummy_roll_ptr;
+
+ if (!(flags & BTR_NO_UNDO_LOG_FLAG)) {
+ if (dberr_t err = trx_undo_report_row_operation(
+ thr, index, entry, NULL, 0, NULL, NULL,
+ &roll_ptr)) {
+ return err;
}
- } else {
- err = trx_undo_report_row_operation(thr, index, entry,
- NULL, 0, NULL, NULL,
- &roll_ptr);
- if (err == DB_SUCCESS) {
- goto upd_sys;
+
+ if (roll_ptr != dummy_roll_ptr) {
+ dfield_t* r = dtuple_get_nth_field(entry,
+ index->db_trx_id());
+ trx_write_trx_id(static_cast<byte*>(r->data),
+ thr_get_trx(thr)->id);
}
}
- return(err);
+ if (!(flags & BTR_KEEP_SYS_FLAG)) {
+ dfield_t* r = dtuple_get_nth_field(
+ entry, index->db_roll_ptr());
+ ut_ad(r->len == DATA_ROLL_PTR_LEN);
+ trx_write_roll_ptr(static_cast<byte*>(r->data), roll_ptr);
+ }
+
+ return DB_SUCCESS;
}
/**
@@ -3510,7 +3524,9 @@ fail_err:
ut_ad(thr->graph->trx->id
== trx_read_trx_id(
static_cast<const byte*>(
- trx_id->data)));
+ trx_id->data))
+ || static_cast<ins_node_t*>(
+ thr->run_node)->bulk_insert);
}
}
#endif
@@ -5506,6 +5522,7 @@ btr_cur_optimistic_delete_func(
/* MDEV-17383: free metadata BLOBs! */
index->clear_instant_alter();
}
+
page_cur_set_after_last(block,
btr_cur_get_page_cur(cursor));
goto func_exit;
@@ -5723,6 +5740,7 @@ btr_cur_pessimistic_delete(
/* MDEV-17383: free metadata BLOBs! */
index->clear_instant_alter();
}
+
page_cur_set_after_last(
block,
btr_cur_get_page_cur(cursor));
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index e74454e243a..a24b7ed3110 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -3141,11 +3141,9 @@ static ulonglong innodb_prepare_commit_versioned(THD* thd, ulonglong *trx_id)
if (const trx_t* trx = thd_to_trx(thd)) {
*trx_id = trx->id;
- for (trx_mod_tables_t::const_iterator t
- = trx->mod_tables.begin();
- t != trx->mod_tables.end(); t++) {
- if (t->second.is_versioned()) {
- DBUG_ASSERT(t->first->versioned_by_id());
+ for (const auto& t : trx->mod_tables) {
+ if (t.second.is_versioned()) {
+ DBUG_ASSERT(t.first->versioned_by_id());
DBUG_ASSERT(trx->rsegs.m_redo.rseg);
return trx_sys.get_new_trx_id();
@@ -12959,16 +12957,14 @@ inline int ha_innobase::delete_table(const char* name, enum_sql_command sqlcom)
SET AUTOCOMMIT=0;
CREATE TABLE t (PRIMARY KEY (a)) ENGINE=INNODB SELECT 1 AS a UNION
ALL SELECT 1 AS a; */
- trx_mod_tables_t::const_iterator iter;
-
- for (iter = parent_trx->mod_tables.begin();
+ for (auto iter = parent_trx->mod_tables.begin();
iter != parent_trx->mod_tables.end();
++iter) {
dict_table_t* table_to_drop = iter->first;
if (strcmp(norm_name, table_to_drop->name.m_name) == 0) {
- parent_trx->mod_tables.erase(table_to_drop);
+ parent_trx->mod_tables.erase(iter);
break;
}
}
@@ -15113,10 +15109,8 @@ ha_innobase::extra(
break;
}
trx_start_if_not_started(m_prebuilt->trx, true);
- m_prebuilt->trx->mod_tables.insert(
- trx_mod_tables_t::value_type(
- const_cast<dict_table_t*>(m_prebuilt->table),
- 0))
+ m_prebuilt->trx->mod_tables.emplace(
+ const_cast<dict_table_t*>(m_prebuilt->table), 0)
.first->second.set_versioned(0);
break;
case HA_EXTRA_END_ALTER_COPY:
@@ -15126,6 +15120,13 @@ ha_innobase::extra(
trx_register_for_2pc(m_prebuilt->trx);
m_prebuilt->sql_stat_start = true;
break;
+ case HA_EXTRA_IGNORE_INSERT:
+ if (ins_node_t* node = m_prebuilt->ins_node) {
+ if (node->bulk_insert) {
+ m_prebuilt->trx->end_bulk_insert(*node->table);
+ }
+ }
+ break;
default:/* Do nothing */
;
}
@@ -15362,6 +15363,7 @@ ha_innobase::external_lock(
m_prebuilt->hint_need_to_fetch_extra_cols = 0;
reset_template();
+ trx->end_bulk_insert();
switch (m_prebuilt->table->quiesce) {
case QUIESCE_START:
diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h
index 8ad293f6a9a..637bb860146 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, 2020, MariaDB Corporation.
+Copyright (c) 2013, 2021, 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
@@ -1368,6 +1368,11 @@ public:
everything in overflow) size of the longest possible row and index
of a field which made index records too big to fit on a page.*/
inline record_size_info_t record_size_info() const;
+
+ /** Clear the index tree and reinitialize the root page, in the
+ rollback of TRX_UNDO_EMPTY. The BTR_SEG_LEAF is freed and reinitialized.
+ @param thr query thread */
+ void clear(que_thr_t *thr);
};
/** Detach a virtual column from an index.
@@ -1950,6 +1955,9 @@ struct dict_table_t {
char (&tbl_name)[NAME_LEN + 1],
size_t *db_name_len, size_t *tbl_name_len) const;
+ /** Clear the table when rolling back TRX_UNDO_EMPTY */
+ void clear(que_thr_t *thr);
+
private:
/** Initialize instant->field_map.
@param[in] table table definition to copy from */
@@ -2127,6 +2135,11 @@ public:
loading the definition or CREATE TABLE, or ALTER TABLE (prepare,
commit, and rollback phases). */
trx_id_t def_trx_id;
+ /** Last transaction that inserted into an empty table.
+ Updated while holding exclusive table lock and an exclusive
+ latch on the clustered index root page (which must also be
+ an empty leaf page), and an ahi_latch (if btr_search_enabled). */
+ Atomic_relaxed<trx_id_t> bulk_trx_id;
/*!< set of foreign key constraints in the table; these refer to
columns in other tables */
@@ -2298,7 +2311,6 @@ private:
table is NOT allowed until this count gets to zero. MySQL does NOT
itself check the number of open handles at DROP. */
Atomic_counter<uint32_t> n_ref_count;
-
public:
/** List of locks on the table. Protected by lock_sys.mutex. */
table_lock_list_t locks;
diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h
index ac3e467708b..b6c548d5bad 100644
--- a/storage/innobase/include/lock0lock.h
+++ b/storage/innobase/include/lock0lock.h
@@ -250,8 +250,6 @@ for a gap x-lock to the lock queue.
dberr_t
lock_rec_insert_check_and_lock(
/*===========================*/
- ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is
- set, does nothing */
const rec_t* rec, /*!< in: record after which to insert */
buf_block_t* block, /*!< in/out: buffer block of rec */
dict_index_t* index, /*!< in: index */
@@ -386,37 +384,6 @@ lock_clust_rec_read_check_and_lock_alt(
que_thr_t* thr) /*!< in: query thread */
MY_ATTRIBUTE((warn_unused_result));
/*********************************************************************//**
-Checks that a record is seen in a consistent read.
-@return true if sees, or false if an earlier version of the record
-should be retrieved */
-bool
-lock_clust_rec_cons_read_sees(
-/*==========================*/
- const rec_t* rec, /*!< in: user record which should be read or
- passed over by a read cursor */
- dict_index_t* index, /*!< in: clustered index */
- const rec_offs* offsets,/*!< in: rec_get_offsets(rec, index) */
- ReadView* view); /*!< in: consistent read view */
-/*********************************************************************//**
-Checks that a non-clustered index record is seen in a consistent read.
-
-NOTE that a non-clustered index page contains so little information on
-its modifications that also in the case false, the present version of
-rec may be the right, but we must check this from the clustered index
-record.
-
-@return true if certainly sees, or false if an earlier version of the
-clustered index record might be needed */
-bool
-lock_sec_rec_cons_read_sees(
-/*========================*/
- const rec_t* rec, /*!< in: user record which
- should be read or passed over
- by a read cursor */
- const dict_index_t* index, /*!< in: index */
- const ReadView* view) /*!< in: consistent read view */
- MY_ATTRIBUTE((warn_unused_result));
-/*********************************************************************//**
Locks the specified database table in the mode given. If the lock cannot
be granted immediately, the query thread is put to wait.
@return DB_SUCCESS, DB_LOCK_WAIT, or DB_DEADLOCK */
@@ -438,6 +405,17 @@ lock_table_ix_resurrect(
dict_table_t* table, /*!< in/out: table */
trx_t* trx); /*!< in/out: transaction */
+/** Create a table X lock object for a resurrected TRX_UNDO_EMPTY transaction.
+@param table table to be X-locked
+@param trx transaction */
+void lock_table_x_resurrect(dict_table_t *table, trx_t *trx);
+
+/** Release a table X lock after rolling back an insert into an empty table
+(which was covered by a TRX_UNDO_EMPTY record).
+@param table table to be X-unlocked
+@param trx transaction */
+void lock_table_x_unlock(dict_table_t *table, trx_t *trx);
+
/** Sets a lock on a table based on the given mode.
@param[in] table table to lock
@param[in,out] trx transaction
diff --git a/storage/innobase/include/lock0prdt.h b/storage/innobase/include/lock0prdt.h
index 43d68996691..7b101af89a2 100644
--- a/storage/innobase/include/lock0prdt.h
+++ b/storage/innobase/include/lock0prdt.h
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2014, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2018, 2020, MariaDB Corporation.
+Copyright (c) 2018, 2021, 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
@@ -126,8 +126,6 @@ a predicate record.
dberr_t
lock_prdt_insert_check_and_lock(
/*============================*/
- ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is
- set, does nothing */
const rec_t* rec, /*!< in: record after which to insert */
buf_block_t* block, /*!< in/out: buffer block of rec */
dict_index_t* index, /*!< in: index */
diff --git a/storage/innobase/include/read0types.h b/storage/innobase/include/read0types.h
index 521097b5298..37d97ebcaac 100644
--- a/storage/innobase/include/read0types.h
+++ b/storage/innobase/include/read0types.h
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2018, 2020, MariaDB Corporation.
+Copyright (c) 2018, 2021, 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
@@ -140,6 +140,20 @@ loop:
*/
static void check_trx_id_sanity(trx_id_t id, const table_name_t &name);
+ /**
+ Check whether the changes by id are visible.
+ @param[in] id transaction id to check against the view
+ @return whether the view sees the modifications of id.
+ */
+ bool changes_visible(trx_id_t id) const
+ MY_ATTRIBUTE((warn_unused_result))
+ {
+ if (id >= m_low_limit_id)
+ return false;
+ return id < m_up_limit_id ||
+ m_ids.empty() ||
+ !std::binary_search(m_ids.begin(), m_ids.end(), id);
+ }
/**
Check whether the changes by id are visible.
@@ -266,7 +280,8 @@ public:
*/
bool changes_visible(trx_id_t id, const table_name_t &name) const
{ return id == m_creator_trx_id || ReadViewBase::changes_visible(id, name); }
-
+ bool changes_visible(trx_id_t id) const
+ { return id == m_creator_trx_id || ReadViewBase::changes_visible(id); }
/**
A wrapper around ReadViewBase::append().
diff --git a/storage/innobase/include/row0ins.h b/storage/innobase/include/row0ins.h
index 9a16394a052..df12d1ddbc8 100644
--- a/storage/innobase/include/row0ins.h
+++ b/storage/innobase/include/row0ins.h
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, 2020, MariaDB Corporation.
+Copyright (c) 2017, 2021, 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
@@ -207,6 +207,9 @@ struct ins_node_t
and buffers for sys fields in row allocated */
void vers_update_end(row_prebuilt_t *prebuilt, bool history_row);
bool vers_history_row() const; /* true if 'row' is historical */
+
+ /** Bulk insert enabled for this table */
+ bool bulk_insert= false;
};
/** Create an insert object.
diff --git a/storage/innobase/include/row0log.h b/storage/innobase/include/row0log.h
index 5ec4b9c1103..3928aceac1a 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, 2020, MariaDB Corporation.
+Copyright (c) 2017, 2021, 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
@@ -101,10 +101,10 @@ void
row_log_online_op(
/*==============*/
dict_index_t* index, /*!< in/out: index, S or X latched */
- const dtuple_t* tuple, /*!< in: index tuple */
+ 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 __attribute__((nonnull));
+ ATTRIBUTE_COLD;
/******************************************************//**
Gets the error status of the online index rebuild log.
diff --git a/storage/innobase/include/trx0rec.h b/storage/innobase/include/trx0rec.h
index 9aeff6312f6..b377847c73a 100644
--- a/storage/innobase/include/trx0rec.h
+++ b/storage/innobase/include/trx0rec.h
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, 2020, MariaDB Corporation.
+Copyright (c) 2017, 2021, 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
@@ -296,6 +296,13 @@ record */
fields of the record can change */
#define TRX_UNDO_DEL_MARK_REC 14 /* delete marking of a record; fields
do not change */
+/** Bulk insert operation. It is written only when the table is
+under exclusive lock and the clustered index root page latch is being held,
+and the clustered index is empty. Rollback will empty the table and
+free the leaf segment of all indexes, re-create the new
+leaf segment and re-initialize the root page alone. */
+#define TRX_UNDO_EMPTY 15
+
#define TRX_UNDO_CMPL_INFO_MULT 16U /* compilation info is multiplied by
this and ORed to the type above */
#define TRX_UNDO_UPD_EXTERN 128U /* This bit can be ORed to type_cmpl
diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h
index 9d1c574e04d..d9049fce924 100644
--- a/storage/innobase/include/trx0trx.h
+++ b/storage/innobase/include/trx0trx.h
@@ -557,58 +557,73 @@ struct trx_lock_t {
/** Logical first modification time of a table in a transaction */
class trx_mod_table_time_t
{
- /** First modification of the table */
- undo_no_t first;
- /** First modification of a system versioned column */
- undo_no_t first_versioned;
-
- /** Magic value signifying that a system versioned column of a
- table was never modified in a transaction. */
- static const undo_no_t UNVERSIONED = IB_ID_MAX;
-
+ /** Impossible value for trx_t::undo_no */
+ static constexpr undo_no_t NONE= ~undo_no_t{0};
+ /** Theoretical maximum value for trx_t::undo_no.
+ DB_ROLL_PTR is only 7 bytes, so it cannot point to more than
+ this many undo log records. */
+ static constexpr undo_no_t LIMIT= (undo_no_t{1} << (7 * 8)) - 1;
+
+ /** Flag in 'first' to indicate that subsequent operations are
+ covered by a TRX_UNDO_EMPTY record (for the first statement to
+ insert into an empty table) */
+ static constexpr undo_no_t BULK= 1ULL << 63;
+ /** Flag in 'first' to indicate that some operations were
+ covered by a TRX_UNDO_EMPTY record (for the first statement to
+ insert into an empty table) */
+ static constexpr undo_no_t WAS_BULK= 1ULL << 63;
+
+ /** First modification of the table, possibly ORed with BULK */
+ undo_no_t first;
+ /** First modification of a system versioned column (or NONE) */
+ undo_no_t first_versioned= NONE;
public:
- /** Constructor
- @param[in] rows number of modified rows so far */
- trx_mod_table_time_t(undo_no_t rows)
- : first(rows), first_versioned(UNVERSIONED) {}
+ /** Constructor
+ @param rows number of modified rows so far */
+ trx_mod_table_time_t(undo_no_t rows) : first(rows) { ut_ad(rows < LIMIT); }
#ifdef UNIV_DEBUG
- /** Validation
- @param[in] rows number of modified rows so far
- @return whether the object is valid */
- bool valid(undo_no_t rows = UNVERSIONED) const
- {
- return first <= first_versioned && first <= rows;
- }
+ /** Validation
+ @param rows number of modified rows so far
+ @return whether the object is valid */
+ bool valid(undo_no_t rows= NONE) const
+ { auto f= first & LIMIT; return f <= first_versioned && f <= rows; }
#endif /* UNIV_DEBUG */
- /** @return if versioned columns were modified */
- bool is_versioned() const { return first_versioned != UNVERSIONED; }
+ /** @return if versioned columns were modified */
+ bool is_versioned() const { return first_versioned != NONE; }
- /** After writing an undo log record, set is_versioned() if needed
- @param[in] rows number of modified rows so far */
- void set_versioned(undo_no_t rows)
- {
- ut_ad(!is_versioned());
- first_versioned = rows;
- ut_ad(valid());
- }
+ /** After writing an undo log record, set is_versioned() if needed
+ @param rows number of modified rows so far */
+ void set_versioned(undo_no_t rows)
+ {
+ ut_ad(!is_versioned());
+ first_versioned= rows;
+ ut_ad(valid(rows));
+ }
- /** Invoked after partial rollback
- @param[in] limit number of surviving modified rows
- @return whether this should be erased from trx_t::mod_tables */
- bool rollback(undo_no_t limit)
- {
- ut_ad(valid());
- if (first >= limit) {
- return true;
- }
+ /** Notify the start of a bulk insert operation */
+ void start_bulk_insert() { first|= BULK | WAS_BULK; }
- if (first_versioned < limit && is_versioned()) {
- first_versioned = UNVERSIONED;
- }
+ /** Notify the end of a bulk insert operation */
+ void end_bulk_insert() { first&= ~BULK; }
- return false;
- }
+ /** @return whether an insert is covered by TRX_UNDO_EMPTY record */
+ bool is_bulk_insert() const { return first & BULK; }
+ /** @return whether an insert was covered by TRX_UNDO_EMPTY record */
+ bool was_bulk_insert() const { return first & WAS_BULK; }
+
+ /** Invoked after partial rollback
+ @param limit number of surviving modified rows (trx_t::undo_no)
+ @return whether this should be erased from trx_t::mod_tables */
+ bool rollback(undo_no_t limit)
+ {
+ ut_ad(valid());
+ if ((LIMIT & first) >= limit)
+ return true;
+ if (first_versioned < limit)
+ first_versioned= NONE;
+ return false;
+ }
};
/** Collection of persistent tables and their first modification
@@ -1086,11 +1101,34 @@ public:
ut_ad(dict_operation == TRX_DICT_OP_NONE);
}
+ /** This has to be invoked on SAVEPOINT or at the end of a statement.
+ Even if a TRX_UNDO_EMPTY record was written for this table to cover an
+ insert into an empty table, subsequent operations will have to be covered
+ by row-level undo log records, so that ROLLBACK TO SAVEPOINT or a
+ rollback to the start of a statement will work.
+ @param table table on which any preceding bulk insert ended */
+ void end_bulk_insert(const dict_table_t &table)
+ {
+ auto it= mod_tables.find(const_cast<dict_table_t*>(&table));
+ if (it != mod_tables.end())
+ it->second.end_bulk_insert();
+ }
+
+ /** This has to be invoked on SAVEPOINT or at the start of a statement.
+ Even if TRX_UNDO_EMPTY records were written for any table to cover an
+ insert into an empty table, subsequent operations will have to be covered
+ by row-level undo log records, so that ROLLBACK TO SAVEPOINT or a
+ rollback to the start of a statement will work. */
+ void end_bulk_insert()
+ {
+ for (auto& t : mod_tables)
+ t.second.end_bulk_insert();
+ }
private:
- /** Assign a rollback segment for modifying temporary tables.
- @return the assigned rollback segment */
- trx_rseg_t* assign_temp_rseg();
+ /** Assign a rollback segment for modifying temporary tables.
+ @return the assigned rollback segment */
+ trx_rseg_t *assign_temp_rseg();
};
/**
diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc
index 22c2f2bfe00..c51e24b5bad 100644
--- a/storage/innobase/lock/lock0lock.cc
+++ b/storage/innobase/lock/lock0lock.cc
@@ -360,83 +360,6 @@ lock_check_trx_id_sanity(
return true;
}
-/*********************************************************************//**
-Checks that a record is seen in a consistent read.
-@return true if sees, or false if an earlier version of the record
-should be retrieved */
-bool
-lock_clust_rec_cons_read_sees(
-/*==========================*/
- const rec_t* rec, /*!< in: user record which should be read or
- passed over by a read cursor */
- dict_index_t* index, /*!< in: clustered index */
- const rec_offs* offsets,/*!< in: rec_get_offsets(rec, index) */
- ReadView* view) /*!< in: consistent read view */
-{
- ut_ad(dict_index_is_clust(index));
- ut_ad(page_rec_is_user_rec(rec));
- ut_ad(rec_offs_validate(rec, index, offsets));
- ut_ad(!rec_is_metadata(rec, *index));
-
- /* Temp-tables are not shared across connections and multiple
- transactions from different connections cannot simultaneously
- operate on same temp-table and so read of temp-table is
- always consistent read. */
- if (index->table->is_temporary()) {
- return(true);
- }
-
- /* NOTE that we call this function while holding the search
- system latch. */
-
- trx_id_t trx_id = row_get_rec_trx_id(rec, index, offsets);
-
- return(view->changes_visible(trx_id, index->table->name));
-}
-
-/*********************************************************************//**
-Checks that a non-clustered index record is seen in a consistent read.
-
-NOTE that a non-clustered index page contains so little information on
-its modifications that also in the case false, the present version of
-rec may be the right, but we must check this from the clustered index
-record.
-
-@return true if certainly sees, or false if an earlier version of the
-clustered index record might be needed */
-bool
-lock_sec_rec_cons_read_sees(
-/*========================*/
- const rec_t* rec, /*!< in: user record which
- should be read or passed over
- by a read cursor */
- const dict_index_t* index, /*!< in: index */
- const ReadView* view) /*!< in: consistent read view */
-{
- ut_ad(page_rec_is_user_rec(rec));
- ut_ad(!index->is_primary());
- ut_ad(!rec_is_metadata(rec, *index));
-
- /* NOTE that we might call this function while holding the search
- system latch. */
-
- if (index->table->is_temporary()) {
-
- /* Temp-tables are not shared across connections and multiple
- transactions from different connections cannot simultaneously
- operate on same temp-table and so read of temp-table is
- always consistent read. */
-
- return(true);
- }
-
- trx_id_t max_trx_id = page_get_max_trx_id(page_align(rec));
-
- ut_ad(max_trx_id > 0);
-
- return(view->sees(max_trx_id));
-}
-
/**
Creates the lock system at database start.
@@ -2125,7 +2048,9 @@ lock_rec_inherit_to_gap_if_gap_lock(
if (!lock_rec_get_insert_intention(lock)
&& (heap_no == PAGE_HEAP_NO_SUPREMUM
- || !lock_rec_get_rec_not_gap(lock))) {
+ || !lock_rec_get_rec_not_gap(lock))
+ && !lock_table_has(lock->trx, lock->index->table,
+ LOCK_X)) {
lock_rec_add_to_queue(
LOCK_REC | LOCK_GAP | lock_get_mode(lock),
@@ -3598,6 +3523,27 @@ lock_table_ix_resurrect(
mutex->wr_unlock();
}
+/** Create a table X lock object for a resurrected TRX_UNDO_EMPTY transaction.
+@param table table to be X-locked
+@param trx transaction */
+void lock_table_x_resurrect(dict_table_t *table, trx_t *trx)
+{
+ ut_ad(trx->is_recovered);
+ if (lock_table_has(trx, table, LOCK_X))
+ return;
+
+ auto mutex= &trx->mutex;
+ lock_sys.mutex_lock();
+ /* We have to check if the new lock is compatible with any locks
+ other transactions have in the table lock queue. */
+ ut_ad(!lock_table_other_has_incompatible(trx, LOCK_WAIT, table, LOCK_X));
+
+ mutex->wr_lock();
+ lock_table_create(table, LOCK_X, trx);
+ lock_sys.mutex_unlock();
+ mutex->wr_unlock();
+}
+
/*********************************************************************//**
Checks if a waiting table lock request still has to wait in a queue.
@return TRUE if still has to wait */
@@ -3664,6 +3610,32 @@ lock_table_dequeue(
}
}
+/** Release a table X lock after rolling back an insert into an empty table
+(which was covered by a TRX_UNDO_EMPTY record).
+@param table table to be X-unlocked
+@param trx transaction */
+void lock_table_x_unlock(dict_table_t *table, trx_t *trx)
+{
+ ut_ad(!trx->is_recovered);
+
+ lock_sys.mutex_lock();
+
+ for (lock_t*& lock : trx->lock.table_locks)
+ {
+ if (lock && lock->trx == trx && lock->type_mode == (LOCK_TABLE | LOCK_X))
+ {
+ ut_ad(!lock_get_wait(lock));
+ lock_table_dequeue(lock);
+ lock= nullptr;
+ goto func_exit;
+ }
+ }
+ ut_ad("lock not found" == 0);
+
+func_exit:
+ lock_sys.mutex_unlock();
+}
+
/** Sets a lock on a table based on the given mode.
@param[in] table table to lock
@param[in,out] trx transaction
@@ -4752,8 +4724,6 @@ for a gap x-lock to the lock queue.
dberr_t
lock_rec_insert_check_and_lock(
/*===========================*/
- ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is
- set, does nothing */
const rec_t* rec, /*!< in: record after which to insert */
buf_block_t* block, /*!< in/out: buffer block of rec */
dict_index_t* index, /*!< in: index */
@@ -4765,17 +4735,9 @@ lock_rec_insert_check_and_lock(
record */
{
ut_ad(block->frame == page_align(rec));
- ut_ad(!dict_index_is_online_ddl(index)
- || index->is_primary()
- || (flags & BTR_CREATE_FLAG));
ut_ad(mtr->is_named_space(index->table->space));
ut_ad(page_rec_is_leaf(rec));
- if (flags & BTR_NO_LOCKING_FLAG) {
-
- return(DB_SUCCESS);
- }
-
ut_ad(!index->table->is_temporary());
ut_ad(page_is_leaf(block->frame));
diff --git a/storage/innobase/lock/lock0prdt.cc b/storage/innobase/lock/lock0prdt.cc
index 1ba05aa01b0..073ab8e47c3 100644
--- a/storage/innobase/lock/lock0prdt.cc
+++ b/storage/innobase/lock/lock0prdt.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2014, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2018, 2020, MariaDB Corporation.
+Copyright (c) 2018, 2021, 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
@@ -509,8 +509,6 @@ a predicate record.
dberr_t
lock_prdt_insert_check_and_lock(
/*============================*/
- ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is
- set, does nothing */
const rec_t* rec, /*!< in: record after which to insert */
buf_block_t* block, /*!< in/out: buffer block of rec */
dict_index_t* index, /*!< in: index */
@@ -520,14 +518,8 @@ lock_prdt_insert_check_and_lock(
Rectangle */
{
ut_ad(block->frame == page_align(rec));
-
- if (flags & BTR_NO_LOCKING_FLAG) {
-
- return(DB_SUCCESS);
- }
-
ut_ad(!index->table->is_temporary());
- ut_ad(!dict_index_is_clust(index));
+ ut_ad(index->is_spatial());
trx_t* trx = thr_get_trx(thr);
diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc
index 78a1da92736..7fb685efb04 100644
--- a/storage/innobase/row/row0ins.cc
+++ b/storage/innobase/row/row0ins.cc
@@ -44,6 +44,9 @@ Created 4/20/1996 Heikki Tuuri
#include "buf0lru.h"
#include "fts0fts.h"
#include "fts0types.h"
+#ifdef BTR_CUR_HASH_ADAPT
+# include "btr0sea.h"
+#endif
#ifdef WITH_WSREP
#include "wsrep_mysqld.h"
#endif /* WITH_WSREP */
@@ -2530,6 +2533,12 @@ row_ins_index_entry_big_rec(
return(error);
}
+#ifdef HAVE_REPLICATION /* Working around MDEV-24622 */
+extern "C" int thd_is_slave(const MYSQL_THD thd);
+#else
+# define thd_is_slave(thd) 0
+#endif
+
/***************************************************************//**
Tries to insert an entry into a clustered index, ignoring foreign key
constraints. If a record with the same unique key is found, the other
@@ -2564,6 +2573,8 @@ row_ins_clust_index_entry_low(
rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
rec_offs* offsets = offsets_;
rec_offs_init(offsets_);
+ trx_t* trx = thr_get_trx(thr);
+ buf_block_t* block;
DBUG_ENTER("row_ins_clust_index_entry_low");
@@ -2571,7 +2582,7 @@ row_ins_clust_index_entry_low(
ut_ad(!dict_index_is_unique(index)
|| n_uniq == dict_index_get_n_unique(index));
ut_ad(!n_uniq || n_uniq == dict_index_get_n_unique(index));
- ut_ad(!thr_get_trx(thr)->in_rollback);
+ ut_ad(!trx->in_rollback);
mtr_start(&mtr);
@@ -2625,6 +2636,7 @@ row_ins_clust_index_entry_low(
auto_inc, &mtr);
if (err != DB_SUCCESS) {
index->table->file_unreadable = true;
+commit_exit:
mtr.commit();
goto func_exit;
}
@@ -2643,6 +2655,47 @@ row_ins_clust_index_entry_low(
}
#endif /* UNIV_DEBUG */
+ block = btr_cur_get_block(cursor);
+
+ DBUG_EXECUTE_IF("row_ins_row_level", goto skip_bulk_insert;);
+
+ if (!(flags & BTR_NO_UNDO_LOG_FLAG)
+ && page_is_empty(block->frame)
+ && !entry->is_metadata() && !trx->duplicates
+ && !trx->ddl && !trx->internal
+ && block->page.id().page_no() == index->page
+ && !index->table->skip_alter_undo
+ && !trx->is_wsrep() /* FIXME: MDEV-24623 */
+ && !thd_is_slave(trx->mysql_thd) /* FIXME: MDEV-24622 */) {
+ DEBUG_SYNC_C("empty_root_page_insert");
+
+ if (!index->table->is_temporary()) {
+ err = lock_table(0, index->table, LOCK_X, thr);
+
+ if (err != DB_SUCCESS) {
+ trx->error_state = err;
+ goto commit_exit;
+ }
+
+#ifdef BTR_CUR_HASH_ADAPT
+ if (btr_search_enabled) {
+ btr_search_x_lock_all();
+ index->table->bulk_trx_id = trx->id;
+ btr_search_x_unlock_all();
+ } else {
+ index->table->bulk_trx_id = trx->id;
+ }
+#else /* BTR_CUR_HASH_ADAPT */
+ index->table->bulk_trx_id = trx->id;
+#endif /* BTR_CUR_HASH_ADAPT */
+ }
+
+ static_cast<ins_node_t*>(thr->run_node)->bulk_insert = true;
+ }
+
+#ifndef DBUG_OFF
+skip_bulk_insert:
+#endif
if (UNIV_UNLIKELY(entry->info_bits != 0)) {
ut_ad(entry->is_metadata());
ut_ad(flags == BTR_NO_LOCKING_FLAG);
@@ -2653,7 +2706,7 @@ row_ins_clust_index_entry_low(
if (rec_get_info_bits(rec, page_rec_is_comp(rec))
& REC_INFO_MIN_REC_FLAG) {
- thr_get_trx(thr)->error_info = index;
+ trx->error_info = index;
err = DB_DUPLICATE_KEY;
goto err_exit;
}
@@ -2686,7 +2739,7 @@ row_ins_clust_index_entry_low(
/* fall through */
case DB_SUCCESS_LOCKED_REC:
case DB_DUPLICATE_KEY:
- thr_get_trx(thr)->error_info = cursor->index;
+ trx->error_info = cursor->index;
}
} else {
/* Note that the following may return also
@@ -2770,7 +2823,7 @@ do_insert:
log_write_up_to(mtr.commit_lsn(), true););
err = row_ins_index_entry_big_rec(
entry, big_rec, offsets, &offsets_heap, index,
- thr_get_trx(thr)->mysql_thd);
+ trx->mysql_thd);
dtuple_convert_back_big_rec(index, entry, big_rec);
} else {
if (err == DB_SUCCESS
@@ -3734,10 +3787,6 @@ row_ins_step(
goto do_insert;
}
- if (UNIV_LIKELY(!node->table->skip_alter_undo)) {
- trx_write_trx_id(&node->sys_buf[DATA_ROW_ID_LEN], trx->id);
- }
-
if (node->state == INS_NODE_SET_IX_LOCK) {
node->state = INS_NODE_ALLOC_ROW_ID;
@@ -3757,7 +3806,7 @@ row_ins_step(
err = DB_LOCK_WAIT;);
if (err != DB_SUCCESS) {
-
+ node->state = INS_NODE_SET_IX_LOCK;
goto error_handling;
}
diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc
index e4f7be83940..64d5dce09fd 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, 2020, MariaDB Corporation.
+Copyright (c) 2017, 2021, 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
@@ -54,7 +54,9 @@ enum row_tab_op {
/** Update a record in place */
ROW_T_UPDATE,
/** Delete (purge) a record */
- ROW_T_DELETE
+ ROW_T_DELETE,
+ /** Empty the table */
+ ROW_T_EMPTY
};
/** Index record modification operations during online index creation */
@@ -62,7 +64,9 @@ enum row_op {
/** Insert a record */
ROW_OP_INSERT = 0x61,
/** Delete a record */
- ROW_OP_DELETE
+ ROW_OP_DELETE,
+ /** Empy the index */
+ ROW_OP_EMPTY
};
/** Size of the modification log entry header, in bytes */
@@ -328,7 +332,7 @@ void
row_log_online_op(
/*==============*/
dict_index_t* index, /*!< in/out: index, S or X latched */
- const dtuple_t* tuple, /*!< in: index tuple */
+ 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 */
{
@@ -339,8 +343,8 @@ row_log_online_op(
ulint avail_size;
row_log_t* log;
- ut_ad(dtuple_validate(tuple));
- ut_ad(dtuple_get_n_fields(tuple) == dict_index_get_n_fields(index));
+ 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()) {
@@ -353,14 +357,20 @@ row_log_online_op(
row_merge_buf_encode(), because here we do not encode
extra_size+1 (and reserve 0 as the end-of-chunk marker). */
- size = rec_get_converted_size_temp(
- index, tuple->fields, tuple->n_fields, &extra_size);
- ut_ad(size >= extra_size);
- ut_ad(size <= sizeof log->tail.buf);
+ if (!tuple) {
+ mrec_size = 4;
+ extra_size = 0;
+ size = 2;
+ } else {
+ size = rec_get_converted_size_temp(
+ index, tuple->fields, tuple->n_fields, &extra_size);
+ ut_ad(size >= extra_size);
+ ut_ad(size <= sizeof log->tail.buf);
- mrec_size = ROW_LOG_HEADER_SIZE
- + (extra_size >= 0x80) + size
- + (trx_id ? DATA_TRX_ID_LEN : 0);
+ mrec_size = ROW_LOG_HEADER_SIZE
+ + (extra_size >= 0x80) + size
+ + (trx_id ? DATA_TRX_ID_LEN : 0);
+ }
log = index->online_log;
mysql_mutex_lock(&log->mutex);
@@ -389,6 +399,8 @@ row_log_online_op(
*b++ = ROW_OP_INSERT;
trx_write_trx_id(b, trx_id);
b += DATA_TRX_ID_LEN;
+ } else if (!tuple) {
+ *b++ = ROW_OP_EMPTY;
} else {
*b++ = ROW_OP_DELETE;
}
@@ -401,8 +413,14 @@ row_log_online_op(
*b++ = (byte) extra_size;
}
- rec_convert_dtuple_to_temp(
- b + extra_size, index, tuple->fields, tuple->n_fields);
+ if (tuple) {
+ rec_convert_dtuple_to_temp(
+ b + extra_size, index, tuple->fields,
+ tuple->n_fields);
+ } else {
+ memset(b, 0, 2);
+ }
+
b += size;
if (mrec_size >= avail_size) {
@@ -2422,11 +2440,6 @@ row_log_table_apply_op(
*error = DB_SUCCESS;
- /* 3 = 1 (op type) + 1 (extra_size) + at least 1 byte payload */
- if (mrec + 3 >= mrec_end) {
- return(NULL);
- }
-
const bool is_instant = log->is_instant(dup->index);
const mrec_t* const mrec_start = mrec;
@@ -2657,6 +2670,11 @@ row_log_table_apply_op(
thr, new_trx_id_col,
mrec, offsets, offsets_heap, heap, dup, old_pk);
break;
+ case ROW_T_EMPTY:
+ dup->index->online_log->table->clear(thr);
+ log->head.total += 1;
+ next_mrec = mrec;
+ break;
}
ut_ad(log->head.total <= log->tail.total);
@@ -3438,6 +3456,9 @@ row_log_apply_op_low(
}
goto duplicate;
+ case ROW_OP_EMPTY:
+ ut_ad(0);
+ break;
}
} else {
switch (op) {
@@ -3508,6 +3529,9 @@ insert_the_rec:
0, NULL, &mtr);
ut_ad(!big_rec);
break;
+ case ROW_OP_EMPTY:
+ ut_ad(0);
+ break;
}
mem_heap_empty(offsets_heap);
}
@@ -3582,6 +3606,16 @@ row_log_apply_op(
op = static_cast<enum row_op>(*mrec++);
trx_id = 0;
break;
+ case ROW_OP_EMPTY:
+ {
+ mem_heap_t* heap = mem_heap_create(512);
+ que_fork_t* fork = que_fork_create(
+ NULL, NULL, QUE_FORK_MYSQL_INTERFACE, heap);
+ que_thr_t* thr = que_thr_create(fork, heap, nullptr);
+ index->clear(thr);
+ mem_heap_free(heap);
+ return mrec + 4;
+ }
default:
corrupted:
ut_ad(0);
@@ -4024,3 +4058,54 @@ row_log_apply(
DBUG_RETURN(error);
}
+
+/** Notify that the table was emptied by concurrent rollback or purge.
+@param index clustered index */
+static void row_log_table_empty(dict_index_t *index)
+{
+ ut_ad(index->lock.have_any());
+ row_log_t* log= index->online_log;
+ ulint avail_size;
+ if (byte *b= row_log_table_open(log, 1, &avail_size))
+ {
+ *b++ = ROW_T_EMPTY;
+ row_log_table_close(index, b, 1, avail_size);
+ }
+}
+
+void dict_table_t::clear(que_thr_t *thr)
+{
+ bool rebuild= false;
+ for (dict_index_t *index= UT_LIST_GET_FIRST(indexes); index;
+ index= UT_LIST_GET_NEXT(indexes, index))
+ {
+ if (index->type & DICT_FTS)
+ continue;
+
+ switch (dict_index_get_online_status(index)) {
+ case ONLINE_INDEX_ABORTED:
+ case ONLINE_INDEX_ABORTED_DROPPED:
+ continue;
+
+ case ONLINE_INDEX_COMPLETE:
+ break;
+
+ case ONLINE_INDEX_CREATION:
+ index->lock.u_lock(SRW_LOCK_CALL);
+ if (dict_index_get_online_status(index) == ONLINE_INDEX_CREATION)
+ {
+ if (index->is_clust())
+ {
+ row_log_table_empty(index);
+ rebuild= true;
+ }
+ else if (!rebuild)
+ row_log_online_op(index, nullptr, 0);
+ }
+
+ index->lock.u_unlock();
+ }
+
+ index->clear(thr);
+ }
+}
diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc
index 3c9331d9909..33990f619c6 100644
--- a/storage/innobase/row/row0merge.cc
+++ b/storage/innobase/row/row0merge.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2005, 2017, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2014, 2020, MariaDB Corporation.
+Copyright (c) 2014, 2021, 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
@@ -1739,7 +1739,7 @@ row_merge_read_clustered_index(
ut_malloc_nokey(n_index * sizeof *merge_buf));
row_merge_dup_t clust_dup = {index[0], table, col_map, 0};
- dfield_t* prev_fields;
+ dfield_t* prev_fields = nullptr;
const ulint n_uniq = dict_index_get_n_unique(index[0]);
ut_ad(trx->mysql_thd != NULL);
@@ -1838,6 +1838,34 @@ row_merge_read_clustered_index(
btr_pcur_move_to_prev_on_page(&pcur);
}
+ uint64_t n_rows = 0;
+
+ /* Check if the table is supposed to be empty for our read view.
+
+ If we read bulk_trx_id as an older transaction ID, it is not
+ incorrect to check here whether that transaction should be
+ visible to us. If bulk_trx_id is not visible to us, the table
+ must have been empty at an earlier point of time, also in our
+ read view.
+
+ An INSERT would only update bulk_trx_id in
+ row_ins_clust_index_entry_low() if the table really was empty
+ (everything had been purged), when holding a leaf page latch
+ in the clustered index (actually, the root page is the only
+ leaf page in that case).
+
+ We are holding a clustered index leaf page latch here.
+ That will obviously prevent any concurrent INSERT from
+ updating bulk_trx_id while we read it. */
+ if (!online) {
+ } else if (trx_id_t bulk_trx_id = old_table->bulk_trx_id) {
+ ut_ad(trx->read_view.is_open());
+ ut_ad(bulk_trx_id != trx->id);
+ if (!trx->read_view.changes_visible(bulk_trx_id)) {
+ goto func_exit;
+ }
+ }
+
if (old_table != new_table) {
/* The table is being rebuilt. Identify the columns
that were flagged NOT NULL in the new table, so that
@@ -1884,13 +1912,10 @@ row_merge_read_clustered_index(
prev_fields = static_cast<dfield_t*>(
ut_malloc_nokey(n_uniq * sizeof *prev_fields));
mtuple_heap = mem_heap_create(sizeof(mrec_buf_t));
- } else {
- prev_fields = NULL;
}
mach_write_to_8(new_sys_trx_start, trx->id);
mach_write_to_8(new_sys_trx_end, TRX_ID_MAX);
- uint64_t n_rows = 0;
/* Scan the clustered index. */
for (;;) {
@@ -2720,7 +2745,7 @@ all_done:
UT_DELETE(clust_btr_bulk);
}
- if (prev_fields != NULL) {
+ if (prev_fields) {
ut_free(prev_fields);
mem_heap_free(mtuple_heap);
}
diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc
index f5992b877a3..c2c452309d3 100644
--- a/storage/innobase/row/row0mysql.cc
+++ b/storage/innobase/row/row0mysql.cc
@@ -1096,6 +1096,12 @@ row_get_prebuilt_insert_row(
&& prebuilt->ins_node->entry_list.size()
== UT_LIST_GET_LEN(table->indexes)) {
+ if (prebuilt->ins_node->bulk_insert
+ && prebuilt->ins_node->trx_id
+ != prebuilt->trx->id) {
+ prebuilt->ins_node->bulk_insert= false;
+ }
+
return(prebuilt->ins_node->row);
}
@@ -1409,6 +1415,7 @@ row_insert_for_mysql(
prebuilt->sql_stat_start = FALSE;
} else {
node->state = INS_NODE_ALLOC_ROW_ID;
+ node->trx_id = trx->id;
}
thr->start_running();
@@ -1437,7 +1444,8 @@ error_exit:
if (was_lock_wait) {
ut_ad(node->state == INS_NODE_INSERT_ENTRIES
- || node->state == INS_NODE_ALLOC_ROW_ID);
+ || node->state == INS_NODE_ALLOC_ROW_ID
+ || node->state == INS_NODE_SET_IX_LOCK);
goto run_again;
}
diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc
index 5ddca6a9d3a..0ddd4b059d1 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, 2020, MariaDB Corporation.
+Copyright (c) 2017, 2021, 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
@@ -892,6 +892,7 @@ row_purge_parse_undo_rec(
switch (type) {
case TRX_UNDO_RENAME_TABLE:
return false;
+ case TRX_UNDO_EMPTY:
case TRX_UNDO_INSERT_METADATA:
case TRX_UNDO_INSERT_REC:
/* These records do not store any transaction identifier.
@@ -982,6 +983,9 @@ err_exit:
if (type == TRX_UNDO_INSERT_METADATA) {
node->ref = &trx_undo_metadata;
return(true);
+ } else if (type == TRX_UNDO_EMPTY) {
+ node->ref = nullptr;
+ return true;
}
ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
@@ -1039,6 +1043,8 @@ row_purge_record_func(
ut_ad(!trx_undo_roll_ptr_is_insert(node->roll_ptr));
switch (node->rec_type) {
+ case TRX_UNDO_EMPTY:
+ break;
case TRX_UNDO_DEL_MARK_REC:
purged = row_purge_del_mark(node);
if (purged) {
diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc
index 419a2e628aa..6e3a5e7613f 100644
--- a/storage/innobase/row/row0sel.cc
+++ b/storage/innobase/row/row0sel.cc
@@ -2,7 +2,7 @@
Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc.
-Copyright (c) 2015, 2020, MariaDB Corporation.
+Copyright (c) 2015, 2021, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -875,6 +875,25 @@ row_sel_test_other_conds(
return(TRUE);
}
+/** Check that a clustered index record is visible in a consistent read view.
+@param rec clustered index record (in leaf page, or in memory)
+@param index clustered index
+@param offsets rec_get_offsets(rec, index)
+@param view consistent read view
+@return whether rec is visible in view */
+static bool row_sel_clust_sees(const rec_t *rec, const dict_index_t &index,
+ const rec_offs *offsets, const ReadView &view)
+{
+ ut_ad(index.is_primary());
+ ut_ad(page_rec_is_user_rec(rec));
+ ut_ad(rec_offs_validate(rec, &index, offsets));
+ ut_ad(!rec_is_metadata(rec, index));
+ ut_ad(!index.table->is_temporary());
+
+ return view.changes_visible(row_get_rec_trx_id(rec, &index, offsets),
+ index.table->name);
+}
+
/*********************************************************************//**
Retrieves the clustered index record corresponding to a record in a
non-clustered index. Does the necessary locking.
@@ -977,8 +996,8 @@ row_sel_get_clust_rec(
old_vers = NULL;
- if (!lock_clust_rec_cons_read_sees(clust_rec, index, offsets,
- node->read_view)) {
+ if (!row_sel_clust_sees(clust_rec, *index, offsets,
+ *node->read_view)) {
err = row_sel_build_prev_vers(
node->read_view, index, clust_rec,
@@ -1450,7 +1469,9 @@ row_sel_try_search_shortcut(
{
dict_index_t* index = plan->index;
+ ut_ad(!index->table->is_temporary());
ut_ad(node->read_view);
+ ut_ad(node->read_view->is_open());
ut_ad(plan->unique_search);
ut_ad(!plan->must_get_clust);
@@ -1474,6 +1495,13 @@ exhausted:
return(SEL_EXHAUSTED);
}
+ if (trx_id_t bulk_trx_id = index->table->bulk_trx_id) {
+ /* See row_search_mvcc() for a comment on bulk_trx_id */
+ if (!node->read_view->changes_visible(bulk_trx_id)) {
+ goto exhausted;
+ }
+ }
+
/* This is a non-locking consistent read: if necessary, fetch
a previous version of the record */
@@ -1485,14 +1513,16 @@ exhausted:
ULINT_UNDEFINED, &heap);
if (dict_index_is_clust(index)) {
- if (!lock_clust_rec_cons_read_sees(rec, index, offsets,
- node->read_view)) {
+ if (!row_sel_clust_sees(rec, *index, offsets,
+ *node->read_view)) {
+ goto retry;
+ }
+ } else if (!srv_read_only_mode) {
+ trx_id_t trx_id = page_get_max_trx_id(page_align(rec));
+ ut_ad(trx_id);
+ if (!node->read_view->sees(trx_id)) {
goto retry;
}
- } else if (!srv_read_only_mode
- && !lock_sec_rec_cons_read_sees(
- rec, index, node->read_view)) {
- goto retry;
}
if (rec_get_deleted_flag(rec, dict_table_is_comp(plan->table))) {
@@ -1541,7 +1571,6 @@ row_sel(
rec_t* rec;
rec_t* old_vers;
rec_t* clust_rec;
- ibool consistent_read;
/* The following flag becomes TRUE when we are doing a
consistent read from a non-clustered index and we must look
@@ -1564,21 +1593,11 @@ row_sel(
rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
rec_offs* offsets = offsets_;
rec_offs_init(offsets_);
+ const trx_t* trx = thr_get_trx(thr);
ut_ad(thr->run_node == node);
-
- if (node->read_view) {
- /* In consistent reads, we try to do with the hash index and
- not to use the buffer page get. This is to reduce memory bus
- load resulting from semaphore operations. The search latch
- will be s-locked when we access an index with a unique search
- condition, but not locked when we access an index with a
- less selective search condition. */
-
- consistent_read = TRUE;
- } else {
- consistent_read = FALSE;
- }
+ ut_ad(!node->read_view || node->read_view == &trx->read_view);
+ ut_ad(!node->read_view || node->read_view->is_open());
table_loop:
/* TABLE LOOP
@@ -1613,7 +1632,7 @@ table_loop:
mtr.start();
#ifdef BTR_CUR_HASH_ADAPT
- if (consistent_read && plan->unique_search && !plan->pcur_is_open
+ if (node->read_view && plan->unique_search && !plan->pcur_is_open
&& !plan->must_get_clust) {
switch (row_sel_try_search_shortcut(node, plan, &mtr)) {
case SEL_FOUND:
@@ -1658,6 +1677,15 @@ table_loop:
}
}
+ if (!node->read_view
+ || trx->isolation_level == TRX_ISO_READ_UNCOMMITTED) {
+ } else if (trx_id_t bulk_trx_id = index->table->bulk_trx_id) {
+ /* See row_search_mvcc() for a comment on bulk_trx_id */
+ if (!trx->read_view.changes_visible(bulk_trx_id)) {
+ goto table_exhausted;
+ }
+ }
+
rec_loop:
/* RECORD LOOP
-----------
@@ -1689,12 +1717,9 @@ rec_loop:
and it might be that these new records should appear in the
search result set, resulting in the phantom problem. */
- if (!consistent_read) {
+ if (!node->read_view) {
rec_t* next_rec = page_rec_get_next(rec);
unsigned lock_type;
- trx_t* trx;
-
- trx = thr_get_trx(thr);
offsets = rec_get_offsets(next_rec, index, offsets,
true,
@@ -1752,16 +1777,13 @@ skip_lock:
goto next_rec;
}
- if (!consistent_read) {
+ if (!node->read_view) {
/* Try to place a lock on the index record */
unsigned lock_type;
- trx_t* trx;
offsets = rec_get_offsets(rec, index, offsets, true,
ULINT_UNDEFINED, &heap);
- trx = thr_get_trx(thr);
-
/* At READ UNCOMMITTED or READ COMMITTED isolation level,
we lock only the record, i.e., next-key locking is
not used. */
@@ -1845,15 +1867,14 @@ skip_lock:
offsets = rec_get_offsets(rec, index, offsets, true,
ULINT_UNDEFINED, &heap);
- if (consistent_read) {
+ if (node->read_view) {
/* This is a non-locking consistent read: if necessary, fetch
a previous version of the record */
if (dict_index_is_clust(index)) {
-
- if (!lock_clust_rec_cons_read_sees(
- rec, index, offsets, node->read_view)) {
-
+ if (!node->read_view->changes_visible(
+ row_get_rec_trx_id(rec, index, offsets),
+ index->table->name)) {
err = row_sel_build_prev_vers(
node->read_view, index, rec,
&offsets, &heap, &plan->old_vers_heap,
@@ -1900,11 +1921,12 @@ skip_lock:
rec = old_vers;
}
- } else if (!srv_read_only_mode
- && !lock_sec_rec_cons_read_sees(
- rec, index, node->read_view)) {
-
- cons_read_requires_clust_rec = TRUE;
+ } else if (!srv_read_only_mode) {
+ trx_id_t trx_id = page_get_max_trx_id(page_align(rec));
+ ut_ad(trx_id);
+ if (!node->read_view->sees(trx_id)) {
+ cons_read_requires_clust_rec = TRUE;
+ }
}
}
@@ -1970,7 +1992,7 @@ skip_lock:
if (clust_rec == NULL) {
/* The record did not exist in the read view */
- ut_ad(consistent_read);
+ ut_ad(node->read_view);
goto next_rec;
}
@@ -3403,13 +3425,13 @@ Row_sel_get_clust_rec_for_mysql::operator()(
old_vers = NULL;
- /* If the isolation level allows reading of uncommitted data,
- then we never look for an earlier version */
-
- if (trx->isolation_level > TRX_ISO_READ_UNCOMMITTED
- && !lock_clust_rec_cons_read_sees(
- clust_rec, clust_index, *offsets,
- &trx->read_view)) {
+ if (trx->isolation_level == TRX_ISO_READ_UNCOMMITTED
+ || clust_index->table->is_temporary()) {
+ /* If the isolation level allows reading of
+ uncommitted data, then we never look for an
+ earlier version */
+ } else if (!row_sel_clust_sees(clust_rec, *clust_index,
+ *offsets, trx->read_view)) {
const buf_page_t& bpage = btr_pcur_get_block(
prebuilt->clust_pcur)->page;
@@ -3847,8 +3869,10 @@ row_sel_try_search_shortcut_for_mysql(
trx_t* trx = prebuilt->trx;
const rec_t* rec;
- ut_ad(dict_index_is_clust(index));
+ ut_ad(index->is_primary());
+ ut_ad(!index->table->is_temporary());
ut_ad(!prebuilt->templ_contains_blob);
+ ut_ad(trx->read_view.is_open());
srw_lock* ahi_latch = btr_search_sys.get_latch(*index);
ahi_latch->rd_lock(SRW_LOCK_CALL);
@@ -3872,14 +3896,21 @@ exhausted:
return(SEL_EXHAUSTED);
}
+ if (trx->isolation_level == TRX_ISO_READ_UNCOMMITTED) {
+ } else if (trx_id_t bulk_trx_id = index->table->bulk_trx_id) {
+ /* See row_search_mvcc() for a comment on bulk_trx_id */
+ if (!trx->read_view.changes_visible(bulk_trx_id)) {
+ goto exhausted;
+ }
+ }
+
/* This is a non-locking consistent read: if necessary, fetch
a previous version of the record */
*offsets = rec_get_offsets(rec, index, *offsets, true,
ULINT_UNDEFINED, heap);
- if (!lock_clust_rec_cons_read_sees(rec, index, *offsets,
- &trx->read_view)) {
+ if (!row_sel_clust_sees(rec, *index, *offsets, trx->read_view)) {
goto retry;
}
@@ -4413,6 +4444,7 @@ early_not_found:
&& unique_search
&& btr_search_enabled
&& dict_index_is_clust(index)
+ && !index->table->is_temporary()
&& !prebuilt->templ_contains_blob
&& !prebuilt->used_in_HANDLER
&& (prebuilt->mysql_row_len < srv_page_size / 8)) {
@@ -4709,6 +4741,50 @@ wait_table_again:
}
}
+ /* Check if the table is supposed to be empty for our read view.
+
+ If we read bulk_trx_id as an older transaction ID, it is not
+ incorrect to check here whether that transaction should be
+ visible to us. If bulk_trx_id is not visible to us, the table
+ must have been empty at an earlier point of time, also in our
+ read view.
+
+ An INSERT would only update bulk_trx_id in
+ row_ins_clust_index_entry_low() if the table really was empty
+ (everything had been purged), when holding a leaf page latch
+ in the clustered index (actually, the root page is the only
+ leaf page in that case).
+
+ We are already holding a leaf page latch here, either
+ in a secondary index or in a clustered index.
+
+ If we are holding a clustered index page latch, there clearly
+ is no potential for race condition with a concurrent INSERT:
+ such INSERT would be blocked by us.
+
+ If we are holding a secondary index page latch, then we are
+ not directly blocking a concurrent INSERT that might update
+ bulk_trx_id to something that does not exist in our read view.
+ But, in that case, the entire table (all indexes) must have
+ been empty. So, even if our read below missed the update of
+ index->table->bulk_trx_id, we can safely proceed to reading
+ the empty secondary index page. Our latch will prevent the
+ INSERT from proceeding to that page. It will first modify
+ the clustered index. Also, we may only look up something in
+ the clustered index if the secondary index page is not empty
+ to begin with. So, only if the table is corrupted
+ (the clustered index is empty but the secondary index is not)
+ we could return corrupted results. */
+ if (trx->isolation_level == TRX_ISO_READ_UNCOMMITTED
+ || !trx->read_view.is_open()) {
+ } else if (trx_id_t bulk_trx_id = index->table->bulk_trx_id) {
+ if (!trx->read_view.changes_visible(bulk_trx_id)) {
+ trx->op_info = "";
+ err = DB_END_OF_INDEX;
+ goto normal_return;
+ }
+ }
+
rec_loop:
DEBUG_SYNC_C("row_search_rec_loop");
if (trx_is_interrupted(trx)) {
@@ -5136,6 +5212,7 @@ no_gap_lock:
a previous version of the record */
if (trx->isolation_level == TRX_ISO_READ_UNCOMMITTED
+ || prebuilt->table->is_temporary()
|| prebuilt->table->no_rollback()) {
/* Do nothing: we let a non-locking SELECT read the
@@ -5148,8 +5225,8 @@ no_gap_lock:
high force recovery level set, we try to avoid crashes
by skipping this lookup */
- if (!lock_clust_rec_cons_read_sees(
- rec, index, offsets, &trx->read_view)) {
+ if (!row_sel_clust_sees(rec, *index, offsets,
+ trx->read_view)) {
ut_ad(srv_force_recovery
< SRV_FORCE_NO_UNDO_LOG_SCAN);
rec_t* old_vers;
@@ -5184,9 +5261,13 @@ no_gap_lock:
ut_ad(!dict_index_is_clust(index));
- if (!srv_read_only_mode
- && !lock_sec_rec_cons_read_sees(
- rec, index, &trx->read_view)) {
+ if (!srv_read_only_mode) {
+ trx_id_t trx_id = page_get_max_trx_id(
+ page_align(rec));
+ ut_ad(trx_id);
+ if (trx->read_view.sees(trx_id)) {
+ goto locks_ok;
+ }
/* We should look at the clustered index.
However, as this is a non-locking read,
we can skip the clustered index lookup if
diff --git a/storage/innobase/row/row0uins.cc b/storage/innobase/row/row0uins.cc
index 492c2f35e62..88db631676f 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, 2020, MariaDB Corporation.
+Copyright (c) 2017, 2021, 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
@@ -384,6 +384,7 @@ static bool row_undo_ins_parse_undo_rec(undo_node_t* node, bool dict_locked)
goto close_table;
case TRX_UNDO_INSERT_METADATA:
case TRX_UNDO_INSERT_REC:
+ case TRX_UNDO_EMPTY:
break;
case TRX_UNDO_RENAME_TABLE:
dict_table_t* table = node->table;
@@ -420,11 +421,16 @@ close_table:
clust_index = dict_table_get_first_index(node->table);
if (clust_index != NULL) {
- if (node->rec_type == TRX_UNDO_INSERT_REC) {
+ switch (node->rec_type) {
+ case TRX_UNDO_INSERT_REC:
ptr = trx_undo_rec_get_row_ref(
ptr, clust_index, &node->ref,
node->heap);
- } else {
+ break;
+ case TRX_UNDO_EMPTY:
+ node->ref = nullptr;
+ return true;
+ default:
node->ref = &trx_undo_metadata;
if (!row_undo_search_clust_to_pcur(node)) {
/* An error probably occurred during
@@ -596,6 +602,11 @@ row_undo_ins(
log_free_check();
ut_ad(!node->table->is_temporary());
err = row_undo_ins_remove_clust_rec(node);
+ break;
+ case TRX_UNDO_EMPTY:
+ node->table->clear(thr);
+ err = DB_SUCCESS;
+ break;
}
dict_table_close(node->table, dict_locked, FALSE);
diff --git a/storage/innobase/row/row0undo.cc b/storage/innobase/row/row0undo.cc
index 375de331255..960d83d32ab 100644
--- a/storage/innobase/row/row0undo.cc
+++ b/storage/innobase/row/row0undo.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, 2020, MariaDB Corporation.
+Copyright (c) 2017, 2021, 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
@@ -362,6 +362,12 @@ static bool row_undo_rec_get(undo_node_t* node)
mtr.commit();
switch (trx_undo_rec_get_type(node->undo_rec)) {
+ case TRX_UNDO_EMPTY:
+ /* This record type was introduced in MDEV-515 bulk insert,
+ which was implemented after MDEV-12288 removed the
+ insert_undo log. */
+ ut_ad(undo == update || undo == temp);
+ goto insert_like;
case TRX_UNDO_INSERT_METADATA:
/* This record type was introduced in MDEV-11369
instant ADD COLUMN, which was implemented after
@@ -374,6 +380,7 @@ static bool row_undo_rec_get(undo_node_t* node)
ut_ad(undo == insert || undo == update);
/* fall through */
case TRX_UNDO_INSERT_REC:
+ insert_like:
ut_ad(undo == insert || undo == update || undo == temp);
node->roll_ptr |= 1ULL << ROLL_PTR_INSERT_FLAG_POS;
node->state = undo == temp
diff --git a/storage/innobase/trx/trx0rec.cc b/storage/innobase/trx/trx0rec.cc
index 2503a2bd3aa..25c6a7f9ebb 100644
--- a/storage/innobase/trx/trx0rec.cc
+++ b/storage/innobase/trx/trx0rec.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2019, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, 2020, MariaDB Corporation.
+Copyright (c) 2017, 2021, 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
@@ -38,6 +38,7 @@ Created 3/26/1996 Heikki Tuuri
#include "trx0rseg.h"
#include "row0row.h"
#include "row0mysql.h"
+#include "row0ins.h"
/** The search tuple corresponding to TRX_UNDO_INSERT_METADATA. */
const dtuple_t trx_undo_metadata = {
@@ -371,19 +372,24 @@ trx_undo_report_insert_virtual(
return(true);
}
-/**********************************************************************//**
-Reports in the undo log of an insert of a clustered index record.
+/** Reports in the undo log of an insert of a clustered index record.
+@param undo_block undo log page
+@param trx transaction
+@param index clustered index
+@param clust_entry index entry which will be inserted to the
+ clustered index
+@param mtr mini-transaction
+@param write_empty write empty table undo log record
@return offset of the inserted entry on the page if succeed, 0 if fail */
static
uint16_t
trx_undo_page_report_insert(
-/*========================*/
- buf_block_t* undo_block, /*!< in: undo log page */
- trx_t* trx, /*!< in: transaction */
- dict_index_t* index, /*!< in: clustered index */
- const dtuple_t* clust_entry, /*!< in: index entry which will be
- inserted to the clustered index */
- mtr_t* mtr) /*!< in: mtr */
+ buf_block_t* undo_block,
+ trx_t* trx,
+ dict_index_t* index,
+ const dtuple_t* clust_entry,
+ mtr_t* mtr,
+ bool write_empty)
{
ut_ad(index->is_primary());
/* MariaDB 10.3.1+ in trx_undo_page_init() always initializes
@@ -411,6 +417,13 @@ trx_undo_page_report_insert(
*ptr++ = TRX_UNDO_INSERT_REC;
ptr += mach_u64_write_much_compressed(ptr, trx->undo_no);
ptr += mach_u64_write_much_compressed(ptr, index->table->id);
+
+ if (write_empty) {
+ /* Table is in bulk operation */
+ undo_block->frame[first_free + 2] = TRX_UNDO_EMPTY;
+ goto done;
+ }
+
/*----------------------------------------*/
/* Store then the fields required to uniquely determine the record
to be inserted in the clustered index */
@@ -488,7 +501,7 @@ trx_undo_rec_get_pars(
type_cmpl &= ~TRX_UNDO_UPD_EXTERN;
*type = type_cmpl & (TRX_UNDO_CMPL_INFO_MULT - 1);
ut_ad(*type >= TRX_UNDO_RENAME_TABLE);
- ut_ad(*type <= TRX_UNDO_DEL_MARK_REC);
+ ut_ad(*type <= TRX_UNDO_EMPTY);
*cmpl_info = type_cmpl / TRX_UNDO_CMPL_INFO_MULT;
*undo_no = mach_read_next_much_compressed(&ptr);
@@ -1965,7 +1978,6 @@ trx_undo_report_row_operation(
undo log record */
{
trx_t* trx;
- mtr_t mtr;
#ifdef UNIV_DEBUG
int loop_count = 0;
#endif /* UNIV_DEBUG */
@@ -1981,6 +1993,34 @@ trx_undo_report_row_operation(
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
ut_ad(!trx->in_rollback);
+ /* We must determine if this is the first time when this
+ transaction modifies this table. */
+ auto m = trx->mod_tables.emplace(index->table, trx->undo_no);
+ ut_ad(m.first->second.valid(trx->undo_no));
+
+ bool bulk = !rec;
+
+ if (!bulk) {
+ /* An UPDATE or DELETE must not be covered by an
+ earlier start_bulk_insert(). */
+ ut_ad(!m.first->second.is_bulk_insert());
+ } else if (m.first->second.is_bulk_insert()) {
+ /* Above, the emplace() tried to insert an object with
+ !is_bulk_insert(). Only an explicit start_bulk_insert()
+ (below) can set the flag. */
+ ut_ad(!m.second);
+ /* We already wrote a TRX_UNDO_EMPTY record. */
+ return DB_SUCCESS;
+ } else if (m.second
+ && thr->run_node
+ && que_node_get_type(thr->run_node) == QUE_NODE_INSERT
+ && static_cast<ins_node_t*>(thr->run_node)->bulk_insert) {
+ m.first->second.start_bulk_insert();
+ } else {
+ bulk = false;
+ }
+
+ mtr_t mtr;
mtr.start();
trx_undo_t** pundo;
trx_rseg_t* rseg;
@@ -2002,10 +2042,11 @@ trx_undo_report_row_operation(
buf_block_t* undo_block = trx_undo_assign_low(trx, rseg, pundo,
&err, &mtr);
trx_undo_t* undo = *pundo;
-
ut_ad((err == DB_SUCCESS) == (undo_block != NULL));
if (UNIV_UNLIKELY(undo_block == NULL)) {
- goto err_exit;
+err_exit:
+ mtr.commit();
+ return err;
}
ut_ad(undo != NULL);
@@ -2013,7 +2054,8 @@ trx_undo_report_row_operation(
do {
uint16_t offset = !rec
? trx_undo_page_report_insert(
- undo_block, trx, index, clust_entry, &mtr)
+ undo_block, trx, index, clust_entry, &mtr,
+ bulk)
: trx_undo_page_report_modify(
undo_block, trx, index, rec, offsets, update,
cmpl_info, clust_entry, &mtr);
@@ -2051,6 +2093,12 @@ trx_undo_report_row_operation(
trx_undo_free_last_page(undo, &mtr);
mysql_mutex_unlock(&rseg->mutex);
+ if (m.second) {
+ /* We are not going to modify
+ this table after all. */
+ trx->mod_tables.erase(m.first);
+ }
+
err = DB_UNDO_RECORD_TOO_BIG;
goto err_exit;
} else {
@@ -2082,28 +2130,24 @@ trx_undo_report_row_operation(
ut_ad(!undo->empty());
if (!is_temp) {
- const undo_no_t limit = undo->top_undo_no;
- /* Determine if this is the first time
- when this transaction modifies a
- system-versioned column in this table. */
- trx_mod_table_time_t& time
- = trx->mod_tables.insert(
- trx_mod_tables_t::value_type(
- index->table, limit))
- .first->second;
- ut_ad(time.valid(limit));
+ trx_mod_table_time_t& time = m.first->second;
+ ut_ad(time.valid(undo->top_undo_no));
if (!time.is_versioned()
&& index->table->versioned_by_id()
&& (!rec /* INSERT */
|| (update
&& update->affects_versioned()))) {
- time.set_versioned(limit);
+ time.set_versioned(undo->top_undo_no);
}
}
- *roll_ptr = trx_undo_build_roll_ptr(
- !rec, rseg->id, undo->top_page_no, offset);
+ if (!bulk) {
+ *roll_ptr = trx_undo_build_roll_ptr(
+ !rec, rseg->id, undo->top_page_no,
+ offset);
+ }
+
return(DB_SUCCESS);
}
@@ -2136,10 +2180,7 @@ trx_undo_report_row_operation(
/* Did not succeed: out of space */
err = DB_OUT_OF_FILE_SPACE;
-
-err_exit:
- mtr_commit(&mtr);
- return(err);
+ goto err_exit;
}
/*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/
diff --git a/storage/innobase/trx/trx0roll.cc b/storage/innobase/trx/trx0roll.cc
index 2967281382d..cf51f871d05 100644
--- a/storage/innobase/trx/trx0roll.cc
+++ b/storage/innobase/trx/trx0roll.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2016, 2020, MariaDB Corporation.
+Copyright (c) 2016, 2021, 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
@@ -137,7 +137,11 @@ inline void trx_t::rollback_low(trx_savept_t *savept)
trx_mod_tables_t::iterator j= i++;
ut_ad(j->second.valid());
if (j->second.rollback(limit))
+ {
+ if (j->second.was_bulk_insert() && !j->first->is_temporary())
+ lock_table_x_unlock(j->first, this);
mod_tables.erase(j);
+ }
}
lock.que_state= TRX_QUE_RUNNING;
MONITOR_INC(MONITOR_TRX_ROLLBACK_SAVEPOINT);
@@ -536,6 +540,8 @@ trx_savepoint_for_mysql(
UT_LIST_ADD_LAST(trx->trx_savepoints, savep);
+ trx->end_bulk_insert();
+
return(DB_SUCCESS);
}
diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc
index 0f3316178c9..2ba782e2f1c 100644
--- a/storage/innobase/trx/trx0trx.cc
+++ b/storage/innobase/trx/trx0trx.cc
@@ -64,12 +64,6 @@ const byte timestamp_max_bytes[7] = {
static const ulint MAX_DETAILED_ERROR_LEN = 256;
-/** Set of table_id */
-typedef std::set<
- table_id_t,
- std::less<table_id_t>,
- ut_allocator<table_id_t> > table_id_set;
-
/*************************************************************//**
Set detailed error message for the transaction. */
void
@@ -563,9 +557,6 @@ trx_resurrect_table_locks(
trx_t* trx, /*!< in/out: transaction */
const trx_undo_t* undo) /*!< in: undo log */
{
- mtr_t mtr;
- table_id_set tables;
-
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE) ||
trx_state_eq(trx, TRX_STATE_PREPARED));
ut_ad(undo->rseg == trx->rsegs.m_redo.rseg);
@@ -574,7 +565,9 @@ trx_resurrect_table_locks(
return;
}
- mtr_start(&mtr);
+ mtr_t mtr;
+ std::map<table_id_t, bool> tables;
+ mtr.start();
/* trx_rseg_mem_create() may have acquired an X-latch on this
page, so we cannot acquire an S-latch. */
@@ -599,19 +592,19 @@ trx_resurrect_table_locks(
trx_undo_rec_get_pars(
undo_rec, &type, &cmpl_info,
&updated_extern, &undo_no, &table_id);
- tables.insert(table_id);
+
+ tables.emplace(table_id, type == TRX_UNDO_EMPTY);
undo_rec = trx_undo_get_prev_rec(
block, page_offset(undo_rec), undo->hdr_page_no,
undo->hdr_offset, false, &mtr);
} while (undo_rec);
- mtr_commit(&mtr);
+ mtr.commit();
- for (table_id_set::const_iterator i = tables.begin();
- i != tables.end(); i++) {
+ for (auto p : tables) {
if (dict_table_t* table = dict_table_open_on_id(
- *i, FALSE, DICT_TABLE_OP_LOAD_TABLESPACE)) {
+ p.first, FALSE, DICT_TABLE_OP_LOAD_TABLESPACE)) {
if (!table->is_readable()) {
dict_sys.mutex_lock();
dict_table_close(table, TRUE, FALSE);
@@ -621,11 +614,14 @@ trx_resurrect_table_locks(
}
if (trx->state == TRX_STATE_PREPARED) {
- trx->mod_tables.insert(
- trx_mod_tables_t::value_type(table,
- 0));
+ trx->mod_tables.emplace(table, 0);
+ }
+
+ if (p.second) {
+ lock_table_x_resurrect(table, trx);
+ } else {
+ lock_table_ix_resurrect(table, trx);
}
- lock_table_ix_resurrect(table, trx);
DBUG_LOG("ib_trx",
"resurrect " << ib::hex(trx->id)
@@ -1230,7 +1226,6 @@ trx_update_mod_tables_timestamp(
expensive here */
const time_t now = time(NULL);
- trx_mod_tables_t::const_iterator end = trx->mod_tables.end();
#if defined SAFE_MUTEX && defined UNIV_DEBUG
const bool preserve_tables = !innodb_evict_tables_on_commit_debug
|| trx->is_recovered /* avoid trouble with XA recovery */
@@ -1242,10 +1237,7 @@ trx_update_mod_tables_timestamp(
;
#endif
- for (trx_mod_tables_t::const_iterator it = trx->mod_tables.begin();
- it != end;
- ++it) {
-
+ for (const auto& p : trx->mod_tables) {
/* This could be executed by multiple threads concurrently
on the same table object. This is fine because time_t is
word size or less. And _purely_ _theoretically_, even if
@@ -1254,10 +1246,11 @@ trx_update_mod_tables_timestamp(
"garbage" in table->update_time is justified because
protecting it with a latch here would be too performance
intrusive. */
- dict_table_t* table = it->first;
+ dict_table_t* table = p.first;
table->update_time = now;
#if defined SAFE_MUTEX && defined UNIV_DEBUG
if (preserve_tables || table->get_ref_count()
+ || table->is_temporary()
|| UT_LIST_GET_LEN(table->locks)) {
/* do not evict when committing DDL operations
or if some other transaction is holding the
@@ -1744,6 +1737,7 @@ trx_mark_sql_stat_end(
fts_savepoint_laststmt_refresh(trx);
}
+ trx->end_bulk_insert();
return;
}
diff --git a/storage/mroonga/ha_mroonga.cpp b/storage/mroonga/ha_mroonga.cpp
index 4747495fd22..532690883e4 100644
--- a/storage/mroonga/ha_mroonga.cpp
+++ b/storage/mroonga/ha_mroonga.cpp
@@ -593,6 +593,9 @@ static const char *mrn_inspect_extra_function(enum ha_extra_function operation)
inspected = "HA_EXTRA_NO_AUTOINC_LOCKING";
break;
#endif
+ case HA_EXTRA_IGNORE_INSERT:
+ inspected = "HA_EXTRA_IGNORE_INSERT";
+ break;
}
return inspected;
}