1
0
mirror of https://github.com/MariaDB/server.git synced 2026-01-06 05:22:24 +03:00

MDEV-515 Reduce InnoDB undo logging for insert into empty table

We implement an idea that was suggested by Michael 'Monty' Widenius
in October 2017: When InnoDB is inserting into an empty table or partition,
we can write a single undo log record TRX_UNDO_EMPTY, which will cause
ROLLBACK to clear the table.

For this to work, the insert into an empty table or partition must be
covered by an exclusive table lock that will be held until the transaction
has been committed or rolled back, or the INSERT operation has been
rolled back (and the table is empty again), in lock_table_x_unlock().

Clustered index records that are covered by the TRX_UNDO_EMPTY record
will carry DB_TRX_ID=0 and DB_ROLL_PTR=1<<55, and thus they cannot
be distinguished from what MDEV-12288 leaves behind after purging the
history of row-logged operations.

Concurrent non-locking reads must be adjusted: If the read view was
created before the INSERT into an empty table, then we must continue
to imagine that the table is empty, and not try to read any records.
If the read view was created after the INSERT was committed, then
all records must be visible normally. To implement this, we introduce
the field dict_table_t::bulk_trx_id.

This special handling only applies to the very first INSERT statement
of a transaction for the empty table or partition. If a subsequent
statement in the transaction is modifying the initially empty table again,
we must enable row-level undo logging, so that we will be able to
roll back to the start of the statement in case of an error (such as
duplicate key).

INSERT IGNORE will continue to use row-level logging and locking, because
implementing it would require the ability to roll back the latest row.
Since the undo log that we write only allows us to roll back the entire
statement, we cannot support INSERT IGNORE. We will introduce a
handler::extra() parameter HA_EXTRA_IGNORE_INSERT to indicate to storage
engines that INSERT IGNORE is being executed.

In many test cases, we add an extra record to the table, so that during
the 'interesting' part of the test, row-level locking and logging will
be used.

Replicas will continue to use row-level logging and locking until
MDEV-24622 has been addressed. Likewise, this optimization will be
disabled in Galera cluster until MDEV-24623 enables it.

dict_table_t::bulk_trx_id: The latest active or committed transaction
that initiated an insert into an empty table or partition.
Protected by exclusive table lock and a clustered index leaf page latch.

ins_node_t::bulk_insert: Whether bulk insert was initiated.

trx_t::mod_tables: Use C++11 style accessors (emplace instead of insert).
Unlike earlier, this collection will cover also temporary tables.

trx_mod_table_time_t: Add start_bulk_insert(), end_bulk_insert(),
is_bulk_insert(), was_bulk_insert().

trx_undo_report_row_operation(): Before accessing any undo log pages,
invoke trx->mod_tables.emplace() in order to determine whether undo
logging was disabled, or whether this is the first INSERT and we are
supposed to write a TRX_UNDO_EMPTY record.

row_ins_clust_index_entry_low(): If we are inserting into an empty
clustered index leaf page, set the ins_node_t::bulk_insert flag for
the subsequent trx_undo_report_row_operation() call.

lock_rec_insert_check_and_lock(), lock_prdt_insert_check_and_lock():
Remove the redundant parameter 'flags' that can be checked in the caller.

btr_cur_ins_lock_and_undo(): Simplify the logic. Correctly write
DB_TRX_ID,DB_ROLL_PTR after invoking trx_undo_report_row_operation().

trx_mark_sql_stat_end(), ha_innobase::extra(HA_EXTRA_IGNORE_INSERT),
ha_innobase::external_lock(): Invoke trx_t::end_bulk_insert() so that
the next statement will not be covered by table-level undo logging.

ReadView::changes_visible(trx_id_t) const: New accessor for the case
where the trx_id_t is not read from a potentially corrupted index page
but directly from the memory. In this case, we can skip a sanity check.

row_sel(), row_sel_try_search_shortcut(), row_search_mvcc():
row_sel_try_search_shortcut_for_mysql(),
row_merge_read_clustered_index(): Check dict_table_t::bulk_trx_id.

row_sel_clust_sees(): Replaces lock_clust_rec_cons_read_sees().

lock_sec_rec_cons_read_sees(): Replaced with lower-level code.

btr_root_page_init(): Refactored from btr_create().

dict_index_t::clear(), dict_table_t::clear(): Empty an index or table,
for the ROLLBACK of an INSERT operation.

ROW_T_EMPTY, ROW_OP_EMPTY: Note a concurrent ROLLBACK of an INSERT
into an empty table.

This is joint work with Thirunarayanan Balathandayuthapani,
who created a working prototype.
Thanks to Matthias Leich for extensive testing.
This commit is contained in:
Marko Mäkelä
2021-01-25 18:41:27 +02:00
parent 7aed5eb76f
commit 3cef4f8f0f
97 changed files with 1279 additions and 587 deletions

View File

@@ -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 */

View File

@@ -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;

View File

@@ -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'

View File

@@ -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.

View File

@@ -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.

View File

@@ -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;

View File

@@ -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;

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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,,);

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 #-#-#

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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`;

View File

@@ -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;

View File

@@ -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';

View File

@@ -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';

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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)

View File

@@ -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;

View File

@@ -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,,);

View File

@@ -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);

View File

@@ -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 (

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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");

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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,
'',
''));

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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';

View File

@@ -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';

View File

@@ -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

View File

@@ -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");

View File

@@ -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);

View File

@@ -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,
'',
''));

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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. #

View File

@@ -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

View File

@@ -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);

View File

@@ -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:
{

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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));

View File

@@ -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:

View File

@@ -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;

View File

@@ -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

View File

@@ -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 */

View File

@@ -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().

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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;
/** 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;
/** 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;
/** 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();
};
/**

View File

@@ -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));

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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 ===============*/

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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;
}