From 62d1e3bf67a12eb6f48ac615bda119e2ed65cf16 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 24 Aug 2020 18:52:44 +0200 Subject: [PATCH 01/40] MDEV-23569 temporary tables can overwrite existing files for internal temporary tables: don't use realpath(), and let them overwrite whatever orphan temp files might've left in the tmpdir (see main.error_simulation test). for user created temporary tables: we have to use realpath(), (see 3a726ab6e2e, remember DATA/INDEX DIRECTORY). don't allow them to overwrite existing files. This bug was reported by RACK911 LABS --- mysql-test/r/temp_table_symlink.result | 13 +++++++++++ mysql-test/t/temp_table_symlink.test | 32 ++++++++++++++++++++++++++ storage/maria/ma_create.c | 6 ++--- storage/myisam/mi_create.c | 6 ++--- 4 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 mysql-test/r/temp_table_symlink.result create mode 100644 mysql-test/t/temp_table_symlink.test diff --git a/mysql-test/r/temp_table_symlink.result b/mysql-test/r/temp_table_symlink.result new file mode 100644 index 00000000000..1c5c68170ff --- /dev/null +++ b/mysql-test/r/temp_table_symlink.result @@ -0,0 +1,13 @@ +create table d1 (a int); +create temporary table t1 (a int); +create temporary table t2 (a int); +Got one of the listed errors +create temporary table t3 (a int) engine=Aria; +Got one of the listed errors +select * from information_schema.columns where table_schema='test'; +Got one of the listed errors +flush tables; +select * from d1; +a +drop temporary table t1; +drop table d1; diff --git a/mysql-test/t/temp_table_symlink.test b/mysql-test/t/temp_table_symlink.test new file mode 100644 index 00000000000..9297b472805 --- /dev/null +++ b/mysql-test/t/temp_table_symlink.test @@ -0,0 +1,32 @@ +source include/not_windows.inc; + +# +# MDEV-23569 temporary tables can overwrite existing files +# + +let datadir=`select @@datadir`; +create table d1 (a int); +create temporary table t1 (a int); +perl; +chdir "$ENV{MYSQL_TMP_DIR}/mysqld.1/"; +for (<#sql*.MYI>) { + /^#sql(.*)_([0-9a-f]+_)([0-9a-f]+)\.MYI$/ or die; + symlink "$ENV{datadir}/test/d1.MYI", sprintf "#sql$1_$2%x.MYI", hex($3)+1; + symlink "$ENV{datadir}/test/d1.MYI", sprintf "#sql$1_$2%x.MAI", hex($3)+1; + symlink "$ENV{datadir}/test/d1.MYI", sprintf "#sql$1_$2%x.MAI", hex($3)+2; + symlink "$ENV{datadir}/test/d1.MYI", "#sql_$1_0.MAI"; +} +EOF + +error 1,1030; +create temporary table t2 (a int); +error 1,1030; +create temporary table t3 (a int) engine=Aria; +error 1,1030; +select * from information_schema.columns where table_schema='test'; + +flush tables; +select * from d1; +drop temporary table t1; +remove_files_wildcard $MYSQL_TMP_DIR/mysqld.1 *sql*; +drop table d1; diff --git a/storage/maria/ma_create.c b/storage/maria/ma_create.c index 263d8a307b1..e1d2d89020d 100644 --- a/storage/maria/ma_create.c +++ b/storage/maria/ma_create.c @@ -322,7 +322,7 @@ int maria_create(const char *name, enum data_file_type datafile_type, { options|= HA_OPTION_TMP_TABLE; tmp_table= TRUE; - create_mode|= O_NOFOLLOW; + create_mode|= O_NOFOLLOW | (internal_table ? 0 : O_EXCL); /* "CREATE TEMPORARY" tables are not crash-safe (dropped at restart) */ ci->transactional= FALSE; flags&= ~HA_CREATE_PAGE_CHECKSUM; @@ -887,8 +887,8 @@ int maria_create(const char *name, enum data_file_type datafile_type, { char *iext= strrchr(name, '.'); int have_iext= iext && !strcmp(iext, MARIA_NAME_IEXT); - fn_format(kfilename, name, "", MARIA_NAME_IEXT, - MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH | + fn_format(kfilename, name, "", MARIA_NAME_IEXT, MY_UNPACK_FILENAME | + (internal_table ? 0 : MY_RETURN_REAL_PATH) | (have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT)); klinkname_ptr= NullS; /* diff --git a/storage/myisam/mi_create.c b/storage/myisam/mi_create.c index 855ac8337b9..7d6095b8a09 100644 --- a/storage/myisam/mi_create.c +++ b/storage/myisam/mi_create.c @@ -184,7 +184,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, if (flags & HA_CREATE_TMP_TABLE) { options|= HA_OPTION_TMP_TABLE; - create_mode|= O_NOFOLLOW; + create_mode|= O_NOFOLLOW | (internal_table ? 0 : O_EXCL); } if (flags & HA_CREATE_CHECKSUM || (options & HA_OPTION_CHECKSUM)) { @@ -618,8 +618,8 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, { char *iext= strrchr(name, '.'); int have_iext= iext && !strcmp(iext, MI_NAME_IEXT); - fn_format(kfilename, name, "", MI_NAME_IEXT, - MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH | + fn_format(kfilename, name, "", MI_NAME_IEXT, MY_UNPACK_FILENAME | + (internal_table ? 0 : MY_RETURN_REAL_PATH) | (have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT)); klinkname_ptr= 0; /* Replace the current file */ From b3e43eeec72ed5678bd9364b75ea4adf3a2fb881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 27 Aug 2020 13:52:15 +0300 Subject: [PATCH 02/40] Remove xtrabackup and innobackupex test cases. --- .../r/galera_innobackupex_backup.result | 17 ---- .../r/galera_ipv6_xtrabackup-v2.result | 22 ----- .../t/galera_innobackupex_backup.cnf | 4 - .../t/galera_innobackupex_backup.test | 80 ------------------- .../t/galera_ipv6_xtrabackup-v2.cnf | 29 ------- .../t/galera_ipv6_xtrabackup-v2.opt | 1 - .../t/galera_ipv6_xtrabackup-v2.test | 58 -------------- 7 files changed, 211 deletions(-) delete mode 100644 mysql-test/suite/galera_3nodes/r/galera_innobackupex_backup.result delete mode 100644 mysql-test/suite/galera_3nodes/r/galera_ipv6_xtrabackup-v2.result delete mode 100644 mysql-test/suite/galera_3nodes/t/galera_innobackupex_backup.cnf delete mode 100644 mysql-test/suite/galera_3nodes/t/galera_innobackupex_backup.test delete mode 100644 mysql-test/suite/galera_3nodes/t/galera_ipv6_xtrabackup-v2.cnf delete mode 100644 mysql-test/suite/galera_3nodes/t/galera_ipv6_xtrabackup-v2.opt delete mode 100644 mysql-test/suite/galera_3nodes/t/galera_ipv6_xtrabackup-v2.test diff --git a/mysql-test/suite/galera_3nodes/r/galera_innobackupex_backup.result b/mysql-test/suite/galera_3nodes/r/galera_innobackupex_backup.result deleted file mode 100644 index 6ed7587303d..00000000000 --- a/mysql-test/suite/galera_3nodes/r/galera_innobackupex_backup.result +++ /dev/null @@ -1,17 +0,0 @@ -connection node_1; -connection node_2; -connection node_3; -connection node_1; -CREATE TABLE t1 (f1 INTEGER); -INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); -connection node_2; -SELECT COUNT(*) = 10 FROM t1; -COUNT(*) = 10 -1 -Killing server ... -connection node_1; -INSERT INTO t1 VALUES (11),(12),(13),(14),(15),(16),(17),(18),(19),(20); -SELECT COUNT(*) = 20 FROM t1; -COUNT(*) = 20 -1 -DROP TABLE t1; diff --git a/mysql-test/suite/galera_3nodes/r/galera_ipv6_xtrabackup-v2.result b/mysql-test/suite/galera_3nodes/r/galera_ipv6_xtrabackup-v2.result deleted file mode 100644 index 5665ed5f46a..00000000000 --- a/mysql-test/suite/galera_3nodes/r/galera_ipv6_xtrabackup-v2.result +++ /dev/null @@ -1,22 +0,0 @@ -SELECT VARIABLE_VALUE LIKE '%[::1]%' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_incoming_addresses'; -VARIABLE_VALUE LIKE '%[::1]%' -1 -SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'; -VARIABLE_VALUE = 3 -1 -connection node_2; -SET GLOBAL wsrep_provider_options='gmcast.isolate=1'; -connection node_1; -CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB; -INSERT INTO t1 VALUES (1); -connection node_2; -SET GLOBAL wsrep_provider_options='gmcast.isolate=0'; -SELECT COUNT(*) = 1 FROM t1; -COUNT(*) = 1 -1 -DROP TABLE t1; -connection node_1; -include/assert_grep.inc [Streaming the backup to joiner at \[::1\]] -include/assert_grep.inc [async IST sender starting to serve tcp://\[::1\]:] -include/assert_grep.inc [IST receiver addr using tcp://\[::1\]] -include/assert_grep.inc [Prepared IST receiver, listening at: tcp://\[::1\]] diff --git a/mysql-test/suite/galera_3nodes/t/galera_innobackupex_backup.cnf b/mysql-test/suite/galera_3nodes/t/galera_innobackupex_backup.cnf deleted file mode 100644 index 35ecb8b5937..00000000000 --- a/mysql-test/suite/galera_3nodes/t/galera_innobackupex_backup.cnf +++ /dev/null @@ -1,4 +0,0 @@ -!include ../galera_3nodes.cnf - -[mysqld] -wsrep-causal-reads=OFF diff --git a/mysql-test/suite/galera_3nodes/t/galera_innobackupex_backup.test b/mysql-test/suite/galera_3nodes/t/galera_innobackupex_backup.test deleted file mode 100644 index 8dfb4660f3e..00000000000 --- a/mysql-test/suite/galera_3nodes/t/galera_innobackupex_backup.test +++ /dev/null @@ -1,80 +0,0 @@ -# -# This test uses innobackupex to take a backup on node #2 and then restores that node from backup -# - ---source include/galera_cluster.inc ---source include/have_innodb.inc ---source include/have_mariabackup.inc - ---let $galera_connection_name = node_3 ---let $galera_server_number = 3 ---source include/galera_connect.inc - -# Save original auto_increment_offset values. ---let $node_1=node_1 ---let $node_2=node_2 ---let $node_3=node_3 ---source ../galera/include/auto_increment_offset_save.inc - ---connection node_1 -CREATE TABLE t1 (f1 INTEGER); -INSERT INTO t1 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); - ---connection node_2 -SELECT COUNT(*) = 10 FROM t1; - ---exec rm -rf $MYSQL_TMP_DIR/innobackupex_backup ---exec mariabackup --innobackupex --defaults-file=$MYSQLTEST_VARDIR/my.cnf --defaults-group=mysqld.2 --galera-info --port=$NODE_MYPORT_2 --host=127.0.0.1 --no-timestamp $MYSQL_TMP_DIR/innobackupex_backup &> $MYSQL_TMP_DIR/innobackupex-backup.log ---exec mariabackup --innobackupex --defaults-file=$MYSQLTEST_VARDIR/my.cnf --defaults-group=mysqld.2 --apply-log --galera-info --port=$NODE_MYPORT_2 --host=127.0.0.1 --no-timestamp $MYSQL_TMP_DIR/innobackupex_backup &> $MYSQL_TMP_DIR/innobackupex-apply.log - ---source ../galera/include/kill_galera.inc ---sleep 1 - ---connection node_1 -INSERT INTO t1 VALUES (11),(12),(13),(14),(15),(16),(17),(18),(19),(20); - ---exec rm -rf $MYSQLTEST_VARDIR/mysqld.2/data/* ---exec mariabackup --innobackupex --defaults-file=$MYSQLTEST_VARDIR/my.cnf --defaults-group=mysqld.2 --copy-back --port=$NODE_MYPORT_2 --host=127.0.0.1 $MYSQL_TMP_DIR/innobackupex_backup &> $MYSQL_TMP_DIR/innobackupex-restore.log - -# -# Convert the xtrabackup_galera_info into a grastate.dat file -# - ---perl - use strict; - my $xtrabackup_galera_info_file = $ENV{'MYSQL_TMP_DIR'}.'/innobackupex_backup/xtrabackup_galera_info'; - open(XTRABACKUP_GALERA_INFO, $xtrabackup_galera_info_file) or die "Can not open $xtrabackup_galera_info_file: $!"; - my $xtrabackup_galera_info = ; - my ($uuid, $seqno) = split(':', $xtrabackup_galera_info); - - my $grastate_dat_file = $ENV{'MYSQLTEST_VARDIR'}.'/mysqld.2/data/grastate.dat'; - die "grastate.dat already exists" if -e $grastate_dat_file; - - open(GRASTATE_DAT, ">$grastate_dat_file") or die "Can not write to $grastate_dat_file: $!"; - print GRASTATE_DAT "version: 2.1\n"; - print GRASTATE_DAT "uuid: $uuid\n"; - print GRASTATE_DAT "seqno: $seqno\n"; - print GRASTATE_DAT "cert_index:\n"; - exit(0); -EOF - ---source include/start_mysqld.inc ---sleep 5 - ---source include/wait_until_connected_again.inc ---let $wait_condition = SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'; ---source include/wait_condition.inc - -SELECT COUNT(*) = 20 FROM t1; - -DROP TABLE t1; - ---sleep 10 - ---let $galera_connection_name = node_2a ---let $galera_server_number = 2 ---source include/galera_connect.inc ---let $node_2=node_2a - -# Restore original auto_increment_offset values. ---source ../galera/include/auto_increment_offset_restore.inc diff --git a/mysql-test/suite/galera_3nodes/t/galera_ipv6_xtrabackup-v2.cnf b/mysql-test/suite/galera_3nodes/t/galera_ipv6_xtrabackup-v2.cnf deleted file mode 100644 index 78fbd793aa0..00000000000 --- a/mysql-test/suite/galera_3nodes/t/galera_ipv6_xtrabackup-v2.cnf +++ /dev/null @@ -1,29 +0,0 @@ -!include ../galera_3nodes.cnf - -[mysqld] -wsrep_sst_method=xtrabackup-v2 -wsrep_sst_auth="root:" -wsrep_node_address=::1 - -[mysqld.1] -wsrep-cluster-address=gcomm:// -wsrep_provider_options='base_host=[::1];base_port=@mysqld.1.#galera_port;gmcast.listen_addr=tcp://[::]:@mysqld.1.#galera_port;ist.recv_addr=[::1]:@mysqld.1.#ist_port' -wsrep_sst_receive_address='[::1]:@mysqld.1.#sst_port' -wsrep_node_incoming_address='[::1]:@mysqld.1.port' - -[mysqld.2] -wsrep_cluster_address='gcomm://[::1]:@mysqld.1.#galera_port' -wsrep_provider_options='base_host=[::1];base_port=@mysqld.2.#galera_port;gmcast.listen_addr=tcp://[::]:@mysqld.2.#galera_port;ist.recv_addr=[::1]:@mysqld.2.#ist_port' -wsrep_sst_receive_address='[::1]:@mysqld.2.#sst_port' -wsrep_node_incoming_address='[::1]:@mysqld.2.port' - -[mysqld.3] -wsrep_cluster_address='gcomm://[::1]:@mysqld.1.#galera_port' -wsrep_provider_options='base_host=[::1];base_port=@mysqld.3.#galera_port;gmcast.listen_addr=tcp://[::]:@mysqld.3.#galera_port;ist.recv_addr=[::1]:@mysqld.3.#ist_port' -wsrep_sst_receive_address='[::1]:@mysqld.3.#sst_port' -wsrep_node_incoming_address='[::1]:@mysqld.3.port' - -[SST] -transferfmt=@ENV.MTR_GALERA_TFMT -streamfmt=xbstream -sockopt=",pf=ip6" diff --git a/mysql-test/suite/galera_3nodes/t/galera_ipv6_xtrabackup-v2.opt b/mysql-test/suite/galera_3nodes/t/galera_ipv6_xtrabackup-v2.opt deleted file mode 100644 index c195dd0f35b..00000000000 --- a/mysql-test/suite/galera_3nodes/t/galera_ipv6_xtrabackup-v2.opt +++ /dev/null @@ -1 +0,0 @@ ---bind-address=:: --skip-innodb-safe-truncate diff --git a/mysql-test/suite/galera_3nodes/t/galera_ipv6_xtrabackup-v2.test b/mysql-test/suite/galera_3nodes/t/galera_ipv6_xtrabackup-v2.test deleted file mode 100644 index fd7e0c50887..00000000000 --- a/mysql-test/suite/galera_3nodes/t/galera_ipv6_xtrabackup-v2.test +++ /dev/null @@ -1,58 +0,0 @@ ---source include/galera_cluster.inc ---source include/check_ipv6.inc - -# Confirm that initial handshake happened over ipv6 - -SELECT VARIABLE_VALUE LIKE '%[::1]%' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_incoming_addresses'; -SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'; - -# Force IST - ---connection node_2 -SET GLOBAL wsrep_provider_options='gmcast.isolate=1'; - ---connection node_1 ---let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'; ---source include/wait_condition.inc - -CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB; -INSERT INTO t1 VALUES (1); - ---connection node_2 -SET GLOBAL wsrep_provider_options='gmcast.isolate=0'; - ---let $wait_condition = SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'; ---source include/wait_condition.inc - ---let $wait_condition = SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready'; ---source include/wait_condition.inc - -SELECT COUNT(*) = 1 FROM t1; - -DROP TABLE t1; - -# Confirm that key messages around SST and IST reference IPv6 - ---connection node_1 ---let $assert_file = $MYSQLTEST_VARDIR/log/mysqld.1.err ---let $assert_only_after = CURRENT_TEST - ---let $assert_count = 2 ---let $assert_text = Streaming the backup to joiner at \[::1\] ---let $assert_select = Streaming the backup to joiner at \[::1\] ---source include/assert_grep.inc - ---let $assert_count = 1 ---let $assert_text = async IST sender starting to serve tcp://\[::1\]: ---let $assert_select = async IST sender starting to serve tcp://\[::1\]: ---source include/assert_grep.inc - ---let $assert_file = $MYSQLTEST_VARDIR/log/mysqld.2.err - ---let $assert_text = IST receiver addr using tcp://\[::1\] ---let $assert_select = IST receiver addr using tcp://\[::1\] ---source include/assert_grep.inc - ---let $assert_text = Prepared IST receiver, listening at: tcp://\[::1\] ---let $assert_select = Prepared IST receiver, listening at: tcp://\[::1\] ---source include/assert_grep.inc From f54295f54615274b32cd473b2070a192fcd26721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 27 Aug 2020 14:49:17 +0300 Subject: [PATCH 03/40] MDEV-23573 : galera_3nodes.galera_ipv6_mariabackup MTR failed: Could not open '../galera/include/have_mariabackup.inc Fix the include and add force_restart to stabilize. --- mysql-test/suite/galera_3nodes/t/galera_ipv6_mariabackup.test | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/galera_3nodes/t/galera_ipv6_mariabackup.test b/mysql-test/suite/galera_3nodes/t/galera_ipv6_mariabackup.test index 863ce65b579..ae489117942 100644 --- a/mysql-test/suite/galera_3nodes/t/galera_ipv6_mariabackup.test +++ b/mysql-test/suite/galera_3nodes/t/galera_ipv6_mariabackup.test @@ -1,7 +1,8 @@ --source include/galera_cluster.inc --source include/check_ipv6.inc --source include/have_innodb.inc ---source ../galera/include/have_mariabackup.inc +--source include/have_mariabackup.inc +--source include/force_restart.inc # Confirm that initial handshake happened over ipv6 From 97d830565f0c1b7e4b720dcf7afed52ac8f746bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 27 Aug 2020 14:50:21 +0300 Subject: [PATCH 04/40] MDEV-23574 : galera_3nodes.galera_ipv6_mariabackup_section MTR failed: Could not open '../galera/include/have_mariabackup.inc' Fix the include and add force_restart to stabilize. --- .../galera_3nodes/r/galera_ipv6_mariabackup_section.result | 5 +++++ .../galera_3nodes/t/galera_ipv6_mariabackup_section.test | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/galera_3nodes/r/galera_ipv6_mariabackup_section.result b/mysql-test/suite/galera_3nodes/r/galera_ipv6_mariabackup_section.result index 53e35939a79..5a844537327 100644 --- a/mysql-test/suite/galera_3nodes/r/galera_ipv6_mariabackup_section.result +++ b/mysql-test/suite/galera_3nodes/r/galera_ipv6_mariabackup_section.result @@ -4,15 +4,20 @@ VARIABLE_VALUE LIKE '%[::1]%' SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'; VARIABLE_VALUE = 3 1 +connection node_2; SET GLOBAL wsrep_provider_options='gmcast.isolate=1'; +connection node_1; CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB; INSERT INTO t1 VALUES (1); +connection node_2; SET GLOBAL wsrep_provider_options='gmcast.isolate=0'; SELECT COUNT(*) = 1 FROM t1; COUNT(*) = 1 1 DROP TABLE t1; +connection node_1; include/assert_grep.inc [Streaming the backup to joiner at \[::1\]] include/assert_grep.inc [async IST sender starting to serve tcp://\[::1\]:] +connection node_2; include/assert_grep.inc [IST receiver addr using tcp://\[::1\]] include/assert_grep.inc [Prepared IST receiver, listening at: tcp://\[::1\]] diff --git a/mysql-test/suite/galera_3nodes/t/galera_ipv6_mariabackup_section.test b/mysql-test/suite/galera_3nodes/t/galera_ipv6_mariabackup_section.test index 78fcf0873fc..2c5aae8534d 100644 --- a/mysql-test/suite/galera_3nodes/t/galera_ipv6_mariabackup_section.test +++ b/mysql-test/suite/galera_3nodes/t/galera_ipv6_mariabackup_section.test @@ -1,7 +1,8 @@ --source include/galera_cluster.inc --source include/check_ipv6.inc --source include/have_innodb.inc ---source ../galera/include/have_mariabackup.inc +--source include/have_mariabackup.inc +--source include/force_restart.inc # Confirm that initial handshake happened over ipv6 From 3135de4266765b5e75c2b3e6350ec0a080380e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 27 Aug 2020 14:51:06 +0300 Subject: [PATCH 05/40] MDEV-23576 : galera_3nodes.galera_ipv6_mysqldump MTR failed: WSREP_SST: [ERROR] rsync daemon port '16008' has been taken Add force_restart to clean up rsync daemon port. --- mysql-test/suite/galera_3nodes/t/galera_ipv6_mysqldump.test | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/suite/galera_3nodes/t/galera_ipv6_mysqldump.test b/mysql-test/suite/galera_3nodes/t/galera_ipv6_mysqldump.test index 4a6de8abb9c..c9e08f58950 100644 --- a/mysql-test/suite/galera_3nodes/t/galera_ipv6_mysqldump.test +++ b/mysql-test/suite/galera_3nodes/t/galera_ipv6_mysqldump.test @@ -1,5 +1,6 @@ --source include/galera_cluster.inc --source include/check_ipv6.inc +--source include/force_restart.inc call mtr.add_suppression("WSREP: wsrep_sst_method is set to 'mysqldump' yet mysqld bind_address is set to'"); call mtr.add_suppression("Failed to load slave replication state from table mysql.gtid_slave_pos"); From 2845ed682cc5c3a86f41711c026d47243e0c987a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 27 Aug 2020 14:52:12 +0300 Subject: [PATCH 06/40] MDEV-23581 : galera_3nodes.galera_ipv6_rsync MTR failed: WSREP_SST: [ERROR] rsync daemon port '16008' has been taken Add force_restart to free rsync daemon port. --- mysql-test/suite/galera_3nodes/t/galera_ipv6_rsync.test | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/suite/galera_3nodes/t/galera_ipv6_rsync.test b/mysql-test/suite/galera_3nodes/t/galera_ipv6_rsync.test index 1937eb43e13..fff776d98b6 100644 --- a/mysql-test/suite/galera_3nodes/t/galera_ipv6_rsync.test +++ b/mysql-test/suite/galera_3nodes/t/galera_ipv6_rsync.test @@ -1,5 +1,6 @@ --source include/galera_cluster.inc --source include/check_ipv6.inc +--source include/force_restart.inc # Confirm that initial handshake happened over ipv6 From fcea7918de382184ab25659e4d42568d664ab61f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 27 Aug 2020 14:56:13 +0300 Subject: [PATCH 07/40] MDEV-23580 : galera_3nodes.galera_ipv6_rsync_section MTR failed: WSREP_SST: [ERROR] rsync daemon port '16008' has been taken Add force_restart to free rsync daemon port. --- mysql-test/suite/galera_3nodes/t/galera_ipv6_rsync_section.test | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/suite/galera_3nodes/t/galera_ipv6_rsync_section.test b/mysql-test/suite/galera_3nodes/t/galera_ipv6_rsync_section.test index 1937eb43e13..fff776d98b6 100644 --- a/mysql-test/suite/galera_3nodes/t/galera_ipv6_rsync_section.test +++ b/mysql-test/suite/galera_3nodes/t/galera_ipv6_rsync_section.test @@ -1,5 +1,6 @@ --source include/galera_cluster.inc --source include/check_ipv6.inc +--source include/force_restart.inc # Confirm that initial handshake happened over ipv6 From a34f93010c2f4e86f5587241d1ce111603aea466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 27 Aug 2020 14:58:55 +0300 Subject: [PATCH 08/40] MDEV-23587 : galera_3nodes.galera_var_dirty_reads2 MTR failed: 1047: WSREP has not yet prepared node for application use Add wait_condition to make sure insert has been replicated and after cluster restore wait until node is ready before continuing. --- mysql-test/suite/galera_3nodes/t/galera_var_dirty_reads2.test | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mysql-test/suite/galera_3nodes/t/galera_var_dirty_reads2.test b/mysql-test/suite/galera_3nodes/t/galera_var_dirty_reads2.test index e3f94a012b8..6476c4fe4fa 100644 --- a/mysql-test/suite/galera_3nodes/t/galera_var_dirty_reads2.test +++ b/mysql-test/suite/galera_3nodes/t/galera_var_dirty_reads2.test @@ -34,6 +34,9 @@ SET SESSION wsrep_sync_wait = 0; SET SESSION wsrep_dirty_reads = 1; +--let $wait_condition = SELECT COUNT(*) = 1 FROM t1; +--source include/wait_condition.inc + # Those statements should succeed --error 0 @@ -111,6 +114,7 @@ SELECT COUNT(*) > 0 FROM INFORMATION_SCHEMA.PROCESSLIST; # Restore cluster SET GLOBAL wsrep_provider_options='gmcast.isolate=0'; +--source include/galera_wait_ready.inc --connection node_1 --let $wait_condition = SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'; From c08afc4363317f516995eca71aa0c71d9da03b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 27 Aug 2020 15:00:25 +0300 Subject: [PATCH 09/40] Update galera_3nodes suite global ignore list. --- mysql-test/suite/galera_3nodes/suite.pm | 29 ++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/galera_3nodes/suite.pm b/mysql-test/suite/galera_3nodes/suite.pm index 4a861f3b9fa..350e4af98ff 100644 --- a/mysql-test/suite/galera_3nodes/suite.pm +++ b/mysql-test/suite/galera_3nodes/suite.pm @@ -22,7 +22,6 @@ push @::global_suppressions, qr(WSREP:.*down context.*), qr(WSREP: Failed to send state UUID:), qr(WSREP: last inactive check more than .* skipping check), - qr(WSREP: SQL statement was ineffective), qr(WSREP: Releasing seqno [0-9]* before [0-9]* was assigned.), qr|WSREP: access file\(.*gvwstate.dat\) failed\(No such file or directory\)|, qr(WSREP: Quorum: No node with complete state), @@ -36,14 +35,38 @@ push @::global_suppressions, qr(WSREP: user message in state LEAVING), qr(WSREP: .* sending install message failed: Transport endpoint is not connected), qr(WSREP: .* sending install message failed: Resource temporarily unavailable), - qr(WSREP: Sending JOIN failed: -107 \(Transport endpoint is not connected\). Will retry in new primary component.), + qr(WSREP: Maximum writeset size exceeded by .*), + qr(WSREP: transaction size exceeded.*), + qr(WSREP: RBR event .*), + qr(WSREP: Ignoring error for TO isolated action: .*), + qr(WSREP: transaction size limit .*), + qr(WSREP: rbr write fail, .*), + qr(WSREP: .*Backend not supported: foo.*), + qr(WSREP: .*Failed to initialize backend using .*), + qr(WSREP: .*Failed to open channel 'my_wsrep_cluster' at .*), + qr(WSREP: gcs connect failed: Socket type not supported), + qr(WSREP: failed to open gcomm backend connection: 110: failed to reach primary view: 110 .*), + qr(WSREP: .*Failed to open backend connection: -110 .*), + qr(WSREP: .*Failed to open channel 'my_wsrep_cluster' at .*), + qr(WSREP: gcs connect failed: Connection timed out), + qr|WSREP: wsrep::connect\(.*\) failed: 7|, + qr(WSREP: SYNC message from member .* in non-primary configuration. Ignored.), qr(WSREP: Could not find peer:), + qr(WSREP: TO isolation failed for: .*), qr|WSREP: gcs_caused\(\) returned .*|, qr|WSREP: Protocol violation. JOIN message sender .* is not in state transfer \(SYNCED\). Message ignored.|, qr|WSREP: Protocol violation. JOIN message sender .* is not in state transfer \(JOINED\). Message ignored.|, + qr|WSREP: Unsupported protocol downgrade: incremental data collection disabled. Expect abort.|, qr(WSREP: Action message in non-primary configuration from member [0-9]*), + qr(WSREP: Last Applied Action message in non-primary configuration from member [0-9]*), + qr(WSREP: discarding established .*), + qr|WSREP: .*core_handle_uuid_msg.*|, qr(WSREP: --wsrep-causal-reads=ON takes precedence over --wsrep-sync-wait=0. WSREP_SYNC_WAIT_BEFORE_READ is on), - qr(WSREP: JOIN message from member .* in non-primary configuration. Ignored.), + qr|WSREP: JOIN message from member .* in non-primary configuration. Ignored.|, + qr(WSREP: Failed to remove page file .*), + qr(WSREP: wsrep_sst_method is set to 'mysqldump' yet mysqld bind_address is set to .*), + qr|WSREP: Sending JOIN failed: -107 \(Transport endpoint is not connected\). Will retry in new primary component.|, + qr|WSREP: Trying to continue unpaused monitor|, ); bless { }; From f69cc26757733724254ee37aec5a092f520d230f Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Thu, 27 Aug 2020 17:34:12 +0530 Subject: [PATCH 10/40] MDEV-23596: Assertion `tab->ref.use_count' failed in join_read_key_unlock_row The issue here was that the query was using ORDER BY LIMIT optimzation where the access method was changed from EQ_REF access to an index scan (index that would resolve the ORDER BY clause). But the parameter READ_RECORD::unlock_row was not reset to rr_unlock_row, which is used when the access method is not EQ_REF access. --- mysql-test/r/order_by.result | 16 ++++++++++++++++ mysql-test/t/order_by.test | 15 +++++++++++++++ sql/sql_select.cc | 3 +++ 3 files changed, 34 insertions(+) diff --git a/mysql-test/r/order_by.result b/mysql-test/r/order_by.result index ffb37c9309f..f7427bcd30e 100644 --- a/mysql-test/r/order_by.result +++ b/mysql-test/r/order_by.result @@ -3356,3 +3356,19 @@ SET max_sort_length= @save_max_sort_length; SET sort_buffer_size= @save_sort_buffer_size; SET max_length_for_sort_data= @save_max_length_for_sort_data; DROP TABLE t1; +# +# MDEV-23596: Assertion `tab->ref.use_count' failed in join_read_key_unlock_row +# +CREATE TABLE t1 (a INT PRIMARY KEY, b INT, KEY(b)); +INSERT INTO t1 VALUES (0, 1),(1, 2); +CREATE TABLE t2 SELECT * FROM t1; +EXPLAIN SELECT (SELECT 1 FROM t1 WHERE t1.a=t2.b ORDER BY t1.b LIMIT 1) AS c FROM t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 +2 DEPENDENT SUBQUERY t1 index PRIMARY b 5 NULL 1 Using where +SELECT (SELECT 1 FROM t1 WHERE t1.a=t2.b ORDER BY t1.b LIMIT 1) AS c FROM t2; +c +1 +NULL +DROP TABLE t1,t2; +# end of 10.1 tests diff --git a/mysql-test/t/order_by.test b/mysql-test/t/order_by.test index 3a30e0b6c76..3e614ed3316 100644 --- a/mysql-test/t/order_by.test +++ b/mysql-test/t/order_by.test @@ -2196,3 +2196,18 @@ SET max_sort_length= @save_max_sort_length; SET sort_buffer_size= @save_sort_buffer_size; SET max_length_for_sort_data= @save_max_length_for_sort_data; DROP TABLE t1; + +--echo # +--echo # MDEV-23596: Assertion `tab->ref.use_count' failed in join_read_key_unlock_row +--echo # + +CREATE TABLE t1 (a INT PRIMARY KEY, b INT, KEY(b)); +INSERT INTO t1 VALUES (0, 1),(1, 2); +CREATE TABLE t2 SELECT * FROM t1; + +EXPLAIN SELECT (SELECT 1 FROM t1 WHERE t1.a=t2.b ORDER BY t1.b LIMIT 1) AS c FROM t2; +SELECT (SELECT 1 FROM t1 WHERE t1.a=t2.b ORDER BY t1.b LIMIT 1) AS c FROM t2; + +DROP TABLE t1,t2; + +--echo # end of 10.1 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4c6e87e4f27..e2e87bb1a86 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -21601,6 +21601,9 @@ check_reverse_order: else if (select && select->quick) select->quick->need_sorted_output(); + tab->read_record.unlock_row= (tab->type == JT_EQ_REF) ? + join_read_key_unlock_row : rr_unlock_row; + } // QEP has been modified /* From 5843dc485bb894e5d3fde2bb6bb7f473c8f78159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 27 Aug 2020 15:00:49 +0300 Subject: [PATCH 11/40] Update galera and galera_3nodes disabled.def file after fixes. --- mysql-test/suite/galera/disabled.def | 1 + mysql-test/suite/galera_3nodes/disabled.def | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/mysql-test/suite/galera/disabled.def b/mysql-test/suite/galera/disabled.def index 02f1bb5eb1f..b4dd37ea7cb 100644 --- a/mysql-test/suite/galera/disabled.def +++ b/mysql-test/suite/galera/disabled.def @@ -12,6 +12,7 @@ MW-286 : MDEV-18464 Killing thread can cause mutex deadlock if done concurrently with Galera/replication victim kill MW-328A : MDEV-22666 galera.MW-328A MTR failed: "Semaphore wait has lasted > 600 seconds" and do not release port 16002 +MW-328B : MDEV-22666 galera.MW-328A MTR failed: "Semaphore wait has lasted > 600 seconds" and do not release port 16002 MW-329 : MDEV-19962 Galera test failure on MW-329 galera.galera_defaults : MDEV-21494 Galera test sporadic failure on galera.galera_defaults galera_as_slave_replication_bundle : MDEV-15785 OPTION_GTID_BEGIN is set in Gtid_log_event::do_apply_event() diff --git a/mysql-test/suite/galera_3nodes/disabled.def b/mysql-test/suite/galera_3nodes/disabled.def index 415da407cf7..c6d3ece75d1 100644 --- a/mysql-test/suite/galera_3nodes/disabled.def +++ b/mysql-test/suite/galera_3nodes/disabled.def @@ -10,12 +10,6 @@ # ############################################################################## -galera_ipv6_mariabackup : MDEV-23573 Could not open '../galera/include/have_mariabackup.inc' -galera_ipv6_mariabackup_section : MDEV-23574 Could not open '../galera/include/have_mariabackup.inc' -galera_ipv6_mysqldump : MDEV-23576 WSREP_SST: [ERROR] rsync daemon port '16008' has been taken -galera_ipv6_rsyn : MDEV-23581 WSREP_SST: [ERROR] rsync daemon port '16008' has been taken -galera_ipv6_rsync_section : MDEV-23580 WSREP_SST: [ERROR] rsync daemon port '16008' has been taken -galera_ipv6_xtrabackup-v2 : MDEV-23575 WSREP_SST: [ERROR] innobackupex not in path galera_ist_gcache_rollover : MDEV-23578 WSREP: exception caused by message: {v=0,t=1,ut=255,o=4,s=0,sr=0,as=1,f=6,src=50524cfe,srcvid=view_id(REG,50524cfe,4),insvid=view_id(UNKNOWN,00000000,0),ru=00000000,r=[-1,-1],fs=75,nl=(} galera_slave_options_do :MDEV-8798 galera_slave_options_ignore : MDEV-8798 From df07ea0b27f891c69e60b75869f474cd03232216 Mon Sep 17 00:00:00 2001 From: sjaakola Date: Thu, 27 Aug 2020 10:52:39 +0300 Subject: [PATCH 12/40] MDEV-23557 Galera heap-buffer-overflow in wsrep_rec_get_foreign_key This commit contains a fix and extended test case for a ASAN failure reported during galera.fk mtr testing. The reported heap buffer overflow happens in test case where a cascading foreign key constraint is defined for a column of varchar type, and galera.fk.test has such vulnerable test scenario. Troubleshoting revealed that erlier fix for MDEV-19660 has made a fix for cascading delete handling to append wsrep keys from pcur->old_rec, in row_ins_foreign_check_on_constraint(). And, the ASAN failuer comes from later scanning of this old_rec reference. The fix in this commit, moves the call for wsrep_append_foreign_key() to happen somewhat earlier, and inside ongoing mtr, and using clust_rec which is set earlier in the same mtr for both update and delete cascade operations. for wsrep key populating, it does not matter when the keys are populated, all keys just have to be appended before wsrep transaction replicates. Note that I also tried similar fix for earlier wsrep key append, but using the old implementation with pcur->old_rec (instead of clust_rec), and same ASAN failure was reported. So it appears that pcur->old_rec is not properly set, to be used for wsrep key appending. galera.galera_fk_cascade_delete test has been extended by two new test scenarios: * FK cascade on varchar column. This test case reproduces same scenario as galera.fk, and this test scenario will also trigger ASAN failure with non fixed MariaDB versions. * multi-master conflict with FK cascading. this scenario causes a conflict between a replicated FK cascading transaction and local transaction trying to modify the cascaded child table row. Local transaction should be aborted and get deadlock error. This test scenario is passing both with old MariaDB version and with this commit as well. --- .../galera/r/galera_fk_cascade_delete.result | 18 ++-- .../r/galera_fk_cascade_delete_debug.result | 67 +++++++++++++ .../galera/t/galera_fk_cascade_delete.test | 18 +++- .../t/galera_fk_cascade_delete_debug.test | 98 +++++++++++++++++++ storage/innobase/row/row0ins.cc | 16 +-- 5 files changed, 200 insertions(+), 17 deletions(-) create mode 100644 mysql-test/suite/galera/r/galera_fk_cascade_delete_debug.result create mode 100644 mysql-test/suite/galera/t/galera_fk_cascade_delete_debug.test diff --git a/mysql-test/suite/galera/r/galera_fk_cascade_delete.result b/mysql-test/suite/galera/r/galera_fk_cascade_delete.result index 73375ae55c5..a6c6504dc39 100644 --- a/mysql-test/suite/galera/r/galera_fk_cascade_delete.result +++ b/mysql-test/suite/galera/r/galera_fk_cascade_delete.result @@ -1,3 +1,8 @@ +# +# test phase with cascading foreign key through 3 tables +# +connection node_1; +set wsrep_sync_wait=0; CREATE TABLE grandparent ( id INT NOT NULL PRIMARY KEY ) ENGINE=InnoDB; @@ -19,14 +24,15 @@ INSERT INTO grandparent VALUES (1),(2); INSERT INTO parent VALUES (1,1), (2,2); INSERT INTO child VALUES (1,1), (2,2); connection node_2; +set wsrep_sync_wait=0; DELETE FROM grandparent WHERE id = 1; connection node_1; -SELECT COUNT(*) = 0 FROM parent WHERE grandparent_id = 1; -COUNT(*) = 0 -1 -SELECT COUNT(*) = 0 FROM child WHERE parent_id = 1; -COUNT(*) = 0 -1 +SELECT COUNT(*), COUNT(*) = 0 FROM parent WHERE grandparent_id = 1; +COUNT(*) COUNT(*) = 0 +0 1 +SELECT COUNT(*), COUNT(*) = 0 FROM child WHERE parent_id = 1; +COUNT(*) COUNT(*) = 0 +0 1 DROP TABLE child; DROP TABLE parent; DROP TABLE grandparent; diff --git a/mysql-test/suite/galera/r/galera_fk_cascade_delete_debug.result b/mysql-test/suite/galera/r/galera_fk_cascade_delete_debug.result new file mode 100644 index 00000000000..89613b2856a --- /dev/null +++ b/mysql-test/suite/galera/r/galera_fk_cascade_delete_debug.result @@ -0,0 +1,67 @@ +# +# test phase with foreign key of varchar type +# +connection node_1; +CREATE TABLE parent ( +`id` varchar(36) COLLATE utf8_unicode_ci NOT NULL, +PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +CREATE TABLE child ( +`id` int NOT NULL, +`parent_id` varchar(36) COLLATE utf8_unicode_ci DEFAULT NULL, +PRIMARY KEY (`id`), +KEY `parent_id` (`parent_id`), +CONSTRAINT `ipallocations_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +INSERT INTO parent VALUES ('row one'), ('row two'); +INSERT INTO child VALUES (1,'row one'), (2,'row two'); +connection node_2; +DELETE FROM parent; +connection node_1; +SELECT COUNT(*), COUNT(*) = 0 FROM parent; +COUNT(*) COUNT(*) = 0 +0 1 +SELECT COUNT(*), COUNT(*) = 0 FROM child; +COUNT(*) COUNT(*) = 0 +0 1 +DROP TABLE child; +DROP TABLE parent; +# +# test phase with MM conflict in FK cascade +# +connection node_1; +set wsrep_retry_autocommit=0; +CREATE TABLE parent ( +id INT NOT NULL PRIMARY KEY +) ENGINE=InnoDB; +CREATE TABLE child ( +id INT NOT NULL PRIMARY KEY, +j int default 0, +parent_id INT, +FOREIGN KEY (parent_id) +REFERENCES parent(id) +ON DELETE CASCADE +) ENGINE=InnoDB; +INSERT INTO parent VALUES (1); +INSERT INTO child VALUES (1,0,1); +connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1; +SET GLOBAL debug_dbug = "d,sync.wsrep_apply_cb"; +connection node_2; +DELETE FROM parent; +connection node_1a; +SET SESSION DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_cb_reached"; +connection node_1; +update child set j=2;; +connection node_1a; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb"; +SET GLOBAL debug_dbug = ""; +SET DEBUG_SYNC = "RESET"; +connection node_1; +SELECT COUNT(*), COUNT(*) = 0 FROM parent; +COUNT(*) COUNT(*) = 0 +0 1 +SELECT COUNT(*), COUNT(*) = 0 FROM child; +COUNT(*) COUNT(*) = 0 +0 1 +DROP TABLE child; +DROP TABLE parent; diff --git a/mysql-test/suite/galera/t/galera_fk_cascade_delete.test b/mysql-test/suite/galera/t/galera_fk_cascade_delete.test index 9b79b4c30b6..6f0de0a1f4a 100644 --- a/mysql-test/suite/galera/t/galera_fk_cascade_delete.test +++ b/mysql-test/suite/galera/t/galera_fk_cascade_delete.test @@ -3,7 +3,13 @@ # --source include/galera_cluster.inc ---source include/have_innodb.inc + +--echo # +--echo # test phase with cascading foreign key through 3 tables +--echo # + +--connection node_1 +set wsrep_sync_wait=0; CREATE TABLE grandparent ( id INT NOT NULL PRIMARY KEY @@ -30,11 +36,17 @@ INSERT INTO parent VALUES (1,1), (2,2); INSERT INTO child VALUES (1,1), (2,2); --connection node_2 +set wsrep_sync_wait=0; + +--let $wait_condition = SELECT COUNT(*) = 2 FROM child; +--source include/wait_condition.inc DELETE FROM grandparent WHERE id = 1; --connection node_1 -SELECT COUNT(*) = 0 FROM parent WHERE grandparent_id = 1; -SELECT COUNT(*) = 0 FROM child WHERE parent_id = 1; +--let $wait_condition = SELECT COUNT(*) = 1 FROM child; +--source include/wait_condition.inc +SELECT COUNT(*), COUNT(*) = 0 FROM parent WHERE grandparent_id = 1; +SELECT COUNT(*), COUNT(*) = 0 FROM child WHERE parent_id = 1; DROP TABLE child; DROP TABLE parent; diff --git a/mysql-test/suite/galera/t/galera_fk_cascade_delete_debug.test b/mysql-test/suite/galera/t/galera_fk_cascade_delete_debug.test new file mode 100644 index 00000000000..f38c028b7d6 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_fk_cascade_delete_debug.test @@ -0,0 +1,98 @@ +--source include/galera_cluster.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc + +--echo # +--echo # test phase with foreign key of varchar type +--echo # +--connection node_1 + CREATE TABLE parent ( + `id` varchar(36) COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + + + CREATE TABLE child ( + `id` int NOT NULL, + `parent_id` varchar(36) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `parent_id` (`parent_id`), + CONSTRAINT `ipallocations_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON DELETE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + + +INSERT INTO parent VALUES ('row one'), ('row two'); +INSERT INTO child VALUES (1,'row one'), (2,'row two'); + +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 2 FROM child; +--source include/wait_condition.inc +DELETE FROM parent; + +--connection node_1 +--let $wait_condition = SELECT COUNT(*) = 0 FROM child; +--source include/wait_condition.inc + +SELECT COUNT(*), COUNT(*) = 0 FROM parent; +SELECT COUNT(*), COUNT(*) = 0 FROM child; + +DROP TABLE child; +DROP TABLE parent; + +--echo # +--echo # test phase with MM conflict in FK cascade +--echo # + +--connection node_1 +set wsrep_retry_autocommit=0; +CREATE TABLE parent ( + id INT NOT NULL PRIMARY KEY +) ENGINE=InnoDB; + +CREATE TABLE child ( + id INT NOT NULL PRIMARY KEY, + j int default 0, + parent_id INT, + FOREIGN KEY (parent_id) + REFERENCES parent(id) + ON DELETE CASCADE +) ENGINE=InnoDB; + +INSERT INTO parent VALUES (1); +INSERT INTO child VALUES (1,0,1); + +# block applier before applying +--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1 +SET GLOBAL debug_dbug = "d,sync.wsrep_apply_cb"; + +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 1 FROM child; +--source include/wait_condition.inc +DELETE FROM parent; + +--connection node_1a +# wait until applier has reached the sync point +SET SESSION DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_cb_reached"; + + +--connection node_1 +# issue conflicting write to child table, it should fail in certification +--error ER_LOCK_DEADLOCK +--send update child set j=2; + +--connection node_1a +# release the applier +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb"; +SET GLOBAL debug_dbug = ""; +SET DEBUG_SYNC = "RESET"; + +--connection node_1 +--reap + +--let $wait_condition = SELECT COUNT(*) = 0 FROM child; +--source include/wait_condition.inc +SELECT COUNT(*), COUNT(*) = 0 FROM parent; +SELECT COUNT(*), COUNT(*) = 0 FROM child; + +DROP TABLE child; +DROP TABLE parent; diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index 50394193a15..c0aa3655b7b 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -1402,20 +1402,20 @@ row_ins_foreign_check_on_constraint( btr_pcur_store_position(cascade->pcur, mtr); } +#ifdef WITH_WSREP + err = wsrep_append_foreign_key(trx, foreign, clust_rec, clust_index, + FALSE, WSREP_KEY_EXCLUSIVE); + if (err != DB_SUCCESS) { + ib::info() << "WSREP: foreign key append failed: " << err; + goto nonstandard_exit_func; + } +#endif /* WITH_WSREP */ mtr_commit(mtr); ut_a(cascade->pcur->rel_pos == BTR_PCUR_ON); cascade->state = UPD_NODE_UPDATE_CLUSTERED; -#ifdef WITH_WSREP - err = wsrep_append_foreign_key(trx, foreign, cascade->pcur->old_rec, clust_index, - FALSE, WSREP_KEY_EXCLUSIVE); - if (err != DB_SUCCESS) { - fprintf(stderr, - "WSREP: foreign key append failed: %d\n", err); - } else -#endif /* WITH_WSREP */ err = row_update_cascade_for_mysql(thr, cascade, foreign->foreign_table); From c710c450e3a5654244fffaabcb4ba5af2dd24dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Fri, 28 Aug 2020 16:40:12 +0300 Subject: [PATCH 13/40] MDEV-21578 : CREATE OR REPLACE TRIGGER in Galera cluster not replicating While doing TOI buffer OR REPLACE option was not added to replicated string. --- .../suite/galera/r/galera_trigger.result | 32 +++++++++++++++++ mysql-test/suite/galera/t/galera_trigger.test | 36 +++++++++++++++++++ sql/wsrep_mysqld.cc | 7 +++- 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/galera/r/galera_trigger.result create mode 100644 mysql-test/suite/galera/t/galera_trigger.test diff --git a/mysql-test/suite/galera/r/galera_trigger.result b/mysql-test/suite/galera/r/galera_trigger.result new file mode 100644 index 00000000000..112d3f39aa0 --- /dev/null +++ b/mysql-test/suite/galera/r/galera_trigger.result @@ -0,0 +1,32 @@ +CREATE TABLE IF NOT EXISTS t1 (id int(10) not null primary key) engine=innodb; +CREATE OR REPLACE TRIGGER tr1 +BEFORE INSERT ON t1 FOR EACH ROW +BEGIN +SET NEW.id = 100; +END| +INSERT INTO t1 VALUES (1); +SELECT * from t1; +id +100 +CREATE OR REPLACE TRIGGER tr1 +BEFORE INSERT ON t1 FOR EACH ROW +BEGIN +SET NEW.id = 200; +END| +connection node_2; +SET SESSION wsrep_sync_wait=15; +SELECT * FROM t1; +id +100 +INSERT INTO t1 values (2); +SELECT * FROM t1; +id +100 +200 +connection node_1; +SELECT * FROM t1; +id +100 +200 +DROP TRIGGER tr1; +DROP TABLE t1; diff --git a/mysql-test/suite/galera/t/galera_trigger.test b/mysql-test/suite/galera/t/galera_trigger.test new file mode 100644 index 00000000000..54508583f4b --- /dev/null +++ b/mysql-test/suite/galera/t/galera_trigger.test @@ -0,0 +1,36 @@ +--source include/galera_cluster.inc +# +# MDEV-21578 CREATE OR REPLACE TRIGGER in Galera cluster not replicating +# +CREATE TABLE IF NOT EXISTS t1 (id int(10) not null primary key) engine=innodb; +--delimiter | +CREATE OR REPLACE TRIGGER tr1 +BEFORE INSERT ON t1 FOR EACH ROW +BEGIN +SET NEW.id = 100; +END| +--delimiter ; + +INSERT INTO t1 VALUES (1); +SELECT * from t1; + +--delimiter | +CREATE OR REPLACE TRIGGER tr1 +BEFORE INSERT ON t1 FOR EACH ROW +BEGIN +SET NEW.id = 200; +END| +--delimiter ; + +--connection node_2 +SET SESSION wsrep_sync_wait=15; +SELECT * FROM t1; +INSERT INTO t1 values (2); +SELECT * FROM t1; + +--connection node_1 +SELECT * FROM t1; + +DROP TRIGGER tr1; +DROP TABLE t1; + diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index f95ef168a23..d392d1c2a61 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -2924,7 +2924,12 @@ static int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len) definer_host.length= 0; } - stmt_query.append(STRING_WITH_LEN("CREATE ")); + const LEX_STRING command[3]= + {{ C_STRING_WITH_LEN("CREATE ") }, + { C_STRING_WITH_LEN("ALTER ") }, + { C_STRING_WITH_LEN("CREATE OR REPLACE ") }}; + stmt_query.append(command[thd->lex->create_view_mode].str, + command[thd->lex->create_view_mode].length); append_definer(thd, &stmt_query, &definer_user, &definer_host); From 9bb17ecf4352e0ba009d6afb9260d291ed70fa4c Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Mon, 31 Aug 2020 13:01:57 +0300 Subject: [PATCH 14/40] fix clang build FAILED: sql/CMakeFiles/sql.dir/sql_test.cc.o /home/kevgs/bin/clang++ -DHAVE_CONFIG_H -DHAVE_EVENT_SCHEDULER -DHAVE_POOL_OF_THREADS -DMYSQL_SERVER -D_FILE_OFFSET_BITS=64 -Iinclude -I../include -I../sql -Ipcre -I../pcre -I../zlib -Izlib -I../extra/yassl/include -I../extra/yassl/taocrypt/include -Isql -I../wsrep -O2 -fdiagnostics-color=always -fno-omit-frame-pointer -gsplit-dwarf -march=native -mtune=native -fPIC -fno-rtti -g -DENABLED_DEBUG_SYNC -ggdb3 -DSAFE_MUTEX -Wall -Wdeclaration-after-statement -Wextra -Wformat-security -Wno-init-self -Wno-null-conversion -Wno-unused-parameter -Wno-unused-private-field -Woverloaded-virtual -Wvla -Wwrite-strings -Werror -DHAVE_YASSL -DYASSL_PREFIX -DHAVE_OPENSSL -DMULTI_THREADED -MD -MT sql/CMakeFiles/sql.dir/sql_test.cc.o -MF sql/CMakeFiles/sql.dir/sql_test.cc.o.d -o sql/CMakeFiles/sql.dir/sql_test.cc.o -c ../sql/sql_test.cc ../sql/sql_test.cc:390:20: error: '::' and '*' tokens forming pointer to member type are separated by whitespace [-Werror,-Wcompound-token-split-by-space] Item* (List:: *dbug_list_item_elem_ptr)(int)= &List::elem; ~~^~ ../sql/sql_test.cc:391:32: error: '::' and '*' tokens forming pointer to member type are separated by whitespace [-Werror,-Wcompound-token-split-by-space] Item_equal* (List:: *dbug_list_item_equal_elem_ptr)(int)= ~~^~ ../sql/sql_test.cc:393:32: error: '::' and '*' tokens forming pointer to member type are separated by whitespace [-Werror,-Wcompound-token-split-by-space] TABLE_LIST* (List:: *dbug_list_table_list_elem_ptr)(int) = ~~^~ 3 errors generated. --- sql/sql_test.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/sql_test.cc b/sql/sql_test.cc index db52ce0aea1..4e68ec2ec2e 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -387,10 +387,10 @@ void print_sjm(SJ_MATERIALIZATION_INFO *sjm) /* Debugging help: force List<...>::elem function not be removed as unused. */ -Item* (List:: *dbug_list_item_elem_ptr)(int)= &List::elem; -Item_equal* (List:: *dbug_list_item_equal_elem_ptr)(int)= +Item* (List::*dbug_list_item_elem_ptr)(int)= &List::elem; +Item_equal* (List::*dbug_list_item_equal_elem_ptr)(int)= &List::elem; -TABLE_LIST* (List:: *dbug_list_table_list_elem_ptr)(int) = +TABLE_LIST* (List::*dbug_list_table_list_elem_ptr)(int) = &List::elem; #endif From 0f080dd60a9a4fbfcd9c8a2d0361c9d7f0c080aa Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Tue, 11 Aug 2020 16:37:48 +0200 Subject: [PATCH 15/40] MDEV-23094: Multiple calls to a Stored Procedure from another Stored Procedure crashes server Added system-SELECT to IF/WHILE/REPET/FOR for correct subqueries connecting. Added control of system/usual selects for correct error detection. --- mysql-test/main/parser.result | 89 ++++++++++++ mysql-test/main/parser.test | 97 +++++++++++++ .../suite/compat/oracle/r/parser.result | 115 +++++++++++++++ mysql-test/suite/compat/oracle/t/parser.test | 132 ++++++++++++++++++ sql/sql_lex.cc | 6 +- sql/sql_lex.h | 14 +- sql/sql_parse.cc | 1 + sql/sql_select.cc | 5 + sql/sql_union.cc | 2 + sql/sql_yacc.yy | 85 +++++++++-- sql/sql_yacc_ora.yy | 85 +++++++++-- 11 files changed, 601 insertions(+), 30 deletions(-) diff --git a/mysql-test/main/parser.result b/mysql-test/main/parser.result index 78156c842ea..49ef2073971 100644 --- a/mysql-test/main/parser.result +++ b/mysql-test/main/parser.result @@ -1908,4 +1908,93 @@ KILL ( SELECT 1 ) + LASTVAL(s); ERROR 42000: KILL does not support subqueries or stored functions KILL LASTVAL(s); ERROR 42000: KILL does not support subqueries or stored functions +# +# MDEV-23094: Multiple calls to a Stored Procedure from another +# Stored Procedure crashes server +# +create table t1 (id1 int primary key, data1 int); +create table t2 (id2 int primary key, data2 int); +create procedure p1(IN id int, IN dt int) +begin +if (exists(select * from t1 where id1 = id and data1 = dt) or +not exists (select * from t2 where id2 = id and data2 = dt)) +then +select 1; +end if; +end // +call p1(1,2); +1 +1 +call p1(1,2); +1 +1 +drop procedure p1; +create procedure p1(IN id int, IN dt int) +begin +case (exists(select * from t1 where id1 = id and data1 = dt) or +not exists (select * from t2 where id2 = id and data2 = dt)) +when 1 then +select 1; +else +select 0; +end case; +end // +call p1(1,2); +1 +1 +call p1(1,2); +1 +1 +drop procedure p1; +create procedure p1(IN id int, IN dt int) +begin +declare wcont int default 1; +while (exists(select * from t1 where id1 = id and data1 = dt) or +not exists (select * from t2 where id2 = id and data2 = dt)) and wcont +do +select 1; +set wcont=0; +end while; +end // +call p1(1,2); +1 +1 +call p1(1,2); +1 +1 +drop procedure p1; +create procedure p1(IN id int, IN dt int) +begin +declare count int default 1; +repeat +select 1; +set count=count+1; +until (exists(select * from t1 where id1 = id and data1 = dt) or +not exists (select * from t2 where id2 = id and data2 = dt)) and +count < 3 +end repeat; +end // +call p1(1,2); +1 +1 +call p1(1,2); +1 +1 +drop procedure p1; +create procedure p1(IN id int, IN dt int) +begin +for i in 1..(exists(select * from t1 where id1 = id and data1 = dt) or +not exists (select * from t2 where id2 = id and data2 = dt)) +do +select 1; +end for; +end // +call p1(1,2); +1 +1 +call p1(1,2); +1 +1 +drop procedure p1; +drop table t1,t2; # End of 10.4 tests diff --git a/mysql-test/main/parser.test b/mysql-test/main/parser.test index 608f76557e5..502bbde5ea5 100644 --- a/mysql-test/main/parser.test +++ b/mysql-test/main/parser.test @@ -1696,4 +1696,101 @@ KILL ( SELECT 1 ) + LASTVAL(s); --error ER_SUBQUERIES_NOT_SUPPORTED KILL LASTVAL(s); +--echo # +--echo # MDEV-23094: Multiple calls to a Stored Procedure from another +--echo # Stored Procedure crashes server +--echo # + +create table t1 (id1 int primary key, data1 int); +create table t2 (id2 int primary key, data2 int); + +delimiter //; +create procedure p1(IN id int, IN dt int) +begin + if (exists(select * from t1 where id1 = id and data1 = dt) or + not exists (select * from t2 where id2 = id and data2 = dt)) + then + select 1; + end if; +end // +delimiter ;// + +call p1(1,2); +call p1(1,2); + +drop procedure p1; + +delimiter //; +create procedure p1(IN id int, IN dt int) +begin +case (exists(select * from t1 where id1 = id and data1 = dt) or + not exists (select * from t2 where id2 = id and data2 = dt)) +when 1 then + select 1; +else + select 0; +end case; +end // +delimiter ;// + +call p1(1,2); +call p1(1,2); + +drop procedure p1; + +delimiter //; +create procedure p1(IN id int, IN dt int) +begin +declare wcont int default 1; +while (exists(select * from t1 where id1 = id and data1 = dt) or + not exists (select * from t2 where id2 = id and data2 = dt)) and wcont +do + select 1; + set wcont=0; +end while; +end // +delimiter ;// + +call p1(1,2); +call p1(1,2); + +drop procedure p1; + +delimiter //; +create procedure p1(IN id int, IN dt int) +begin +declare count int default 1; +repeat + select 1; + set count=count+1; +until (exists(select * from t1 where id1 = id and data1 = dt) or + not exists (select * from t2 where id2 = id and data2 = dt)) and + count < 3 +end repeat; +end // +delimiter ;// + +call p1(1,2); +call p1(1,2); + +drop procedure p1; + +delimiter //; +create procedure p1(IN id int, IN dt int) +begin +for i in 1..(exists(select * from t1 where id1 = id and data1 = dt) or + not exists (select * from t2 where id2 = id and data2 = dt)) +do + select 1; +end for; +end // +delimiter ;// + +call p1(1,2); +call p1(1,2); + +drop procedure p1; + +drop table t1,t2; + --echo # End of 10.4 tests diff --git a/mysql-test/suite/compat/oracle/r/parser.result b/mysql-test/suite/compat/oracle/r/parser.result index 0b573027c6d..2d9416d59c1 100644 --- a/mysql-test/suite/compat/oracle/r/parser.result +++ b/mysql-test/suite/compat/oracle/r/parser.result @@ -643,3 +643,118 @@ END; # # End of 10.3 tests # +# +# MDEV-21998: Server crashes in st_select_lex::add_table_to_list +# upon mix of KILL and sequences +# +KILL ( SELECT 1 ) + LASTVAL(s); +ERROR 42000: KILL does not support subqueries or stored functions +KILL LASTVAL(s); +ERROR 42000: KILL does not support subqueries or stored functions +# +# MDEV-23094: Multiple calls to a Stored Procedure from another +# Stored Procedure crashes server +# +create table t1 (id1 int primary key, data1 int); +create table t2 (id2 int primary key, data2 int); +create procedure p1(id int,dt int) as +begin +if (exists(select * from t1 where id1 = id and data1 = dt) or +not exists (select * from t2 where id2 = id and data2 = dt)) +then +select 1; +end if; +end // +call p1(1,2); +1 +1 +call p1(1,2); +1 +1 +drop procedure p1; +create procedure p1(id int, dt int) as +begin +case (exists(select * from t1 where id1 = id and data1 = dt) or +not exists (select * from t2 where id2 = id and data2 = dt)) +when 1 then +select 1; +else +select 0; +end case; +end // +call p1(1,2); +1 +1 +call p1(1,2); +1 +1 +drop procedure p1; +create procedure p1(id int, dt int) as +begin +declare wcont int default 1; +begin +while (exists(select * from t1 where id1 = id and data1 = dt) or +not exists (select * from t2 where id2 = id and data2 = dt)) and wcont +loop +select 1; +set wcont=0; +end loop; +end; +end // +call p1(1,2); +1 +1 +call p1(1,2); +1 +1 +drop procedure p1; +create procedure p1(id int, dt int) as +begin +declare count int default 1; +begin +repeat +select 1; +set count=count+1; +until (exists(select * from t1 where id1 = id and data1 = dt) or +not exists (select * from t2 where id2 = id and data2 = dt)) and +count < 3 +end repeat; +end; +end // +call p1(1,2); +1 +1 +call p1(1,2); +1 +1 +drop procedure p1; +create procedure p1(id int, dt int) as +begin +for i in 1..(exists(select * from t1 where id1 = id and data1 = dt) or +not exists (select * from t2 where id2 = id and data2 = dt)) +loop +select 1; +end loop; +end // +call p1(1,2); +1 +1 +call p1(1,2); +1 +1 +drop procedure p1; +set sql_mode=ORACLE; +create or replace procedure p1(id int, dt int) as +begin +while (1) +loop +exit when (exists(select * from t1 where id1 = id and data1 = dt) or +not exists (select * from t2 where id2 = id and data2 = dt)); +end loop; +end; +// +call p1(1,2); +call p1(1,2); +drop procedure p1; +drop table t1,t2; +# End of 10.4 tests diff --git a/mysql-test/suite/compat/oracle/t/parser.test b/mysql-test/suite/compat/oracle/t/parser.test index 4d558c5d153..8dabdc1b3bd 100644 --- a/mysql-test/suite/compat/oracle/t/parser.test +++ b/mysql-test/suite/compat/oracle/t/parser.test @@ -459,3 +459,135 @@ DELIMITER ;// --echo # --echo # End of 10.3 tests --echo # + + +--echo # +--echo # MDEV-21998: Server crashes in st_select_lex::add_table_to_list +--echo # upon mix of KILL and sequences +--echo # + +--error ER_SUBQUERIES_NOT_SUPPORTED +KILL ( SELECT 1 ) + LASTVAL(s); +--error ER_SUBQUERIES_NOT_SUPPORTED +KILL LASTVAL(s); + +--echo # +--echo # MDEV-23094: Multiple calls to a Stored Procedure from another +--echo # Stored Procedure crashes server +--echo # + +create table t1 (id1 int primary key, data1 int); +create table t2 (id2 int primary key, data2 int); + +delimiter //; +create procedure p1(id int,dt int) as +begin + if (exists(select * from t1 where id1 = id and data1 = dt) or + not exists (select * from t2 where id2 = id and data2 = dt)) + then + select 1; + end if; +end // +delimiter ;// + +call p1(1,2); +call p1(1,2); + +drop procedure p1; + +delimiter //; +create procedure p1(id int, dt int) as +begin +case (exists(select * from t1 where id1 = id and data1 = dt) or + not exists (select * from t2 where id2 = id and data2 = dt)) +when 1 then + select 1; +else + select 0; +end case; +end // +delimiter ;// + +call p1(1,2); +call p1(1,2); + +drop procedure p1; + +delimiter //; +create procedure p1(id int, dt int) as +begin +declare wcont int default 1; +begin + while (exists(select * from t1 where id1 = id and data1 = dt) or + not exists (select * from t2 where id2 = id and data2 = dt)) and wcont + loop + select 1; + set wcont=0; + end loop; +end; +end // +delimiter ;// + +call p1(1,2); +call p1(1,2); + +drop procedure p1; + +delimiter //; +create procedure p1(id int, dt int) as +begin +declare count int default 1; +begin + repeat + select 1; + set count=count+1; + until (exists(select * from t1 where id1 = id and data1 = dt) or + not exists (select * from t2 where id2 = id and data2 = dt)) and + count < 3 + end repeat; +end; +end // +delimiter ;// + +call p1(1,2); +call p1(1,2); + +drop procedure p1; + +delimiter //; +create procedure p1(id int, dt int) as +begin +for i in 1..(exists(select * from t1 where id1 = id and data1 = dt) or + not exists (select * from t2 where id2 = id and data2 = dt)) +loop + select 1; +end loop; +end // +delimiter ;// + +call p1(1,2); +call p1(1,2); + +drop procedure p1; + +delimiter //; +set sql_mode=ORACLE; +create or replace procedure p1(id int, dt int) as +begin + while (1) + loop + exit when (exists(select * from t1 where id1 = id and data1 = dt) or + not exists (select * from t2 where id2 = id and data2 = dt)); + end loop; +end; +// +delimiter ;// + +call p1(1,2); +call p1(1,2); + +drop procedure p1; + +drop table t1,t2; + +--echo # End of 10.4 tests diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index bb75ebd12a1..cbb1138fe4d 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2428,6 +2428,7 @@ void st_select_lex::init_query() changed_elements= 0; first_natural_join_processing= 1; first_cond_optimization= 1; + is_service_select= 0; parsing_place= NO_MATTER; save_parsing_place= NO_MATTER; exclude_from_table_unique_test= no_wrap_view_item= FALSE; @@ -7614,7 +7615,7 @@ Item *LEX::create_item_ident_sp(THD *thd, Lex_ident_sys_st *name, return new (thd->mem_root) Item_func_sqlerrm(thd); } - if (!select_stack_head() && + if (fields_are_impossible() && (current_select->parsing_place != FOR_LOOP_BOUND || spcont->find_cursor(name, &unused_off, false) == NULL)) { @@ -8940,11 +8941,12 @@ void st_select_lex::add_statistics(SELECT_LEX_UNIT *unit) } -bool LEX::main_select_push() +bool LEX::main_select_push(bool service) { DBUG_ENTER("LEX::main_select_push"); current_select_number= 1; builtin_select.select_number= 1; + builtin_select.is_service_select= service; if (push_select(&builtin_select)) DBUG_RETURN(TRUE); DBUG_RETURN(FALSE); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index ce30a630388..c8fca748b77 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1249,6 +1249,8 @@ public: bool no_wrap_view_item; /* exclude this select from check of unique_table() */ bool exclude_from_table_unique_test; + /* the select is "service-select" and can not have tables*/ + bool is_service_select; /* index in the select list of the expression currently being fixed */ int cur_pos_in_select_list; @@ -4413,7 +4415,7 @@ public: SELECT_LEX_UNIT *create_unit(SELECT_LEX*); SELECT_LEX *wrap_unit_into_derived(SELECT_LEX_UNIT *unit); SELECT_LEX *wrap_select_chain_into_derived(SELECT_LEX *sel); - bool main_select_push(); + bool main_select_push(bool service= false); bool insert_select_hack(SELECT_LEX *sel); SELECT_LEX *create_priority_nest(SELECT_LEX *first_in_nest); @@ -4530,6 +4532,16 @@ public: Lex_field_type_st *type) const; void mark_first_table_as_inserting(); + + bool fields_are_impossible() + { + // no select or it is last select with no tables (service select) + return !select_stack_head() || + (select_stack_top == 1 && + select_stack[0]->is_service_select); + } + + }; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index bf33148811e..909cb245b2e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -8049,6 +8049,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, (alias ? alias->str : table->table.str), table, this, select_number)); + DBUG_ASSERT(!is_service_select || (table_options & TL_OPTION_SEQUENCE)); if (unlikely(!table)) DBUG_RETURN(0); // End of memory diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 7091ffc2c58..a813de0ef85 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1131,6 +1131,8 @@ JOIN::prepare(TABLE_LIST *tables_init, proc_param= proc_param_init; tables_list= tables_init; select_lex= select_lex_arg; + DBUG_PRINT("info", ("select %p (%u) = JOIN %p", + select_lex, select_lex->select_number, this)); select_lex->join= this; join_list= &select_lex->top_join_list; union_part= unit_arg->is_unit_op(); @@ -4494,6 +4496,9 @@ int JOIN::destroy() { DBUG_ENTER("JOIN::destroy"); + + DBUG_PRINT("info", ("select %p (%u) <> JOIN %p", + select_lex, select_lex->select_number, this)); select_lex->join= 0; cond_equal= 0; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index e8b66bb8bd7..a487f4d4d73 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -2080,6 +2080,8 @@ bool st_select_lex::cleanup() bool error= FALSE; DBUG_ENTER("st_select_lex::cleanup()"); + DBUG_PRINT("info", ("select: %p (%u) JOIN %p", + this, select_number, join)); cleanup_order(order_list.first); cleanup_order(group_list.first); cleanup_ftfuncs(this); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 14b7a5589e1..83323e3f7e3 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2809,8 +2809,6 @@ create: { Lex->create_info.default_table_charset= NULL; Lex->create_info.used_fields= 0; - if (Lex->main_select_push()) - MYSQL_YYABORT; } opt_create_database_options { @@ -2819,7 +2817,6 @@ create: $1 | $3))) MYSQL_YYABORT; lex->name= $4; - Lex->pop_select(); //main select } | create_or_replace definer_opt opt_view_suid VIEW_SYM opt_if_not_exists table_ident @@ -3653,10 +3650,13 @@ sp_cursor_stmt: { DBUG_ASSERT(thd->free_list == NULL); Lex->sphead->reset_lex(thd, $1); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; } select { DBUG_ASSERT(Lex == $1); + Lex->pop_select(); //main select if (unlikely($1->stmt_finalize(thd)) || unlikely($1->sphead->restore_lex(thd))) MYSQL_YYABORT; @@ -4154,6 +4154,11 @@ sp_proc_stmt_statement: Lex_input_stream *lip= YYLIP; lex->sphead->reset_lex(thd); + /* + We should not push main select here, it will be done or not + done by the statement, we just provide only a new LEX for the + statement here as if it is start of parsing a new statement. + */ lex->sphead->m_tmp_query= lip->get_tok_start(); } statement @@ -4172,11 +4177,16 @@ RETURN_ALLMODES_SYM: sp_proc_stmt_return: RETURN_ALLMODES_SYM - { Lex->sphead->reset_lex(thd); } + { + Lex->sphead->reset_lex(thd); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; + } expr { LEX *lex= Lex; sp_head *sp= lex->sphead; + Lex->pop_select(); //main select if (unlikely(sp->m_handler->add_instr_freturn(thd, sp, lex->spcont, $3, lex)) || unlikely(sp->restore_lex(thd))) @@ -4193,7 +4203,16 @@ sp_proc_stmt_return: ; reset_lex_expr: - { Lex->sphead->reset_lex(thd); } expr { $$= $2; } + { + Lex->sphead->reset_lex(thd); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; + } + expr + { + Lex->pop_select(); //main select + $$= $2; + } ; sp_proc_stmt_exit_oracle: @@ -4285,6 +4304,8 @@ assignment_source_expr: { DBUG_ASSERT(thd->free_list == NULL); Lex->sphead->reset_lex(thd, $1); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; } expr { @@ -4293,6 +4314,7 @@ assignment_source_expr: $$->sp_lex_in_use= true; $$->set_item_and_free_list($3, thd->free_list); thd->free_list= NULL; + Lex->pop_select(); //min select if ($$->sphead->restore_lex(thd)) MYSQL_YYABORT; } @@ -4302,6 +4324,8 @@ for_loop_bound_expr: assignment_source_lex { Lex->sphead->reset_lex(thd, $1); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; Lex->current_select->parsing_place= FOR_LOOP_BOUND; } expr @@ -4310,6 +4334,7 @@ for_loop_bound_expr: $$= $1; $$->sp_lex_in_use= true; $$->set_item_and_free_list($3, NULL); + Lex->pop_select(); //main select if (unlikely($$->sphead->restore_lex(thd))) MYSQL_YYABORT; Lex->current_select->parsing_place= NO_MATTER; @@ -4424,7 +4449,11 @@ sp_fetch_list: ; sp_if: - { Lex->sphead->reset_lex(thd); } + { + Lex->sphead->reset_lex(thd); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; + } expr THEN_SYM { LEX *lex= Lex; @@ -4438,6 +4467,7 @@ sp_if: unlikely(sp->add_cont_backpatch(i)) || unlikely(sp->add_instr(i))) MYSQL_YYABORT; + Lex->pop_select(); //main select if (unlikely(sp->restore_lex(thd))) MYSQL_YYABORT; } @@ -4538,12 +4568,17 @@ case_stmt_specification: ; case_stmt_body: - { Lex->sphead->reset_lex(thd); /* For expr $2 */ } + { + Lex->sphead->reset_lex(thd); /* For expr $2 */ + if (Lex->main_select_push(true)) + MYSQL_YYABORT; + } expr { if (unlikely(Lex->case_stmt_action_expr($2))) MYSQL_YYABORT; + Lex->pop_select(); //main select if (Lex->sphead->restore_lex(thd)) MYSQL_YYABORT; } @@ -4567,6 +4602,8 @@ simple_when_clause: WHEN_SYM { Lex->sphead->reset_lex(thd); /* For expr $3 */ + if (Lex->main_select_push(true)) + MYSQL_YYABORT; } expr { @@ -4575,6 +4612,7 @@ simple_when_clause: LEX *lex= Lex; if (unlikely(lex->case_stmt_action_when($3, true))) MYSQL_YYABORT; + Lex->pop_select(); //main select /* For expr $3 */ if (unlikely(lex->sphead->restore_lex(thd))) MYSQL_YYABORT; @@ -4591,12 +4629,15 @@ searched_when_clause: WHEN_SYM { Lex->sphead->reset_lex(thd); /* For expr $3 */ + if (Lex->main_select_push(true)) + MYSQL_YYABORT; } expr { LEX *lex= Lex; if (unlikely(lex->case_stmt_action_when($3, false))) MYSQL_YYABORT; + Lex->pop_select(); //main select /* For expr $3 */ if (unlikely(lex->sphead->restore_lex(thd))) MYSQL_YYABORT; @@ -4695,9 +4736,15 @@ opt_sp_for_loop_direction: ; sp_for_loop_index_and_bounds: - ident sp_for_loop_bounds + ident { - if (unlikely(Lex->sp_for_loop_declarations(thd, &$$, &$1, $2))) + if (Lex->main_select_push(true)) + MYSQL_YYABORT; + } + sp_for_loop_bounds + { + Lex->pop_select(); //main select + if (unlikely(Lex->sp_for_loop_declarations(thd, &$$, &$1, $3))) MYSQL_YYABORT; } ; @@ -4743,8 +4790,11 @@ while_body: LEX *lex= Lex; if (unlikely(lex->sp_while_loop_expression(thd, $1))) MYSQL_YYABORT; + Lex->pop_select(); //main select if (lex->sphead->restore_lex(thd)) MYSQL_YYABORT; + if (lex->main_select_push(true)) + MYSQL_YYABORT; } sp_proc_stmts1 END WHILE_SYM { @@ -4755,7 +4805,11 @@ while_body: repeat_body: sp_proc_stmts1 UNTIL_SYM - { Lex->sphead->reset_lex(thd); } + { + Lex->sphead->reset_lex(thd); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; + } expr END REPEAT_SYM { LEX *lex= Lex; @@ -4766,6 +4820,7 @@ repeat_body: if (unlikely(i == NULL) || unlikely(lex->sphead->add_instr(i))) MYSQL_YYABORT; + Lex->pop_select(); //main select if (lex->sphead->restore_lex(thd)) MYSQL_YYABORT; /* We can shortcut the cont_backpatch here */ @@ -4794,6 +4849,8 @@ sp_labeled_control: if (unlikely(Lex->sp_push_loop_label(thd, &$1))) MYSQL_YYABORT; Lex->sphead->reset_lex(thd); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; } while_body pop_sp_loop_label { } @@ -4845,6 +4902,8 @@ sp_unlabeled_control: if (unlikely(Lex->sp_push_loop_empty_label(thd))) MYSQL_YYABORT; Lex->sphead->reset_lex(thd); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; } while_body { @@ -7816,7 +7875,7 @@ alter: { Lex->create_info.default_table_charset= NULL; Lex->create_info.used_fields= 0; - if (Lex->main_select_push()) + if (Lex->main_select_push(true)) MYSQL_YYABORT; } create_database_options @@ -13265,7 +13324,7 @@ do: { LEX *lex=Lex; lex->sql_command = SQLCOM_DO; - if (lex->main_select_push()) + if (lex->main_select_push(true)) MYSQL_YYABORT; mysql_init_select(lex); } @@ -16453,7 +16512,7 @@ set: SET { LEX *lex=Lex; - if (lex->main_select_push()) + if (lex->main_select_push(true)) MYSQL_YYABORT; lex->set_stmt_init(); lex->var_list.empty(); diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 9366a7bcd9f..c8084f8109c 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -2311,8 +2311,6 @@ create: { Lex->create_info.default_table_charset= NULL; Lex->create_info.used_fields= 0; - if (Lex->main_select_push()) - MYSQL_YYABORT; } opt_create_database_options { @@ -2321,7 +2319,6 @@ create: $1 | $3))) MYSQL_YYABORT; lex->name= $4; - Lex->pop_select(); //main select } | create_or_replace definer_opt opt_view_suid VIEW_SYM opt_if_not_exists table_ident @@ -3547,10 +3544,13 @@ sp_cursor_stmt: { DBUG_ASSERT(thd->free_list == NULL); Lex->sphead->reset_lex(thd, $1); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; } select { DBUG_ASSERT(Lex == $1); + Lex->pop_select(); //main select if (unlikely($1->stmt_finalize(thd)) || unlikely($1->sphead->restore_lex(thd))) MYSQL_YYABORT; @@ -4061,6 +4061,11 @@ sp_proc_stmt_statement: Lex_input_stream *lip= YYLIP; lex->sphead->reset_lex(thd); + /* + We should not push main select here, it will be done or not + done by the statement, we just provide only new LEX for the + statement here as if it is start of parsing new statement. + */ lex->sphead->m_tmp_query= lip->get_tok_start(); } sp_statement @@ -4079,11 +4084,16 @@ RETURN_ALLMODES_SYM: sp_proc_stmt_return: RETURN_ALLMODES_SYM - { Lex->sphead->reset_lex(thd); } + { + Lex->sphead->reset_lex(thd); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; + } expr { LEX *lex= Lex; sp_head *sp= lex->sphead; + Lex->pop_select(); //main select if (unlikely(sp->m_handler->add_instr_freturn(thd, sp, lex->spcont, $3, lex)) || unlikely(sp->restore_lex(thd))) @@ -4100,9 +4110,16 @@ sp_proc_stmt_return: ; reset_lex_expr: - { Lex->sphead->reset_lex(thd); } + { + Lex->sphead->reset_lex(thd); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; + } expr - { $$= $2; } + { + $$= $2; + Lex->pop_select(); //main select + } ; sp_proc_stmt_exit_oracle: @@ -4201,6 +4218,8 @@ assignment_source_expr: { DBUG_ASSERT(thd->free_list == NULL); Lex->sphead->reset_lex(thd, $1); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; } expr { @@ -4209,6 +4228,7 @@ assignment_source_expr: $$->sp_lex_in_use= true; $$->set_item_and_free_list($3, thd->free_list); thd->free_list= NULL; + Lex->pop_select(); //main select if ($$->sphead->restore_lex(thd)) MYSQL_YYABORT; } @@ -4218,6 +4238,8 @@ for_loop_bound_expr: assignment_source_lex { Lex->sphead->reset_lex(thd, $1); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; Lex->current_select->parsing_place= FOR_LOOP_BOUND; } expr @@ -4226,6 +4248,7 @@ for_loop_bound_expr: $$= $1; $$->sp_lex_in_use= true; $$->set_item_and_free_list($3, NULL); + Lex->pop_select(); //main select if (unlikely($$->sphead->restore_lex(thd))) MYSQL_YYABORT; Lex->current_select->parsing_place= NO_MATTER; @@ -4338,7 +4361,11 @@ sp_fetch_list: ; sp_if: - { Lex->sphead->reset_lex(thd); } + { + Lex->sphead->reset_lex(thd); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; + } expr THEN_SYM { LEX *lex= Lex; @@ -4352,6 +4379,7 @@ sp_if: unlikely(sp->add_cont_backpatch(i)) || unlikely(sp->add_instr(i))) MYSQL_YYABORT; + Lex->pop_select(); //main select if (unlikely(sp->restore_lex(thd))) MYSQL_YYABORT; } @@ -4452,12 +4480,17 @@ case_stmt_specification: ; case_stmt_body: - { Lex->sphead->reset_lex(thd); /* For expr $2 */ } + { + Lex->sphead->reset_lex(thd); /* For expr $2 */ + if (Lex->main_select_push(true)) + MYSQL_YYABORT; + } expr { if (unlikely(Lex->case_stmt_action_expr($2))) MYSQL_YYABORT; + Lex->pop_select(); //main select if (Lex->sphead->restore_lex(thd)) MYSQL_YYABORT; } @@ -4481,6 +4514,8 @@ simple_when_clause: WHEN_SYM { Lex->sphead->reset_lex(thd); /* For expr $3 */ + if (Lex->main_select_push(true)) + MYSQL_YYABORT; } expr { @@ -4489,6 +4524,7 @@ simple_when_clause: LEX *lex= Lex; if (unlikely(lex->case_stmt_action_when($3, true))) MYSQL_YYABORT; + Lex->pop_select(); //main select /* For expr $3 */ if (unlikely(lex->sphead->restore_lex(thd))) MYSQL_YYABORT; @@ -4505,12 +4541,15 @@ searched_when_clause: WHEN_SYM { Lex->sphead->reset_lex(thd); /* For expr $3 */ + if (Lex->main_select_push(true)) + MYSQL_YYABORT; } expr { LEX *lex= Lex; if (unlikely(lex->case_stmt_action_when($3, false))) MYSQL_YYABORT; + Lex->pop_select(); //main select /* For expr $3 */ if (unlikely(lex->sphead->restore_lex(thd))) MYSQL_YYABORT; @@ -4699,9 +4738,15 @@ opt_sp_for_loop_direction: ; sp_for_loop_index_and_bounds: - ident_directly_assignable sp_for_loop_bounds + ident_directly_assignable { - if (unlikely(Lex->sp_for_loop_declarations(thd, &$$, &$1, $2))) + if (Lex->main_select_push(true)) + MYSQL_YYABORT; + } + sp_for_loop_bounds + { + Lex->pop_select(); //main select + if (unlikely(Lex->sp_for_loop_declarations(thd, &$$, &$1, $3))) MYSQL_YYABORT; } ; @@ -4747,8 +4792,11 @@ while_body: LEX *lex= Lex; if (unlikely(lex->sp_while_loop_expression(thd, $1))) MYSQL_YYABORT; + Lex->pop_select(); //main select if (lex->sphead->restore_lex(thd)) MYSQL_YYABORT; + if (lex->main_select_push(true)) + MYSQL_YYABORT; } sp_proc_stmts1 END LOOP_SYM { @@ -4759,7 +4807,11 @@ while_body: repeat_body: sp_proc_stmts1 UNTIL_SYM - { Lex->sphead->reset_lex(thd); } + { + Lex->sphead->reset_lex(thd); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; + } expr END REPEAT_SYM { LEX *lex= Lex; @@ -4770,6 +4822,7 @@ repeat_body: if (unlikely(i == NULL) || unlikely(lex->sphead->add_instr(i))) MYSQL_YYABORT; + Lex->pop_select(); //main select if (lex->sphead->restore_lex(thd)) MYSQL_YYABORT; /* We can shortcut the cont_backpatch here */ @@ -4798,6 +4851,8 @@ sp_labeled_control: if (unlikely(Lex->sp_push_loop_label(thd, &$1))) MYSQL_YYABORT; Lex->sphead->reset_lex(thd); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; } while_body pop_sp_loop_label { } @@ -4849,6 +4904,8 @@ sp_unlabeled_control: if (unlikely(Lex->sp_push_loop_empty_label(thd))) MYSQL_YYABORT; Lex->sphead->reset_lex(thd); + if (Lex->main_select_push(true)) + MYSQL_YYABORT; } while_body { @@ -7914,7 +7971,7 @@ alter: { Lex->create_info.default_table_charset= NULL; Lex->create_info.used_fields= 0; - if (Lex->main_select_push()) + if (Lex->main_select_push(true)) MYSQL_YYABORT; } create_database_options @@ -13372,7 +13429,7 @@ do: { LEX *lex=Lex; lex->sql_command = SQLCOM_DO; - if (lex->main_select_push()) + if (lex->main_select_push(true)) MYSQL_YYABORT; mysql_init_select(lex); } @@ -16619,7 +16676,7 @@ set: SET { LEX *lex=Lex; - if (lex->main_select_push()) + if (lex->main_select_push(true)) MYSQL_YYABORT; lex->set_stmt_init(); lex->var_list.empty(); From 6112a0f93d137b9754bc04449873311784e0edd9 Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Tue, 11 Aug 2020 21:45:09 +0300 Subject: [PATCH 16/40] MDEV-16372 ER_BASE64_DECODE_ERROR upon replaying binary log via mysqlbinlog --verbose (This commit is exclusively for 10.2 branch. Do not merge it to 10.3) In case of a pattern of non-STMT_END-marked Rows-log-event (A) followed by a STMT_END marked one (B) mysqlbinlog mixes up the base64 encoded rows events with their pseudo sql representation produced by the verbose option: BINLOG ' base64 encoded data for A ### verbose section for A base64 encoded data for B ### verbose section for B '/*!*/; In effect the produced BINLOG '...' query is not valid and is rejected with the error. Examples of this way malformed BINLOG could have been found in binlog_row_annotate.result that gets corrected with the patch. The issue is fixed with introduction an auxiliary IO_CACHE to hold on the verbose comments until the terminal STMT_END event is found. The new cache is emptied out after two pre-existing ones are done at that time. The correctly produced output now for the above case is as the following: BINLOG ' base64 encoded data for A base64 encoded data for B '/*!*/; ### verbose section for A ### verbose section for B Thanks to Alexey Midenkov for the problem recognition and attempt to tackle, and to Venkatesh Duggirala who produced a patch for the upstream whose idea is exploited here, as well as to MDEV-23077 reporter LukeXwang who also contributed a piece of a patch aiming at this issue. --- client/mysqlbinlog.cc | 11 +++-- mysql-test/r/mysqlbinlog_row_minimal.result | 44 +++++++++++++------ .../suite/binlog/r/binlog_row_annotate.result | 2 - .../binlog/t/binlog_mysqlbinlog_row_frag.test | 3 +- .../binlog_row_annotate.result | 1 - mysql-test/t/mysqlbinlog_row_minimal.test | 32 +++++++++++++- sql/log_event.cc | 38 +++++++--------- sql/log_event.h | 6 ++- sql/log_event_old.cc | 8 ++++ 9 files changed, 96 insertions(+), 49 deletions(-) diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 13a92c0b581..4e28876cdf6 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -891,7 +891,8 @@ static bool print_base64(PRINT_EVENT_INFO *print_event_info, Log_event *ev) ev->print(result_file, print_event_info); return print_event_info->head_cache.error == -1 || - print_event_info->body_cache.error == -1; + print_event_info->body_cache.error == -1 || + print_event_info->tail_cache.error == -1; } @@ -962,8 +963,12 @@ static bool print_row_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, my_b_printf(body_cache, "'%s\n", print_event_info->delimiter); // flush cache - if ((copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, result_file) || - copy_event_cache_to_file_and_reinit(&print_event_info->body_cache, result_file))) + if ((copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, + result_file) || + copy_event_cache_to_file_and_reinit(&print_event_info->body_cache, + result_file) || + copy_event_cache_to_file_and_reinit(&print_event_info->tail_cache, + result_file))) return 1; } } diff --git a/mysql-test/r/mysqlbinlog_row_minimal.result b/mysql-test/r/mysqlbinlog_row_minimal.result index e78c3ca3a40..776c7ac5b53 100644 --- a/mysql-test/r/mysqlbinlog_row_minimal.result +++ b/mysql-test/r/mysqlbinlog_row_minimal.result @@ -319,6 +319,24 @@ ROLLBACK /* added by mysqlbinlog */; /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; DROP TABLE t1,t2; +# +# MDEV-16372 ER_BASE64_DECODE_ERROR upon replaying binary log with system table +# +FLUSH BINARY LOGS; +CREATE TABLE t1 (pk INT PRIMARY KEY); +INSERT INTO t1 VALUES (1); +INSERT INTO t1 VALUES (1), (2) ON DUPLICATE KEY UPDATE pk= pk + 10; +FLUSH BINARY LOGS; +Proof: two subsequent patterns must be found +FOUND 1 /### UPDATE `test`.`t1`/ in mysqlbinlog.sql +FOUND 2 /### INSERT INTO `test`.`t1`/ in mysqlbinlog.sql +DROP TABLE t1; +SELECT * FROM t1; +pk +2 +11 +# Cleanup +DROP TABLE t1; CREATE TABLE `t1` ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, `is_deleted` BIT(1) DEFAULT b'0', @@ -343,33 +361,33 @@ FLUSH BINARY LOGS; /*!40019 SET @@session.max_insert_delayed_threads=0*/; /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; DELIMITER /*!*/; -# at 387 -# server id 1 end_log_pos 429 CRC32 XXX GTID 0-1-16 +# at POS +# server id 1 end_log_pos END_LOG_POS CRC32 XXX GTID D-S-N /*!100101 SET @@session.skip_parallel_replication=0*//*!*/; /*!100001 SET @@session.gtid_domain_id=0*//*!*/; /*!100001 SET @@session.server_id=1*//*!*/; -/*!100001 SET @@session.gtid_seq_no=16*//*!*/; +/*!100001 SET @@session.gtid_seq_no=21*//*!*/; BEGIN /*!*/; -# at 429 -# at 543 -# server id 1 end_log_pos 543 CRC32 XXX Annotate_rows: +# at POS +# at POS +# server id 1 end_log_pos END_LOG_POS CRC32 XXX Annotate_rows: #Q> UPDATE t1 t1 INNER JOIN t2 t2 ON t1.ref_id = t2.id #Q> SET t1.is_deleted = TRUE #Q> WHERE t1.id = -# server id 1 end_log_pos 594 CRC32 XXX Table_map: `test`.`t1` mapped to number 34 -# at 594 -# server id 1 end_log_pos 643 CRC32 XXX Update_rows: table id 34 flags: STMT_END_F +# server id 1 end_log_pos END_LOG_POS CRC32 XXX Table_map: `test`.`t1` mapped to number TID +# at POS +# server id 1 end_log_pos END_LOG_POS CRC32 XXX Update_rows: table id TID flags: STMT_END_F ### UPDATE `test`.`t1` ### WHERE ### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */ ### SET ### @2=b'1' /* BIT(1) meta=1 nullable=1 is_null=0 */ ### @3=X /* TIMESTAMP(0) meta=0 nullable=0 is_null=0 */ -# at 643 -# server id 1 end_log_pos 725 CRC32 XXX Query thread_id=5 exec_time=x error_code=0 +# at POS +# server id 1 end_log_pos END_LOG_POS CRC32 XXX Query thread_id=TID exec_time=x error_code=0 SET TIMESTAMP=X/*!*/; -SET @@session.pseudo_thread_id=5/*!*/; +SET @@session.pseudo_thread_id=TID/*!*/; SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1, @@session.check_constraint_checks=1/*!*/; SET @@session.sql_mode=#/*!*/; SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; @@ -379,8 +397,6 @@ SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; COMMIT /*!*/; -# at 725 -# server id 1 end_log_pos 773 CRC32 XXX Rotate to master-bin.000004 pos: 4 DELIMITER ; # End of log file ROLLBACK /* added by mysqlbinlog */; diff --git a/mysql-test/suite/binlog/r/binlog_row_annotate.result b/mysql-test/suite/binlog/r/binlog_row_annotate.result index 2104d6b0e51..732ecdd02fd 100644 --- a/mysql-test/suite/binlog/r/binlog_row_annotate.result +++ b/mysql-test/suite/binlog/r/binlog_row_annotate.result @@ -442,7 +442,6 @@ BEGIN ### DELETE FROM `test1`.`t1` ### WHERE ### @1=3 /* INT meta=0 nullable=1 is_null=0 */ -'/*!*/; # at # #010909 4:46:40 server id # end_log_pos # Query thread_id=# exec_time=# error_code=0 SET TIMESTAMP=1000000000/*!*/; @@ -1053,7 +1052,6 @@ BEGIN ### DELETE FROM `test1`.`t1` ### WHERE ### @1=3 /* INT meta=0 nullable=1 is_null=0 */ -'/*!*/; # at # #010909 4:46:40 server id # end_log_pos # Query thread_id=# exec_time=# error_code=0 SET TIMESTAMP=1000000000/*!*/; diff --git a/mysql-test/suite/binlog/t/binlog_mysqlbinlog_row_frag.test b/mysql-test/suite/binlog/t/binlog_mysqlbinlog_row_frag.test index a1791d9701d..9f79deea3db 100644 --- a/mysql-test/suite/binlog/t/binlog_mysqlbinlog_row_frag.test +++ b/mysql-test/suite/binlog/t/binlog_mysqlbinlog_row_frag.test @@ -11,8 +11,7 @@ SELECT a from t into @a; FLUSH LOGS; DELETE FROM t; -# Todo: MDEV-10362 to test multi-row Rows_log_event:s in verbose mode ---exec $MYSQL_BINLOG -vv --debug-binlog-row-event-max-encoded-size=256 $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql +--exec $MYSQL_BINLOG --verbose --debug-binlog-row-event-max-encoded-size=256 $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql --let SEARCH_PATTERN= BINLOG @binlog_fragment_0, @binlog_fragment_1 --let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql diff --git a/mysql-test/suite/binlog_encryption/binlog_row_annotate.result b/mysql-test/suite/binlog_encryption/binlog_row_annotate.result index 9b843dc8a6b..83903ecc18a 100644 --- a/mysql-test/suite/binlog_encryption/binlog_row_annotate.result +++ b/mysql-test/suite/binlog_encryption/binlog_row_annotate.result @@ -449,7 +449,6 @@ BEGIN ### DELETE FROM `test1`.`t1` ### WHERE ### @1=3 /* INT meta=0 nullable=1 is_null=0 */ -'/*!*/; # at # #010909 4:46:40 server id # end_log_pos # Query thread_id=# exec_time=# error_code=0 SET TIMESTAMP=1000000000/*!*/; diff --git a/mysql-test/t/mysqlbinlog_row_minimal.test b/mysql-test/t/mysqlbinlog_row_minimal.test index b69aa6da14b..b399cc4ac45 100644 --- a/mysql-test/t/mysqlbinlog_row_minimal.test +++ b/mysql-test/t/mysqlbinlog_row_minimal.test @@ -33,6 +33,33 @@ FLUSH BINARY LOGS; DROP TABLE t1,t2; +--echo # +--echo # MDEV-16372 ER_BASE64_DECODE_ERROR upon replaying binary log with system table +--echo # +FLUSH BINARY LOGS; +CREATE TABLE t1 (pk INT PRIMARY KEY); +INSERT INTO t1 VALUES (1); +INSERT INTO t1 VALUES (1), (2) ON DUPLICATE KEY UPDATE pk= pk + 10; + +--let $binlog = query_get_value(SHOW MASTER STATUS, File, 1) + +FLUSH BINARY LOGS; +--exec $MYSQL_BINLOG --verbose $datadir/$binlog > $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql +--echo Proof: two subsequent patterns must be found +--let SEARCH_PATTERN= ### UPDATE `test`.`t1` +--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql +--source include/search_pattern_in_file.inc +--let SEARCH_PATTERN= ### INSERT INTO `test`.`t1` +--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql +--source include/search_pattern_in_file.inc + +DROP TABLE t1; +--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql +SELECT * FROM t1; + +--echo # Cleanup +DROP TABLE t1; +--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql # # MDEV-14605 ON UPDATE CURRENT_TIMESTAMP fields by multi-table UPDATE are not logged with binlog_row_image=MINIMAL # @@ -64,11 +91,12 @@ UPDATE t1 t1 INNER JOIN t2 t2 ON t1.ref_id = t2.id WHERE t1.id = 1; --let $binlog = query_get_value(SHOW MASTER STATUS, File, 1) +--let $binlog_end= query_get_value(SHOW MASTER STATUS, Position, 1) FLUSH BINARY LOGS; --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR ---replace_regex /\d{6} *\d*:\d\d:\d\d// /SET TIMESTAMP=\d*/SET TIMESTAMP=X/ /exec_time=\d*/exec_time=x/ /@3=\d*/@3=X/ /CRC32 0x[0-9a-f]+/CRC32 XXX/ /@@session.sql_mode=\d+/@@session.sql_mode=#/ /collation_server=\d+/collation_server=#/ ---exec $MYSQL_BINLOG --verbose --verbose --base64-output=DECODE-ROWS $datadir/$binlog --start-position=$binlog_pos +--replace_regex /table id \d*/table id TID/ /mapped to number \d*/mapped to number TID/ /at \d*/at POS/ /end_log_pos \d*/end_log_pos END_LOG_POS/ /GTID \d*-\d*-\d*/GTID D-S-N/ /\d{6} *\d*:\d\d:\d\d// /SET TIMESTAMP=\d*/SET TIMESTAMP=X/ /exec_time=\d*/exec_time=x/ /@3=\d*/@3=X/ /CRC32 0x[0-9a-f]+/CRC32 XXX/ /@@session.sql_mode=\d+/@@session.sql_mode=#/ /collation_server=\d+/collation_server=#/ /thread_id=\d*/thread_id=TID/ +--exec $MYSQL_BINLOG --verbose --verbose --base64-output=DECODE-ROWS $datadir/$binlog --start-position=$binlog_pos --stop-position=$binlog_end DROP TABLE t1,t2; diff --git a/sql/log_event.cc b/sql/log_event.cc index 26c9cb6f9cb..e21a18273b3 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3579,7 +3579,7 @@ void Log_event::print_base64(IO_CACHE* file, #ifdef WHEN_FLASHBACK_REVIEW_READY ev->need_flashback_review= need_flashback_review; if (print_event_info->verbose) - ev->print_verbose(file, print_event_info); + ev->print_verbose(&print_event_info->tail_cache, print_event_info); else { IO_CACHE tmp_cache; @@ -3589,18 +3589,7 @@ void Log_event::print_base64(IO_CACHE* file, } #else if (print_event_info->verbose) - { - /* - Verbose event printout can't start before encoded data - got enquoted. This is done at this point though multi-row - statement remain vulnerable. - TODO: fix MDEV-10362 to remove this workaround. - */ - if (print_event_info->base64_output_mode != - BASE64_OUTPUT_DECODE_ROWS) - my_b_printf(file, "'%s\n", print_event_info->delimiter); - ev->print_verbose(file, print_event_info); - } + ev->print_verbose(&print_event_info->tail_cache, print_event_info); #endif delete ev; } @@ -11538,11 +11527,8 @@ void copy_cache_to_string_wrapped(IO_CACHE *cache, str_tmp.length= sprintf(str_tmp.str, fmt_frag, 1); ret.append(&str_tmp); ret.append(cache, uint32(cache->end_of_file - (cache_size/2 + 1))); - if (!is_verbose) - { - str_tmp.length= sprintf(str_tmp.str, fmt_delim, delimiter); - ret.append(&str_tmp); - } + str_tmp.length= sprintf(str_tmp.str, fmt_delim, delimiter); + ret.append(&str_tmp); str_tmp.length= sprintf(str_tmp.str, "BINLOG @binlog_fragment_0, @binlog_fragment_1%s\n", delimiter); ret.append(&str_tmp); @@ -11552,11 +11538,8 @@ void copy_cache_to_string_wrapped(IO_CACHE *cache, str_tmp.length= sprintf(str_tmp.str, str_binlog); ret.append(&str_tmp); ret.append(cache, (uint32) cache->end_of_file); - if (!is_verbose) - { - str_tmp.length= sprintf(str_tmp.str, fmt_delim, delimiter); - ret.append(&str_tmp); - } + str_tmp.length= sprintf(str_tmp.str, fmt_delim, delimiter); + ret.append(&str_tmp); } to->length= ret.length(); @@ -11605,6 +11588,7 @@ void Rows_log_event::print_helper(FILE *file, { IO_CACHE *const head= &print_event_info->head_cache; IO_CACHE *const body= &print_event_info->body_cache; + IO_CACHE *const tail= &print_event_info->tail_cache; #ifdef WHEN_FLASHBACK_REVIEW_READY IO_CACHE *const sql= &print_event_info->review_sql_cache; #endif @@ -11646,6 +11630,13 @@ void Rows_log_event::print_helper(FILE *file, print_event_info->verbose); output_buf.append(&tmp_str); my_free(tmp_str.str); + if (copy_event_cache_to_string_and_reinit(tail, &tmp_str)) + { + tail->error= -1; + return; + } + output_buf.append(&tmp_str); + my_free(tmp_str.str); } } #endif @@ -14316,6 +14307,7 @@ st_print_event_info::st_print_event_info() myf const flags = MYF(MY_WME | MY_NABP); open_cached_file(&head_cache, NULL, NULL, 0, flags); open_cached_file(&body_cache, NULL, NULL, 0, flags); + open_cached_file(&tail_cache, NULL, NULL, 0, flags); #ifdef WHEN_FLASHBACK_REVIEW_READY open_cached_file(&review_sql_cache, NULL, NULL, 0, flags); #endif diff --git a/sql/log_event.h b/sql/log_event.h index 1fae201057f..3fc44a9669f 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -848,18 +848,19 @@ typedef struct st_print_event_info ~st_print_event_info() { close_cached_file(&head_cache); close_cached_file(&body_cache); + close_cached_file(&tail_cache); #ifdef WHEN_FLASHBACK_REVIEW_READY close_cached_file(&review_sql_cache); #endif } bool init_ok() /* tells if construction was successful */ - { return my_b_inited(&head_cache) && my_b_inited(&body_cache) + { return my_b_inited(&head_cache) && my_b_inited(&body_cache) && + my_b_inited(&tail_cache) #ifdef WHEN_FLASHBACK_REVIEW_READY && my_b_inited(&review_sql_cache) #endif ; } - /* Settings on how to print the events */ bool short_form; enum_base64_output_mode base64_output_mode; @@ -885,6 +886,7 @@ typedef struct st_print_event_info */ IO_CACHE head_cache; IO_CACHE body_cache; + IO_CACHE tail_cache; #ifdef WHEN_FLASHBACK_REVIEW_READY /* Storing the SQL for reviewing */ IO_CACHE review_sql_cache; diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index b655d510bd5..2de316a4c55 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1859,6 +1859,7 @@ void Old_rows_log_event::print_helper(FILE *file, { IO_CACHE *const head= &print_event_info->head_cache; IO_CACHE *const body= &print_event_info->body_cache; + IO_CACHE *const tail= &print_event_info->tail_cache; bool do_print_encoded= print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS && !print_event_info->short_form; @@ -1890,6 +1891,13 @@ void Old_rows_log_event::print_helper(FILE *file, print_event_info->verbose); output_buf.append(&tmp_str); my_free(tmp_str.str); + if (copy_event_cache_to_string_and_reinit(tail, &tmp_str)) + { + tail->error= -1; + return; + } + output_buf.append(&tmp_str); + my_free(tmp_str.str); } } #endif From caa35f8e25ce22d6b4f4c377970354cf582c7f41 Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Tue, 11 Aug 2020 21:45:09 +0300 Subject: [PATCH 17/40] MDEV-16372 ER_BASE64_DECODE_ERROR upon replaying binary log via mysqlbinlog --verbose (This commit is for 10.3 and upper branches) In case of a pattern of non-STMT_END-marked Rows-log-event (A) followed by a STMT_END marked one (B) mysqlbinlog mixes up the base64 encoded rows events with their pseudo sql representation produced by the verbose option: BINLOG ' base64 encoded data for A ### verbose section for A base64 encoded data for B ### verbose section for B '/*!*/; In effect the produced BINLOG '...' query is not valid and is rejected with the error. Examples of this way malformed BINLOG could have been found in binlog_row_annotate.result that gets corrected with the patch. The issue is fixed with introduction an auxiliary IO_CACHE to hold on the verbose comments until the terminal STMT_END event is found. The new cache is emptied out after two pre-existing ones are done at that time. The correctly produced output now for the above case is as the following: BINLOG ' base64 encoded data for A base64 encoded data for B '/*!*/; ### verbose section for A ### verbose section for B Thanks to Alexey Midenkov for the problem recognition and attempt to tackle, and to Venkatesh Duggirala who produced a patch for the upstream whose idea is exploited here, as well as to MDEV-23077 reporter LukeXwang who also contributed a piece of a patch aiming at this issue. --- client/mysqlbinlog.cc | 8 +++- .../main/mysqlbinlog_row_minimal.result | 44 +++++++++++++------ mysql-test/main/mysqlbinlog_row_minimal.test | 32 +++++++++++++- .../binlog/r/binlog_mysqlbinlog_row.result | 4 +- .../suite/binlog/r/binlog_row_annotate.result | 2 - .../binlog/t/binlog_mysqlbinlog_row_frag.test | 3 +- .../binlog_row_annotate.result | 1 - sql/log_event.cc | 37 ++++++---------- sql/log_event.h | 2 + sql/log_event_old.cc | 6 ++- 10 files changed, 88 insertions(+), 51 deletions(-) diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index a732a6ab8c3..1fa16015f4a 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -969,8 +969,12 @@ static bool print_row_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, my_b_printf(body_cache, "'%s\n", print_event_info->delimiter); // flush cache - if ((copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, result_file) || - copy_event_cache_to_file_and_reinit(&print_event_info->body_cache, result_file))) + if ((copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, + result_file) || + copy_event_cache_to_file_and_reinit(&print_event_info->body_cache, + result_file) || + copy_event_cache_to_file_and_reinit(&print_event_info->tail_cache, + result_file))) return 1; } } diff --git a/mysql-test/main/mysqlbinlog_row_minimal.result b/mysql-test/main/mysqlbinlog_row_minimal.result index 933457edc25..aeaabab623a 100644 --- a/mysql-test/main/mysqlbinlog_row_minimal.result +++ b/mysql-test/main/mysqlbinlog_row_minimal.result @@ -327,6 +327,24 @@ ROLLBACK /* added by mysqlbinlog */; /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; DROP TABLE t1,t2; +# +# MDEV-16372 ER_BASE64_DECODE_ERROR upon replaying binary log with system table +# +FLUSH BINARY LOGS; +CREATE TABLE t1 (pk INT PRIMARY KEY); +INSERT INTO t1 VALUES (1); +INSERT INTO t1 VALUES (1), (2) ON DUPLICATE KEY UPDATE pk= pk + 10; +FLUSH BINARY LOGS; +Proof: two subsequent patterns must be found +FOUND 1 /### UPDATE `test`.`t1`/ in mysqlbinlog.sql +FOUND 2 /### INSERT INTO `test`.`t1`/ in mysqlbinlog.sql +DROP TABLE t1; +SELECT * FROM t1; +pk +2 +11 +# Cleanup +DROP TABLE t1; CREATE TABLE `t1` ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, `is_deleted` BIT(1) DEFAULT b'0', @@ -351,23 +369,23 @@ FLUSH BINARY LOGS; /*!40019 SET @@session.max_insert_delayed_threads=0*/; /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; DELIMITER /*!*/; -# at 387 -# server id 1 end_log_pos 429 CRC32 XXX GTID 0-1-16 +# at POS +# server id 1 end_log_pos END_LOG_POS CRC32 XXX GTID D-S-N /*!100101 SET @@session.skip_parallel_replication=0*//*!*/; /*!100001 SET @@session.gtid_domain_id=0*//*!*/; /*!100001 SET @@session.server_id=1*//*!*/; -/*!100001 SET @@session.gtid_seq_no=16*//*!*/; +/*!100001 SET @@session.gtid_seq_no=21*//*!*/; START TRANSACTION /*!*/; -# at 429 -# at 543 -# server id 1 end_log_pos 543 CRC32 XXX Annotate_rows: +# at POS +# at POS +# server id 1 end_log_pos END_LOG_POS CRC32 XXX Annotate_rows: #Q> UPDATE t1 t1 INNER JOIN t2 t2 ON t1.ref_id = t2.id #Q> SET t1.is_deleted = TRUE #Q> WHERE t1.id = -# server id 1 end_log_pos 594 CRC32 XXX Table_map: `test`.`t1` mapped to number 35 -# at 594 -# server id 1 end_log_pos 643 CRC32 XXX Update_rows: table id 35 flags: STMT_END_F +# server id 1 end_log_pos END_LOG_POS CRC32 XXX Table_map: `test`.`t1` mapped to number TID +# at POS +# server id 1 end_log_pos END_LOG_POS CRC32 XXX Update_rows: table id TID flags: STMT_END_F ### UPDATE `test`.`t1` ### WHERE ### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */ @@ -375,10 +393,10 @@ START TRANSACTION ### @2=b'1' /* BIT(1) meta=1 nullable=1 is_null=0 */ ### @3=X /* TIMESTAMP(0) meta=0 nullable=0 is_null=0 */ # Number of rows: 1 -# at 643 -# server id 1 end_log_pos 725 CRC32 XXX Query thread_id=5 exec_time=x error_code=0 +# at POS +# server id 1 end_log_pos END_LOG_POS CRC32 XXX Query thread_id=TID exec_time=x error_code=0 SET TIMESTAMP=X/*!*/; -SET @@session.pseudo_thread_id=5/*!*/; +SET @@session.pseudo_thread_id=TID/*!*/; SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1, @@session.check_constraint_checks=1/*!*/; SET @@session.sql_mode=#/*!*/; SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; @@ -388,8 +406,6 @@ SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; COMMIT /*!*/; -# at 725 -# server id 1 end_log_pos 773 CRC32 XXX Rotate to master-bin.000004 pos: 4 DELIMITER ; # End of log file ROLLBACK /* added by mysqlbinlog */; diff --git a/mysql-test/main/mysqlbinlog_row_minimal.test b/mysql-test/main/mysqlbinlog_row_minimal.test index b69aa6da14b..b399cc4ac45 100644 --- a/mysql-test/main/mysqlbinlog_row_minimal.test +++ b/mysql-test/main/mysqlbinlog_row_minimal.test @@ -33,6 +33,33 @@ FLUSH BINARY LOGS; DROP TABLE t1,t2; +--echo # +--echo # MDEV-16372 ER_BASE64_DECODE_ERROR upon replaying binary log with system table +--echo # +FLUSH BINARY LOGS; +CREATE TABLE t1 (pk INT PRIMARY KEY); +INSERT INTO t1 VALUES (1); +INSERT INTO t1 VALUES (1), (2) ON DUPLICATE KEY UPDATE pk= pk + 10; + +--let $binlog = query_get_value(SHOW MASTER STATUS, File, 1) + +FLUSH BINARY LOGS; +--exec $MYSQL_BINLOG --verbose $datadir/$binlog > $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql +--echo Proof: two subsequent patterns must be found +--let SEARCH_PATTERN= ### UPDATE `test`.`t1` +--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql +--source include/search_pattern_in_file.inc +--let SEARCH_PATTERN= ### INSERT INTO `test`.`t1` +--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql +--source include/search_pattern_in_file.inc + +DROP TABLE t1; +--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql +SELECT * FROM t1; + +--echo # Cleanup +DROP TABLE t1; +--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql # # MDEV-14605 ON UPDATE CURRENT_TIMESTAMP fields by multi-table UPDATE are not logged with binlog_row_image=MINIMAL # @@ -64,11 +91,12 @@ UPDATE t1 t1 INNER JOIN t2 t2 ON t1.ref_id = t2.id WHERE t1.id = 1; --let $binlog = query_get_value(SHOW MASTER STATUS, File, 1) +--let $binlog_end= query_get_value(SHOW MASTER STATUS, Position, 1) FLUSH BINARY LOGS; --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR ---replace_regex /\d{6} *\d*:\d\d:\d\d// /SET TIMESTAMP=\d*/SET TIMESTAMP=X/ /exec_time=\d*/exec_time=x/ /@3=\d*/@3=X/ /CRC32 0x[0-9a-f]+/CRC32 XXX/ /@@session.sql_mode=\d+/@@session.sql_mode=#/ /collation_server=\d+/collation_server=#/ ---exec $MYSQL_BINLOG --verbose --verbose --base64-output=DECODE-ROWS $datadir/$binlog --start-position=$binlog_pos +--replace_regex /table id \d*/table id TID/ /mapped to number \d*/mapped to number TID/ /at \d*/at POS/ /end_log_pos \d*/end_log_pos END_LOG_POS/ /GTID \d*-\d*-\d*/GTID D-S-N/ /\d{6} *\d*:\d\d:\d\d// /SET TIMESTAMP=\d*/SET TIMESTAMP=X/ /exec_time=\d*/exec_time=x/ /@3=\d*/@3=X/ /CRC32 0x[0-9a-f]+/CRC32 XXX/ /@@session.sql_mode=\d+/@@session.sql_mode=#/ /collation_server=\d+/collation_server=#/ /thread_id=\d*/thread_id=TID/ +--exec $MYSQL_BINLOG --verbose --verbose --base64-output=DECODE-ROWS $datadir/$binlog --start-position=$binlog_pos --stop-position=$binlog_end DROP TABLE t1,t2; diff --git a/mysql-test/suite/binlog/r/binlog_mysqlbinlog_row.result b/mysql-test/suite/binlog/r/binlog_mysqlbinlog_row.result index 469eb97badc..424abc92bc4 100644 --- a/mysql-test/suite/binlog/r/binlog_mysqlbinlog_row.result +++ b/mysql-test/suite/binlog/r/binlog_mysqlbinlog_row.result @@ -5356,7 +5356,5 @@ START TRANSACTION #010909 4:46:40 server id 1 end_log_pos # CRC32 XXX Table_map: `test`.`t1dec102` mapped to number # # at # #010909 4:46:40 server id 1 end_log_pos # CRC32 XXX Write_rows: table id # flags: STMT_END_F -### INSERT INTO `test`.`t1dec102` -### SET -### @1= + Error: Found Old DECIMAL (mysql-4.1 or earlier). Not enough metadata to display the value. diff --git a/mysql-test/suite/binlog/r/binlog_row_annotate.result b/mysql-test/suite/binlog/r/binlog_row_annotate.result index e65569853aa..04a007ffa41 100644 --- a/mysql-test/suite/binlog/r/binlog_row_annotate.result +++ b/mysql-test/suite/binlog/r/binlog_row_annotate.result @@ -449,7 +449,6 @@ START TRANSACTION ### DELETE FROM `test1`.`t1` ### WHERE ### @1=3 /* INT meta=0 nullable=1 is_null=0 */ -'/*!*/; # at # #010909 4:46:40 server id # end_log_pos # Query thread_id=# exec_time=# error_code=0 SET TIMESTAMP=1000000000/*!*/; @@ -1073,7 +1072,6 @@ START TRANSACTION ### DELETE FROM `test1`.`t1` ### WHERE ### @1=3 /* INT meta=0 nullable=1 is_null=0 */ -'/*!*/; # at # #010909 4:46:40 server id # end_log_pos # Query thread_id=# exec_time=# error_code=0 SET TIMESTAMP=1000000000/*!*/; diff --git a/mysql-test/suite/binlog/t/binlog_mysqlbinlog_row_frag.test b/mysql-test/suite/binlog/t/binlog_mysqlbinlog_row_frag.test index a1791d9701d..9f79deea3db 100644 --- a/mysql-test/suite/binlog/t/binlog_mysqlbinlog_row_frag.test +++ b/mysql-test/suite/binlog/t/binlog_mysqlbinlog_row_frag.test @@ -11,8 +11,7 @@ SELECT a from t into @a; FLUSH LOGS; DELETE FROM t; -# Todo: MDEV-10362 to test multi-row Rows_log_event:s in verbose mode ---exec $MYSQL_BINLOG -vv --debug-binlog-row-event-max-encoded-size=256 $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql +--exec $MYSQL_BINLOG --verbose --debug-binlog-row-event-max-encoded-size=256 $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql --let SEARCH_PATTERN= BINLOG @binlog_fragment_0, @binlog_fragment_1 --let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql diff --git a/mysql-test/suite/binlog_encryption/binlog_row_annotate.result b/mysql-test/suite/binlog_encryption/binlog_row_annotate.result index 004f163f192..3940717bf0b 100644 --- a/mysql-test/suite/binlog_encryption/binlog_row_annotate.result +++ b/mysql-test/suite/binlog_encryption/binlog_row_annotate.result @@ -456,7 +456,6 @@ START TRANSACTION ### DELETE FROM `test1`.`t1` ### WHERE ### @1=3 /* INT meta=0 nullable=1 is_null=0 */ -'/*!*/; # at # #010909 4:46:40 server id # end_log_pos # Query thread_id=# exec_time=# error_code=0 SET TIMESTAMP=1000000000/*!*/; diff --git a/sql/log_event.cc b/sql/log_event.cc index 4432a51b010..1e382cab55f 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3875,7 +3875,7 @@ bool Log_event::print_base64(IO_CACHE* file, ev->need_flashback_review= need_flashback_review; if (print_event_info->verbose) { - if (ev->print_verbose(file, print_event_info)) + if (ev->print_verbose(&print_event_info->tail_cache, print_event_info)) goto err; } else @@ -3899,22 +3899,9 @@ bool Log_event::print_base64(IO_CACHE* file, } #else if (print_event_info->verbose) - { - /* - Verbose event printout can't start before encoded data - got enquoted. This is done at this point though multi-row - statement remain vulnerable. - TODO: fix MDEV-10362 to remove this workaround. - */ - if (print_event_info->base64_output_mode != - BASE64_OUTPUT_DECODE_ROWS) - my_b_printf(file, "'%s\n", print_event_info->delimiter); - error= ev->print_verbose(file, print_event_info); - } + error= ev->print_verbose(&print_event_info->tail_cache, print_event_info); else - { ev->count_row_events(print_event_info); - } #endif delete ev; if (unlikely(error)) @@ -11992,7 +11979,7 @@ bool copy_cache_to_file_wrapped(IO_CACHE *body, FILE *file, bool do_wrap, const char *delimiter, - bool is_verbose) + bool is_verbose /*TODO: remove */) { const my_off_t cache_size= my_b_tell(body); @@ -12025,8 +12012,7 @@ bool copy_cache_to_file_wrapped(IO_CACHE *body, my_fprintf(file, fmt_frag, 1); if (my_b_copy_to_file(body, file, SIZE_T_MAX)) goto err; - if (!is_verbose) - my_fprintf(file, fmt_delim, delimiter); + my_fprintf(file, fmt_delim, delimiter); my_fprintf(file, fmt_binlog2, delimiter); } @@ -12035,8 +12021,7 @@ bool copy_cache_to_file_wrapped(IO_CACHE *body, my_fprintf(file, str_binlog); if (my_b_copy_to_file(body, file, SIZE_T_MAX)) goto err; - if (!is_verbose) - my_fprintf(file, fmt_delim, delimiter); + my_fprintf(file, fmt_delim, delimiter); } reinit_io_cache(body, WRITE_CACHE, 0, FALSE, TRUE); @@ -12122,7 +12107,6 @@ bool copy_cache_to_string_wrapped(IO_CACHE *cache, goto err; str += (add_to_len= uint32(cache->end_of_file - (cache_size/2 + 1))); to->length += add_to_len; - if (!is_verbose) { str += (add_to_len= sprintf(str , fmt_delim, delimiter)); to->length += add_to_len; @@ -12138,7 +12122,6 @@ bool copy_cache_to_string_wrapped(IO_CACHE *cache, goto err; str += cache->end_of_file; to->length += (size_t)cache->end_of_file; - if (!is_verbose) to->length += sprintf(str , fmt_delim, delimiter); } @@ -12186,6 +12169,7 @@ bool Rows_log_event::print_helper(FILE *file, { IO_CACHE *const head= &print_event_info->head_cache; IO_CACHE *const body= &print_event_info->body_cache; + IO_CACHE *const tail= &print_event_info->tail_cache; #ifdef WHEN_FLASHBACK_REVIEW_READY IO_CACHE *const sql= &print_event_info->review_sql_cache; #endif @@ -12216,7 +12200,8 @@ bool Rows_log_event::print_helper(FILE *file, if (copy_event_cache_to_file_and_reinit(head, file) || copy_cache_to_file_wrapped(body, file, do_print_encoded, print_event_info->delimiter, - print_event_info->verbose)) + print_event_info->verbose) || + copy_event_cache_to_file_and_reinit(tail, file)) goto err; } else @@ -12234,6 +12219,11 @@ bool Rows_log_event::print_helper(FILE *file, return 1; output_buf.append(tmp_str.str, tmp_str.length); my_free(tmp_str.str); + if (copy_event_cache_to_string_and_reinit(tail, &tmp_str)) + return 1; + output_buf.append(tmp_str.str, tmp_str.length); + my_free(tmp_str.str); + #ifdef WHEN_FLASHBACK_REVIEW_READY if (copy_event_cache_to_string_and_reinit(sql, &tmp_str)) return 1; @@ -15056,6 +15046,7 @@ st_print_event_info::st_print_event_info() base64_output_mode=BASE64_OUTPUT_UNSPEC; open_cached_file(&head_cache, NULL, NULL, 0, flags); open_cached_file(&body_cache, NULL, NULL, 0, flags); + open_cached_file(&tail_cache, NULL, NULL, 0, flags); #ifdef WHEN_FLASHBACK_REVIEW_READY open_cached_file(&review_sql_cache, NULL, NULL, 0, flags); #endif diff --git a/sql/log_event.h b/sql/log_event.h index b9514e874e8..8a342cb5cd3 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -889,6 +889,7 @@ typedef struct st_print_event_info */ IO_CACHE head_cache; IO_CACHE body_cache; + IO_CACHE tail_cache; #ifdef WHEN_FLASHBACK_REVIEW_READY /* Storing the SQL for reviewing */ IO_CACHE review_sql_cache; @@ -899,6 +900,7 @@ typedef struct st_print_event_info ~st_print_event_info() { close_cached_file(&head_cache); close_cached_file(&body_cache); + close_cached_file(&tail_cache); #ifdef WHEN_FLASHBACK_REVIEW_READY close_cached_file(&review_sql_cache); #endif diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 8ec823d3d64..c71a1f39e28 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1848,6 +1848,7 @@ bool Old_rows_log_event::print_helper(FILE *file, { IO_CACHE *const head= &print_event_info->head_cache; IO_CACHE *const body= &print_event_info->body_cache; + IO_CACHE *const tail= &print_event_info->tail_cache; bool do_print_encoded= print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS && print_event_info->base64_output_mode != BASE64_OUTPUT_NEVER && @@ -1867,8 +1868,9 @@ bool Old_rows_log_event::print_helper(FILE *file, { if (copy_event_cache_to_file_and_reinit(head, file) || copy_cache_to_file_wrapped(body, file, do_print_encoded, - print_event_info->delimiter, - print_event_info->verbose)) + print_event_info->delimiter, + print_event_info->verbose) || + copy_event_cache_to_file_and_reinit(tail, file)) goto err; } return 0; From feac078f15c0be6c0592d74d20cb65b8f5ad2f85 Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Tue, 11 Aug 2020 21:45:09 +0300 Subject: [PATCH 18/40] MDEV-16372 ER_BASE64_DECODE_ERROR upon replaying binary log via mysqlbinlog --verbose (This commit is exclusively for 10.1 branch, do not merge it to upper ones) In case of a pattern of non-STMT_END-marked Rows-log-event (A) followed by a STMT_END marked one (B) mysqlbinlog mixes up the base64 encoded rows events with their pseudo sql representation produced by the verbose option: BINLOG ' base64 encoded data for A ### verbose section for A base64 encoded data for B ### verbose section for B '/*!*/; In effect the produced BINLOG '...' query is not valid and is rejected with the error. Examples of this way malformed BINLOG could have been found in binlog_row_annotate.result that gets corrected with the patch. The issue is fixed with introduction an auxiliary IO_CACHE to hold on the verbose comments until the terminal STMT_END event is found. The new cache is emptied out after two pre-existing ones are done at that time. The correctly produced output now for the above case is as the following: BINLOG ' base64 encoded data for A base64 encoded data for B '/*!*/; ### verbose section for A ### verbose section for B Thanks to Alexey Midenkov for the problem recognition and attempt to tackle, Venkatesh Duggirala who produced a patch for the upstream whose idea is exploited here, as well as to MDEV-23077 reporter LukeXwang who also contributed a piece of a patch aiming at this issue. Extra: mysqlbinlog_row_minimal refined to not produce mutable numeric values into the result file. --- client/mysqlbinlog.cc | 11 +++-- mysql-test/r/mysqlbinlog_row_minimal.result | 40 +++++++++++++------ .../suite/binlog/r/binlog_row_annotate.result | 2 - .../binlog/t/binlog_mysqlbinlog_row_frag.test | 2 +- .../binlog_row_annotate.result | 1 - mysql-test/t/mysqlbinlog_row_minimal.test | 34 +++++++++++++++- sql/log_event.cc | 11 ++++- sql/log_event.h | 6 ++- sql/log_event_old.cc | 6 +++ 9 files changed, 89 insertions(+), 24 deletions(-) diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 979a8f29d12..449e4f0e8a9 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -860,7 +860,8 @@ static bool print_base64(PRINT_EVENT_INFO *print_event_info, Log_event *ev) ev->print(result_file, print_event_info); return print_event_info->head_cache.error == -1 || - print_event_info->body_cache.error == -1; + print_event_info->body_cache.error == -1 || + print_event_info->tail_cache.error == -1; } @@ -912,8 +913,12 @@ static bool print_row_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev, my_b_printf(body_cache, "'%s\n", print_event_info->delimiter); // flush cache - if ((copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, result_file) || - copy_event_cache_to_file_and_reinit(&print_event_info->body_cache, result_file))) + if ((copy_event_cache_to_file_and_reinit(&print_event_info->head_cache, + result_file) || + copy_event_cache_to_file_and_reinit(&print_event_info->body_cache, + result_file) || + copy_event_cache_to_file_and_reinit(&print_event_info->tail_cache, + result_file))) return 1; } } diff --git a/mysql-test/r/mysqlbinlog_row_minimal.result b/mysql-test/r/mysqlbinlog_row_minimal.result index 2e34b2885ca..a7fecc7e4bc 100644 --- a/mysql-test/r/mysqlbinlog_row_minimal.result +++ b/mysql-test/r/mysqlbinlog_row_minimal.result @@ -295,6 +295,24 @@ ROLLBACK /* added by mysqlbinlog */; /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; DROP TABLE t1,t2; +# +# MDEV-16372 ER_BASE64_DECODE_ERROR upon replaying binary log with system table +# +FLUSH BINARY LOGS; +CREATE TABLE t1 (pk INT PRIMARY KEY); +INSERT INTO t1 VALUES (1); +INSERT INTO t1 VALUES (1), (2) ON DUPLICATE KEY UPDATE pk= pk + 10; +FLUSH BINARY LOGS; +Proof: two subsequent patterns must be found +FOUND /### UPDATE `test`.`t1`/ in mysqlbinlog.sql +FOUND /### INSERT INTO `test`.`t1`/ in mysqlbinlog.sql +DROP TABLE t1; +SELECT * FROM t1; +pk +2 +11 +# Cleanup +DROP TABLE t1; CREATE TABLE `t1` ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, `is_deleted` BIT(1) DEFAULT b'0', @@ -319,28 +337,28 @@ FLUSH BINARY LOGS; /*!40019 SET @@session.max_insert_delayed_threads=0*/; /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; DELIMITER /*!*/; -# at 368 -# server id 1 end_log_pos 406 GTID 0-1-16 +# at POS +# server id 1 end_log_pos END_LOG_POS GTID D-S-N /*!100101 SET @@session.skip_parallel_replication=0*//*!*/; /*!100001 SET @@session.gtid_domain_id=0*//*!*/; /*!100001 SET @@session.server_id=1*//*!*/; -/*!100001 SET @@session.gtid_seq_no=16*//*!*/; +/*!100001 SET @@session.gtid_seq_no=21*//*!*/; BEGIN /*!*/; -# at 406 -# server id 1 end_log_pos 453 Table_map: `test`.`t1` mapped to number 34 -# at 453 -# server id 1 end_log_pos 498 Update_rows: table id 34 flags: STMT_END_F +# at POS +# server id 1 end_log_pos END_LOG_POS Table_map: `test`.`t1` mapped to number TID +# at POS +# server id 1 end_log_pos END_LOG_POS Update_rows: table id TID flags: STMT_END_F ### UPDATE `test`.`t1` ### WHERE ### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */ ### SET ### @2=b'1' /* BIT(1) meta=1 nullable=1 is_null=0 */ ### @3=X /* TIMESTAMP(0) meta=0 nullable=0 is_null=0 */ -# at 498 -# server id 1 end_log_pos 576 Query thread_id=4 exec_time=x error_code=0 +# at POS +# server id 1 end_log_pos END_LOG_POS Query thread_id=TID exec_time=x error_code=0 SET TIMESTAMP=X/*!*/; -SET @@session.pseudo_thread_id=4/*!*/; +SET @@session.pseudo_thread_id=TID/*!*/; SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/; SET @@session.sql_mode=1342177280/*!*/; SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; @@ -350,8 +368,6 @@ SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; COMMIT /*!*/; -# at 576 -# server id 1 end_log_pos 620 Rotate to master-bin.000004 pos: 4 DELIMITER ; # End of log file ROLLBACK /* added by mysqlbinlog */; diff --git a/mysql-test/suite/binlog/r/binlog_row_annotate.result b/mysql-test/suite/binlog/r/binlog_row_annotate.result index 6070f2da608..4e72ed8b032 100644 --- a/mysql-test/suite/binlog/r/binlog_row_annotate.result +++ b/mysql-test/suite/binlog/r/binlog_row_annotate.result @@ -449,7 +449,6 @@ BEGIN ### DELETE FROM `test1`.`t1` ### WHERE ### @1=3 /* INT meta=0 nullable=1 is_null=0 */ -'/*!*/; # at # #010909 4:46:40 server id # end_log_pos # Query thread_id=# exec_time=# error_code=0 SET TIMESTAMP=1000000000/*!*/; @@ -1117,7 +1116,6 @@ BEGIN ### DELETE FROM `test1`.`t1` ### WHERE ### @1=3 /* INT meta=0 nullable=1 is_null=0 */ -'/*!*/; # at # #010909 4:46:40 server id # end_log_pos # Query thread_id=# exec_time=# error_code=0 SET TIMESTAMP=1000000000/*!*/; diff --git a/mysql-test/suite/binlog/t/binlog_mysqlbinlog_row_frag.test b/mysql-test/suite/binlog/t/binlog_mysqlbinlog_row_frag.test index 40360421c6b..9f79deea3db 100644 --- a/mysql-test/suite/binlog/t/binlog_mysqlbinlog_row_frag.test +++ b/mysql-test/suite/binlog/t/binlog_mysqlbinlog_row_frag.test @@ -11,7 +11,7 @@ SELECT a from t into @a; FLUSH LOGS; DELETE FROM t; ---exec $MYSQL_BINLOG --debug-binlog-row-event-max-encoded-size=256 $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql +--exec $MYSQL_BINLOG --verbose --debug-binlog-row-event-max-encoded-size=256 $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql --let SEARCH_PATTERN= BINLOG @binlog_fragment_0, @binlog_fragment_1 --let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql diff --git a/mysql-test/suite/binlog_encryption/binlog_row_annotate.result b/mysql-test/suite/binlog_encryption/binlog_row_annotate.result index 41721441a72..6485b723975 100644 --- a/mysql-test/suite/binlog_encryption/binlog_row_annotate.result +++ b/mysql-test/suite/binlog_encryption/binlog_row_annotate.result @@ -449,7 +449,6 @@ BEGIN ### DELETE FROM `test1`.`t1` ### WHERE ### @1=3 /* INT meta=0 nullable=1 is_null=0 */ -'/*!*/; # at # #010909 4:46:40 server id # end_log_pos # Query thread_id=# exec_time=# error_code=0 SET TIMESTAMP=1000000000/*!*/; diff --git a/mysql-test/t/mysqlbinlog_row_minimal.test b/mysql-test/t/mysqlbinlog_row_minimal.test index 39cae408d4e..57bae4247e1 100644 --- a/mysql-test/t/mysqlbinlog_row_minimal.test +++ b/mysql-test/t/mysqlbinlog_row_minimal.test @@ -33,6 +33,35 @@ FLUSH BINARY LOGS; DROP TABLE t1,t2; + +--echo # +--echo # MDEV-16372 ER_BASE64_DECODE_ERROR upon replaying binary log with system table +--echo # + +FLUSH BINARY LOGS; +CREATE TABLE t1 (pk INT PRIMARY KEY); +INSERT INTO t1 VALUES (1); +INSERT INTO t1 VALUES (1), (2) ON DUPLICATE KEY UPDATE pk= pk + 10; + +--let $binlog = query_get_value(SHOW MASTER STATUS, File, 1) + +FLUSH BINARY LOGS; +--exec $MYSQL_BINLOG --verbose $datadir/$binlog > $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql +--echo Proof: two subsequent patterns must be found +--let SEARCH_PATTERN= ### UPDATE `test`.`t1` +--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql +--source include/search_pattern_in_file.inc +--let SEARCH_PATTERN= ### INSERT INTO `test`.`t1` +--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql +--source include/search_pattern_in_file.inc + +DROP TABLE t1; +--exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql +SELECT * FROM t1; + +--echo # Cleanup +DROP TABLE t1; +--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql # # MDEV-14605 ON UPDATE CURRENT_TIMESTAMP fields by multi-table UPDATE are not logged with binlog_row_image=MINIMAL # @@ -64,11 +93,12 @@ UPDATE t1 t1 INNER JOIN t2 t2 ON t1.ref_id = t2.id WHERE t1.id = 1; --let $binlog = query_get_value(SHOW MASTER STATUS, File, 1) +--let $binlog_end= query_get_value(SHOW MASTER STATUS, Position, 1) FLUSH BINARY LOGS; --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR ---replace_regex /\d{6} *\d*:\d\d:\d\d// /SET TIMESTAMP=\d*/SET TIMESTAMP=X/ /exec_time=\d*/exec_time=x/ /@3=\d*/@3=X/ /CRC32 0x[0-9a-f]+/CRC32 XXX/ ---exec $MYSQL_BINLOG --verbose --verbose --base64-output=DECODE-ROWS $datadir/$binlog --start-position=$binlog_pos +--replace_regex /table id \d*/table id TID/ /mapped to number \d*/mapped to number TID/ /at \d*/at POS/ /end_log_pos \d*/end_log_pos END_LOG_POS/ /GTID \d*-\d*-\d*/GTID D-S-N/ /\d{6} *\d*:\d\d:\d\d// /SET TIMESTAMP=\d*/SET TIMESTAMP=X/ /exec_time=\d*/exec_time=x/ /@3=\d*/@3=X/ /CRC32 0x[0-9a-f]+/CRC32 XXX/ /thread_id=\d*/thread_id=TID/ +--exec $MYSQL_BINLOG --verbose --verbose --base64-output=DECODE-ROWS $datadir/$binlog --start-position=$binlog_pos --stop-position=$binlog_end DROP TABLE t1,t2; diff --git a/sql/log_event.cc b/sql/log_event.cc index a1a442df43f..4d7bb1726ed 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -2865,10 +2865,10 @@ void Log_event::print_base64(IO_CACHE* file, default: break; } - + if (ev) { - ev->print_verbose(file, print_event_info); + ev->print_verbose(&print_event_info->tail_cache, print_event_info); delete ev; } } @@ -10671,6 +10671,7 @@ void Rows_log_event::print_helper(FILE *file, { IO_CACHE *const head= &print_event_info->head_cache; IO_CACHE *const body= &print_event_info->body_cache; + IO_CACHE *const tail= &print_event_info->tail_cache; bool do_print_encoded= print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS && !print_event_info->short_form; @@ -10694,6 +10695,11 @@ void Rows_log_event::print_helper(FILE *file, } copy_cache_to_file_wrapped(file, body, do_print_encoded, print_event_info->delimiter); + if (copy_event_cache_to_file_and_reinit(tail, file)) + { + tail->error= -1; + return; + } } } #endif @@ -13220,6 +13226,7 @@ st_print_event_info::st_print_event_info() myf const flags = MYF(MY_WME | MY_NABP); open_cached_file(&head_cache, NULL, NULL, 0, flags); open_cached_file(&body_cache, NULL, NULL, 0, flags); + open_cached_file(&tail_cache, NULL, NULL, 0, flags); } #endif diff --git a/sql/log_event.h b/sql/log_event.h index bdac9f2e581..58e1281c179 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -781,9 +781,12 @@ typedef struct st_print_event_info ~st_print_event_info() { close_cached_file(&head_cache); close_cached_file(&body_cache); + close_cached_file(&tail_cache); } bool init_ok() /* tells if construction was successful */ - { return my_b_inited(&head_cache) && my_b_inited(&body_cache); } + { return my_b_inited(&head_cache) && + my_b_inited(&body_cache) && + my_b_inited(&tail_cache); } /* Settings on how to print the events */ @@ -811,6 +814,7 @@ typedef struct st_print_event_info */ IO_CACHE head_cache; IO_CACHE body_cache; + IO_CACHE tail_cache; } PRINT_EVENT_INFO; #endif diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 0a4d7227dfb..8f4f6c296c4 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1856,6 +1856,7 @@ void Old_rows_log_event::print_helper(FILE *file, { IO_CACHE *const head= &print_event_info->head_cache; IO_CACHE *const body= &print_event_info->body_cache; + IO_CACHE *const tail= &print_event_info->tail_cache; bool do_print_encoded= print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS && !print_event_info->short_form; @@ -1879,6 +1880,11 @@ void Old_rows_log_event::print_helper(FILE *file, } copy_cache_to_file_wrapped(file, body, do_print_encoded, print_event_info->delimiter); + if (copy_event_cache_to_file_and_reinit(tail, file)) + { + tail->error= -1; + return; + } } } #endif From a3d66090c789dfd9f31a63187ef2cf7a85df9c1b Mon Sep 17 00:00:00 2001 From: Nikita Malyavin Date: Fri, 21 Aug 2020 20:05:02 +1000 Subject: [PATCH 19/40] MDEV-18366 Crash on SELECT on a table with indexed virtual columns The problem was in improper error handling behavior in `row_upd_build_difference_binary`: `innobase_free_row_for_vcol` wasn't called. To eliminate this problem in all potential places, a refactoring has been made: * class ib_vcol_row is added. It owns VCOL_STORAGE and heap and maintains it in RAII manner * all innobase_allocate_row_for_vcol/innobase_free_row_for_vcol pairs are substituted with ib_vcol_row usage * row_merge_buf_add is only left untouched because it doesn't own vheap passed as an argument * innobase_allocate_row_for_vcol does not allocate VCOL_STORAGE anymore and accepts it as an argument -- this reduces a number of memory allocations * move rec_printer out of `#ifndef DBUG_OFF` and mark it cold --- .../suite/gcol/r/innodb_virtual_index.result | 3 + .../suite/gcol/t/innodb_virtual_index.test | 3 +- storage/innobase/handler/ha_innodb.cc | 89 ++++++++----------- storage/innobase/include/rem0rec.h | 8 +- storage/innobase/include/row0mysql.h | 36 +++++++- storage/innobase/row/row0ins.cc | 47 ++++------ storage/innobase/row/row0merge.cc | 22 ++--- storage/innobase/row/row0sel.cc | 34 ++----- storage/innobase/row/row0upd.cc | 41 ++------- storage/innobase/row/row0vers.cc | 21 +---- 10 files changed, 132 insertions(+), 172 deletions(-) diff --git a/mysql-test/suite/gcol/r/innodb_virtual_index.result b/mysql-test/suite/gcol/r/innodb_virtual_index.result index cd079544ed0..20311a21136 100644 --- a/mysql-test/suite/gcol/r/innodb_virtual_index.result +++ b/mysql-test/suite/gcol/r/innodb_virtual_index.result @@ -259,6 +259,9 @@ ERROR 22007: Incorrect date value: '20190132' for column `test`.`t1`.`vb` at row SELECT * FROM t1; a b vb ROLLBACK; +SELECT * FROM t1; +a b vb +1 20190132 0000-00-00 CHECK TABLE t1; Table Op Msg_type Msg_text test.t1 check status OK diff --git a/mysql-test/suite/gcol/t/innodb_virtual_index.test b/mysql-test/suite/gcol/t/innodb_virtual_index.test index c2f9cd78fe2..353841840dc 100644 --- a/mysql-test/suite/gcol/t/innodb_virtual_index.test +++ b/mysql-test/suite/gcol/t/innodb_virtual_index.test @@ -278,7 +278,6 @@ DELETE FROM t1; INSERT INTO t1 (a,b) VALUES(1,20190123); SELECT * FROM t1; ROLLBACK; -# MDEV-18366 FIXME: fix the crash and enable this -# SELECT * FROM t1; +SELECT * FROM t1; CHECK TABLE t1; DROP TABLE t1; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 3ab1e9c7cc2..d396c71da5f 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -21820,64 +21820,53 @@ innobase_get_field_from_update_vector( Allocate a heap and record for calculating virtual fields Used mainly for virtual fields in indexes -@param[in] thd MariaDB THD -@param[in] index Index in use -@param[out] heap Heap that holds temporary row -@param[in,out] table MariaDB table -@param[out] record Pointer to allocated MariaDB record -@param[out] storage Internal storage for blobs etc +@param[in] thd MariaDB THD +@param[in] index Index in use +@param[out] heap Heap that holds temporary row +@param[in,out] table MariaDB table +@param[out] record Pointer to allocated MariaDB record +@param[out] storage Internal storage for blobs etc -@retval false on success -@retval true on malloc failure or failed to open the maria table +@retval true on success +@retval false on malloc failure or failed to open the maria table for purge thread. */ -bool innobase_allocate_row_for_vcol( - THD * thd, - dict_index_t* index, - mem_heap_t** heap, - TABLE** table, - byte** record, - VCOL_STORAGE** storage) +bool innobase_allocate_row_for_vcol(THD *thd, dict_index_t *index, + mem_heap_t **heap, TABLE **table, + VCOL_STORAGE *storage) { - TABLE *maria_table; - String *blob_value_storage; - if (!*table) - *table= innodb_find_table_for_vc(thd, index->table); + TABLE *maria_table; + String *blob_value_storage; + if (!*table) + *table = innodb_find_table_for_vc(thd, index->table); - /* For purge thread, there is a possiblity that table could have - dropped, corrupted or unaccessible. */ - if (!*table) - return true; - maria_table= *table; - if (!*heap && !(*heap= mem_heap_create(srv_page_size))) - { - *storage= 0; - return TRUE; - } - *record= static_cast(mem_heap_alloc(*heap, - maria_table->s->reclength)); - *storage= static_cast - (mem_heap_alloc(*heap, sizeof(**storage))); - blob_value_storage= static_cast - (mem_heap_alloc(*heap, - maria_table->s->virtual_not_stored_blob_fields * - sizeof(String))); - if (!*record || !*storage || !blob_value_storage) - { - *storage= 0; - return TRUE; - } - (*storage)->maria_table= maria_table; - (*storage)->innobase_record= *record; - (*storage)->maria_record= maria_table->field[0]->record_ptr(); - (*storage)->blob_value_storage= blob_value_storage; + /* For purge thread, there is a possiblity that table could have + dropped, corrupted or unaccessible. */ + if (!*table) + return false; + maria_table = *table; + if (!*heap && !(*heap = mem_heap_create(srv_page_size))) + return false; - maria_table->move_fields(maria_table->field, *record, - (*storage)->maria_record); - maria_table->remember_blob_values(blob_value_storage); + uchar *record = static_cast(mem_heap_alloc(*heap, + maria_table->s->reclength)); - return FALSE; + size_t len = maria_table->s->virtual_not_stored_blob_fields * sizeof(String); + blob_value_storage = static_cast(mem_heap_alloc(*heap, len)); + + if (!record || !blob_value_storage) + return false; + + storage->maria_table = maria_table; + storage->innobase_record = record; + storage->maria_record = maria_table->field[0]->record_ptr(); + storage->blob_value_storage = blob_value_storage; + + maria_table->move_fields(maria_table->field, record, storage->maria_record); + maria_table->remember_blob_values(blob_value_storage); + + return true; } diff --git a/storage/innobase/include/rem0rec.h b/storage/innobase/include/rem0rec.h index 728944d4373..81a09afa3d8 100644 --- a/storage/innobase/include/rem0rec.h +++ b/storage/innobase/include/rem0rec.h @@ -1076,16 +1076,17 @@ struct rec_offsets_print @param[in,out] o output stream @param[in] r record to display @return the output stream */ +ATTRIBUTE_COLD std::ostream& operator<<(std::ostream& o, const rec_offsets_print& r); -# ifndef DBUG_OFF /** Pretty-printer of records and tuples */ class rec_printer : public std::ostringstream { public: /** Construct a pretty-printed record. @param rec record with header @param offsets rec_get_offsets(rec, ...) */ + ATTRIBUTE_COLD rec_printer(const rec_t* rec, const rec_offs* offsets) : std::ostringstream () @@ -1099,6 +1100,7 @@ public: @param rec record, possibly lacking header @param info rec_get_info_bits(rec) @param offsets rec_get_offsets(rec, ...) */ + ATTRIBUTE_COLD rec_printer(const rec_t* rec, ulint info, const rec_offs* offsets) : std::ostringstream () @@ -1108,6 +1110,7 @@ public: /** Construct a pretty-printed tuple. @param tuple data tuple */ + ATTRIBUTE_COLD rec_printer(const dtuple_t* tuple) : std::ostringstream () @@ -1118,6 +1121,7 @@ public: /** Construct a pretty-printed tuple. @param field array of data tuple fields @param n number of fields */ + ATTRIBUTE_COLD rec_printer(const dfield_t* field, ulint n) : std::ostringstream () @@ -1134,7 +1138,7 @@ private: /** Assignment operator */ rec_printer& operator=(const rec_printer& other); }; -# endif /* !DBUG_OFF */ + # ifdef UNIV_DEBUG /** Read the DB_TRX_ID of a clustered index record. diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h index 60f53221c9a..3b177ac1a24 100644 --- a/storage/innobase/include/row0mysql.h +++ b/storage/innobase/include/row0mysql.h @@ -814,6 +814,8 @@ struct VCOL_STORAGE byte *innobase_record; byte *maria_record; String *blob_value_storage; + VCOL_STORAGE(): maria_table(NULL), innobase_record(NULL), + maria_record(NULL), blob_value_storage(NULL) {} }; /** @@ -836,12 +838,42 @@ bool innobase_allocate_row_for_vcol( dict_index_t* index, mem_heap_t** heap, TABLE** table, - byte** record, - VCOL_STORAGE** storage); + VCOL_STORAGE* storage); /** Free memory allocated by innobase_allocate_row_for_vcol() */ void innobase_free_row_for_vcol(VCOL_STORAGE *storage); +class ib_vcol_row +{ + VCOL_STORAGE storage; +public: + mem_heap_t *heap; + + ib_vcol_row(mem_heap_t *heap) : heap(heap) {} + + byte *record(THD *thd, dict_index_t *index, TABLE **table) + { + if (!storage.innobase_record) + { + bool ok = innobase_allocate_row_for_vcol(thd, index, &heap, table, + &storage); + if (!ok) + return NULL; + } + return storage.innobase_record; + }; + + ~ib_vcol_row() + { + if (heap) + { + if (storage.innobase_record) + innobase_free_row_for_vcol(&storage); + mem_heap_free(heap); + } + } +}; + /** Get the computed value by supplying the base column values. @param[in,out] row the data row @param[in] col virtual column diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index c0aa3655b7b..b8c8a73842f 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -916,16 +916,15 @@ row_ins_invalidate_query_cache( @param[in] index clustered index of child table @param[in] node parent update node @param[in] foreign foreign key information -@param[out] err error code. */ +@return error code. */ static -void +dberr_t row_ins_foreign_fill_virtual( upd_node_t* cascade, const rec_t* rec, dict_index_t* index, upd_node_t* node, - dict_foreign_t* foreign, - dberr_t* err) + dict_foreign_t* foreign) { THD* thd = current_thd; row_ext_t* ext; @@ -934,10 +933,7 @@ row_ins_foreign_fill_virtual( const rec_offs* offsets = rec_get_offsets(rec, index, offsets_, true, ULINT_UNDEFINED, &cascade->heap); - mem_heap_t* v_heap = NULL; TABLE* mysql_table= NULL; - VCOL_STORAGE* vcol_storage= NULL; - byte* record; upd_t* update = cascade->update; ulint n_v_fld = index->table->n_v_def; ulint n_diff; @@ -957,12 +953,10 @@ row_ins_foreign_fill_virtual( innobase_init_vc_templ(index->table); } - if (innobase_allocate_row_for_vcol(thd, index, &v_heap, - &mysql_table, - &record, &vcol_storage)) { - if (v_heap) mem_heap_free(v_heap); - *err = DB_OUT_OF_MEMORY; - goto func_exit; + ib_vcol_row vc(NULL); + uchar *record = vc.record(thd, index, &mysql_table); + if (!record) { + return DB_OUT_OF_MEMORY; } for (ulint i = 0; i < n_v_fld; i++) { @@ -978,12 +972,11 @@ row_ins_foreign_fill_virtual( dfield_t* vfield = innobase_get_computed_value( update->old_vrow, col, index, - &v_heap, update->heap, NULL, thd, mysql_table, + &vc.heap, update->heap, NULL, thd, mysql_table, record, NULL, NULL, NULL); if (vfield == NULL) { - *err = DB_COMPUTE_VALUE_FAILED; - goto func_exit; + return DB_COMPUTE_VALUE_FAILED; } upd_field = upd_get_nth_field(update, n_diff); @@ -1008,13 +1001,12 @@ row_ins_foreign_fill_virtual( dfield_t* new_vfield = innobase_get_computed_value( update->old_vrow, col, index, - &v_heap, update->heap, NULL, thd, + &vc.heap, update->heap, NULL, thd, mysql_table, record, NULL, node->update, foreign); if (new_vfield == NULL) { - *err = DB_COMPUTE_VALUE_FAILED; - goto func_exit; + return DB_COMPUTE_VALUE_FAILED; } dfield_copy(&(upd_field->new_val), new_vfield); @@ -1024,14 +1016,7 @@ row_ins_foreign_fill_virtual( } update->n_fields = n_diff; - *err = DB_SUCCESS; - -func_exit: - if (v_heap) { - if (vcol_storage) - innobase_free_row_for_vcol(vcol_storage); - mem_heap_free(v_heap); - } + return DB_SUCCESS; } #ifdef WITH_WSREP @@ -1312,9 +1297,9 @@ row_ins_foreign_check_on_constraint( if (foreign->v_cols != NULL && foreign->v_cols->size() > 0) { - row_ins_foreign_fill_virtual( + err = row_ins_foreign_fill_virtual( cascade, clust_rec, clust_index, - node, foreign, &err); + node, foreign); if (err != DB_SUCCESS) { goto nonstandard_exit_func; @@ -1351,9 +1336,9 @@ row_ins_foreign_check_on_constraint( node, foreign, tmp_heap, trx); if (foreign->v_cols && !foreign->v_cols->empty()) { - row_ins_foreign_fill_virtual( + err = row_ins_foreign_fill_virtual( cascade, clust_rec, clust_index, - node, foreign, &err); + node, foreign); if (err != DB_SUCCESS) { goto nonstandard_exit_func; diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index 3a6d8cea4a8..a07ac220419 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -520,8 +520,7 @@ row_merge_buf_add( ulint bucket = 0; doc_id_t write_doc_id; ulint n_row_added = 0; - VCOL_STORAGE* vcol_storage= 0; - byte* record; + VCOL_STORAGE vcol_storage; DBUG_ENTER("row_merge_buf_add"); if (buf->n_tuples >= buf->max_tuples) { @@ -594,8 +593,11 @@ row_merge_buf_add( dict_index_t* clust_index = dict_table_get_first_index(new_table); - if (!vcol_storage && - innobase_allocate_row_for_vcol(trx->mysql_thd, clust_index, v_heap, &my_table, &record, &vcol_storage)) { + if (!vcol_storage.innobase_record && + !innobase_allocate_row_for_vcol( + trx->mysql_thd, clust_index, + v_heap, &my_table, + &vcol_storage)) { *err = DB_OUT_OF_MEMORY; goto error; } @@ -603,8 +605,8 @@ row_merge_buf_add( row_field = innobase_get_computed_value( row, v_col, clust_index, v_heap, NULL, ifield, trx->mysql_thd, - my_table, record, old_table, NULL, - NULL); + my_table, vcol_storage.innobase_record, + old_table, NULL, NULL); if (row_field == NULL) { *err = DB_COMPUTE_VALUE_FAILED; @@ -846,13 +848,13 @@ row_merge_buf_add( } end: - if (vcol_storage) - innobase_free_row_for_vcol(vcol_storage); + if (vcol_storage.innobase_record) + innobase_free_row_for_vcol(&vcol_storage); DBUG_RETURN(n_row_added); error: - if (vcol_storage) - innobase_free_row_for_vcol(vcol_storage); + if (vcol_storage.innobase_record) + innobase_free_row_for_vcol(&vcol_storage); DBUG_RETURN(0); } diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 9340d5060d9..bb15625f748 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -176,9 +176,6 @@ row_sel_sec_rec_is_for_clust_rec( rec_offs sec_offsets_[REC_OFFS_SMALL_SIZE]; rec_offs* clust_offs = clust_offsets_; rec_offs* sec_offs = sec_offsets_; - ibool is_equal = TRUE; - VCOL_STORAGE* vcol_storage= 0; - byte* record; rec_offs_init(clust_offsets_); rec_offs_init(sec_offsets_); @@ -197,6 +194,7 @@ row_sel_sec_rec_is_for_clust_rec( } heap = mem_heap_create(256); + ib_vcol_row vc(heap); clust_offs = rec_get_offsets(clust_rec, clust_index, clust_offs, true, ULINT_UNDEFINED, &heap); @@ -225,16 +223,9 @@ row_sel_sec_rec_is_for_clust_rec( dfield_t* vfield; row_ext_t* ext; - if (!vcol_storage) - { - TABLE *mysql_table= thr->prebuilt->m_mysql_table; - innobase_allocate_row_for_vcol(thr_get_trx(thr)->mysql_thd, - clust_index, - &heap, - &mysql_table, - &record, - &vcol_storage); - } + byte *record = vc.record(thr_get_trx(thr)->mysql_thd, + clust_index, + &thr->prebuilt->m_mysql_table); v_col = reinterpret_cast(col); @@ -284,7 +275,7 @@ row_sel_sec_rec_is_for_clust_rec( sec_field, sec_len, ifield->prefix_len, clust_index->table)) { - goto inequal; + return FALSE; } continue; @@ -320,28 +311,19 @@ row_sel_sec_rec_is_for_clust_rec( rtr_read_mbr(sec_field, &sec_mbr); if (!MBR_EQUAL_CMP(&sec_mbr, &tmp_mbr)) { - is_equal = FALSE; - goto func_exit; + return FALSE; } } else { if (0 != cmp_data_data(col->mtype, col->prtype, clust_field, len, sec_field, sec_len)) { -inequal: - is_equal = FALSE; - goto func_exit; + return FALSE; } } } -func_exit: - if (UNIV_LIKELY_NULL(heap)) { - if (UNIV_LIKELY_NULL(vcol_storage)) - innobase_free_row_for_vcol(vcol_storage); - mem_heap_free(heap); - } - return(is_equal); + return TRUE; } /*********************************************************************//** diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc index 6d210cfd6bb..acc7c02abf2 100644 --- a/storage/innobase/row/row0upd.cc +++ b/storage/innobase/row/row0upd.cc @@ -1116,10 +1116,6 @@ row_upd_build_difference_binary( for purge/mvcc purpose) */ if (n_v_fld > 0) { row_ext_t* ext; - mem_heap_t* v_heap = NULL; - byte* record; - VCOL_STORAGE* vcol_storage; - THD* thd; if (trx == NULL) { @@ -1130,9 +1126,8 @@ row_upd_build_difference_binary( ut_ad(!update->old_vrow); - innobase_allocate_row_for_vcol(thd, index, &v_heap, - &mysql_table, - &record, &vcol_storage); + ib_vcol_row vc(NULL); + uchar *record = vc.record(thd, index, &mysql_table); for (i = 0; i < n_v_fld; i++) { const dict_v_col_t* col @@ -1150,10 +1145,9 @@ row_upd_build_difference_binary( dfield_t* vfield = innobase_get_computed_value( update->old_vrow, col, index, - &v_heap, heap, NULL, thd, mysql_table, record, + &vc.heap, heap, NULL, thd, mysql_table, record, NULL, NULL, NULL); if (vfield == NULL) { - if (v_heap) mem_heap_free(v_heap); *error = DB_COMPUTE_VALUE_FAILED; return(NULL); } @@ -1182,12 +1176,6 @@ row_upd_build_difference_binary( } } - - if (v_heap) { - if (vcol_storage) - innobase_free_row_for_vcol(vcol_storage); - mem_heap_free(v_heap); - } } update->n_fields = n_diff; @@ -2122,14 +2110,8 @@ row_upd_store_v_row( THD* thd, TABLE* mysql_table) { - mem_heap_t* heap = NULL; dict_index_t* index = dict_table_get_first_index(node->table); - byte* record= 0; - VCOL_STORAGE *vcol_storage= 0; - - if (!update) - innobase_allocate_row_for_vcol(thd, index, &heap, &mysql_table, - &record, &vcol_storage); + ib_vcol_row vc(NULL); for (ulint col_no = 0; col_no < dict_table_get_n_v_cols(node->table); col_no++) { @@ -2179,24 +2161,19 @@ row_upd_store_v_row( dfield_dup(dfield, node->heap); } } else { + uchar *record = vc.record(thd, index, + &mysql_table); /* Need to compute, this happens when deleting row */ innobase_get_computed_value( node->row, col, index, - &heap, node->heap, NULL, - thd, mysql_table, record, NULL, - NULL, NULL); + &vc.heap, node->heap, + NULL, thd, mysql_table, + record, NULL, NULL, NULL); } } } } - - if (heap) { - if (vcol_storage) - innobase_free_row_for_vcol(vcol_storage); - mem_heap_free(heap); - } - } /** Stores to the heap the row on which the node->pcur is positioned. diff --git a/storage/innobase/row/row0vers.cc b/storage/innobase/row/row0vers.cc index 77bbb1b3459..047027e4950 100644 --- a/storage/innobase/row/row0vers.cc +++ b/storage/innobase/row/row0vers.cc @@ -455,11 +455,8 @@ row_vers_build_clust_v_col( mem_heap_t* heap, purge_vcol_info_t* vcol_info) { - mem_heap_t* local_heap = NULL; - VCOL_STORAGE *vcol_storage= NULL; THD* thd= current_thd; TABLE* maria_table= 0; - byte* record= 0; ut_ad(dict_index_has_virtual(index)); ut_ad(index->table == clust_index->table); @@ -469,15 +466,12 @@ row_vers_build_clust_v_col( maria_table = vcol_info->table(); } - innobase_allocate_row_for_vcol(thd, index, - &local_heap, - &maria_table, - &record, - &vcol_storage); + ib_vcol_row vc(NULL); + byte *record = vc.record(thd, index, &maria_table); if (vcol_info && !vcol_info->table()) { vcol_info->set_table(maria_table); - goto func_exit; + return; } for (ulint i = 0; i < dict_index_get_n_fields(index); i++) { @@ -491,18 +485,11 @@ row_vers_build_clust_v_col( ind_field->col); innobase_get_computed_value( - row, col, clust_index, &local_heap, + row, col, clust_index, &vc.heap, heap, NULL, thd, maria_table, record, NULL, NULL, NULL); } } - -func_exit: - if (local_heap) { - if (vcol_storage) - innobase_free_row_for_vcol(vcol_storage); - mem_heap_free(local_heap); - } } /** Build latest virtual column data from undo log From 97db6c15ea3e83a21df137c222dbd5a40fbe7c82 Mon Sep 17 00:00:00 2001 From: Nikita Malyavin Date: Tue, 11 Aug 2020 00:38:32 +1000 Subject: [PATCH 20/40] MDEV-20618 Assertion failed in row_upd_sec_index_entry Add a proper error handling of innobase_get_computed_value results in row_upd_store_row/row_upd_store_v_row. Also add an assertion in row_vers_build_clust_v_col to fail during row purge. Add one more assertion in row_sel_sec_rec_is_for_clust_rec for possible future catches. --- mysql-test/suite/gcol/inc/gcol_keys.inc | 62 ++++++++++++++++++ .../suite/gcol/r/gcol_keys_innodb.result | 50 +++++++++++++++ .../suite/gcol/r/gcol_keys_myisam.result | 50 +++++++++++++++ mysql-test/suite/vcol/r/vcol_misc.result | 6 +- mysql-test/suite/vcol/t/vcol_misc.test | 2 + sql/sql_insert.cc | 6 +- storage/innobase/handler/ha_innodb.cc | 15 +++-- storage/innobase/include/row0mysql.h | 6 ++ storage/innobase/row/row0sel.cc | 64 ++++++++++++------- storage/innobase/row/row0upd.cc | 50 +++++++++++---- storage/innobase/row/row0vers.cc | 28 ++++++-- 11 files changed, 280 insertions(+), 59 deletions(-) diff --git a/mysql-test/suite/gcol/inc/gcol_keys.inc b/mysql-test/suite/gcol/inc/gcol_keys.inc index 97c9e41b5e8..475ab96e56f 100644 --- a/mysql-test/suite/gcol/inc/gcol_keys.inc +++ b/mysql-test/suite/gcol/inc/gcol_keys.inc @@ -747,4 +747,66 @@ ANALYZE TABLE t1, t2; --eval $query DROP TABLE t1, t2; +if($support_virtual_index) +{ --echo # +--echo # MDEV-20618 Assertion `btr_validate_index(index, 0, false)' failed +--echo # in row_upd_sec_index_entry +--echo # +CREATE TABLE t1 (A BIT(15), VA BIT(10) GENERATED ALWAYS AS (A),PK INT, + PRIMARY KEY (PK), UNIQUE KEY (VA)); + +INSERT IGNORE INTO t1 VALUES ( '\r1','a',1); +--error ER_DATA_TOO_LONG +REPLACE INTO t1 (PK) VALUES (1); + +DROP TABLE t1; + +--echo # +--echo # MDEV-17890 Record in index was not found on update, server crash in +--echo # row_upd_build_difference_binary or +--echo # Assertion `0' failed in row_upd_sec_index_entry +--echo # +CREATE TABLE t1 ( + pk BIGINT AUTO_INCREMENT, + b BIT(15), + v BIT(10) AS (b) VIRTUAL, + PRIMARY KEY(pk), + UNIQUE(v) +); + +INSERT IGNORE INTO t1 (b) VALUES (b'101110001110100'),(b'011101'); +SELECT pk, b INTO OUTFILE 'load.data' FROM t1; +--error ER_DATA_TOO_LONG +LOAD DATA INFILE 'load.data' REPLACE INTO TABLE t1 (pk, b); + +--let $datadir= `SELECT @@datadir` +--remove_file $datadir/test/load.data +DROP TABLE t1; + +--echo # +--echo # MDEV-17834 Server crashes in row_upd_build_difference_binary +--echo # on LOAD DATA into table with indexed virtual column +--echo # +CREATE TABLE t1 ( + pk INT, + i TINYINT, + ts TIMESTAMP NULL, + vi TINYINT AS (i+1) PERSISTENT, + vts TIMESTAMP(5) AS (ts) VIRTUAL, + PRIMARY KEY(pk), + UNIQUE(vts) +); + +INSERT IGNORE INTO t1 (pk,i) VALUES (1,127); + +--write_file $MYSQLTEST_VARDIR/tmp/load.data +1 4 2019-01-01 00:00:00 +EOF +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--error ER_WARN_DATA_OUT_OF_RANGE +eval LOAD DATA INFILE '$MYSQLTEST_VARDIR/tmp/load.data' REPLACE INTO TABLE t1 (pk,i,ts); + +--remove_file $MYSQLTEST_VARDIR/tmp/load.data +DROP TABLE t1; +} diff --git a/mysql-test/suite/gcol/r/gcol_keys_innodb.result b/mysql-test/suite/gcol/r/gcol_keys_innodb.result index 04ba512aad9..ae2843dd2ec 100644 --- a/mysql-test/suite/gcol/r/gcol_keys_innodb.result +++ b/mysql-test/suite/gcol/r/gcol_keys_innodb.result @@ -825,6 +825,56 @@ a1 a2 b 0 NULL 1 DROP TABLE t1, t2; # +# MDEV-20618 Assertion `btr_validate_index(index, 0, false)' failed +# in row_upd_sec_index_entry +# +CREATE TABLE t1 (A BIT(15), VA BIT(10) GENERATED ALWAYS AS (A),PK INT, +PRIMARY KEY (PK), UNIQUE KEY (VA)); +INSERT IGNORE INTO t1 VALUES ( '\r1','a',1); +Warnings: +Warning 1906 The value specified for generated column 'VA' in table 't1' has been ignored +Warning 1264 Out of range value for column 'VA' at row 1 +REPLACE INTO t1 (PK) VALUES (1); +ERROR 22001: Data too long for column 'VA' at row 1 +DROP TABLE t1; +# +# MDEV-17890 Record in index was not found on update, server crash in +# row_upd_build_difference_binary or +# Assertion `0' failed in row_upd_sec_index_entry +# +CREATE TABLE t1 ( +pk BIGINT AUTO_INCREMENT, +b BIT(15), +v BIT(10) AS (b) VIRTUAL, +PRIMARY KEY(pk), +UNIQUE(v) +); +INSERT IGNORE INTO t1 (b) VALUES (b'101110001110100'),(b'011101'); +Warnings: +Warning 1264 Out of range value for column 'v' at row 1 +SELECT pk, b INTO OUTFILE 'load.data' FROM t1; +LOAD DATA INFILE 'load.data' REPLACE INTO TABLE t1 (pk, b); +ERROR 22001: Data too long for column 'v' at row 1 +DROP TABLE t1; +# +# MDEV-17834 Server crashes in row_upd_build_difference_binary +# on LOAD DATA into table with indexed virtual column +# +CREATE TABLE t1 ( +pk INT, +i TINYINT, +ts TIMESTAMP NULL, +vi TINYINT AS (i+1) PERSISTENT, +vts TIMESTAMP(5) AS (ts) VIRTUAL, +PRIMARY KEY(pk), +UNIQUE(vts) +); +INSERT IGNORE INTO t1 (pk,i) VALUES (1,127); +Warnings: +Warning 1264 Out of range value for column 'vi' at row 1 +LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/load.data' REPLACE INTO TABLE t1 (pk,i,ts); +ERROR 22003: Out of range value for column 'vi' at row 1 +DROP TABLE t1; # # BUG#21365158 WL8149:ASSERTION `!TABLE || (!TABLE->WRITE_SET # diff --git a/mysql-test/suite/gcol/r/gcol_keys_myisam.result b/mysql-test/suite/gcol/r/gcol_keys_myisam.result index dcbba1f70c1..a91a77aedf3 100644 --- a/mysql-test/suite/gcol/r/gcol_keys_myisam.result +++ b/mysql-test/suite/gcol/r/gcol_keys_myisam.result @@ -827,6 +827,56 @@ a1 a2 b 0 NULL 1 DROP TABLE t1, t2; # +# MDEV-20618 Assertion `btr_validate_index(index, 0, false)' failed +# in row_upd_sec_index_entry +# +CREATE TABLE t1 (A BIT(15), VA BIT(10) GENERATED ALWAYS AS (A),PK INT, +PRIMARY KEY (PK), UNIQUE KEY (VA)); +INSERT IGNORE INTO t1 VALUES ( '\r1','a',1); +Warnings: +Warning 1906 The value specified for generated column 'VA' in table 't1' has been ignored +Warning 1264 Out of range value for column 'VA' at row 1 +REPLACE INTO t1 (PK) VALUES (1); +ERROR 22001: Data too long for column 'VA' at row 1 +DROP TABLE t1; +# +# MDEV-17890 Record in index was not found on update, server crash in +# row_upd_build_difference_binary or +# Assertion `0' failed in row_upd_sec_index_entry +# +CREATE TABLE t1 ( +pk BIGINT AUTO_INCREMENT, +b BIT(15), +v BIT(10) AS (b) VIRTUAL, +PRIMARY KEY(pk), +UNIQUE(v) +); +INSERT IGNORE INTO t1 (b) VALUES (b'101110001110100'),(b'011101'); +Warnings: +Warning 1264 Out of range value for column 'v' at row 1 +SELECT pk, b INTO OUTFILE 'load.data' FROM t1; +LOAD DATA INFILE 'load.data' REPLACE INTO TABLE t1 (pk, b); +ERROR 22001: Data too long for column 'v' at row 1 +DROP TABLE t1; +# +# MDEV-17834 Server crashes in row_upd_build_difference_binary +# on LOAD DATA into table with indexed virtual column +# +CREATE TABLE t1 ( +pk INT, +i TINYINT, +ts TIMESTAMP NULL, +vi TINYINT AS (i+1) PERSISTENT, +vts TIMESTAMP(5) AS (ts) VIRTUAL, +PRIMARY KEY(pk), +UNIQUE(vts) +); +INSERT IGNORE INTO t1 (pk,i) VALUES (1,127); +Warnings: +Warning 1264 Out of range value for column 'vi' at row 1 +LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/load.data' REPLACE INTO TABLE t1 (pk,i,ts); +ERROR 22003: Out of range value for column 'vi' at row 1 +DROP TABLE t1; DROP VIEW IF EXISTS v1,v2; DROP TABLE IF EXISTS t1,t2,t3; DROP PROCEDURE IF EXISTS p1; diff --git a/mysql-test/suite/vcol/r/vcol_misc.result b/mysql-test/suite/vcol/r/vcol_misc.result index a9b5bc29018..6c4975f2178 100644 --- a/mysql-test/suite/vcol/r/vcol_misc.result +++ b/mysql-test/suite/vcol/r/vcol_misc.result @@ -456,8 +456,7 @@ select pk, col_bit+0, vcol_bit+0 from t1; pk col_bit+0 vcol_bit+0 99 10000 1023 REPLACE LOW_PRIORITY INTO `t1` (`pk`) VALUES (99); -Warnings: -Warning 1264 Out of range value for column 'vcol_bit' at row 1 +ERROR 22001: Data too long for column 'vcol_bit' at row 1 drop table t1; # # MDEV-17837 REPLACE on table with virtual_field can cause crash in set_ok_status() @@ -476,8 +475,7 @@ INSERT IGNORE INTO t1 (pk,i) VALUES (1,127); Warnings: Warning 1264 Out of range value for column 'vi' at row 1 REPLACE INTO t1 (pk,i) VALUES (1,2); -Warnings: -Warning 1264 Out of range value for column 'vi' at row 1 +ERROR 22003: Out of range value for column 'vi' at row 1 DROP TABLE t1; SET @sql_mode=@old_sql_mode; # diff --git a/mysql-test/suite/vcol/t/vcol_misc.test b/mysql-test/suite/vcol/t/vcol_misc.test index 73b23c78977..07f96f4e0b8 100644 --- a/mysql-test/suite/vcol/t/vcol_misc.test +++ b/mysql-test/suite/vcol/t/vcol_misc.test @@ -431,6 +431,7 @@ replace INTO `t1` (`pk`,col_bit) VALUES (99,1000); select pk, col_bit+0, vcol_bit+0 from t1; replace INTO `t1` (`pk`,col_bit) VALUES (99,10000); select pk, col_bit+0, vcol_bit+0 from t1; +--error ER_DATA_TOO_LONG REPLACE LOW_PRIORITY INTO `t1` (`pk`) VALUES (99); drop table t1; @@ -451,6 +452,7 @@ CREATE TABLE t1 ( INSERT INTO t1 (pk,i) VALUES (1,1); TRUNCATE TABLE t1; INSERT IGNORE INTO t1 (pk,i) VALUES (1,127); +--error ER_WARN_DATA_OUT_OF_RANGE REPLACE INTO t1 (pk,i) VALUES (1,2); DROP TABLE t1; SET @sql_mode=@old_sql_mode; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index ec784bc6df4..0c50a251d26 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1748,15 +1748,13 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) } if (table->vfield) { - my_bool abort_on_warning= thd->abort_on_warning; /* We have not yet called update_virtual_fields(VOL_UPDATE_FOR_READ) in handler methods for the just read row in record[1]. */ table->move_fields(table->field, table->record[1], table->record[0]); - thd->abort_on_warning= 0; - table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_REPLACE); - thd->abort_on_warning= abort_on_warning; + if (table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_REPLACE)) + goto err; table->move_fields(table->field, table->record[0], table->record[1]); } if (info->handle_duplicates == DUP_UPDATE) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index d396c71da5f..3b404928cf4 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -103,6 +103,7 @@ this program; if not, write to the Free Software Foundation, Inc., #include "srv0mon.h" #include "srv0srv.h" #include "srv0start.h" +#include "rem0rec.h" #ifdef UNIV_DEBUG #include "trx0purge.h" #endif /* UNIV_DEBUG */ @@ -21881,6 +21882,13 @@ void innobase_free_row_for_vcol(VCOL_STORAGE *storage) } +void innobase_report_computed_value_failed(dtuple_t *row) +{ + ib::error() << "Compute virtual column values failed for " + << rec_printer(row).str(); +} + + /** Get the computed value by supplying the base column values. @param[in,out] row the data row @param[in] col virtual column @@ -22008,13 +22016,6 @@ innobase_get_computed_value( dbug_tmp_restore_column_map(mysql_table->write_set, old_write_set); if (ret != 0) { - // FIXME: Why this error message is macro-hidden? -#ifdef INNODB_VIRTUAL_DEBUG - ib::warn() << "Compute virtual column values failed "; - fputs("InnoDB: Cannot compute value for following record ", - stderr); - dtuple_print(stderr, row); -#endif /* INNODB_VIRTUAL_DEBUG */ DBUG_RETURN(NULL); } diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h index 3b177ac1a24..d5cdd4a3f17 100644 --- a/storage/innobase/include/row0mysql.h +++ b/storage/innobase/include/row0mysql.h @@ -874,6 +874,12 @@ public: } }; +/** Report virtual value computation failure in ib::error +@param[in] row the data row +*/ +ATTRIBUTE_COLD +void innobase_report_computed_value_failed(dtuple_t *row); + /** Get the computed value by supplying the base column values. @param[in,out] row the data row @param[in] col virtual column diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index bb15625f748..7fd0210dbab 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -154,11 +154,15 @@ fields are compared with collation! must be protected by a page s-latch @param[in] clust_index clustered index @param[in] thr query thread -@return TRUE if the secondary record is equal to the corresponding -fields in the clustered record, when compared with collation; -FALSE if not equal or if the clustered record has been marked for deletion */ +@retval DB_COMPUTE_VALUE_FAILED in case of virtual column value computation + failure. +@retval DB_SUCCESS_LOCKED_REC if the secondary record is equal to the + corresponding fields in the clustered record, when compared with + collation; +@retval DB_SUCCESS if not equal or if the clustered record has been marked + for deletion */ static -ibool +dberr_t row_sel_sec_rec_is_for_clust_rec( const rec_t* sec_rec, dict_index_t* sec_index, @@ -190,7 +194,7 @@ row_sel_sec_rec_is_for_clust_rec( it is not visible in the read view. Besides, if there are any externally stored columns, some of them may have already been purged. */ - return(FALSE); + return DB_SUCCESS; } heap = mem_heap_create(256); @@ -242,6 +246,10 @@ row_sel_sec_rec_is_for_clust_rec( thr->prebuilt->m_mysql_table, record, NULL, NULL, NULL); + if (vfield == NULL) { + innobase_report_computed_value_failed(row); + return DB_COMPUTE_VALUE_FAILED; + } clust_len = vfield->len; clust_field = static_cast(vfield->data); } else { @@ -275,7 +283,7 @@ row_sel_sec_rec_is_for_clust_rec( sec_field, sec_len, ifield->prefix_len, clust_index->table)) { - return FALSE; + return DB_SUCCESS; } continue; @@ -311,19 +319,19 @@ row_sel_sec_rec_is_for_clust_rec( rtr_read_mbr(sec_field, &sec_mbr); if (!MBR_EQUAL_CMP(&sec_mbr, &tmp_mbr)) { - return FALSE; + return DB_SUCCESS; } } else { if (0 != cmp_data_data(col->mtype, col->prtype, clust_field, len, sec_field, sec_len)) { - return FALSE; + return DB_SUCCESS; } } } - return TRUE; + return DB_SUCCESS_LOCKED_REC; } /*********************************************************************//** @@ -890,7 +898,7 @@ row_sel_get_clust_rec( dict_index_t* index; rec_t* clust_rec; rec_t* old_vers; - dberr_t err; + dberr_t err = DB_SUCCESS; mem_heap_t* heap = NULL; rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs* offsets = offsets_; @@ -932,7 +940,7 @@ row_sel_get_clust_rec( clustered index record did not exist in the read view of trx. */ - goto func_exit; + goto err_exit; } offsets = rec_get_offsets(clust_rec, index, offsets, true, @@ -997,7 +1005,7 @@ row_sel_get_clust_rec( clust_rec = old_vers; if (clust_rec == NULL) { - goto func_exit; + goto err_exit; } } @@ -1014,13 +1022,14 @@ row_sel_get_clust_rec( visit through secondary index records that would not really exist in our snapshot. */ - if ((old_vers - || rec_get_deleted_flag(rec, dict_table_is_comp( - plan->table))) - && !row_sel_sec_rec_is_for_clust_rec(rec, plan->index, - clust_rec, index, - thr)) { - goto func_exit; + if (old_vers || rec_get_deleted_flag(rec, dict_table_is_comp( + plan->table))) { + err = row_sel_sec_rec_is_for_clust_rec(rec, + plan->index, clust_rec, + index, thr); + if (err != DB_SUCCESS_LOCKED_REC) { + goto err_exit; + } } } @@ -1033,7 +1042,6 @@ row_sel_get_clust_rec( row_sel_fetch_columns(index, clust_rec, offsets, UT_LIST_GET_FIRST(plan->columns)); *out_rec = clust_rec; -func_exit: err = DB_SUCCESS; err_exit: if (UNIV_LIKELY_NULL(heap)) { @@ -3565,10 +3573,18 @@ Row_sel_get_clust_rec_for_mysql::operator()( || trx->isolation_level <= TRX_ISO_READ_UNCOMMITTED || dict_index_is_spatial(sec_index) || rec_get_deleted_flag(rec, dict_table_is_comp( - sec_index->table))) - && !row_sel_sec_rec_is_for_clust_rec( - rec, sec_index, clust_rec, clust_index, thr)) { - clust_rec = NULL; + sec_index->table)))) { + err = row_sel_sec_rec_is_for_clust_rec(rec, sec_index, + clust_rec, clust_index, thr); + switch (err) { + case DB_SUCCESS: + clust_rec = NULL; + break; + case DB_SUCCESS_LOCKED_REC: + break; + default: + goto err_exit; + } } err = DB_SUCCESS; diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc index acc7c02abf2..10156c7d3ab 100644 --- a/storage/innobase/row/row0upd.cc +++ b/storage/innobase/row/row0upd.cc @@ -2101,9 +2101,11 @@ row_upd_eval_new_vals( @param[in,out] node row update node @param[in] update an update vector if it is update @param[in] thd mysql thread handle -@param[in,out] mysql_table mysql table object */ +@param[in,out] mysql_table mysql table object +@return true if success + false if virtual column value computation fails. */ static -void +bool row_upd_store_v_row( upd_node_t* node, const upd_t* update, @@ -2165,24 +2167,33 @@ row_upd_store_v_row( &mysql_table); /* Need to compute, this happens when deleting row */ - innobase_get_computed_value( - node->row, col, index, - &vc.heap, node->heap, - NULL, thd, mysql_table, - record, NULL, NULL, NULL); + dfield_t* vfield = + innobase_get_computed_value( + node->row, col, index, + &vc.heap, node->heap, + NULL, thd, mysql_table, + record, NULL, NULL, + NULL); + if (vfield == NULL) { + return false; + } } } } } + + return true; } /** Stores to the heap the row on which the node->pcur is positioned. @param[in] node row update node @param[in] thd mysql thread handle @param[in,out] mysql_table NULL, or mysql table object when - user thread invokes dml */ + user thread invokes dml +@return false if virtual column value computation fails + true otherwise. */ static -void +bool row_upd_store_row( upd_node_t* node, THD* thd, @@ -2226,8 +2237,12 @@ row_upd_store_row( NULL, NULL, NULL, ext, node->heap); if (node->table->n_v_cols) { - row_upd_store_v_row(node, node->is_delete ? NULL : node->update, + bool ok = row_upd_store_v_row(node, + node->is_delete ? NULL : node->update, thd, mysql_table); + if (!ok) { + return false; + } } if (node->is_delete) { @@ -2242,6 +2257,7 @@ row_upd_store_row( if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } + return true; } /***********************************************************//** @@ -2957,9 +2973,12 @@ row_upd_del_mark_clust_rec( /* Store row because we have to build also the secondary index entries */ - row_upd_store_row(node, trx->mysql_thd, + if (!row_upd_store_row(node, trx->mysql_thd, thr->prebuilt && thr->prebuilt->table == node->table - ? thr->prebuilt->m_mysql_table : NULL); + ? thr->prebuilt->m_mysql_table : NULL)) { + err = DB_COMPUTE_VALUE_FAILED; + return err; + } /* Mark the clustered index record deleted; we do not have to check locks, because we assume that we have an x-lock on the record */ @@ -3168,8 +3187,11 @@ row_upd_clust_step( goto exit_func; } - row_upd_store_row(node, trx->mysql_thd, - thr->prebuilt ? thr->prebuilt->m_mysql_table : NULL); + if(!row_upd_store_row(node, trx->mysql_thd, + thr->prebuilt ? thr->prebuilt->m_mysql_table : NULL)) { + err = DB_COMPUTE_VALUE_FAILED; + goto exit_func; + } if (row_upd_changes_ord_field_binary(index, node->update, thr, node->row, node->ext)) { diff --git a/storage/innobase/row/row0vers.cc b/storage/innobase/row/row0vers.cc index 047027e4950..2d8704764d1 100644 --- a/storage/innobase/row/row0vers.cc +++ b/storage/innobase/row/row0vers.cc @@ -445,9 +445,11 @@ row_vers_must_preserve_del_marked( @param[in] clust_index clustered index @param[in] index the secondary index @param[in] heap heap used to build virtual dtuple -@param[in,out] vcol_info virtual column information. */ +@param[in,out] vcol_info virtual column information. +@return true in case of success + false if virtual column computation fails */ static -void +bool row_vers_build_clust_v_col( dtuple_t* row, dict_index_t* clust_index, @@ -471,7 +473,8 @@ row_vers_build_clust_v_col( if (vcol_info && !vcol_info->table()) { vcol_info->set_table(maria_table); - return; + // wait for second fetch + return true; } for (ulint i = 0; i < dict_index_get_n_fields(index); i++) { @@ -484,12 +487,18 @@ row_vers_build_clust_v_col( col = reinterpret_cast( ind_field->col); - innobase_get_computed_value( + dfield_t *vfield = innobase_get_computed_value( row, col, clust_index, &vc.heap, heap, NULL, thd, maria_table, record, NULL, NULL, NULL); + if (vfield == NULL) { + innobase_report_computed_value_failed(row); + ut_ad(0); + return false; + } } } + return true; } /** Build latest virtual column data from undo log @@ -826,8 +835,11 @@ row_vers_build_cur_vrow( mtr->commit(); } - row_vers_build_clust_v_col( + bool res = row_vers_build_clust_v_col( row, clust_index, index, heap, vcol_info); + if (!res) { + return NULL; + } if (vcol_info != NULL && vcol_info->is_first_fetch()) { return NULL; @@ -948,10 +960,14 @@ row_vers_old_has_index_entry( mtr->commit(); } - row_vers_build_clust_v_col( + bool res = row_vers_build_clust_v_col( row, clust_index, index, heap, vcol_info); + if (!res) { + goto unsafe_to_purge; + } + if (vcol_info && vcol_info->is_first_fetch()) { goto unsafe_to_purge; } From 94e9dc95d4bebf711f001e16bbb53fd8d6a58b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 1 Sep 2020 15:52:36 +0300 Subject: [PATCH 21/40] MDEV-23600 Division by 0 in row_search_with_covering_prefix The InnoDB index fields store bytes, not characters. Remove some unnecessary conversions from characters to bytes. This also fixes MDEV-20422 and the wrong-result bug MDEV-12486. --- ...ix_index_cluster_optimization.combinations | 5 + ...nodb_prefix_index_cluster_optimization.inc | 1 + .../r/fast_prefix_index_fetch_innodb.result | 57 ++- mysql-test/r/innodb_ext_key,off.rdiff | 295 ++++++++++++ mysql-test/r/innodb_ext_key.result | 422 ------------------ .../t/fast_prefix_index_fetch_innodb.test | 52 ++- mysql-test/t/innodb_ext_key.combinations | 5 + mysql-test/t/innodb_ext_key.test | 222 +-------- storage/innobase/row/row0sel.cc | 31 +- storage/xtradb/row/row0sel.cc | 32 +- 10 files changed, 439 insertions(+), 683 deletions(-) create mode 100644 mysql-test/include/innodb_prefix_index_cluster_optimization.combinations create mode 100644 mysql-test/include/innodb_prefix_index_cluster_optimization.inc create mode 100644 mysql-test/r/innodb_ext_key,off.rdiff create mode 100644 mysql-test/t/innodb_ext_key.combinations diff --git a/mysql-test/include/innodb_prefix_index_cluster_optimization.combinations b/mysql-test/include/innodb_prefix_index_cluster_optimization.combinations new file mode 100644 index 00000000000..ad82f82aa9d --- /dev/null +++ b/mysql-test/include/innodb_prefix_index_cluster_optimization.combinations @@ -0,0 +1,5 @@ +[covering] +innodb_prefix_index_cluster_optimization=on + +[unoptimized] +innodb_prefix_index_cluster_optimization=off diff --git a/mysql-test/include/innodb_prefix_index_cluster_optimization.inc b/mysql-test/include/innodb_prefix_index_cluster_optimization.inc new file mode 100644 index 00000000000..c841fece702 --- /dev/null +++ b/mysql-test/include/innodb_prefix_index_cluster_optimization.inc @@ -0,0 +1 @@ +--source include/have_innodb.inc diff --git a/mysql-test/r/fast_prefix_index_fetch_innodb.result b/mysql-test/r/fast_prefix_index_fetch_innodb.result index c6d96389b08..885ee000c28 100644 --- a/mysql-test/r/fast_prefix_index_fetch_innodb.result +++ b/mysql-test/r/fast_prefix_index_fetch_innodb.result @@ -1,4 +1,4 @@ -drop table if exists prefixinno; +SET @save_opt= @@GLOBAL.innodb_prefix_index_cluster_optimization; set global innodb_prefix_index_cluster_optimization = ON; show variables like 'innodb_prefix_index_cluster_optimization'; Variable_name Value @@ -346,10 +346,10 @@ f1 🐱🌑 select @cluster_lookups; @cluster_lookups -2 +1 select @cluster_lookups_avoided; @cluster_lookups_avoided -0 +1 # Eligible - record length is shorter than prefix length SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '🌑%'; f1 @@ -366,10 +366,10 @@ f1 🌒 select @cluster_lookups; @cluster_lookups -1 +0 select @cluster_lookups_avoided; @cluster_lookups_avoided -1 +2 DROP TABLE t1; CREATE TABLE t1( col1 INT, @@ -398,4 +398,49 @@ select @cluster_lookups_avoided; @cluster_lookups_avoided 0 DROP TABLE t1; -set global innodb_prefix_index_cluster_optimization = OFF; +# +# MDEV-23600 Division by 0 in row_search_with_covering_prefix() +# +CREATE TABLE t(c POINT UNIQUE) ENGINE=InnoDB; +INSERT t SET c=POINT(1,1); +SELECT * FROM t WHERE c > (SELECT MAX(c) FROM t); +c +DROP TABLE t; +# +# MDEV-12486 Wrong results with innodb_prefix_index_cluster_optimization +# +CREATE TABLE wp_blogs ( +blog_id bigint(20) NOT NULL auto_increment, +site_id bigint(20) NOT NULL default '0', +domain varchar(200) NOT NULL default '', +path varchar(100) NOT NULL default '', +registered datetime NOT NULL default '0000-00-00 00:00:00', +last_updated datetime NOT NULL default '0000-00-00 00:00:00', +public tinyint(2) NOT NULL default '1', +archived tinyint(2) NOT NULL default '0', +mature tinyint(2) NOT NULL default '0', +spam tinyint(2) NOT NULL default '0', +deleted tinyint(2) NOT NULL default '0', +lang_id int(11) NOT NULL default '0', +PRIMARY KEY (blog_id), +KEY domain (domain(50),path(5)), +KEY lang_id (lang_id) +) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; +INSERT INTO wp_blogs (domain, path) VALUES +('domain.no', '/fondsinvesteringer/'), ('domain.no', '/'), +('foo', 'bar'), ('bar', 'foo'), ('foo', 'foo'), ('bar', 'bar'), +('foo', 'foobar'), ('bar', 'foobar'), ('foobar', 'foobar'); +SET GLOBAL innodb_prefix_index_cluster_optimization=off; +SELECT blog_id FROM wp_blogs WHERE domain IN ('domain.no') +AND path IN ( '/fondsinvesteringer/', '/' ); +blog_id +2 +1 +SET GLOBAL innodb_prefix_index_cluster_optimization=on; +SELECT blog_id FROM wp_blogs WHERE domain IN ('domain.no') +AND path IN ( '/fondsinvesteringer/', '/' ); +blog_id +2 +1 +DROP TABLE wp_blogs; +SET GLOBAL innodb_prefix_index_cluster_optimization = @save_opt; diff --git a/mysql-test/r/innodb_ext_key,off.rdiff b/mysql-test/r/innodb_ext_key,off.rdiff new file mode 100644 index 00000000000..47ca1e6849a --- /dev/null +++ b/mysql-test/r/innodb_ext_key,off.rdiff @@ -0,0 +1,295 @@ +@@ -4,7 +4,7 @@ + explain + select count(*) from lineitem where l_orderkey=130 and l_shipdate='1992-07-01'; + id select_type table type possible_keys key key_len ref rows Extra +-1 SIMPLE lineitem ref PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate 8 const,const 1 Using index ++1 SIMPLE lineitem ref PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity PRIMARY 4 const 5 Using where + flush status; + select count(*) from lineitem where l_orderkey=130 and l_shipdate='1992-07-01'; + count(*) +@@ -14,7 +14,7 @@ + Handler_read_first 0 + Handler_read_key 1 + Handler_read_last 0 +-Handler_read_next 1 ++Handler_read_next 5 + Handler_read_prev 0 + Handler_read_retry 0 + Handler_read_rnd 0 +@@ -45,7 +45,7 @@ + select count(*) from lineitem + where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000; + id select_type table type possible_keys key key_len ref rows Extra +-1 SIMPLE lineitem range PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate 8 NULL 1 Using where; Using index ++1 SIMPLE lineitem ref PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate 4 const 6 Using where; Using index + flush status; + select count(*) from lineitem + where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000; +@@ -56,7 +56,7 @@ + Handler_read_first 0 + Handler_read_key 1 + Handler_read_last 0 +-Handler_read_next 1 ++Handler_read_next 6 + Handler_read_prev 0 + Handler_read_retry 0 + Handler_read_rnd 0 +@@ -66,7 +66,7 @@ + select l_orderkey, l_linenumber from lineitem + where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; + id select_type table type possible_keys key key_len ref rows Extra +-1 SIMPLE lineitem range PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate 8 NULL 3 Using where; Using index ++1 SIMPLE lineitem ref PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate 4 const 6 Using where; Using index + flush status; + select l_orderkey, l_linenumber from lineitem + where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; +@@ -79,7 +79,7 @@ + Handler_read_first 0 + Handler_read_key 1 + Handler_read_last 0 +-Handler_read_next 3 ++Handler_read_next 6 + Handler_read_prev 0 + Handler_read_retry 0 + Handler_read_rnd 0 +@@ -88,7 +88,7 @@ + explain + select min(l_orderkey) from lineitem where l_shipdate='1992-07-01'; + id select_type table type possible_keys key key_len ref rows Extra +-1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away ++1 SIMPLE lineitem ref i_l_shipdate i_l_shipdate 4 const 6 Using index + flush status; + select min(l_orderkey) from lineitem where l_shipdate='1992-07-01'; + min(l_orderkey) +@@ -98,7 +98,7 @@ + Handler_read_first 0 + Handler_read_key 1 + Handler_read_last 0 +-Handler_read_next 0 ++Handler_read_next 6 + Handler_read_prev 0 + Handler_read_retry 0 + Handler_read_rnd 0 +@@ -108,7 +108,7 @@ + select min(l_orderkey) from lineitem + where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; + id select_type table type possible_keys key key_len ref rows Extra +-1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away ++1 SIMPLE lineitem ref PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate 4 const 6 Using where; Using index + flush status; + select min(l_orderkey) from lineitem + where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; +@@ -119,7 +119,7 @@ + Handler_read_first 0 + Handler_read_key 1 + Handler_read_last 0 +-Handler_read_next 0 ++Handler_read_next 6 + Handler_read_prev 0 + Handler_read_retry 0 + Handler_read_rnd 0 +@@ -129,7 +129,7 @@ + select max(l_linenumber) from lineitem + where l_shipdate='1992-07-01' and l_orderkey=130; + id select_type table type possible_keys key key_len ref rows Extra +-1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away ++1 SIMPLE lineitem ref PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity PRIMARY 4 const 5 Using where + flush status; + select max(l_linenumber) from lineitem + where l_shipdate='1992-07-01' and l_orderkey=130; +@@ -140,7 +140,7 @@ + Handler_read_first 0 + Handler_read_key 1 + Handler_read_last 0 +-Handler_read_next 0 ++Handler_read_next 5 + Handler_read_prev 0 + Handler_read_retry 0 + Handler_read_rnd 0 +@@ -152,7 +152,7 @@ + where l_shipdate='1992-07-01' and l_orderkey=130 + or l_receiptdate='1992-07-01' and l_orderkey=5603; + id select_type table type possible_keys key key_len ref rows Extra +-1 SIMPLE lineitem index_merge i_l_shipdate,i_l_receiptdate i_l_shipdate,i_l_receiptdate 8,8 NULL 2 Using union(i_l_shipdate,i_l_receiptdate); Using where ++1 SIMPLE lineitem index_merge i_l_shipdate,i_l_receiptdate i_l_shipdate,i_l_receiptdate 4,4 NULL 9 Using union(i_l_shipdate,i_l_receiptdate); Using where + flush status; + select l_orderkey, l_linenumber + from lineitem use index (i_l_shipdate, i_l_receiptdate) +@@ -166,10 +166,10 @@ + Handler_read_first 0 + Handler_read_key 2 + Handler_read_last 0 +-Handler_read_next 2 ++Handler_read_next 9 + Handler_read_prev 0 + Handler_read_retry 0 +-Handler_read_rnd 2 ++Handler_read_rnd 9 + Handler_read_rnd_deleted 0 + Handler_read_rnd_next 0 + explain +@@ -178,7 +178,7 @@ + where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000 + or l_receiptdate='1992-07-01' and l_orderkey between 5001 and 6000; + id select_type table type possible_keys key key_len ref rows Extra +-1 SIMPLE lineitem index_merge i_l_shipdate,i_l_receiptdate i_l_shipdate,i_l_receiptdate 8,8 NULL 3 Using sort_union(i_l_shipdate,i_l_receiptdate); Using where ++1 SIMPLE lineitem index_merge i_l_shipdate,i_l_receiptdate i_l_shipdate,i_l_receiptdate 4,4 NULL 9 Using union(i_l_shipdate,i_l_receiptdate); Using where + flush status; + select l_orderkey, l_linenumber + from lineitem use index (i_l_shipdate, i_l_receiptdate) +@@ -193,10 +193,10 @@ + Handler_read_first 0 + Handler_read_key 2 + Handler_read_last 0 +-Handler_read_next 3 ++Handler_read_next 9 + Handler_read_prev 0 + Handler_read_retry 0 +-Handler_read_rnd 3 ++Handler_read_rnd 9 + Handler_read_rnd_deleted 0 + Handler_read_rnd_next 0 + explain +@@ -204,7 +204,7 @@ + where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000 + or l_receiptdate='1992-07-01' and l_orderkey between 5001 and 6000; + id select_type table type possible_keys key key_len ref rows Extra +-1 SIMPLE lineitem index_merge PRIMARY,i_l_shipdate,i_l_receiptdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate,i_l_receiptdate 8,8 NULL 3 Using sort_union(i_l_shipdate,i_l_receiptdate); Using where ++1 SIMPLE lineitem index_merge PRIMARY,i_l_shipdate,i_l_receiptdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate,PRIMARY,i_l_receiptdate,PRIMARY 4,4,4,4 NULL 2 Using union(intersect(i_l_shipdate,PRIMARY),intersect(i_l_receiptdate,PRIMARY)); Using where + flush status; + select l_orderkey, l_linenumber from lineitem + where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000 +@@ -218,7 +218,7 @@ + Handler_read_first 0 + Handler_read_key 2 + Handler_read_last 0 +-Handler_read_next 3 ++Handler_read_next 9 + Handler_read_prev 0 + Handler_read_retry 0 + Handler_read_rnd 3 +@@ -228,7 +228,7 @@ + select max(l_orderkey) from lineitem + where l_partkey between 1 and 10 group by l_partkey; + id select_type table type possible_keys key key_len ref rows Extra +-1 SIMPLE lineitem range i_l_suppkey_partkey,i_l_partkey i_l_partkey 5 NULL # Using where; Using index for group-by ++1 SIMPLE lineitem range i_l_suppkey_partkey,i_l_partkey i_l_partkey 5 NULL # Using where; Using index + flush status; + select max(l_orderkey) from lineitem + where l_partkey between 1 and 10 group by l_partkey; +@@ -246,9 +246,9 @@ + show status like 'handler_read%'; + Variable_name Value + Handler_read_first 0 +-Handler_read_key 21 +-Handler_read_last 1 +-Handler_read_next 0 ++Handler_read_key 1 ++Handler_read_last 0 ++Handler_read_next 294 + Handler_read_prev 0 + Handler_read_retry 0 + Handler_read_rnd 0 +@@ -258,7 +258,7 @@ + select max(l_orderkey) from lineitem + where l_suppkey in (1,4) group by l_suppkey; + id select_type table type possible_keys key key_len ref rows Extra +-1 SIMPLE lineitem range i_l_suppkey i_l_suppkey 5 NULL # Using where; Using index for group-by ++1 SIMPLE lineitem range i_l_suppkey i_l_suppkey 5 NULL # Using where; Using index + flush status; + select max(l_orderkey) from lineitem + where l_suppkey in (1,4) group by l_suppkey; +@@ -268,9 +268,9 @@ + show status like 'handler_read%'; + Variable_name Value + Handler_read_first 0 +-Handler_read_key 6 +-Handler_read_last 1 +-Handler_read_next 0 ++Handler_read_key 2 ++Handler_read_last 0 ++Handler_read_next 1230 + Handler_read_prev 0 + Handler_read_retry 0 + Handler_read_rnd 0 +@@ -286,7 +286,7 @@ + id select_type table type possible_keys key key_len ref rows Extra + 1 SIMPLE part range i_p_retailprice i_p_retailprice 9 NULL # Using where; Using index + 1 SIMPLE orders ref PRIMARY,i_o_orderdate i_o_orderdate 4 const # Using index +-1 SIMPLE lineitem ref i_l_partkey i_l_partkey 9 dbt3_s001.part.p_partkey,dbt3_s001.orders.o_orderkey # Using index ++1 SIMPLE lineitem ref i_l_partkey i_l_partkey 5 dbt3_s001.part.p_partkey # Using where; Using index + flush status; + select o_orderkey, p_partkey + from part use index (i_p_retailprice), +@@ -300,7 +300,7 @@ + Handler_read_first 0 + Handler_read_key 3 + Handler_read_last 0 +-Handler_read_next 3 ++Handler_read_next 26 + Handler_read_prev 0 + Handler_read_retry 0 + Handler_read_rnd 0 +@@ -317,8 +317,8 @@ + select * from t0, part ignore index (primary) + where p_partkey=t0.a and p_size=1; + id select_type table type possible_keys key key_len ref rows Extra +-1 SIMPLE t0 ALL NULL NULL NULL NULL 5 Using where +-1 SIMPLE part eq_ref i_p_size i_p_size 9 const,dbt3_s001.t0.a 1 ++1 SIMPLE t0 ALL NULL NULL NULL NULL 5 ++1 SIMPLE part ref i_p_size i_p_size 5 const 5 Using index condition + select * from t0, part ignore index (primary) + where p_partkey=t0.a and p_size=1; + a p_partkey p_name p_mfgr p_brand p_type p_size p_container p_retailprice p_comment +@@ -495,7 +495,7 @@ + select * from t1, t3 where t3.col1=t1.a and t3.col2=t1.a and t3.pk1=t1.a; + id select_type table type possible_keys key key_len ref rows Extra + 1 SIMPLE t1 ALL NULL NULL NULL NULL # Using where +-1 SIMPLE t3 ref PRIMARY,col1 col1 12 test.t1.a,test.t1.a,test.t1.a # Using index ++1 SIMPLE t3 ref PRIMARY,col1 col1 8 test.t1.a,test.t1.a # Using where; Using index + drop table t1,t2,t3; + # + # Bug mdev-4340: performance regression with extended_keys=on +@@ -705,13 +705,13 @@ + select * from t1 force index(index_date_updated) + where index_date_updated= 10 and index_id < 800; + id select_type table type possible_keys key key_len ref rows Extra +-1 SIMPLE t1 range index_date_updated index_date_updated 13 NULL # Using index condition ++1 SIMPLE t1 ref index_date_updated index_date_updated 5 const # Using index condition + # This used to work from the start: + explain + select * from t2 force index(index_date_updated) + where index_date_updated= 10 and index_id < 800; + id select_type table type possible_keys key key_len ref rows Extra +-1 SIMPLE t2 range index_date_updated index_date_updated 13 NULL # Using index condition ++1 SIMPLE t2 ref index_date_updated index_date_updated 5 const # Using index condition + drop table t0,t1,t2; + # + # MDEV-11196: Error:Run-Time Check Failure #2 - Stack around the variable 'key_buff' +@@ -746,11 +746,12 @@ + "select_id": 1, + "table": { + "table_name": "t1", +- "access_type": "range", ++ "access_type": "ref", + "possible_keys": ["f2"], + "key": "f2", +- "key_length": "3070", +- "used_key_parts": ["f2", "pk1"], ++ "key_length": "3066", ++ "used_key_parts": ["f2"], ++ "ref": ["const"], + "rows": 1, + "filtered": 100, + "index_condition": "t1.pk1 <= 5 and t1.pk2 <= 5 and t1.f2 = 'abc'", +@@ -779,8 +780,8 @@ + "access_type": "range", + "possible_keys": ["k1"], + "key": "k1", +- "key_length": "3011", +- "used_key_parts": ["pk1", "f2", "pk2"], ++ "key_length": "3007", ++ "used_key_parts": ["pk1", "f2"], + "rows": 1, + "filtered": 100, + "index_condition": "t1.f2 <= 5 and t1.pk2 <= 5 and t1.pk1 = 'abc'", diff --git a/mysql-test/r/innodb_ext_key.result b/mysql-test/r/innodb_ext_key.result index e02b78cad15..233b359157b 100644 --- a/mysql-test/r/innodb_ext_key.result +++ b/mysql-test/r/innodb_ext_key.result @@ -1,30 +1,6 @@ -DROP TABLE IF EXISTS t1,t2,t3,t4; -DROP DATABASE IF EXISTS dbt3_s001; SET SESSION STORAGE_ENGINE='InnoDB'; CREATE DATABASE dbt3_s001; use dbt3_s001; -set @save_ext_key_optimizer_switch=@@optimizer_switch; -set optimizer_switch='extended_keys=off'; -explain -select count(*) from lineitem where l_orderkey=130 and l_shipdate='1992-07-01'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE lineitem ref PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity PRIMARY 4 const 5 Using where -flush status; -select count(*) from lineitem where l_orderkey=130 and l_shipdate='1992-07-01'; -count(*) -1 -show status like 'handler_read%'; -Variable_name Value -Handler_read_first 0 -Handler_read_key 1 -Handler_read_last 0 -Handler_read_next 5 -Handler_read_prev 0 -Handler_read_retry 0 -Handler_read_rnd 0 -Handler_read_rnd_deleted 0 -Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=on'; explain select count(*) from lineitem where l_orderkey=130 and l_shipdate='1992-07-01'; id select_type table type possible_keys key key_len ref rows Extra @@ -44,29 +20,6 @@ Handler_read_retry 0 Handler_read_rnd 0 Handler_read_rnd_deleted 0 Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=off'; -explain -select count(*) from lineitem -where l_orderkey=130 and l_linenumber=2 and l_shipdate='1992-07-01'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE lineitem const PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity PRIMARY 8 const,const 1 -flush status; -select count(*) from lineitem -where l_orderkey=130 and l_linenumber=2 and l_shipdate='1992-07-01'; -count(*) -1 -show status like 'handler_read%'; -Variable_name Value -Handler_read_first 0 -Handler_read_key 1 -Handler_read_last 0 -Handler_read_next 0 -Handler_read_prev 0 -Handler_read_retry 0 -Handler_read_rnd 0 -Handler_read_rnd_deleted 0 -Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=on'; explain select count(*) from lineitem where l_orderkey=130 and l_linenumber=2 and l_shipdate='1992-07-01'; @@ -88,29 +41,6 @@ Handler_read_retry 0 Handler_read_rnd 0 Handler_read_rnd_deleted 0 Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=off'; -explain -select count(*) from lineitem -where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE lineitem ref PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate 4 const 6 Using where; Using index -flush status; -select count(*) from lineitem -where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000; -count(*) -1 -show status like 'handler_read%'; -Variable_name Value -Handler_read_first 0 -Handler_read_key 1 -Handler_read_last 0 -Handler_read_next 6 -Handler_read_prev 0 -Handler_read_retry 0 -Handler_read_rnd 0 -Handler_read_rnd_deleted 0 -Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=on'; explain select count(*) from lineitem where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000; @@ -132,31 +62,6 @@ Handler_read_retry 0 Handler_read_rnd 0 Handler_read_rnd_deleted 0 Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=off'; -explain -select l_orderkey, l_linenumber from lineitem -where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE lineitem ref PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate 4 const 6 Using where; Using index -flush status; -select l_orderkey, l_linenumber from lineitem -where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; -l_orderkey l_linenumber -1088 3 -1217 1 -1221 3 -show status like 'handler_read%'; -Variable_name Value -Handler_read_first 0 -Handler_read_key 1 -Handler_read_last 0 -Handler_read_next 6 -Handler_read_prev 0 -Handler_read_retry 0 -Handler_read_rnd 0 -Handler_read_rnd_deleted 0 -Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=on'; explain select l_orderkey, l_linenumber from lineitem where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; @@ -180,27 +85,6 @@ Handler_read_retry 0 Handler_read_rnd 0 Handler_read_rnd_deleted 0 Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=off'; -explain -select min(l_orderkey) from lineitem where l_shipdate='1992-07-01'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE lineitem ref i_l_shipdate i_l_shipdate 4 const 6 Using index -flush status; -select min(l_orderkey) from lineitem where l_shipdate='1992-07-01'; -min(l_orderkey) -130 -show status like 'handler_read%'; -Variable_name Value -Handler_read_first 0 -Handler_read_key 1 -Handler_read_last 0 -Handler_read_next 6 -Handler_read_prev 0 -Handler_read_retry 0 -Handler_read_rnd 0 -Handler_read_rnd_deleted 0 -Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=on'; explain select min(l_orderkey) from lineitem where l_shipdate='1992-07-01'; id select_type table type possible_keys key key_len ref rows Extra @@ -220,29 +104,6 @@ Handler_read_retry 0 Handler_read_rnd 0 Handler_read_rnd_deleted 0 Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=off'; -explain -select min(l_orderkey) from lineitem -where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE lineitem ref PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate 4 const 6 Using where; Using index -flush status; -select min(l_orderkey) from lineitem -where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; -min(l_orderkey) -1088 -show status like 'handler_read%'; -Variable_name Value -Handler_read_first 0 -Handler_read_key 1 -Handler_read_last 0 -Handler_read_next 6 -Handler_read_prev 0 -Handler_read_retry 0 -Handler_read_rnd 0 -Handler_read_rnd_deleted 0 -Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=on'; explain select min(l_orderkey) from lineitem where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; @@ -264,29 +125,6 @@ Handler_read_retry 0 Handler_read_rnd 0 Handler_read_rnd_deleted 0 Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=off'; -explain -select max(l_linenumber) from lineitem -where l_shipdate='1992-07-01' and l_orderkey=130; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE lineitem ref PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity PRIMARY 4 const 5 Using where -flush status; -select max(l_linenumber) from lineitem -where l_shipdate='1992-07-01' and l_orderkey=130; -max(l_linenumber) -2 -show status like 'handler_read%'; -Variable_name Value -Handler_read_first 0 -Handler_read_key 1 -Handler_read_last 0 -Handler_read_next 5 -Handler_read_prev 0 -Handler_read_retry 0 -Handler_read_rnd 0 -Handler_read_rnd_deleted 0 -Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=on'; explain select max(l_linenumber) from lineitem where l_shipdate='1992-07-01' and l_orderkey=130; @@ -308,34 +146,6 @@ Handler_read_retry 0 Handler_read_rnd 0 Handler_read_rnd_deleted 0 Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=off'; -explain -select l_orderkey, l_linenumber -from lineitem use index (i_l_shipdate, i_l_receiptdate) -where l_shipdate='1992-07-01' and l_orderkey=130 -or l_receiptdate='1992-07-01' and l_orderkey=5603; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE lineitem index_merge i_l_shipdate,i_l_receiptdate i_l_shipdate,i_l_receiptdate 4,4 NULL 9 Using union(i_l_shipdate,i_l_receiptdate); Using where -flush status; -select l_orderkey, l_linenumber -from lineitem use index (i_l_shipdate, i_l_receiptdate) -where l_shipdate='1992-07-01' and l_orderkey=130 -or l_receiptdate='1992-07-01' and l_orderkey=5603; -l_orderkey l_linenumber -130 2 -5603 2 -show status like 'handler_read%'; -Variable_name Value -Handler_read_first 0 -Handler_read_key 2 -Handler_read_last 0 -Handler_read_next 9 -Handler_read_prev 0 -Handler_read_retry 0 -Handler_read_rnd 9 -Handler_read_rnd_deleted 0 -Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=on'; explain select l_orderkey, l_linenumber from lineitem use index (i_l_shipdate, i_l_receiptdate) @@ -362,35 +172,6 @@ Handler_read_retry 0 Handler_read_rnd 2 Handler_read_rnd_deleted 0 Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=off'; -explain -select l_orderkey, l_linenumber -from lineitem use index (i_l_shipdate, i_l_receiptdate) -where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000 -or l_receiptdate='1992-07-01' and l_orderkey between 5001 and 6000; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE lineitem index_merge i_l_shipdate,i_l_receiptdate i_l_shipdate,i_l_receiptdate 4,4 NULL 9 Using union(i_l_shipdate,i_l_receiptdate); Using where -flush status; -select l_orderkey, l_linenumber -from lineitem use index (i_l_shipdate, i_l_receiptdate) -where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000 -or l_receiptdate='1992-07-01' and l_orderkey between 5001 and 6000; -l_orderkey l_linenumber -130 2 -5603 2 -5959 3 -show status like 'handler_read%'; -Variable_name Value -Handler_read_first 0 -Handler_read_key 2 -Handler_read_last 0 -Handler_read_next 9 -Handler_read_prev 0 -Handler_read_retry 0 -Handler_read_rnd 9 -Handler_read_rnd_deleted 0 -Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=on'; explain select l_orderkey, l_linenumber from lineitem use index (i_l_shipdate, i_l_receiptdate) @@ -418,33 +199,6 @@ Handler_read_retry 0 Handler_read_rnd 3 Handler_read_rnd_deleted 0 Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=off'; -explain -select l_orderkey, l_linenumber from lineitem -where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000 -or l_receiptdate='1992-07-01' and l_orderkey between 5001 and 6000; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE lineitem index_merge PRIMARY,i_l_shipdate,i_l_receiptdate,i_l_orderkey,i_l_orderkey_quantity i_l_shipdate,PRIMARY,i_l_receiptdate,PRIMARY 4,4,4,4 NULL 2 Using union(intersect(i_l_shipdate,PRIMARY),intersect(i_l_receiptdate,PRIMARY)); Using where -flush status; -select l_orderkey, l_linenumber from lineitem -where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000 -or l_receiptdate='1992-07-01' and l_orderkey between 5001 and 6000; -l_orderkey l_linenumber -130 2 -5603 2 -5959 3 -show status like 'handler_read%'; -Variable_name Value -Handler_read_first 0 -Handler_read_key 2 -Handler_read_last 0 -Handler_read_next 9 -Handler_read_prev 0 -Handler_read_retry 0 -Handler_read_rnd 3 -Handler_read_rnd_deleted 0 -Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=on'; explain select l_orderkey, l_linenumber from lineitem where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000 @@ -470,38 +224,6 @@ Handler_read_retry 0 Handler_read_rnd 3 Handler_read_rnd_deleted 0 Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=off'; -explain -select max(l_orderkey) from lineitem -where l_partkey between 1 and 10 group by l_partkey; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE lineitem range i_l_suppkey_partkey,i_l_partkey i_l_partkey 5 NULL # Using where; Using index -flush status; -select max(l_orderkey) from lineitem -where l_partkey between 1 and 10 group by l_partkey; -max(l_orderkey) -5984 -5957 -5892 -5856 -5959 -5957 -5794 -5894 -5859 -5632 -show status like 'handler_read%'; -Variable_name Value -Handler_read_first 0 -Handler_read_key 1 -Handler_read_last 0 -Handler_read_next 294 -Handler_read_prev 0 -Handler_read_retry 0 -Handler_read_rnd 0 -Handler_read_rnd_deleted 0 -Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=on'; explain select max(l_orderkey) from lineitem where l_partkey between 1 and 10 group by l_partkey; @@ -532,30 +254,6 @@ Handler_read_retry 0 Handler_read_rnd 0 Handler_read_rnd_deleted 0 Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=off'; -explain -select max(l_orderkey) from lineitem -where l_suppkey in (1,4) group by l_suppkey; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE lineitem range i_l_suppkey i_l_suppkey 5 NULL # Using where; Using index -flush status; -select max(l_orderkey) from lineitem -where l_suppkey in (1,4) group by l_suppkey; -max(l_orderkey) -5988 -5984 -show status like 'handler_read%'; -Variable_name Value -Handler_read_first 0 -Handler_read_key 2 -Handler_read_last 0 -Handler_read_next 1230 -Handler_read_prev 0 -Handler_read_retry 0 -Handler_read_rnd 0 -Handler_read_rnd_deleted 0 -Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=on'; explain select max(l_orderkey) from lineitem where l_suppkey in (1,4) group by l_suppkey; @@ -579,37 +277,6 @@ Handler_read_rnd 0 Handler_read_rnd_deleted 0 Handler_read_rnd_next 0 create index i_p_retailprice on part(p_retailprice); -set optimizer_switch='extended_keys=off'; -explain -select o_orderkey, p_partkey -from part use index (i_p_retailprice), -lineitem use index (i_l_partkey), orders -where p_retailprice > 1100 and o_orderdate='1997-01-01' -and o_orderkey=l_orderkey and p_partkey=l_partkey; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE part range i_p_retailprice i_p_retailprice 9 NULL # Using where; Using index -1 SIMPLE orders ref PRIMARY,i_o_orderdate i_o_orderdate 4 const # Using index -1 SIMPLE lineitem ref i_l_partkey i_l_partkey 5 dbt3_s001.part.p_partkey # Using where; Using index -flush status; -select o_orderkey, p_partkey -from part use index (i_p_retailprice), -lineitem use index (i_l_partkey), orders -where p_retailprice > 1100 and o_orderdate='1997-01-01' -and o_orderkey=l_orderkey and p_partkey=l_partkey; -o_orderkey p_partkey -5895 200 -show status like 'handler_read%'; -Variable_name Value -Handler_read_first 0 -Handler_read_key 3 -Handler_read_last 0 -Handler_read_next 26 -Handler_read_prev 0 -Handler_read_retry 0 -Handler_read_rnd 0 -Handler_read_rnd_deleted 0 -Handler_read_rnd_next 0 -set optimizer_switch='extended_keys=on'; explain select o_orderkey, p_partkey from part use index (i_p_retailprice), @@ -646,7 +313,6 @@ Handler_read_rnd_next 0 create table t0 (a int); insert into t0 values (1), (2), (3), (4), (5); create index i_p_size on part(p_size); -set optimizer_switch='extended_keys=on'; explain select * from t0, part ignore index (primary) where p_partkey=t0.a and p_size=1; @@ -667,7 +333,6 @@ use test; # set @save_optimizer_switch=@@optimizer_switch; SET optimizer_switch='materialization=on,semijoin=on'; -SET optimizer_switch='extended_keys=on'; CREATE TABLE t1 (a int, b int) ENGINE=MyISAM; INSERT INTO t1 VALUES (1,1), (2,2); SELECT * FROM t1 WHERE 2 IN (SELECT MAX(s1.a) FROM t1 AS s1, t1 AS s2); @@ -688,9 +353,7 @@ set optimizer_switch=@save_optimizer_switch; # + extended_keys = on # (valgrinf complains fixed by the patch for bug #914560) # -set @save_optimizer_switch=@@optimizer_switch; SET optimizer_switch = 'derived_with_keys=on'; -SET optimizer_switch = 'extended_keys=on'; CREATE TABLE t1 (a varchar(1)) ENGINE=MyISAM; INSERT INTO t1 VALUES ('j'), ('v'); CREATE TABLE t2 (b varchar(1)) ENGINE=MyISAM; @@ -720,8 +383,6 @@ c int NOT NULL PRIMARY KEY INSERT INTO t2 VALUES (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), (24); -set @save_optimizer_switch=@@optimizer_switch; -SET optimizer_switch = 'extended_keys=off'; EXPLAIN SELECT a FROM t1 AS t, t2 WHERE c = a AND b IN (SELECT b FROM t1, t2 WHERE b = t.b); @@ -734,20 +395,6 @@ SELECT a FROM t1 AS t, t2 WHERE c = a AND b IN (SELECT b FROM t1, t2 WHERE b = t.b); a 24 -SET optimizer_switch = 'extended_keys=on'; -EXPLAIN -SELECT a FROM t1 AS t, t2 -WHERE c = a AND b IN (SELECT b FROM t1, t2 WHERE b = t.b); -id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t index a,b b 7 NULL 10 Using index -1 PRIMARY t1 ref b b 3 test.t.b 2 Using index -1 PRIMARY t2 index NULL PRIMARY 4 NULL 11 Using index; FirstMatch(t) -1 PRIMARY t2 eq_ref PRIMARY PRIMARY 4 test.t.a 1 Using index -SELECT a FROM t1 AS t, t2 -WHERE c = a AND b IN (SELECT b FROM t1, t2 WHERE b = t.b); -a -24 -set optimizer_switch=@save_optimizer_switch; DROP TABLE t1,t2; # # LP Bug #923236: hash join + extended_keys = on @@ -756,12 +403,10 @@ CREATE TABLE t1 (a int) ENGINE=MyISAM; CREATE TABLE t2 (b int) ENGINE=MyISAM; INSERT INTO t1 (a) VALUES (4), (6); INSERT INTO t2 (b) VALUES (0), (8); -set @save_optimizer_switch=@@optimizer_switch; set @save_join_cache_level=@@join_cache_level; SET join_cache_level=3; SET optimizer_switch='join_cache_hashed=on'; SET optimizer_switch='join_cache_bka=on'; -SET optimizer_switch='extended_keys=on'; EXPLAIN SELECT * FROM t1, t2 WHERE b=a; id select_type table type possible_keys key key_len ref rows Extra @@ -786,26 +431,16 @@ UNIQUE KEY uq (c2,c3), KEY c3 (c3), KEY c4 (c4) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; -set @save_optimizer_switch=@@optimizer_switch; -set session optimizer_switch='extended_keys=off'; INSERT INTO t1 (c2, c3, c4) VALUES (58291525, 2580, 'foobar') ON DUPLICATE KEY UPDATE c4 = VALUES(c4); INSERT INTO t1 (c2, c3, c4) VALUES (58291525, 2580, 'foobar') ON DUPLICATE KEY UPDATE c4 = VALUES(c4); -DELETE FROM t1; -set session optimizer_switch='extended_keys=on'; -INSERT INTO t1 (c2, c3, c4) VALUES (58291525, 2580, 'foobar') -ON DUPLICATE KEY UPDATE c4 = VALUES(c4); -INSERT INTO t1 (c2, c3, c4) VALUES (58291525, 2580, 'foobar') -ON DUPLICATE KEY UPDATE c4 = VALUES(c4); -set optimizer_switch=@save_optimizer_switch; DROP TABLE t1; # # Bug mdev-4220: using ref instead of eq_ref # with extended_keys=on # (performance regression introduced in the patch for mdev-3851) # -set @save_optimizer_switch=@@optimizer_switch; create table t1 (a int not null) engine=innodb; insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); create table t2 ( @@ -815,29 +450,6 @@ insert into t2 select A.a + 10 * B.a, A.a + 10 * B.a, A.a + 10 * B.a from t1 A, t1 B; -set optimizer_switch='extended_keys=off'; -explain -select * from t1, t2 where t2.a=t1.a and t2.b < 2; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 10 -1 SIMPLE t2 eq_ref a a 4 test.t1.a 1 Using where -flush status; -select * from t1, t2 where t2.a=t1.a and t2.b < 2; -a pk a b -0 0 0 0 -1 1 1 1 -show status like 'handler_read%'; -Variable_name Value -Handler_read_first 0 -Handler_read_key 10 -Handler_read_last 0 -Handler_read_next 0 -Handler_read_prev 0 -Handler_read_retry 0 -Handler_read_rnd 0 -Handler_read_rnd_deleted 0 -Handler_read_rnd_next 11 -set optimizer_switch='extended_keys=on'; explain select * from t1, t2 where t2.a=t1.a and t2.b < 2; id select_type table type possible_keys key key_len ref rows Extra @@ -874,18 +486,6 @@ analyze table t1,t3; Table Op Msg_type Msg_text test.t1 analyze status OK test.t3 analyze status OK -set optimizer_switch='extended_keys=off'; -explain -select * from t1, t3 where t3.col1=t1.a and t3.col2=t1.a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL # Using where -1 SIMPLE t3 ref col1 col1 8 test.t1.a,test.t1.a # Using index -explain -select * from t1, t3 where t3.col1=t1.a and t3.col2=t1.a and t3.pk1=t1.a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL # Using where -1 SIMPLE t3 ref PRIMARY,col1 col1 8 test.t1.a,test.t1.a # Using where; Using index -set optimizer_switch='extended_keys=on'; explain select * from t1, t3 where t3.col1=t1.a and t3.col2=t1.a; id select_type table type possible_keys key key_len ref rows Extra @@ -900,7 +500,6 @@ drop table t1,t2,t3; # # Bug mdev-4340: performance regression with extended_keys=on # -set @save_optimizer_switch=@@optimizer_switch; CREATE TABLE t1 ( page_id int(8) unsigned NOT NULL AUTO_INCREMENT, page_namespace int(11) NOT NULL DEFAULT '0', @@ -1000,16 +599,6 @@ INSERT INTO t3 VALUES (89,'text-8008',''),(90,'text-9008',''),(91,'text-9',''),(92,'text-1009',''), (93,'text-2009',''),(94,'text-3009',''),(95,'text-4009',''),(96,'text-5009',''), (97,'text-6009',''),(98,'text-7009',''),(99,'text-8009',''),(100,'text-9009',''); -set optimizer_switch='extended_keys=off'; -EXPLAIN -SELECT * FROM t1, t2 IGNORE INDEX (PRIMARY), t3 -WHERE page_id=rev_page AND rev_text_id=old_id AND page_namespace=4 AND page_title='Sandbox' -ORDER BY rev_timestamp ASC LIMIT 10; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 const PRIMARY,name_title name_title 261 const,const 1 -1 SIMPLE t2 ref page_timestamp page_timestamp 4 const 10 Using where -1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.rev_text_id 1 -set optimizer_switch='extended_keys=on'; EXPLAIN SELECT * FROM t1, t2 IGNORE INDEX (PRIMARY), t3 WHERE page_id=rev_page AND rev_text_id=old_id AND page_namespace=4 AND page_title='Sandbox' @@ -1027,7 +616,6 @@ create table t1 (a bigint not null unique auto_increment, b varchar(10), primary create table t2 (a bigint not null unique auto_increment, b varchar(10), primary key (a), key (b(2))) engine = innodb default character set utf8; insert into t1 (b) values (null), (null), (null); insert into t2 (b) values (null), (null), (null); -set optimizer_switch='extended_keys=on'; explain select a from t1 where b is null order by a desc limit 2; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 index b PRIMARY 8 NULL 3 Using where @@ -1042,14 +630,6 @@ select a from t2 where b is null order by a desc limit 2; a 3 2 -set optimizer_switch='extended_keys=off'; -explain select a from t2 where b is null order by a desc limit 2; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t2 range b b 9 NULL 3 Using where; Using filesort -select a from t2 where b is null order by a desc limit 2; -a -3 -2 explain select a from t2 where b is null order by a desc; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 index b PRIMARY 8 NULL 3 Using where @@ -1067,7 +647,6 @@ a 2 1 drop table t1, t2; -set optimizer_switch=@save_optimizer_switch; # # MDEV-10325: Queries examines all rows of a tables when it should not # @@ -1213,7 +792,6 @@ EXPLAIN } } drop table t1; -set optimizer_switch=@save_ext_key_optimizer_switch; set global innodb_file_format = @save_innodb_file_format; set global innodb_large_prefix = @save_innodb_large_prefix; SET SESSION STORAGE_ENGINE=DEFAULT; diff --git a/mysql-test/t/fast_prefix_index_fetch_innodb.test b/mysql-test/t/fast_prefix_index_fetch_innodb.test index c3b3440d82d..7112f14bbd5 100644 --- a/mysql-test/t/fast_prefix_index_fetch_innodb.test +++ b/mysql-test/t/fast_prefix_index_fetch_innodb.test @@ -1,9 +1,6 @@ -- source include/have_innodb.inc ---disable_warnings -drop table if exists prefixinno; ---enable_warnings - +SET @save_opt= @@GLOBAL.innodb_prefix_index_cluster_optimization; set global innodb_prefix_index_cluster_optimization = ON; show variables like 'innodb_prefix_index_cluster_optimization'; @@ -665,4 +662,49 @@ select @cluster_lookups; select @cluster_lookups_avoided; DROP TABLE t1; -set global innodb_prefix_index_cluster_optimization = OFF; + +--echo # +--echo # MDEV-23600 Division by 0 in row_search_with_covering_prefix() +--echo # +CREATE TABLE t(c POINT UNIQUE) ENGINE=InnoDB; +INSERT t SET c=POINT(1,1); +SELECT * FROM t WHERE c > (SELECT MAX(c) FROM t); +DROP TABLE t; + +--echo # +--echo # MDEV-12486 Wrong results with innodb_prefix_index_cluster_optimization +--echo # +CREATE TABLE wp_blogs ( + blog_id bigint(20) NOT NULL auto_increment, + site_id bigint(20) NOT NULL default '0', + domain varchar(200) NOT NULL default '', + path varchar(100) NOT NULL default '', + registered datetime NOT NULL default '0000-00-00 00:00:00', + last_updated datetime NOT NULL default '0000-00-00 00:00:00', + public tinyint(2) NOT NULL default '1', + archived tinyint(2) NOT NULL default '0', + mature tinyint(2) NOT NULL default '0', + spam tinyint(2) NOT NULL default '0', + deleted tinyint(2) NOT NULL default '0', + lang_id int(11) NOT NULL default '0', + PRIMARY KEY (blog_id), + KEY domain (domain(50),path(5)), + KEY lang_id (lang_id) +) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; + +INSERT INTO wp_blogs (domain, path) VALUES +('domain.no', '/fondsinvesteringer/'), ('domain.no', '/'), +('foo', 'bar'), ('bar', 'foo'), ('foo', 'foo'), ('bar', 'bar'), +('foo', 'foobar'), ('bar', 'foobar'), ('foobar', 'foobar'); + +SET GLOBAL innodb_prefix_index_cluster_optimization=off; +SELECT blog_id FROM wp_blogs WHERE domain IN ('domain.no') +AND path IN ( '/fondsinvesteringer/', '/' ); + +SET GLOBAL innodb_prefix_index_cluster_optimization=on; +SELECT blog_id FROM wp_blogs WHERE domain IN ('domain.no') +AND path IN ( '/fondsinvesteringer/', '/' ); + +DROP TABLE wp_blogs; + +SET GLOBAL innodb_prefix_index_cluster_optimization = @save_opt; diff --git a/mysql-test/t/innodb_ext_key.combinations b/mysql-test/t/innodb_ext_key.combinations new file mode 100644 index 00000000000..6ad97eaf75b --- /dev/null +++ b/mysql-test/t/innodb_ext_key.combinations @@ -0,0 +1,5 @@ +[on] +optimizer_switch=extended_keys=on + +[off] +optimizer_switch=extended_keys=off diff --git a/mysql-test/t/innodb_ext_key.test b/mysql-test/t/innodb_ext_key.test index bec4462df08..c936e4b5170 100644 --- a/mysql-test/t/innodb_ext_key.test +++ b/mysql-test/t/innodb_ext_key.test @@ -1,9 +1,4 @@ ---source include/have_innodb.inc - ---disable_warnings -DROP TABLE IF EXISTS t1,t2,t3,t4; -DROP DATABASE IF EXISTS dbt3_s001; ---enable_warnings +--source include/innodb_prefix_index_cluster_optimization.inc SET SESSION STORAGE_ENGINE='InnoDB'; @@ -19,32 +14,12 @@ use dbt3_s001; --enable_result_log --enable_query_log -set @save_ext_key_optimizer_switch=@@optimizer_switch; - -set optimizer_switch='extended_keys=off'; explain select count(*) from lineitem where l_orderkey=130 and l_shipdate='1992-07-01'; flush status; select count(*) from lineitem where l_orderkey=130 and l_shipdate='1992-07-01'; show status like 'handler_read%'; -set optimizer_switch='extended_keys=on'; -explain -select count(*) from lineitem where l_orderkey=130 and l_shipdate='1992-07-01'; -flush status; -select count(*) from lineitem where l_orderkey=130 and l_shipdate='1992-07-01'; -show status like 'handler_read%'; - -set optimizer_switch='extended_keys=off'; -explain -select count(*) from lineitem - where l_orderkey=130 and l_linenumber=2 and l_shipdate='1992-07-01'; -flush status; -select count(*) from lineitem - where l_orderkey=130 and l_linenumber=2 and l_shipdate='1992-07-01'; -show status like 'handler_read%'; - -set optimizer_switch='extended_keys=on'; explain select count(*) from lineitem where l_orderkey=130 and l_linenumber=2 and l_shipdate='1992-07-01'; @@ -53,7 +28,6 @@ select count(*) from lineitem where l_orderkey=130 and l_linenumber=2 and l_shipdate='1992-07-01'; show status like 'handler_read%'; -set optimizer_switch='extended_keys=off'; explain select count(*) from lineitem where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000; @@ -62,16 +36,6 @@ select count(*) from lineitem where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000; show status like 'handler_read%'; -set optimizer_switch='extended_keys=on'; -explain -select count(*) from lineitem - where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000; -flush status; -select count(*) from lineitem - where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000; -show status like 'handler_read%'; - -set optimizer_switch='extended_keys=off'; explain select l_orderkey, l_linenumber from lineitem where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; @@ -80,30 +44,12 @@ select l_orderkey, l_linenumber from lineitem where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; show status like 'handler_read%'; -set optimizer_switch='extended_keys=on'; -explain -select l_orderkey, l_linenumber from lineitem - where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; -flush status; -select l_orderkey, l_linenumber from lineitem - where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; -show status like 'handler_read%'; - -set optimizer_switch='extended_keys=off'; explain select min(l_orderkey) from lineitem where l_shipdate='1992-07-01'; flush status; select min(l_orderkey) from lineitem where l_shipdate='1992-07-01'; show status like 'handler_read%'; -set optimizer_switch='extended_keys=on'; -explain -select min(l_orderkey) from lineitem where l_shipdate='1992-07-01'; -flush status; -select min(l_orderkey) from lineitem where l_shipdate='1992-07-01'; -show status like 'handler_read%'; - -set optimizer_switch='extended_keys=off'; explain select min(l_orderkey) from lineitem where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; @@ -112,16 +58,6 @@ select min(l_orderkey) from lineitem where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; show status like 'handler_read%'; -set optimizer_switch='extended_keys=on'; -explain -select min(l_orderkey) from lineitem - where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; -flush status; -select min(l_orderkey) from lineitem - where l_shipdate='1992-07-01' and l_orderkey between 1001 and 2000; -show status like 'handler_read%'; - -set optimizer_switch='extended_keys=off'; explain select max(l_linenumber) from lineitem where l_shipdate='1992-07-01' and l_orderkey=130; @@ -130,16 +66,6 @@ select max(l_linenumber) from lineitem where l_shipdate='1992-07-01' and l_orderkey=130; show status like 'handler_read%'; -set optimizer_switch='extended_keys=on'; -explain -select max(l_linenumber) from lineitem - where l_shipdate='1992-07-01' and l_orderkey=130; -flush status; -select max(l_linenumber) from lineitem - where l_shipdate='1992-07-01' and l_orderkey=130; -show status like 'handler_read%'; - -set optimizer_switch='extended_keys=off'; explain select l_orderkey, l_linenumber from lineitem use index (i_l_shipdate, i_l_receiptdate) @@ -152,20 +78,6 @@ select l_orderkey, l_linenumber or l_receiptdate='1992-07-01' and l_orderkey=5603; show status like 'handler_read%'; -set optimizer_switch='extended_keys=on'; -explain -select l_orderkey, l_linenumber - from lineitem use index (i_l_shipdate, i_l_receiptdate) - where l_shipdate='1992-07-01' and l_orderkey=130 - or l_receiptdate='1992-07-01' and l_orderkey=5603; -flush status; -select l_orderkey, l_linenumber - from lineitem use index (i_l_shipdate, i_l_receiptdate) - where l_shipdate='1992-07-01' and l_orderkey=130 - or l_receiptdate='1992-07-01' and l_orderkey=5603; -show status like 'handler_read%'; - -set optimizer_switch='extended_keys=off'; explain select l_orderkey, l_linenumber from lineitem use index (i_l_shipdate, i_l_receiptdate) @@ -178,20 +90,6 @@ select l_orderkey, l_linenumber or l_receiptdate='1992-07-01' and l_orderkey between 5001 and 6000; show status like 'handler_read%'; -set optimizer_switch='extended_keys=on'; -explain -select l_orderkey, l_linenumber - from lineitem use index (i_l_shipdate, i_l_receiptdate) - where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000 - or l_receiptdate='1992-07-01' and l_orderkey between 5001 and 6000; -flush status; -select l_orderkey, l_linenumber - from lineitem use index (i_l_shipdate, i_l_receiptdate) - where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000 - or l_receiptdate='1992-07-01' and l_orderkey between 5001 and 6000; -show status like 'handler_read%'; - -set optimizer_switch='extended_keys=off'; explain select l_orderkey, l_linenumber from lineitem where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000 @@ -202,18 +100,6 @@ select l_orderkey, l_linenumber from lineitem or l_receiptdate='1992-07-01' and l_orderkey between 5001 and 6000; show status like 'handler_read%'; -set optimizer_switch='extended_keys=on'; -explain -select l_orderkey, l_linenumber from lineitem - where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000 - or l_receiptdate='1992-07-01' and l_orderkey between 5001 and 6000; -flush status; -select l_orderkey, l_linenumber from lineitem - where l_shipdate='1992-07-01' and l_orderkey between 1 and 1000 - or l_receiptdate='1992-07-01' and l_orderkey between 5001 and 6000; -show status like 'handler_read%'; - -set optimizer_switch='extended_keys=off'; --replace_column 9 # explain select max(l_orderkey) from lineitem @@ -223,27 +109,6 @@ select max(l_orderkey) from lineitem where l_partkey between 1 and 10 group by l_partkey; show status like 'handler_read%'; -set optimizer_switch='extended_keys=on'; ---replace_column 9 # -explain -select max(l_orderkey) from lineitem - where l_partkey between 1 and 10 group by l_partkey; -flush status; -select max(l_orderkey) from lineitem - where l_partkey between 1 and 10 group by l_partkey; -show status like 'handler_read%'; - -set optimizer_switch='extended_keys=off'; ---replace_column 9 # -explain -select max(l_orderkey) from lineitem - where l_suppkey in (1,4) group by l_suppkey; -flush status; -select max(l_orderkey) from lineitem - where l_suppkey in (1,4) group by l_suppkey; -show status like 'handler_read%'; - -set optimizer_switch='extended_keys=on'; --replace_column 9 # explain select max(l_orderkey) from lineitem @@ -255,23 +120,6 @@ show status like 'handler_read%'; create index i_p_retailprice on part(p_retailprice); -set optimizer_switch='extended_keys=off'; ---replace_column 9 # -explain -select o_orderkey, p_partkey - from part use index (i_p_retailprice), - lineitem use index (i_l_partkey), orders - where p_retailprice > 1100 and o_orderdate='1997-01-01' - and o_orderkey=l_orderkey and p_partkey=l_partkey; -flush status; -select o_orderkey, p_partkey - from part use index (i_p_retailprice), - lineitem use index (i_l_partkey), orders - where p_retailprice > 1100 and o_orderdate='1997-01-01' - and o_orderkey=l_orderkey and p_partkey=l_partkey; -show status like 'handler_read%'; - -set optimizer_switch='extended_keys=on'; --replace_column 9 # explain select o_orderkey, p_partkey @@ -296,8 +144,6 @@ create table t0 (a int); insert into t0 values (1), (2), (3), (4), (5); create index i_p_size on part(p_size); -set optimizer_switch='extended_keys=on'; - explain select * from t0, part ignore index (primary) where p_partkey=t0.a and p_size=1; @@ -320,7 +166,6 @@ use test; set @save_optimizer_switch=@@optimizer_switch; SET optimizer_switch='materialization=on,semijoin=on'; -SET optimizer_switch='extended_keys=on'; CREATE TABLE t1 (a int, b int) ENGINE=MyISAM; INSERT INTO t1 VALUES (1,1), (2,2); @@ -339,10 +184,7 @@ set optimizer_switch=@save_optimizer_switch; --echo # (valgrinf complains fixed by the patch for bug #914560) --echo # -set @save_optimizer_switch=@@optimizer_switch; - SET optimizer_switch = 'derived_with_keys=on'; -SET optimizer_switch = 'extended_keys=on'; CREATE TABLE t1 (a varchar(1)) ENGINE=MyISAM; INSERT INTO t1 VALUES ('j'), ('v'); @@ -382,24 +224,12 @@ INSERT INTO t2 VALUES (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), (24); -set @save_optimizer_switch=@@optimizer_switch; - -SET optimizer_switch = 'extended_keys=off'; EXPLAIN SELECT a FROM t1 AS t, t2 WHERE c = a AND b IN (SELECT b FROM t1, t2 WHERE b = t.b); SELECT a FROM t1 AS t, t2 WHERE c = a AND b IN (SELECT b FROM t1, t2 WHERE b = t.b); -SET optimizer_switch = 'extended_keys=on'; -EXPLAIN -SELECT a FROM t1 AS t, t2 - WHERE c = a AND b IN (SELECT b FROM t1, t2 WHERE b = t.b); -SELECT a FROM t1 AS t, t2 - WHERE c = a AND b IN (SELECT b FROM t1, t2 WHERE b = t.b); - -set optimizer_switch=@save_optimizer_switch; - DROP TABLE t1,t2; --echo # @@ -413,13 +243,11 @@ CREATE TABLE t2 (b int) ENGINE=MyISAM; INSERT INTO t1 (a) VALUES (4), (6); INSERT INTO t2 (b) VALUES (0), (8); -set @save_optimizer_switch=@@optimizer_switch; set @save_join_cache_level=@@join_cache_level; SET join_cache_level=3; SET optimizer_switch='join_cache_hashed=on'; SET optimizer_switch='join_cache_bka=on'; -SET optimizer_switch='extended_keys=on'; EXPLAIN SELECT * FROM t1, t2 WHERE b=a; @@ -448,24 +276,11 @@ KEY c4 (c4) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; -set @save_optimizer_switch=@@optimizer_switch; - -set session optimizer_switch='extended_keys=off'; INSERT INTO t1 (c2, c3, c4) VALUES (58291525, 2580, 'foobar') ON DUPLICATE KEY UPDATE c4 = VALUES(c4); INSERT INTO t1 (c2, c3, c4) VALUES (58291525, 2580, 'foobar') ON DUPLICATE KEY UPDATE c4 = VALUES(c4); -DELETE FROM t1; - -set session optimizer_switch='extended_keys=on'; -INSERT INTO t1 (c2, c3, c4) VALUES (58291525, 2580, 'foobar') - ON DUPLICATE KEY UPDATE c4 = VALUES(c4); -INSERT INTO t1 (c2, c3, c4) VALUES (58291525, 2580, 'foobar') - ON DUPLICATE KEY UPDATE c4 = VALUES(c4); - -set optimizer_switch=@save_optimizer_switch; - DROP TABLE t1; --echo # @@ -474,8 +289,6 @@ DROP TABLE t1; --echo # (performance regression introduced in the patch for mdev-3851) --echo # -set @save_optimizer_switch=@@optimizer_switch; - create table t1 (a int not null) engine=innodb; insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); @@ -489,14 +302,6 @@ select A.a + 10 * B.a, A.a + 10 * B.a, A.a + 10 * B.a from t1 A, t1 B; -set optimizer_switch='extended_keys=off'; -explain -select * from t1, t2 where t2.a=t1.a and t2.b < 2; -flush status; -select * from t1, t2 where t2.a=t1.a and t2.b < 2; -show status like 'handler_read%'; - -set optimizer_switch='extended_keys=on'; explain select * from t1, t2 where t2.a=t1.a and t2.b < 2; flush status; @@ -522,15 +327,6 @@ alter table t3 add primary key (pk1, pk2); alter table t3 add key (col1, col2); analyze table t1,t3; -set optimizer_switch='extended_keys=off'; ---replace_column 9 # -explain -select * from t1, t3 where t3.col1=t1.a and t3.col2=t1.a; ---replace_column 9 # -explain -select * from t1, t3 where t3.col1=t1.a and t3.col2=t1.a and t3.pk1=t1.a; - -set optimizer_switch='extended_keys=on'; --replace_column 9 # explain select * from t1, t3 where t3.col1=t1.a and t3.col2=t1.a; @@ -544,8 +340,6 @@ drop table t1,t2,t3; --echo # Bug mdev-4340: performance regression with extended_keys=on --echo # -set @save_optimizer_switch=@@optimizer_switch; - CREATE TABLE t1 ( page_id int(8) unsigned NOT NULL AUTO_INCREMENT, page_namespace int(11) NOT NULL DEFAULT '0', @@ -649,13 +443,6 @@ INSERT INTO t3 VALUES (97,'text-6009',''),(98,'text-7009',''),(99,'text-8009',''),(100,'text-9009',''); -set optimizer_switch='extended_keys=off'; -EXPLAIN -SELECT * FROM t1, t2 IGNORE INDEX (PRIMARY), t3 - WHERE page_id=rev_page AND rev_text_id=old_id AND page_namespace=4 AND page_title='Sandbox' -ORDER BY rev_timestamp ASC LIMIT 10; - -set optimizer_switch='extended_keys=on'; EXPLAIN SELECT * FROM t1, t2 IGNORE INDEX (PRIMARY), t3 WHERE page_id=rev_page AND rev_text_id=old_id AND page_namespace=4 AND page_title='Sandbox' @@ -674,14 +461,10 @@ create table t2 (a bigint not null unique auto_increment, b varchar(10), primary insert into t1 (b) values (null), (null), (null); insert into t2 (b) values (null), (null), (null); -set optimizer_switch='extended_keys=on'; explain select a from t1 where b is null order by a desc limit 2; select a from t1 where b is null order by a desc limit 2; explain select a from t2 where b is null order by a desc limit 2; select a from t2 where b is null order by a desc limit 2; -set optimizer_switch='extended_keys=off'; -explain select a from t2 where b is null order by a desc limit 2; -select a from t2 where b is null order by a desc limit 2; explain select a from t2 where b is null order by a desc; select a from t2 where b is null order by a desc; @@ -691,8 +474,6 @@ select a from t2 where b is null order by a desc,a,a; drop table t1, t2; -set optimizer_switch=@save_optimizer_switch; - --echo # --echo # MDEV-10325: Queries examines all rows of a tables when it should not --echo # @@ -828,7 +609,6 @@ explain format= json select * from t1 force index(k1) where f2 <= 5 and pk2 <=5 and pk1 = 'abc' and f1 <= '3'; drop table t1; -set optimizer_switch=@save_ext_key_optimizer_switch; set global innodb_file_format = @save_innodb_file_format; set global innodb_large_prefix = @save_innodb_large_prefix; SET SESSION STORAGE_ENGINE=DEFAULT; diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 936b0eccfb7..f45702157c8 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -2,7 +2,7 @@ Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. -Copyright (c) 2015, 2018, MariaDB Corporation. +Copyright (c) 2015, 2020, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -3723,9 +3723,16 @@ bool row_search_with_covering_prefix( return false; } + /* We can avoid a clustered index lookup if + all of the following hold: + (1) all columns are in the secondary index + (2) all values for columns that are prefix-only + indexes are shorter than the prefix size + This optimization can avoid many IOs for certain schemas. */ for (ulint i = 0; i < prebuilt->n_template; i++) { mysql_row_templ_t* templ = prebuilt->mysql_template + i; ulint j = templ->rec_prefix_field_no; + ut_ad(!templ->mbminlen == !templ->mbmaxlen); /** Condition (1) : is the field in the index. */ if (j == ULINT_UNDEFINED) { @@ -3735,33 +3742,29 @@ bool row_search_with_covering_prefix( /** Condition (2): If this is a prefix index then row's value size shorter than prefix length. */ - if (!templ->rec_field_is_prefix) { + if (!templ->rec_field_is_prefix + || rec_offs_nth_sql_null(offsets, j)) { continue; } - ulint rec_size = rec_offs_nth_size(offsets, j); const dict_field_t* field = dict_index_get_nth_field(index, j); - ulint max_chars = field->prefix_len / templ->mbmaxlen; - ut_a(field->prefix_len > 0); - - if (rec_size < max_chars) { - /* Record in bytes shorter than the index - prefix length in char. */ + if (!field->prefix_len) { continue; } - if (rec_size * templ->mbminlen >= field->prefix_len) { + const ulint rec_size = rec_offs_nth_size(offsets, j); + + if (rec_size >= field->prefix_len) { /* Shortest representation string by the byte length of the record is longer than the maximum possible index prefix. */ return false; } - size_t num_chars = rec_field_len_in_chars( - field->col, j, rec, offsets); - - if (num_chars >= max_chars) { + if (templ->mbminlen != templ->mbmaxlen + && rec_field_len_in_chars(field->col, j, rec, offsets) + >= field->prefix_len / templ->mbmaxlen) { /* No of chars to store the record exceeds the index prefix character length. */ return false; diff --git a/storage/xtradb/row/row0sel.cc b/storage/xtradb/row/row0sel.cc index c9c7f1dd9c1..a968fc83243 100644 --- a/storage/xtradb/row/row0sel.cc +++ b/storage/xtradb/row/row0sel.cc @@ -2,7 +2,7 @@ Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. -Copyright (c) 2015, 2018, MariaDB Corporation. +Copyright (c) 2015, 2020, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -3740,9 +3740,16 @@ bool row_search_with_covering_prefix( return false; } + /* We can avoid a clustered index lookup if + all of the following hold: + (1) all columns are in the secondary index + (2) all values for columns that are prefix-only + indexes are shorter than the prefix size + This optimization can avoid many IOs for certain schemas. */ for (ulint i = 0; i < prebuilt->n_template; i++) { mysql_row_templ_t* templ = prebuilt->mysql_template + i; ulint j = templ->rec_prefix_field_no; + ut_ad(!templ->mbminlen == !templ->mbmaxlen); /** Condition (1) : is the field in the index. */ if (j == ULINT_UNDEFINED) { @@ -3752,34 +3759,29 @@ bool row_search_with_covering_prefix( /** Condition (2): If this is a prefix index then row's value size shorter than prefix length. */ - if (!templ->rec_field_is_prefix) { + if (!templ->rec_field_is_prefix + || rec_offs_nth_sql_null(offsets, j)) { continue; } - ulint rec_size = rec_offs_nth_size(offsets, j); const dict_field_t* field = dict_index_get_nth_field(index, j); - ulint max_chars = field->prefix_len / templ->mbmaxlen; - ut_a(field->prefix_len > 0); - - if (rec_size < max_chars) { - /* Record in bytes shorter than the index - prefix length in char. */ + if (!field->prefix_len) { continue; } - if (rec_size * templ->mbminlen >= field->prefix_len) { + const ulint rec_size = rec_offs_nth_size(offsets, j); + + if (rec_size >= field->prefix_len) { /* Shortest representation string by the byte length of the record is longer than the maximum possible index prefix. */ return false; } - - size_t num_chars = rec_field_len_in_chars( - field->col, j, rec, offsets); - - if (num_chars >= max_chars) { + if (templ->mbminlen != templ->mbmaxlen + && rec_field_len_in_chars(field->col, j, rec, offsets) + >= field->prefix_len / templ->mbmaxlen) { /* No of chars to store the record exceeds the index prefix character length. */ return false; From 4a165f37114eafc0cced25dfea80b3f3bccc2fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 31 Aug 2020 16:48:28 +0300 Subject: [PATCH 22/40] Fix GCC 10.2.0 -Og -Wmaybe-uninitialized --- sql/opt_range.cc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 6a5f1c9f750..e933d2af355 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -7555,13 +7555,15 @@ SEL_TREE *Item_bool_func::get_full_func_mm_tree(RANGE_OPT_PARAM *param, table_map param_comp= ~(param->prev_tables | param->read_tables | param->current_table); #ifdef HAVE_SPATIAL - Field::geometry_type sav_geom_type; - const bool geometry= field_item->field->type() == MYSQL_TYPE_GEOMETRY; - if (geometry) + Field::geometry_type sav_geom_type= Field::GEOM_GEOMETRY, *geom_type= + field_item->field->type() == MYSQL_TYPE_GEOMETRY + ? &(static_cast(field_item->field))->geom_type + : NULL; + if (geom_type) { - sav_geom_type= ((Field_geom*) field_item->field)->geom_type; + sav_geom_type= *geom_type; /* We have to be able to store all sorts of spatial features here */ - ((Field_geom*) field_item->field)->geom_type= Field::GEOM_GEOMETRY; + *geom_type= Field::GEOM_GEOMETRY; } #endif /*HAVE_SPATIAL*/ @@ -7592,9 +7594,9 @@ SEL_TREE *Item_bool_func::get_full_func_mm_tree(RANGE_OPT_PARAM *param, } #ifdef HAVE_SPATIAL - if (geometry) + if (geom_type) { - ((Field_geom*) field_item->field)->geom_type= sav_geom_type; + *geom_type= sav_geom_type; } #endif /*HAVE_SPATIAL*/ DBUG_RETURN(ftree); From ebafe5a2367478413bd582487c9afc18ccd7f4f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 31 Aug 2020 17:20:03 +0300 Subject: [PATCH 23/40] Cleanup: Avoid repeated calls to dict_col_t::is_virtual() --- storage/innobase/handler/ha_innodb.cc | 9 ++++---- storage/innobase/row/row0merge.cc | 30 +++++++++++---------------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 3b404928cf4..e171b6a99b3 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -5775,14 +5775,13 @@ innobase_vcol_build_templ( mysql_row_templ_t* templ, ulint col_no) { - if (dict_col_is_virtual(col)) { - templ->is_virtual = true; - templ->col_no = col_no; + templ->col_no = col_no; + templ->is_virtual = col->is_virtual(); + + if (templ->is_virtual) { templ->clust_rec_field_no = ULINT_UNDEFINED; templ->rec_field_no = col->ind; } else { - templ->is_virtual = false; - templ->col_no = col_no; templ->clust_rec_field_no = dict_col_get_clust_pos( col, clust_index); ut_a(templ->clust_rec_field_no != ULINT_UNDEFINED); diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index a07ac220419..6571a7fbfec 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -554,23 +554,16 @@ row_merge_buf_add( for (i = 0; i < n_fields; i++, field++, ifield++) { ulint len; - const dict_col_t* col; - ulint col_no; ulint fixed_len; const dfield_t* row_field; - - col = ifield->col; - const dict_v_col_t* v_col = NULL; - if (dict_col_is_virtual(col)) { - v_col = reinterpret_cast(col); - } - - col_no = dict_col_get_no(col); + const dict_col_t* const col = ifield->col; + const dict_v_col_t* const v_col = col->is_virtual() + ? reinterpret_cast(col) + : NULL; /* Process the Doc ID column */ - if (*doc_id > 0 - && col_no == index->table->fts->doc_col - && !dict_col_is_virtual(col)) { + if (!v_col && *doc_id + && col->ind == index->table->fts->doc_col) { fts_write_doc_id((byte*) &write_doc_id, *doc_id); /* Note: field->data now points to a value on the @@ -589,7 +582,7 @@ row_merge_buf_add( field->type.len = ifield->col->len; } else { /* Use callback to get the virtual column value */ - if (dict_col_is_virtual(col)) { + if (v_col) { dict_index_t* clust_index = dict_table_get_first_index(new_table); @@ -614,7 +607,8 @@ row_merge_buf_add( } dfield_copy(field, row_field); } else { - row_field = dtuple_get_nth_field(row, col_no); + row_field = dtuple_get_nth_field(row, + col->ind); dfield_copy(field, row_field); } @@ -720,7 +714,7 @@ row_merge_buf_add( } else if (!ext) { } else if (dict_index_is_clust(index)) { /* Flag externally stored fields. */ - const byte* buf = row_ext_lookup(ext, col_no, + const byte* buf = row_ext_lookup(ext, col->ind, &len); if (UNIV_LIKELY_NULL(buf)) { ut_a(buf != field_ref_zero); @@ -731,9 +725,9 @@ row_merge_buf_add( len = dfield_get_len(field); } } - } else if (!dict_col_is_virtual(col)) { + } else if (!v_col) { /* Only non-virtual column are stored externally */ - const byte* buf = row_ext_lookup(ext, col_no, + const byte* buf = row_ext_lookup(ext, col->ind, &len); if (UNIV_LIKELY_NULL(buf)) { ut_a(buf != field_ref_zero); From 594aad7b0607b752c3815d2c31370a63006d4542 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Tue, 1 Sep 2020 21:12:01 +1000 Subject: [PATCH 24/40] travis: osx build time out storing cache. Ensure not Cellar --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a430ff14640..25159e30191 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,8 +20,6 @@ cache: timeout: 500 apt: true ccache: true - directories: - - /usr/local/Cellar # Fails do to permission error: https://github.com/travis-ci/travis-ci/issues/8092 env: matrix: From 56ae0adee35c1bc053711a8579857e6b0301eb80 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Wed, 2 Sep 2020 11:23:18 +1000 Subject: [PATCH 25/40] travis: update osx to xcode12u in attempt to solve openssl build failure --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 25159e30191..809a08fd6a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ language: cpp os: - linux - osx -osx_image: xcode10.1 +osx_image: xcode12u compiler: - gcc - clang From 054f96ecff111c0925edba5657a69611cb243643 Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Wed, 2 Sep 2020 12:54:14 +1000 Subject: [PATCH 26/40] innodb: osx build failure fix Build failure was: storage/innobase/os/os0proc.cc:144:3: error: use of undeclared identifier 'MEM_UNDEFINED' MEM_UNDEFINED(ptr, size); Assumed to be introduced in MDEV-20377 commit: c36834c8324974f26770d64192898f4f45d9f772 --- storage/innobase/os/os0proc.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/innobase/os/os0proc.cc b/storage/innobase/os/os0proc.cc index 508a13de2ca..71cb88ae372 100644 --- a/storage/innobase/os/os0proc.cc +++ b/storage/innobase/os/os0proc.cc @@ -29,6 +29,7 @@ Created 9/30/1995 Heikki Tuuri #ifdef HAVE_LINUX_LARGE_PAGES # include "mysqld.h" #endif +#include "my_valgrind.h" /* FreeBSD for example has only MAP_ANON, Linux has MAP_ANONYMOUS and MAP_ANON but MAP_ANON is marked as deprecated */ From cd36bc01a5f1c1cd126cdd0668c6049cc78be40a Mon Sep 17 00:00:00 2001 From: Sujatha Date: Wed, 26 Aug 2020 16:25:28 +0530 Subject: [PATCH 27/40] MDEV-23534: SIGSEGV in sf_malloc_usable_size/my_free on SET GLOBAL REPLICATE_DO_TABLE Backporting fixes for: MDEV-22317: SIGSEGV in my_free/delete_dynamic in optimized builds (ARIA) Backported following commits from: 10.5.3 commit 77e1b0c39771f47bb2602530b8d1e584ac1d3731 -- Post push fix. commit 2e6b21be4a8d0bf094da288cadff866f1bb38062 MDEV-22059: MSAN report at replicate_ignore_table_grant Backported following commits from: 10.5.4 commit 840fb495ce2c0c00b20f2a9ba44b6fcc20c56118 --- .../rpl/r/rpl_filter_tables_dynamic.result | 2 + .../r/rpl_filter_wild_tables_dynamic.result | 2 + .../rpl/t/rpl_filter_tables_dynamic.test | 2 + .../rpl/t/rpl_filter_wild_tables_dynamic.test | 2 + sql/rpl_filter.cc | 56 +++++++++++++------ 5 files changed, 48 insertions(+), 16 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_filter_tables_dynamic.result b/mysql-test/suite/rpl/r/rpl_filter_tables_dynamic.result index 5a746c88458..9709e24fbde 100644 --- a/mysql-test/suite/rpl/r/rpl_filter_tables_dynamic.result +++ b/mysql-test/suite/rpl/r/rpl_filter_tables_dynamic.result @@ -5,6 +5,8 @@ ERROR HY000: This operation cannot be performed as you have a running slave ''; SET @@GLOBAL.replicate_ignore_table="test.t4,test.t5,test.t6"; ERROR HY000: This operation cannot be performed as you have a running slave ''; run STOP SLAVE '' first include/stop_slave.inc +SET @@GLOBAL.replicate_do_table=""; +SET @@GLOBAL.replicate_ignore_table=""; SET @@GLOBAL.replicate_do_table="test.t1,test.t2,test.t3"; SET @@GLOBAL.replicate_ignore_table="test.t4,test.t5,test.t6"; include/start_slave.inc diff --git a/mysql-test/suite/rpl/r/rpl_filter_wild_tables_dynamic.result b/mysql-test/suite/rpl/r/rpl_filter_wild_tables_dynamic.result index 19d8e513e6f..338f4b3bbcf 100644 --- a/mysql-test/suite/rpl/r/rpl_filter_wild_tables_dynamic.result +++ b/mysql-test/suite/rpl/r/rpl_filter_wild_tables_dynamic.result @@ -5,6 +5,8 @@ ERROR HY000: This operation cannot be performed as you have a running slave ''; SET @@GLOBAL.replicate_wild_ignore_table="test.b%"; ERROR HY000: This operation cannot be performed as you have a running slave ''; run STOP SLAVE '' first include/stop_slave.inc +SET @@GLOBAL.replicate_wild_do_table=""; +SET @@GLOBAL.replicate_wild_ignore_table=""; SET @@GLOBAL.replicate_wild_do_table="test.a%"; SET @@GLOBAL.replicate_wild_ignore_table="test.b%"; include/start_slave.inc diff --git a/mysql-test/suite/rpl/t/rpl_filter_tables_dynamic.test b/mysql-test/suite/rpl/t/rpl_filter_tables_dynamic.test index 97ecc167356..ebededc36b3 100644 --- a/mysql-test/suite/rpl/t/rpl_filter_tables_dynamic.test +++ b/mysql-test/suite/rpl/t/rpl_filter_tables_dynamic.test @@ -51,6 +51,8 @@ SET @@GLOBAL.replicate_ignore_table="test.t4,test.t5,test.t6"; connection slave; source include/stop_slave.inc; +SET @@GLOBAL.replicate_do_table=""; +SET @@GLOBAL.replicate_ignore_table=""; SET @@GLOBAL.replicate_do_table="test.t1,test.t2,test.t3"; SET @@GLOBAL.replicate_ignore_table="test.t4,test.t5,test.t6"; source include/start_slave.inc; diff --git a/mysql-test/suite/rpl/t/rpl_filter_wild_tables_dynamic.test b/mysql-test/suite/rpl/t/rpl_filter_wild_tables_dynamic.test index c822c81f270..09db91aa4d3 100644 --- a/mysql-test/suite/rpl/t/rpl_filter_wild_tables_dynamic.test +++ b/mysql-test/suite/rpl/t/rpl_filter_wild_tables_dynamic.test @@ -13,6 +13,8 @@ SET @@GLOBAL.replicate_wild_ignore_table="test.b%"; connection slave; source include/stop_slave.inc; +SET @@GLOBAL.replicate_wild_do_table=""; +SET @@GLOBAL.replicate_wild_ignore_table=""; SET @@GLOBAL.replicate_wild_do_table="test.a%"; SET @@GLOBAL.replicate_wild_ignore_table="test.b%"; source include/start_slave.inc; diff --git a/sql/rpl_filter.cc b/sql/rpl_filter.cc index 366902c1f26..0d5d9ffeea8 100644 --- a/sql/rpl_filter.cc +++ b/sql/rpl_filter.cc @@ -349,16 +349,22 @@ Rpl_filter::set_do_table(const char* table_spec) int status; if (do_table_inited) - my_hash_reset(&do_table); - - status= parse_filter_rule(table_spec, &Rpl_filter::add_do_table); - - if (!do_table.records) { my_hash_free(&do_table); do_table_inited= 0; } + status= parse_filter_rule(table_spec, &Rpl_filter::add_do_table); + + if (do_table_inited && status) + { + if (!do_table.records) + { + my_hash_free(&do_table); + do_table_inited= 0; + } + } + return status; } @@ -369,16 +375,22 @@ Rpl_filter::set_ignore_table(const char* table_spec) int status; if (ignore_table_inited) - my_hash_reset(&ignore_table); - - status= parse_filter_rule(table_spec, &Rpl_filter::add_ignore_table); - - if (!ignore_table.records) { my_hash_free(&ignore_table); ignore_table_inited= 0; } + status= parse_filter_rule(table_spec, &Rpl_filter::add_ignore_table); + + if (ignore_table_inited && status) + { + if (!ignore_table.records) + { + my_hash_free(&ignore_table); + ignore_table_inited= 0; + } + } + return status; } @@ -411,14 +423,20 @@ Rpl_filter::set_wild_do_table(const char* table_spec) int status; if (wild_do_table_inited) + { free_string_array(&wild_do_table); + wild_do_table_inited= 0; + } status= parse_filter_rule(table_spec, &Rpl_filter::add_wild_do_table); - if (!wild_do_table.elements) + if (wild_do_table_inited && status) { - delete_dynamic(&wild_do_table); - wild_do_table_inited= 0; + if (!wild_do_table.elements) + { + delete_dynamic(&wild_do_table); + wild_do_table_inited= 0; + } } return status; @@ -431,14 +449,20 @@ Rpl_filter::set_wild_ignore_table(const char* table_spec) int status; if (wild_ignore_table_inited) + { free_string_array(&wild_ignore_table); + wild_ignore_table_inited= 0; + } status= parse_filter_rule(table_spec, &Rpl_filter::add_wild_ignore_table); - if (!wild_ignore_table.elements) + if (wild_ignore_table_inited && status) { - delete_dynamic(&wild_ignore_table); - wild_ignore_table_inited= 0; + if (!wild_ignore_table.elements) + { + delete_dynamic(&wild_ignore_table); + wild_ignore_table_inited= 0; + } } return status; From 837bbbafc535d14ea102ced983349fd29b84f782 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Wed, 2 Sep 2020 16:15:02 +0530 Subject: [PATCH 28/40] MDEV-23470 InnoDB: Failing assertion: cmp < 0 in row_ins_check_foreign_constraint During insertion of clustered index, InnoDB does the check for foreign key constraints. Problem is that it uses the clustered index entry to search indexes of referenced tables and it could lead to unexpected result when there is no foreign index. Solution: ======== Rebuild the tuple based on foreign column names before searching it on reference index when there is no foreign index. --- mysql-test/suite/innodb/r/foreign-keys.result | 16 ++++ mysql-test/suite/innodb/t/foreign-keys.test | 19 +++++ storage/innobase/row/row0ins.cc | 74 ++++++++++++++++--- 3 files changed, 100 insertions(+), 9 deletions(-) diff --git a/mysql-test/suite/innodb/r/foreign-keys.result b/mysql-test/suite/innodb/r/foreign-keys.result index 5cbbb5298de..05e68b0c3db 100644 --- a/mysql-test/suite/innodb/r/foreign-keys.result +++ b/mysql-test/suite/innodb/r/foreign-keys.result @@ -220,3 +220,19 @@ drop table t1,t2; ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails drop table t1,t2; ERROR 42S02: Unknown table 'test.t2' +# +# MDEV-23470 InnoDB: Failing assertion: cmp < 0 in +# row_ins_check_foreign_constraint +# +CREATE TABLE t1(f1 INT NOT NULL PRIMARY KEY, f2 INT NOT NULL)ENGINE=InnoDB; +CREATE TABLE t2(f1 VARCHAR(100), f2 INT NOT NULL, +INDEX(f2))ENGINE=InnoDB; +INSERT INTO t1 VALUES(99, 2); +ALTER TABLE t2 ADD FOREIGN KEY(f2) REFERENCES t1(f1); +SET FOREIGN_KEY_CHECKS=0; +DROP INDEX f2 ON t2; +SET FOREIGN_KEY_CHECKS=1; +INSERT INTO t2 VALUES('G', 3); +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`f2`) REFERENCES `t1` (`f1`)) +DROP TABLE t2, t1; +SET FOREIGN_KEY_CHECKS=DEFAULT; diff --git a/mysql-test/suite/innodb/t/foreign-keys.test b/mysql-test/suite/innodb/t/foreign-keys.test index 58a71d11a6a..53fbe8d00dc 100644 --- a/mysql-test/suite/innodb/t/foreign-keys.test +++ b/mysql-test/suite/innodb/t/foreign-keys.test @@ -250,3 +250,22 @@ show create table t2; drop table t1,t2; --error ER_BAD_TABLE_ERROR drop table t1,t2; + +--echo # +--echo # MDEV-23470 InnoDB: Failing assertion: cmp < 0 in +--echo # row_ins_check_foreign_constraint +--echo # +CREATE TABLE t1(f1 INT NOT NULL PRIMARY KEY, f2 INT NOT NULL)ENGINE=InnoDB; +CREATE TABLE t2(f1 VARCHAR(100), f2 INT NOT NULL, + INDEX(f2))ENGINE=InnoDB; + +INSERT INTO t1 VALUES(99, 2); +ALTER TABLE t2 ADD FOREIGN KEY(f2) REFERENCES t1(f1); + +SET FOREIGN_KEY_CHECKS=0; +DROP INDEX f2 ON t2; +SET FOREIGN_KEY_CHECKS=1; +--error ER_NO_REFERENCED_ROW_2 +INSERT INTO t2 VALUES('G', 3); +DROP TABLE t2, t1; +SET FOREIGN_KEY_CHECKS=DEFAULT; diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index b8c8a73842f..af6d2f5fa83 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -1905,6 +1905,39 @@ exit_func: DBUG_RETURN(err); } +/** Sets the values of the dtuple fields in ref_entry from the values of +foreign columns in entry. +@param[in] foreign foreign key constraint +@param[in] index clustered index +@param[in] entry tuple of clustered index +@param[in] ref_entry tuple of foreign columns +@return true if all foreign key fields present in clustered index */ +static +bool row_ins_foreign_index_entry(dict_foreign_t *foreign, + const dict_index_t *index, + const dtuple_t *entry, + dtuple_t *ref_entry) +{ + for (ulint i= 0; i < foreign->n_fields; i++) + { + for (ulint j= 0; j < index->n_fields; j++) + { + const char *col_name= dict_table_get_col_name( + index->table, dict_index_get_nth_col_no(index, j)); + if (0 == innobase_strcasecmp(col_name, foreign->foreign_col_names[i])) + { + dfield_copy(&ref_entry->fields[i], &entry->fields[j]); + goto got_match; + } + } + return false; +got_match: + continue; + } + + return true; +} + /***************************************************************//** Checks if foreign key constraints fail for an index entry. If index is not mentioned in any constraint, this function does nothing, @@ -1923,9 +1956,10 @@ row_ins_check_foreign_constraints( que_thr_t* thr) /*!< in: query thread */ { dict_foreign_t* foreign; - dberr_t err; + dberr_t err = DB_SUCCESS; trx_t* trx; ibool got_s_lock = FALSE; + mem_heap_t* heap = NULL; DBUG_ASSERT(index->is_primary() == pk); @@ -1935,13 +1969,36 @@ row_ins_check_foreign_constraints( "foreign_constraint_check_for_ins"); for (dict_foreign_set::iterator it = table->foreign_set.begin(); - it != table->foreign_set.end(); + err == DB_SUCCESS && it != table->foreign_set.end(); ++it) { foreign = *it; if (foreign->foreign_index == index || (pk && !foreign->foreign_index)) { + + dtuple_t* ref_tuple = entry; + if (UNIV_UNLIKELY(!foreign->foreign_index)) { + /* Change primary key entry to + foreign key index entry */ + if (!heap) { + heap = mem_heap_create(1000); + } else { + mem_heap_empty(heap); + } + + ref_tuple = dtuple_create( + heap, foreign->n_fields); + dtuple_set_n_fields_cmp( + ref_tuple, foreign->n_fields); + if (!row_ins_foreign_index_entry( + foreign, index, entry, ref_tuple)) { + err = DB_NO_REFERENCED_ROW; + break; + } + + } + dict_table_t* ref_table = NULL; dict_table_t* referenced_table = foreign->referenced_table; @@ -1971,7 +2028,7 @@ row_ins_check_foreign_constraints( table from being dropped while the check is running. */ err = row_ins_check_foreign_constraint( - TRUE, foreign, table, entry, thr); + TRUE, foreign, table, ref_tuple, thr); if (referenced_table) { my_atomic_addlint( @@ -1986,15 +2043,14 @@ row_ins_check_foreign_constraints( if (ref_table != NULL) { dict_table_close(ref_table, FALSE, FALSE); } - - if (err != DB_SUCCESS) { - - return(err); - } } } - return(DB_SUCCESS); + if (UNIV_LIKELY_NULL(heap)) { + mem_heap_free(heap); + } + + return err; } /***************************************************************//** From 9aea50616c2d8e067e5ce3efb1152e81877bf931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Wed, 2 Sep 2020 14:57:48 +0300 Subject: [PATCH 29/40] Increase mariabackup SST initial timeout to avoid timeouts. --- scripts/wsrep_sst_mariabackup.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/wsrep_sst_mariabackup.sh b/scripts/wsrep_sst_mariabackup.sh index 86378cb699c..ef2a3b66de8 100644 --- a/scripts/wsrep_sst_mariabackup.sh +++ b/scripts/wsrep_sst_mariabackup.sh @@ -1,6 +1,6 @@ #!/bin/bash -ue # Copyright (C) 2013 Percona Inc -# Copyright (C) 2017-2019 MariaDB +# Copyright (C) 2017-2020 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 @@ -363,7 +363,7 @@ read_cnf() iopts=$(parse_cnf sst inno-backup-opts "") iapts=$(parse_cnf sst inno-apply-opts "") impts=$(parse_cnf sst inno-move-opts "") - stimeout=$(parse_cnf sst sst-initial-timeout 100) + stimeout=$(parse_cnf sst sst-initial-timeout 300) ssyslog=$(parse_cnf sst sst-syslog 0) ssystag=$(parse_cnf mysqld_safe syslog-tag "${SST_SYSLOG_TAG:-}") ssystag+="-" @@ -624,7 +624,8 @@ recv_joiner() popd 1>/dev/null if [[ ${RC[0]} -eq 124 ]];then - wsrep_log_error "Possible timeout in receving first data from donor in gtid stage" + wsrep_log_error "Possible timeout in receiving first data from " + "donor in gtid stage: exit codes: ${RC[@]}" exit 32 fi From a256070e7d94fdd1d63a4823c638ff5c76ca9c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 3 Sep 2020 08:41:47 +0300 Subject: [PATCH 30/40] MDEV-7110 follow-up fix: Do not pass NULL as nonnull parameter Passing a null pointer to the "%s" argument of a printf-like function is undefined behaviour. In the GNU libc implementation of the printf() family of functions, it happens to work. GCC 10.2.0 would diagnose this with -Wformat-overflow -Og. In -fsanitize=undefined (WITH_UBSAN=ON) builds, a runtime error would be generated. In some other builds, GCC 8 or later might infer that the parameter is nonnull and optimize away further checks whether the parameter is null, leading to SIGSEGV. --- sql/mysqld.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 6a5162e2f03..8d00b5af948 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3974,7 +3974,8 @@ rpl_make_log_name(const char *opt, const char *ext) { DBUG_ENTER("rpl_make_log_name"); - DBUG_PRINT("enter", ("opt: %s, def: %s, ext: %s", opt, def, ext)); + DBUG_PRINT("enter", ("opt: %s, def: %s, ext: %s", opt ? opt : "(null)", + def, ext)); char buff[FN_REFLEN]; const char *base= opt ? opt : def; unsigned int options= From 94a520ddbe39ae97de1135d98699cf2674e6b77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 3 Sep 2020 09:05:56 +0300 Subject: [PATCH 31/40] MDEV-22387: Do not pass null pointer to some memcpy() Passing a null pointer to a nonnull argument is not only undefined behaviour, but it also grants the compiler the permission to optimize away further checks whether the pointer is null. GCC -O2 at least starting with version 8 may do that, potentially causing SIGSEGV. These problems were caught in a WITH_UBSAN=ON build with the Bug#7024 test in main.view. --- mysys/mf_iocache.c | 8 +++++--- mysys/my_alloc.c | 4 +++- sql/debug_sync.cc | 4 +++- sql/net_serv.cc | 5 +++-- sql/sql_string.h | 5 +++-- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index 1245a622262..11f7bbd81b4 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2011, Oracle and/or its affiliates - Copyright (c) 2010, 2015, MariaDB + Copyright (c) 2010, 2020, 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 @@ -703,7 +703,8 @@ int _my_b_cache_read(IO_CACHE *info, uchar *Buffer, size_t Count) info->read_pos=info->buffer+Count; info->read_end=info->buffer+length; info->pos_in_file=pos_in_file; - memcpy(Buffer, info->buffer, Count); + if (Count) + memcpy(Buffer, info->buffer, Count); DBUG_RETURN(0); } @@ -1206,7 +1207,8 @@ static int _my_b_cache_read_r(IO_CACHE *cache, uchar *Buffer, size_t Count) DBUG_RETURN(1); } cnt= (len > Count) ? Count : len; - memcpy(Buffer, cache->read_pos, cnt); + if (cnt) + memcpy(Buffer, cache->read_pos, cnt); Count -= cnt; Buffer+= cnt; left_length+= cnt; diff --git a/mysys/my_alloc.c b/mysys/my_alloc.c index 3b614cb4c15..5bfcb455b8a 100644 --- a/mysys/my_alloc.c +++ b/mysys/my_alloc.c @@ -1,5 +1,6 @@ /* Copyright (c) 2000, 2010, Oracle and/or its affiliates + Copyright (c) 2010, 2020, 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 @@ -449,7 +450,8 @@ char *strmake_root(MEM_ROOT *root, const char *str, size_t len) char *pos; if ((pos=alloc_root(root,len+1))) { - memcpy(pos,str,len); + if (len) + memcpy(pos,str,len); pos[len]=0; } return pos; diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc index 591ce5800a8..79f3a241907 100644 --- a/sql/debug_sync.cc +++ b/sql/debug_sync.cc @@ -1,4 +1,5 @@ /* Copyright (c) 2009, 2013, Oracle and/or its affiliates. + Copyright (c) 2013, 2020, 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 @@ -319,7 +320,8 @@ static char *debug_sync_bmove_len(char *to, char *to_end, DBUG_ASSERT(to_end); DBUG_ASSERT(!length || from); set_if_smaller(length, (size_t) (to_end - to)); - memcpy(to, from, length); + if (length) + memcpy(to, from, length); return (to + length); } diff --git a/sql/net_serv.cc b/sql/net_serv.cc index ea5e4353c83..22eee6297c9 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. - Copyright (c) 2012, 2017, MariaDB Corporation + Copyright (c) 2012, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -577,7 +577,8 @@ net_write_buff(NET *net, const uchar *packet, ulong len) return net_real_write(net, packet, len) ? 1 : 0; /* Send out rest of the blocks as full sized blocks */ } - memcpy((char*) net->write_pos,packet,len); + if (len) + memcpy((char*) net->write_pos,packet,len); net->write_pos+= len; return 0; } diff --git a/sql/sql_string.h b/sql/sql_string.h index 6fd3d7a10db..fe756fe1cdf 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -3,7 +3,7 @@ /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2008, 2013, Monty Program Ab. + Copyright (c) 2008, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -512,7 +512,8 @@ public: } void q_append(const char *data, uint32 data_len) { - memcpy(Ptr + str_length, data, data_len); + if (data_len) + memcpy(Ptr + str_length, data, data_len); str_length += data_len; } From 33ae1616e01b564d03c507769564d37c582783cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 3 Sep 2020 14:10:42 +0300 Subject: [PATCH 32/40] MDEV-21578 : CREATE OR REPLACE TRIGGER in Galera cluster not replicating In 10.3 OR REPLACE trigger option is part of create_info. --- sql/wsrep_mysqld.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 20f2cd99edd..de234770788 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -2900,11 +2900,14 @@ static int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len) definer_host.length= 0; } - const LEX_CSTRING command[3]= + const LEX_CSTRING command[2]= {{ C_STRING_WITH_LEN("CREATE ") }, - { C_STRING_WITH_LEN("ALTER ") }, { C_STRING_WITH_LEN("CREATE OR REPLACE ") }}; - stmt_query.append(command[thd->lex->create_view->mode]); + + if (thd->lex->create_info.or_replace()) + stmt_query.append(command[1]); + else + stmt_query.append(command[0]); append_definer(thd, &stmt_query, &definer_user, &definer_host); From a7dd7c899356b2d3a7f79e6ebba5d854ed63ae9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 3 Sep 2020 14:49:11 +0300 Subject: [PATCH 33/40] MDEV-23651 InnoDB: Failing assertion: !space->referenced() commit de942c9f618b590a01a7960c171d7e50e435708f (MDEV-15983) introduced a race condition that we inadequately fixed in commit 93b69825adf5e55601ba44b4c89ec36ce62f8112 (MDEV-16169). Because fil_space_t::release() or fil_space_t::acquire() are not protected by fil_system.mutex like their predecessors, it is possible that stop_new_ops was set between the time a thread checked fil_space_t::is_stopping() and invoked fil_space_t::acquire(). In an execution trace, this happened in fil_system_t::keyrotate_next(), causing an assertion failure in fil_delete_tablespace() in the other thread that seeked to stop new operations. We fix this bug by merging the flag fil_space_t::stop_new_ops and the reference count fil_space_t::n_pending_ops into a single word that is only being accessed by atomic memory operations. fil_space_t::set_stopping(): Accessor for changing the state of the former stop_new_ops flag. fil_space_t::acquire(): Return whether the acquisition succeeded. It would fail between set_stopping(true) and set_stopping(false). --- storage/innobase/fil/fil0crypt.cc | 39 ++++---- storage/innobase/fil/fil0fil.cc | 51 +++------- storage/innobase/handler/i_s.cc | 6 +- storage/innobase/include/fil0fil.h | 144 ++++++++++++++++++----------- storage/innobase/trx/trx0purge.cc | 6 +- 5 files changed, 128 insertions(+), 118 deletions(-) diff --git a/storage/innobase/fil/fil0crypt.cc b/storage/innobase/fil/fil0crypt.cc index 4f80b7e409f..c9663837fa6 100644 --- a/storage/innobase/fil/fil0crypt.cc +++ b/storage/innobase/fil/fil0crypt.cc @@ -1418,12 +1418,15 @@ inline fil_space_t *fil_system_t::keyrotate_next(fil_space_t *space, } } - if (it == end) - return NULL; + while (it != end) + { + space= &*it; + if (space->acquire()) + return space; + while (++it != end && (!UT_LIST_GET_LEN(it->chain) || it->is_stopping())); + } - space= &*it; - space->acquire(); - return space; + return NULL; } /** Return the next tablespace. @@ -1445,12 +1448,14 @@ static fil_space_t *fil_space_next(fil_space_t *space, bool recheck, space= UT_LIST_GET_FIRST(fil_system.space_list); /* We can trust that space is not NULL because at least the system tablespace is always present and loaded first. */ - space->acquire(); + if (!space->acquire()) + goto next; } else { /* Move on to the next fil_space_t */ space->release(); +next: space= UT_LIST_GET_NEXT(space_list, space); /* Skip abnormal tablespaces or those that are being created by @@ -1460,8 +1465,8 @@ static fil_space_t *fil_space_next(fil_space_t *space, bool recheck, space->is_stopping() || space->purpose != FIL_TYPE_TABLESPACE)) space= UT_LIST_GET_NEXT(space_list, space); - if (space) - space->acquire(); + if (space && !space->acquire()) + goto next; } mutex_exit(&fil_system.mutex); @@ -2330,31 +2335,27 @@ static void fil_crypt_rotation_list_fill() space = UT_LIST_GET_NEXT(space_list, space)) { if (space->purpose != FIL_TYPE_TABLESPACE || space->is_in_rotation_list - || space->is_stopping() - || UT_LIST_GET_LEN(space->chain) == 0) { + || UT_LIST_GET_LEN(space->chain) == 0 + || !space->acquire()) { continue; } /* Ensure that crypt_data has been initialized. */ if (!space->size) { - /* Protect the tablespace while we may - release fil_system.mutex. */ - ut_d(space->acquire()); ut_d(const fil_space_t* s=) fil_system.read_page0(space->id); ut_ad(!s || s == space); - ut_d(space->release()); if (!space->size) { /* Page 0 was not loaded. Skip this tablespace. */ - continue; + goto next; } } /* Skip ENCRYPTION!=DEFAULT tablespaces. */ if (space->crypt_data && !space->crypt_data->is_default_encryption()) { - continue; + goto next; } if (srv_encrypt_tables) { @@ -2362,19 +2363,21 @@ static void fil_crypt_rotation_list_fill() innodb_encrypt_tables!=OFF */ if (space->crypt_data && space->crypt_data->min_key_version) { - continue; + goto next; } } else { /* Skip unencrypted tablespaces if innodb_encrypt_tables=OFF */ if (!space->crypt_data || !space->crypt_data->min_key_version) { - continue; + goto next; } } fil_system.rotation_list.push_back(*space); space->is_in_rotation_list = true; +next: + space->release(); } } diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index 73208d4dddc..f354f7905bb 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -763,8 +763,7 @@ fil_try_to_close_file_in_LRU( static void fil_flush_low(fil_space_t* space, bool metadata = false) { ut_ad(mutex_own(&fil_system.mutex)); - ut_ad(space); - ut_ad(!space->stop_new_ops); + ut_ad(!space->is_stopping()); if (fil_buffering_disabled(space)) { @@ -1934,10 +1933,8 @@ fil_space_acquire_low(ulint id, bool silent) ib::warn() << "Trying to access missing" " tablespace " << id; } - } else if (space->is_stopping()) { + } else if (!space->acquire()) { space = NULL; - } else { - space->acquire(); } mutex_exit(&fil_system.mutex); @@ -2254,14 +2251,14 @@ fil_check_pending_ops(const fil_space_t* space, ulint count) { ut_ad(mutex_own(&fil_system.mutex)); - if (space == NULL) { + if (!space) { return 0; } - if (ulint n_pending_ops = my_atomic_loadlint(&space->n_pending_ops)) { + if (uint32_t n_pending_ops = space->referenced()) { - /* Give a warning every 10 second, starting after 1 second */ - if ((count % 500) == 50) { + /* Give a warning every 10 second, starting after 1 second */ + if ((count % 500) == 50) { ib::warn() << "Trying to close/delete/truncate" " tablespace '" << space->name << "' but there are " << n_pending_ops @@ -2347,14 +2344,13 @@ fil_check_pending_operations( fil_space_t* sp = fil_space_get_by_id(id); if (sp) { - sp->stop_new_ops = true; - if (sp->crypt_data) { - sp->acquire(); + if (sp->crypt_data && sp->acquire()) { mutex_exit(&fil_system.mutex); fil_space_crypt_close_tablespace(sp); mutex_enter(&fil_system.mutex); sp->release(); } + sp->set_stopping(true); } /* Check for pending operations. */ @@ -2875,7 +2871,7 @@ fil_rename_tablespace( multiple datafiles per tablespace. */ ut_a(UT_LIST_GET_LEN(space->chain) == 1); node = UT_LIST_GET_FIRST(space->chain); - space->n_pending_ops++; + ut_a(space->acquire()); mutex_exit(&fil_system.mutex); @@ -2897,8 +2893,7 @@ fil_rename_tablespace( /* log_sys.mutex is above fil_system.mutex in the latching order */ ut_ad(log_mutex_own()); mutex_enter(&fil_system.mutex); - ut_ad(space->n_pending_ops); - space->n_pending_ops--; + space->release(); ut_ad(space->name == old_space_name); ut_ad(node->name == old_file_name); bool success; @@ -4204,7 +4199,7 @@ fil_io( if (space == NULL || (req_type.is_read() && !sync - && space->stop_new_ops + && space->is_stopping() && !space->is_being_truncated)) { mutex_exit(&fil_system.mutex); @@ -4844,7 +4839,7 @@ fil_space_validate_for_mtr_commit( fil_space_acquire() before mtr_start() and fil_space_t::release() after mtr_commit(). This is why n_pending_ops should not be zero if stop_new_ops is set. */ - ut_ad(!space->stop_new_ops + ut_ad(!space->is_stopping() || space->is_being_truncated /* fil_truncate_prepare() */ || space->referenced()); } @@ -5070,7 +5065,7 @@ truncate_t::truncate( err = DB_ERROR; } - space->stop_new_ops = false; + space->set_stopping(false); /* If we opened the file in this function, close it. */ if (!already_open) { @@ -5171,26 +5166,6 @@ fil_space_get_block_size(const fil_space_t* space, unsigned offset) return block_size; } -/*******************************************************************//** -Returns the table space by a given id, NULL if not found. */ -fil_space_t* -fil_space_found_by_id( -/*==================*/ - ulint id) /*!< in: space id */ -{ - fil_space_t* space = NULL; - mutex_enter(&fil_system.mutex); - space = fil_space_get_by_id(id); - - /* Not found if space is being deleted */ - if (space && space->stop_new_ops) { - space = NULL; - } - - mutex_exit(&fil_system.mutex); - return space; -} - /** Get should we punch hole to tablespace. @param[in] node File node diff --git a/storage/innobase/handler/i_s.cc b/storage/innobase/handler/i_s.cc index 9da81f96e88..d3e951ce5a0 100644 --- a/storage/innobase/handler/i_s.cc +++ b/storage/innobase/handler/i_s.cc @@ -8579,8 +8579,7 @@ i_s_tablespaces_encryption_fill_table( for (fil_space_t* space = UT_LIST_GET_FIRST(fil_system.space_list); space; space = UT_LIST_GET_NEXT(space_list, space)) { if (space->purpose == FIL_TYPE_TABLESPACE - && !space->is_stopping()) { - space->acquire(); + && space->acquire()) { mutex_exit(&fil_system.mutex); if (int err = i_s_dict_fill_tablespaces_encryption( thd, space, tables->table)) { @@ -8842,8 +8841,7 @@ i_s_tablespaces_scrubbing_fill_table( for (fil_space_t* space = UT_LIST_GET_FIRST(fil_system.space_list); space; space = UT_LIST_GET_NEXT(space_list, space)) { if (space->purpose == FIL_TYPE_TABLESPACE - && !space->is_stopping()) { - space->acquire(); + && space->acquire()) { mutex_exit(&fil_system.mutex); if (int err = i_s_dict_fill_tablespaces_scrubbing( thd, space, tables->table)) { diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index 0a1d78230c7..898caf1a813 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -93,18 +93,6 @@ struct fil_space_t : ilist_node, /** Log sequence number of the latest MLOG_INDEX_LOAD record that was found while parsing the redo log */ lsn_t enable_lsn; - bool stop_new_ops; - /*!< we set this true when we start - deleting a single-table tablespace. - When this is set following new ops - are not allowed: - * read IO request - * ibuf merge - * file flush - Note that we can still possibly have - new write operations because we don't - check this flag when doing flush - batches. */ /** whether undo tablespace truncation is in progress */ bool is_being_truncated; #ifdef UNIV_DEBUG @@ -142,14 +130,24 @@ struct fil_space_t : ilist_node, ulint n_pending_flushes; /*!< this is positive when flushing the tablespace to disk; dropping of the tablespace is forbidden if this is positive */ - /** Number of pending buffer pool operations accessing the tablespace - without holding a table lock or dict_operation_lock S-latch - that would prevent the table (and tablespace) from being - dropped. An example is change buffer merge. - The tablespace cannot be dropped while this is nonzero, - or while fil_node_t::n_pending is nonzero. - Protected by fil_system.mutex and my_atomic_loadlint() and friends. */ - ulint n_pending_ops; +private: + /** Number of pending buffer pool operations accessing the + tablespace without holding a table lock or dict_operation_lock + S-latch that would prevent the table (and tablespace) from being + dropped. An example is change buffer merge. + + The tablespace cannot be dropped while this is nonzero, or while + fil_node_t::n_pending is nonzero. + + The most significant bit contains the STOP_NEW_OPS flag. + + Protected by my_atomic. */ + uint32_t n_pending_ops; + + /** Flag in n_pending_ops that indicates that the tablespace is being + deleted, and no further operations should be performed */ + static const uint32_t STOP_NEW_OPS= 1U << 31; +public: /** Number of pending block read or write operations (when a write is imminent or a read has recently completed). The tablespace object cannot be freed while this is nonzero, @@ -183,9 +181,6 @@ struct fil_space_t : ilist_node, ulint magic_n;/*!< FIL_SPACE_MAGIC_N */ - /** @return whether the tablespace is about to be dropped */ - bool is_stopping() const { return stop_new_ops; } - /** Clamp a page number for batched I/O, such as read-ahead. @param offset page number limit @return offset clamped to the tablespace size */ @@ -267,39 +262,78 @@ struct fil_space_t : ilist_node, /** Close each file. Only invoked on fil_system.temp_space. */ void close(); - /** Acquire a tablespace reference. */ - void acquire() { my_atomic_addlint(&n_pending_ops, 1); } - /** Release a tablespace reference. - @return whether this was the last reference */ - bool release() - { - ulint n = my_atomic_addlint(&n_pending_ops, ulint(-1)); - ut_ad(n); - return n == 1; - } - /** @return whether references are being held */ - bool referenced() { return my_atomic_loadlint(&n_pending_ops); } - /** @return whether references are being held */ - bool referenced() const - { - return const_cast(this)->referenced(); - } + /** @return whether the tablespace is about to be dropped or is referenced */ + uint32_t is_stopping_or_referenced() + { + return my_atomic_load32(&n_pending_ops); + } - /** Acquire a tablespace reference for I/O. */ - void acquire_for_io() { my_atomic_addlint(&n_pending_ios, 1); } - /** Release a tablespace reference for I/O. */ - void release_for_io() - { - ut_ad(pending_io()); - my_atomic_addlint(&n_pending_ios, ulint(-1)); - } - /** @return whether I/O is pending */ - bool pending_io() { return my_atomic_loadlint(&n_pending_ios); } - /** @return whether I/O is pending */ - bool pending_io() const - { - return const_cast(this)->pending_io(); - } + /** @return whether the tablespace is about to be dropped or is referenced */ + uint32_t is_stopping_or_referenced() const + { + return const_cast(this)->is_stopping_or_referenced(); + } + + /** @return whether the tablespace is about to be dropped */ + bool is_stopping() const + { + return is_stopping_or_referenced() & STOP_NEW_OPS; + } + + /** @return number of references being held */ + uint32_t referenced() const + { + return is_stopping_or_referenced() & ~STOP_NEW_OPS; + } + + /** Note that operations on the tablespace must stop or can resume */ + void set_stopping(bool stopping) + { + /* Note: starting with 10.4 this should be std::atomic::fetch_xor() */ + uint32_t n= stopping ? 0 : STOP_NEW_OPS; + while (!my_atomic_cas32_strong_explicit(&n_pending_ops, &n, + n ^ STOP_NEW_OPS, + MY_MEMORY_ORDER_ACQUIRE, + MY_MEMORY_ORDER_RELAXED)) + ut_ad(!(n & STOP_NEW_OPS) == stopping); + } + + MY_ATTRIBUTE((warn_unused_result)) + /** @return whether a tablespace reference was successfully acquired */ + bool acquire() + { + uint32_t n= 0; + while (!my_atomic_cas32_strong_explicit(&n_pending_ops, &n, n + 1, + MY_MEMORY_ORDER_ACQUIRE, + MY_MEMORY_ORDER_RELAXED)) + if (UNIV_UNLIKELY(n & STOP_NEW_OPS)) + return false; + return true; + } + /** Release a tablespace reference. + @return whether this was the last reference */ + bool release() + { + uint32_t n= my_atomic_add32(&n_pending_ops, uint32_t(-1)); + ut_ad(n & ~STOP_NEW_OPS); + return (n & ~STOP_NEW_OPS) == 1; + } + + /** Acquire a tablespace reference for I/O. */ + void acquire_for_io() { my_atomic_addlint(&n_pending_ios, 1); } + /** Release a tablespace reference for I/O. */ + void release_for_io() + { + ut_d(ulint n=) my_atomic_addlint(&n_pending_ios, ulint(-1)); + ut_ad(n); + } + /** @return whether I/O is pending */ + bool pending_io() { return my_atomic_loadlint(&n_pending_ios); } + /** @return whether I/O is pending */ + bool pending_io() const + { + return const_cast(this)->pending_io(); + } }; /** Value of fil_space_t::magic_n */ diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc index 279ba21d4db..02a524d6850 100644 --- a/storage/innobase/trx/trx0purge.cc +++ b/storage/innobase/trx/trx0purge.cc @@ -1030,13 +1030,13 @@ not_found: /* This is only executed by the srv_purge_coordinator_thread. */ export_vars.innodb_undo_truncations++; - /* TODO: PUNCH_HOLE the garbage (with write-ahead logging) */ + /* In MDEV-8319 (10.5) we will PUNCH_HOLE the garbage + (with write-ahead logging). */ mutex_enter(&fil_system.mutex); - ut_ad(space->stop_new_ops); ut_ad(space->is_being_truncated); - space->stop_new_ops = false; space->is_being_truncated = false; + space->set_stopping(false); mutex_exit(&fil_system.mutex); if (purge_sys.rseg != NULL From b795adcff72a84421576748646e2446e3ef24202 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Thu, 3 Sep 2020 15:37:04 +0300 Subject: [PATCH 34/40] Make rowid_filter_innodb test stable It was failing on mac-1012-bintar. --- mysql-test/main/rowid_filter.result | 6 ++++++ mysql-test/main/rowid_filter.test | 1 + mysql-test/main/rowid_filter_innodb.result | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/mysql-test/main/rowid_filter.result b/mysql-test/main/rowid_filter.result index a5223666fb4..b0cc5496a4d 100644 --- a/mysql-test/main/rowid_filter.result +++ b/mysql-test/main/rowid_filter.result @@ -2139,6 +2139,12 @@ A.a+1000*B.a + 10000, 'filler-data-filler-data' from t11 A, t10 B; +analyze table t1 persistent for all; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze Warning Engine-independent statistics are not collected for column 'el_index' +test.t1 analyze Warning Engine-independent statistics are not collected for column 'filler' +test.t1 analyze status Table is already up to date # This must not use rowid_filter with key=el_index|el_index_60: explain select * from t1 diff --git a/mysql-test/main/rowid_filter.test b/mysql-test/main/rowid_filter.test index c47710e65b8..ed1227d3770 100644 --- a/mysql-test/main/rowid_filter.test +++ b/mysql-test/main/rowid_filter.test @@ -377,6 +377,7 @@ select 'filler-data-filler-data' from t11 A, t10 B; +analyze table t1 persistent for all; --echo # This must not use rowid_filter with key=el_index|el_index_60: explain diff --git a/mysql-test/main/rowid_filter_innodb.result b/mysql-test/main/rowid_filter_innodb.result index 05d97b746a7..b2971cc2aa3 100644 --- a/mysql-test/main/rowid_filter_innodb.result +++ b/mysql-test/main/rowid_filter_innodb.result @@ -2068,6 +2068,12 @@ A.a+1000*B.a + 10000, 'filler-data-filler-data' from t11 A, t10 B; +analyze table t1 persistent for all; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze Warning Engine-independent statistics are not collected for column 'el_index' +test.t1 analyze Warning Engine-independent statistics are not collected for column 'filler' +test.t1 analyze status OK # This must not use rowid_filter with key=el_index|el_index_60: explain select * from t1 From d63fcbc2f07bb51ea4a5424361deef319da9bd39 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Thu, 3 Sep 2020 15:26:55 +0000 Subject: [PATCH 35/40] MDEV-23661: RocksDB produces "missing initializer for member" warnings Add -Wno-missing-field-initializers for MyRocks and gcc version below 5.0 --- storage/rocksdb/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/storage/rocksdb/CMakeLists.txt b/storage/rocksdb/CMakeLists.txt index 506dead7a6e..a82794e9f07 100644 --- a/storage/rocksdb/CMakeLists.txt +++ b/storage/rocksdb/CMakeLists.txt @@ -54,6 +54,9 @@ IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU") SKIP_ROCKSDB_PLUGIN("${OLD_COMPILER_MSG}") ENDIF() SET(CXX11_FLAGS "-std=c++11") + IF (GCC_VERSION VERSION_LESS 5.0) + SET(CXX11_FLAGS "-Wno-missing-field-initializers") + ENDIF() ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang") IF ((CMAKE_CXX_COMPILER_VERSION AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.3) OR (CLANG_VERSION_STRING AND CLANG_VERSION_STRING VERSION_LESS 3.3)) From 24f510bba4f0c3236aa5a8c96c209eb71317f4fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 4 Sep 2020 10:31:41 +0300 Subject: [PATCH 36/40] MDEV-23633 MY_RELAX_CPU performs unnecessary compare-and-swap on ARM This follows up MDEV-14374, which was filed against MariaDB Server 10.3. Back then, on a 48-core Qualcomm Centriq 2400, the performance of delay loops for spinloops was tested both with and without the dummy compare-and-swap operation, and it was decided to keep the dummy operation. On target architectures where nothing special is available (other than x86 (IA-32, AMD64) or POWER), we perform a dummy compare-and-swap operation. This is contrary to the idea of the x86 PAUSE instruction and the __ppc_get_timebase(), which aim to keep the memory bus idle for a while, to allow other cores to better execute code while a spinloop is waiting for something to be changed. On MariaDB Server 10.4 and another implementation of the ARMv8 ISA, omitting the dummy compare-and-swap improved performance by up to 12%. So, let us avoid the dummy compare-and-swap on ARM. For now, we are retaining the dummy compare-and-swap on other ISAs (such as SPARC, MIPS, S390x, RISC-V) because we do not have any performance data for them. --- include/my_cpu.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/my_cpu.h b/include/my_cpu.h index b7d7008a8e3..0b51d3ef90f 100644 --- a/include/my_cpu.h +++ b/include/my_cpu.h @@ -53,6 +53,7 @@ #ifdef _WIN32 #elif defined HAVE_PAUSE_INSTRUCTION #elif defined(_ARCH_PWR8) +#elif defined __GNUC__ && (defined __arm__ || defined __aarch64__) #else # include "my_atomic.h" #endif @@ -80,6 +81,9 @@ static inline void MY_RELAX_CPU(void) #endif #elif defined(_ARCH_PWR8) __ppc_get_timebase(); +#elif defined __GNUC__ && (defined __arm__ || defined __aarch64__) + /* Mainly, prevent the compiler from optimizing away delay loops */ + __asm__ __volatile__ ("":::"memory") #else int32 var, oldval = 0; my_atomic_cas32_strong_explicit(&var, &oldval, 1, MY_MEMORY_ORDER_RELAXED, From b0c194cab4ba0d8618080756d3af592a6d84fe7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 4 Sep 2020 11:40:17 +0300 Subject: [PATCH 37/40] MDEV-23633 fixup: Add missing semicolon --- include/my_cpu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/my_cpu.h b/include/my_cpu.h index 0b51d3ef90f..57f2bf814d4 100644 --- a/include/my_cpu.h +++ b/include/my_cpu.h @@ -83,7 +83,7 @@ static inline void MY_RELAX_CPU(void) __ppc_get_timebase(); #elif defined __GNUC__ && (defined __arm__ || defined __aarch64__) /* Mainly, prevent the compiler from optimizing away delay loops */ - __asm__ __volatile__ ("":::"memory") + __asm__ __volatile__ ("":::"memory"); #else int32 var, oldval = 0; my_atomic_cas32_strong_explicit(&var, &oldval, 1, MY_MEMORY_ORDER_RELAXED, From 8c2909a2a4629f542a52b12cc41bcbf6c36fc588 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 4 Sep 2020 09:12:27 +0000 Subject: [PATCH 38/40] Fix a typo in the previous cset --- storage/rocksdb/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/rocksdb/CMakeLists.txt b/storage/rocksdb/CMakeLists.txt index a82794e9f07..f9384848243 100644 --- a/storage/rocksdb/CMakeLists.txt +++ b/storage/rocksdb/CMakeLists.txt @@ -55,7 +55,7 @@ IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU") ENDIF() SET(CXX11_FLAGS "-std=c++11") IF (GCC_VERSION VERSION_LESS 5.0) - SET(CXX11_FLAGS "-Wno-missing-field-initializers") + SET(CXX11_FLAGS "-std=c++11 -Wno-missing-field-initializers") ENDIF() ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang") IF ((CMAKE_CXX_COMPILER_VERSION AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.3) OR From c029d4562322d8c792f96717e0fb7f18d2379b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 4 Sep 2020 12:07:46 +0300 Subject: [PATCH 39/40] MDEV-23600 follow-up: uninitialized rec_field_is_prefix build_template_field(): Initialize templ->rec_field_is_prefix also for indexes on virtual columns. This was caught on 10.5 by MemorySanitizer as use-of-uninitialized-value in row_search_with_covering_prefix() when running the test main.fast_prefix_index_fetch_innodb. --- storage/innobase/handler/ha_innodb.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index e171b6a99b3..2493a11b554 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -7455,6 +7455,8 @@ build_template_field( #ifdef HAVE_valgrind_or_MSAN MEM_UNDEFINED(templ, sizeof *templ); #endif /* HAVE_valgrind_or_MSAN */ + templ->rec_field_is_prefix = FALSE; + templ->rec_prefix_field_no = ULINT_UNDEFINED; templ->is_virtual = !field->stored_in_db(); if (!templ->is_virtual) { @@ -7516,8 +7518,6 @@ build_template_field( << " query " << innobase_get_stmt_unsafe(current_thd, &size); } - templ->rec_field_is_prefix = FALSE; - templ->rec_prefix_field_no = ULINT_UNDEFINED; if (dict_index_is_clust(index)) { templ->rec_field_no = templ->clust_rec_field_no; @@ -7535,7 +7535,6 @@ build_template_field( DBUG_ASSERT(!ha_innobase::omits_virtual_cols(*table->s)); col = &dict_table_get_nth_v_col(index->table, v_no)->m_col; templ->clust_rec_field_no = v_no; - templ->rec_prefix_field_no = ULINT_UNDEFINED; if (dict_index_is_clust(index)) { templ->rec_field_no = templ->clust_rec_field_no; From 1a3ce7e77c61c4e8e95825dde5d706de95ffb0e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 4 Sep 2020 12:17:35 +0300 Subject: [PATCH 40/40] MDEV-23651: Fix the Windows build In the Microsoft environment, my_atomic requires int32. --- storage/innobase/include/fil0fil.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index 898caf1a813..6a184fe6f94 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -142,11 +142,11 @@ private: The most significant bit contains the STOP_NEW_OPS flag. Protected by my_atomic. */ - uint32_t n_pending_ops; + int32 n_pending_ops; /** Flag in n_pending_ops that indicates that the tablespace is being deleted, and no further operations should be performed */ - static const uint32_t STOP_NEW_OPS= 1U << 31; + static const int32 STOP_NEW_OPS= 1 << 31; public: /** Number of pending block read or write operations (when a write is imminent or a read has recently completed). @@ -263,13 +263,13 @@ public: void close(); /** @return whether the tablespace is about to be dropped or is referenced */ - uint32_t is_stopping_or_referenced() + int32 is_stopping_or_referenced() { return my_atomic_load32(&n_pending_ops); } /** @return whether the tablespace is about to be dropped or is referenced */ - uint32_t is_stopping_or_referenced() const + int32 is_stopping_or_referenced() const { return const_cast(this)->is_stopping_or_referenced(); } @@ -281,7 +281,7 @@ public: } /** @return number of references being held */ - uint32_t referenced() const + int32 referenced() const { return is_stopping_or_referenced() & ~STOP_NEW_OPS; } @@ -290,7 +290,7 @@ public: void set_stopping(bool stopping) { /* Note: starting with 10.4 this should be std::atomic::fetch_xor() */ - uint32_t n= stopping ? 0 : STOP_NEW_OPS; + int32 n= stopping ? 0 : STOP_NEW_OPS; while (!my_atomic_cas32_strong_explicit(&n_pending_ops, &n, n ^ STOP_NEW_OPS, MY_MEMORY_ORDER_ACQUIRE, @@ -302,7 +302,7 @@ public: /** @return whether a tablespace reference was successfully acquired */ bool acquire() { - uint32_t n= 0; + int32 n= 0; while (!my_atomic_cas32_strong_explicit(&n_pending_ops, &n, n + 1, MY_MEMORY_ORDER_ACQUIRE, MY_MEMORY_ORDER_RELAXED)) @@ -314,7 +314,7 @@ public: @return whether this was the last reference */ bool release() { - uint32_t n= my_atomic_add32(&n_pending_ops, uint32_t(-1)); + int32 n= my_atomic_add32(&n_pending_ops, -1); ut_ad(n & ~STOP_NEW_OPS); return (n & ~STOP_NEW_OPS) == 1; }