From ef0dc50c05594e4287a2a3e06aef8e2ab576b8cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Wed, 13 Oct 2021 11:56:33 +0300 Subject: MDEV-26815 : galera.galera_ftwrl_drain fails with wrong errno 1146 Add wait_conditions to stabilize --- mysql-test/suite/galera/r/galera_ftwrl_drain.result | 16 ++++++++-------- mysql-test/suite/galera/t/galera_ftwrl_drain.test | 11 +++++++---- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/mysql-test/suite/galera/r/galera_ftwrl_drain.result b/mysql-test/suite/galera/r/galera_ftwrl_drain.result index 751811b88fd..f90e6dfcdfd 100644 --- a/mysql-test/suite/galera/r/galera_ftwrl_drain.result +++ b/mysql-test/suite/galera/r/galera_ftwrl_drain.result @@ -8,9 +8,9 @@ connection node_2; SET SESSION wsrep_sync_wait = 0; SET SESSION wsrep_on = 0; SET SESSION wsrep_on = 1; -SELECT COUNT(*) = 0 FROM t1; -COUNT(*) = 0 -1 +SELECT COUNT(*) AS EXPECT_0 FROM t1; +EXPECT_0 +0 connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2; connection node_2a; FLUSH TABLES WITH READ LOCK;; @@ -27,12 +27,12 @@ ERROR HY000: Lock wait timeout exceeded; try restarting transaction connection node_2a; UNLOCK TABLES; connection node_2; -SELECT COUNT(*) = 1 FROM t1; -COUNT(*) = 1 +SELECT COUNT(*) AS EXPECT_1 FROM t1; +EXPECT_1 1 INSERT INTO t1 VALUES (3); connection node_1; -SELECT COUNT(*) = 2 FROM t1; -COUNT(*) = 2 -1 +SELECT COUNT(*) AS EXPECT_2 FROM t1; +EXPECT_2 +2 DROP TABLE t1; diff --git a/mysql-test/suite/galera/t/galera_ftwrl_drain.test b/mysql-test/suite/galera/t/galera_ftwrl_drain.test index 690e890cdea..9ed93643b3f 100644 --- a/mysql-test/suite/galera/t/galera_ftwrl_drain.test +++ b/mysql-test/suite/galera/t/galera_ftwrl_drain.test @@ -18,7 +18,8 @@ CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB; --connection node_2 - +--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1' +--source include/wait_condition.inc --let $galera_sync_point = apply_monitor_slave_enter_sync --source include/galera_set_sync_point.inc @@ -31,7 +32,7 @@ SET SESSION wsrep_sync_wait = 0; # Wait until applier has blocked --source include/galera_wait_sync_point.inc -SELECT COUNT(*) = 0 FROM t1; +SELECT COUNT(*) AS EXPECT_0 FROM t1; --connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2 --connection node_2a @@ -61,9 +62,11 @@ INSERT INTO t2 VALUES (2); UNLOCK TABLES; --connection node_2 -SELECT COUNT(*) = 1 FROM t1; +--let $wait_condition = SELECT COUNT(*) = 1 FROM t1 +--source include/wait_condition.inc +SELECT COUNT(*) AS EXPECT_1 FROM t1; INSERT INTO t1 VALUES (3); --connection node_1 -SELECT COUNT(*) = 2 FROM t1; +SELECT COUNT(*) AS EXPECT_2 FROM t1; DROP TABLE t1; -- cgit v1.2.1 From bbae2d398f866b00f7e8ad71984884ff3375df52 Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Wed, 29 Sep 2021 19:19:38 +0300 Subject: MDEV-26712 row events never reset thd->mem_root but must do that at the end of the statement. A provide template patch is elaborated also to match to the upstream fixes of the very same bug. --- sql/log_event.cc | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 04577be4f6f..9108b57fec1 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -11208,12 +11208,17 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) } #endif /* WITH_WSREP && HAVE_QUERY_CACHE */ - if (get_flags(STMT_END_F) && (error= rows_event_stmt_cleanup(rgi, thd))) - slave_rows_error_report(ERROR_LEVEL, - thd->is_error() ? 0 : error, - rgi, thd, table, - get_type_str(), - RPL_LOG_NAME, (ulong) log_pos); + if (get_flags(STMT_END_F)) + { + if ((error= rows_event_stmt_cleanup(rgi, thd))) + slave_rows_error_report(ERROR_LEVEL, + thd->is_error() ? 0 : error, + rgi, thd, table, + get_type_str(), + RPL_LOG_NAME, (ulong) log_pos); + if (thd->slave_thread) + free_root(thd->mem_root, MYF(MY_KEEP_PREALLOC)); + } DBUG_RETURN(error); err: -- cgit v1.2.1 From a2a42f4eba7409264d4b4fb2dc7c04e40c50bd25 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Wed, 13 Oct 2021 12:57:57 +0400 Subject: MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset There were two independent problems which lead to the crash and to the non-relevant records returned in I_S queries: - The code in the I_S implementation was not secure about values with 0x00 bytes. It's fixed by using check_db_name() and check_table_name() inside make_table_name_list(), and by adding the test for 0x00 inside check_table_name(). - The code in Item_string::print() did not convert strings without introducers when restoring the CREATE VIEW statement from an Item tree. This made wrong literals inside the "query" line in the view FRM file in cases when the VIEW parse time character_set_client!=character_set_connection. That's fixed by adding a proper conversion. This change also fixed a similar problem in SHOW PROCEDURE CODE - the literals were displayed in wrong character set in SP instructions in cases when the SP parse time character_set_client!=character_set_connection. --- mysql-test/r/ctype_utf16le.result | 33 +++++++++++++++++++++++++ mysql-test/r/ctype_utf8.result | 17 +++++++++++++ mysql-test/r/information_schema.result | 20 ++++++++++++++++ mysql-test/r/sp-code.result | 24 +++++++++++++++++++ mysql-test/t/ctype_utf16le.test | 37 ++++++++++++++++++++++++++++ mysql-test/t/ctype_utf8.test | 18 ++++++++++++++ mysql-test/t/information_schema.test | 21 ++++++++++++++++ mysql-test/t/sp-code.test | 24 +++++++++++++++++++ sql/item.cc | 44 ++++++++++++++++++++++++++++++++-- sql/sql_show.cc | 7 +++++- sql/table.cc | 15 ++++++++++++ 11 files changed, 257 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/ctype_utf16le.result b/mysql-test/r/ctype_utf16le.result index a43ed6ee538..bc28f89b7ea 100644 --- a/mysql-test/r/ctype_utf16le.result +++ b/mysql-test/r/ctype_utf16le.result @@ -3000,5 +3000,38 @@ DROP TABLE t1; # SET STORAGE_ENGINE=Default; # +# MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset +# +SET NAMES utf8; +SET SESSION character_set_connection= utf16le; +CREATE TABLE kv (v BLOB); +CREATE TABLE t (a INT); +CREATE VIEW v AS SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'; +LOAD DATA INFILE 'MYSQLD_DATADIR/test/v.frm' REPLACE INTO TABLE kv; +SELECT * FROM kv WHERE v LIKE _binary'query=%'; +v +query=select `information_schema`.`TABLES`.`TABLE_CATALOG` AS `TABLE_CATALOG`,`information_schema`.`TABLES`.`TABLE_SCHEMA` AS `TABLE_SCHEMA`,`information_schema`.`TABLES`.`TABLE_NAME` AS `TABLE_NAME`,`information_schema`.`TABLES`.`TABLE_TYPE` AS `TABLE_TYPE`,`information_schema`.`TABLES`.`ENGINE` AS `ENGINE`,`information_schema`.`TABLES`.`VERSION` AS `VERSION`,`information_schema`.`TABLES`.`ROW_FORMAT` AS `ROW_FORMAT`,`information_schema`.`TABLES`.`TABLE_ROWS` AS `TABLE_ROWS`,`information_schema`.`TABLES`.`AVG_ROW_LENGTH` AS `AVG_ROW_LENGTH`,`information_schema`.`TABLES`.`DATA_LENGTH` AS `DATA_LENGTH`,`information_schema`.`TABLES`.`MAX_DATA_LENGTH` AS `MAX_DATA_LENGTH`,`information_schema`.`TABLES`.`INDEX_LENGTH` AS `INDEX_LENGTH`,`information_schema`.`TABLES`.`DATA_FREE` AS `DATA_FREE`,`information_schema`.`TABLES`.`AUTO_INCREMENT` AS `AUTO_INCREMENT`,`information_schema`.`TABLES`.`CREATE_TIME` AS `CREATE_TIME`,`information_schema`.`TABLES`.`UPDATE_TIME` AS `UPDATE_TIME`,`information_schema`.`TABLES`.`CHECK_TIME` AS `CHECK_TIME`,`information_schema`.`TABLES`.`TABLE_COLLATION` AS `TABLE_COLLATION`,`information_schema`.`TABLES`.`CHECKSUM` AS `CHECKSUM`,`information_schema`.`TABLES`.`CREATE_OPTIONS` AS `CREATE_OPTIONS`,`information_schema`.`TABLES`.`TABLE_COMMENT` AS `TABLE_COMMENT` from `INFORMATION_SCHEMA`.`TABLES` where `information_schema`.`TABLES`.`TABLE_NAME` = 't1' +TRUNCATE TABLE kv; +SELECT * FROM v; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE VERSION ROW_FORMAT TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE AUTO_INCREMENT CREATE_TIME UPDATE_TIME CHECK_TIME TABLE_COLLATION CHECKSUM CREATE_OPTIONS TABLE_COMMENT +LOCK TABLE t WRITE; +UNLOCK TABLES; +DROP VIEW v; +DROP TABLE t; +DROP TABLE kv; +CREATE TABLE t (a INT); +SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1'); +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE VERSION ROW_FORMAT TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE AUTO_INCREMENT CREATE_TIME UPDATE_TIME CHECK_TIME TABLE_COLLATION CHECKSUM CREATE_OPTIONS TABLE_COMMENT +LOCK TABLE t WRITE; +UNLOCK TABLES; +DROP TABLE t; +CREATE TABLE t (a INT); +SELECT TABLE_NAME, HEX(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1'); +TABLE_NAME HEX(TABLE_NAME) +SELECT TABLE_NAME, TABLE_SCHEMA, HEX(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=CONCAT('test',0x00,'1'); +TABLE_NAME TABLE_SCHEMA HEX(TABLE_NAME) +DROP TABLE t; +SET NAMES utf8; +# # End of 10.2 tests # diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index 7189629b570..18398f2556a 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -11239,5 +11239,22 @@ DROP TABLE t1; # SET STORAGE_ENGINE=Default; # +# MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset +# +SET NAMES utf8; +SET SESSION character_set_connection=latin1; +CREATE VIEW v1 AS SELECT 'ä' AS c1; +SELECT c1, HEX(c1) FROM v1; +c1 HEX(c1) +ä E4 +CREATE TABLE kv (v BLOB); +LOAD DATA INFILE 'MYSQLD_DATADIR/test/v1.frm' REPLACE INTO TABLE kv; +SELECT * FROM kv WHERE v LIKE _binary'query=%'; +v +query=select 'ä' AS `c1` +DROP TABLE kv; +DROP VIEW v1; +SET NAMES utf8; +# # End of 10.2 tests # diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index c7153bd6383..cc9cf842570 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -2210,5 +2210,25 @@ SELECT * FROM v LIMIT ROWS EXAMINED 9; ERROR HY000: Sort aborted: DROP VIEW v; # +# MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset +# +CREATE TABLE t (a INT); +SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1'); +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE VERSION ROW_FORMAT TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE AUTO_INCREMENT CREATE_TIME UPDATE_TIME CHECK_TIME TABLE_COLLATION CHECKSUM CREATE_OPTIONS TABLE_COMMENT +SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=CONCAT('test',0x00,'1'); +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE VERSION ROW_FORMAT TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE AUTO_INCREMENT CREATE_TIME UPDATE_TIME CHECK_TIME TABLE_COLLATION CHECKSUM CREATE_OPTIONS TABLE_COMMENT +DROP TABLE t; +CREATE TABLE `a/~.b` (a INT); +SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='a/~.b'; +TABLE_SCHEMA TABLE_NAME +test a/~.b +DROP TABLE `a/~.b`; +CREATE DATABASE `a/~.b`; +CREATE TABLE `a/~.b`.t1 (a INT); +SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='a/~.b'; +TABLE_SCHEMA TABLE_NAME +a/~.b t1 +DROP DATABASE `a/~.b`; +# # End of 10.2 Test # diff --git a/mysql-test/r/sp-code.result b/mysql-test/r/sp-code.result index 67932447c2a..4ad79f50d45 100644 --- a/mysql-test/r/sp-code.result +++ b/mysql-test/r/sp-code.result @@ -971,3 +971,27 @@ Pos Instruction DROP PROCEDURE testp_bug11763507; DROP FUNCTION testf_bug11763507; #END OF BUG#11763507 test. +# +# MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset +# +SET NAMES utf8; +SET SESSION character_set_connection=latin1; +CREATE PROCEDURE p1() +BEGIN +DECLARE a VARCHAR(10) CHARACTER SET utf8; +SET a='ä'; +SELECT a, 'ä' AS b; +END; +$$ +SHOW PROCEDURE CODE p1; +Pos Instruction +0 set a@0 NULL +1 set a@0 'ä' +2 stmt 0 "SELECT a, 'ä' AS b" +CALL p1; +a b +ä ä +DROP PROCEDURE p1; +# +# End of 10.2 tests +# diff --git a/mysql-test/t/ctype_utf16le.test b/mysql-test/t/ctype_utf16le.test index 204df136274..671100c2d9d 100644 --- a/mysql-test/t/ctype_utf16le.test +++ b/mysql-test/t/ctype_utf16le.test @@ -3,6 +3,7 @@ -- source include/have_utf32.inc -- source include/have_utf8mb4.inc +let $MYSQLD_DATADIR= `select @@datadir`; SET TIME_ZONE='+03:00'; @@ -810,6 +811,42 @@ let $coll='utf16le_nopad_bin'; let $coll_pad='utf16le_bin'; --source include/ctype_pad_all_engines.inc +--echo # +--echo # MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset +--echo # + + + +SET NAMES utf8; +SET SESSION character_set_connection= utf16le; + +CREATE TABLE kv (v BLOB); +CREATE TABLE t (a INT); +CREATE VIEW v AS SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'; +--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR +eval LOAD DATA INFILE '$MYSQLD_DATADIR/test/v.frm' REPLACE INTO TABLE kv; +SELECT * FROM kv WHERE v LIKE _binary'query=%'; +TRUNCATE TABLE kv; +SELECT * FROM v; +LOCK TABLE t WRITE; +UNLOCK TABLES; +DROP VIEW v; +DROP TABLE t; +DROP TABLE kv; + +CREATE TABLE t (a INT); +SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1'); +LOCK TABLE t WRITE; +UNLOCK TABLES; +DROP TABLE t; + +CREATE TABLE t (a INT); +SELECT TABLE_NAME, HEX(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1'); +SELECT TABLE_NAME, TABLE_SCHEMA, HEX(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=CONCAT('test',0x00,'1'); +DROP TABLE t; + +SET NAMES utf8; + --echo # --echo # End of 10.2 tests --echo # diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test index 9a64821db66..b34de4175e9 100644 --- a/mysql-test/t/ctype_utf8.test +++ b/mysql-test/t/ctype_utf8.test @@ -2,6 +2,8 @@ # Tests with the utf8 character set # +let $MYSQLD_DATADIR= `select @@datadir`; + let collation=utf8_unicode_ci; --source include/have_collation.inc SET TIME_ZONE='+03:00'; @@ -2165,6 +2167,22 @@ let $coll='utf8_nopad_bin'; let $coll_pad='utf8_bin'; --source include/ctype_pad_all_engines.inc +--echo # +--echo # MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset +--echo # + +SET NAMES utf8; +SET SESSION character_set_connection=latin1; +CREATE VIEW v1 AS SELECT 'ä' AS c1; +SELECT c1, HEX(c1) FROM v1; +CREATE TABLE kv (v BLOB); +--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR +eval LOAD DATA INFILE '$MYSQLD_DATADIR/test/v1.frm' REPLACE INTO TABLE kv; +SELECT * FROM kv WHERE v LIKE _binary'query=%'; +DROP TABLE kv; +DROP VIEW v1; +SET NAMES utf8; + --echo # --echo # End of 10.2 tests --echo # diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index 9ff94d2deb7..b7f4a7e4407 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -1934,6 +1934,27 @@ SELECT * FROM v LIMIT ROWS EXAMINED 9; DROP VIEW v; +--echo # +--echo # MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset +--echo # + +# Expect empty sets if requested TABLE_NAME or TABLE_SCHEMA with zero bytes +CREATE TABLE t (a INT); +SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=CONCAT('t',0x00,'1'); +SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=CONCAT('test',0x00,'1'); +DROP TABLE t; + +# Make sure check_table_name() does not reject special characters +CREATE TABLE `a/~.b` (a INT); +SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='a/~.b'; +DROP TABLE `a/~.b`; + +# Make sure check_db_name() does not reject special characters +CREATE DATABASE `a/~.b`; +CREATE TABLE `a/~.b`.t1 (a INT); +SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='a/~.b'; +DROP DATABASE `a/~.b`; + --echo # --echo # End of 10.2 Test --echo # diff --git a/mysql-test/t/sp-code.test b/mysql-test/t/sp-code.test index 129a68204ba..0f19627c78c 100644 --- a/mysql-test/t/sp-code.test +++ b/mysql-test/t/sp-code.test @@ -735,3 +735,27 @@ DROP PROCEDURE testp_bug11763507; DROP FUNCTION testf_bug11763507; --echo #END OF BUG#11763507 test. + + +--echo # +--echo # MDEV-23408 Wrong result upon query from I_S and further Assertion `!alias_arg || strlen(alias_arg->str) == alias_arg->length' failed with certain connection charset +--echo # + +SET NAMES utf8; +SET SESSION character_set_connection=latin1; +DELIMITER $$; +CREATE PROCEDURE p1() +BEGIN + DECLARE a VARCHAR(10) CHARACTER SET utf8; + SET a='ä'; + SELECT a, 'ä' AS b; +END; +$$ +DELIMITER ;$$ +SHOW PROCEDURE CODE p1; +CALL p1; +DROP PROCEDURE p1; + +--echo # +--echo # End of 10.2 tests +--echo # diff --git a/sql/item.cc b/sql/item.cc index 2a7c620b864..3ff0219c3b3 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3338,8 +3338,48 @@ void Item_string::print(String *str, enum_query_type query_type) } else { - // Caller wants a result in the charset of str_value. - str_value.print(str); + /* + We're restoring a parse-able statement from an Item tree. + Make sure to revert character set conversions that previously + happened in the parser when Item_string was created. + */ + if (print_introducer) + { + /* + Print the string as is, without conversion: + Strings with introducers are not converted in the parser. + */ + str_value.print(str); + } + else + { + /* + Print the string with conversion. + Strings without introducers are converted in the parser, + from character_set_client to character_set_connection. + + When restoring a CREATE VIEW statement, + - str_value.charsets() contains parse time character_set_connection + - str->charset() contains parse time character_set_client + So we convert the string back from parse-time character_set_connection + to parse time character_set_client. + + In some cases, e.g. SHOW PROCEDURE CODE, it's also possible + that str->charset() is "utf8mb3" instead of parse time + character_set_client. In these cases we convert + here from the parse-time character_set_connection to utf8mb3. + + QQ: perhaps the code behind SHOW PROCEDURE CODE should + also request the result in the parse-time character_set_client + (like the code restoring CREATE VIEW statements does), + rather than in utf8mb3: + - utf8mb3 does not work well with non-BMP characters (e.g. emoji). + - Simply changing utf8mb3 to utf8mb4 will not fully help: + some character sets have unassigned characters, + they get lost during during cs->utf8mb4->cs round trip. + */ + str_value.print_with_conversion(str, str->charset()); + } } str->append('\''); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 721bb053343..710c68d2551 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -4227,7 +4227,9 @@ make_table_name_list(THD *thd, Dynamic_array *table_names, if (!lookup_field_vals->wild_table_value && lookup_field_vals->table_value.str) { - if (lookup_field_vals->table_value.length > NAME_LEN) + if (check_table_name(lookup_field_vals->table_value.str, + lookup_field_vals->table_value.length, + false)) { /* Impossible value for a table name, @@ -4264,6 +4266,9 @@ make_table_name_list(THD *thd, Dynamic_array *table_names, return (schema_tables_add(thd, table_names, lookup_field_vals->table_value.str)); + if (check_db_name((LEX_STRING*)db_name)) + return 0; // Impossible TABLE_SCHEMA name + find_files_result res= find_files(thd, table_names, db_name, path, &lookup_field_vals->table_value); if (res != FIND_FILES_OK) diff --git a/sql/table.cc b/sql/table.cc index 87b3c158a67..d4f8170e0af 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -4183,6 +4183,21 @@ bool check_table_name(const char *name, size_t length, bool check_for_path_chars if (check_for_path_chars && (*name == '/' || *name == '\\' || *name == '~' || *name == FN_EXTCHAR)) return 1; + /* + We don't allow zero byte in table/schema names: + - Some code still uses NULL-terminated strings. + Zero bytes will confuse this code. + - There is a little practical use of zero bytes in names anyway. + Note, if the string passed as "name" comes here + from the parser as an identifier, it does not contain zero bytes, + as the parser rejects zero bytes in identifiers. + But "name" can also come here from queries like this: + SELECT * FROM I_S.TABLES WHERE TABLE_NAME='str'; + In this case "name" is a general string expression + and it can have any arbitrary bytes, including zero bytes. + */ + if (*name == 0x00) + return 1; name++; name_length++; } -- cgit v1.2.1 From 9e6c383867ed9145ae88af6eb933a1fdd4d5c757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Wed, 13 Oct 2021 13:13:27 +0300 Subject: MDEV-17964: Assertion `status == 0' failed in add_role_user_mapping_action This happens upon CREATE USER and DROP ROLE. The underlying problem is that our HASH implementation shuffles elements around when performing an update or delete. This means that when doing a scan through the HASH table by index, in search of elements to delete or update one must restart the scan to make sure nothing is missed if at least one delete / update happened. More specifically, what happened in this case: The hash has 131 element, DROP ROLE removes the element [119]. Its [119]->next was element [129], so [129] is moved to [119]. Now we need to compact the hash, removing the last element [130]. It gets one bit off its hash value and becomes element [2]. The existing element [2] is moved to [129], and old [130] is moved to [2]. We cannot simply move [130] to [129] and make [2]->next=130, it won't work if [2] is itself in the collision list and doesn't belong in [2]. The handle_grant_struct code assumed that it is safe to continue by only reexamining the currently modified / deleted element index, but that is not true. Missing to delete an element in the hash triggered the assertion in the test case. DROP ROLE would not clear all necessary role->role or role->user mappings. To fix the problem we ensure that the scan is restarted, only if an element was deleted / updated, similar to how bubble-sort keeps sorting until it finds no more elements to swap. --- mysql-test/suite/roles/rebuild_role_grants.result | 266 ++++++++++++++++ mysql-test/suite/roles/rebuild_role_grants.test | 31 ++ sql/sql_acl.cc | 371 +++++++++++----------- 3 files changed, 479 insertions(+), 189 deletions(-) diff --git a/mysql-test/suite/roles/rebuild_role_grants.result b/mysql-test/suite/roles/rebuild_role_grants.result index 72eabe38b93..101efd47569 100644 --- a/mysql-test/suite/roles/rebuild_role_grants.result +++ b/mysql-test/suite/roles/rebuild_role_grants.result @@ -65,3 +65,269 @@ drop role look, isp, xxx, ppp; connection default; disconnect con1; drop user nnnn@'%'; +CREATE USER u@localhost; +CREATE ROLE r1; +CREATE ROLE r2; +CREATE ROLE r3; +CREATE ROLE r4; +CREATE ROLE r5; +CREATE ROLE r6; +CREATE ROLE r7; +CREATE ROLE r8; +CREATE ROLE r9; +CREATE ROLE r10; +CREATE ROLE r11; +CREATE ROLE r12; +CREATE ROLE r13; +CREATE ROLE r14; +CREATE ROLE r15; +CREATE ROLE r16; +CREATE ROLE r17; +CREATE ROLE r18; +CREATE ROLE r19; +CREATE ROLE r20; +CREATE ROLE r21; +CREATE ROLE r22; +CREATE ROLE r23; +CREATE ROLE r24; +CREATE ROLE r25; +CREATE ROLE r26; +CREATE ROLE r27; +CREATE ROLE r28; +CREATE ROLE r29; +CREATE ROLE r30; +CREATE ROLE r31; +CREATE ROLE r32; +CREATE ROLE r33; +CREATE ROLE r34; +CREATE ROLE r35; +CREATE ROLE r36; +CREATE ROLE r37; +CREATE ROLE r38; +CREATE ROLE r39; +CREATE ROLE r40; +CREATE ROLE r41; +CREATE ROLE r42; +CREATE ROLE r43; +CREATE ROLE r44; +CREATE ROLE r45; +CREATE ROLE r46; +CREATE ROLE r47; +CREATE ROLE r48; +CREATE ROLE r49; +CREATE ROLE r50; +CREATE ROLE r51; +CREATE ROLE r52; +CREATE ROLE r53; +CREATE ROLE r54; +CREATE ROLE r55; +CREATE ROLE r56; +CREATE ROLE r57; +CREATE ROLE r58; +CREATE ROLE r59; +CREATE ROLE r60; +CREATE ROLE r61; +CREATE ROLE r62; +CREATE ROLE r63; +CREATE ROLE r64; +CREATE ROLE r65; +CREATE ROLE r66; +CREATE ROLE r67; +CREATE ROLE r68; +CREATE ROLE r69; +CREATE ROLE r70; +CREATE ROLE r71; +CREATE ROLE r72; +CREATE ROLE r73; +CREATE ROLE r74; +CREATE ROLE r75; +CREATE ROLE r76; +CREATE ROLE r77; +CREATE ROLE r78; +CREATE ROLE r79; +CREATE ROLE r80; +CREATE ROLE r81; +CREATE ROLE r82; +CREATE ROLE r83; +CREATE ROLE r84; +CREATE ROLE r85; +CREATE ROLE r86; +CREATE ROLE r87; +CREATE ROLE r88; +CREATE ROLE r89; +CREATE ROLE r90; +CREATE ROLE r91; +CREATE ROLE r92; +CREATE ROLE r93; +CREATE ROLE r94; +CREATE ROLE r95; +CREATE ROLE r96; +CREATE ROLE r97; +CREATE ROLE r98; +CREATE ROLE r99; +CREATE ROLE r100; +CREATE ROLE r101; +CREATE ROLE r102; +CREATE ROLE r103; +CREATE ROLE r104; +CREATE ROLE r105; +CREATE ROLE r106; +CREATE ROLE r107; +CREATE ROLE r108; +CREATE ROLE r109; +CREATE ROLE r110; +CREATE ROLE r111; +CREATE ROLE r112; +CREATE ROLE r113; +CREATE ROLE r114; +CREATE ROLE r115; +CREATE ROLE r116; +CREATE ROLE r117; +CREATE ROLE r118; +CREATE ROLE r119; +CREATE ROLE r120; +CREATE ROLE r121; +CREATE ROLE r122; +CREATE ROLE r123; +CREATE ROLE r124; +CREATE ROLE r125; +CREATE ROLE r126; +CREATE ROLE r127; +CREATE ROLE r128; +CREATE ROLE n; +CREATE ROLE d WITH ADMIN n; +CREATE ROLE '%' WITH ADMIN u@localhost; +DROP ROLE n; +CREATE USER 't'; +DROP ROLE r1; +DROP ROLE r2; +DROP ROLE r3; +DROP ROLE r4; +DROP ROLE r5; +DROP ROLE r6; +DROP ROLE r7; +DROP ROLE r8; +DROP ROLE r9; +DROP ROLE r10; +DROP ROLE r11; +DROP ROLE r12; +DROP ROLE r13; +DROP ROLE r14; +DROP ROLE r15; +DROP ROLE r16; +DROP ROLE r17; +DROP ROLE r18; +DROP ROLE r19; +DROP ROLE r20; +DROP ROLE r21; +DROP ROLE r22; +DROP ROLE r23; +DROP ROLE r24; +DROP ROLE r25; +DROP ROLE r26; +DROP ROLE r27; +DROP ROLE r28; +DROP ROLE r29; +DROP ROLE r30; +DROP ROLE r31; +DROP ROLE r32; +DROP ROLE r33; +DROP ROLE r34; +DROP ROLE r35; +DROP ROLE r36; +DROP ROLE r37; +DROP ROLE r38; +DROP ROLE r39; +DROP ROLE r40; +DROP ROLE r41; +DROP ROLE r42; +DROP ROLE r43; +DROP ROLE r44; +DROP ROLE r45; +DROP ROLE r46; +DROP ROLE r47; +DROP ROLE r48; +DROP ROLE r49; +DROP ROLE r50; +DROP ROLE r51; +DROP ROLE r52; +DROP ROLE r53; +DROP ROLE r54; +DROP ROLE r55; +DROP ROLE r56; +DROP ROLE r57; +DROP ROLE r58; +DROP ROLE r59; +DROP ROLE r60; +DROP ROLE r61; +DROP ROLE r62; +DROP ROLE r63; +DROP ROLE r64; +DROP ROLE r65; +DROP ROLE r66; +DROP ROLE r67; +DROP ROLE r68; +DROP ROLE r69; +DROP ROLE r70; +DROP ROLE r71; +DROP ROLE r72; +DROP ROLE r73; +DROP ROLE r74; +DROP ROLE r75; +DROP ROLE r76; +DROP ROLE r77; +DROP ROLE r78; +DROP ROLE r79; +DROP ROLE r80; +DROP ROLE r81; +DROP ROLE r82; +DROP ROLE r83; +DROP ROLE r84; +DROP ROLE r85; +DROP ROLE r86; +DROP ROLE r87; +DROP ROLE r88; +DROP ROLE r89; +DROP ROLE r90; +DROP ROLE r91; +DROP ROLE r92; +DROP ROLE r93; +DROP ROLE r94; +DROP ROLE r95; +DROP ROLE r96; +DROP ROLE r97; +DROP ROLE r98; +DROP ROLE r99; +DROP ROLE r100; +DROP ROLE r101; +DROP ROLE r102; +DROP ROLE r103; +DROP ROLE r104; +DROP ROLE r105; +DROP ROLE r106; +DROP ROLE r107; +DROP ROLE r108; +DROP ROLE r109; +DROP ROLE r110; +DROP ROLE r111; +DROP ROLE r112; +DROP ROLE r113; +DROP ROLE r114; +DROP ROLE r115; +DROP ROLE r116; +DROP ROLE r117; +DROP ROLE r118; +DROP ROLE r119; +DROP ROLE r120; +DROP ROLE r121; +DROP ROLE r122; +DROP ROLE r123; +DROP ROLE r124; +DROP ROLE r125; +DROP ROLE r126; +DROP ROLE r127; +DROP ROLE r128; +DROP ROLE d; +DROP ROLE '%'; +DROP USER 't'; +DROP USER u@localhost; diff --git a/mysql-test/suite/roles/rebuild_role_grants.test b/mysql-test/suite/roles/rebuild_role_grants.test index 84dbdf78fb8..7007df0ecdd 100644 --- a/mysql-test/suite/roles/rebuild_role_grants.test +++ b/mysql-test/suite/roles/rebuild_role_grants.test @@ -67,3 +67,34 @@ drop role look, isp, xxx, ppp; connection default; disconnect con1; drop user nnnn@'%'; + +# +# MDEV-17964 Assertion `status == 0' failed in add_role_user_mapping_action +# upon CREATE USER and DROP ROLE +# +CREATE USER u@localhost; + +--let $n= 1 +while ($n < 129) +{ + eval CREATE ROLE r$n; + inc $n; +} + +CREATE ROLE n; +CREATE ROLE d WITH ADMIN n; +CREATE ROLE '%' WITH ADMIN u@localhost; +DROP ROLE n; +CREATE USER 't'; + +--let $n= 1 +while ($n < 129) +{ + eval DROP ROLE r$n; + inc $n; +} + +DROP ROLE d; +DROP ROLE '%'; +DROP USER 't'; +DROP USER u@localhost; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 89fecc92e9b..f62dd5471eb 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -9645,8 +9645,8 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, LEX_USER *user_from, LEX_USER *user_to) { int result= 0; - int idx; int elements; + bool restart; const char *UNINIT_VAR(user); const char *UNINIT_VAR(host); ACL_USER *acl_user= NULL; @@ -9747,82 +9747,98 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, DBUG_RETURN(-1); } + #ifdef EXTRA_DEBUG DBUG_PRINT("loop",("scan struct: %u search user: '%s' host: '%s'", struct_no, user_from->user.str, user_from->host.str)); #endif - /* Loop over all elements *backwards* (see the comment below). */ - for (idx= elements - 1; idx >= 0; idx--) - { - /* - Get a pointer to the element. - */ - switch (struct_no) { - case USER_ACL: - acl_user= dynamic_element(&acl_users, idx, ACL_USER*); - user= acl_user->user.str; - host= acl_user->host.hostname; - break; + /* Loop over elements backwards as it may reduce the number of mem-moves + for dynamic arrays. - case DB_ACL: - acl_db= &acl_dbs.at(idx); - user= acl_db->user; - host= acl_db->host.hostname; + We restart the loop, if we deleted or updated anything in a hash table + because calling my_hash_delete or my_hash_update shuffles elements indices + and we can miss some if we do only one scan. + */ + do { + restart= false; + for (int idx= elements - 1; idx >= 0; idx--) + { + /* + Get a pointer to the element. + */ + switch (struct_no) { + case USER_ACL: + acl_user= dynamic_element(&acl_users, idx, ACL_USER*); + user= acl_user->user.str; + host= acl_user->host.hostname; break; - case COLUMN_PRIVILEGES_HASH: - case PROC_PRIVILEGES_HASH: - case FUNC_PRIVILEGES_HASH: - grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx); - user= grant_name->user; - host= grant_name->host.hostname; - break; + case DB_ACL: + acl_db= &acl_dbs.at(idx); + user= acl_db->user; + host= acl_db->host.hostname; + break; - case PROXY_USERS_ACL: - acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*); - user= acl_proxy_user->get_user(); - host= acl_proxy_user->get_host(); - break; + case COLUMN_PRIVILEGES_HASH: + case PROC_PRIVILEGES_HASH: + case FUNC_PRIVILEGES_HASH: + grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx); + user= grant_name->user; + host= grant_name->host.hostname; + break; - case ROLES_MAPPINGS_HASH: - role_grant_pair= (ROLE_GRANT_PAIR *) my_hash_element(roles_mappings_hash, idx); - user= role_grant_pair->u_uname; - host= role_grant_pair->u_hname; - break; + case PROXY_USERS_ACL: + acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*); + user= acl_proxy_user->get_user(); + host= acl_proxy_user->get_host(); + break; - default: - DBUG_ASSERT(0); - } - if (! user) - user= ""; - if (! host) - host= ""; + case ROLES_MAPPINGS_HASH: + role_grant_pair= (ROLE_GRANT_PAIR *) my_hash_element(roles_mappings_hash, idx); + user= role_grant_pair->u_uname; + host= role_grant_pair->u_hname; + break; + + default: + DBUG_ASSERT(0); + } + if (! user) + user= ""; + if (! host) + host= ""; #ifdef EXTRA_DEBUG - DBUG_PRINT("loop",("scan struct: %u index: %u user: '%s' host: '%s'", - struct_no, idx, user, host)); + DBUG_PRINT("loop",("scan struct: %u index: %u user: '%s' host: '%s'", + struct_no, idx, user, host)); #endif - if (struct_no == ROLES_MAPPINGS_HASH) - { - const char* role= role_grant_pair->r_uname? role_grant_pair->r_uname: ""; - if (user_from->is_role()) + if (struct_no == ROLES_MAPPINGS_HASH) { - /* When searching for roles within the ROLES_MAPPINGS_HASH, we have - to check both the user field as well as the role field for a match. + const char* role= role_grant_pair->r_uname? role_grant_pair->r_uname: ""; + if (user_from->is_role()) + { + /* When searching for roles within the ROLES_MAPPINGS_HASH, we have + to check both the user field as well as the role field for a match. - It is possible to have a role granted to a role. If we are going - to modify the mapping entry, it needs to be done on either on the - "user" end (here represented by a role) or the "role" end. At least - one part must match. + It is possible to have a role granted to a role. If we are going + to modify the mapping entry, it needs to be done on either on the + "user" end (here represented by a role) or the "role" end. At least + one part must match. - If the "user" end has a not-empty host string, it can never match - as we are searching for a role here. A role always has an empty host - string. - */ - if ((*host || strcmp(user_from->user.str, user)) && - strcmp(user_from->user.str, role)) - continue; + If the "user" end has a not-empty host string, it can never match + as we are searching for a role here. A role always has an empty host + string. + */ + if ((*host || strcmp(user_from->user.str, user)) && + strcmp(user_from->user.str, role)) + continue; + } + else + { + if (strcmp(user_from->user.str, user) || + my_strcasecmp(system_charset_info, user_from->host.str, host)) + continue; + } } else { @@ -9830,154 +9846,131 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, my_strcasecmp(system_charset_info, user_from->host.str, host)) continue; } - } - else - { - if (strcmp(user_from->user.str, user) || - my_strcasecmp(system_charset_info, user_from->host.str, host)) - continue; - } - result= 1; /* At least one element found. */ - if ( drop ) - { - elements--; - switch ( struct_no ) { - case USER_ACL: - free_acl_user(dynamic_element(&acl_users, idx, ACL_USER*)); - delete_dynamic_element(&acl_users, idx); - break; + result= 1; /* At least one element found. */ + if ( drop ) + { + elements--; + switch ( struct_no ) { + case USER_ACL: + free_acl_user(dynamic_element(&acl_users, idx, ACL_USER*)); + delete_dynamic_element(&acl_users, idx); + break; - case DB_ACL: - acl_dbs.del(idx); - break; + case DB_ACL: + acl_dbs.del(idx); + break; - case COLUMN_PRIVILEGES_HASH: - case PROC_PRIVILEGES_HASH: - case FUNC_PRIVILEGES_HASH: - my_hash_delete(grant_name_hash, (uchar*) grant_name); - /* - In our HASH implementation on deletion one elements - is moved into a place where a deleted element was, - and the last element is moved into the empty space. - Thus we need to re-examine the current element, but - we don't have to restart the search from the beginning. - */ - if (idx != elements) - idx++; - break; + case COLUMN_PRIVILEGES_HASH: + case PROC_PRIVILEGES_HASH: + case FUNC_PRIVILEGES_HASH: + my_hash_delete(grant_name_hash, (uchar*) grant_name); + restart= true; + break; - case PROXY_USERS_ACL: - delete_dynamic_element(&acl_proxy_users, idx); - break; + case PROXY_USERS_ACL: + delete_dynamic_element(&acl_proxy_users, idx); + break; - case ROLES_MAPPINGS_HASH: - my_hash_delete(roles_mappings_hash, (uchar*) role_grant_pair); - if (idx != elements) - idx++; - break; + case ROLES_MAPPINGS_HASH: + my_hash_delete(roles_mappings_hash, (uchar*) role_grant_pair); + restart= true; + break; - default: - DBUG_ASSERT(0); - break; + default: + DBUG_ASSERT(0); + break; + } } - } - else if ( user_to ) - { - switch ( struct_no ) { - case USER_ACL: - acl_user->user.str= strdup_root(&acl_memroot, user_to->user.str); - acl_user->user.length= user_to->user.length; - update_hostname(&acl_user->host, strdup_root(&acl_memroot, user_to->host.str)); - acl_user->hostname_length= strlen(acl_user->host.hostname); - break; + else if ( user_to ) + { + switch ( struct_no ) { + case USER_ACL: + acl_user->user.str= strdup_root(&acl_memroot, user_to->user.str); + acl_user->user.length= user_to->user.length; + update_hostname(&acl_user->host, strdup_root(&acl_memroot, user_to->host.str)); + acl_user->hostname_length= strlen(acl_user->host.hostname); + break; - case DB_ACL: - acl_db->user= strdup_root(&acl_memroot, user_to->user.str); - update_hostname(&acl_db->host, strdup_root(&acl_memroot, user_to->host.str)); - break; + case DB_ACL: + acl_db->user= strdup_root(&acl_memroot, user_to->user.str); + update_hostname(&acl_db->host, strdup_root(&acl_memroot, user_to->host.str)); + break; - case COLUMN_PRIVILEGES_HASH: - case PROC_PRIVILEGES_HASH: - case FUNC_PRIVILEGES_HASH: - { - /* - Save old hash key and its length to be able to properly update - element position in hash. - */ - char *old_key= grant_name->hash_key; - size_t old_key_length= grant_name->key_length; + case COLUMN_PRIVILEGES_HASH: + case PROC_PRIVILEGES_HASH: + case FUNC_PRIVILEGES_HASH: + { + /* + Save old hash key and its length to be able to properly update + element position in hash. + */ + char *old_key= grant_name->hash_key; + size_t old_key_length= grant_name->key_length; + + /* + Update the grant structure with the new user name and host name. + */ + grant_name->set_user_details(user_to->host.str, grant_name->db, + user_to->user.str, grant_name->tname, + TRUE); + + /* + Since username is part of the hash key, when the user name + is renamed, the hash key is changed. Update the hash to + ensure that the position matches the new hash key value + */ + my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key, + old_key_length); + restart= true; + break; + } - /* - Update the grant structure with the new user name and host name. - */ - grant_name->set_user_details(user_to->host.str, grant_name->db, - user_to->user.str, grant_name->tname, - TRUE); - - /* - Since username is part of the hash key, when the user name - is renamed, the hash key is changed. Update the hash to - ensure that the position matches the new hash key value - */ - my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key, - old_key_length); - /* - hash_update() operation could have moved element from the tail or - the head of the hash to the current position. But it can never - move an element from the head to the tail or from the tail to the - head over the current element. - So we need to examine the current element once again, but - we don't need to restart the search from the beginning. - */ - idx++; + case PROXY_USERS_ACL: + acl_proxy_user->set_user (&acl_memroot, user_to->user.str); + acl_proxy_user->set_host (&acl_memroot, user_to->host.str); break; - } - case PROXY_USERS_ACL: - acl_proxy_user->set_user (&acl_memroot, user_to->user.str); - acl_proxy_user->set_host (&acl_memroot, user_to->host.str); - break; + case ROLES_MAPPINGS_HASH: + { + /* + Save old hash key and its length to be able to properly update + element position in hash. + */ + char *old_key= role_grant_pair->hashkey.str; + size_t old_key_length= role_grant_pair->hashkey.length; + bool oom; + + if (user_to->is_role()) + oom= role_grant_pair->init(&acl_memroot, role_grant_pair->u_uname, + role_grant_pair->u_hname, + user_to->user.str, false); + else + oom= role_grant_pair->init(&acl_memroot, user_to->user.str, + user_to->host.str, + role_grant_pair->r_uname, false); + if (oom) + DBUG_RETURN(-1); + + my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair, + (uchar*) old_key, old_key_length); + restart= true; + break; + } - case ROLES_MAPPINGS_HASH: - { - /* - Save old hash key and its length to be able to properly update - element position in hash. - */ - char *old_key= role_grant_pair->hashkey.str; - size_t old_key_length= role_grant_pair->hashkey.length; - bool oom; - - if (user_to->is_role()) - oom= role_grant_pair->init(&acl_memroot, role_grant_pair->u_uname, - role_grant_pair->u_hname, - user_to->user.str, false); - else - oom= role_grant_pair->init(&acl_memroot, user_to->user.str, - user_to->host.str, - role_grant_pair->r_uname, false); - if (oom) - DBUG_RETURN(-1); - - my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair, - (uchar*) old_key, old_key_length); - idx++; // see the comment above + default: + DBUG_ASSERT(0); break; } - default: - DBUG_ASSERT(0); + } + else + { + /* If search is requested, we do not need to search further. */ break; } - } - else - { - /* If search is requested, we do not need to search further. */ - break; - } - } + } while (restart); #ifdef EXTRA_DEBUG DBUG_PRINT("loop",("scan struct: %u result %d", struct_no, result)); #endif -- cgit v1.2.1 From 5f63f5dc60a48105d739f606cbf0a575925029d1 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 15 Oct 2021 21:56:17 +0400 Subject: A clean-up patch for MDEV-23408: fixing test failure on Windows Schema and table names in a veiw FRM files are: - in upper case on Linux - in lower case on Windows Using the LOWER() function when displaying an FRM file fragment, to avoid the OS-specific difference. --- mysql-test/r/ctype_utf16le.result | 10 +++++----- mysql-test/t/ctype_utf16le.test | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/ctype_utf16le.result b/mysql-test/r/ctype_utf16le.result index bc28f89b7ea..8dc51eb262b 100644 --- a/mysql-test/r/ctype_utf16le.result +++ b/mysql-test/r/ctype_utf16le.result @@ -3004,13 +3004,13 @@ SET STORAGE_ENGINE=Default; # SET NAMES utf8; SET SESSION character_set_connection= utf16le; -CREATE TABLE kv (v BLOB); +CREATE TABLE kv (v TEXT CHARACTER SET latin1); CREATE TABLE t (a INT); CREATE VIEW v AS SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'; -LOAD DATA INFILE 'MYSQLD_DATADIR/test/v.frm' REPLACE INTO TABLE kv; -SELECT * FROM kv WHERE v LIKE _binary'query=%'; -v -query=select `information_schema`.`TABLES`.`TABLE_CATALOG` AS `TABLE_CATALOG`,`information_schema`.`TABLES`.`TABLE_SCHEMA` AS `TABLE_SCHEMA`,`information_schema`.`TABLES`.`TABLE_NAME` AS `TABLE_NAME`,`information_schema`.`TABLES`.`TABLE_TYPE` AS `TABLE_TYPE`,`information_schema`.`TABLES`.`ENGINE` AS `ENGINE`,`information_schema`.`TABLES`.`VERSION` AS `VERSION`,`information_schema`.`TABLES`.`ROW_FORMAT` AS `ROW_FORMAT`,`information_schema`.`TABLES`.`TABLE_ROWS` AS `TABLE_ROWS`,`information_schema`.`TABLES`.`AVG_ROW_LENGTH` AS `AVG_ROW_LENGTH`,`information_schema`.`TABLES`.`DATA_LENGTH` AS `DATA_LENGTH`,`information_schema`.`TABLES`.`MAX_DATA_LENGTH` AS `MAX_DATA_LENGTH`,`information_schema`.`TABLES`.`INDEX_LENGTH` AS `INDEX_LENGTH`,`information_schema`.`TABLES`.`DATA_FREE` AS `DATA_FREE`,`information_schema`.`TABLES`.`AUTO_INCREMENT` AS `AUTO_INCREMENT`,`information_schema`.`TABLES`.`CREATE_TIME` AS `CREATE_TIME`,`information_schema`.`TABLES`.`UPDATE_TIME` AS `UPDATE_TIME`,`information_schema`.`TABLES`.`CHECK_TIME` AS `CHECK_TIME`,`information_schema`.`TABLES`.`TABLE_COLLATION` AS `TABLE_COLLATION`,`information_schema`.`TABLES`.`CHECKSUM` AS `CHECKSUM`,`information_schema`.`TABLES`.`CREATE_OPTIONS` AS `CREATE_OPTIONS`,`information_schema`.`TABLES`.`TABLE_COMMENT` AS `TABLE_COMMENT` from `INFORMATION_SCHEMA`.`TABLES` where `information_schema`.`TABLES`.`TABLE_NAME` = 't1' +LOAD DATA INFILE 'MYSQLD_DATADIR/test/v.frm' REPLACE INTO TABLE kv CHARACTER SET latin1; +SELECT LOWER(v) FROM kv WHERE v LIKE _binary'query=%'; +LOWER(v) +query=select `information_schema`.`tables`.`table_catalog` as `table_catalog`,`information_schema`.`tables`.`table_schema` as `table_schema`,`information_schema`.`tables`.`table_name` as `table_name`,`information_schema`.`tables`.`table_type` as `table_type`,`information_schema`.`tables`.`engine` as `engine`,`information_schema`.`tables`.`version` as `version`,`information_schema`.`tables`.`row_format` as `row_format`,`information_schema`.`tables`.`table_rows` as `table_rows`,`information_schema`.`tables`.`avg_row_length` as `avg_row_length`,`information_schema`.`tables`.`data_length` as `data_length`,`information_schema`.`tables`.`max_data_length` as `max_data_length`,`information_schema`.`tables`.`index_length` as `index_length`,`information_schema`.`tables`.`data_free` as `data_free`,`information_schema`.`tables`.`auto_increment` as `auto_increment`,`information_schema`.`tables`.`create_time` as `create_time`,`information_schema`.`tables`.`update_time` as `update_time`,`information_schema`.`tables`.`check_time` as `check_time`,`information_schema`.`tables`.`table_collation` as `table_collation`,`information_schema`.`tables`.`checksum` as `checksum`,`information_schema`.`tables`.`create_options` as `create_options`,`information_schema`.`tables`.`table_comment` as `table_comment` from `information_schema`.`tables` where `information_schema`.`tables`.`table_name` = 't1' TRUNCATE TABLE kv; SELECT * FROM v; TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ENGINE VERSION ROW_FORMAT TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE AUTO_INCREMENT CREATE_TIME UPDATE_TIME CHECK_TIME TABLE_COLLATION CHECKSUM CREATE_OPTIONS TABLE_COMMENT diff --git a/mysql-test/t/ctype_utf16le.test b/mysql-test/t/ctype_utf16le.test index 671100c2d9d..c0cac7f5d67 100644 --- a/mysql-test/t/ctype_utf16le.test +++ b/mysql-test/t/ctype_utf16le.test @@ -820,12 +820,12 @@ let $coll_pad='utf16le_bin'; SET NAMES utf8; SET SESSION character_set_connection= utf16le; -CREATE TABLE kv (v BLOB); +CREATE TABLE kv (v TEXT CHARACTER SET latin1); CREATE TABLE t (a INT); CREATE VIEW v AS SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'; --replace_result $MYSQLD_DATADIR MYSQLD_DATADIR -eval LOAD DATA INFILE '$MYSQLD_DATADIR/test/v.frm' REPLACE INTO TABLE kv; -SELECT * FROM kv WHERE v LIKE _binary'query=%'; +eval LOAD DATA INFILE '$MYSQLD_DATADIR/test/v.frm' REPLACE INTO TABLE kv CHARACTER SET latin1; +SELECT LOWER(v) FROM kv WHERE v LIKE _binary'query=%'; TRUNCATE TABLE kv; SELECT * FROM v; LOCK TABLE t WRITE; -- cgit v1.2.1 From a46665090b96f5598b534d31524af830dfc225b0 Mon Sep 17 00:00:00 2001 From: Kentoku SHIBA Date: Sat, 6 Jul 2019 23:54:53 +0900 Subject: MDEV-19866 With a Spider table, a SELECT with WHERE involving primary key breaks following SELECTs (#1356) Change checking scanning partitions from part_spec to part_info->read_partitions --- .../spider/bugfix/include/mdev_19866_deinit.inc | 14 +++ .../spider/bugfix/include/mdev_19866_init.inc | 52 ++++++++++ .../mysql-test/spider/bugfix/r/mdev_19866.result | 111 +++++++++++++++++++++ .../mysql-test/spider/bugfix/t/mdev_19866.cnf | 4 + .../mysql-test/spider/bugfix/t/mdev_19866.test | 97 ++++++++++++++++++ storage/spider/spd_group_by_handler.cc | 62 ++++++------ 6 files changed, 310 insertions(+), 30 deletions(-) create mode 100644 storage/spider/mysql-test/spider/bugfix/include/mdev_19866_deinit.inc create mode 100644 storage/spider/mysql-test/spider/bugfix/include/mdev_19866_init.inc create mode 100644 storage/spider/mysql-test/spider/bugfix/r/mdev_19866.result create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_19866.cnf create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_19866.test diff --git a/storage/spider/mysql-test/spider/bugfix/include/mdev_19866_deinit.inc b/storage/spider/mysql-test/spider/bugfix/include/mdev_19866_deinit.inc new file mode 100644 index 00000000000..9d255152dd8 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/include/mdev_19866_deinit.inc @@ -0,0 +1,14 @@ +--let $MASTER_1_COMMENT_2_1= $MASTER_1_COMMENT_2_1_BACKUP +--let $CHILD2_1_DROP_TABLES= $CHILD2_1_DROP_TABLES_BACKUP +--let $CHILD2_1_CREATE_TABLES= $CHILD2_1_CREATE_TABLES_BACKUP +--let $CHILD2_1_SELECT_TABLES= $CHILD2_1_SELECT_TABLES_BACKUP +--let $CHILD2_2_DROP_TABLES= $CHILD2_2_DROP_TABLES_BACKUP +--let $CHILD2_2_CREATE_TABLES= $CHILD2_2_CREATE_TABLES_BACKUP +--let $CHILD2_2_SELECT_TABLES= $CHILD2_2_SELECT_TABLES_BACKUP +--disable_warnings +--disable_query_log +--disable_result_log +--source ../t/test_deinit.inc +--enable_result_log +--enable_query_log +--enable_warnings diff --git a/storage/spider/mysql-test/spider/bugfix/include/mdev_19866_init.inc b/storage/spider/mysql-test/spider/bugfix/include/mdev_19866_init.inc new file mode 100644 index 00000000000..dceae8226b0 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/include/mdev_19866_init.inc @@ -0,0 +1,52 @@ +--disable_warnings +--disable_query_log +--disable_result_log +--source ../t/test_init.inc +if (!$HAVE_PARTITION) +{ + --source group_by_order_by_limit_deinit.inc + --enable_result_log + --enable_query_log + --enable_warnings + skip Test requires partitioning; +} +--enable_result_log +--enable_query_log +--enable_warnings +--let $MASTER_1_COMMENT_2_1_BACKUP= $MASTER_1_COMMENT_2_1 +let $MASTER_1_COMMENT_2_1= + COMMENT='table "tbl_a"' + PARTITION BY KEY(pkey) ( + PARTITION pt1 COMMENT='srv "s_2_1"', + PARTITION pt2 COMMENT='srv "s_2_2"' + ); +--let $CHILD2_1_DROP_TABLES_BACKUP= $CHILD2_1_DROP_TABLES +let $CHILD2_1_DROP_TABLES= + DROP TABLE IF EXISTS tbl_a; +--let $CHILD2_1_CREATE_TABLES_BACKUP= $CHILD2_1_CREATE_TABLES +let $CHILD2_1_CREATE_TABLES= + CREATE TABLE tbl_a ( + pkey int NOT NULL, + val char(1) NOT NULL, + PRIMARY KEY (pkey) + ) $CHILD2_1_ENGINE $CHILD2_1_CHARSET; +--let $CHILD2_1_SELECT_TABLES_BACKUP= $CHILD2_1_SELECT_TABLES +let $CHILD2_1_SELECT_TABLES= + SELECT pkey, val FROM tbl_a ORDER BY pkey; +let $CHILD2_1_SELECT_ARGUMENT1= + SELECT argument FROM mysql.general_log WHERE argument LIKE '%select %'; +--let $CHILD2_2_DROP_TABLES_BACKUP= $CHILD2_2_DROP_TABLES +let $CHILD2_2_DROP_TABLES= + DROP TABLE IF EXISTS tbl_a; +--let $CHILD2_2_CREATE_TABLES_BACKUP= $CHILD2_2_CREATE_TABLES +let $CHILD2_2_CREATE_TABLES= + CREATE TABLE tbl_a ( + pkey int NOT NULL, + val char(1) NOT NULL, + PRIMARY KEY (pkey) + ) $CHILD2_2_ENGINE $CHILD2_2_CHARSET; +--let $CHILD2_2_SELECT_TABLES_BACKUP= $CHILD2_2_SELECT_TABLES +let $CHILD2_2_SELECT_TABLES= + SELECT pkey, val FROM tbl_a ORDER BY pkey; +let $CHILD2_2_SELECT_ARGUMENT1= + SELECT argument FROM mysql.general_log WHERE argument LIKE '%select %'; diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_19866.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_19866.result new file mode 100644 index 00000000000..5d483481edd --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_19866.result @@ -0,0 +1,111 @@ +for master_1 +for child2 +child2_1 +child2_2 +child2_3 +for child3 + +this test is for MDEV-19866 + +drop and create databases +connection master_1; +CREATE DATABASE auto_test_local; +USE auto_test_local; +connection child2_1; +SET @old_log_output = @@global.log_output; +SET GLOBAL log_output = 'TABLE,FILE'; +CREATE DATABASE auto_test_remote; +USE auto_test_remote; +connection child2_2; +SET @old_log_output = @@global.log_output; +SET GLOBAL log_output = 'TABLE,FILE'; +CREATE DATABASE auto_test_remote2; +USE auto_test_remote2; + +create table and insert +connection child2_1; +CHILD2_1_CREATE_TABLES +TRUNCATE TABLE mysql.general_log; +connection child2_2; +CHILD2_2_CREATE_TABLES +TRUNCATE TABLE mysql.general_log; +connection master_1; +CREATE TABLE tbl_a ( +pkey int NOT NULL, +val char(1) NOT NULL, +PRIMARY KEY (pkey) +) MASTER_1_ENGINE MASTER_1_CHARSET MASTER_1_COMMENT_2_1 +INSERT INTO tbl_a (pkey,val) VALUES (1,'1'),(2,'2'),(3,'3'),(4,'4'); + +select test 1 +connection child2_1; +TRUNCATE TABLE mysql.general_log; +connection child2_2; +TRUNCATE TABLE mysql.general_log; +connection master_1; +SELECT * FROM tbl_a; +pkey val +1 1 +3 3 +2 2 +4 4 +SELECT * FROM tbl_a WHERE pkey = 1; +pkey val +1 1 +SELECT * FROM tbl_a; +pkey val +1 1 +3 3 +2 2 +4 4 +SELECT * FROM tbl_a WHERE pkey = 2; +pkey val +2 2 +SELECT * FROM tbl_a; +pkey val +1 1 +3 3 +2 2 +4 4 +connection child2_1; +SELECT argument FROM mysql.general_log WHERE argument LIKE '%select %'; +argument +select `pkey`,`val` from `auto_test_remote`.`tbl_a` +select `pkey`,`val` from `auto_test_remote`.`tbl_a` where `pkey` = 1 +select `pkey`,`val` from `auto_test_remote`.`tbl_a` +select `pkey`,`val` from `auto_test_remote`.`tbl_a` +SELECT argument FROM mysql.general_log WHERE argument LIKE '%select %' +SELECT pkey, val FROM tbl_a ORDER BY pkey; +pkey val +1 1 +3 3 +connection child2_2; +SELECT argument FROM mysql.general_log WHERE argument LIKE '%select %'; +argument +select `pkey`,`val` from `auto_test_remote2`.`tbl_a` +select `pkey`,`val` from `auto_test_remote2`.`tbl_a` +select `pkey`,`val` from `auto_test_remote2`.`tbl_a` where `pkey` = 2 +select `pkey`,`val` from `auto_test_remote2`.`tbl_a` +SELECT argument FROM mysql.general_log WHERE argument LIKE '%select %' +SELECT pkey, val FROM tbl_a ORDER BY pkey; +pkey val +2 2 +4 4 + +deinit +connection master_1; +DROP DATABASE IF EXISTS auto_test_local; +connection child2_1; +DROP DATABASE IF EXISTS auto_test_remote; +SET GLOBAL log_output = @old_log_output; +connection child2_2; +DROP DATABASE IF EXISTS auto_test_remote2; +SET GLOBAL log_output = @old_log_output; +for master_1 +for child2 +child2_1 +child2_2 +child2_3 +for child3 + +end of test diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_19866.cnf b/storage/spider/mysql-test/spider/bugfix/t/mdev_19866.cnf new file mode 100644 index 00000000000..e0ffb99c38e --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_19866.cnf @@ -0,0 +1,4 @@ +!include include/default_mysqld.cnf +!include ../my_1_1.cnf +!include ../my_2_1.cnf +!include ../my_2_2.cnf diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_19866.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_19866.test new file mode 100644 index 00000000000..05b753ae8bb --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_19866.test @@ -0,0 +1,97 @@ +--source ../include/mdev_19866_init.inc +--echo +--echo this test is for MDEV-19866 +--echo +--echo drop and create databases +--connection master_1 +--disable_warnings +CREATE DATABASE auto_test_local; +USE auto_test_local; + +--connection child2_1 +SET @old_log_output = @@global.log_output; +SET GLOBAL log_output = 'TABLE,FILE'; +CREATE DATABASE auto_test_remote; +USE auto_test_remote; + +--connection child2_2 +SET @old_log_output = @@global.log_output; +SET GLOBAL log_output = 'TABLE,FILE'; +CREATE DATABASE auto_test_remote2; +USE auto_test_remote2; +--enable_warnings + +--echo +--echo create table and insert + +--connection child2_1 +--disable_query_log +echo CHILD2_1_CREATE_TABLES; +eval $CHILD2_1_CREATE_TABLES; +--enable_query_log +TRUNCATE TABLE mysql.general_log; + +--connection child2_2 +--disable_query_log +echo CHILD2_2_CREATE_TABLES; +eval $CHILD2_2_CREATE_TABLES; +--enable_query_log +TRUNCATE TABLE mysql.general_log; + +--connection master_1 +--disable_query_log +echo CREATE TABLE tbl_a ( + pkey int NOT NULL, + val char(1) NOT NULL, + PRIMARY KEY (pkey) +) MASTER_1_ENGINE MASTER_1_CHARSET MASTER_1_COMMENT_2_1; +eval CREATE TABLE tbl_a ( + pkey int NOT NULL, + val char(1) NOT NULL, + PRIMARY KEY (pkey) +) $MASTER_1_ENGINE $MASTER_1_CHARSET $MASTER_1_COMMENT_2_1; +--enable_query_log +INSERT INTO tbl_a (pkey,val) VALUES (1,'1'),(2,'2'),(3,'3'),(4,'4'); + +--echo +--echo select test 1 + +--connection child2_1 +TRUNCATE TABLE mysql.general_log; + +--connection child2_2 +TRUNCATE TABLE mysql.general_log; + +--connection master_1 +SELECT * FROM tbl_a; +SELECT * FROM tbl_a WHERE pkey = 1; +SELECT * FROM tbl_a; +SELECT * FROM tbl_a WHERE pkey = 2; +SELECT * FROM tbl_a; + +--connection child2_1 +eval $CHILD2_1_SELECT_ARGUMENT1; +eval $CHILD2_1_SELECT_TABLES; + +--connection child2_2 +eval $CHILD2_2_SELECT_ARGUMENT1; +eval $CHILD2_2_SELECT_TABLES; + +--echo +--echo deinit +--disable_warnings +--connection master_1 +DROP DATABASE IF EXISTS auto_test_local; + +--connection child2_1 +DROP DATABASE IF EXISTS auto_test_remote; +SET GLOBAL log_output = @old_log_output; + +--connection child2_2 +DROP DATABASE IF EXISTS auto_test_remote2; +SET GLOBAL log_output = @old_log_output; + +--enable_warnings +--source ../include/mdev_19866_deinit.inc +--echo +--echo end of test diff --git a/storage/spider/spd_group_by_handler.cc b/storage/spider/spd_group_by_handler.cc index 8bd0eca507f..de041897239 100644 --- a/storage/spider/spd_group_by_handler.cc +++ b/storage/spider/spd_group_by_handler.cc @@ -1654,21 +1654,18 @@ group_by_handler *spider_create_group_by_handler( if (from->table->part_info) { DBUG_PRINT("info",("spider partition handler")); -#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) && defined(PARTITION_HAS_GET_PART_SPEC) - ha_partition *partition = (ha_partition *) from->table->file; - part_id_range *part_spec = partition->get_part_spec(); - DBUG_PRINT("info",("spider part_spec->start_part=%u", part_spec->start_part)); - DBUG_PRINT("info",("spider part_spec->end_part=%u", part_spec->end_part)); - if ( - part_spec->start_part == partition->get_no_current_part_id() || - part_spec->start_part != part_spec->end_part - ) { +#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) + partition_info *part_info = from->table->part_info; + uint bits = bitmap_bits_set(&part_info->read_partitions); + DBUG_PRINT("info",("spider bits=%u", bits)); + if (bits != 1) + { DBUG_PRINT("info",("spider using multiple partitions is not supported by this feature yet")); #else DBUG_PRINT("info",("spider partition is not supported by this feature yet")); #endif DBUG_RETURN(NULL); -#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) && defined(PARTITION_HAS_GET_PART_SPEC) +#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) } #endif } @@ -1686,17 +1683,18 @@ group_by_handler *spider_create_group_by_handler( /* all tables are const_table */ DBUG_RETURN(NULL); } -#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) && defined(PARTITION_HAS_GET_PART_SPEC) +#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) if (from->table->part_info) { + partition_info *part_info = from->table->part_info; + uint part = bitmap_get_first_set(&part_info->read_partitions); ha_partition *partition = (ha_partition *) from->table->file; - part_id_range *part_spec = partition->get_part_spec(); handler **handlers = partition->get_child_handlers(); - spider = (ha_spider *) handlers[part_spec->start_part]; + spider = (ha_spider *) handlers[part]; } else { #endif spider = (ha_spider *) from->table->file; -#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) && defined(PARTITION_HAS_GET_PART_SPEC) +#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) } #endif share = spider->share; @@ -1717,17 +1715,18 @@ group_by_handler *spider_create_group_by_handler( { if (from->table->const_table) continue; -#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) && defined(PARTITION_HAS_GET_PART_SPEC) +#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) if (from->table->part_info) { + partition_info *part_info = from->table->part_info; + uint part = bitmap_get_first_set(&part_info->read_partitions); ha_partition *partition = (ha_partition *) from->table->file; - part_id_range *part_spec = partition->get_part_spec(); handler **handlers = partition->get_child_handlers(); - spider = (ha_spider *) handlers[part_spec->start_part]; + spider = (ha_spider *) handlers[part]; } else { #endif spider = (ha_spider *) from->table->file; -#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) && defined(PARTITION_HAS_GET_PART_SPEC) +#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) } #endif share = spider->share; @@ -1755,17 +1754,18 @@ group_by_handler *spider_create_group_by_handler( do { if (from->table->const_table) continue; -#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) && defined(PARTITION_HAS_GET_PART_SPEC) +#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) if (from->table->part_info) { + partition_info *part_info = from->table->part_info; + uint part = bitmap_get_first_set(&part_info->read_partitions); ha_partition *partition = (ha_partition *) from->table->file; - part_id_range *part_spec = partition->get_part_spec(); handler **handlers = partition->get_child_handlers(); - spider = (ha_spider *) handlers[part_spec->start_part]; + spider = (ha_spider *) handlers[part]; } else { #endif spider = (ha_spider *) from->table->file; -#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) && defined(PARTITION_HAS_GET_PART_SPEC) +#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) } #endif share = spider->share; @@ -1908,17 +1908,18 @@ group_by_handler *spider_create_group_by_handler( { from = from->next_local; } -#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) && defined(PARTITION_HAS_GET_PART_SPEC) +#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) if (from->table->part_info) { + partition_info *part_info = from->table->part_info; + uint part = bitmap_get_first_set(&part_info->read_partitions); ha_partition *partition = (ha_partition *) from->table->file; - part_id_range *part_spec = partition->get_part_spec(); handler **handlers = partition->get_child_handlers(); - spider = (ha_spider *) handlers[part_spec->start_part]; + spider = (ha_spider *) handlers[part]; } else { #endif spider = (ha_spider *) from->table->file; -#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) && defined(PARTITION_HAS_GET_PART_SPEC) +#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) } #endif share = spider->share; @@ -1996,17 +1997,18 @@ group_by_handler *spider_create_group_by_handler( continue; fields->clear_conn_holder_from_conn(); -#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) && defined(PARTITION_HAS_GET_PART_SPEC) +#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) if (from->table->part_info) { + partition_info *part_info = from->table->part_info; + uint part = bitmap_get_first_set(&part_info->read_partitions); ha_partition *partition = (ha_partition *) from->table->file; - part_id_range *part_spec = partition->get_part_spec(); handler **handlers = partition->get_child_handlers(); - spider = (ha_spider *) handlers[part_spec->start_part]; + spider = (ha_spider *) handlers[part]; } else { #endif spider = (ha_spider *) from->table->file; -#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) && defined(PARTITION_HAS_GET_PART_SPEC) +#if defined(PARTITION_HAS_GET_CHILD_HANDLERS) } #endif share = spider->share; -- cgit v1.2.1 From 39f6315612149860ffdb92d433a6bace2096b9c1 Mon Sep 17 00:00:00 2001 From: Nayuta Yanagisawa Date: Tue, 10 Aug 2021 11:32:31 +0000 Subject: MDEV-19866 follow-up Cherry-picking the fix for MDEV-19866 changes the behavior of the Spider slightly. So, I modified a existing test to match the new behavior. --- storage/spider/mysql-test/spider/bugfix/r/mdev_20100.result | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_20100.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_20100.result index fc4fb02d72f..9d2297b4daf 100644 --- a/storage/spider/mysql-test/spider/bugfix/r/mdev_20100.result +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_20100.result @@ -78,16 +78,16 @@ a b c connection child2_1; SELECT argument FROM mysql.general_log WHERE argument LIKE '%select %'; argument -select `a`,`b`,`c` from `auto_test_remote`.`ta_r3` where (`b` = 'c') +select t0.`a` `a`,t0.`b` `b`,t0.`c` `c` from `auto_test_remote`.`ta_r3` t0 where (t0.`b` = 'c') select `a`,`b`,`c` from `auto_test_remote`.`ta_r2` select `a`,`b`,`c` from `auto_test_remote`.`ta_r3` -select `a`,`b`,`c` from `auto_test_remote`.`ta_r4` where (`b` = 'c') +select t0.`a` `a`,t0.`b` `b`,t0.`c` `c` from `auto_test_remote`.`ta_r4` t0 where (t0.`b` = 'c') select `a`,`b`,`c` from `auto_test_remote`.`ta_r2` select `a`,`b`,`c` from `auto_test_remote`.`ta_r3` -select `a`,`b`,`c` from `auto_test_remote`.`ta_r2` where (`b` = 'c') +select t0.`a` `a`,t0.`b` `b`,t0.`c` `c` from `auto_test_remote`.`ta_r2` t0 where (t0.`b` = 'c') select `a`,`b`,`c` from `auto_test_remote`.`ta_r2` select `a`,`b`,`c` from `auto_test_remote`.`ta_r4` -select `a`,`b`,`c` from `auto_test_remote`.`ta_r2` where (`b` = 'c') +select t0.`a` `a`,t0.`b` `b`,t0.`c` `c` from `auto_test_remote`.`ta_r2` t0 where (t0.`b` = 'c') select `a`,`b`,`c` from `auto_test_remote`.`ta_r3` select `a`,`b`,`c` from `auto_test_remote`.`ta_r4` SELECT argument FROM mysql.general_log WHERE argument LIKE '%select %' -- cgit v1.2.1 From 2291f8ef73489fb8ed79768484df1ee4db3583a7 Mon Sep 17 00:00:00 2001 From: Brandon Nesterenko Date: Wed, 13 Oct 2021 07:31:32 -0600 Subject: MDEV-25284: Assertion `info->type == READ_CACHE || info->type == WRITE_CACHE' failed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: ======== This patch addresses two issues. First, if a CHANGE MASTER command is issued and an error happens while locating the replica’s relay logs, the logs can be put into an invalid state where future updates fail and future CHANGE MASTER calls crash the server. More specifically, right before a replica purges the relay logs (part of the `CHANGE MASTER TO` logic), the relay log is temporarily closed with state LOG_TO_BE_OPENED. If the server errors in-between the temporary log closure and purge, i.e. during the function find_log_pos, the log should be closed. MDEV-25284 reveals the log is not properly closed. Second, upon issuing a RESET SLAVE ALL command, a slave’s GTID filters are not cleared (DO_DOMAIN_IDS, IGNORE_DOMIAN_IDS, IGNORE_SERVER_IDS). MySQL had a similar bug report, Bug #18816897, which fixed this issue to clear IGNORE_SERVER_IDS after issuing RESET SLAVE ALL in version 5.7. Solution: ========= To fix the first problem, the CHANGE MASTER error handling logic was extended to transition the relay log state to LOG_CLOSED from LOG_TO_BE_OPENED. To fix the second problem, the RESET SLAVE ALL logic is extended to clear the domain_id filter and ignore_server_ids. Reviewed By: ============ Andrei Elkin --- .../rpl/include/rpl_reset_slave_all_check.inc | 48 +++++++++++ .../r/rpl_change_master_find_log_pos_err.result | 43 ++++++++++ .../r/rpl_reset_slave_all_clears_filters.result | 54 +++++++++++++ .../rpl/t/rpl_change_master_find_log_pos_err.test | 93 ++++++++++++++++++++++ .../rpl/t/rpl_reset_slave_all_clears_filters.test | 72 +++++++++++++++++ sql/log.h | 14 ++++ sql/rpl_mi.cc | 8 ++ sql/rpl_mi.h | 5 ++ sql/sql_repl.cc | 10 +++ 9 files changed, 347 insertions(+) create mode 100644 mysql-test/suite/rpl/include/rpl_reset_slave_all_check.inc create mode 100644 mysql-test/suite/rpl/r/rpl_change_master_find_log_pos_err.result create mode 100644 mysql-test/suite/rpl/r/rpl_reset_slave_all_clears_filters.result create mode 100644 mysql-test/suite/rpl/t/rpl_change_master_find_log_pos_err.test create mode 100644 mysql-test/suite/rpl/t/rpl_reset_slave_all_clears_filters.test diff --git a/mysql-test/suite/rpl/include/rpl_reset_slave_all_check.inc b/mysql-test/suite/rpl/include/rpl_reset_slave_all_check.inc new file mode 100644 index 00000000000..adbaf32ebd7 --- /dev/null +++ b/mysql-test/suite/rpl/include/rpl_reset_slave_all_check.inc @@ -0,0 +1,48 @@ +# This file ensures that a slave's id filtering variables (i.e. DO_DOMAIN_IDS, +# IGNORE_DOMAIN_IDS, and IGNORE_SERVER_IDS) are cleared after issuing +# `RESET SLAVE ALL`. +# +# param $_do_domain_ids Integer list of values to use for DO_DOMAIN_IDS +# param $_ignore_domain_ids Integer list of values to use for IGNORE_DOMAIN_IDS +# param $_ignore_server_ids Integer list of values to use for IGNORE_SERVER_IDS +# + +--echo # Id filtering variable values should be empty initially +let $do_domain_ids_before= query_get_value(SHOW SLAVE STATUS, Replicate_Do_Domain_Ids, 1); +let $ignore_domain_ids_before= query_get_value(SHOW SLAVE STATUS, Replicate_Ignore_Domain_Ids, 1); +let $ignore_server_ids_before= query_get_value(SHOW SLAVE STATUS, Replicate_Ignore_Server_Ids, 1); + +if (`SELECT "$do_domain_ids_before" != "" OR + "$ignore_domain_ids_before" != "" OR + "$ignore_server_ids_before" != ""`) +{ + die("CHANGE MASTER TO id filter variables are not empty initially"); +} + + +--echo # Set id filtering variables +eval CHANGE MASTER TO DO_DOMAIN_IDS=$_do_domain_ids, IGNORE_DOMAIN_IDS=$_ignore_domain_ids, IGNORE_SERVER_IDS=$_ignore_server_ids, MASTER_USE_GTID=SLAVE_POS; +let $do_domain_ids_set= query_get_value(SHOW SLAVE STATUS, Replicate_Do_Domain_Ids, 1); +let $ignore_domain_ids_set= query_get_value(SHOW SLAVE STATUS, Replicate_Ignore_Domain_Ids, 1); +let $ignore_server_ids_set= query_get_value(SHOW SLAVE STATUS, Replicate_Ignore_Server_Ids, 1); +--echo # do domain id list: $do_domain_ids_set +--echo # ignore domain id list: $ignore_domain_ids_set +--echo # ignore server id list: $ignore_server_ids_set + + +--echo # RESET SLAVE ALL should clear values for all id filtering variables +RESET SLAVE ALL; +--replace_result $MASTER_MYPORT MASTER_MYPORT +eval change master to master_port=$MASTER_MYPORT, master_host='127.0.0.1', master_user='root'; +--source include/start_slave.inc +--source include/stop_slave.inc + +let $do_domain_ids_cleared= query_get_value(SHOW SLAVE STATUS, Replicate_Do_Domain_Ids, 1); +let $ignore_domain_ids_cleared= query_get_value(SHOW SLAVE STATUS, Replicate_Ignore_Domain_Ids, 1); +let $ignore_server_ids_cleared= query_get_value(SHOW SLAVE STATUS, Replicate_Ignore_Server_Ids, 1); +if (`SELECT "$do_domain_ids_cleared" != "" OR + "$ignore_domain_ids_cleared" != "" OR + "$ignore_server_ids_cleared" != ""`) +{ + die("RESET SLAVE ALL did not clear id filtering variables"); +} diff --git a/mysql-test/suite/rpl/r/rpl_change_master_find_log_pos_err.result b/mysql-test/suite/rpl/r/rpl_change_master_find_log_pos_err.result new file mode 100644 index 00000000000..0ff76b5b60f --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_change_master_find_log_pos_err.result @@ -0,0 +1,43 @@ +include/master-slave.inc +[connection master] +# +# Failed CHANGE MASTER TO should not change relay log status +# +connection slave; +include/stop_slave.inc +SET @@debug_dbug="d,simulate_find_log_pos_error"; +CHANGE MASTER TO IGNORE_DOMAIN_IDS=(1), MASTER_USE_GTID=SLAVE_POS; +ERROR HY000: Target log not found in binlog index +SET @@debug_dbug=""; +include/start_slave.inc +# +# Ensure relay log can be updated after a failed CHANGE MASTER +# +FLUSH RELAY LOGS; +include/wait_for_slave_param.inc [Relay_Log_File] +# +# Slave should continue to receive data from old master after failed +# CHANGE MASTER TO +# +connection master; +CREATE TABLE t1 (a int); +insert into t1 values (1); +connection slave; +connection slave; +# +# Future CHANGE MASTER calls should succeed +# +include/stop_slave.inc +CHANGE MASTER TO MASTER_USE_GTID=SLAVE_POS; +include/start_slave.inc +######################## +# Cleanup +######################## +connection master; +DROP TABLE t1; +connection slave; +include/stop_slave.inc +RESET SLAVE ALL; +change master to master_port=MASTER_MYPORT, master_host='127.0.0.1', master_user='root'; +include/start_slave.inc +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/r/rpl_reset_slave_all_clears_filters.result b/mysql-test/suite/rpl/r/rpl_reset_slave_all_clears_filters.result new file mode 100644 index 00000000000..a273aeaa678 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_reset_slave_all_clears_filters.result @@ -0,0 +1,54 @@ +include/master-slave.inc +[connection master] +connection slave; +include/stop_slave.inc +# +# Category 1) DO_DOMAIN_IDS and IGNORE_SERVER_IDS specified together +# +# Id filtering variable values should be empty initially +# Set id filtering variables +CHANGE MASTER TO DO_DOMAIN_IDS=(1), IGNORE_DOMAIN_IDS=(), IGNORE_SERVER_IDS=(3), MASTER_USE_GTID=SLAVE_POS; +# do domain id list: 1 +# ignore domain id list: +# ignore server id list: 3 +# RESET SLAVE ALL should clear values for all id filtering variables +RESET SLAVE ALL; +change master to master_port=MASTER_MYPORT, master_host='127.0.0.1', master_user='root'; +include/start_slave.inc +include/stop_slave.inc +# +# Category 2) IGNORE_DOMAIN_IDS and IGNORE_SERVER_IDS specified together +# +# Id filtering variable values should be empty initially +# Set id filtering variables +CHANGE MASTER TO DO_DOMAIN_IDS=(), IGNORE_DOMAIN_IDS=(2), IGNORE_SERVER_IDS=(3), MASTER_USE_GTID=SLAVE_POS; +# do domain id list: +# ignore domain id list: 2 +# ignore server id list: 3 +# RESET SLAVE ALL should clear values for all id filtering variables +RESET SLAVE ALL; +change master to master_port=MASTER_MYPORT, master_host='127.0.0.1', master_user='root'; +include/start_slave.inc +include/stop_slave.inc +# +# Category 3) Null check - edge case with all empty lists to ensure a +# lack of specification doesn't break anything +# +# Id filtering variable values should be empty initially +# Set id filtering variables +CHANGE MASTER TO DO_DOMAIN_IDS=(), IGNORE_DOMAIN_IDS=(), IGNORE_SERVER_IDS=(), MASTER_USE_GTID=SLAVE_POS; +# do domain id list: +# ignore domain id list: +# ignore server id list: +# RESET SLAVE ALL should clear values for all id filtering variables +RESET SLAVE ALL; +change master to master_port=MASTER_MYPORT, master_host='127.0.0.1', master_user='root'; +include/start_slave.inc +include/stop_slave.inc +############################ +# Cleanup +############################ +connection slave; +change master to master_port=MASTER_MYPORT, master_host='127.0.0.1', master_user='root'; +include/start_slave.inc +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_change_master_find_log_pos_err.test b/mysql-test/suite/rpl/t/rpl_change_master_find_log_pos_err.test new file mode 100644 index 00000000000..d1c2c03f010 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_change_master_find_log_pos_err.test @@ -0,0 +1,93 @@ +# +# Purpose: +# This test ensures that issuing a CHANGE MASTER will not put a replica into +# an inconsistent state if the slave cannot find the log files (i.e. the call to +# find_log_pos in reset_logs fails). More specifically, right before a replica +# purges the relay logs (part of the `CHANGE MASTER TO` logic), the relay log is +# temporarily closed with state LOG_TO_BE_OPENED. If the server is issued a +# CHANGE MASTER and it errors in-between the temporary log closure and purge, +# i.e. during the function find_log_pos, the log should be closed. The bug +# reported by MDEV-25284 revealed the log is not properly closed, such that +# future relay log updates fail, and future CHANGE MASTER calls crash the +# server. +# +# Methodology: +# This test ensures that the relay log is properly closed by ensuring future +# updates and CHANGE MASTER calls succeed. +# +# References: +# MDEV-25284: Assertion `info->type == READ_CACHE || +# info->type == WRITE_CACHE' failed +# +--source include/master-slave.inc +--source include/have_debug.inc + +--echo # +--echo # Failed CHANGE MASTER TO should not change relay log status +--echo # + +--connection slave +--source include/stop_slave.inc +SET @@debug_dbug="d,simulate_find_log_pos_error"; +error 1373; +CHANGE MASTER TO IGNORE_DOMAIN_IDS=(1), MASTER_USE_GTID=SLAVE_POS; +SET @@debug_dbug=""; +--source include/start_slave.inc + + +--echo # +--echo # Ensure relay log can be updated after a failed CHANGE MASTER +--echo # + +FLUSH RELAY LOGS; +--let $slave_param= Relay_Log_File +--let $slave_param_value= slave-relay-bin.000003 +--source include/wait_for_slave_param.inc + + +--echo # +--echo # Slave should continue to receive data from old master after failed +--echo # CHANGE MASTER TO +--echo # + +--connection master +CREATE TABLE t1 (a int); +insert into t1 values (1); +--let $master_checksum= `CHECKSUM TABLE t1` +--sync_slave_with_master + +--connection slave +if ($master_checksum != `CHECKSUM TABLE t1`) +{ + die("Replica failed to pull data from primary after failed CHANGE MASTER TO"); +} + + +--echo # +--echo # Future CHANGE MASTER calls should succeed +--echo # + +--source include/stop_slave.inc +CHANGE MASTER TO MASTER_USE_GTID=SLAVE_POS; +--source include/start_slave.inc + + +--echo ######################## +--echo # Cleanup +--echo ######################## + +--connection master +DROP TABLE t1; + +--connection slave +--source include/stop_slave.inc +RESET SLAVE ALL; +--replace_result $MASTER_MYPORT MASTER_MYPORT +eval change master to master_port=$MASTER_MYPORT, master_host='127.0.0.1', master_user='root'; +--source include/start_slave.inc + +--disable_query_log +call mtr.add_suppression("Failed to locate old binlog or relay log files"); +--enable_query_log + +--source include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_reset_slave_all_clears_filters.test b/mysql-test/suite/rpl/t/rpl_reset_slave_all_clears_filters.test new file mode 100644 index 00000000000..7c01ce16586 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_reset_slave_all_clears_filters.test @@ -0,0 +1,72 @@ +# +# Purpose: +# This test validates that after issuing the `SLAVE RESET ALL` command, +# any corresponding IGNORE_DOMAIN_IDS/DO_DOMAIN_IDS and IGNORE_SERVER_IDS +# values are cleared. +# +# +# Methodology: +# To ensure the filtering variables are properly cleared after issuing +# SLAVE RESET ALL, we categorize different combinations of allowable input +# into three different options, and ensure that the variables are cleared for +# each category. The categories are as follows: +# Category 1) DO_DOMAIN_IDS and IGNORE_SERVER_IDS specified together +# Category 2) IGNORE_DOMAIN_IDS and IGNORE_SERVER_IDS specified together +# Category 3) Null check - edge case with all empty lists to ensure a lack +# of specification doesn't break anything +# +# To specify the values, the variables are set in `CHANGE MASTER TO`. To +# ensure the slave state is correct, we test the domain/server id filtering +# variable values at the following times while testing each category. +# +# Before CHANGE MASTER TO the filtering variables are tested to all be +# empty. +# +# After CHANGE MASTER TO the variables are tested to ensure they reflect +# those set in the CHANGE MASTER command. +# +# After RESET SLAVE ALL the filtering variables are tested to all be +# empty. +# + +--source include/master-slave.inc +--source include/have_debug.inc + +--connection slave +--source include/stop_slave.inc + +--echo # +--echo # Category 1) DO_DOMAIN_IDS and IGNORE_SERVER_IDS specified together +--echo # +--let $_do_domain_ids= (1) +--let $_ignore_domain_ids= () +--let $_ignore_server_ids= (3) +--source include/rpl_reset_slave_all_check.inc + +--echo # +--echo # Category 2) IGNORE_DOMAIN_IDS and IGNORE_SERVER_IDS specified together +--echo # +--let $_do_domain_ids= () +--let $_ignore_domain_ids= (2) +--let $_ignore_server_ids= (3) +--source include/rpl_reset_slave_all_check.inc + +--echo # +--echo # Category 3) Null check - edge case with all empty lists to ensure a +--echo # lack of specification doesn't break anything +--echo # +--let $_do_domain_ids= () +--let $_ignore_domain_ids= () +--let $_ignore_server_ids= () +--source include/rpl_reset_slave_all_check.inc + + +--echo ############################ +--echo # Cleanup +--echo ############################ +--connection slave +--replace_result $MASTER_MYPORT MASTER_MYPORT +eval change master to master_port=$MASTER_MYPORT, master_host='127.0.0.1', master_user='root'; +--source include/start_slave.inc + +--source include/rpl_end.inc diff --git a/sql/log.h b/sql/log.h index 0770861fe01..6896a4ff550 100644 --- a/sql/log.h +++ b/sql/log.h @@ -896,6 +896,20 @@ public: void unlock_binlog_end_pos() { mysql_mutex_unlock(&LOCK_binlog_end_pos); } mysql_mutex_t* get_binlog_end_pos_lock() { return &LOCK_binlog_end_pos; } + /* + Ensures the log's state is either LOG_OPEN or LOG_CLOSED. If something + failed along the desired path and left the log in invalid state, i.e. + LOG_TO_BE_OPENED, forces the state to be LOG_CLOSED. + */ + void try_fix_log_state() + { + mysql_mutex_lock(get_log_lock()); + /* Only change the log state if it is LOG_TO_BE_OPENED */ + if (log_state == LOG_TO_BE_OPENED) + log_state= LOG_CLOSED; + mysql_mutex_unlock(get_log_lock()); + } + int wait_for_update_binlog_end_pos(THD* thd, struct timespec * timeout); /* diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index 82a462d742b..8ed14962dd9 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -170,6 +170,8 @@ void Master_info::clear_in_memory_info(bool all) { port= MYSQL_PORT; host[0] = 0; user[0] = 0; password[0] = 0; + domain_id_filter.clear_ids(); + reset_dynamic(&ignore_server_ids); } } @@ -1788,6 +1790,12 @@ void Domain_id_filter::reset_filter() m_filter= false; } +void Domain_id_filter::clear_ids() +{ + reset_dynamic(&m_domain_ids[DO_DOMAIN_IDS]); + reset_dynamic(&m_domain_ids[IGNORE_DOMAIN_IDS]); +} + /** Update the do/ignore domain id filter lists. diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h index 12574285de0..e80c14fc340 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -78,6 +78,11 @@ public: */ void reset_filter(); + /* + Clear do_ids and ignore_ids to disable domain id filtering + */ + void clear_ids(); + /* Update the do/ignore domain id filter lists. diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 7ff0e27b008..d6d2dbc0d39 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -3840,6 +3840,16 @@ err: mi->unlock_slave_threads(); if (ret == FALSE) my_ok(thd); + else + { + /* + Depending on where CHANGE MASTER failed, the logs may be waiting to be + reopened. This would break future log updates and CHANGE MASTER calls. + `try_fix_log_state()` allows the relay log to fix its state to no longer + expect to be reopened. + */ + mi->rli.relay_log.try_fix_log_state(); + } DBUG_RETURN(ret); } -- cgit v1.2.1 From 27bf57fd6dcfbaf6a116570e861b272eeae0b43c Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Fri, 1 Oct 2021 14:46:22 +0200 Subject: MDEV-26299: Some views force server (and mysqldump) to generate invalid SQL for their definitions Do not print illegal table field names for non-top-level SELECT list, they will not be refered in any case but create problem for parsing of printed result. --- mysql-test/r/view.result | 11 +++++++++++ mysql-test/t/view.test | 19 +++++++++++++++++++ sql/sql_select.cc | 23 +++++++++++++++++++++-- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index bae415c17ea..001d26fc466 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -6876,5 +6876,16 @@ SELECT 1 FROM (SELECT count(((SELECT i1 FROM v1))) FROM v1) dt ; drop view v1; drop table t1; # +# MDEV-26299: Some views force server (and mysqldump) to generate +# invalid SQL for their definitions +# +create view v1 as +select * from +(select +"12345678901234567890123456789012345678901234567890123456789012345") as t1; +drop view v1; +CREATE VIEW v1 AS select `t1`.`12345678901234567890123456789012345678901234567890123456789012345` AS `Name_exp_1` from (select '12345678901234567890123456789012345678901234567890123456789012345') `t1`; +drop view v1; +# # End of 10.2 tests # diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 128fa853e10..e6e6ccce8bd 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -6608,6 +6608,25 @@ SELECT 1 FROM (SELECT count(((SELECT i1 FROM v1))) FROM v1) dt ; drop view v1; drop table t1; + +--echo # +--echo # MDEV-26299: Some views force server (and mysqldump) to generate +--echo # invalid SQL for their definitions +--echo # + +create view v1 as + select * from + (select + "12345678901234567890123456789012345678901234567890123456789012345") as t1; + +let $definition=`select VIEW_DEFINITION from information_schema.views where TABLE_NAME="v1"`; + +drop view v1; + +eval CREATE VIEW v1 AS $definition; + +drop view v1; + --echo # --echo # End of 10.2 tests --echo # diff --git a/sql/sql_select.cc b/sql/sql_select.cc index bf33623a684..54a2facfe9f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -25804,6 +25804,11 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) //Item List bool first= 1; + /* + outer_select() can not be used here because it is for name resolution + and will return NULL at any end of name resolution chain (view/derived) + */ + bool top_level= (get_master()->get_master() == 0); List_iterator_fast it(item_list); Item *item; while ((item= it++)) @@ -25813,7 +25818,8 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) else str->append(','); - if (is_subquery_function() && item->is_autogenerated_name) + if ((is_subquery_function() && item->is_autogenerated_name) || + !item->name) { /* Do not print auto-generated aliases in subqueries. It has no purpose @@ -25822,7 +25828,20 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) item->print(str, query_type); } else - item->print_item_w_name(str, query_type); + { + /* + Do not print illegal names (if it is not top level SELECT). + Top level view checked (and correct name are assigned), + other cases of top level SELECT are not important, because + it is not "table field". + */ + if (top_level || + !item->is_autogenerated_name || + !check_column_name(item->name)) + item->print_item_w_name(str, query_type); + else + item->print(str, query_type); + } } /* -- cgit v1.2.1 From 5316703141c00103d8f4155cf4672b1f74ca0c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 19 Oct 2021 08:46:16 +0300 Subject: MDEV-14804 innodb.update_time failed in buildbot with wrong result Let us use a minimal-size buffer pool to ensure that page flushing will be slow enough so that LRU eviction cannot be avoided. --- mysql-test/suite/innodb/r/update_time.result | 3 +-- mysql-test/suite/innodb/t/update_time-master.opt | 2 +- mysql-test/suite/innodb/t/update_time.test | 14 ++------------ 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/mysql-test/suite/innodb/r/update_time.result b/mysql-test/suite/innodb/r/update_time.result index d8b9069b1ae..96d1af4c09c 100644 --- a/mysql-test/suite/innodb/r/update_time.result +++ b/mysql-test/suite/innodb/r/update_time.result @@ -24,8 +24,7 @@ SELECT COUNT(*) FROM information_schema.innodb_buffer_page WHERE table_name = '`test`.`t`'; COUNT(*) 1 -# INSERT lots of data in table 'big': begin -# INSERT lots of data in table 'big': end +INSERT INTO big SELECT REPEAT('a', 1024) FROM seq_1_to_10240; SELECT COUNT(*) FROM information_schema.innodb_buffer_page WHERE table_name = '`test`.`t`'; COUNT(*) diff --git a/mysql-test/suite/innodb/t/update_time-master.opt b/mysql-test/suite/innodb/t/update_time-master.opt index 9f283a9503f..f0fd647546d 100644 --- a/mysql-test/suite/innodb/t/update_time-master.opt +++ b/mysql-test/suite/innodb/t/update_time-master.opt @@ -1 +1 @@ ---innodb-buffer-pool-size=10M +--innodb-buffer-pool-size=5M diff --git a/mysql-test/suite/innodb/t/update_time.test b/mysql-test/suite/innodb/t/update_time.test index a95c5171e9b..fd1e082f5f2 100644 --- a/mysql-test/suite/innodb/t/update_time.test +++ b/mysql-test/suite/innodb/t/update_time.test @@ -10,6 +10,7 @@ -- source include/not_embedded.inc # This test is slow on buildbot. --source include/big_test.inc +--source include/have_sequence.inc CREATE TABLE t (a INT) ENGINE=INNODB; @@ -33,18 +34,7 @@ SELECT COUNT(*) FROM information_schema.innodb_buffer_page WHERE table_name = '`test`.`t`'; # evict table 't' by inserting as much data as the BP size itself --- echo # INSERT lots of data in table 'big': begin --- disable_query_log -BEGIN; --- let $i = 10240 -while ($i) -{ - INSERT INTO big VALUES (REPEAT('a', 1024)); - dec $i; -} -COMMIT; --- enable_query_log --- echo # INSERT lots of data in table 'big': end +INSERT INTO big SELECT REPEAT('a', 1024) FROM seq_1_to_10240; # confirm that all pages for table 't' have been evicted SELECT COUNT(*) FROM information_schema.innodb_buffer_page -- cgit v1.2.1 From 1a54cf62f85044a7686dc3becfe39e6b52209484 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Mon, 11 Oct 2021 15:05:44 +0400 Subject: MDEV-24585 Assertion `je->s.cs == nice_js->charset()' failed in json_nice. We should set the charset in Item_func_json_format::fix_length_and_dec(). --- mysql-test/r/func_json.result | 6 ++++++ mysql-test/t/func_json.test | 6 ++++++ sql/item_jsonfunc.cc | 1 + 3 files changed, 13 insertions(+) diff --git a/mysql-test/r/func_json.result b/mysql-test/r/func_json.result index 8f014c89733..f8e78c79f5d 100644 --- a/mysql-test/r/func_json.result +++ b/mysql-test/r/func_json.result @@ -934,5 +934,11 @@ CAST(JSON_EXTRACT('{"x":false}', '$.x') AS DECIMAL) AS cd; cf cd 0 0 # +# MDEV-24585 Assertion `je->s.cs == nice_js->charset()' failed in json_nice. +# +SELECT JSON_REPLACE( JSON_DETAILED('["x"]'), '$.a', 'xx' ); +JSON_REPLACE( JSON_DETAILED('["x"]'), '$.a', 'xx' ) +["x"] +# # End of 10.2 tests # diff --git a/mysql-test/t/func_json.test b/mysql-test/t/func_json.test index 805e9954b81..e4e093225f8 100644 --- a/mysql-test/t/func_json.test +++ b/mysql-test/t/func_json.test @@ -554,6 +554,12 @@ SELECT CAST(JSON_EXTRACT('{"x":false}', '$.x') AS DECIMAL) AS cd; +--echo # +--echo # MDEV-24585 Assertion `je->s.cs == nice_js->charset()' failed in json_nice. +--echo # + +SELECT JSON_REPLACE( JSON_DETAILED('["x"]'), '$.a', 'xx' ); + --echo # --echo # End of 10.2 tests --echo # diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 7db1ae1ffaf..ea70fbeebdb 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -3534,6 +3534,7 @@ const char *Item_func_json_format::func_name() const bool Item_func_json_format::fix_length_and_dec() { decimals= 0; + collation.set(args[0]->collation); max_length= args[0]->max_length; maybe_null= 1; return FALSE; -- cgit v1.2.1 From f502ccbcb5dfce29067434885a23db8d1bd5f134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 15 Oct 2021 16:51:05 +0300 Subject: Link with libatomic to enable C11 atomics support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some architectures (mips) require libatomic to support proper atomic operations. Check first if support is available without linking, otherwise use the library. Contributors: James Cowgill Jessica Clarke VicenÈ›iu Ciorbaru --- configure.cmake | 20 +++++++++++++++++++- mysys/CMakeLists.txt | 4 ++++ sql/CMakeLists.txt | 1 - 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/configure.cmake b/configure.cmake index 7a1369d7770..db8742bb93b 100644 --- a/configure.cmake +++ b/configure.cmake @@ -895,7 +895,25 @@ int main() long long int *ptr= &var; return (int)__atomic_load_n(ptr, __ATOMIC_SEQ_CST); }" -HAVE_GCC_C11_ATOMICS) +HAVE_GCC_C11_ATOMICS_WITHOUT_LIBATOMIC) +IF (HAVE_GCC_C11_ATOMICS_WITHOUT_LIBATOMIC) + SET(HAVE_GCC_C11_ATOMICS True) +ELSE() + SET(OLD_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + LIST(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") + CHECK_CXX_SOURCE_COMPILES(" + int main() + { + long long int var= 1; + long long int *ptr= &var; + return (int)__atomic_load_n(ptr, __ATOMIC_SEQ_CST); + }" + HAVE_GCC_C11_ATOMICS_WITH_LIBATOMIC) + IF(HAVE_GCC_C11_ATOMICS_WITH_LIBATOMIC) + SET(HAVE_GCC_C11_ATOMICS True) + ENDIF() + SET(CMAKE_REQUIRED_LIBRARIES ${OLD_CMAKE_REQUIRED_LIBRARIES}) +ENDIF() IF(WITH_VALGRIND) SET(HAVE_valgrind 1) diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index f97e3b4d390..09d3f726ffc 100644 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -78,6 +78,10 @@ TARGET_LINK_LIBRARIES(mysys dbug strings ${ZLIB_LIBRARY} ${LIBNSL} ${LIBM} ${LIBRT} ${CMAKE_DL_LIBS} ${LIBSOCKET} ${LIBEXECINFO} ${CRC32_LIBRARY}) DTRACE_INSTRUMENT(mysys) +IF (HAVE_GCC_C11_ATOMICS_WITH_LIBATOMIC) + TARGET_LINK_LIBRARIES(mysys atomic) +ENDIF() + IF(HAVE_BFD_H) TARGET_LINK_LIBRARIES(mysys bfd) ENDIF(HAVE_BFD_H) diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 5f5d7daf1a5..f574f1f2029 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -307,7 +307,6 @@ IF(WITH_MYSQLD_LDFLAGS) "${MYSQLD_LINK_FLAGS} ${WITH_MYSQLD_LDFLAGS}") ENDIF() - FIND_PACKAGE(BISON 2.0) -- cgit v1.2.1 From a33c1082dab7ab4b08acf957d6364be95e4c6a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 15 Oct 2021 17:06:17 +0300 Subject: Fix MIPS build failure: Handle unaligned buffers in connect's TYPBLK class On MIPS platforms (and probably others) unaligned memory access results in a bus error. In the connect storage engine, block data for some data formats is stored packed in memory and the TYPBLK class is used to read values from it. Since TYPBLK does not have special handling for this packed memory, it can quite easily result in unaligned memory accesses. The simple way to fix this is to perform all accesses to the main buffer through memcpy. With GCC and optimizations turned on, this call to memcpy is completely optimized away on architectures where unaligned accesses are ok (like x86). Contributors: James Cowgill --- storage/connect/valblk.cpp | 41 +++++++++++++++++++------------------ storage/connect/valblk.h | 50 ++++++++++++++++++++++++++++------------------ 2 files changed, 51 insertions(+), 40 deletions(-) diff --git a/storage/connect/valblk.cpp b/storage/connect/valblk.cpp index a993c1371c2..8702f606a8f 100644 --- a/storage/connect/valblk.cpp +++ b/storage/connect/valblk.cpp @@ -268,14 +268,14 @@ bool TYPBLK::Init(PGLOBAL g, bool check) template char *TYPBLK::GetCharString(char *p, int n) { - sprintf(p, Fmt, Typp[n]); + sprintf(p, Fmt, UnalignedRead(n)); return p; } // end of GetCharString template <> char *TYPBLK::GetCharString(char *p, int n) { - sprintf(p, Fmt, Prec, Typp[n]); + sprintf(p, Fmt, Prec, UnalignedRead(n)); return p; } // end of GetCharString @@ -291,7 +291,7 @@ void TYPBLK::SetValue(PVAL valp, int n) ChkTyp(valp); if (!(b = valp->IsNull())) - Typp[n] = GetTypedValue(valp); + UnalignedWrite(n, GetTypedValue(valp)); else Reset(n); @@ -353,9 +353,9 @@ void TYPBLK::SetValue(PCSZ p, int n) ulonglong val = CharToNumber(p, strlen(p), maxval, Unsigned, &minus); if (minus && val < maxval) - Typp[n] = (TYPE)(-(signed)val); + UnalignedWrite(n, (TYPE)(-(signed)val)); else - Typp[n] = (TYPE)val; + UnalignedWrite(n, (TYPE)val); SetNull(n, false); } // end of SetValue @@ -398,7 +398,7 @@ void TYPBLK::SetValue(PCSZ p, int n) throw Type; } // endif Check - Typp[n] = atof(p); + UnalignedWrite(n, atof(p)); SetNull(n, false); } // end of SetValue @@ -430,7 +430,7 @@ void TYPBLK::SetValue(PVBLK pv, int n1, int n2) ChkTyp(pv); if (!(b = pv->IsNull(n2) && Nullable)) - Typp[n1] = GetTypedValue(pv, n2); + UnalignedWrite(n1, GetTypedValue(pv, n2)); else Reset(n1); @@ -481,10 +481,10 @@ void TYPBLK::SetMin(PVAL valp, int n) { CheckParms(valp, n) TYPE tval = GetTypedValue(valp); - TYPE& tmin = Typp[n]; + TYPE tmin = UnalignedRead(n); if (tval < tmin) - tmin = tval; + UnalignedWrite(n, tval); } // end of SetMin @@ -496,10 +496,10 @@ void TYPBLK::SetMax(PVAL valp, int n) { CheckParms(valp, n) TYPE tval = GetTypedValue(valp); - TYPE& tmin = Typp[n]; + TYPE tmin = UnalignedRead(n); if (tval > tmin) - tmin = tval; + UnalignedWrite(n, tval); } // end of SetMax @@ -513,8 +513,7 @@ void TYPBLK::SetValues(PVBLK pv, int k, int n) CheckType(pv) TYPE *lp = ((TYPBLK*)pv)->Typp; - for (int i = k; i < n; i++) // TODO - Typp[i] = lp[i]; + memcpy(Typp + k, lp + k, sizeof(TYPE) * n); } // end of SetValues #endif // 0 @@ -525,7 +524,7 @@ void TYPBLK::SetValues(PVBLK pv, int k, int n) template void TYPBLK::Move(int i, int j) { - Typp[j] = Typp[i]; + UnalignedWrite(j, UnalignedRead(i)); MoveNull(i, j); } // end of Move @@ -539,7 +538,7 @@ int TYPBLK::CompVal(PVAL vp, int n) ChkIndx(n); ChkTyp(vp); #endif // _DEBUG - TYPE mlv = Typp[n]; + TYPE mlv = UnalignedRead(n); TYPE vlv = GetTypedValue(vp); return (vlv > mlv) ? 1 : (vlv < mlv) ? (-1) : 0; @@ -551,8 +550,8 @@ int TYPBLK::CompVal(PVAL vp, int n) template int TYPBLK::CompVal(int i1, int i2) { - TYPE lv1 = Typp[i1]; - TYPE lv2 = Typp[i2]; + TYPE lv1 = UnalignedRead(i1); + TYPE lv2 = UnalignedRead(i2); return (lv1 > lv2) ? 1 : (lv1 < lv2) ? (-1) : 0; } // end of CompVal @@ -589,7 +588,7 @@ int TYPBLK::Find(PVAL vp) TYPE n = GetTypedValue(vp); for (i = 0; i < Nval; i++) - if (n == Typp[i]) + if (n == UnalignedRead(i)) break; return (i < Nval) ? i : (-1); @@ -605,7 +604,7 @@ int TYPBLK::GetMaxLength(void) int i, n, m; for (i = n = 0; i < Nval; i++) { - m = sprintf(buf, Fmt, Typp[i]); + m = sprintf(buf, Fmt, UnalignedRead(i)); n = MY_MAX(n, m); } // endfor i @@ -1335,7 +1334,7 @@ char *DATBLK::GetCharString(char *p, int n) char *vp; if (Dvalp) { - Dvalp->SetValue(Typp[n]); + Dvalp->SetValue(UnalignedRead(n)); vp = Dvalp->GetCharString(p); } else vp = TYPBLK::GetCharString(p, n); @@ -1351,7 +1350,7 @@ void DATBLK::SetValue(PCSZ p, int n) if (Dvalp) { // Decode the string according to format Dvalp->SetValue_psz(p); - Typp[n] = Dvalp->GetIntValue(); + UnalignedWrite(n, Dvalp->GetIntValue()); } else TYPBLK::SetValue(p, n); diff --git a/storage/connect/valblk.h b/storage/connect/valblk.h index 568fc172c6a..537e838c99f 100644 --- a/storage/connect/valblk.h +++ b/storage/connect/valblk.h @@ -151,40 +151,41 @@ class TYPBLK : public VALBLK { // Implementation virtual bool Init(PGLOBAL g, bool check); virtual int GetVlen(void) {return sizeof(TYPE);} - virtual char GetTinyValue(int n) {return (char)Typp[n];} - virtual uchar GetUTinyValue(int n) {return (uchar)Typp[n];} - virtual short GetShortValue(int n) {return (short)Typp[n];} - virtual ushort GetUShortValue(int n) {return (ushort)Typp[n];} - virtual int GetIntValue(int n) {return (int)Typp[n];} - virtual uint GetUIntValue(int n) {return (uint)Typp[n];} - virtual longlong GetBigintValue(int n) {return (longlong)Typp[n];} - virtual ulonglong GetUBigintValue(int n) {return (ulonglong)Typp[n];} - virtual double GetFloatValue(int n) {return (double)Typp[n];} + + virtual char GetTinyValue(int n) {return (char)UnalignedRead(n);} + virtual uchar GetUTinyValue(int n) {return (uchar)UnalignedRead(n);} + virtual short GetShortValue(int n) {return (short)UnalignedRead(n);} + virtual ushort GetUShortValue(int n) {return (ushort)UnalignedRead(n);} + virtual int GetIntValue(int n) {return (int)UnalignedRead(n);} + virtual uint GetUIntValue(int n) {return (uint)UnalignedRead(n);} + virtual longlong GetBigintValue(int n) {return (longlong)UnalignedRead(n);} + virtual ulonglong GetUBigintValue(int n) {return (ulonglong)UnalignedRead(n);} + virtual double GetFloatValue(int n) {return (double)UnalignedRead(n);} virtual char *GetCharString(char *p, int n); - virtual void Reset(int n) {Typp[n] = 0;} + virtual void Reset(int n) {UnalignedWrite(n, 0);} // Methods using VALBLK::SetValue; virtual void SetValue(PCSZ sp, int n); virtual void SetValue(const char *sp, uint len, int n); virtual void SetValue(short sval, int n) - {Typp[n] = (TYPE)sval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)sval); SetNull(n, false);} virtual void SetValue(ushort sval, int n) - {Typp[n] = (TYPE)sval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)sval); SetNull(n, false);} virtual void SetValue(int lval, int n) - {Typp[n] = (TYPE)lval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)lval); SetNull(n, false);} virtual void SetValue(uint lval, int n) - {Typp[n] = (TYPE)lval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)lval); SetNull(n, false);} virtual void SetValue(longlong lval, int n) - {Typp[n] = (TYPE)lval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)lval); SetNull(n, false);} virtual void SetValue(ulonglong lval, int n) - {Typp[n] = (TYPE)lval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)lval); SetNull(n, false);} virtual void SetValue(double fval, int n) - {Typp[n] = (TYPE)fval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)fval); SetNull(n, false);} virtual void SetValue(char cval, int n) - {Typp[n] = (TYPE)cval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)cval); SetNull(n, false);} virtual void SetValue(uchar cval, int n) - {Typp[n] = (TYPE)cval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)cval); SetNull(n, false);} virtual void SetValue(PVAL valp, int n); virtual void SetValue(PVBLK pv, int n1, int n2); virtual void SetMin(PVAL valp, int n); @@ -206,6 +207,17 @@ class TYPBLK : public VALBLK { // Members TYPE* const &Typp; const char *Fmt; + + // Unaligned access + TYPE UnalignedRead(int n) const { + TYPE result; + memcpy(&result, Typp + n, sizeof(TYPE)); + return result; + } + + void UnalignedWrite(int n, TYPE value) { + memcpy(Typp + n, &value, sizeof(TYPE)); + } }; // end of class TYPBLK /***********************************************************************/ -- cgit v1.2.1 From 1388845e048011932a6d715936a781479c5e6af3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Tue, 19 Oct 2021 19:20:23 +0300 Subject: Fix Groonga crash on MIPS: Correctly link to libatomic MIPS (and possibly other) platforms require linking against libatomic to support 64-bit atomic integers. Groonga was failing to do so and all related tests were failing with an atomics relocation error on MIPS. Contributors: James Cowgill --- storage/mroonga/vendor/groonga/lib/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/storage/mroonga/vendor/groonga/lib/CMakeLists.txt b/storage/mroonga/vendor/groonga/lib/CMakeLists.txt index 8c71563f722..4f076458a36 100644 --- a/storage/mroonga/vendor/groonga/lib/CMakeLists.txt +++ b/storage/mroonga/vendor/groonga/lib/CMakeLists.txt @@ -89,7 +89,12 @@ else() endif() set_target_properties(libgroonga PROPERTIES OUTPUT_NAME "groonga") +if (HAVE_GCC_C11_ATOMICS_WITH_LIBATOMIC) + set(ATOMIC_LIBS atomic) +endif() + set(GRN_ALL_LIBRARIES + ${ATOMIC_LIBS} ${EXECINFO_LIBS} ${RT_LIBS} ${PTHREAD_LIBS} -- cgit v1.2.1 From a8401ad5afd766d2febc63a8bcfedda2978ff44c Mon Sep 17 00:00:00 2001 From: Nikita Malyavin Date: Wed, 21 Jul 2021 15:42:21 +0300 Subject: restore default.test, default.result after MDEV-23597 c47e4aab62c65 commit --- mysql-test/r/default.result | 4 ++-- mysql-test/t/default.test | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/default.result b/mysql-test/r/default.result index 1a35a29aa7f..c7179e29723 100644 --- a/mysql-test/r/default.result +++ b/mysql-test/r/default.result @@ -3089,8 +3089,8 @@ DROP TABLE t1; # # Collations # -CREATE TABLE t1 (a VARCHAR(20) CHARACTER SET latin1 DEFAULT CONCAT('�')) CHARACTER SET koi8r COLLATE koi8r_bin; -ERROR 22007: Encountered illegal value '�' when converting to koi8r +CREATE TABLE t1 (a VARCHAR(20) CHARACTER SET latin1 DEFAULT CONCAT('ö')) CHARACTER SET koi8r COLLATE koi8r_bin; +ERROR 22007: Encountered illegal value 'ö' when converting to koi8r CREATE OR REPLACE TABLE t1 (a char(2) default concat('A') COLLATE utf8mb4_unicode_ci); SHOW CREATE TABLE t1; Table Create Table diff --git a/mysql-test/t/default.test b/mysql-test/t/default.test index 472b1fa4796..a4fe74aefb5 100644 --- a/mysql-test/t/default.test +++ b/mysql-test/t/default.test @@ -1855,7 +1855,7 @@ DROP TABLE t1; --echo # --error ER_BAD_DATA -CREATE TABLE t1 (a VARCHAR(20) CHARACTER SET latin1 DEFAULT CONCAT('�')) CHARACTER SET koi8r COLLATE koi8r_bin; +CREATE TABLE t1 (a VARCHAR(20) CHARACTER SET latin1 DEFAULT CONCAT('ö')) CHARACTER SET koi8r COLLATE koi8r_bin; CREATE OR REPLACE TABLE t1 (a char(2) default concat('A') COLLATE utf8mb4_unicode_ci); SHOW CREATE TABLE t1; DROP TABLE t1; -- cgit v1.2.1 From 1811fd51fbae9e6c1f06ce93faef2bf1279cd3b6 Mon Sep 17 00:00:00 2001 From: Nikita Malyavin Date: Mon, 2 Aug 2021 14:24:54 +0300 Subject: MDEV-26262 frm is corrupted after ER_EXPRESSION_REFERS_TO_UNINIT_FIELD This is a duplicate of MDEV-18278 89936f11e965, but I will add an additional assertion Description: The frm corruption should not be reported during CREATE TABLE. Normally it doesn't, and the data to fill TABLE is taken by open_table_from_share call. However, the vcol data is stored as SQL string in table->s->vcol_defs.str and is anyway parsed on each table open. It is impossible [or hard] to avoid, because it's hard to clone the expression tree in general (it's easier to parse). Normally parse_vcol_defs should only fail on semantic errors. If so, error_reported is set to true. Any other failure is not expected during table creation. There is either unhandled/unacknowledged error, or something went really wrong, like memory reject. This all should be asserted anyway. Solution: * Set *error_reported=true for the forward references check; * Assert for every unacknowledged error during table creation. --- mysql-test/suite/gcol/inc/gcol_column_def_options.inc | 11 +++++++++++ mysql-test/suite/gcol/r/gcol_column_def_options_innodb.result | 6 ++++++ mysql-test/suite/gcol/r/gcol_column_def_options_myisam.result | 6 ++++++ sql/table.cc | 3 +++ 4 files changed, 26 insertions(+) diff --git a/mysql-test/suite/gcol/inc/gcol_column_def_options.inc b/mysql-test/suite/gcol/inc/gcol_column_def_options.inc index f4350d25ae9..17e926758ee 100644 --- a/mysql-test/suite/gcol/inc/gcol_column_def_options.inc +++ b/mysql-test/suite/gcol/inc/gcol_column_def_options.inc @@ -585,3 +585,14 @@ ALTER TABLE t1 ALTER COLUMN a SET DEFAULT 7, --disable_info DROP TABLE t1; --enable_warnings + +--echo # +--echo # MDEV-26262 frm is corrupted after ER_EXPRESSION_REFERS_TO_UNINIT_FIELD +--echo # + +--error ER_EXPRESSION_REFERS_TO_UNINIT_FIELD +CREATE TABLE MDEV_26262 (a INT,b INT AS (b) VIRTUAL); + +--let SEARCH_FILE=$MYSQLTEST_VARDIR/log/mysqld.1.err +--let SEARCH_PATTERN=Incorrect information in file: './test/MDEV_26262.frm' +--source include/search_pattern_in_file.inc diff --git a/mysql-test/suite/gcol/r/gcol_column_def_options_innodb.result b/mysql-test/suite/gcol/r/gcol_column_def_options_innodb.result index 48c4613f6ca..2c0adc3d493 100644 --- a/mysql-test/suite/gcol/r/gcol_column_def_options_innodb.result +++ b/mysql-test/suite/gcol/r/gcol_column_def_options_innodb.result @@ -699,6 +699,12 @@ ADD COLUMN c INT AS (1 + DEFAULT(a)) VIRTUAL; affected rows: 1 info: Records: 1 Duplicates: 0 Warnings: 0 DROP TABLE t1; +# +# MDEV-26262 frm is corrupted after ER_EXPRESSION_REFERS_TO_UNINIT_FIELD +# +CREATE TABLE MDEV_26262 (a INT,b INT AS (b) VIRTUAL); +ERROR 01000: Expression for field `b` is referring to uninitialized field `b` +NOT FOUND /Incorrect information in file: './test/MDEV_26262.frm'/ in mysqld.1.err DROP VIEW IF EXISTS v1,v2; DROP TABLE IF EXISTS t1,t2,t3; DROP PROCEDURE IF EXISTS p1; diff --git a/mysql-test/suite/gcol/r/gcol_column_def_options_myisam.result b/mysql-test/suite/gcol/r/gcol_column_def_options_myisam.result index b7ae6488c95..126a6c5e271 100644 --- a/mysql-test/suite/gcol/r/gcol_column_def_options_myisam.result +++ b/mysql-test/suite/gcol/r/gcol_column_def_options_myisam.result @@ -699,6 +699,12 @@ ADD COLUMN c INT AS (1 + DEFAULT(a)) VIRTUAL; affected rows: 1 info: Records: 1 Duplicates: 0 Warnings: 0 DROP TABLE t1; +# +# MDEV-26262 frm is corrupted after ER_EXPRESSION_REFERS_TO_UNINIT_FIELD +# +CREATE TABLE MDEV_26262 (a INT,b INT AS (b) VIRTUAL); +ERROR 01000: Expression for field `b` is referring to uninitialized field `b` +NOT FOUND /Incorrect information in file: './test/MDEV_26262.frm'/ in mysqld.1.err DROP VIEW IF EXISTS v1,v2; DROP TABLE IF EXISTS t1,t2,t3; DROP PROCEDURE IF EXISTS p1; diff --git a/sql/table.cc b/sql/table.cc index d4f8170e0af..349d04a60ed 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3282,6 +3282,9 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, &error_reported, mode)) { error= OPEN_FRM_CORRUPTED; + // parse_vcol_defs may fail by semantic reasons, which is ok, but the + // real corruption should never be reported during table creation + DBUG_ASSERT(!is_create_table || !error_reported); goto err; } -- cgit v1.2.1 From caebe151c18a3415466cca88dbdacb8ec9597a29 Mon Sep 17 00:00:00 2001 From: Nikita Malyavin Date: Thu, 7 Oct 2021 17:02:26 +0300 Subject: MDEV-22445 Crash on HANDLER READ NEXT after XA PREPARE The assertion is absolutely correct since no data access is possible after XA PREPARE. The check is added in mysql_ha_read. --- mysql-test/r/xa.result | 15 +++++++++++++++ mysql-test/t/xa.test | 17 +++++++++++++++++ sql/sql_handler.cc | 3 +++ 3 files changed, 35 insertions(+) diff --git a/mysql-test/r/xa.result b/mysql-test/r/xa.result index 4e4d7bc6048..f6bae2adac2 100644 --- a/mysql-test/r/xa.result +++ b/mysql-test/r/xa.result @@ -341,5 +341,20 @@ Message XAER_RMFAIL: The command cannot be executed when global transaction is i xa commit 'foo'; drop table t1; # +# MDEV-22445 Crash on HANDLER READ NEXT after XA PREPARE +# +CREATE TABLE t (a INT KEY) ENGINE=InnoDB; +HANDLER t OPEN AS t; +XA START '0'; +SELECT * FROM t; +a +XA END '0'; +XA PREPARE '0'; +HANDLER t READ NEXT; +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +# Cleanup +XA COMMIT '0'; +DROP TABLE t; +# # End of 10.2 tests # diff --git a/mysql-test/t/xa.test b/mysql-test/t/xa.test index b13d12d2019..176b8c3189b 100644 --- a/mysql-test/t/xa.test +++ b/mysql-test/t/xa.test @@ -485,6 +485,23 @@ show status like 'foo'; xa commit 'foo'; drop table t1; +--echo # +--echo # MDEV-22445 Crash on HANDLER READ NEXT after XA PREPARE +--echo # + +CREATE TABLE t (a INT KEY) ENGINE=InnoDB; +HANDLER t OPEN AS t; +XA START '0'; +SELECT * FROM t; +XA END '0'; +XA PREPARE '0'; +--error ER_XAER_RMFAIL +HANDLER t READ NEXT; + +--echo # Cleanup +XA COMMIT '0'; +DROP TABLE t; + --echo # --echo # End of 10.2 tests --echo # diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 7c2122b6a0a..ec3756eceba 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -697,6 +697,9 @@ retry: if (!(handler= mysql_ha_find_handler(thd, tables->alias))) goto err0; + if (thd->transaction.xid_state.check_has_uncommitted_xa()) + goto err0; + table= handler->table; tables->table= table; // This is used by fix_fields table->pos_in_table_list= tables; -- cgit v1.2.1 From b06e8167a7b8f9ae9cf2f7c19054ecc9c0c78398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 20 Oct 2021 15:54:25 +0300 Subject: MDEV-22627 Failing assertion: dict_tf2_is_valid(flags, flags2) create_table_info_t::innobase_table_flags(): Refuse to create a PAGE_COMPRESSED table with PAGE_COMPRESSION_LEVEL=0 if also innodb_compression_level=0. The parameter value innodb_compression_level=0 was only somewhat meaningful for testing or debugging ROW_FORMAT=COMPRESSED tables. For the page_compressed format, it never made any sense, and the check in dict_tf_is_valid_not_redundant() that was added in 72378a25830184f91005be7e80cfb28381c79f23 (MDEV-12873) would cause the server to crash. --- .../r/default_row_format_create,redundant.rdiff | 9 ++++++++ .../innodb/r/default_row_format_create.result | 15 ++++++++++++++ .../suite/innodb/t/default_row_format_create.test | 16 +++++++++++++++ storage/innobase/handler/ha_innodb.cc | 24 ++++++++++++++-------- 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/mysql-test/suite/innodb/r/default_row_format_create,redundant.rdiff b/mysql-test/suite/innodb/r/default_row_format_create,redundant.rdiff index 29bc560907e..0e94bfd1c0f 100644 --- a/mysql-test/suite/innodb/r/default_row_format_create,redundant.rdiff +++ b/mysql-test/suite/innodb/r/default_row_format_create,redundant.rdiff @@ -20,3 +20,12 @@ SET @save_format = @@GLOBAL.innodb_default_row_format; SET GLOBAL innodb_default_row_format = redundant; CREATE TABLE t1 (c1 INT) ENGINE=InnoDB; +@@ -49,7 +49,7 @@ + ERROR HY000: Can't create table `test`.`t` (errno: 140 "Wrong create options") + SHOW WARNINGS; + Level Code Message +-Warning 1478 InnoDB: PAGE_COMPRESSED requires PAGE_COMPRESSION_LEVEL or innodb_compression_level > 0 ++Warning 140 InnoDB: PAGE_COMPRESSED table can't have ROW_TYPE=REDUNDANT + Error 1005 Can't create table `test`.`t` (errno: 140 "Wrong create options") + Warning 1030 Got error 140 "Wrong create options" from storage engine InnoDB + CREATE TABLE t(c INT) ENGINE=InnoDB page_compressed=1 page_compression_level=1; diff --git a/mysql-test/suite/innodb/r/default_row_format_create.result b/mysql-test/suite/innodb/r/default_row_format_create.result index d51a63dbd01..d85fed35d92 100644 --- a/mysql-test/suite/innodb/r/default_row_format_create.result +++ b/mysql-test/suite/innodb/r/default_row_format_create.result @@ -42,3 +42,18 @@ SHOW TABLE STATUS LIKE 't1'; Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment t1 InnoDB # Redundant # # # # # # NULL # NULL NULL latin1_swedish_ci NULL DROP TABLE t1; +SET @save_level=@@GLOBAL.innodb_compression_level; +SET GLOBAL innodb_compression_level=0; +CREATE TABLE t(c INT) ENGINE=InnoDB page_compressed=1; +ERROR HY000: Can't create table `test`.`t` (errno: 140 "Wrong create options") +SHOW WARNINGS; +Level Code Message +Warning 1478 InnoDB: PAGE_COMPRESSED requires PAGE_COMPRESSION_LEVEL or innodb_compression_level > 0 +Error 1005 Can't create table `test`.`t` (errno: 140 "Wrong create options") +Warning 1030 Got error 140 "Wrong create options" from storage engine InnoDB +CREATE TABLE t(c INT) ENGINE=InnoDB page_compressed=1 page_compression_level=1; +DROP TABLE IF EXISTS t; +SET GLOBAL innodb_compression_level=1; +CREATE TABLE t(c INT) ENGINE=InnoDB page_compressed=1; +DROP TABLE IF EXISTS t; +SET GLOBAL innodb_compression_level=@save_level; diff --git a/mysql-test/suite/innodb/t/default_row_format_create.test b/mysql-test/suite/innodb/t/default_row_format_create.test index 03a7ebd3752..a1c6ff0d588 100644 --- a/mysql-test/suite/innodb/t/default_row_format_create.test +++ b/mysql-test/suite/innodb/t/default_row_format_create.test @@ -44,3 +44,19 @@ TRUNCATE TABLE t1; --replace_column 3 # 5 # 6 # 7 # 8 # 9 # 10 # 12 # SHOW TABLE STATUS LIKE 't1'; DROP TABLE t1; + +SET @save_level=@@GLOBAL.innodb_compression_level; +SET GLOBAL innodb_compression_level=0; +--error ER_CANT_CREATE_TABLE +CREATE TABLE t(c INT) ENGINE=InnoDB page_compressed=1; +SHOW WARNINGS; +--disable_warnings +--error 0,ER_CANT_CREATE_TABLE +CREATE TABLE t(c INT) ENGINE=InnoDB page_compressed=1 page_compression_level=1; +DROP TABLE IF EXISTS t; +SET GLOBAL innodb_compression_level=1; +--error 0,ER_CANT_CREATE_TABLE +CREATE TABLE t(c INT) ENGINE=InnoDB page_compressed=1; +DROP TABLE IF EXISTS t; +--enable_warnings +SET GLOBAL innodb_compression_level=@save_level; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index fefd0bdde00..592492cac60 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -11991,10 +11991,6 @@ bool create_table_info_t::innobase_table_flags() modified by another thread while the table is being created. */ const ulint file_format_allowed = srv_file_format; - /* Cache the value of innobase_compression_level, in case it is - modified by another thread while the table is being created. */ - const ulint default_compression_level = page_zip_level; - ha_table_option_struct *options= m_form->s->option_struct; m_flags = 0; @@ -12199,13 +12195,23 @@ index_bad: m_flags2 |= DICT_TF2_USE_FILE_PER_TABLE; } + ulint level = options->page_compression_level; + if (!level) { + level = page_zip_level; + if (!level && options->page_compressed) { + push_warning_printf( + m_thd, Sql_condition::WARN_LEVEL_WARN, + ER_ILLEGAL_HA_CREATE_OPTION, + "InnoDB: PAGE_COMPRESSED requires" + " PAGE_COMPRESSION_LEVEL or" + " innodb_compression_level > 0"); + DBUG_RETURN(false); + } + } + /* Set the table flags */ dict_tf_set(&m_flags, innodb_row_format, zip_ssize, - m_use_data_dir, - options->page_compressed, - options->page_compression_level == 0 ? - default_compression_level : static_cast(options->page_compression_level), - 0); + m_use_data_dir, options->page_compressed, level, 0); /* Set the flags2 when create table or alter tables */ m_flags2 |= DICT_TF2_FTS_AUX_HEX_NAME; -- cgit v1.2.1 From 69b3de830d531e5cbc57c1a43c7bd55b31f7197e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 20 Oct 2021 15:55:27 +0300 Subject: Update libmariadb --- libmariadb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmariadb b/libmariadb index b99172386a7..735a7299dba 160000 --- a/libmariadb +++ b/libmariadb @@ -1 +1 @@ -Subproject commit b99172386a740ef0c8136e9a6cd7d9ad9a77b31f +Subproject commit 735a7299dbae19cc2b82b9697becaf90e9b43047 -- cgit v1.2.1 From 05c3dced8611542df7eae686ab9d28147f0dbe05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 20 Oct 2021 22:16:23 +0300 Subject: MDEV-22627 fixup: Cover also ALTER TABLE...ALGORITHM=INPLACE --- mysql-test/suite/innodb/r/default_row_format_create.result | 8 ++++++++ mysql-test/suite/innodb/t/default_row_format_create.test | 9 +++++++++ storage/innobase/handler/handler0alter.cc | 2 ++ 3 files changed, 19 insertions(+) diff --git a/mysql-test/suite/innodb/r/default_row_format_create.result b/mysql-test/suite/innodb/r/default_row_format_create.result index d85fed35d92..0dfcbd80e88 100644 --- a/mysql-test/suite/innodb/r/default_row_format_create.result +++ b/mysql-test/suite/innodb/r/default_row_format_create.result @@ -56,4 +56,12 @@ DROP TABLE IF EXISTS t; SET GLOBAL innodb_compression_level=1; CREATE TABLE t(c INT) ENGINE=InnoDB page_compressed=1; DROP TABLE IF EXISTS t; +SET GLOBAL innodb_compression_level=1; +CREATE TABLE t(a INT)ENGINE=InnoDB ROW_FORMAT=DYNAMIC page_compressed=1; +SET GLOBAL innodb_compression_level=0; +ALTER TABLE t FORCE, ROW_FORMAT=DEFAULT, ALGORITHM=INPLACE; +ERROR HY000: Table storage engine 'InnoDB' does not support the create option 'PAGE_COMPRESSED' +ALTER TABLE t FORCE, ROW_FORMAT=DEFAULT, ALGORITHM=COPY; +ERROR HY000: Can't create table `test`.`t` (errno: 140 "Wrong create options") +DROP TABLE t; SET GLOBAL innodb_compression_level=@save_level; diff --git a/mysql-test/suite/innodb/t/default_row_format_create.test b/mysql-test/suite/innodb/t/default_row_format_create.test index a1c6ff0d588..534a7312620 100644 --- a/mysql-test/suite/innodb/t/default_row_format_create.test +++ b/mysql-test/suite/innodb/t/default_row_format_create.test @@ -59,4 +59,13 @@ SET GLOBAL innodb_compression_level=1; CREATE TABLE t(c INT) ENGINE=InnoDB page_compressed=1; DROP TABLE IF EXISTS t; --enable_warnings + +SET GLOBAL innodb_compression_level=1; +CREATE TABLE t(a INT)ENGINE=InnoDB ROW_FORMAT=DYNAMIC page_compressed=1; +SET GLOBAL innodb_compression_level=0; +--error ER_ILLEGAL_HA_CREATE_OPTION +ALTER TABLE t FORCE, ROW_FORMAT=DEFAULT, ALGORITHM=INPLACE; +--error ER_CANT_CREATE_TABLE +ALTER TABLE t FORCE, ROW_FORMAT=DEFAULT, ALGORITHM=COPY; +DROP TABLE t; SET GLOBAL innodb_compression_level=@save_level; diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 2dd88e50c07..1bbe99ee34d 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -5584,6 +5584,8 @@ check_if_ok_to_rename: } if (!info.innobase_table_flags()) { + my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), + table_type(), "PAGE_COMPRESSED"); goto err_exit_no_heap; } -- cgit v1.2.1 From 2e844a08f75ea311a4c6f72b0449b27318d3ec02 Mon Sep 17 00:00:00 2001 From: Sergei Krivonos Date: Thu, 21 Oct 2021 11:54:01 +0300 Subject: MDEV-19129: Xcode compatibility update: mysql-test-run.pl --- mysql-test/mysql-test-run.pl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 29aaebd44b0..4bc212aedbe 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -420,6 +420,7 @@ sub check_wsrep_support() { my $my_print_defaults_exe= mtr_exe_maybe_exists( "$bindir/extra/my_print_defaults", + "$bindir/extra/Debug/my_print_defaults", "$path_client_bindir/my_print_defaults"); my $epath= ""; if ($my_print_defaults_exe ne "") { @@ -1415,6 +1416,7 @@ sub command_line_setup { { $path_client_bindir= mtr_path_exists("$bindir/client_release", "$bindir/client_debug", + "$bindir/client/debug", "$bindir/client$opt_vs_config", "$bindir/client", "$bindir/bin"); -- cgit v1.2.1 From d3426c4c0c112f40f81fb4e3958a68b1e06233fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 21 Oct 2021 12:26:54 +0300 Subject: MDEV-26262 fixup: Remove a bogus assertion In commit 1811fd51fbae9e6c1f06ce93faef2bf1279cd3b6 the assertion should have said error_reported instead of !error_reported. But, that revised assertion would still fail in main.defaults where ER_BAD_DATA is reported during CREATE TABLE. --- sql/table.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/sql/table.cc b/sql/table.cc index 349d04a60ed..d4f8170e0af 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -3282,9 +3282,6 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, &error_reported, mode)) { error= OPEN_FRM_CORRUPTED; - // parse_vcol_defs may fail by semantic reasons, which is ok, but the - // real corruption should never be reported during table creation - DBUG_ASSERT(!is_create_table || !error_reported); goto err; } -- cgit v1.2.1 From 6b4fad94029bb73fbb3f9d05f2dfed09e83ec31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 21 Oct 2021 12:27:38 +0300 Subject: MDEV-22627 fixup: Add a type cast for 32-bit platforms --- storage/innobase/handler/ha_innodb.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 592492cac60..10e5cbe216f 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -12195,7 +12195,7 @@ index_bad: m_flags2 |= DICT_TF2_USE_FILE_PER_TABLE; } - ulint level = options->page_compression_level; + ulint level = ulint(options->page_compression_level); if (!level) { level = page_zip_level; if (!level && options->page_compressed) { -- cgit v1.2.1 From 8ce8c269f461b7a578cf0bb5cce18f1d83ebeb9b Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Wed, 6 Oct 2021 18:50:56 +0530 Subject: MDEV-19522 InnoDB commit fails when FTS_DOC_ID value is greater than 4294967295 InnoDB commit fails when consecutive FTS_DOC_ID value is greater than 4294967295. Fix is that InnoDB should remove the delta FTS_DOC_ID value limitations and fts should encode 8 byte value, remove FTS_DOC_ID_MAX_STEP variable. Replaced the fts0vlc.ic file with fts0vlc.h fts_encode_int(): Should be able to encode 10 bytes value fts_get_encoded_len(): Should get the length of the value which has 10 bytes fts_decode_vlc(): Add debug assertion to verify the maximum length allowed is 10. mach_read_uint64_little_endian(): Reads 64 bit stored in little endian format Added a unit test case which check for minimum and maximum value to do the fts encoding --- mysql-test/suite/innodb_fts/r/basic.result | 2 - .../suite/innodb_fts/r/innodb_fts_misc_1.result | 21 +++ mysql-test/suite/innodb_fts/t/basic.test | 2 - .../suite/innodb_fts/t/innodb_fts_misc_1.test | 18 +++ storage/innobase/CMakeLists.txt | 4 + storage/innobase/fts/fts0fts.cc | 10 +- storage/innobase/fts/fts0opt.cc | 10 +- storage/innobase/fts/fts0que.cc | 7 +- storage/innobase/handler/ha_innodb.cc | 13 +- storage/innobase/handler/i_s.cc | 18 +-- storage/innobase/include/fts0fts.h | 4 - storage/innobase/include/fts0types.h | 33 ----- storage/innobase/include/fts0vlc.h | 124 ++++++++++++++++++ storage/innobase/include/fts0vlc.ic | 142 --------------------- storage/innobase/include/mach0data.h | 22 ++++ storage/innobase/row/row0mysql.cc | 17 --- storage/innobase/unittest/CMakeLists.txt | 22 ++++ storage/innobase/unittest/innodb_fts-t.cc | 52 ++++++++ 18 files changed, 286 insertions(+), 235 deletions(-) create mode 100644 storage/innobase/include/fts0vlc.h delete mode 100644 storage/innobase/include/fts0vlc.ic create mode 100644 storage/innobase/unittest/CMakeLists.txt create mode 100644 storage/innobase/unittest/innodb_fts-t.cc diff --git a/mysql-test/suite/innodb_fts/r/basic.result b/mysql-test/suite/innodb_fts/r/basic.result index b3fd94509c3..a98de60674a 100644 --- a/mysql-test/suite/innodb_fts/r/basic.result +++ b/mysql-test/suite/innodb_fts/r/basic.result @@ -313,9 +313,7 @@ FTS_DOC_ID 65536 131071 drop table t1; -call mtr.add_suppression("\\[ERROR\\] InnoDB: Doc ID 20030101000000 is too big. Its difference with largest used Doc ID 0 cannot exceed or equal to 65535"); CREATE TABLE t1 (FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY, title VARCHAR(200), FULLTEXT(title)) ENGINE=InnoDB; INSERT INTO t1 VALUES (NULL, NULL), (20030101000000, 20030102000000); -ERROR HY000: Invalid InnoDB FTS Doc ID DROP TABLE t1; diff --git a/mysql-test/suite/innodb_fts/r/innodb_fts_misc_1.result b/mysql-test/suite/innodb_fts/r/innodb_fts_misc_1.result index 8ec0157728c..42730d7916a 100644 --- a/mysql-test/suite/innodb_fts/r/innodb_fts_misc_1.result +++ b/mysql-test/suite/innodb_fts/r/innodb_fts_misc_1.result @@ -972,3 +972,24 @@ SELECT * FROM information_schema.innodb_ft_deleted; DOC_ID DROP TABLE t1; SET GLOBAL innodb_ft_aux_table=DEFAULT; +# +# MDEV-19522 InnoDB commit fails when FTS_DOC_ID value +# is greater than 4294967295 +# +CREATE TABLE t1( +FTS_DOC_ID BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, +f1 TEXT, f2 TEXT, PRIMARY KEY (FTS_DOC_ID), +FULLTEXT KEY (f1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1,'txt','bbb'); +UPDATE t1 SET FTS_DOC_ID = 4294967298; +SELECT * FROM t1 WHERE match(f1) against("txt"); +FTS_DOC_ID f1 f2 +4294967298 txt bbb +SET @@session.insert_id = 100000000000; +INSERT INTO t1(f1, f2) VALUES ('aaa', 'bbb'); +CREATE FULLTEXT INDEX i ON t1 (f2); +SELECT * FROM t1 WHERE match(f2) against("bbb"); +FTS_DOC_ID f1 f2 +4294967298 txt bbb +100000000000 aaa bbb +DROP TABLE t1; diff --git a/mysql-test/suite/innodb_fts/t/basic.test b/mysql-test/suite/innodb_fts/t/basic.test index 7a5c83ffb06..53ad978a5b1 100644 --- a/mysql-test/suite/innodb_fts/t/basic.test +++ b/mysql-test/suite/innodb_fts/t/basic.test @@ -277,9 +277,7 @@ insert into t1(f1, f2) values(3, "This is the third record"); select FTS_DOC_ID from t1; drop table t1; -call mtr.add_suppression("\\[ERROR\\] InnoDB: Doc ID 20030101000000 is too big. Its difference with largest used Doc ID 0 cannot exceed or equal to 65535"); CREATE TABLE t1 (FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY, title VARCHAR(200), FULLTEXT(title)) ENGINE=InnoDB; ---error 182 INSERT INTO t1 VALUES (NULL, NULL), (20030101000000, 20030102000000); DROP TABLE t1; diff --git a/mysql-test/suite/innodb_fts/t/innodb_fts_misc_1.test b/mysql-test/suite/innodb_fts/t/innodb_fts_misc_1.test index adc10886d66..b0bf2c669ad 100644 --- a/mysql-test/suite/innodb_fts/t/innodb_fts_misc_1.test +++ b/mysql-test/suite/innodb_fts/t/innodb_fts_misc_1.test @@ -942,3 +942,21 @@ SET GLOBAL innodb_ft_aux_table='test/t1'; SELECT * FROM information_schema.innodb_ft_deleted; DROP TABLE t1; SET GLOBAL innodb_ft_aux_table=DEFAULT; + +--echo # +--echo # MDEV-19522 InnoDB commit fails when FTS_DOC_ID value +--echo # is greater than 4294967295 +--echo # +CREATE TABLE t1( + FTS_DOC_ID BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + f1 TEXT, f2 TEXT, PRIMARY KEY (FTS_DOC_ID), + FULLTEXT KEY (f1)) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1,'txt','bbb'); +UPDATE t1 SET FTS_DOC_ID = 4294967298; +SELECT * FROM t1 WHERE match(f1) against("txt"); +SET @@session.insert_id = 100000000000; +INSERT INTO t1(f1, f2) VALUES ('aaa', 'bbb'); +CREATE FULLTEXT INDEX i ON t1 (f2); +SELECT * FROM t1 WHERE match(f2) against("bbb"); +# Cleanup +DROP TABLE t1; diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index b965c4fbb45..ff646e00f39 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -188,3 +188,7 @@ IF(MSVC) ENDIF() ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/extra/mariabackup ${CMAKE_BINARY_DIR}/extra/mariabackup) + +IF(WITH_UNIT_TESTS) + ADD_SUBDIRECTORY(unittest) +ENDIF() diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc index 07077006096..bbe53f4d163 100644 --- a/storage/innobase/fts/fts0fts.cc +++ b/storage/innobase/fts/fts0fts.cc @@ -32,7 +32,7 @@ Full Text Search interface #include "fts0priv.h" #include "fts0types.h" #include "fts0types.ic" -#include "fts0vlc.ic" +#include "fts0vlc.h" #include "fts0plugin.h" #include "dict0priv.h" #include "dict0stats.h" @@ -1247,7 +1247,7 @@ fts_cache_node_add_positions( ulint enc_len; ulint last_pos; byte* ptr_start; - ulint doc_id_delta; + doc_id_t doc_id_delta; #ifdef UNIV_DEBUG if (cache) { @@ -1258,7 +1258,7 @@ fts_cache_node_add_positions( ut_ad(doc_id >= node->last_doc_id); /* Calculate the space required to store the ilist. */ - doc_id_delta = (ulint)(doc_id - node->last_doc_id); + doc_id_delta = doc_id - node->last_doc_id; enc_len = fts_get_encoded_len(doc_id_delta); last_pos = 0; @@ -1307,14 +1307,14 @@ fts_cache_node_add_positions( ptr_start = ptr; /* Encode the new fragment. */ - ptr += fts_encode_int(doc_id_delta, ptr); + ptr = fts_encode_int(doc_id_delta, ptr); last_pos = 0; for (i = 0; i < ib_vector_size(positions); i++) { ulint pos = *(static_cast( ib_vector_get(positions, i))); - ptr += fts_encode_int(pos - last_pos, ptr); + ptr = fts_encode_int(pos - last_pos, ptr); last_pos = pos; } diff --git a/storage/innobase/fts/fts0opt.cc b/storage/innobase/fts/fts0opt.cc index c4cbbfafff4..2d506a757a0 100644 --- a/storage/innobase/fts/fts0opt.cc +++ b/storage/innobase/fts/fts0opt.cc @@ -36,6 +36,7 @@ Completed 2011/7/10 Sunny and Jimmy Yang #include "ut0list.h" #include "zlib.h" #include "fts0opt.h" +#include "fts0vlc.h" /** The FTS optimize thread's work queue. */ ib_wqueue_t* fts_optimize_wq; @@ -1116,7 +1117,7 @@ fts_optimize_encode_node( ulint pos_enc_len; doc_id_t doc_id_delta; dberr_t error = DB_SUCCESS; - byte* src = enc->src_ilist_ptr; + const byte* src = enc->src_ilist_ptr; if (node->first_doc_id == 0) { ut_a(node->last_doc_id == 0); @@ -1173,7 +1174,7 @@ fts_optimize_encode_node( /* Encode the doc id. Cast to ulint, the delta should be small and therefore no loss of precision. */ - dst += fts_encode_int((ulint) doc_id_delta, dst); + dst = fts_encode_int(doc_id_delta, dst); /* Copy the encoded pos array. */ memcpy(dst, src, pos_enc_len); @@ -1220,7 +1221,8 @@ fts_optimize_node( doc_id_t delta; doc_id_t del_doc_id = FTS_NULL_DOC_ID; - delta = fts_decode_vlc(&enc->src_ilist_ptr); + delta = fts_decode_vlc( + (const byte**)&enc->src_ilist_ptr); test_again: /* Check whether the doc id is in the delete list, if @@ -1248,7 +1250,7 @@ test_again: /* Skip the entries for this document. */ while (*enc->src_ilist_ptr) { - fts_decode_vlc(&enc->src_ilist_ptr); + fts_decode_vlc((const byte**)&enc->src_ilist_ptr); } /* Skip the end of word position marker. */ diff --git a/storage/innobase/fts/fts0que.cc b/storage/innobase/fts/fts0que.cc index b4c72e53afe..965c79bfaa5 100644 --- a/storage/innobase/fts/fts0que.cc +++ b/storage/innobase/fts/fts0que.cc @@ -34,6 +34,7 @@ Completed 2011/7/10 Sunny and Jimmy Yang #include "fts0pars.h" #include "fts0types.h" #include "fts0plugin.h" +#include "fts0vlc.h" #include #include @@ -3224,7 +3225,7 @@ fts_query_filter_doc_ids( ulint len, /*!< in: doc id ilist size */ ibool calc_doc_count) /*!< in: whether to remember doc count */ { - byte* ptr = static_cast(data); + const byte* ptr = static_cast(data); doc_id_t doc_id = 0; ulint decoded = 0; ib_rbt_t* doc_freqs = word_freq->doc_freqs; @@ -3234,8 +3235,8 @@ fts_query_filter_doc_ids( ulint freq = 0; fts_doc_freq_t* doc_freq; fts_match_t* match = NULL; - ulint last_pos = 0; - ulint pos = fts_decode_vlc(&ptr); + doc_id_t last_pos = 0; + doc_id_t pos = fts_decode_vlc(&ptr); /* Some sanity checks. */ if (doc_id == 0) { diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 10e5cbe216f..91d245ad0e2 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -8543,8 +8543,7 @@ calc_row_difference( && prebuilt->table->fts && innobase_strcasecmp( field->field_name, FTS_DOC_ID_COL_NAME) == 0) { - doc_id = (doc_id_t) mach_read_from_n_little_endian( - n_ptr, 8); + doc_id = mach_read_uint64_little_endian(n_ptr); if (doc_id == 0) { return(DB_FTS_INVALID_DOCID); } @@ -8787,16 +8786,6 @@ calc_row_difference( << innodb_table->name; return(DB_FTS_INVALID_DOCID); - } else if ((doc_id - - prebuilt->table->fts->cache->next_doc_id) - >= FTS_DOC_ID_MAX_STEP) { - - ib::warn() << "Doc ID " << doc_id << " is too" - " big. Its difference with largest" - " Doc ID used " << prebuilt->table->fts - ->cache->next_doc_id - 1 - << " cannot exceed or equal to " - << FTS_DOC_ID_MAX_STEP; } diff --git a/storage/innobase/handler/i_s.cc b/storage/innobase/handler/i_s.cc index 03c0efff027..1111d974ad0 100644 --- a/storage/innobase/handler/i_s.cc +++ b/storage/innobase/handler/i_s.cc @@ -58,6 +58,7 @@ Modified Dec 29, 2014 Jan Lindström (Added sys_semaphore_waits) #include "fil0fil.h" #include "fil0crypt.h" #include "dict0crea.h" +#include "fts0vlc.h" /** The latest successfully looked up innodb_fts_aux_table */ UNIV_INTERN table_id_t innodb_ft_aux_table_id; @@ -2775,7 +2776,7 @@ i_s_fts_index_cache_fill_one_index( /* Decrypt the ilist, and display Dod ID and word position */ for (ulint i = 0; i < ib_vector_size(word->nodes); i++) { fts_node_t* node; - byte* ptr; + const byte* ptr; ulint decoded = 0; doc_id_t doc_id = 0; @@ -2785,13 +2786,11 @@ i_s_fts_index_cache_fill_one_index( ptr = node->ilist; while (decoded < node->ilist_size) { - ulint pos = fts_decode_vlc(&ptr); - doc_id += pos; + doc_id += fts_decode_vlc(&ptr); /* Get position info */ while (*ptr) { - pos = fts_decode_vlc(&ptr); OK(field_store_string( fields[I_S_FTS_WORD], @@ -2812,7 +2811,7 @@ i_s_fts_index_cache_fill_one_index( doc_id, true)); OK(fields[I_S_FTS_ILIST_DOC_POS]->store( - pos, true)); + fts_decode_vlc(&ptr), true)); OK(schema_table_store_record( thd, table)); @@ -3146,7 +3145,7 @@ i_s_fts_index_table_fill_one_fetch( /* Decrypt the ilist, and display Dod ID and word position */ for (ulint i = 0; i < ib_vector_size(word->nodes); i++) { fts_node_t* node; - byte* ptr; + const byte* ptr; ulint decoded = 0; doc_id_t doc_id = 0; @@ -3156,13 +3155,10 @@ i_s_fts_index_table_fill_one_fetch( ptr = node->ilist; while (decoded < node->ilist_size) { - ulint pos = fts_decode_vlc(&ptr); - - doc_id += pos; + doc_id += fts_decode_vlc(&ptr); /* Get position info */ while (*ptr) { - pos = fts_decode_vlc(&ptr); OK(field_store_string( fields[I_S_FTS_WORD], @@ -3181,7 +3177,7 @@ i_s_fts_index_table_fill_one_fetch( longlong(doc_id), true)); OK(fields[I_S_FTS_ILIST_DOC_POS]->store( - pos, true)); + fts_decode_vlc(&ptr), true)); OK(schema_table_store_record( thd, table)); diff --git a/storage/innobase/include/fts0fts.h b/storage/innobase/include/fts0fts.h index 84d8ccd26ef..dfac5117c17 100644 --- a/storage/innobase/include/fts0fts.h +++ b/storage/innobase/include/fts0fts.h @@ -96,10 +96,6 @@ those defined in mysql file ft_global.h */ /** Threshold where our optimize thread automatically kicks in */ #define FTS_OPTIMIZE_THRESHOLD 10000000 -/** Threshold to avoid exhausting of doc ids. Consecutive doc id difference -should not exceed FTS_DOC_ID_MAX_STEP */ -#define FTS_DOC_ID_MAX_STEP 65535 - /** Maximum possible Fulltext word length in bytes (assuming mbmaxlen=4) */ #define FTS_MAX_WORD_LEN (HA_FT_MAXCHARLEN * 4) diff --git a/storage/innobase/include/fts0types.h b/storage/innobase/include/fts0types.h index f5760a16c0e..21d32c7d313 100644 --- a/storage/innobase/include/fts0types.h +++ b/storage/innobase/include/fts0types.h @@ -314,16 +314,6 @@ int fts_doc_id_cmp( const void* p1, /*!< in: id1 */ const void* p2); /*!< in: id2 */ -/******************************************************************//** -Decode and return the integer that was encoded using our VLC scheme.*/ -UNIV_INLINE -ulint -fts_decode_vlc( -/*===========*/ - /*!< out: value decoded */ - byte** ptr); /*!< in: ptr to decode from, this ptr is - incremented by the number of bytes decoded */ - /******************************************************************//** Duplicate a string. */ UNIV_INLINE @@ -338,28 +328,6 @@ fts_string_dup( const fts_string_t* src, /*!< in: src string */ mem_heap_t* heap); /*!< in: heap to use */ -/******************************************************************//** -Return length of val if it were encoded using our VLC scheme. */ -UNIV_INLINE -ulint -fts_get_encoded_len( -/*================*/ - /*!< out: length of value - encoded, in bytes */ - ulint val); /*!< in: value to encode */ - -/******************************************************************//** -Encode an integer using our VLC scheme and return the length in bytes. */ -UNIV_INLINE -ulint -fts_encode_int( -/*===========*/ - /*!< out: length of value - encoded, in bytes */ - ulint val, /*!< in: value to encode */ - byte* buf); /*!< in: buffer, must have - enough space */ - /******************************************************************//** Get the selected FTS aux INDEX suffix. */ UNIV_INLINE @@ -381,6 +349,5 @@ fts_select_index( ulint len); #include "fts0types.ic" -#include "fts0vlc.ic" #endif /* INNOBASE_FTS0TYPES_H */ diff --git a/storage/innobase/include/fts0vlc.h b/storage/innobase/include/fts0vlc.h new file mode 100644 index 00000000000..d6e6037777e --- /dev/null +++ b/storage/innobase/include/fts0vlc.h @@ -0,0 +1,124 @@ +/** + +Copyright (c) 2021, MariaDB Corporation. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA +**/ +/** +@file include/fts0vlc.h +Full text variable length integer encoding/decoding. + +Created 2021-10-19 Thirunarayanan Balathandayuthapani +**/ + +/** Return length of val if it were encoded using our VLC scheme. +@param val value to encode +@return length of value encoded, in bytes */ +inline size_t fts_get_encoded_len(doc_id_t val) +{ + if (val < static_cast(1) << 7) + return 1; + if (val < static_cast(1) << 14) + return 2; + if (val < static_cast(1) << 21) + return 3; + if (val < static_cast(1) << 28) + return 4; + if (val < static_cast(1) << 35) + return 5; + if (val < static_cast(1) << 42) + return 6; + if (val < static_cast(1) << 49) + return 7; + if (val < static_cast(1) << 56) + return 8; + if (val < static_cast(1) << 63) + return 9; + return 10; +} + +/** Encode an integer using our VLC scheme and return the +length in bytes. +@param val value to encode +@param buf buffer, must have enough space +@return length of value encoded, in bytes */ +inline byte *fts_encode_int(doc_id_t val, byte *buf) +{ + if (val < static_cast(1) << 7) + goto add_1; + if (val < static_cast(1) << 14) + goto add_2; + if (val < static_cast(1) << 21) + goto add_3; + if (val < static_cast(1) << 28) + goto add_4; + if (val < static_cast(1) << 35) + goto add_5; + if (val < static_cast(1) << 42) + goto add_6; + if (val < static_cast(1) << 49) + goto add_7; + if (val < static_cast(1) << 56) + goto add_8; + if (val < static_cast(1) << 63) + goto add_9; + + *buf++= static_cast(val >> 63); +add_9: + *buf++= static_cast(val >> 56) & 0x7F; +add_8: + *buf++= static_cast(val >> 49) & 0x7F; +add_7: + *buf++= static_cast(val >> 42) & 0x7F; +add_6: + *buf++= static_cast(val >> 35) & 0x7F; +add_5: + *buf++= static_cast(val >> 28) & 0x7F; +add_4: + *buf++= static_cast(val >> 21) & 0x7F; +add_3: + *buf++= static_cast(val >> 14) & 0x7F; +add_2: + *buf++= static_cast(val >> 7) & 0x7F; +add_1: + *buf++= static_cast(val) | 0x80; + return buf; +} + +/** Decode and return the integer that was encoded using +our VLC scheme. +@param ptr pointer to decode from, this ptr is + incremented by the number of bytes decoded +@return value decoded */ +inline doc_id_t fts_decode_vlc(const byte **ptr) +{ + ut_d(const byte *const start= *ptr); + ut_ad(*start); + + doc_id_t val= 0; + for (;;) + { + byte b= *(*ptr)++; + val|= (b & 0x7F); + + /* High-bit on means "last byte in the encoded integer". */ + if (b & 0x80) + break; + ut_ad(val < static_cast(1) << (64 - 7)); + val <<= 7; + } + + ut_ad(*ptr - start <= 10); + + return(val); +} diff --git a/storage/innobase/include/fts0vlc.ic b/storage/innobase/include/fts0vlc.ic deleted file mode 100644 index 75d8535057e..00000000000 --- a/storage/innobase/include/fts0vlc.ic +++ /dev/null @@ -1,142 +0,0 @@ -/***************************************************************************** - -Copyright (c) 2007, 2011, Oracle and/or its affiliates. All Rights Reserved. - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; version 2 of the License. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA - -*****************************************************************************/ - -/******************************************************************//** -@file include/fts0vlc.ic -Full text variable length integer encoding/decoding. - -Created 2007-03-27 Sunny Bains -*******************************************************/ - -#ifndef INNOBASE_FTS0VLC_IC -#define INNOBASE_FTS0VLC_IC - -#include "fts0types.h" - -/******************************************************************//** -Return length of val if it were encoded using our VLC scheme. -FIXME: We will need to be able encode 8 bytes value -@return length of value encoded, in bytes */ -UNIV_INLINE -ulint -fts_get_encoded_len( -/*================*/ - ulint val) /* in: value to encode */ -{ - if (val <= 127) { - return(1); - } else if (val <= 16383) { - return(2); - } else if (val <= 2097151) { - return(3); - } else if (val <= 268435455) { - return(4); - } else { - /* Possibly we should care that on 64-bit machines ulint can - contain values that we can't encode in 5 bytes, but - fts_encode_int doesn't handle them either so it doesn't much - matter. */ - - return(5); - } -} - -/******************************************************************//** -Encode an integer using our VLC scheme and return the length in bytes. -@return length of value encoded, in bytes */ -UNIV_INLINE -ulint -fts_encode_int( -/*===========*/ - ulint val, /* in: value to encode */ - byte* buf) /* in: buffer, must have enough space */ -{ - ulint len; - - if (val <= 127) { - *buf = (byte) val; - - len = 1; - } else if (val <= 16383) { - *buf++ = (byte)(val >> 7); - *buf = (byte)(val & 0x7F); - - len = 2; - } else if (val <= 2097151) { - *buf++ = (byte)(val >> 14); - *buf++ = (byte)((val >> 7) & 0x7F); - *buf = (byte)(val & 0x7F); - - len = 3; - } else if (val <= 268435455) { - *buf++ = (byte)(val >> 21); - *buf++ = (byte)((val >> 14) & 0x7F); - *buf++ = (byte)((val >> 7) & 0x7F); - *buf = (byte)(val & 0x7F); - - len = 4; - } else { - /* Best to keep the limitations of the 32/64 bit versions - identical, at least for the time being. */ - ut_ad(val <= 4294967295u); - - *buf++ = (byte)(val >> 28); - *buf++ = (byte)((val >> 21) & 0x7F); - *buf++ = (byte)((val >> 14) & 0x7F); - *buf++ = (byte)((val >> 7) & 0x7F); - *buf = (byte)(val & 0x7F); - - len = 5; - } - - /* High-bit on means "last byte in the encoded integer". */ - *buf |= 0x80; - - return(len); -} - -/******************************************************************//** -Decode and return the integer that was encoded using our VLC scheme. -@return value decoded */ -UNIV_INLINE -ulint -fts_decode_vlc( -/*===========*/ - byte** ptr) /* in: ptr to decode from, this ptr is - incremented by the number of bytes decoded */ -{ - ulint val = 0; - - for (;;) { - byte b = **ptr; - - ++*ptr; - val |= (b & 0x7F); - - /* High-bit on means "last byte in the encoded integer". */ - if (b & 0x80) { - break; - } else { - val <<= 7; - } - } - - return(val); -} - -#endif diff --git a/storage/innobase/include/mach0data.h b/storage/innobase/include/mach0data.h index 8141c8a91e0..860ef20b8bd 100644 --- a/storage/innobase/include/mach0data.h +++ b/storage/innobase/include/mach0data.h @@ -316,6 +316,28 @@ mach_read_from_n_little_endian( const byte* buf, /*!< in: from where to read */ ulint buf_size) /*!< in: from how many bytes to read */ MY_ATTRIBUTE((warn_unused_result)); + + +/** Reads a 64 bit stored in big endian format +@param buf From where to read +@return uint64_t */ +UNIV_INLINE +uint64_t +mach_read_uint64_little_endian(const byte* buf) +{ +#ifdef WORDS_BIGENDIAN + return + uint64_t(buf[0]) | uint64_t(buf[1]) << 8 | + uint64_t(buf[2]) << 16 | uint64_t(buf[3]) << 24 | + uint64_t(buf[4]) << 32 | uint64_t(buf[5]) << 40 | + uint64_t(buf[6]) << 48 | uint64_t(buf[7]) << 56; +#else + uint64_t n; + memcpy(&n, buf, sizeof(uint64_t)); + return n; +#endif +} + /*********************************************************//** Writes a ulint in the little-endian format. */ UNIV_INLINE diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index c2f9186d408..6445f67f3c2 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -1465,23 +1465,6 @@ error_exit: trx->error_state = DB_FTS_INVALID_DOCID; goto error_exit; } - - /* Difference between Doc IDs are restricted within - 4 bytes integer. See fts_get_encoded_len(). Consecutive - doc_ids difference should not exceed - FTS_DOC_ID_MAX_STEP value. */ - - if (doc_id - next_doc_id >= FTS_DOC_ID_MAX_STEP) { - ib::error() << "Doc ID " << doc_id - << " is too big. Its difference with" - " largest used Doc ID " - << next_doc_id - 1 << " cannot" - " exceed or equal to " - << FTS_DOC_ID_MAX_STEP; - err = DB_FTS_INVALID_DOCID; - trx->error_state = DB_FTS_INVALID_DOCID; - goto error_exit; - } } if (table->skip_alter_undo) { diff --git a/storage/innobase/unittest/CMakeLists.txt b/storage/innobase/unittest/CMakeLists.txt new file mode 100644 index 00000000000..df98cddf73e --- /dev/null +++ b/storage/innobase/unittest/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (c) 2021, MariaDB Corporation. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/unittest/mytap + ${CMAKE_SOURCE_DIR}/storage/innobase/include) +ADD_EXECUTABLE(innodb_fts-t innodb_fts-t.cc) +TARGET_LINK_LIBRARIES(innodb_fts-t mysys mytap) +ADD_DEPENDENCIES(innodb_fts-t GenError) +MY_ADD_TEST(innodb_fts) diff --git a/storage/innobase/unittest/innodb_fts-t.cc b/storage/innobase/unittest/innodb_fts-t.cc new file mode 100644 index 00000000000..57585e337c2 --- /dev/null +++ b/storage/innobase/unittest/innodb_fts-t.cc @@ -0,0 +1,52 @@ +#include "tap.h" +#include "fts0fts.h" +#include "fts0vlc.h" + +struct fts_encode_info +{ + const byte buf[10]; + int32_t len; + doc_id_t val; +}; + +/* Contains fts encoding min & max value for each length bytes */ +static const fts_encode_info fts_info[]= +{ + {{0x80}, 1, 0}, + {{0xFF}, 1, (1 << 7) - 1}, + {{0x01, 0x80}, 2, 1 << 7}, + {{0x7F, 0XFF}, 2, (1 << 14) - 1}, + {{0x01, 0x00, 0x80}, 3, 1 << 14}, + {{0x7F, 0X7F, 0XFF}, 3, (1 << 21) - 1}, + {{0x01, 0x00, 0x00, 0x80}, 4, 1 << 21}, + {{0x7F, 0X7F, 0X7F, 0xFF}, 4, (1 << 28) - 1}, + {{0x01, 0x00, 0x00, 0x00, 0x80}, 5, 1 << 28}, + {{0x7F, 0X7F, 0X7F, 0x7F, 0xFF}, 5, (1ULL << 35) - 1}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x80}, 6, 1ULL << 35}, + {{0x7F, 0X7F, 0X7F, 0x7F, 0x7F, 0xFF}, 6, (1ULL << 42) - 1}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}, 7, 1ULL << 42}, + {{0x7F, 0X7F, 0X7F, 0x7F, 0x7F, 0x7F, 0XFF}, 7, (1ULL << 49) - 1}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}, 8, 1ULL << 49}, + {{0x7F, 0X7F, 0X7F, 0x7F, 0x7F, 0x7F, 0X7F, 0XFF}, 8, (1ULL << 56) -1}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}, 9, 1ULL << 56}, + {{0x7F, 0X7F, 0X7F, 0x7F, 0x7F, 0x7F, 0X7F, 0x7F, 0XFF}, 9, (1ULL << 63) -1}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}, 10, 1ULL << 63}, + {{0x01, 0X7F, 0X7F, 0x7F, 0x7F, 0x7F, 0X7F, 0x7F, 0x7F, 0xFF}, 10, ~0ULL} +}; + +int main(int, char**) +{ + for (int i= array_elements(fts_info); i--;) + { + byte buf[10]; + const byte* fts_buf= buf; + int32_t len= fts_encode_int(fts_info[i].val, buf) - &buf[0]; + if (fts_info[i].len == len && + !memcmp(&fts_info[i].buf, buf, len) && + fts_decode_vlc(&fts_buf) == fts_info[i].val && + fts_buf == &buf[len]) + ok(true, "FTS Encoded for %d bytes", fts_info[i].len); + else + ok(false, "FTS Encoded for %d bytes", fts_info[i].len); + } +} -- cgit v1.2.1 From c484a358c897413be390d03bdcb8dc4d70c7d1c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 21 Oct 2021 12:29:33 +0300 Subject: MDEV-26864 Race condition between transaction commit and undo log truncation trx_commit_in_memory(): Do not release the rseg reference before trx_undo_commit_cleanup() has been invoked and the current transaction is truly done with the rollback segment. The purpose of the reference count is to prevent data races with trx_purge_truncate_history(). This is based on mysql/mysql-server@ac79aa1522f33e6eb912133a81fa2614db764c9c. --- storage/innobase/trx/trx0trx.cc | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 3e558a7181d..6669d15a31b 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -1775,12 +1775,7 @@ trx_commit_in_memory( ut_ad(!trx->rsegs.m_redo.update_undo); - if (trx_rseg_t* rseg = trx->rsegs.m_redo.rseg) { - mutex_enter(&rseg->mutex); - ut_ad(rseg->trx_ref_count > 0); - --rseg->trx_ref_count; - mutex_exit(&rseg->mutex); - + if (ut_d(trx_rseg_t* rseg =) trx->rsegs.m_redo.rseg) { if (trx_undo_t*& insert = trx->rsegs.m_redo.insert_undo) { ut_ad(insert->rseg == rseg); trx_undo_commit_cleanup(insert, false); @@ -1849,6 +1844,15 @@ trx_commit_in_memory( ut_ad(!trx->rsegs.m_noredo.undo); + /* Only after trx_undo_commit_cleanup() it is safe to release + our rseg reference. */ + if (trx_rseg_t* rseg = trx->rsegs.m_redo.rseg) { + mutex_enter(&rseg->mutex); + ut_ad(rseg->trx_ref_count > 0); + --rseg->trx_ref_count; + mutex_exit(&rseg->mutex); + } + /* Free all savepoints, starting from the first. */ trx_named_savept_t* savep = UT_LIST_GET_FIRST(trx->trx_savepoints); -- cgit v1.2.1 From 2d98b967e31623d9027c0db55330dde2c9d1d99a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 21 Oct 2021 12:44:27 +0300 Subject: MDEV-26865 fts_optimize_thread cannot keep up with workload fts_cache_t::total_size_at_sync: New field, to sample total_size. fts_add_doc_by_id(): Invoke sync if total_size has grown too much since the previous sync request. (Maintain cache->total_size_at_sync.) ib_wqueue_t::length: Caches ib_list_len(*items). ib_wqueue_len(): Removed. We will refer to fts_optimize_wq->length directly. Based on mysql/mysql-server@bc9c46bf2894673d0df17cd0ee872d0d99663121 --- storage/innobase/buf/buf0mtflu.cc | 8 -------- storage/innobase/fts/fts0fts.cc | 14 +++++++++----- storage/innobase/include/fts0types.h | 5 ++++- storage/innobase/include/ut0wqueue.h | 11 +++-------- storage/innobase/ut/ut0wqueue.cc | 29 ++++++++--------------------- 5 files changed, 24 insertions(+), 43 deletions(-) diff --git a/storage/innobase/buf/buf0mtflu.cc b/storage/innobase/buf/buf0mtflu.cc index aae90e48168..ee52a11c394 100644 --- a/storage/innobase/buf/buf0mtflu.cc +++ b/storage/innobase/buf/buf0mtflu.cc @@ -367,14 +367,6 @@ DECLARE_THREAD(mtflush_io_thread)(void* arg) mutex_exit(&(mtflush_io->thread_global_mtx)); while (TRUE) { - -#ifdef UNIV_MTFLUSH_DEBUG - fprintf(stderr, "InnoDB: Note. Thread %lu work queue len %lu return queue len %lu\n", - os_thread_get_curr_id(), - ib_wqueue_len(mtflush_io->wq), - ib_wqueue_len(mtflush_io->wr_cq)); -#endif /* UNIV_MTFLUSH_DEBUG */ - mtflush_service_io(mtflush_io, this_thread_data); diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc index bbe53f4d163..f9c7bcd75c4 100644 --- a/storage/innobase/fts/fts0fts.cc +++ b/storage/innobase/fts/fts0fts.cc @@ -580,6 +580,7 @@ fts_cache_init( cache->sync_heap->arg = mem_heap_create(1024); cache->total_size = 0; + cache->total_size_at_sync = 0; mutex_enter((ib_mutex_t*) &cache->deleted_lock); cache->deleted_doc_ids = ib_vector_create( @@ -3571,11 +3572,14 @@ fts_add_doc_by_id( get_doc->index_cache, doc_id, doc.tokens); - bool need_sync = false; - if ((cache->total_size > fts_max_cache_size / 10 - || fts_need_sync) - && !cache->sync->in_progress) { - need_sync = true; + bool need_sync = !cache->sync->in_progress + && (fts_need_sync + || (cache->total_size + - cache->total_size_at_sync) + > fts_max_cache_size / 10); + if (need_sync) { + cache->total_size_at_sync = + cache->total_size; } rw_lock_x_unlock(&table->fts->cache->lock); diff --git a/storage/innobase/include/fts0types.h b/storage/innobase/include/fts0types.h index 21d32c7d313..d49bc7c0254 100644 --- a/storage/innobase/include/fts0types.h +++ b/storage/innobase/include/fts0types.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2007, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2020, MariaDB Corporation. +Copyright (c) 2017, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -150,6 +150,9 @@ struct fts_cache_t { size_t total_size; /*!< total size consumed by the ilist field of all nodes. SYNC is run whenever this gets too big */ + /** total_size at the time of the previous SYNC request */ + size_t total_size_at_sync; + fts_sync_t* sync; /*!< sync structure to sync data to disk */ ib_alloc_t* sync_heap; /*!< The heap allocator, for indexes diff --git a/storage/innobase/include/ut0wqueue.h b/storage/innobase/include/ut0wqueue.h index 5a895f4ea3c..d9cc7aec9c9 100644 --- a/storage/innobase/include/ut0wqueue.h +++ b/storage/innobase/include/ut0wqueue.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2006, 2014, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2021, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -46,6 +46,8 @@ struct ib_wqueue_t ib_mutex_t mutex; /** Work item list */ ib_list_t* items; + /** ib_list_len(*items) */ + size_t length; /** event we use to signal additions to list; os_event_set() and os_event_reset() are protected by the mutex */ os_event_t event; @@ -103,12 +105,5 @@ void* ib_wqueue_nowait( /*=============*/ ib_wqueue_t* wq); /*items = ib_list_create(); wq->event = os_event_create(0); + wq->length = 0; return(wq); } @@ -76,6 +77,8 @@ ib_wqueue_add(ib_wqueue_t* wq, void* item, mem_heap_t* heap, bool wq_locked) } ib_list_add_last(wq->items, item, heap); + wq->length++; + ut_ad(wq->length == ib_list_len(wq->items)); os_event_set(wq->event); if (!wq_locked) { @@ -102,12 +105,12 @@ ib_wqueue_wait( if (node) { ib_list_remove(wq->items, node); - - if (!ib_list_get_first(wq->items)) { + if (!--wq->length) { /* We must reset the event when the list gets emptied. */ os_event_reset(wq->event); } + ut_ad(wq->length == ib_list_len(wq->items)); break; } @@ -142,7 +145,8 @@ ib_wqueue_timedwait( if (node) { ib_list_remove(wq->items, node); - + wq->length--; + ut_ad(wq->length == ib_list_len(wq->items)); mutex_exit(&wq->mutex); break; } @@ -204,20 +208,3 @@ bool ib_wqueue_is_empty(ib_wqueue_t* wq) mutex_exit(&wq->mutex); return is_empty; } - -/******************************************************************** -Get number of items on queue. -@return number of items on queue */ -ulint -ib_wqueue_len( -/*==========*/ - ib_wqueue_t* wq) /*mutex); - len = ib_list_len(wq->items); - mutex_exit(&wq->mutex); - - return(len); -} -- cgit v1.2.1 From 1a2308d3f443d8fcacf5506cb96f802dee3a3519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 21 Oct 2021 12:57:09 +0300 Subject: MDEV-26865: Add test case and instrumentation Based on mysql/mysql-server@bc9c46bf2894673d0df17cd0ee872d0d99663121 but without sleeps. The test was verified to hit the debug assertion if the change to fts_add_doc_by_id() in commit 2d98b967e31623d9027c0db55330dde2c9d1d99a was reverted. --- mysql-test/suite/innodb_fts/r/bug_32831765.result | 131 +++++++++++++++++ mysql-test/suite/innodb_fts/t/bug_32831765.test | 164 ++++++++++++++++++++++ storage/innobase/fts/fts0opt.cc | 3 + 3 files changed, 298 insertions(+) create mode 100644 mysql-test/suite/innodb_fts/r/bug_32831765.result create mode 100644 mysql-test/suite/innodb_fts/t/bug_32831765.test diff --git a/mysql-test/suite/innodb_fts/r/bug_32831765.result b/mysql-test/suite/innodb_fts/r/bug_32831765.result new file mode 100644 index 00000000000..1b828f4266b --- /dev/null +++ b/mysql-test/suite/innodb_fts/r/bug_32831765.result @@ -0,0 +1,131 @@ +# +# Bug#32831765 SERVER HITS OOM CONDITION WHEN LOADING TWO +# INNODB TABLES WITH FTS INDEXES +# +create table t1 ( `id` int unsigned NOT NULL AUTO_INCREMENT, `col01` text, +`col02` text, `col03` text, `col04` text, `col05` text, `col06` text, `col07` +text, `col08` text, `col09` text, `col10` text, `col11` text, `col12` text, +`col13` text, `col14` text, `col15` text, `col16` text, `col17` text, `col18` +text, `col19` text, `col20` text, `col21` text, `col22` text, `col23` text, +`col24` text, `col25` text, `col26` text, `col27` text, `col28` text, `col29` +text, `col30` text, PRIMARY KEY (`id`), FULLTEXT KEY (`col01`), FULLTEXT KEY +(`col02`), FULLTEXT KEY (`col03`), FULLTEXT KEY (`col04`), FULLTEXT KEY +(`col05`), FULLTEXT KEY (`col06`), FULLTEXT KEY (`col07`), FULLTEXT KEY +(`col08`), FULLTEXT KEY (`col09`), FULLTEXT KEY (`col10`), FULLTEXT KEY +(`col11`), FULLTEXT KEY (`col12`), FULLTEXT KEY (`col13`), FULLTEXT KEY +(`col14`), FULLTEXT KEY (`col15`), FULLTEXT KEY (`col16`), FULLTEXT KEY +(`col17`), FULLTEXT KEY (`col18`), FULLTEXT KEY (`col19`), FULLTEXT KEY +(`col20`), FULLTEXT KEY (`col21`), FULLTEXT KEY (`col22`), FULLTEXT KEY +(`col23`), FULLTEXT KEY (`col24`), FULLTEXT KEY (`col25`), FULLTEXT KEY +(`col26`), FULLTEXT KEY (`col27`), FULLTEXT KEY (`col28`), FULLTEXT KEY +(`col29`), FULLTEXT KEY (`col30`)) engine=innodb; +create table t2 ( `id` int unsigned NOT NULL AUTO_INCREMENT, `col01` text, +`col02` text, `col03` text, `col04` text, `col05` text, `col06` text, `col07` +text, `col08` text, `col09` text, `col10` text, `col11` text, `col12` text, +`col13` text, `col14` text, `col15` text, `col16` text, `col17` text, `col18` +text, `col19` text, `col20` text, `col21` text, `col22` text, `col23` text, +`col24` text, `col25` text, `col26` text, `col27` text, `col28` text, `col29` +text, `col30` text, PRIMARY KEY (`id`), FULLTEXT KEY (`col01`), FULLTEXT KEY +(`col02`), FULLTEXT KEY (`col03`), FULLTEXT KEY (`col04`), FULLTEXT KEY +(`col05`), FULLTEXT KEY (`col06`), FULLTEXT KEY (`col07`), FULLTEXT KEY +(`col08`), FULLTEXT KEY (`col09`), FULLTEXT KEY (`col10`), FULLTEXT KEY +(`col11`), FULLTEXT KEY (`col12`), FULLTEXT KEY (`col13`), FULLTEXT KEY +(`col14`), FULLTEXT KEY (`col15`), FULLTEXT KEY (`col16`), FULLTEXT KEY +(`col17`), FULLTEXT KEY (`col18`), FULLTEXT KEY (`col19`), FULLTEXT KEY +(`col20`), FULLTEXT KEY (`col21`), FULLTEXT KEY (`col22`), FULLTEXT KEY +(`col23`), FULLTEXT KEY (`col24`), FULLTEXT KEY (`col25`), FULLTEXT KEY +(`col26`), FULLTEXT KEY (`col27`), FULLTEXT KEY (`col28`), FULLTEXT KEY +(`col29`), FULLTEXT KEY (`col30`)) engine=innodb; +create table t3 ( `id` int unsigned NOT NULL AUTO_INCREMENT, `col01` text, +`col02` text, `col03` text, `col04` text, `col05` text, `col06` text, `col07` +text, `col08` text, `col09` text, `col10` text, `col11` text, `col12` text, +`col13` text, `col14` text, `col15` text, `col16` text, `col17` text, `col18` +text, `col19` text, `col20` text, `col21` text, `col22` text, `col23` text, +`col24` text, `col25` text, `col26` text, `col27` text, `col28` text, `col29` +text, `col30` text, PRIMARY KEY (`id`), FULLTEXT KEY (`col01`), FULLTEXT KEY +(`col02`), FULLTEXT KEY (`col03`), FULLTEXT KEY (`col04`), FULLTEXT KEY +(`col05`), FULLTEXT KEY (`col06`), FULLTEXT KEY (`col07`), FULLTEXT KEY +(`col08`), FULLTEXT KEY (`col09`), FULLTEXT KEY (`col10`), FULLTEXT KEY +(`col11`), FULLTEXT KEY (`col12`), FULLTEXT KEY (`col13`), FULLTEXT KEY +(`col14`), FULLTEXT KEY (`col15`), FULLTEXT KEY (`col16`), FULLTEXT KEY +(`col17`), FULLTEXT KEY (`col18`), FULLTEXT KEY (`col19`), FULLTEXT KEY +(`col20`), FULLTEXT KEY (`col21`), FULLTEXT KEY (`col22`), FULLTEXT KEY +(`col23`), FULLTEXT KEY (`col24`), FULLTEXT KEY (`col25`), FULLTEXT KEY +(`col26`), FULLTEXT KEY (`col27`), FULLTEXT KEY (`col28`), FULLTEXT KEY +(`col29`), FULLTEXT KEY (`col30`)) engine=innodb; +create table t4 ( `id` int unsigned NOT NULL AUTO_INCREMENT, `col01` text, +`col02` text, `col03` text, `col04` text, `col05` text, `col06` text, `col07` +text, `col08` text, `col09` text, `col10` text, `col11` text, `col12` text, +`col13` text, `col14` text, `col15` text, `col16` text, `col17` text, `col18` +text, `col19` text, `col20` text, `col21` text, `col22` text, `col23` text, +`col24` text, `col25` text, `col26` text, `col27` text, `col28` text, `col29` +text, `col30` text, PRIMARY KEY (`id`), FULLTEXT KEY (`col01`), FULLTEXT KEY +(`col02`), FULLTEXT KEY (`col03`), FULLTEXT KEY (`col04`), FULLTEXT KEY +(`col05`), FULLTEXT KEY (`col06`), FULLTEXT KEY (`col07`), FULLTEXT KEY +(`col08`), FULLTEXT KEY (`col09`), FULLTEXT KEY (`col10`), FULLTEXT KEY +(`col11`), FULLTEXT KEY (`col12`), FULLTEXT KEY (`col13`), FULLTEXT KEY +(`col14`), FULLTEXT KEY (`col15`), FULLTEXT KEY (`col16`), FULLTEXT KEY +(`col17`), FULLTEXT KEY (`col18`), FULLTEXT KEY (`col19`), FULLTEXT KEY +(`col20`), FULLTEXT KEY (`col21`), FULLTEXT KEY (`col22`), FULLTEXT KEY +(`col23`), FULLTEXT KEY (`col24`), FULLTEXT KEY (`col25`), FULLTEXT KEY +(`col26`), FULLTEXT KEY (`col27`), FULLTEXT KEY (`col28`), FULLTEXT KEY +(`col29`), FULLTEXT KEY (`col30`)) engine=innodb; +#create procedure to inset into the table. +CREATE PROCEDURE `proc_insert`(IN tab_name VARCHAR(40)) +BEGIN +DECLARE i INT DEFAULT 1; +SET @insert_tbl =CONCAT('INSERT INTO ', tab_name, '( `col01`, `col02`, + `col03`, `col04`, `col05`, `col06`, `col07`, `col08`, `col09`, `col10`, + `col11`, `col12`, `col13`, `col14`, `col15`, `col16`, `col17`, `col18`, + `col19`, `col20`, `col21`, `col22`, `col23`, `col24`, `col25`, `col26`, + `col27`, `col28`, `col29`, `col30`) + VALUES ( MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), + MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), + MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), + MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), + MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), + MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), + MD5(RAND()))'); +PREPARE ins_stmt FROM @insert_tbl; +while (i <= 2000) DO +EXECUTE ins_stmt; +SET i = i + 1; +END WHILE; +DEALLOCATE PREPARE ins_stmt; +END | +SET @save_dbug= @@GLOBAL.debug_dbug; +SET GLOBAL debug_dbug="+d,fts_optimize_wq_count_check"; +connect con1,localhost,root,,; +call proc_insert('t1'); +connect con2,localhost,root,,; +call proc_insert('t1'); +connect con3,localhost,root,,; +call proc_insert('t2'); +connect con4,localhost,root,,; +call proc_insert('t2'); +connect con5,localhost,root,,; +call proc_insert('t3'); +connect con6,localhost,root,,; +call proc_insert('t3'); +connect con7,localhost,root,,; +call proc_insert('t4'); +connection default; +call proc_insert('t4'); +SET GLOBAL debug_dbug= @save_dbug; +connection con1; +disconnect con1; +connection con2; +disconnect con2; +connection con3; +disconnect con3; +connection con4; +disconnect con4; +connection con5; +disconnect con5; +connection con6; +disconnect con6; +connection con7; +disconnect con7; +connection default; +DROP TABLE t1,t2,t3,t4; +DROP PROCEDURE proc_insert; diff --git a/mysql-test/suite/innodb_fts/t/bug_32831765.test b/mysql-test/suite/innodb_fts/t/bug_32831765.test new file mode 100644 index 00000000000..a4551cf91ef --- /dev/null +++ b/mysql-test/suite/innodb_fts/t/bug_32831765.test @@ -0,0 +1,164 @@ +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/big_test.inc + +--echo # +--echo # Bug#32831765 SERVER HITS OOM CONDITION WHEN LOADING TWO +--echo # INNODB TABLES WITH FTS INDEXES +--echo # + +create table t1 ( `id` int unsigned NOT NULL AUTO_INCREMENT, `col01` text, +`col02` text, `col03` text, `col04` text, `col05` text, `col06` text, `col07` +text, `col08` text, `col09` text, `col10` text, `col11` text, `col12` text, +`col13` text, `col14` text, `col15` text, `col16` text, `col17` text, `col18` +text, `col19` text, `col20` text, `col21` text, `col22` text, `col23` text, +`col24` text, `col25` text, `col26` text, `col27` text, `col28` text, `col29` +text, `col30` text, PRIMARY KEY (`id`), FULLTEXT KEY (`col01`), FULLTEXT KEY +(`col02`), FULLTEXT KEY (`col03`), FULLTEXT KEY (`col04`), FULLTEXT KEY +(`col05`), FULLTEXT KEY (`col06`), FULLTEXT KEY (`col07`), FULLTEXT KEY +(`col08`), FULLTEXT KEY (`col09`), FULLTEXT KEY (`col10`), FULLTEXT KEY +(`col11`), FULLTEXT KEY (`col12`), FULLTEXT KEY (`col13`), FULLTEXT KEY +(`col14`), FULLTEXT KEY (`col15`), FULLTEXT KEY (`col16`), FULLTEXT KEY +(`col17`), FULLTEXT KEY (`col18`), FULLTEXT KEY (`col19`), FULLTEXT KEY +(`col20`), FULLTEXT KEY (`col21`), FULLTEXT KEY (`col22`), FULLTEXT KEY +(`col23`), FULLTEXT KEY (`col24`), FULLTEXT KEY (`col25`), FULLTEXT KEY +(`col26`), FULLTEXT KEY (`col27`), FULLTEXT KEY (`col28`), FULLTEXT KEY +(`col29`), FULLTEXT KEY (`col30`)) engine=innodb; + +create table t2 ( `id` int unsigned NOT NULL AUTO_INCREMENT, `col01` text, +`col02` text, `col03` text, `col04` text, `col05` text, `col06` text, `col07` +text, `col08` text, `col09` text, `col10` text, `col11` text, `col12` text, +`col13` text, `col14` text, `col15` text, `col16` text, `col17` text, `col18` +text, `col19` text, `col20` text, `col21` text, `col22` text, `col23` text, +`col24` text, `col25` text, `col26` text, `col27` text, `col28` text, `col29` +text, `col30` text, PRIMARY KEY (`id`), FULLTEXT KEY (`col01`), FULLTEXT KEY +(`col02`), FULLTEXT KEY (`col03`), FULLTEXT KEY (`col04`), FULLTEXT KEY +(`col05`), FULLTEXT KEY (`col06`), FULLTEXT KEY (`col07`), FULLTEXT KEY +(`col08`), FULLTEXT KEY (`col09`), FULLTEXT KEY (`col10`), FULLTEXT KEY +(`col11`), FULLTEXT KEY (`col12`), FULLTEXT KEY (`col13`), FULLTEXT KEY +(`col14`), FULLTEXT KEY (`col15`), FULLTEXT KEY (`col16`), FULLTEXT KEY +(`col17`), FULLTEXT KEY (`col18`), FULLTEXT KEY (`col19`), FULLTEXT KEY +(`col20`), FULLTEXT KEY (`col21`), FULLTEXT KEY (`col22`), FULLTEXT KEY +(`col23`), FULLTEXT KEY (`col24`), FULLTEXT KEY (`col25`), FULLTEXT KEY +(`col26`), FULLTEXT KEY (`col27`), FULLTEXT KEY (`col28`), FULLTEXT KEY +(`col29`), FULLTEXT KEY (`col30`)) engine=innodb; + + +create table t3 ( `id` int unsigned NOT NULL AUTO_INCREMENT, `col01` text, +`col02` text, `col03` text, `col04` text, `col05` text, `col06` text, `col07` +text, `col08` text, `col09` text, `col10` text, `col11` text, `col12` text, +`col13` text, `col14` text, `col15` text, `col16` text, `col17` text, `col18` +text, `col19` text, `col20` text, `col21` text, `col22` text, `col23` text, +`col24` text, `col25` text, `col26` text, `col27` text, `col28` text, `col29` +text, `col30` text, PRIMARY KEY (`id`), FULLTEXT KEY (`col01`), FULLTEXT KEY +(`col02`), FULLTEXT KEY (`col03`), FULLTEXT KEY (`col04`), FULLTEXT KEY +(`col05`), FULLTEXT KEY (`col06`), FULLTEXT KEY (`col07`), FULLTEXT KEY +(`col08`), FULLTEXT KEY (`col09`), FULLTEXT KEY (`col10`), FULLTEXT KEY +(`col11`), FULLTEXT KEY (`col12`), FULLTEXT KEY (`col13`), FULLTEXT KEY +(`col14`), FULLTEXT KEY (`col15`), FULLTEXT KEY (`col16`), FULLTEXT KEY +(`col17`), FULLTEXT KEY (`col18`), FULLTEXT KEY (`col19`), FULLTEXT KEY +(`col20`), FULLTEXT KEY (`col21`), FULLTEXT KEY (`col22`), FULLTEXT KEY +(`col23`), FULLTEXT KEY (`col24`), FULLTEXT KEY (`col25`), FULLTEXT KEY +(`col26`), FULLTEXT KEY (`col27`), FULLTEXT KEY (`col28`), FULLTEXT KEY +(`col29`), FULLTEXT KEY (`col30`)) engine=innodb; + +create table t4 ( `id` int unsigned NOT NULL AUTO_INCREMENT, `col01` text, +`col02` text, `col03` text, `col04` text, `col05` text, `col06` text, `col07` +text, `col08` text, `col09` text, `col10` text, `col11` text, `col12` text, +`col13` text, `col14` text, `col15` text, `col16` text, `col17` text, `col18` +text, `col19` text, `col20` text, `col21` text, `col22` text, `col23` text, +`col24` text, `col25` text, `col26` text, `col27` text, `col28` text, `col29` +text, `col30` text, PRIMARY KEY (`id`), FULLTEXT KEY (`col01`), FULLTEXT KEY +(`col02`), FULLTEXT KEY (`col03`), FULLTEXT KEY (`col04`), FULLTEXT KEY +(`col05`), FULLTEXT KEY (`col06`), FULLTEXT KEY (`col07`), FULLTEXT KEY +(`col08`), FULLTEXT KEY (`col09`), FULLTEXT KEY (`col10`), FULLTEXT KEY +(`col11`), FULLTEXT KEY (`col12`), FULLTEXT KEY (`col13`), FULLTEXT KEY +(`col14`), FULLTEXT KEY (`col15`), FULLTEXT KEY (`col16`), FULLTEXT KEY +(`col17`), FULLTEXT KEY (`col18`), FULLTEXT KEY (`col19`), FULLTEXT KEY +(`col20`), FULLTEXT KEY (`col21`), FULLTEXT KEY (`col22`), FULLTEXT KEY +(`col23`), FULLTEXT KEY (`col24`), FULLTEXT KEY (`col25`), FULLTEXT KEY +(`col26`), FULLTEXT KEY (`col27`), FULLTEXT KEY (`col28`), FULLTEXT KEY +(`col29`), FULLTEXT KEY (`col30`)) engine=innodb; + +delimiter |; + +--echo #create procedure to inset into the table. +CREATE PROCEDURE `proc_insert`(IN tab_name VARCHAR(40)) +BEGIN + DECLARE i INT DEFAULT 1; + SET @insert_tbl =CONCAT('INSERT INTO ', tab_name, '( `col01`, `col02`, + `col03`, `col04`, `col05`, `col06`, `col07`, `col08`, `col09`, `col10`, + `col11`, `col12`, `col13`, `col14`, `col15`, `col16`, `col17`, `col18`, + `col19`, `col20`, `col21`, `col22`, `col23`, `col24`, `col25`, `col26`, + `col27`, `col28`, `col29`, `col30`) + VALUES ( MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), + MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), + MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), + MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), + MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), + MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), MD5(RAND()), + MD5(RAND()))'); + PREPARE ins_stmt FROM @insert_tbl; + while (i <= 2000) DO + EXECUTE ins_stmt; + SET i = i + 1; + END WHILE; + DEALLOCATE PREPARE ins_stmt; +END | + +delimiter ;| + +# Ensure that the number of SYNC requests will not exceed 1000. +SET @save_dbug= @@GLOBAL.debug_dbug; +SET GLOBAL debug_dbug="+d,fts_optimize_wq_count_check"; + +connect (con1,localhost,root,,); +send call proc_insert('t1'); +connect (con2,localhost,root,,); +send call proc_insert('t1'); +connect (con3,localhost,root,,); +send call proc_insert('t2'); +connect (con4,localhost,root,,); +send call proc_insert('t2'); +connect (con5,localhost,root,,); +send call proc_insert('t3'); +connect (con6,localhost,root,,); +send call proc_insert('t3'); +connect (con7,localhost,root,,); +send call proc_insert('t4'); + +connection default; +call proc_insert('t4'); +SET GLOBAL debug_dbug= @save_dbug; + +connection con1; +reap; +disconnect con1; + +connection con2; +reap; +disconnect con2; + +connection con3; +reap; +disconnect con3; + +connection con4; +reap; +disconnect con4; + +connection con5; +reap; +disconnect con5; + +connection con6; +reap; +disconnect con6; + +connection con7; +reap; +disconnect con7; + +connection default; +DROP TABLE t1,t2,t3,t4; +DROP PROCEDURE proc_insert; diff --git a/storage/innobase/fts/fts0opt.cc b/storage/innobase/fts/fts0opt.cc index 2d506a757a0..13e01befe55 100644 --- a/storage/innobase/fts/fts0opt.cc +++ b/storage/innobase/fts/fts0opt.cc @@ -2641,6 +2641,9 @@ fts_optimize_request_sync_table( ib_wqueue_add(fts_optimize_wq, msg, msg->heap, true); + DBUG_EXECUTE_IF("fts_optimize_wq_count_check", + DBUG_ASSERT(fts_optimize_wq->length <= 1000);); + mutex_exit(&fts_optimize_wq->mutex); } -- cgit v1.2.1 From fbb1e92e2553cbc98fbbbe23399f2a4342de41fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 21 Oct 2021 14:35:23 +0300 Subject: MDEV-19522 fixup: Integer type mismatch in unit test --- storage/innobase/unittest/innodb_fts-t.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/innobase/unittest/innodb_fts-t.cc b/storage/innobase/unittest/innodb_fts-t.cc index 57585e337c2..4e10c0e439e 100644 --- a/storage/innobase/unittest/innodb_fts-t.cc +++ b/storage/innobase/unittest/innodb_fts-t.cc @@ -5,7 +5,7 @@ struct fts_encode_info { const byte buf[10]; - int32_t len; + size_t len; doc_id_t val; }; @@ -40,7 +40,7 @@ int main(int, char**) { byte buf[10]; const byte* fts_buf= buf; - int32_t len= fts_encode_int(fts_info[i].val, buf) - &buf[0]; + size_t len= fts_encode_int(fts_info[i].val, buf) - &buf[0]; if (fts_info[i].len == len && !memcmp(&fts_info[i].buf, buf, len) && fts_decode_vlc(&fts_buf) == fts_info[i].val && -- cgit v1.2.1