From 10cd281820cdcbef2fd9bc68c97325659f84de4a Mon Sep 17 00:00:00 2001 From: Sachin Kumar Date: Wed, 19 May 2021 15:46:57 +0100 Subject: MDEV-25444 mysql --binary-mode is not able to replay some mysqlbinlog outputs Problem:- Some binary data is inserted into the table using Jconnector. When binlog dump of the data is applied using mysql cleint it gives syntax error. Reason:- After investigating it turns out to be a issue of mysql client not able to properly handle \\\0 <0 in binary>. In all binary files where mysql client fails to insert these 2 bytes are commom (0x5c00) Solution:- I have changed mysql.cc to include for the possibility that binary string can have \\\0 in it --- client/mysql.cc | 8 ++++++-- mysql-test/main/binary_zero_insert.result | 6 ++++++ mysql-test/main/binary_zero_insert.test | 15 +++++++++++++++ mysql-test/std_data/binary_zero_insert.bin | Bin 0 -> 87 bytes 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 mysql-test/main/binary_zero_insert.result create mode 100644 mysql-test/main/binary_zero_insert.test create mode 100644 mysql-test/std_data/binary_zero_insert.bin diff --git a/client/mysql.cc b/client/mysql.cc index 2a7c4eaf3e5..8530c105820 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -2319,8 +2319,12 @@ static bool add_line(String &buffer, char *line, size_t line_length, { // Found possbile one character command like \c - if (!(inchar = (uchar) *++pos)) - break; // readline adds one '\' + inchar = (uchar) *++pos; + // In Binary mode , when in_string is not null \0 should not be treated as + // end statement. This can happen when we are in middle of binary data which + // can contain \0 and its quoted with ' '. + if (!real_binary_mode && !*in_string && !inchar) + break; // readline adds one '\' if (*in_string || inchar == 'N') // \N is short for NULL { // Don't allow commands in string *out++='\\'; diff --git a/mysql-test/main/binary_zero_insert.result b/mysql-test/main/binary_zero_insert.result new file mode 100644 index 00000000000..c46dc74f019 --- /dev/null +++ b/mysql-test/main/binary_zero_insert.result @@ -0,0 +1,6 @@ +CREATE TABLE `tb` (`id` int(11) NOT NULL AUTO_INCREMENT,`cb` longblob DEFAULT NULL, +PRIMARY KEY (`id`)) ENGINE=myisam AUTO_INCREMENT=5 DEFAULT CHARSET=latin1; +select count(*)=2 from tb; +count(*)=2 +1 +drop table tb; diff --git a/mysql-test/main/binary_zero_insert.test b/mysql-test/main/binary_zero_insert.test new file mode 100644 index 00000000000..a8769199859 --- /dev/null +++ b/mysql-test/main/binary_zero_insert.test @@ -0,0 +1,15 @@ +# +# MDEV-25444 mysql --binary-mode is not able to replay some mysqlbinlog outputs +# +# After investigating it turns out to be a issue of mysql client not able to properly +# handle \\\0 <0 in binary>. +# In this test case we will be pipelining binary_zero_insert.bin into mysql client. +# binary_zero_insert.bin contains insert stmt with \\\0 + +CREATE TABLE `tb` (`id` int(11) NOT NULL AUTO_INCREMENT,`cb` longblob DEFAULT NULL, +PRIMARY KEY (`id`)) ENGINE=myisam AUTO_INCREMENT=5 DEFAULT CHARSET=latin1; + +--exec $MYSQL --binary-mode test < $MYSQL_TEST_DIR/std_data/binary_zero_insert.bin +select count(*)=2 from tb; + +drop table tb; diff --git a/mysql-test/std_data/binary_zero_insert.bin b/mysql-test/std_data/binary_zero_insert.bin new file mode 100644 index 00000000000..8a91ae5d3e1 Binary files /dev/null and b/mysql-test/std_data/binary_zero_insert.bin differ -- cgit v1.2.1 From 1755ea4b4903489e1e53375bc1f26fa5f42a7a12 Mon Sep 17 00:00:00 2001 From: Brandon Nesterenko Date: Thu, 30 Sep 2021 15:03:44 -0600 Subject: MDEV-25444: mysql --binary-mode is not able to replay some mysqlbinlog outputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes on top of Sachin’s patch. Specifically: 1) Refined the parsing break condition to only change the parser’s behavior for parsing strings in binary mode (behavior of \0 outside of strings is unchanged). 2) Prefixed binary_zero_insert.test with ‘mysql_’ to more clearly associate the purpose of the test. 3) As the input of the test contains binary zeros (0x5c00), different text editors can visualize this sequence differently, and Github would not display it at all. Therefore, the input itself was consolidated into the test and created out of hex sequences to make it easier to understand what is happening. 4) Extended test to validate that the rows which correspond to the INSERTS with 0x5c00 have the correct binary zero data. Reviewed By: =========== Andrei Elkin --- client/mysql.cc | 13 +- mysql-test/main/binary_zero_insert.result | 6 - mysql-test/main/binary_zero_insert.test | 15 --- mysql-test/main/mysql_binary_zero_insert.result | 54 ++++++++ mysql-test/main/mysql_binary_zero_insert.test | 170 ++++++++++++++++++++++++ mysql-test/std_data/binary_zero_insert.bin | Bin 87 -> 0 bytes 6 files changed, 232 insertions(+), 26 deletions(-) delete mode 100644 mysql-test/main/binary_zero_insert.result delete mode 100644 mysql-test/main/binary_zero_insert.test create mode 100644 mysql-test/main/mysql_binary_zero_insert.result create mode 100644 mysql-test/main/mysql_binary_zero_insert.test delete mode 100644 mysql-test/std_data/binary_zero_insert.bin diff --git a/client/mysql.cc b/client/mysql.cc index 8530c105820..21060013c75 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -2319,11 +2319,14 @@ static bool add_line(String &buffer, char *line, size_t line_length, { // Found possbile one character command like \c - inchar = (uchar) *++pos; - // In Binary mode , when in_string is not null \0 should not be treated as - // end statement. This can happen when we are in middle of binary data which - // can contain \0 and its quoted with ' '. - if (!real_binary_mode && !*in_string && !inchar) + /* + The null-terminating character (ASCII '\0') marks the end of user + input. Then, by default, upon encountering a '\0' while parsing, it + should stop. However, some data naturally contains binary zeros + (e.g., zipped files). Real_binary_mode signals the parser to expect + '\0' within the data and not to end parsing if found. + */ + if (!(inchar = (uchar) *++pos) && (!real_binary_mode || !*in_string)) break; // readline adds one '\' if (*in_string || inchar == 'N') // \N is short for NULL { // Don't allow commands in string diff --git a/mysql-test/main/binary_zero_insert.result b/mysql-test/main/binary_zero_insert.result deleted file mode 100644 index c46dc74f019..00000000000 --- a/mysql-test/main/binary_zero_insert.result +++ /dev/null @@ -1,6 +0,0 @@ -CREATE TABLE `tb` (`id` int(11) NOT NULL AUTO_INCREMENT,`cb` longblob DEFAULT NULL, -PRIMARY KEY (`id`)) ENGINE=myisam AUTO_INCREMENT=5 DEFAULT CHARSET=latin1; -select count(*)=2 from tb; -count(*)=2 -1 -drop table tb; diff --git a/mysql-test/main/binary_zero_insert.test b/mysql-test/main/binary_zero_insert.test deleted file mode 100644 index a8769199859..00000000000 --- a/mysql-test/main/binary_zero_insert.test +++ /dev/null @@ -1,15 +0,0 @@ -# -# MDEV-25444 mysql --binary-mode is not able to replay some mysqlbinlog outputs -# -# After investigating it turns out to be a issue of mysql client not able to properly -# handle \\\0 <0 in binary>. -# In this test case we will be pipelining binary_zero_insert.bin into mysql client. -# binary_zero_insert.bin contains insert stmt with \\\0 - -CREATE TABLE `tb` (`id` int(11) NOT NULL AUTO_INCREMENT,`cb` longblob DEFAULT NULL, -PRIMARY KEY (`id`)) ENGINE=myisam AUTO_INCREMENT=5 DEFAULT CHARSET=latin1; - ---exec $MYSQL --binary-mode test < $MYSQL_TEST_DIR/std_data/binary_zero_insert.bin -select count(*)=2 from tb; - -drop table tb; diff --git a/mysql-test/main/mysql_binary_zero_insert.result b/mysql-test/main/mysql_binary_zero_insert.result new file mode 100644 index 00000000000..0bed7487b3e --- /dev/null +++ b/mysql-test/main/mysql_binary_zero_insert.result @@ -0,0 +1,54 @@ +# Note: This test assumes NO_BACKSLASH_ESCAPES is not set in SQL_MODE. +############################## +# Setup +############################## +# +# Saving old state +# +set @old_sql_mode= @@global.SQL_MODE; +set @@global.SQL_MODE= ""; +# +# Create table for data entry +# +CREATE TABLE tb (`id` int(11) NOT NULL AUTO_INCREMENT,`cb` longblob DEFAULT NULL, PRIMARY KEY (`id`)) AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; +RESET MASTER; +############################## +# Test Case +############################## +# +# \0 (0x5c00 in binary) should be allowed in data strings if +# --binary-mode is enabled. +# +FOUND 10 /\x5c\x00/ in binary_zero_inserts.sql +# MYSQL --binary-mode test < MYSQL_TMP_DIR/binary_zero_inserts.sql +# +# Ensure a row exists from each insert statement with a \0 +# +SELECT COUNT(*)=8 from tb; +COUNT(*)=8 +1 +# +# Ensure that the binary zero was parsed and exists in the row data +# Note: We only look for 00 because the 5c only served as an escape +# in parsing. +# +# MYSQL_DUMP test tb --hex-blob | grep INSERT > MYSQL_TMP_DIR/dump.sql +FOUND 10 /00/ in dump.sql +# +# Ensure data consistency on mysqlbinlog replay +# +FLUSH LOGS; +# MYSQL_BINLOG MYSQLD_DATADIR/binlog_file > MYSQL_TMP_DIR/binlog_zeros.sql +FOUND 10 /\x5c\x00/ in binlog_zeros.sql +# MYSQL --binary-mode test < MYSQL_TMP_DIR/binlog_zeros.sql +# Table checksum is equivalent before and after binlog replay +# +# A \0 should still be treated as end-of-query in binary mode. +# +# MYSQL --binary-mode -B test < MYSQL_TMP_DIR/binary_zero_eoq.sql +############################## +# Cleanup +############################## +SET @@global.sql_mode= @old_sql_mode; +drop table tb; +RESET MASTER; diff --git a/mysql-test/main/mysql_binary_zero_insert.test b/mysql-test/main/mysql_binary_zero_insert.test new file mode 100644 index 00000000000..b327c8a4d1e --- /dev/null +++ b/mysql-test/main/mysql_binary_zero_insert.test @@ -0,0 +1,170 @@ +# +# Purpose: +# This test ensures that the mysql client is able to properly handle the +# binary data sequence 0x5c00, i.e. the null-terminating character \0, in a +# string when --binary-mode is enabled. Specifically, this sequence is valid to +# appear anywhere within a binary data string, and it should not end the string +# or SQL command. Additionally, \0 outside of a string should still end the +# query. +# +# Methodology: +# This test initially inserts data with binary strings containing \0. To +# ensure the mysql client is able to process this data correctly, perl is used +# to create a SQL file that contains \0 in strings, and this file is used as +# input into the client. The row data is then validated by searching for binary +# zeros in mysqldump output. +# +# +# References: +# MDEV-25444: mysql --binary-mode is not able to replay some mysqlbinlog +# outputs + +--echo # Note: This test assumes NO_BACKSLASH_ESCAPES is not set in SQL_MODE. + +--source include/have_log_bin.inc + +--echo ############################## +--echo # Setup +--echo ############################## + +--echo # +--echo # Saving old state +--echo # +set @old_sql_mode= @@global.SQL_MODE; +set @@global.SQL_MODE= ""; + +--echo # +--echo # Create table for data entry +--echo # +CREATE TABLE tb (`id` int(11) NOT NULL AUTO_INCREMENT,`cb` longblob DEFAULT NULL, PRIMARY KEY (`id`)) AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; + +# Will replay binlog later and we don't want to recreate the table +RESET MASTER; + + +--echo ############################## +--echo # Test Case +--echo ############################## + +--echo # +--echo # \0 (0x5c00 in binary) should be allowed in data strings if +--echo # --binary-mode is enabled. +--echo # +--perl + my $dir= $ENV{'MYSQL_TMP_DIR'}; + open (my $FILE, '>', "$dir/binary_zero_inserts.sql") or die "open(): $!"; + + print $FILE "TRUNCATE TABLE tb;\n"; + + # INSERT INTO tb(cb) VALUES(_binary '\0'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","5c00"; + print $FILE "');\n"; + + # INSERT INTO tb(cb) VALUES(_binary '\0A'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","5c0041"; + print $FILE "');\n"; + + # INSERT INTO tb(cb) VALUES(_binary 'A\0'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","415c00"; + print $FILE "');\n"; + + # INSERT INTO tb(cb) VALUES(_binary 'A\0B'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","415c0042"; + print $FILE "');\n"; + + # INSERT INTO tb(cb) VALUES(_binary '\0A\0'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","5c00415c00"; + print $FILE "');\n"; + + # INSERT INTO tb(cb) VALUES(_binary '\\\0'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","5c5c5c00"; + print $FILE "');\n"; + + # INSERT INTO tb(cb) VALUES(_binary '\0\0'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","5c005c00"; + print $FILE "');\n"; + + # INSERT INTO tb(cb) VALUES(_binary '\\0'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","5c5c00"; + print $FILE "');\n"; + + close ($FILE); +EOF +--let SEARCH_PATTERN= \x5c\x00 +--let SEARCH_FILE= $MYSQL_TMP_DIR/binary_zero_inserts.sql +--source include/search_pattern_in_file.inc +--echo # MYSQL --binary-mode test < MYSQL_TMP_DIR/binary_zero_inserts.sql +--exec $MYSQL --binary-mode test < $MYSQL_TMP_DIR/binary_zero_inserts.sql + +--echo # +--echo # Ensure a row exists from each insert statement with a \0 +--echo # +SELECT COUNT(*)=8 from tb; + +--echo # +--echo # Ensure that the binary zero was parsed and exists in the row data +--echo # Note: We only look for 00 because the 5c only served as an escape +--echo # in parsing. +--echo # +--echo # MYSQL_DUMP test tb --hex-blob | grep INSERT > MYSQL_TMP_DIR/dump.sql +--exec $MYSQL_DUMP test tb --hex-blob | grep INSERT > $MYSQL_TMP_DIR/dump.sql +--let SEARCH_PATTERN= 00 +--let SEARCH_FILE= $MYSQL_TMP_DIR/dump.sql +--source include/search_pattern_in_file.inc + +--echo # +--echo # Ensure data consistency on mysqlbinlog replay +--echo # +--let $good_checksum= `CHECKSUM TABLE tb` +let $MYSQLD_DATADIR= `SELECT @@datadir`; +let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1); +FLUSH LOGS; +--echo # MYSQL_BINLOG MYSQLD_DATADIR/binlog_file > MYSQL_TMP_DIR/binlog_zeros.sql +--exec $MYSQL_BINLOG $MYSQLD_DATADIR/$binlog_file > $MYSQL_TMP_DIR/binlog_zeros.sql +--let SEARCH_PATTERN= \x5c\x00 +--let SEARCH_FILE= $MYSQL_TMP_DIR/binlog_zeros.sql +--source include/search_pattern_in_file.inc +--echo # MYSQL --binary-mode test < MYSQL_TMP_DIR/binlog_zeros.sql +--exec $MYSQL --binary-mode test < $MYSQL_TMP_DIR/binlog_zeros.sql +if ($good_checksum != `CHECKSUM TABLE tb`) +{ + die "Blob with binary zero data changed after binary log replay"; +} +--echo # Table checksum is equivalent before and after binlog replay + +--echo # +--echo # A \0 should still be treated as end-of-query in binary mode. +--echo # +--perl + my $dir= $ENV{'MYSQL_TMP_DIR'}; + open (my $FILE, '>', "$dir/binary_zero_eoq.sql") or die "open(): $!"; + + # INSERT INTO tb(cb) VALUES(_binary 'text')\0 + print $FILE "INSERT INTO tb(cb) VALUES (_binary 'text')"; + print $FILE pack "H*","5c00"; + + close ($FILE); +EOF +--echo # MYSQL --binary-mode -B test < MYSQL_TMP_DIR/binary_zero_eoq.sql +--exec $MYSQL --binary-mode -B test < $MYSQL_TMP_DIR/binary_zero_eoq.sql + + +--echo ############################## +--echo # Cleanup +--echo ############################## + +--remove_file $MYSQL_TMP_DIR/binary_zero_inserts.sql +--remove_file $MYSQL_TMP_DIR/binary_zero_eoq.sql +--remove_file $MYSQL_TMP_DIR/binlog_zeros.sql +--remove_file $MYSQL_TMP_DIR/dump.sql +SET @@global.sql_mode= @old_sql_mode; +drop table tb; +RESET MASTER; diff --git a/mysql-test/std_data/binary_zero_insert.bin b/mysql-test/std_data/binary_zero_insert.bin deleted file mode 100644 index 8a91ae5d3e1..00000000000 Binary files a/mysql-test/std_data/binary_zero_insert.bin and /dev/null differ -- cgit v1.2.1 From 1ce35c327ed90fa67da53a3f9d470f195d879417 Mon Sep 17 00:00:00 2001 From: Brandon Nesterenko Date: Wed, 6 Oct 2021 07:42:03 -0600 Subject: MDEV-25444: mysql --binary-mode is not able to replay some mysqlbinlog outputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note: This patch backports commits 10cd281 and 1755ea4 from 10.3. 10cd281: Problem:- Some binary data is inserted into the table using Jconnector. When binlog dump of the data is applied using mysql client it gives syntax error. Reason:- After investigating it turns out to be a issue of mysql client not able to properly handle \\0 <0 in binary>. In all binary files where mysql client fails to insert these 2 bytes are common (0x5c00) Solution:- I have changed mysql.cc to include for the possibility that binary string can have \\0 in it 1755ea4: Changes on top of Sachin’s patch. Specifically: 1) Refined the parsing break condition to only change the parser’s behavior for parsing strings in binary mode (behavior of \0 outside of strings is unchanged). 2) Prefixed binary_zero_insert.test with ‘mysql_’ to more clearly associate the purpose of the test. 3) As the input of the test contains binary zeros (0x5c00), different text editors can visualize this sequence differently, and Github would not display it at all. Therefore, the input itself was consolidated into the test and created out of hex sequences to make it easier to understand what is happening. 4) Extended test to validate that the rows which correspond to the INSERTS with 0x5c00 have the correct binary zero data. Reviewed By: ============ Andrei Elkin --- client/mysql.cc | 9 +- mysql-test/r/mysql_binary_zero_insert.result | 54 +++++++++ mysql-test/t/mysql_binary_zero_insert.test | 170 +++++++++++++++++++++++++++ 3 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 mysql-test/r/mysql_binary_zero_insert.result create mode 100644 mysql-test/t/mysql_binary_zero_insert.test diff --git a/client/mysql.cc b/client/mysql.cc index 2eb44d46512..2baac7b92b8 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -2335,7 +2335,14 @@ static bool add_line(String &buffer, char *line, ulong line_length, { // Found possbile one character command like \c - if (!(inchar = (uchar) *++pos)) + /* + The null-terminating character (ASCII '\0') marks the end of user + input. Then, by default, upon encountering a '\0' while parsing, it + should stop. However, some data naturally contains binary zeros + (e.g., zipped files). Real_binary_mode signals the parser to expect + '\0' within the data and not to end parsing if found. + */ + if (!(inchar = (uchar) *++pos) && (!real_binary_mode || !*in_string)) break; // readline adds one '\' if (*in_string || inchar == 'N') // \N is short for NULL { // Don't allow commands in string diff --git a/mysql-test/r/mysql_binary_zero_insert.result b/mysql-test/r/mysql_binary_zero_insert.result new file mode 100644 index 00000000000..0bed7487b3e --- /dev/null +++ b/mysql-test/r/mysql_binary_zero_insert.result @@ -0,0 +1,54 @@ +# Note: This test assumes NO_BACKSLASH_ESCAPES is not set in SQL_MODE. +############################## +# Setup +############################## +# +# Saving old state +# +set @old_sql_mode= @@global.SQL_MODE; +set @@global.SQL_MODE= ""; +# +# Create table for data entry +# +CREATE TABLE tb (`id` int(11) NOT NULL AUTO_INCREMENT,`cb` longblob DEFAULT NULL, PRIMARY KEY (`id`)) AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; +RESET MASTER; +############################## +# Test Case +############################## +# +# \0 (0x5c00 in binary) should be allowed in data strings if +# --binary-mode is enabled. +# +FOUND 10 /\x5c\x00/ in binary_zero_inserts.sql +# MYSQL --binary-mode test < MYSQL_TMP_DIR/binary_zero_inserts.sql +# +# Ensure a row exists from each insert statement with a \0 +# +SELECT COUNT(*)=8 from tb; +COUNT(*)=8 +1 +# +# Ensure that the binary zero was parsed and exists in the row data +# Note: We only look for 00 because the 5c only served as an escape +# in parsing. +# +# MYSQL_DUMP test tb --hex-blob | grep INSERT > MYSQL_TMP_DIR/dump.sql +FOUND 10 /00/ in dump.sql +# +# Ensure data consistency on mysqlbinlog replay +# +FLUSH LOGS; +# MYSQL_BINLOG MYSQLD_DATADIR/binlog_file > MYSQL_TMP_DIR/binlog_zeros.sql +FOUND 10 /\x5c\x00/ in binlog_zeros.sql +# MYSQL --binary-mode test < MYSQL_TMP_DIR/binlog_zeros.sql +# Table checksum is equivalent before and after binlog replay +# +# A \0 should still be treated as end-of-query in binary mode. +# +# MYSQL --binary-mode -B test < MYSQL_TMP_DIR/binary_zero_eoq.sql +############################## +# Cleanup +############################## +SET @@global.sql_mode= @old_sql_mode; +drop table tb; +RESET MASTER; diff --git a/mysql-test/t/mysql_binary_zero_insert.test b/mysql-test/t/mysql_binary_zero_insert.test new file mode 100644 index 00000000000..b327c8a4d1e --- /dev/null +++ b/mysql-test/t/mysql_binary_zero_insert.test @@ -0,0 +1,170 @@ +# +# Purpose: +# This test ensures that the mysql client is able to properly handle the +# binary data sequence 0x5c00, i.e. the null-terminating character \0, in a +# string when --binary-mode is enabled. Specifically, this sequence is valid to +# appear anywhere within a binary data string, and it should not end the string +# or SQL command. Additionally, \0 outside of a string should still end the +# query. +# +# Methodology: +# This test initially inserts data with binary strings containing \0. To +# ensure the mysql client is able to process this data correctly, perl is used +# to create a SQL file that contains \0 in strings, and this file is used as +# input into the client. The row data is then validated by searching for binary +# zeros in mysqldump output. +# +# +# References: +# MDEV-25444: mysql --binary-mode is not able to replay some mysqlbinlog +# outputs + +--echo # Note: This test assumes NO_BACKSLASH_ESCAPES is not set in SQL_MODE. + +--source include/have_log_bin.inc + +--echo ############################## +--echo # Setup +--echo ############################## + +--echo # +--echo # Saving old state +--echo # +set @old_sql_mode= @@global.SQL_MODE; +set @@global.SQL_MODE= ""; + +--echo # +--echo # Create table for data entry +--echo # +CREATE TABLE tb (`id` int(11) NOT NULL AUTO_INCREMENT,`cb` longblob DEFAULT NULL, PRIMARY KEY (`id`)) AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; + +# Will replay binlog later and we don't want to recreate the table +RESET MASTER; + + +--echo ############################## +--echo # Test Case +--echo ############################## + +--echo # +--echo # \0 (0x5c00 in binary) should be allowed in data strings if +--echo # --binary-mode is enabled. +--echo # +--perl + my $dir= $ENV{'MYSQL_TMP_DIR'}; + open (my $FILE, '>', "$dir/binary_zero_inserts.sql") or die "open(): $!"; + + print $FILE "TRUNCATE TABLE tb;\n"; + + # INSERT INTO tb(cb) VALUES(_binary '\0'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","5c00"; + print $FILE "');\n"; + + # INSERT INTO tb(cb) VALUES(_binary '\0A'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","5c0041"; + print $FILE "');\n"; + + # INSERT INTO tb(cb) VALUES(_binary 'A\0'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","415c00"; + print $FILE "');\n"; + + # INSERT INTO tb(cb) VALUES(_binary 'A\0B'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","415c0042"; + print $FILE "');\n"; + + # INSERT INTO tb(cb) VALUES(_binary '\0A\0'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","5c00415c00"; + print $FILE "');\n"; + + # INSERT INTO tb(cb) VALUES(_binary '\\\0'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","5c5c5c00"; + print $FILE "');\n"; + + # INSERT INTO tb(cb) VALUES(_binary '\0\0'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","5c005c00"; + print $FILE "');\n"; + + # INSERT INTO tb(cb) VALUES(_binary '\\0'); + print $FILE "INSERT INTO tb(cb) VALUES (_binary '"; + print $FILE pack "H*","5c5c00"; + print $FILE "');\n"; + + close ($FILE); +EOF +--let SEARCH_PATTERN= \x5c\x00 +--let SEARCH_FILE= $MYSQL_TMP_DIR/binary_zero_inserts.sql +--source include/search_pattern_in_file.inc +--echo # MYSQL --binary-mode test < MYSQL_TMP_DIR/binary_zero_inserts.sql +--exec $MYSQL --binary-mode test < $MYSQL_TMP_DIR/binary_zero_inserts.sql + +--echo # +--echo # Ensure a row exists from each insert statement with a \0 +--echo # +SELECT COUNT(*)=8 from tb; + +--echo # +--echo # Ensure that the binary zero was parsed and exists in the row data +--echo # Note: We only look for 00 because the 5c only served as an escape +--echo # in parsing. +--echo # +--echo # MYSQL_DUMP test tb --hex-blob | grep INSERT > MYSQL_TMP_DIR/dump.sql +--exec $MYSQL_DUMP test tb --hex-blob | grep INSERT > $MYSQL_TMP_DIR/dump.sql +--let SEARCH_PATTERN= 00 +--let SEARCH_FILE= $MYSQL_TMP_DIR/dump.sql +--source include/search_pattern_in_file.inc + +--echo # +--echo # Ensure data consistency on mysqlbinlog replay +--echo # +--let $good_checksum= `CHECKSUM TABLE tb` +let $MYSQLD_DATADIR= `SELECT @@datadir`; +let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1); +FLUSH LOGS; +--echo # MYSQL_BINLOG MYSQLD_DATADIR/binlog_file > MYSQL_TMP_DIR/binlog_zeros.sql +--exec $MYSQL_BINLOG $MYSQLD_DATADIR/$binlog_file > $MYSQL_TMP_DIR/binlog_zeros.sql +--let SEARCH_PATTERN= \x5c\x00 +--let SEARCH_FILE= $MYSQL_TMP_DIR/binlog_zeros.sql +--source include/search_pattern_in_file.inc +--echo # MYSQL --binary-mode test < MYSQL_TMP_DIR/binlog_zeros.sql +--exec $MYSQL --binary-mode test < $MYSQL_TMP_DIR/binlog_zeros.sql +if ($good_checksum != `CHECKSUM TABLE tb`) +{ + die "Blob with binary zero data changed after binary log replay"; +} +--echo # Table checksum is equivalent before and after binlog replay + +--echo # +--echo # A \0 should still be treated as end-of-query in binary mode. +--echo # +--perl + my $dir= $ENV{'MYSQL_TMP_DIR'}; + open (my $FILE, '>', "$dir/binary_zero_eoq.sql") or die "open(): $!"; + + # INSERT INTO tb(cb) VALUES(_binary 'text')\0 + print $FILE "INSERT INTO tb(cb) VALUES (_binary 'text')"; + print $FILE pack "H*","5c00"; + + close ($FILE); +EOF +--echo # MYSQL --binary-mode -B test < MYSQL_TMP_DIR/binary_zero_eoq.sql +--exec $MYSQL --binary-mode -B test < $MYSQL_TMP_DIR/binary_zero_eoq.sql + + +--echo ############################## +--echo # Cleanup +--echo ############################## + +--remove_file $MYSQL_TMP_DIR/binary_zero_inserts.sql +--remove_file $MYSQL_TMP_DIR/binary_zero_eoq.sql +--remove_file $MYSQL_TMP_DIR/binlog_zeros.sql +--remove_file $MYSQL_TMP_DIR/dump.sql +SET @@global.sql_mode= @old_sql_mode; +drop table tb; +RESET MASTER; -- cgit v1.2.1 From 478020171dd048e2584fd774e3cb30a9c4ae690b Mon Sep 17 00:00:00 2001 From: Brandon Nesterenko Date: Thu, 7 Oct 2021 11:28:49 -0600 Subject: MDEV-25444: mysql --binary-mode is not able to replay some mysqlbinlog outputs This is a documentation-only patch to refine the description of binary mode for the mariadb client. Reviewed By: ============ Andrei Elkin --- client/mysql.cc | 13 ++++++++----- man/mysql.1 | 9 ++++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/client/mysql.cc b/client/mysql.cc index 2baac7b92b8..cd2c37972bc 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1698,11 +1698,14 @@ static struct my_option my_long_options[] = &opt_default_auth, &opt_default_auth, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"binary-mode", 0, - "By default, ASCII '\\0' is disallowed and '\\r\\n' is translated to '\\n'. " - "This switch turns off both features, and also turns off parsing of all client" - "commands except \\C and DELIMITER, in non-interactive mode (for input " - "piped to mysql or loaded using the 'source' command). This is necessary " - "when processing output from mysqlbinlog that may contain blobs.", + "Binary mode allows certain character sequences to be processed as data " + "that would otherwise be treated with a special meaning by the parser. " + "Specifically, this switch turns off parsing of all client commands except " + "\\C and DELIMITER in non-interactive mode (i.e., when binary mode is " + "combined with either 1) piped input, 2) the --batch mysql option, or 3) " + "the 'source' command). Also, in binary mode, occurrences of '\\r\\n' and " + "ASCII '\\0' are preserved within strings, whereas by default, '\\r\\n' is " + "translated to '\\n' and '\\0' is disallowed in user input.", &opt_binary_mode, &opt_binary_mode, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; diff --git a/man/mysql.1 b/man/mysql.1 index 56e753160ce..6c05bbab311 100644 --- a/man/mysql.1 +++ b/man/mysql.1 @@ -206,7 +206,14 @@ option\&. .\" binary-mode option: mysql \fB\-\-binary\-mode\fR .sp -By default, ASCII '\e0' is disallowed and '\er\en' is translated to '\en'\&. This switch turns off both features, and also turns off parsing of all client commands except \eC and DELIMITER, in non-interactive mode (for input piped to mysql or loaded using the 'source' command)\&. This is necessary when processing output from mysqlbinlog that may contain blobs\&. +Binary mode allows certain character sequences to be processed as data that +would otherwise be treated with a special meaning by the parser\&. +Specifically, this switch turns off parsing of all client commands except \eC +and DELIMITER in non-interactive mode (i\&.e\&., when binary mode is combined +with either 1) piped input, 2) the --batch mysql option, or 3) the 'source' +command)\&. Also, in binary mode, occurrences of '\er\en' and ASCII '\e0' are +preserved within strings, whereas by default, '\er\en' is translated to '\en' +and '\e0' is disallowed in user input\&. .RE .sp .RS 4 -- cgit v1.2.1 From 26eb6664636dd969e2e9c14cf0ac5d038a0c403c Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 8 Oct 2021 19:17:06 +0300 Subject: Make Explain_node::children protected --- sql/sql_explain.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sql/sql_explain.h b/sql/sql_explain.h index 08af84b3562..852dc2df604 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -121,11 +121,13 @@ public: */ enum explain_connection_type connection_type; +protected: /* A node may have children nodes. When a node's explain structure is created, children nodes may not yet have QPFs. This is why we store ids. */ Dynamic_array children; +public: void add_child(int select_no) { children.append(select_no); -- cgit v1.2.1 From 1d71dacd519a8ea51e7c8a9207e0b146d794c8eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 11 Oct 2021 10:15:52 +0300 Subject: MDEV-24454 fixup: Fix plugins.feedback_plugin_send In commit 3690c549c6e72646ba74f6b4c83813ee4ac3aea4 this test was not adjusted. --- .../suite/plugins/r/feedback_plugin_send.result | 20 ++++++++++++++++++-- mysql-test/suite/plugins/t/feedback_plugin_send.test | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/plugins/r/feedback_plugin_send.result b/mysql-test/suite/plugins/r/feedback_plugin_send.result index 70dfbcb62dc..fec42f577ad 100644 --- a/mysql-test/suite/plugins/r/feedback_plugin_send.result +++ b/mysql-test/suite/plugins/r/feedback_plugin_send.result @@ -24,6 +24,22 @@ VARIABLE_VALUE>0 VARIABLE_NAME 1 Collation used latin1_swedish_ci 1 Collation used utf8_bin 1 Collation used utf8_general_ci +prepare stmt from "SELECT VARIABLE_VALUE>0, VARIABLE_NAME FROM INFORMATION_SCHEMA.FEEDBACK WHERE VARIABLE_NAME LIKE 'Collation used %' ORDER BY VARIABLE_NAME"; +execute stmt; +VARIABLE_VALUE>0 VARIABLE_NAME +1 Collation used binary +1 Collation used latin1_bin +1 Collation used latin1_swedish_ci +1 Collation used utf8_bin +1 Collation used utf8_general_ci +execute stmt; +VARIABLE_VALUE>0 VARIABLE_NAME +1 Collation used binary +1 Collation used latin1_bin +1 Collation used latin1_swedish_ci +1 Collation used utf8_bin +1 Collation used utf8_general_ci +deallocate prepare stmt; set global sql_mode=ONLY_FULL_GROUP_BY; -6: feedback plugin: report to 'http://mariadb.org/feedback_plugin/post' was sent -6: feedback plugin: server replied 'ok' +feedback plugin: report to 'http://mariadb.org/feedback_plugin/post' was sent +feedback plugin: server replied 'ok' diff --git a/mysql-test/suite/plugins/t/feedback_plugin_send.test b/mysql-test/suite/plugins/t/feedback_plugin_send.test index b28f9d4cb38..0ea1814ec29 100644 --- a/mysql-test/suite/plugins/t/feedback_plugin_send.test +++ b/mysql-test/suite/plugins/t/feedback_plugin_send.test @@ -38,6 +38,6 @@ perl; while ($_=) { $logg{$&}++ if /feedback plugin:.*/; } - print "$logg{$_}: $_\n" for sort keys %logg; + print "$_\n" for sort keys %logg; close LOG; EOF -- cgit v1.2.1 From 275e7d23f73f036b31b2d67f9256008c8e5561e1 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Wed, 22 Sep 2021 19:09:37 +0300 Subject: MDEV-14846 InnoDB: assertion on trx->state because of deadlock error ignored On deadlock transaction is rolled back (and trx->state is cleared) but SELECT continued the loop because evaluate_join_record() ignored the error status returned from lower join evaluation. val_int() does not return error status so it is checked by thd->is_error(). Test case was created by Thirunarayanan Balathandayuthapani --- mysql-test/suite/innodb/r/mdev-14846.result | 52 +++++++++++++++++++++ mysql-test/suite/innodb/t/mdev-14846.opt | 1 + mysql-test/suite/innodb/t/mdev-14846.test | 70 +++++++++++++++++++++++++++++ sql/sql_select.cc | 41 ++++++++++------- 4 files changed, 147 insertions(+), 17 deletions(-) create mode 100644 mysql-test/suite/innodb/r/mdev-14846.result create mode 100644 mysql-test/suite/innodb/t/mdev-14846.opt create mode 100644 mysql-test/suite/innodb/t/mdev-14846.test diff --git a/mysql-test/suite/innodb/r/mdev-14846.result b/mysql-test/suite/innodb/r/mdev-14846.result new file mode 100644 index 00000000000..219bd718feb --- /dev/null +++ b/mysql-test/suite/innodb/r/mdev-14846.result @@ -0,0 +1,52 @@ +CREATE TABLE t1 ( +pk INT, +f1 VARCHAR(10) NOT NULL, +f2 VARCHAR(10) NULL, +f3 INT UNSIGNED NULL, +KEY (f1), +PRIMARY KEY (pk) +) ENGINE=InnoDB; +CREATE OR REPLACE ALGORITHM=MERGE VIEW v4 AS SELECT * FROM t1; +INSERT INTO t1 VALUES (1,'k','g',6),(2,'y','r',0),(3,'t','q',1),(4,'a','r',NULL),(5,'z','t',NULL); +CREATE TABLE t2 (f VARCHAR(10) NULL) ENGINE=InnoDB; +INSERT INTO t2 VALUES (NULL),('g'),('e'),('g'); +CREATE TABLE t3 ( +f1 VARCHAR(10) NOT NULL, +f2 VARCHAR(10) NULL, +f3 INT UNSIGNED NULL +) ENGINE=InnoDB; +INSERT INTO t3 VALUES ('k','n',9),('y','b',8),('m','w',6); +CREATE TABLE t4 (f INT NULL) ENGINE=InnoDB; +INSERT INTO t4 VALUES (8),(9); +UPDATE t1 SET t1.pk = -109 WHERE t1.f1 IN ( SELECT 'a' FROM t4 WHERE f >= 1 ); +SET DEBUG_SYNC='now SIGNAL con1_dml'; +connect con1,localhost,root,,test; +SET DEBUG_SYNC='now WAIT_FOR con1_dml'; +begin; +SELECT * FROM t1 for update; +pk f1 f2 f3 +-109 a r NULL +1 k g 6 +2 y r 0 +3 t q 1 +5 z t NULL +SET DEBUG_SYNC='now SIGNAL default_dml'; +connection default; +SET DEBUG_SYNC='now WAIT_FOR default_dml'; +UPDATE t3 AS alias1 LEFT JOIN t3 AS alias2 ON ( alias1.f1 <> alias1.f2 ) SET alias1.f3 = 59 WHERE ( EXISTS ( SELECT t1.f3 FROM t1 WHERE t1.f1 = alias1.f1 ) ) OR alias2.f1 = 'h'; +connect con2,localhost,root,,test; +set debug_sync='now WAIT_FOR default_dml'; +SET DEBUG_SYNC='now SIGNAL con1_dml2'; +disconnect con2; +connection con1; +SET DEBUG_SYNC='now WAIT_FOR con1_dml2'; +UPDATE v4, t1 SET t1.pk = 76 WHERE t1.f2 IN ( SELECT t2.f FROM t2 INNER JOIN t3 ); +connection default; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +connection con1; +COMMIT; +disconnect con1; +connection default; +DROP VIEW v4; +DROP TABLE t1, t2, t3, t4; +set debug_sync= reset; diff --git a/mysql-test/suite/innodb/t/mdev-14846.opt b/mysql-test/suite/innodb/t/mdev-14846.opt new file mode 100644 index 00000000000..c8fe0561390 --- /dev/null +++ b/mysql-test/suite/innodb/t/mdev-14846.opt @@ -0,0 +1 @@ +--loose-innodb_lock_waits diff --git a/mysql-test/suite/innodb/t/mdev-14846.test b/mysql-test/suite/innodb/t/mdev-14846.test new file mode 100644 index 00000000000..adcefecd52f --- /dev/null +++ b/mysql-test/suite/innodb/t/mdev-14846.test @@ -0,0 +1,70 @@ +--source include/have_innodb.inc +--source include/count_sessions.inc +--source include/have_debug_sync.inc + +CREATE TABLE t1 ( + pk INT, + f1 VARCHAR(10) NOT NULL, + f2 VARCHAR(10) NULL, + f3 INT UNSIGNED NULL, + KEY (f1), + PRIMARY KEY (pk) +) ENGINE=InnoDB; + +CREATE OR REPLACE ALGORITHM=MERGE VIEW v4 AS SELECT * FROM t1; +INSERT INTO t1 VALUES (1,'k','g',6),(2,'y','r',0),(3,'t','q',1),(4,'a','r',NULL),(5,'z','t',NULL); + +CREATE TABLE t2 (f VARCHAR(10) NULL) ENGINE=InnoDB; +INSERT INTO t2 VALUES (NULL),('g'),('e'),('g'); + +CREATE TABLE t3 ( + f1 VARCHAR(10) NOT NULL, + f2 VARCHAR(10) NULL, + f3 INT UNSIGNED NULL +) ENGINE=InnoDB; + +INSERT INTO t3 VALUES ('k','n',9),('y','b',8),('m','w',6); + +CREATE TABLE t4 (f INT NULL) ENGINE=InnoDB; +INSERT INTO t4 VALUES (8),(9); +UPDATE t1 SET t1.pk = -109 WHERE t1.f1 IN ( SELECT 'a' FROM t4 WHERE f >= 1 ); +SET DEBUG_SYNC='now SIGNAL con1_dml'; + +--connect (con1,localhost,root,,test) +SET DEBUG_SYNC='now WAIT_FOR con1_dml'; +begin; +SELECT * FROM t1 for update; # Holds x lock of all records in the table t1 +SET DEBUG_SYNC='now SIGNAL default_dml'; + +--connection default +SET DEBUG_SYNC='now WAIT_FOR default_dml'; +--send UPDATE t3 AS alias1 LEFT JOIN t3 AS alias2 ON ( alias1.f1 <> alias1.f2 ) SET alias1.f3 = 59 WHERE ( EXISTS ( SELECT t1.f3 FROM t1 WHERE t1.f1 = alias1.f1 ) ) OR alias2.f1 = 'h' +# It holds the lock of all record in t3 and tries to acquire record lock for the table t1. + +--connect (con2,localhost,root,,test) +set debug_sync='now WAIT_FOR default_dml'; +let $wait_condition= +select count(*) > 0 from information_schema.innodb_lock_waits; +--source include/wait_condition.inc +SET DEBUG_SYNC='now SIGNAL con1_dml2'; +disconnect con2; + +# Cleanup +--connection con1 +SET DEBUG_SYNC='now WAIT_FOR con1_dml2'; +UPDATE v4, t1 SET t1.pk = 76 WHERE t1.f2 IN ( SELECT t2.f FROM t2 INNER JOIN t3 ); +# It holds the record lock on table t1 and tries to acquire record lock on t3. +# leads to deadlock (con1 trx is waiting for default trx and vice versa) + +--connection default +--error ER_LOCK_DEADLOCK +--reap + +connection con1; +COMMIT; +disconnect con1; + +--connection default +DROP VIEW v4; +DROP TABLE t1, t2, t3, t4; +set debug_sync= reset; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ff584e936b7..bf33623a684 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -19045,26 +19045,33 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, will be re-evaluated again. It could be fixed, but, probably, it's not worth doing now. */ - if (tab->select_cond && !tab->select_cond->val_int()) + if (tab->select_cond) { - /* The condition attached to table tab is false */ - if (tab == join_tab) - { - found= 0; - if (not_exists_opt_is_applicable) - DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS); - } - else + const longlong res= tab->select_cond->val_int(); + if (join->thd->is_error()) + DBUG_RETURN(NESTED_LOOP_ERROR); + + if (!res) { - /* - Set a return point if rejected predicate is attached - not to the last table of the current nest level. - */ - join->return_tab= tab; - if (not_exists_opt_is_applicable) - DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS); + /* The condition attached to table tab is false */ + if (tab == join_tab) + { + found= 0; + if (not_exists_opt_is_applicable) + DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS); + } else - DBUG_RETURN(NESTED_LOOP_OK); + { + /* + Set a return point if rejected predicate is attached + not to the last table of the current nest level. + */ + join->return_tab= tab; + if (not_exists_opt_is_applicable) + DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS); + else + DBUG_RETURN(NESTED_LOOP_OK); + } } } } -- cgit v1.2.1 From 89936f11e965472d65fbee1e240f3d4b7726e77f Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 28 Sep 2021 16:00:41 +0300 Subject: MDEV-18278 Misleading error message in error log upon failed table creation If error_reported is not set upper caller open_table_from_share() throws error ER_NOT_FORM_FILE itself via open_table_error(). --- mysql-test/r/default.result | 8 ++++++++ mysql-test/t/default.test | 6 ++++++ sql/table.cc | 3 +++ 3 files changed, 17 insertions(+) diff --git a/mysql-test/r/default.result b/mysql-test/r/default.result index 369734ae92a..1a35a29aa7f 100644 --- a/mysql-test/r/default.result +++ b/mysql-test/r/default.result @@ -3396,4 +3396,12 @@ CREATE OR REPLACE TABLE t1(i int); ALTER TABLE t1 ADD b CHAR(255) DEFAULT `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`; ERROR 42S22: Unknown column 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' in 'DEFAULT' DROP TABLE t1; +# +# MDEV-18278 Misleading error message in error log upon failed table creation +# +create table t1 (a int as (a)); +ERROR 01000: Expression for field `a` is referring to uninitialized field `a` +show warnings; +Level Code Message +Error 4029 Expression for field `a` is referring to uninitialized field `a` # end of 10.2 test diff --git a/mysql-test/t/default.test b/mysql-test/t/default.test index aec518d94a6..472b1fa4796 100644 --- a/mysql-test/t/default.test +++ b/mysql-test/t/default.test @@ -2109,5 +2109,11 @@ CREATE OR REPLACE TABLE t1(i int); ALTER TABLE t1 ADD b CHAR(255) DEFAULT `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`; DROP TABLE t1; +--echo # +--echo # MDEV-18278 Misleading error message in error log upon failed table creation +--echo # +--error ER_EXPRESSION_REFERS_TO_UNINIT_FIELD +create table t1 (a int as (a)); +show warnings; --echo # end of 10.2 test diff --git a/sql/table.cc b/sql/table.cc index 281b8f82abc..87b3c158a67 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1157,7 +1157,10 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table, if (check_vcol_forward_refs(field, field->vcol_info) || check_vcol_forward_refs(field, field->check_constraint) || check_vcol_forward_refs(field, field->default_value)) + { + *error_reported= true; goto end; + } } res=0; -- cgit v1.2.1 From 911c803db19de7ffd45c39ff3614abcf19c63536 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 11 Oct 2021 13:36:06 +0300 Subject: MDEV-22660 System versioning cleanups - Cleaned up Vers_parse_info::check_sys_fields(); - Renamed VERS_SYS_START_FLAG, VERS_SYS_END_FLAG to VERS_ROW_START, VERS_ROW_END. --- include/mysql_com.h | 6 ++-- sql/field.cc | 4 +-- sql/field.h | 13 ++++++-- sql/handler.cc | 86 +++++++++++++++++++++++------------------------------ sql/item_vers.cc | 2 +- sql/sql_lex.cc | 4 +-- sql/sql_show.cc | 6 ++-- sql/sql_table.cc | 6 ++-- sql/table.cc | 4 +-- 9 files changed, 64 insertions(+), 67 deletions(-) diff --git a/include/mysql_com.h b/include/mysql_com.h index b1534b1d746..234b6133676 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -193,13 +193,13 @@ enum enum_indicator_type #define FIELD_FLAGS_COLUMN_FORMAT_MASK (3U << FIELD_FLAGS_COLUMN_FORMAT) #define FIELD_IS_DROPPED (1U << 26) /* Intern: Field is being dropped */ -#define VERS_SYS_START_FLAG (1 << 27) /* autogenerated column declared with +#define VERS_ROW_START (1 << 27) /* autogenerated column declared with `generated always as row start` (see II.a SQL Standard) */ -#define VERS_SYS_END_FLAG (1 << 28) /* autogenerated column declared with +#define VERS_ROW_END (1 << 28) /* autogenerated column declared with `generated always as row end` (see II.a SQL Standard).*/ -#define VERS_SYSTEM_FIELD (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG) +#define VERS_SYSTEM_FIELD (VERS_ROW_START | VERS_ROW_END) #define VERS_UPDATE_UNVERSIONED_FLAG (1 << 29) /* column that doesn't support system versioning when table itself supports it*/ diff --git a/sql/field.cc b/sql/field.cc index cba6df52b62..ddb309c3262 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2360,7 +2360,7 @@ Field *Field::make_new_field(MEM_ROOT *root, TABLE *new_table, tmp->unireg_check= Field::NONE; tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG | ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG | - VERS_SYS_START_FLAG | VERS_SYS_END_FLAG | + VERS_ROW_START | VERS_ROW_END | VERS_UPDATE_UNVERSIONED_FLAG); tmp->reset_fields(); tmp->invisible= VISIBLE; @@ -10967,7 +10967,7 @@ Field *make_field(TABLE_SHARE *share, f_is_zerofill(pack_flag) != 0, f_is_dec(pack_flag) == 0); case MYSQL_TYPE_LONGLONG: - if (flags & (VERS_SYS_START_FLAG|VERS_SYS_END_FLAG)) + if (flags & (VERS_ROW_START|VERS_ROW_END)) { return new (mem_root) Field_vers_trx_id(ptr, field_length, null_pos, null_bit, diff --git a/sql/field.h b/sql/field.h index 825a0dae7aa..7d60e0953f8 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1499,7 +1499,7 @@ public: bool vers_sys_field() const { - return flags & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG); + return flags & (VERS_ROW_START | VERS_ROW_END); } bool vers_update_unversioned() const @@ -4469,7 +4469,7 @@ public: } bool vers_sys_field() const { - return flags & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG); + return flags & (VERS_ROW_START | VERS_ROW_END); } void create_length_to_internal_length_bit(); void create_length_to_internal_length_newdecimal(); @@ -4813,6 +4813,15 @@ public: } /* Used to make a clone of this object for ALTER/CREATE TABLE */ Create_field *clone(MEM_ROOT *mem_root) const; + + bool is_some_bigint() const + { + return type_handler() == &type_handler_longlong || + type_handler() == &type_handler_vers_trx_id; + } + + bool vers_check_timestamp(const Lex_table_name &table_name) const; + bool vers_check_bigint(const Lex_table_name &table_name) const; }; diff --git a/sql/handler.cc b/sql/handler.cc index c0a810a72bc..f46e51a34ce 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -7122,11 +7122,11 @@ bool Vers_parse_info::is_end(const char *name) const } bool Vers_parse_info::is_start(const Create_field &f) const { - return f.flags & VERS_SYS_START_FLAG; + return f.flags & VERS_ROW_START; } bool Vers_parse_info::is_end(const Create_field &f) const { - return f.flags & VERS_SYS_END_FLAG; + return f.flags & VERS_ROW_END; } static Create_field *vers_init_sys_field(THD *thd, const char *field_name, int flags, bool integer) @@ -7186,8 +7186,8 @@ bool Vers_parse_info::fix_implicit(THD *thd, Alter_info *alter_info) system_time= start_end_t(default_start, default_end); as_row= system_time; - if (vers_create_sys_field(thd, default_start, alter_info, VERS_SYS_START_FLAG) || - vers_create_sys_field(thd, default_end, alter_info, VERS_SYS_END_FLAG)) + if (vers_create_sys_field(thd, default_start, alter_info, VERS_ROW_START) || + vers_create_sys_field(thd, default_end, alter_info, VERS_ROW_END)) { return true; } @@ -7346,7 +7346,7 @@ bool Vers_parse_info::fix_alter_info(THD *thd, Alter_info *alter_info, return true; } my_error(ER_VERS_DUPLICATE_ROW_START_END, MYF(0), - f->flags & VERS_SYS_START_FLAG ? "START" : "END", f->field_name.str); + f->flags & VERS_ROW_START ? "START" : "END", f->field_name.str); return true; } } @@ -7451,13 +7451,13 @@ Vers_parse_info::fix_create_like(Alter_info &alter_info, HA_CREATE_INFO &create_ while ((f= it++)) { - if (f->flags & VERS_SYS_START_FLAG) + if (f->flags & VERS_ROW_START) { f_start= f; if (f_end) break; } - else if (f->flags & VERS_SYS_END_FLAG) + else if (f->flags & VERS_ROW_END) { f_end= f; if (f_start) @@ -7519,37 +7519,31 @@ bool Vers_parse_info::check_conditions(const Lex_table_name &table_name, return false; } -static bool is_versioning_timestamp(const Create_field *f) -{ - return f->type_handler() == &type_handler_timestamp2 && - f->length == MAX_DATETIME_FULL_WIDTH; -} - -static bool is_some_bigint(const Create_field *f) -{ - return f->type_handler() == &type_handler_longlong || - f->type_handler() == &type_handler_vers_trx_id; -} -static bool is_versioning_bigint(const Create_field *f) +bool Create_field::vers_check_timestamp(const Lex_table_name &table_name) const { - return is_some_bigint(f) && f->flags & UNSIGNED_FLAG && - f->length == MY_INT64_NUM_DECIMAL_DIGITS - 1; -} + if (type_handler() == &type_handler_timestamp2 && + length == MAX_DATETIME_FULL_WIDTH) + return false; -static bool require_timestamp(const Create_field *f, Lex_table_name table_name) -{ - my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), f->field_name.str, "TIMESTAMP(6)", + my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), field_name.str, "TIMESTAMP(6)", table_name.str); return true; } -static bool require_bigint(const Create_field *f, Lex_table_name table_name) + + +bool Create_field::vers_check_bigint(const Lex_table_name &table_name) const { - my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), f->field_name.str, + if (is_some_bigint() && flags & UNSIGNED_FLAG && + length == MY_INT64_NUM_DECIMAL_DIGITS - 1) + return false; + + my_error(ER_VERS_FIELD_WRONG_TYPE, MYF(0), field_name.str, "BIGINT(20) UNSIGNED", table_name.str); return true; } + bool Vers_parse_info::check_sys_fields(const Lex_table_name &table_name, const Lex_table_name &db, Alter_info *alter_info, @@ -7564,37 +7558,31 @@ bool Vers_parse_info::check_sys_fields(const Lex_table_name &table_name, List_iterator it(alter_info->create_list); while (Create_field *f= it++) { - if (!row_start && f->flags & VERS_SYS_START_FLAG) + if (!row_start && f->flags & VERS_ROW_START) row_start= f; - else if (!row_end && f->flags & VERS_SYS_END_FLAG) + else if (!row_end && f->flags & VERS_ROW_END) row_end= f; } - const bool expect_timestamp= - !can_native || !is_some_bigint(row_start) || !is_some_bigint(row_end); - - if (expect_timestamp) + if (!can_native || + !row_start->is_some_bigint() || + !row_end->is_some_bigint()) { - if (!is_versioning_timestamp(row_start)) - return require_timestamp(row_start, table_name); - - if (!is_versioning_timestamp(row_end)) - return require_timestamp(row_end, table_name); + if (row_start->vers_check_timestamp(table_name) || + row_end->vers_check_timestamp(table_name)) + return true; } else { - if (!is_versioning_bigint(row_start)) - return require_bigint(row_start, table_name); - - if (!is_versioning_bigint(row_end)) - return require_bigint(row_end, table_name); - } + if (row_start->vers_check_bigint(table_name) || + row_end->vers_check_bigint(table_name)) + return true; - if (is_versioning_bigint(row_start) && is_versioning_bigint(row_end) && - !TR_table::use_transaction_registry) - { - my_error(ER_VERS_TRT_IS_DISABLED, MYF(0)); - return true; + if (!TR_table::use_transaction_registry) + { + my_error(ER_VERS_TRT_IS_DISABLED, MYF(0)); + return true; + } } return false; diff --git a/sql/item_vers.cc b/sql/item_vers.cc index 76cda6a5ea2..6103c53fd51 100644 --- a/sql/item_vers.cc +++ b/sql/item_vers.cc @@ -30,7 +30,7 @@ bool Item_func_history::val_bool() { Item_field *f= static_cast(args[0]); DBUG_ASSERT(f->fixed); - DBUG_ASSERT(f->field->flags & VERS_SYS_END_FLAG); + DBUG_ASSERT(f->field->flags & VERS_ROW_END); return !f->field->is_max(); } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 4fc8ebb5a60..00e1d30f209 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -8322,7 +8322,7 @@ bool LEX::last_field_generated_always_as_row_start() Vers_parse_info &info= vers_get_info(); Lex_ident *p= &info.as_row.start; return last_field_generated_always_as_row_start_or_end(p, "START", - VERS_SYS_START_FLAG); + VERS_ROW_START); } @@ -8331,7 +8331,7 @@ bool LEX::last_field_generated_always_as_row_end() Vers_parse_info &info= vers_get_info(); Lex_ident *p= &info.as_row.end; return last_field_generated_always_as_row_start_or_end(p, "END", - VERS_SYS_END_FLAG); + VERS_ROW_END); } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 35e0c7364f5..5a3c27832ac 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2288,11 +2288,11 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, } else { - if (field->flags & VERS_SYS_START_FLAG) + if (field->flags & VERS_ROW_START) { packet->append(STRING_WITH_LEN(" GENERATED ALWAYS AS ROW START")); } - else if (field->flags & VERS_SYS_END_FLAG) + else if (field->flags & VERS_ROW_END) { packet->append(STRING_WITH_LEN(" GENERATED ALWAYS AS ROW END")); } @@ -6080,7 +6080,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, } else if (field->flags & VERS_SYSTEM_FIELD) { - if (field->flags & VERS_SYS_START_FLAG) + if (field->flags & VERS_ROW_START) { table->field[21]->store(STRING_WITH_LEN("ROW START"), cs); buf.set(STRING_WITH_LEN("STORED GENERATED"), cs); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 9aae74a613d..2b796403dc8 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -8209,7 +8209,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, def= new (thd->mem_root) Create_field(thd, field, field); def->invisible= INVISIBLE_SYSTEM; alter_info->flags|= ALTER_CHANGE_COLUMN; - if (field->flags & VERS_SYS_START_FLAG) + if (field->flags & VERS_ROW_START) create_info->vers_info.as_row.start= def->field_name= Vers_parse_info::default_start; else create_info->vers_info.as_row.end= def->field_name= Vers_parse_info::default_end; @@ -8250,9 +8250,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, !vers_system_invisible) { StringBuffer tmp; - if (!(dropped_sys_vers_fields & VERS_SYS_START_FLAG)) + if (!(dropped_sys_vers_fields & VERS_ROW_START)) append_drop_column(thd, &tmp, table->vers_start_field()); - if (!(dropped_sys_vers_fields & VERS_SYS_END_FLAG)) + if (!(dropped_sys_vers_fields & VERS_ROW_END)) append_drop_column(thd, &tmp, table->vers_end_field()); my_error(ER_MISSING, MYF(0), table->s->table_name.str, tmp.c_ptr()); goto err; diff --git a/sql/table.cc b/sql/table.cc index 18bb28e0863..0bef0fc29a1 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2059,9 +2059,9 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (versioned) { if (i == row_start_field) - flags|= VERS_SYS_START_FLAG; + flags|= VERS_ROW_START; else if (i == row_end_field) - flags|= VERS_SYS_END_FLAG; + flags|= VERS_ROW_END; if (flags & VERS_SYSTEM_FIELD) { -- cgit v1.2.1 From d31f953789def34f20d29b31add847a3c30a8ecc Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 11 Oct 2021 13:36:07 +0300 Subject: MDEV-22660 SIGSEGV on adding system versioning and modifying system column Second alter subcommand correctly removed VERS_ROW_END flag. We throw ER_VERS_PERIOD_COLUMNS in such case. --- mysql-test/suite/versioning/r/alter.result | 19 +++++++++++++++++++ mysql-test/suite/versioning/t/alter.test | 22 ++++++++++++++++++++++ sql/handler.cc | 6 ++++++ 3 files changed, 47 insertions(+) diff --git a/mysql-test/suite/versioning/r/alter.result b/mysql-test/suite/versioning/r/alter.result index 5092adc2b11..9af25d2f858 100644 --- a/mysql-test/suite/versioning/r/alter.result +++ b/mysql-test/suite/versioning/r/alter.result @@ -759,3 +759,22 @@ delete from t1; set system_versioning_alter_history= keep; alter ignore table t1 drop pk; drop table t1; +# +# MDEV-22660 SIGSEGV on adding system versioning and modifying system column +# +create or replace table t1 (a int); +alter table t1 +add row_start timestamp(6) as row start, +add row_end timestamp(6) as row end, +add period for system_time(row_start, row_end), +with system versioning, +modify row_end varchar(8); +ERROR HY000: PERIOD FOR SYSTEM_TIME must use columns `row_start` and `row_end` +alter table t1 +add row_start timestamp(6) as row start, +add row_end timestamp(6) as row end, +add period for system_time(row_start, row_end), +with system versioning, +modify row_start varchar(8); +ERROR HY000: PERIOD FOR SYSTEM_TIME must use columns `row_start` and `row_end` +drop table t1; diff --git a/mysql-test/suite/versioning/t/alter.test b/mysql-test/suite/versioning/t/alter.test index 3ce87817e06..0900e424bd0 100644 --- a/mysql-test/suite/versioning/t/alter.test +++ b/mysql-test/suite/versioning/t/alter.test @@ -652,3 +652,25 @@ set system_versioning_alter_history= keep; alter ignore table t1 drop pk; # cleanup drop table t1; + + +--echo # +--echo # MDEV-22660 SIGSEGV on adding system versioning and modifying system column +--echo # +create or replace table t1 (a int); +--error ER_VERS_PERIOD_COLUMNS +alter table t1 + add row_start timestamp(6) as row start, + add row_end timestamp(6) as row end, + add period for system_time(row_start, row_end), + with system versioning, + modify row_end varchar(8); +--error ER_VERS_PERIOD_COLUMNS +alter table t1 + add row_start timestamp(6) as row start, + add row_end timestamp(6) as row end, + add period for system_time(row_start, row_end), + with system versioning, + modify row_start varchar(8); +# cleanup +drop table t1; diff --git a/sql/handler.cc b/sql/handler.cc index f46e51a34ce..bbd0f3bf515 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -7564,6 +7564,12 @@ bool Vers_parse_info::check_sys_fields(const Lex_table_name &table_name, row_end= f; } + if (!row_start || !row_end) + { + my_error(ER_VERS_PERIOD_COLUMNS, MYF(0), as_row.start.str, as_row.end.str); + return true; + } + if (!can_native || !row_start->is_some_bigint() || !row_end->is_some_bigint()) -- cgit v1.2.1 From 1e70b287e702b7ff9191454d1316d9137b9be0c1 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 11 Oct 2021 13:36:07 +0300 Subject: MDEV-25891 Computed default for INVISIBLE column is ignored in INSERT There are two fill_record() functions (lines 8343 and 8618). First one is used when there are some explicit values, the second one is used for all implicit values. First one does update_default_fields(), the second one did not. Added update_default_fields() call to the implicit version of fill_record(). --- mysql-test/main/invisible_field.result | 17 ++++++++++++++++- mysql-test/main/invisible_field.test | 13 +++++++++++++ sql/sql_base.cc | 5 ++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/mysql-test/main/invisible_field.result b/mysql-test/main/invisible_field.result index ee45567d212..cd1bb77be71 100644 --- a/mysql-test/main/invisible_field.result +++ b/mysql-test/main/invisible_field.result @@ -538,7 +538,7 @@ a b insert into t2 values(1); select a,b from t2; a b -NULL 1 +12 1 drop table t1,t2; create table t1 (a int invisible, b int, c int); create table t2 (a int, b int, d int); @@ -623,3 +623,18 @@ drop table t1; create table t1 (a int, b int invisible); insert delayed into t1 values (1); drop table t1; +# +# MDEV-25891 Computed default for INVISIBLE column is ignored in INSERT +# +create table t1( +a int, +x int default (a), +y int default (a) invisible, +z int default (33) invisible); +insert into t1 values (1, default); +insert into t1 (a) values (2); +select a, x, y, z from t1; +a x y z +1 1 1 33 +2 2 2 33 +drop table t1; diff --git a/mysql-test/main/invisible_field.test b/mysql-test/main/invisible_field.test index 7a48347ec29..558ca7aa3a2 100644 --- a/mysql-test/main/invisible_field.test +++ b/mysql-test/main/invisible_field.test @@ -279,3 +279,16 @@ create table t1 (a int, b int invisible); insert delayed into t1 values (1); # cleanup drop table t1; + +--echo # +--echo # MDEV-25891 Computed default for INVISIBLE column is ignored in INSERT +--echo # +create table t1( + a int, + x int default (a), + y int default (a) invisible, + z int default (33) invisible); +insert into t1 values (1, default); +insert into t1 (a) values (2); +select a, x, y, z from t1; +drop table t1; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b65737bfb02..a1f3f6116b9 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8699,8 +8699,11 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List &values, goto err; field->set_has_explicit_value(); } - /* Update virtual fields */ thd->abort_on_warning= FALSE; + if (table->default_field && + table->update_default_fields(ignore_errors)) + goto err; + /* Update virtual fields */ if (table->vfield && table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_WRITE)) goto err; -- cgit v1.2.1 From ff77a09bda884fe6bf3917eb29b9d3a2f53f919b Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 11 Oct 2021 13:36:07 +0300 Subject: MDEV-22464 Server crash on UPDATE with nested subquery Uninitialized ref_pointer_array[] because setup_fields() got empty fields list. mysql_multi_update() for some reason does that by substituting the fields list with empty total_list for the mysql_select() call (looks like wrong merge since total_list is not used anywhere else and is always empty). The fix would be to return back the original fields list. But this fails update_use_source.test case: --error ER_BAD_FIELD_ERROR update v1 set t1c1=2 order by 1; Actually not failing the above seems to be ok. The other fix would be to keep resolve_in_select_list false (and that keeps outer context from being resolved in Item_ref::fix_fields()). This fix is more consistent with how SELECT behaves: --error ER_SUBQUERY_NO_1_ROW select a from t1 where a= (select 2 from t1 having (a = 3)); So this patch implements this fix. --- mysql-test/main/multi_update.result | 10 ++++++++++ mysql-test/main/multi_update.test | 11 +++++++++++ sql/sql_select.cc | 3 ++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/multi_update.result b/mysql-test/main/multi_update.result index 520199d562c..71eafbf7e17 100644 --- a/mysql-test/main/multi_update.result +++ b/mysql-test/main/multi_update.result @@ -1151,3 +1151,13 @@ b 1 3 drop tables t1, t2; +# +# MDEV-22464 Server crash on UPDATE with nested subquery +# +create table t1 (a int) ; +insert into t1 (a) values (1),(2),(3) ; +select a from t1 where a= (select 2 from t1 having (a = 3)); +ERROR 21000: Subquery returns more than 1 row +update t1 set a= (select 2 from t1 having (a = 3)); +ERROR 21000: Subquery returns more than 1 row +drop tables t1; diff --git a/mysql-test/main/multi_update.test b/mysql-test/main/multi_update.test index 84f06a7c165..3ee36f97fc5 100644 --- a/mysql-test/main/multi_update.test +++ b/mysql-test/main/multi_update.test @@ -1087,3 +1087,14 @@ update t1 left join t2 on a = b set b= 3 order by b; select * from t2; drop tables t1, t2; + +--echo # +--echo # MDEV-22464 Server crash on UPDATE with nested subquery +--echo # +create table t1 (a int) ; +insert into t1 (a) values (1),(2),(3) ; +--error ER_SUBQUERY_NO_1_ROW +select a from t1 where a= (select 2 from t1 having (a = 3)); +--error ER_SUBQUERY_NO_1_ROW +update t1 set a= (select 2 from t1 having (a = 3)); +drop tables t1; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 09d890161f6..e44ba4b59e1 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4268,7 +4268,8 @@ mysql_select(THD *thd, bool free_join= 1; DBUG_ENTER("mysql_select"); - select_lex->context.resolve_in_select_list= TRUE; + if (!fields.is_empty()) + select_lex->context.resolve_in_select_list= true; JOIN *join; if (select_lex->join != 0) { -- cgit v1.2.1 From 5e3e5ccbea2ab90548fc82c5d730dea2c9315a16 Mon Sep 17 00:00:00 2001 From: SergMariaDB <91733807+SergMariaDB@users.noreply.github.com> Date: Mon, 11 Oct 2021 08:26:33 -0400 Subject: Apple Silicon is a 64-bit platform (#1922) Co-authored-by: FX Coudert --- include/my_global.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/my_global.h b/include/my_global.h index 5e96400eac1..dba63e75097 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -164,7 +164,7 @@ # if defined(__i386__) || defined(__ppc__) # define SIZEOF_CHARP 4 # define SIZEOF_LONG 4 -# elif defined(__x86_64__) || defined(__ppc64__) +# elif defined(__x86_64__) || defined(__ppc64__) || defined(__aarch64__) # define SIZEOF_CHARP 8 # define SIZEOF_LONG 8 # else -- cgit v1.2.1 From 9300b6657752e566a3c72da7ca6eaa8ee052e4e1 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Sun, 10 Oct 2021 18:42:32 +0400 Subject: MDEV-24742 Server crashes in Charset::numchars / String::numchars The crash happened because Item_aes_crypt::val_str() did not set the character set of the result. --- mysql-test/r/func_str.result | 12 ++++++++++++ mysql-test/t/func_str.test | 16 ++++++++++++++++ sql/item_strfunc.cc | 2 ++ 3 files changed, 30 insertions(+) diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index 798b4f9ff09..f9346a25151 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -4835,3 +4835,15 @@ DROP TABLE t1; # # End of 10.1 tests # +# +# Start of 10.2 tests +# +# +# MDEV-24742 Server crashes in Charset::numchars / String::numchars +# +SELECT NULL IN (RIGHT(AES_ENCRYPT('foo','bar'), LAST_INSERT_ID()), 'qux'); +NULL IN (RIGHT(AES_ENCRYPT('foo','bar'), LAST_INSERT_ID()), 'qux') +NULL +# +# End of 10.2 tests +# diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index 1a59946e97e..366aacb1945 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -1932,3 +1932,19 @@ DROP TABLE t1; --echo # --echo # End of 10.1 tests --echo # + + +--echo # +--echo # Start of 10.2 tests +--echo # + +--echo # +--echo # MDEV-24742 Server crashes in Charset::numchars / String::numchars +--echo # + +SELECT NULL IN (RIGHT(AES_ENCRYPT('foo','bar'), LAST_INSERT_ID()), 'qux'); + + +--echo # +--echo # End of 10.2 tests +--echo # diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 26a23b0cb55..e5935178f10 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -338,6 +338,8 @@ String *Item_aes_crypt::val_str(String *str2) rkey, AES_KEY_LENGTH / 8, 0, 0)) { str2->length((uint) aes_length); + DBUG_ASSERT(collation.collation == &my_charset_bin); + str2->set_charset(&my_charset_bin); return str2; } } -- cgit v1.2.1 From eadd87880887e6ca73e6f292c9d856df7e56c3c0 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 8 Oct 2021 20:44:38 +0400 Subject: MDEV-23269 SIGSEGV in ft_boolean_check_syntax_string on setting ft_boolean_syntax The crash happened because my_isalnum() does not support character sets with mbminlen>1. The value of "ft_boolean_syntax" is converted to utf8 in do_string_check(). So calling my_isalnum() is combination with "default_charset_info" was wrong. Adding new parameters (size_t length, CHARSET_INFO *cs) to ft_boolean_check_syntax_string() and passing self->charset(thd) as the character set. --- include/ft_global.h | 3 ++- mysql-test/r/ctype_utf16_def.result | 5 +++++ mysql-test/r/ctype_utf32_def.result | 6 ++++++ mysql-test/t/ctype_utf16_def.test | 7 +++++++ mysql-test/t/ctype_utf32_def-master.opt | 1 + mysql-test/t/ctype_utf32_def.test | 9 +++++++++ sql/mysqld.cc | 4 +++- sql/sys_vars.cc | 4 +++- storage/myisam/ft_parser.c | 13 ++++++++++--- 9 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 mysql-test/r/ctype_utf32_def.result create mode 100644 mysql-test/t/ctype_utf32_def-master.opt create mode 100644 mysql-test/t/ctype_utf32_def.test diff --git a/include/ft_global.h b/include/ft_global.h index 725363c3aa8..9f2d52610ba 100644 --- a/include/ft_global.h +++ b/include/ft_global.h @@ -90,7 +90,8 @@ void ft_free_stopwords(void); FT_INFO *ft_init_search(uint,void *, uint, uchar *, size_t, CHARSET_INFO *, uchar *); -my_bool ft_boolean_check_syntax_string(const uchar *); +my_bool ft_boolean_check_syntax_string(const uchar *, size_t length, + CHARSET_INFO *cs); /* Internal symbols for fulltext between maria and MyISAM */ diff --git a/mysql-test/r/ctype_utf16_def.result b/mysql-test/r/ctype_utf16_def.result index 98b6f7d913d..b5827d45619 100644 --- a/mysql-test/r/ctype_utf16_def.result +++ b/mysql-test/r/ctype_utf16_def.result @@ -8,3 +8,8 @@ character_set_server utf16 SHOW VARIABLES LIKE 'ft_stopword_file'; Variable_name Value ft_stopword_file (built-in) +# +# MDEV-23269 SIGSEGV in ft_boolean_check_syntax_string on setting ft_boolean_syntax +# +SET GLOBAL ft_boolean_syntax='+ -><()~*:""&|'; +SET GLOBAL ft_boolean_syntax=DEFAULT; diff --git a/mysql-test/r/ctype_utf32_def.result b/mysql-test/r/ctype_utf32_def.result new file mode 100644 index 00000000000..611072eb75b --- /dev/null +++ b/mysql-test/r/ctype_utf32_def.result @@ -0,0 +1,6 @@ +call mtr.add_suppression("'utf32' can not be used as client character set"); +# +# MDEV-23269 SIGSEGV in ft_boolean_check_syntax_string on setting ft_boolean_syntax +# +SET GLOBAL ft_boolean_syntax='+ -><()~*:""&|'; +SET GLOBAL ft_boolean_syntax=DEFAULT; diff --git a/mysql-test/t/ctype_utf16_def.test b/mysql-test/t/ctype_utf16_def.test index 0829cd53285..c6de842f618 100644 --- a/mysql-test/t/ctype_utf16_def.test +++ b/mysql-test/t/ctype_utf16_def.test @@ -7,3 +7,10 @@ call mtr.add_suppression("'utf16' can not be used as client character set"); SHOW VARIABLES LIKE 'collation_server'; SHOW VARIABLES LIKE 'character_set_server'; SHOW VARIABLES LIKE 'ft_stopword_file'; + +--echo # +--echo # MDEV-23269 SIGSEGV in ft_boolean_check_syntax_string on setting ft_boolean_syntax +--echo # + +SET GLOBAL ft_boolean_syntax='+ -><()~*:""&|'; +SET GLOBAL ft_boolean_syntax=DEFAULT; diff --git a/mysql-test/t/ctype_utf32_def-master.opt b/mysql-test/t/ctype_utf32_def-master.opt new file mode 100644 index 00000000000..3b0880cbff3 --- /dev/null +++ b/mysql-test/t/ctype_utf32_def-master.opt @@ -0,0 +1 @@ +--character-set-server=utf32,latin1 --collation-server=utf32_general_ci diff --git a/mysql-test/t/ctype_utf32_def.test b/mysql-test/t/ctype_utf32_def.test new file mode 100644 index 00000000000..e23f96052d3 --- /dev/null +++ b/mysql-test/t/ctype_utf32_def.test @@ -0,0 +1,9 @@ +--source include/have_utf32.inc +call mtr.add_suppression("'utf32' can not be used as client character set"); + +--echo # +--echo # MDEV-23269 SIGSEGV in ft_boolean_check_syntax_string on setting ft_boolean_syntax +--echo # + +SET GLOBAL ft_boolean_syntax='+ -><()~*:""&|'; +SET GLOBAL ft_boolean_syntax=DEFAULT; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 9afc701c6ba..d723c36e4cb 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -9666,7 +9666,9 @@ static int get_options(int *argc_ptr, char ***argv_ptr) if (global_system_variables.low_priority_updates) thr_upgraded_concurrent_insert_lock= TL_WRITE_LOW_PRIORITY; - if (ft_boolean_check_syntax_string((uchar*) ft_boolean_syntax)) + if (ft_boolean_check_syntax_string((uchar*) ft_boolean_syntax, + strlen(ft_boolean_syntax), + system_charset_info)) { sql_print_error("Invalid ft-boolean-syntax string: %s\n", ft_boolean_syntax); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index e4de3d8d0aa..f231f49a667 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1022,7 +1022,9 @@ static Sys_var_ulong Sys_flush_time( static bool check_ftb_syntax(sys_var *self, THD *thd, set_var *var) { return ft_boolean_check_syntax_string((uchar*) - (var->save_result.string_value.str)); + (var->save_result.string_value.str), + var->save_result.string_value.length, + self->charset(thd)); } static bool query_cache_flush(sys_var *self, THD *thd, enum_var_type type) { diff --git a/storage/myisam/ft_parser.c b/storage/myisam/ft_parser.c index f6930e91e6e..b2170a93cde 100644 --- a/storage/myisam/ft_parser.c +++ b/storage/myisam/ft_parser.c @@ -78,18 +78,25 @@ FT_WORD * ft_linearize(TREE *wtree, MEM_ROOT *mem_root) DBUG_RETURN(wlist); } -my_bool ft_boolean_check_syntax_string(const uchar *str) +my_bool ft_boolean_check_syntax_string(const uchar *str, size_t length, + CHARSET_INFO *cs) { uint i, j; + if (cs->mbminlen != 1) + { + DBUG_ASSERT(0); + return 1; + } + if (!str || - (strlen((char*) str)+1 != sizeof(DEFAULT_FTB_SYNTAX)) || + (length + 1 != sizeof(DEFAULT_FTB_SYNTAX)) || (str[0] != ' ' && str[1] != ' ')) return 1; for (i=0; i 127 || my_isalnum(default_charset_info, str[i])) + if ((unsigned char)(str[i]) > 127 || my_isalnum(cs, str[i])) return 1; for (j=0; j Date: Sat, 9 Oct 2021 22:27:31 +0400 Subject: MDEV-25925 Warning: Memory not freed: 32 on INSERT DELAYED Also fixes MDEV-24467 Memory not freed after failed INSERT DELAYED Description: In case of an error (e.g. data truncation) during mysql_insert() handling an INSERT DELAYED, the data type specific data in fields (e.g. Field_blob::value) is not taken over by the delayed writer thread. All fields in table_list->table are freed by free_root() immediately after mysql_insert(). To avoid a memory leak, we need to free the specific data before exiting mysql_insert() on error. --- mysql-test/r/delayed_blob.result | 17 +++++++++++++++++ mysql-test/t/delayed_blob-master.opt | 1 + mysql-test/t/delayed_blob.test | 21 +++++++++++++++++++++ sql/sql_insert.cc | 11 +++++++++++ 4 files changed, 50 insertions(+) create mode 100644 mysql-test/r/delayed_blob.result create mode 100644 mysql-test/t/delayed_blob-master.opt create mode 100644 mysql-test/t/delayed_blob.test diff --git a/mysql-test/r/delayed_blob.result b/mysql-test/r/delayed_blob.result new file mode 100644 index 00000000000..caa2e3ae5fe --- /dev/null +++ b/mysql-test/r/delayed_blob.result @@ -0,0 +1,17 @@ +# +# MDEV-25925 Warning: Memory not freed: 32 on INSERT DELAYED +# +SET sql_mode='TRADITIONAL'; +CREATE TABLE t1 (c BLOB) ENGINE=MyISAM; +INSERT DELAYED INTO t1 VALUES (''||''); +ERROR 22007: Truncated incorrect DOUBLE value: '' +DROP TABLE t1; +SET sql_mode=DEFAULT; +# +# MDEV-24467 Memory not freed after failed INSERT DELAYED +# +CREATE TABLE t1 (a VARCHAR(1)) ENGINE=MyISAM; +ALTER TABLE t1 ADD b BLOB DEFAULT 'x'; +INSERT DELAYED INTO t1 (a) VALUES ('foo'); +ERROR 22001: Data too long for column 'a' at row 1 +DROP TABLE t1; diff --git a/mysql-test/t/delayed_blob-master.opt b/mysql-test/t/delayed_blob-master.opt new file mode 100644 index 00000000000..e442a822046 --- /dev/null +++ b/mysql-test/t/delayed_blob-master.opt @@ -0,0 +1 @@ +--init_connect="set @a='something unique to have MTR start a dedicated mariadbd for this test and shutdown it after the test'" diff --git a/mysql-test/t/delayed_blob.test b/mysql-test/t/delayed_blob.test new file mode 100644 index 00000000000..bf3e01a8825 --- /dev/null +++ b/mysql-test/t/delayed_blob.test @@ -0,0 +1,21 @@ +--echo # +--echo # MDEV-25925 Warning: Memory not freed: 32 on INSERT DELAYED +--echo # + +SET sql_mode='TRADITIONAL'; +CREATE TABLE t1 (c BLOB) ENGINE=MyISAM; +--error ER_TRUNCATED_WRONG_VALUE +INSERT DELAYED INTO t1 VALUES (''||''); +DROP TABLE t1; +SET sql_mode=DEFAULT; + + +--echo # +--echo # MDEV-24467 Memory not freed after failed INSERT DELAYED +--echo # + +CREATE TABLE t1 (a VARCHAR(1)) ENGINE=MyISAM; +ALTER TABLE t1 ADD b BLOB DEFAULT 'x'; +--error ER_DATA_TOO_LONG +INSERT DELAYED INTO t1 (a) VALUES ('foo'); +DROP TABLE t1; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 51b2c84cfea..718682f767e 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1257,7 +1257,18 @@ values_loop_end: abort: #ifndef EMBEDDED_LIBRARY if (lock_type == TL_WRITE_DELAYED) + { end_delayed_insert(thd); + /* + In case of an error (e.g. data truncation), the data type specific data + in fields (e.g. Field_blob::value) was not taken over + by the delayed writer thread. All fields in table_list->table + will be freed by free_root() soon. We need to free the specific + data before free_root() to avoid a memory leak. + */ + for (Field **ptr= table_list->table->field ; *ptr ; ptr++) + (*ptr)->free(); + } #endif if (table != NULL) table->file->ha_release_auto_increment(); -- cgit v1.2.1 From 6f32b28be511e96d9265ce727aa4a38810b41f96 Mon Sep 17 00:00:00 2001 From: Sergei Krivonos Date: Wed, 6 Oct 2021 11:31:08 +0300 Subject: Xcode compatibility update --- .gitignore | 2 ++ include/my_context.h | 3 +++ mysys/my_context.c | 7 +++++++ storage/connect/bsonudf.cpp | 9 +++++---- storage/connect/inihandl.cpp | 10 +++++----- storage/connect/tabbson.cpp | 2 +- storage/innobase/row/row0upd.cc | 12 ++++++------ 7 files changed, 29 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index dc8f6940fdf..c1a80c15aa6 100644 --- a/.gitignore +++ b/.gitignore @@ -338,6 +338,7 @@ x86/ build/ bld/ [Bb]in/ +/cmake-build-debug/ [Oo]bj/ # Roslyn cache directories @@ -518,4 +519,5 @@ compile_commands.json # Visual Studio Code workspace .vscode/ +/.idea/ .cache/clangd diff --git a/include/my_context.h b/include/my_context.h index ea0e3496887..45d2a7d7ffa 100644 --- a/include/my_context.h +++ b/include/my_context.h @@ -52,6 +52,9 @@ struct my_context { #ifdef MY_CONTEXT_USE_UCONTEXT +#if defined(__APPLE__) && !defined(_XOPEN_SOURCE) +#define _XOPEN_SOURCE +#endif #include struct my_context { diff --git a/mysys/my_context.c b/mysys/my_context.c index cf10738bdbd..f3aef23e07a 100644 --- a/mysys/my_context.c +++ b/mysys/my_context.c @@ -29,6 +29,10 @@ #endif #ifdef MY_CONTEXT_USE_UCONTEXT +#ifdef __APPLE__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif /* The makecontext() only allows to pass integers into the created context :-( We want to pass pointers, so we do it this kinda hackish way. @@ -154,6 +158,9 @@ my_context_destroy(struct my_context *c) DBUG_FREE_CODE_STATE(&c->dbug_state); } +#ifdef __APPLE__ +#pragma GCC diagnostic pop +#endif #endif /* MY_CONTEXT_USE_UCONTEXT */ diff --git a/storage/connect/bsonudf.cpp b/storage/connect/bsonudf.cpp index 783be52602a..ed795edb363 100644 --- a/storage/connect/bsonudf.cpp +++ b/storage/connect/bsonudf.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "bsonudf.h" @@ -621,7 +622,7 @@ PVAL BJNX::GetCalcValue(PGLOBAL g, PBVAL bap, int n) { // For calculated arrays, a local Value must be used int lng = 0; - short type, prec = 0; + short type = 0, prec = 0; bool b = n < Nod - 1; PVAL valp; PBVAL vlp, vp; @@ -690,7 +691,7 @@ PVAL BJNX::GetCalcValue(PGLOBAL g, PBVAL bap, int n) break; default: - break; + DBUG_ASSERT(!"Implement new op type support."); } // endswitch Op return valp = AllocateValue(g, type, lng, prec); @@ -4978,7 +4979,7 @@ char *bbin_array_add(UDF_INIT *initid, UDF_ARGS *args, char *result, uint n = 2; int* x = GetIntArgPtr(g, args, n); BJNX bnx(g, NULL, TYPE_STRING); - PBVAL jarp, top, jvp = NULL; + PBVAL jarp = NULL, top = NULL, jvp = NULL; PBVAL jsp = bnx.MakeValue(args, 0, true, &top); if (bnx.CheckPath(g, args, jsp, jvp, 2)) @@ -5611,7 +5612,7 @@ char *bbin_object_values(UDF_INIT *initid, UDF_ARGS *args, char *result, if (!bsp) { if (!CheckMemory(g, initid, args, 1, true, true)) { BJNX bnx(g); - PBVAL top, jarp; + PBVAL top, jarp = NULL; PBVAL jvp = bnx.MakeValue(args, 0, true, &top); if (jvp->Type == TYPE_JOB) { diff --git a/storage/connect/inihandl.cpp b/storage/connect/inihandl.cpp index 8e79aeac7ef..7d8741ebfa3 100644 --- a/storage/connect/inihandl.cpp +++ b/storage/connect/inihandl.cpp @@ -193,17 +193,17 @@ static void PROFILE_Save( FILE *file, PROFILESECTION *section ) secno++; } - for (key = section->key; key; key = key->next) - if (key->name && key->name[0]) { + for (key= section->key; key; key= key->next) { + if (key->name[0]) { fprintf(file, "%s", SVP(key->name)); if (key->value) fprintf(file, "=%s", SVP(key->value)); fprintf(file, "\n"); - } // endif key->name - - } // endfor section + } // endif key->name + } + } // endfor section } // end of PROFILE_Save diff --git a/storage/connect/tabbson.cpp b/storage/connect/tabbson.cpp index 3b1f1c84d1a..a7c561318c7 100644 --- a/storage/connect/tabbson.cpp +++ b/storage/connect/tabbson.cpp @@ -871,7 +871,7 @@ PBVAL BCUTIL::MakeBson(PGLOBAL g, PBVAL jsp, int n) /***********************************************************************/ PBVAL BCUTIL::GetRowValue(PGLOBAL g, PBVAL row, int i) { - int nod = Cp->Nod, n = nod - 1; + int nod = Cp->Nod; JNODE *nodes = Cp->Nodes; PBVAL arp; PBVAL bvp = NULL; diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc index 1f3e34d493a..a58c3993e90 100644 --- a/storage/innobase/row/row0upd.cc +++ b/storage/innobase/row/row0upd.cc @@ -2470,13 +2470,13 @@ row_upd_sec_index_entry( case DB_LOCK_WAIT_TIMEOUT: WSREP_DEBUG("Foreign key check fail: " "%s on table %s index %s query %s", - ut_strerr(err), index->name, index->table->name, + ut_strerr(err), index->name(), index->table->name.m_name, wsrep_thd_query(trx->mysql_thd)); break; default: WSREP_ERROR("Foreign key check fail: " "%s on table %s index %s query %s", - ut_strerr(err), index->name, index->table->name, + ut_strerr(err), index->name(), index->table->name.m_name, wsrep_thd_query(trx->mysql_thd)); break; } @@ -2806,14 +2806,14 @@ check_fk: case DB_LOCK_WAIT_TIMEOUT: WSREP_DEBUG("Foreign key check fail: " "%s on table %s index %s query %s", - ut_strerr(err), index->name, index->table->name, + ut_strerr(err), index->name(), index->table->name.m_name, wsrep_thd_query(trx->mysql_thd)); goto err_exit; default: WSREP_ERROR("Foreign key check fail: " "%s on table %s index %s query %s", - ut_strerr(err), index->name, index->table->name, + ut_strerr(err), index->name(), index->table->name.m_name, wsrep_thd_query(trx->mysql_thd)); goto err_exit; @@ -3037,13 +3037,13 @@ row_upd_del_mark_clust_rec( case DB_LOCK_WAIT_TIMEOUT: WSREP_DEBUG("Foreign key check fail: " "%d on table %s index %s query %s", - err, index->name, index->table->name, + err, index->name(), index->table->name.m_name, wsrep_thd_query(trx->mysql_thd)); break; default: WSREP_ERROR("Foreign key check fail: " "%d on table %s index %s query %s", - err, index->name, index->table->name, + err, index->name(), index->table->name.m_name, wsrep_thd_query(trx->mysql_thd)); break; } -- cgit v1.2.1 From 2bb8d7c2f36439ab6a3944476665eb1218c36f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 13 Oct 2021 10:38:41 +0300 Subject: MDEV-26811: Assertion "log_sys->n_pending_flushes == 1" fails In commit 1cb218c37cc3fe01a1ff2fe9b1cbfb591e90d5ce (MDEV-26450) we introduced the function log_write_and_flush(), which may compete with log_checkpoint() invoking log_write_flush_to_disk_low() from another thread. The assertion n_pending_flushes==1 is too strict. There is no possibility of a race condition here, because fil_flush() is protected by fil_system->mutex and the rest will be protected by log_sys->mutex. log_write_flush_to_disk_low(), log_write_and_flush(): Relax the assertions to test for a nonzero count. --- storage/innobase/log/log0log.cc | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/storage/innobase/log/log0log.cc b/storage/innobase/log/log0log.cc index 945c97daf4a..8dc29e302dd 100644 --- a/storage/innobase/log/log0log.cc +++ b/storage/innobase/log/log0log.cc @@ -983,13 +983,9 @@ loop: /** Flush the recently written changes to the log file. and invoke log_mutex_enter(). */ -static -void -log_write_flush_to_disk_low() +static void log_write_flush_to_disk_low() { - /* FIXME: This is not holding log_sys->mutex while - calling os_event_set()! */ - ut_a(log_sys->n_pending_flushes == 1); /* No other threads here */ + ut_a(log_sys->n_pending_flushes); bool do_flush = srv_file_flush_method != SRV_O_DSYNC; @@ -997,7 +993,6 @@ log_write_flush_to_disk_low() fil_flush(SRV_LOG_SPACE_FIRST_ID); } - log_mutex_enter(); if (do_flush) { log_sys->flushed_to_disk_lsn = log_sys->current_flush_lsn; @@ -1329,7 +1324,7 @@ ATTRIBUTE_COLD void log_write_and_flush() /* Code adapted from log_write_flush_to_disk_low() */ - ut_a(log_sys->n_pending_flushes == 1); /* No other threads here */ + ut_a(log_sys->n_pending_flushes); if (srv_file_flush_method != SRV_O_DSYNC) fil_flush(SRV_LOG_SPACE_FIRST_ID); -- cgit v1.2.1