summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--extra/mariabackup/backup_mysql.cc8
-rw-r--r--extra/mariabackup/xtrabackup.cc20
-rw-r--r--mysql-test/include/mtr_warnings.sql2
-rw-r--r--mysql-test/main/sp-vars.result6
-rw-r--r--mysql-test/main/sp-vars.test6
-rw-r--r--mysql-test/main/subselect4.result116
-rw-r--r--mysql-test/main/subselect4.test74
-rw-r--r--mysql-test/main/type_row.result23
-rw-r--r--mysql-test/main/type_row.test28
-rw-r--r--mysql-test/suite/compat/oracle/r/sp-row.result8
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-row.test8
-rw-r--r--mysql-test/suite/galera/r/galera_FK_duplicate_client_insert.result40
-rw-r--r--mysql-test/suite/galera/t/galera_FK_duplicate_client_insert.test4
-rw-r--r--mysql-test/suite/innodb/r/cursor-restore-locking.result4
-rw-r--r--mysql-test/suite/innodb/r/deadlock_victim_race.result42
-rw-r--r--mysql-test/suite/innodb/r/foreign_key_not_windows.result17
-rw-r--r--mysql-test/suite/innodb/r/update-cascade.result8
-rw-r--r--mysql-test/suite/innodb/t/cursor-restore-locking.test6
-rw-r--r--mysql-test/suite/innodb/t/deadlock_victim_race.test102
-rw-r--r--mysql-test/suite/innodb/t/foreign_key_not_windows.test34
-rw-r--r--mysql-test/suite/innodb/t/update-cascade.test8
-rw-r--r--mysql-test/suite/innodb_fts/r/innodb_fts_plugin.result36
-rw-r--r--mysql-test/suite/innodb_fts/t/innodb_fts_plugin.test24
-rw-r--r--mysql-test/suite/mariabackup/backup_grants.result8
-rw-r--r--mysql-test/suite/mariabackup/backup_grants.test59
-rw-r--r--mysql-test/suite/period/r/alter.result14
-rw-r--r--mysql-test/suite/period/t/alter.test23
-rw-r--r--mysql-test/suite/rpl/r/rpl_filter_set_var_missing_data.result55
-rw-r--r--mysql-test/suite/rpl/t/rpl_filter_set_var_missing_data.test83
-rw-r--r--mysys/my_win_popen.cc2
-rw-r--r--plugin/type_uuid/mysql-test/type_uuid/type_uuid.result16
-rw-r--r--plugin/type_uuid/mysql-test/type_uuid/type_uuid.test12
-rw-r--r--sql/field.cc12
-rw-r--r--sql/item_subselect.cc3
-rw-r--r--sql/sql_acl.cc94
-rw-r--r--sql/sql_lex.h6
-rw-r--r--sql/sql_table.cc2
-rw-r--r--sql/table.cc9
-rw-r--r--sql/table.h1
-rw-r--r--sql/vers_string.h2
-rw-r--r--storage/connect/domdoc.cpp1
-rw-r--r--storage/innobase/btr/btr0btr.cc6
-rw-r--r--storage/innobase/btr/btr0cur.cc10
-rw-r--r--storage/innobase/btr/btr0sea.cc4
-rw-r--r--storage/innobase/buf/buf0buf.cc6
-rw-r--r--storage/innobase/dict/dict0crea.cc2
-rw-r--r--storage/innobase/dict/dict0dict.cc10
-rw-r--r--storage/innobase/dict/drop.cc3
-rw-r--r--storage/innobase/handler/ha_innodb.cc2
-rw-r--r--storage/innobase/include/mtr0mtr.h6
-rw-r--r--storage/innobase/include/trx0trx.h30
-rw-r--r--storage/innobase/lock/lock0lock.cc107
-rw-r--r--storage/innobase/mtr/mtr0mtr.cc52
-rw-r--r--storage/innobase/os/os0file.cc2
-rw-r--r--storage/innobase/trx/trx0purge.cc4
-rw-r--r--storage/innobase/trx/trx0rec.cc4
-rw-r--r--storage/innobase/trx/trx0roll.cc10
-rw-r--r--storage/innobase/trx/trx0trx.cc3
-rw-r--r--strings/ctype-czech.c75
-rw-r--r--strings/ctype-latin1.c16
-rw-r--r--tpool/tpool_structs.h130
61 files changed, 1150 insertions, 358 deletions
diff --git a/extra/mariabackup/backup_mysql.cc b/extra/mariabackup/backup_mysql.cc
index 0e5c9e43e68..9a09da21d74 100644
--- a/extra/mariabackup/backup_mysql.cc
+++ b/extra/mariabackup/backup_mysql.cc
@@ -305,7 +305,7 @@ check_server_version(ulong version_number, const char *version_string)
}
/*********************************************************************//**
-Receive options important for XtraBackup from MySQL server.
+Receive options important for XtraBackup from server.
@return true on success. */
bool get_mysql_vars(MYSQL *connection)
{
@@ -1773,8 +1773,8 @@ static std::string make_local_paths(const char *data_file_path)
bool write_backup_config_file()
{
int rc= backup_file_printf("backup-my.cnf",
- "# This MySQL options file was generated by innobackupex.\n\n"
- "# The MySQL server\n"
+ "# This options file was generated by innobackupex.\n\n"
+ "# The server\n"
"[mysqld]\n"
"innodb_checksum_algorithm=%s\n"
"innodb_data_file_path=%s\n"
@@ -1859,7 +1859,7 @@ flush_changed_page_bitmaps()
/*********************************************************************//**
-Deallocate memory, disconnect from MySQL server, etc.
+Deallocate memory, disconnect from server, etc.
@return true on success. */
void
backup_cleanup()
diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc
index 55c32d10ac0..9b6a0561552 100644
--- a/extra/mariabackup/xtrabackup.cc
+++ b/extra/mariabackup/xtrabackup.cc
@@ -6227,22 +6227,28 @@ static bool check_all_privileges()
}
/* KILL ... */
- if ((!opt_no_lock && (opt_kill_long_queries_timeout || opt_lock_ddl_per_table))
- /* START SLAVE SQL_THREAD */
- /* STOP SLAVE SQL_THREAD */
- || opt_safe_slave_backup) {
+ if (!opt_no_lock && (opt_kill_long_queries_timeout || opt_kill_long_query_type)) {
check_result |= check_privilege(
granted_privileges,
- "SUPER", "*", "*",
+ "CONNECTION ADMIN", "*", "*",
+ PRIVILEGE_WARNING);
+ }
+
+ /* START SLAVE SQL_THREAD */
+ /* STOP SLAVE SQL_THREAD */
+ if (opt_safe_slave_backup) {
+ check_result |= check_privilege(
+ granted_privileges,
+ "REPLICATION SLAVE ADMIN", "*", "*",
PRIVILEGE_WARNING);
}
/* SHOW MASTER STATUS */
/* SHOW SLAVE STATUS */
if (opt_galera_info || opt_slave_info
- || (opt_no_lock && opt_safe_slave_backup)) {
+ || opt_safe_slave_backup) {
check_result |= check_privilege(granted_privileges,
- "REPLICATION CLIENT", "*", "*",
+ "SLAVE MONITOR", "*", "*",
PRIVILEGE_WARNING);
}
diff --git a/mysql-test/include/mtr_warnings.sql b/mysql-test/include/mtr_warnings.sql
index 06fad4204f7..d02f504a32d 100644
--- a/mysql-test/include/mtr_warnings.sql
+++ b/mysql-test/include/mtr_warnings.sql
@@ -173,7 +173,7 @@ INSERT INTO global_suppressions VALUES
/* Added 2009-08-XX after fixing Bug #42408 */
- ("Although a path was specified for the .* option, log tables are used"),
+ ("Although a .* file was specified, log tables are used. To enable logging to files "),
("Backup: Operation aborted"),
("Restore: Operation aborted"),
("Restore: The grant .* was skipped because the user does not exist"),
diff --git a/mysql-test/main/sp-vars.result b/mysql-test/main/sp-vars.result
index feef67ccb1d..b90542dfb49 100644
--- a/mysql-test/main/sp-vars.result
+++ b/mysql-test/main/sp-vars.result
@@ -1026,11 +1026,11 @@ BEGIN
SELECT arg;
END|
CALL p1((1, 2));
-ERROR 21000: Operand should contain 1 column(s)
+ERROR HY000: Cannot cast 'row' as 'tinyint' in assignment of `arg`
CALL p1((SELECT * FROM t1 LIMIT 1));
-ERROR 21000: Operand should contain 1 column(s)
+ERROR HY000: Cannot cast 'row' as 'tinyint' in assignment of `arg`
CALL p1((SELECT col1, col2 FROM t1 LIMIT 1));
-ERROR 21000: Operand should contain 1 column(s)
+ERROR HY000: Cannot cast 'row' as 'tinyint' in assignment of `arg`
DROP PROCEDURE p1;
DROP TABLE t1;
diff --git a/mysql-test/main/sp-vars.test b/mysql-test/main/sp-vars.test
index 9edf245acbe..5e1e07888ca 100644
--- a/mysql-test/main/sp-vars.test
+++ b/mysql-test/main/sp-vars.test
@@ -1221,13 +1221,13 @@ BEGIN
END|
delimiter ;|
---error ER_OPERAND_COLUMNS
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
CALL p1((1, 2));
---error ER_OPERAND_COLUMNS
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
CALL p1((SELECT * FROM t1 LIMIT 1));
---error ER_OPERAND_COLUMNS
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
CALL p1((SELECT col1, col2 FROM t1 LIMIT 1));
#
diff --git a/mysql-test/main/subselect4.result b/mysql-test/main/subselect4.result
index f3277c285d4..d00f7ccd690 100644
--- a/mysql-test/main/subselect4.result
+++ b/mysql-test/main/subselect4.result
@@ -3054,6 +3054,122 @@ a
3
2
drop table t1,t2,t3;
+#
+# MDEV-29139: Redundant IN/ALL/ANY predicand in GROUP BY clause of
+# IN/ALL/ANY/EXISTS subquery
+#
+create table t1 (a int);
+create table t2 (b int);
+create table t3 (c int);
+create table t4 (d int);
+insert into t1 values (3), (1);
+insert into t2 values (3), (2);
+insert into t3 values (4), (2);
+insert into t4 values (1), (7);
+explain extended select b from t2
+where exists (select c from t3
+group by (select a from t1 where a = 1) in (select d from t4));
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00
+2 SUBQUERY t3 ALL NULL NULL NULL NULL 2 100.00
+Warnings:
+Note 1003 /* select#1 */ select `test`.`t2`.`b` AS `b` from `test`.`t2` where 1
+select b from t2
+where exists (select c from t3
+group by (select a from t1 where a = 1) in (select d from t4));
+b
+3
+2
+prepare stmt from "select b from t2
+where exists (select c from t3
+group by (select a from t1 where a = 1) in (select d from t4))";
+execute stmt;
+b
+3
+2
+execute stmt;
+b
+3
+2
+deallocate prepare stmt;
+explain extended select b from t2
+where exists (select c from t3
+group by (select a from t1 where a = 1) >=
+any (select d from t4));
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00
+2 SUBQUERY t3 ALL NULL NULL NULL NULL 2 100.00
+Warnings:
+Note 1003 /* select#1 */ select `test`.`t2`.`b` AS `b` from `test`.`t2` where 1
+select b from t2
+where exists (select c from t3
+group by (select a from t1 where a = 1) >=
+any (select d from t4));
+b
+3
+2
+explain extended select b from t2
+where exists (select c from t3
+group by (select a from t1 where a = 1) <
+all (select d from t4));
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00
+2 SUBQUERY t3 ALL NULL NULL NULL NULL 2 100.00
+Warnings:
+Note 1003 /* select#1 */ select `test`.`t2`.`b` AS `b` from `test`.`t2` where 1
+select b from t2
+where exists (select c from t3
+group by (select a from t1 where a = 1) <
+all (select d from t4));
+b
+3
+2
+explain extended select b from t2
+where b in (select c from t3
+group by (select a from t1 where a = 1) in (select d from t4));
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00
+1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1 100.00
+2 MATERIALIZED t3 ALL NULL NULL NULL NULL 2 100.00
+Warnings:
+Note 1003 select `test`.`t2`.`b` AS `b` from `test`.`t2` semi join (`test`.`t3`) where 1
+select b from t2
+where b in (select c from t3
+group by (select a from t1 where a = 1) in (select d from t4));
+b
+2
+explain extended select b from t2
+where b >= any (select c from t3
+group by (select a from t1 where a = 1) in
+(select d from t4));
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00 Using where
+2 SUBQUERY t3 ALL NULL NULL NULL NULL 2 100.00
+Warnings:
+Note 1003 /* select#1 */ select `test`.`t2`.`b` AS `b` from `test`.`t2` where <nop>(<in_optimizer>(`test`.`t2`.`b`,(/* select#2 */ select min(`test`.`t3`.`c`) from `test`.`t3`) <= <cache>(`test`.`t2`.`b`)))
+select b from t2
+where b >= any (select c from t3
+group by (select a from t1 where a = 1) in
+(select d from t4));
+b
+3
+2
+explain extended select b from t2
+where b <= all (select c from t3
+group by (select a from t1 where a = 1) in
+(select d from t4));
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2 100.00 Using where
+2 SUBQUERY t3 ALL NULL NULL NULL NULL 2 100.00
+Warnings:
+Note 1003 /* select#1 */ select `test`.`t2`.`b` AS `b` from `test`.`t2` where <not>(<in_optimizer>(`test`.`t2`.`b`,<min>(/* select#2 */ select `test`.`t3`.`c` from `test`.`t3`) < <cache>(`test`.`t2`.`b`)))
+select b from t2
+where b <= all (select c from t3
+group by (select a from t1 where a = 1) in
+(select d from t4));
+b
+2
+drop table t1,t2,t3,t4;
# End of 10.3 tests
#
# MDEV-19134: EXISTS() slower if ORDER BY is defined
diff --git a/mysql-test/main/subselect4.test b/mysql-test/main/subselect4.test
index ffad0d28b58..8e1df40022a 100644
--- a/mysql-test/main/subselect4.test
+++ b/mysql-test/main/subselect4.test
@@ -2481,6 +2481,80 @@ eval $q3;
drop table t1,t2,t3;
+--echo #
+--echo # MDEV-29139: Redundant IN/ALL/ANY predicand in GROUP BY clause of
+--echo # IN/ALL/ANY/EXISTS subquery
+--echo #
+
+create table t1 (a int);
+create table t2 (b int);
+create table t3 (c int);
+create table t4 (d int);
+
+insert into t1 values (3), (1);
+insert into t2 values (3), (2);
+insert into t3 values (4), (2);
+insert into t4 values (1), (7);
+
+let $q1=
+select b from t2
+ where exists (select c from t3
+ group by (select a from t1 where a = 1) in (select d from t4));
+
+eval explain extended $q1;
+eval $q1;
+
+eval prepare stmt from "$q1";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+let $q2=
+select b from t2
+ where exists (select c from t3
+ group by (select a from t1 where a = 1) >=
+ any (select d from t4));
+
+eval explain extended $q2;
+eval $q2;
+
+let $q3=
+select b from t2
+ where exists (select c from t3
+ group by (select a from t1 where a = 1) <
+ all (select d from t4));
+
+eval explain extended $q3;
+eval $q3;
+
+let $q4=
+select b from t2
+ where b in (select c from t3
+ group by (select a from t1 where a = 1) in (select d from t4));
+
+eval explain extended $q4;
+eval $q4;
+
+let $q5=
+select b from t2
+ where b >= any (select c from t3
+ group by (select a from t1 where a = 1) in
+ (select d from t4));
+
+eval explain extended $q5;
+eval $q5;
+
+let $q6=
+select b from t2
+ where b <= all (select c from t3
+ group by (select a from t1 where a = 1) in
+ (select d from t4));
+
+eval explain extended $q6;
+eval $q6;
+
+drop table t1,t2,t3,t4;
+
--echo # End of 10.3 tests
--echo #
diff --git a/mysql-test/main/type_row.result b/mysql-test/main/type_row.result
index dc74cfc88a4..45f4fd895bf 100644
--- a/mysql-test/main/type_row.result
+++ b/mysql-test/main/type_row.result
@@ -49,3 +49,26 @@ ERROR HY000: Illegal parameter data types row and int for operation 'MOD'
#
# End of 10.5 tests
#
+#
+# Start of 10.7 tests
+#
+#
+# MDEV-29356 Assertion `0' failed in Type_handler_row::Item_save_in_field on INSERT
+#
+SET sql_mode='';
+CREATE TABLE t1 (c1 INT,c2 INT);
+CREATE TRIGGER t BEFORE INSERT ON t1 FOR EACH ROW SET NEW.c1=(SELECT * FROM t1);
+INSERT INTO t1 VALUES (0,0);
+ERROR HY000: Cannot cast 'row' as 'int' in assignment of `test`.`t1`.`c1`
+DROP TABLE t1;
+SET sql_mode=DEFAULT;
+SET sql_mode='';
+CREATE TABLE t1 (c1 INT,c2 INT) ENGINE=MyISAM;
+CREATE TRIGGER t BEFORE INSERT ON t1 FOR EACH ROW SET NEW.c1=ROW(1,1);
+INSERT INTO t1 VALUES (0,0);
+ERROR HY000: Cannot cast 'row' as 'int' in assignment of `test`.`t1`.`c1`
+DROP TABLE t1;
+SET sql_mode=DEFAULT;
+#
+# End of 10.7 tests
+#
diff --git a/mysql-test/main/type_row.test b/mysql-test/main/type_row.test
index 2a5902351e2..6f8312d5f18 100644
--- a/mysql-test/main/type_row.test
+++ b/mysql-test/main/type_row.test
@@ -60,3 +60,31 @@ SELECT ROW(1,1) % 1;
--echo #
--echo # End of 10.5 tests
--echo #
+
+--echo #
+--echo # Start of 10.7 tests
+--echo #
+
+--echo #
+--echo # MDEV-29356 Assertion `0' failed in Type_handler_row::Item_save_in_field on INSERT
+--echo #
+
+SET sql_mode='';
+CREATE TABLE t1 (c1 INT,c2 INT);
+CREATE TRIGGER t BEFORE INSERT ON t1 FOR EACH ROW SET NEW.c1=(SELECT * FROM t1);
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+INSERT INTO t1 VALUES (0,0);
+DROP TABLE t1;
+SET sql_mode=DEFAULT;
+
+SET sql_mode='';
+CREATE TABLE t1 (c1 INT,c2 INT) ENGINE=MyISAM;
+CREATE TRIGGER t BEFORE INSERT ON t1 FOR EACH ROW SET NEW.c1=ROW(1,1);
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
+INSERT INTO t1 VALUES (0,0);
+DROP TABLE t1;
+SET sql_mode=DEFAULT;
+
+--echo #
+--echo # End of 10.7 tests
+--echo #
diff --git a/mysql-test/suite/compat/oracle/r/sp-row.result b/mysql-test/suite/compat/oracle/r/sp-row.result
index 7fd986a71c8..c978d4be983 100644
--- a/mysql-test/suite/compat/oracle/r/sp-row.result
+++ b/mysql-test/suite/compat/oracle/r/sp-row.result
@@ -24,7 +24,7 @@ RETURN a;
END;
$$
SELECT f1(ROW(10,20));
-ERROR 21000: Operand should contain 1 column(s)
+ERROR HY000: Cannot cast 'row' as 'int' in assignment of `f1(ROW(10,20))`
DROP FUNCTION f1;
#
# ROW as an SP parameter
@@ -261,7 +261,7 @@ SELECT f1(a);
END;
$$
CALL p1();
-ERROR 21000: Operand should contain 1 column(s)
+ERROR HY000: Cannot cast 'row' as 'int' in assignment of `a`
DROP PROCEDURE p1;
DROP FUNCTION f1;
CREATE FUNCTION f1(a INT) RETURN INT
@@ -278,7 +278,7 @@ SELECT f1(a);
END;
$$
CALL p1();
-ERROR 21000: Operand should contain 1 column(s)
+ERROR HY000: Cannot cast 'row' as 'int' in assignment of `a`
DROP PROCEDURE p1;
DROP FUNCTION f1;
#
@@ -332,7 +332,7 @@ RETURN rec;
END;
$$
SELECT f1(10);
-ERROR 21000: Operand should contain 1 column(s)
+ERROR HY000: Cannot cast 'row' as 'int' in assignment of `f1(10)`
DROP FUNCTION f1;
#
# Using the entire ROW in SELECT..CREATE
diff --git a/mysql-test/suite/compat/oracle/t/sp-row.test b/mysql-test/suite/compat/oracle/t/sp-row.test
index ebd0a2a2137..c7658c76838 100644
--- a/mysql-test/suite/compat/oracle/t/sp-row.test
+++ b/mysql-test/suite/compat/oracle/t/sp-row.test
@@ -35,7 +35,7 @@ BEGIN
END;
$$
DELIMITER ;$$
---error ER_OPERAND_COLUMNS
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
SELECT f1(ROW(10,20));
DROP FUNCTION f1;
@@ -334,7 +334,7 @@ BEGIN
END;
$$
DELIMITER ;$$
---error ER_OPERAND_COLUMNS
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
CALL p1();
DROP PROCEDURE p1;
DROP FUNCTION f1;
@@ -355,7 +355,7 @@ BEGIN
END;
$$
DELIMITER ;$$
---error ER_OPERAND_COLUMNS
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
CALL p1();
DROP PROCEDURE p1;
DROP FUNCTION f1;
@@ -427,7 +427,7 @@ BEGIN
END;
$$
DELIMITER ;$$
---error ER_OPERAND_COLUMNS
+--error ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION
SELECT f1(10);
DROP FUNCTION f1;
diff --git a/mysql-test/suite/galera/r/galera_FK_duplicate_client_insert.result b/mysql-test/suite/galera/r/galera_FK_duplicate_client_insert.result
index 5ae577a6323..b40c57c5d90 100644
--- a/mysql-test/suite/galera/r/galera_FK_duplicate_client_insert.result
+++ b/mysql-test/suite/galera/r/galera_FK_duplicate_client_insert.result
@@ -14,7 +14,7 @@ connection node_1_u;
begin;
update user set j = j + 1 WHERE id > 0;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -32,7 +32,7 @@ connection node_1_u;
begin;
update user set j = j + 1 WHERE id > 0;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -50,7 +50,7 @@ connection node_1_u;
begin;
update user set j = j + 1 WHERE id > 0;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -68,7 +68,7 @@ connection node_1_u;
begin;
update user set j = j + 1 WHERE id > 0;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -86,7 +86,7 @@ connection node_1_u;
begin;
update user set j = j + 1 WHERE id > 0;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -104,7 +104,7 @@ connection node_1_u;
begin;
update user set j = j + 1 WHERE id > 0;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -122,7 +122,7 @@ connection node_1_u;
begin;
update user set j = j + 1 WHERE id > 0;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -140,7 +140,7 @@ connection node_1_u;
begin;
update user set j = j + 1 WHERE id > 0;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -158,7 +158,7 @@ connection node_1_u;
begin;
update user set j = j + 1 WHERE id > 0;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -176,7 +176,7 @@ connection node_1_u;
begin;
update user set j = j + 1 WHERE id > 0;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
insert into user_session(id,fk1,fk2) values (2, 2, 2);
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -202,7 +202,7 @@ connection node_1_u;
begin;
execute upd;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
execute ins1;
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -220,7 +220,7 @@ connection node_1_u;
begin;
execute upd;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
execute ins1;
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -238,7 +238,7 @@ connection node_1_u;
begin;
execute upd;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
execute ins1;
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -256,7 +256,7 @@ connection node_1_u;
begin;
execute upd;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
execute ins1;
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -274,7 +274,7 @@ connection node_1_u;
begin;
execute upd;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
execute ins1;
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -292,7 +292,7 @@ connection node_1_u;
begin;
execute upd;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
execute ins1;
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -310,7 +310,7 @@ connection node_1_u;
begin;
execute upd;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
execute ins1;
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -328,7 +328,7 @@ connection node_1_u;
begin;
execute upd;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
execute ins1;
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -346,7 +346,7 @@ connection node_1_u;
begin;
execute upd;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
execute ins1;
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
@@ -364,7 +364,7 @@ connection node_1_u;
begin;
execute upd;
connection node_1_i;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
execute ins1;
connection node_1;
set debug_sync='now WAIT_FOR ins_waiting';
diff --git a/mysql-test/suite/galera/t/galera_FK_duplicate_client_insert.test b/mysql-test/suite/galera/t/galera_FK_duplicate_client_insert.test
index 02322fc02ec..6dd1fee1d4e 100644
--- a/mysql-test/suite/galera/t/galera_FK_duplicate_client_insert.test
+++ b/mysql-test/suite/galera/t/galera_FK_duplicate_client_insert.test
@@ -72,7 +72,7 @@ while($counter > 0)
update user set j = j + 1 WHERE id > 0;
--connection node_1_i
- set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+ set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
send insert into user_session(id,fk1,fk2) values (2, 2, 2);
--connection node_1
@@ -126,7 +126,7 @@ while($counter > 0)
#update user set j = j + 1 WHERE id > 0;
--connection node_1_i
- set debug_sync='lock_wait_suspend_thread_enter SIGNAL ins_waiting WAIT_FOR cont_ins';
+ set debug_sync='lock_wait_start SIGNAL ins_waiting WAIT_FOR cont_ins';
send execute ins1;
--connection node_1
diff --git a/mysql-test/suite/innodb/r/cursor-restore-locking.result b/mysql-test/suite/innodb/r/cursor-restore-locking.result
index bc1127f57b3..beeb5a87844 100644
--- a/mysql-test/suite/innodb/r/cursor-restore-locking.result
+++ b/mysql-test/suite/innodb/r/cursor-restore-locking.result
@@ -7,12 +7,12 @@ SET DEBUG_SYNC = 'innodb_row_search_for_mysql_exit SIGNAL first_del_row_search_m
DELETE FROM t WHERE b = 20;
connect con_ins_1,localhost,root,,;
SET DEBUG_SYNC = 'now WAIT_FOR first_del_row_search_mvcc_finished';
-SET DEBUG_SYNC = 'lock_wait_suspend_thread_enter SIGNAL first_ins_locked';
+SET DEBUG_SYNC = 'lock_wait_start SIGNAL first_ins_locked';
SET DEBUG_SYNC = 'ib_after_row_insert SIGNAL first_ins_row_inserted WAIT_FOR first_ins_cont';
INSERT INTO t VALUES(10, 20);
connect con_del_2,localhost,root,,;
SET DEBUG_SYNC = 'now WAIT_FOR first_ins_locked';
-SET DEBUG_SYNC = 'lock_wait_suspend_thread_enter SIGNAL second_del_locked';
+SET DEBUG_SYNC = 'lock_wait_start SIGNAL second_del_locked';
DELETE FROM t WHERE b = 20;
connection default;
SET DEBUG_SYNC = 'now WAIT_FOR second_del_locked';
diff --git a/mysql-test/suite/innodb/r/deadlock_victim_race.result b/mysql-test/suite/innodb/r/deadlock_victim_race.result
new file mode 100644
index 00000000000..9a7ef51ef24
--- /dev/null
+++ b/mysql-test/suite/innodb/r/deadlock_victim_race.result
@@ -0,0 +1,42 @@
+CREATE TABLE t (a int PRIMARY KEY, b int) engine = InnoDB;
+CREATE TABLE t2 (a int PRIMARY KEY) engine = InnoDB;
+INSERT INTO t VALUES (10, 10), (20, 20), (30, 30);
+INSERT INTO t2 VALUES (10), (20), (30);
+BEGIN;
+SELECT * FROM t WHERE a = 20 FOR UPDATE;
+a b
+20 20
+connect con_2,localhost,root,,;
+SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
+BEGIN;
+SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont EXECUTE 2';
+UPDATE t SET b = 100;
+connect con_3,localhost,root,,;
+BEGIN;
+UPDATE t2 SET a = a + 100;
+SELECT * FROM t WHERE a = 30 FOR UPDATE;
+a b
+30 30
+SET DEBUG_SYNC='now WAIT_FOR upd_locked';
+SET DEBUG_SYNC = 'lock_wait_start SIGNAL sel_locked';
+SELECT * FROM t WHERE a = 20 FOR UPDATE;
+connection default;
+SET DEBUG_SYNC='now WAIT_FOR sel_locked';
+ROLLBACK;
+SET DEBUG_SYNC='now SIGNAL upd_cont';
+SET innodb_lock_wait_timeout=1;
+SET DEBUG_SYNC="now WAIT_FOR upd_locked";
+SET DEBUG_SYNC="lock_wait_end SIGNAL upd_cont";
+SELECT * FROM t WHERE a = 10 FOR UPDATE;
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+connection con_3;
+a b
+20 20
+connection con_2;
+ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
+disconnect con_3;
+disconnect con_2;
+connection default;
+SET DEBUG_SYNC = 'RESET';
+DROP TABLE t;
+DROP TABLE t2;
diff --git a/mysql-test/suite/innodb/r/foreign_key_not_windows.result b/mysql-test/suite/innodb/r/foreign_key_not_windows.result
index 764ba911214..aaff06f8d68 100644
--- a/mysql-test/suite/innodb/r/foreign_key_not_windows.result
+++ b/mysql-test/suite/innodb/r/foreign_key_not_windows.result
@@ -11,6 +11,21 @@ CREATE TABLE `d255`.`_##################################################`
ERROR HY000: Long database name and identifier for object resulted in path length exceeding 512 characters. Path: './@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023/_@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023@0023
CREATE TABLE `d255`.`##################################################`
(a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB;
+#
+# MDEV-29258 Failing assertion for name length on RENAME TABLE
+#
+CREATE TABLE `d255`.`d245` (x INT) ENGINE=InnoDB;
+DROP TABLE `d255`.`d250`;
+RENAME TABLE `d250#`.`d245` TO `d250#`.`d250`;
+RENAME TABLE `d255`.`d250` TO a;
+DROP TABLE a,t;
+#
+# MDEV-29409 Buffer overflow in my_wc_mb_filename() on RENAME TABLE
+#
+CREATE TABLE `d255`.t(a INT PRIMARY KEY)ENGINE=InnoDB;
+CREATE TABLE `d255`.u(a INT PRIMARY KEY,
+CONSTRAINT `d320` FOREIGN KEY (a) REFERENCES `d255`.t (a)) ENGINE=InnoDB;
+RENAME TABLE `d255`.u TO u;
+DROP TABLE u;
DROP DATABASE `d255`;
-DROP TABLE t;
# End of 10.3 tests
diff --git a/mysql-test/suite/innodb/r/update-cascade.result b/mysql-test/suite/innodb/r/update-cascade.result
index a3c8fed931e..21d8f11e1cb 100644
--- a/mysql-test/suite/innodb/r/update-cascade.result
+++ b/mysql-test/suite/innodb/r/update-cascade.result
@@ -38,7 +38,7 @@ select f1, f2 from t2 for update;
f1 f2
1 2
connection default;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
+set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
update t1 set f1 = 10 where f1 = 2;
connection con1;
set debug_sync='now WAIT_FOR upd_waiting';
@@ -97,7 +97,7 @@ select f1, f2 from t2 for update;
f1 f2
1 91
connection default;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
+set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
update t1 set f2 = 28 where f2 = 91;
connection con1;
set debug_sync='now WAIT_FOR upd_waiting';
@@ -164,7 +164,7 @@ select f1 from t3 for update;
f1
2
connection default;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
+set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
update t1 set f1 = 10 where f1 = 2;
connection con1;
set debug_sync='now WAIT_FOR upd_waiting';
@@ -253,7 +253,7 @@ select f1 from t3 for update;
f1
2
connection default;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
+set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
update t1 set f2 = 28 where f2 = 91;
connection con1;
set debug_sync='now WAIT_FOR upd_waiting';
diff --git a/mysql-test/suite/innodb/t/cursor-restore-locking.test b/mysql-test/suite/innodb/t/cursor-restore-locking.test
index d032d8a8def..a398768fc66 100644
--- a/mysql-test/suite/innodb/t/cursor-restore-locking.test
+++ b/mysql-test/suite/innodb/t/cursor-restore-locking.test
@@ -16,19 +16,19 @@ SET DEBUG_SYNC = 'innodb_row_search_for_mysql_exit SIGNAL first_del_row_search_m
--connect(con_ins_1,localhost,root,,)
SET DEBUG_SYNC = 'now WAIT_FOR first_del_row_search_mvcc_finished';
# It's supposed the following INSERT will be suspended just after
-# lock_wait_suspend_thread_enter syncpoint, and will be awaken
+# lock_wait_start syncpoint, and will be awaken
# after the previous DELETE commits. ib_after_row_insert will be executed
# after the INSERT is woken up. The previous DELETE will wait for
# first_del_cont signal before commit, and this signal will be sent later.
# So it's safe to use two signals in a row here, it's guaranted the first
# signal will be received before the second signal is sent.
-SET DEBUG_SYNC = 'lock_wait_suspend_thread_enter SIGNAL first_ins_locked';
+SET DEBUG_SYNC = 'lock_wait_start SIGNAL first_ins_locked';
SET DEBUG_SYNC = 'ib_after_row_insert SIGNAL first_ins_row_inserted WAIT_FOR first_ins_cont';
--send INSERT INTO t VALUES(10, 20)
--connect(con_del_2,localhost,root,,)
SET DEBUG_SYNC = 'now WAIT_FOR first_ins_locked';
-SET DEBUG_SYNC = 'lock_wait_suspend_thread_enter SIGNAL second_del_locked';
+SET DEBUG_SYNC = 'lock_wait_start SIGNAL second_del_locked';
###############################################################################
# This DELETE is locked by the previous DELETE, after that DELETE is
# committed, it will still be locked by the next INSERT on delete-marked
diff --git a/mysql-test/suite/innodb/t/deadlock_victim_race.test b/mysql-test/suite/innodb/t/deadlock_victim_race.test
new file mode 100644
index 00000000000..3c9dd15fb4d
--- /dev/null
+++ b/mysql-test/suite/innodb/t/deadlock_victim_race.test
@@ -0,0 +1,102 @@
+--source include/have_innodb.inc
+--source include/have_debug_sync.inc
+--source include/count_sessions.inc
+
+CREATE TABLE t (a int PRIMARY KEY, b int) engine = InnoDB;
+CREATE TABLE t2 (a int PRIMARY KEY) engine = InnoDB;
+
+INSERT INTO t VALUES (10, 10), (20, 20), (30, 30);
+INSERT INTO t2 VALUES (10), (20), (30);
+
+BEGIN; # trx 1
+SELECT * FROM t WHERE a = 20 FOR UPDATE;
+# Locking order:
+# (10,10) (20,20) (30,30)
+# ^
+# trx 1
+
+--connect(con_2,localhost,root,,)
+# RC is neccessary to do semi-consistent read
+SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
+BEGIN; # trx 2
+# The first time it will be hit on trying to lock (20,20), the second hit
+# will be on (30,30).
+SET DEBUG_SYNC = 'lock_trx_handle_wait_enter SIGNAL upd_locked WAIT_FOR upd_cont EXECUTE 2';
+# We must not modify primary key fields to cause rr_sequential() read record
+# function choosing in mysql_update(), i.e. both query_plan.using_filesort and
+# query_plan.using_io_buffer must be false during init_read_record() call.
+--send UPDATE t SET b = 100
+
+--connect(con_3,localhost,root,,)
+BEGIN; # trx 3
+# The following update is necessary to increase the transaction weight, which is
+# calculated as the number of locks + the number of undo records during deadlock
+# report. Victim's transaction should have minimum weight. We need trx 2 to be
+# choosen as victim, that's why we need to increase the current transaction
+# weight.
+UPDATE t2 SET a = a + 100;
+SELECT * FROM t WHERE a = 30 FOR UPDATE;
+SET DEBUG_SYNC='now WAIT_FOR upd_locked';
+# Locking queue:
+# (10,10) (20,20) (30,30)
+# ^ ^ ^
+# trx 2 trx 1 trx 3
+# trx 2 (waiting for 1)
+
+SET DEBUG_SYNC = 'lock_wait_start SIGNAL sel_locked';
+--send SELECT * FROM t WHERE a = 20 FOR UPDATE
+--connection default
+SET DEBUG_SYNC='now WAIT_FOR sel_locked';
+# Locking queue:
+# (10,10) (20,20) (30,30)
+# ^ ^ ^
+# trx 2 trx 1 trx 3
+# trx 2 (waiting for 1)
+# trx 3 (waiting for 1)
+#
+# Note trx 1 must grant lock to trx2 before trx 2 checks the lock state in
+# lock_trx_handle_wait(), i.e. the function must return DB_SUCCESS, that's why
+# the following ROLLBACK must be executed before sending upd_cont signal.
+ROLLBACK;
+SET DEBUG_SYNC='now SIGNAL upd_cont';
+
+SET innodb_lock_wait_timeout=1;
+SET DEBUG_SYNC="now WAIT_FOR upd_locked";
+# Locking queue:
+# (10,10) (20,20) (30,30)
+# ^ ^ ^
+# trx 2 trx 2 trx 3
+# trx 3 (waiting for 2) trx 2 (waiting for 3)
+#
+# Deadlock happened after trx 1 granted lock to trx 2, and trx2 continued
+# sequential read (with rr_sequential() read record function), and requested
+# lock on (30,30). But the deadlock has not been determined yet.
+
+SET DEBUG_SYNC="lock_wait_end SIGNAL upd_cont";
+--error ER_LOCK_WAIT_TIMEOUT
+# The deadlock will be determined in lock_wait() after lock wait timeout
+# expired.
+SELECT * FROM t WHERE a = 10 FOR UPDATE;
+
+--connection con_3
+--reap
+
+--connection con_2
+# As lock_trx_handle_wait() wrongly returned DB_SUCCESS instead of
+# DB_DEADLOCK, row_search_mvcc() of trx 2 behaves so as if (30,30) was locked.
+# But the waiting(for trx 3) lock was cancelled by deadlock checker after
+# trx 2 was choosen as a victim (see lock_cancel_waiting_and_release() call
+# from Deadlock::report() for details). The try to update non-locked record
+# will cause assertion if the bug is not fixed.
+--error ER_LOCK_DEADLOCK
+--reap
+
+--disconnect con_3
+--disconnect con_2
+
+--connection default
+SET DEBUG_SYNC = 'RESET';
+DROP TABLE t;
+DROP TABLE t2;
+
+--source include/wait_until_count_sessions.inc
diff --git a/mysql-test/suite/innodb/t/foreign_key_not_windows.test b/mysql-test/suite/innodb/t/foreign_key_not_windows.test
index 7ad3723d5de..e5f42a0ddab 100644
--- a/mysql-test/suite/innodb/t/foreign_key_not_windows.test
+++ b/mysql-test/suite/innodb/t/foreign_key_not_windows.test
@@ -38,8 +38,40 @@ eval CREATE TABLE `$d255`.`_$d250`
--replace_result $d255 d255
eval CREATE TABLE `$d255`.`$d250`
(a INT PRIMARY KEY, FOREIGN KEY(a) REFERENCES test.t(a)) ENGINE=InnoDB;
+
+--echo #
+--echo # MDEV-29258 Failing assertion for name length on RENAME TABLE
+--echo #
+
+let $d245=-------------------------------------------------;
+--replace_result $d245 d245 $d255 d255
+eval CREATE TABLE `$d255`.`$d245` (x INT) ENGINE=InnoDB;
+--replace_result $d250 d250 $d255 d255
+eval DROP TABLE `$d255`.`$d250`;
+
+--replace_result $d245 d245 $d250 d250 d255 d255
+eval RENAME TABLE `$d255`.`$d245` TO `$d255`.`$d250`;
+--replace_result $d250 d250 $d255 d255
+eval RENAME TABLE `$d255`.`$d250` TO a;
+--replace_result $d255 d255
+DROP TABLE a,t;
+
+--echo #
+--echo # MDEV-29409 Buffer overflow in my_wc_mb_filename() on RENAME TABLE
+--echo #
+
+let $d225=#############################################;
+let $d320=################################################################;
+
+--replace_result $d255 d255
+eval CREATE TABLE `$d255`.t(a INT PRIMARY KEY)ENGINE=InnoDB;
+--replace_result $d255 d255 $d320 d320
+eval CREATE TABLE `$d255`.u(a INT PRIMARY KEY,
+CONSTRAINT `$d320` FOREIGN KEY (a) REFERENCES `$d255`.t (a)) ENGINE=InnoDB;
+--replace_result $d255 d255
+eval RENAME TABLE `$d255`.u TO u;
+DROP TABLE u;
--replace_result $d255 d255
eval DROP DATABASE `$d255`;
-DROP TABLE t;
--echo # End of 10.3 tests
diff --git a/mysql-test/suite/innodb/t/update-cascade.test b/mysql-test/suite/innodb/t/update-cascade.test
index de8294703b4..69e81ac4a2f 100644
--- a/mysql-test/suite/innodb/t/update-cascade.test
+++ b/mysql-test/suite/innodb/t/update-cascade.test
@@ -28,7 +28,7 @@ start transaction;
select f1, f2 from t2 for update;
connection default;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
+set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
send update t1 set f1 = 10 where f1 = 2;
connection con1;
@@ -72,7 +72,7 @@ start transaction;
select f1, f2 from t2 for update;
connection default;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
+set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
send update t1 set f2 = 28 where f2 = 91;
connection con1;
@@ -120,7 +120,7 @@ start transaction;
select f1 from t3 for update;
connection default;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
+set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
send update t1 set f1 = 10 where f1 = 2;
connection con1;
@@ -183,7 +183,7 @@ start transaction;
select f1 from t3 for update;
connection default;
-set debug_sync='lock_wait_suspend_thread_enter SIGNAL upd_waiting WAIT_FOR go_upd';
+set debug_sync='lock_wait_start SIGNAL upd_waiting WAIT_FOR go_upd';
send update t1 set f2 = 28 where f2 = 91;
connection con1;
diff --git a/mysql-test/suite/innodb_fts/r/innodb_fts_plugin.result b/mysql-test/suite/innodb_fts/r/innodb_fts_plugin.result
index 60be41d5839..b9b2350c00b 100644
--- a/mysql-test/suite/innodb_fts/r/innodb_fts_plugin.result
+++ b/mysql-test/suite/innodb_fts/r/innodb_fts_plugin.result
@@ -1,4 +1,5 @@
INSTALL PLUGIN simple_parser SONAME 'mypluglib';
+FLUSH TABLES;
# Test Part 1: Grammar Test
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
@@ -31,7 +32,7 @@ INSERT INTO articles (title, body) VALUES
('1001 MySQL Tricks','How to use full-text search engine'),
('Go MySQL Tricks','How to use full text search engine');
SELECT * FROM articles WHERE
-MATCH(title, body) AGAINST('mysql');
+MATCH(title, body) AGAINST('mysql') ORDER BY id;
id title body
1 MySQL Tutorial DBMS stands for MySQL DataBase ...
2 How To Use MySQL Well After you went through a ...
@@ -68,7 +69,7 @@ INSERT INTO articles (title, body) VALUES
('Go MySQL Tricks','How to use full text search engine');
ALTER TABLE articles ADD FULLTEXT INDEX (title, body) WITH PARSER simple_parser;
SELECT * FROM articles WHERE
-MATCH(title, body) AGAINST('mysql');
+MATCH(title, body) AGAINST('mysql') ORDER BY id;
id title body
1 MySQL Tutorial DBMS stands for MySQL DataBase ...
2 How To Use MySQL Well After you went through a ...
@@ -88,21 +89,23 @@ MATCH(title, body) AGAINST('full text');
id title body
5 Go MySQL Tricks How to use full text search engine
SELECT * FROM articles WHERE
-MATCH(title, body) AGAINST('full-text' WITH QUERY EXPANSION);
+MATCH(title, body) AGAINST('full-text' WITH QUERY EXPANSION)
+ORDER BY id;
id title body
-4 1001 MySQL Tricks How to use full-text search engine
-5 Go MySQL Tricks How to use full text search engine
-2 How To Use MySQL Well After you went through a ...
1 MySQL Tutorial DBMS stands for MySQL DataBase ...
+2 How To Use MySQL Well After you went through a ...
3 Optimizing MySQL In this tutorial we will show ...
+4 1001 MySQL Tricks How to use full-text search engine
+5 Go MySQL Tricks How to use full text search engine
SELECT * FROM articles WHERE
-MATCH(title, body) AGAINST('full text' WITH QUERY EXPANSION);
+MATCH(title, body) AGAINST('full text' WITH QUERY EXPANSION)
+ORDER BY id;
id title body
-5 Go MySQL Tricks How to use full text search engine
-4 1001 MySQL Tricks How to use full-text search engine
-2 How To Use MySQL Well After you went through a ...
1 MySQL Tutorial DBMS stands for MySQL DataBase ...
+2 How To Use MySQL Well After you went through a ...
3 Optimizing MySQL In this tutorial we will show ...
+4 1001 MySQL Tricks How to use full-text search engine
+5 Go MySQL Tricks How to use full text search engine
SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('"mysql database"' IN BOOLEAN MODE);
id title body
@@ -138,27 +141,27 @@ INSERT INTO articles (title, body) VALUES
('Go MariaDB Tricks','How to use full text search engine');
# restart
SELECT * FROM articles WHERE
-MATCH(title, body) AGAINST('MySQL');
+MATCH(title, body) AGAINST('MySQL') ORDER BY id;
id title body
1 MySQL Tutorial DBMS stands for MySQL DataBase ...
2 How To Use MySQL Well After you went through a ...
3 Optimizing MySQL In this tutorial we will show ...
4 1001 MySQL Tricks How to use full-text search engine
SELECT * FROM articles WHERE
-MATCH(title, body) AGAINST('tutorial');
+MATCH(title, body) AGAINST('tutorial') ORDER BY id;
id title body
1 MySQL Tutorial DBMS stands for MySQL DataBase ...
3 Optimizing MySQL In this tutorial we will show ...
SELECT * FROM articles WHERE
-MATCH(title, body) AGAINST('Tricks');
+MATCH(title, body) AGAINST('Tricks') ORDER BY id;
id title body
4 1001 MySQL Tricks How to use full-text search engine
5 Go MariaDB Tricks How to use full text search engine
SELECT * FROM articles WHERE
-MATCH(title, body) AGAINST('full text search');
+MATCH(title, body) AGAINST('full text search') ORDER BY id;
id title body
-5 Go MariaDB Tricks How to use full text search engine
4 1001 MySQL Tricks How to use full-text search engine
+5 Go MariaDB Tricks How to use full text search engine
SELECT COUNT(*) FROM articles;
COUNT(*)
5
@@ -186,7 +189,8 @@ UNINSTALL PLUGIN simple_parser;
Warnings:
Warning 1620 Plugin is busy and will be uninstalled on shutdown
SELECT * FROM articles WHERE
-MATCH(title, body) AGAINST('mysql');
+MATCH(title, body) AGAINST('mysql')
+ORDER BY id;
id title body
1 MySQL Tutorial DBMS stands for MySQL DataBase ...
2 How To Use MySQL Well After you went through a ...
diff --git a/mysql-test/suite/innodb_fts/t/innodb_fts_plugin.test b/mysql-test/suite/innodb_fts/t/innodb_fts_plugin.test
index aa52ef5051a..6a7ad8c7d82 100644
--- a/mysql-test/suite/innodb_fts/t/innodb_fts_plugin.test
+++ b/mysql-test/suite/innodb_fts/t/innodb_fts_plugin.test
@@ -6,6 +6,9 @@
# Install fts parser plugin
INSTALL PLUGIN simple_parser SONAME 'mypluglib';
+# Flush the table mysql.plugin in case the server shutdown would time out.
+FLUSH TABLES;
+
-- echo # Test Part 1: Grammar Test
# Create a myisam table and alter it to innodb table
CREATE TABLE articles (
@@ -52,7 +55,7 @@ INSERT INTO articles (title, body) VALUES
# Simple term search
SELECT * FROM articles WHERE
- MATCH(title, body) AGAINST('mysql');
+ MATCH(title, body) AGAINST('mysql') ORDER BY id;
# Test stopword and word len less than fts_min_token_size
SELECT * FROM articles WHERE
@@ -90,7 +93,7 @@ ALTER TABLE articles ADD FULLTEXT INDEX (title, body) WITH PARSER simple_parser;
# Simple term search
SELECT * FROM articles WHERE
- MATCH(title, body) AGAINST('mysql');
+ MATCH(title, body) AGAINST('mysql') ORDER BY id;
# Test stopword and word len less than fts_min_token_size
SELECT * FROM articles WHERE
@@ -105,10 +108,12 @@ SELECT * FROM articles WHERE
# Test query expansion
SELECT * FROM articles WHERE
- MATCH(title, body) AGAINST('full-text' WITH QUERY EXPANSION);
+ MATCH(title, body) AGAINST('full-text' WITH QUERY EXPANSION)
+ ORDER BY id;
SELECT * FROM articles WHERE
- MATCH(title, body) AGAINST('full text' WITH QUERY EXPANSION);
+ MATCH(title, body) AGAINST('full text' WITH QUERY EXPANSION)
+ ORDER BY id;
# No result here, we get '"mysql' 'database"' by simple parser
SELECT * FROM articles WHERE
@@ -152,13 +157,13 @@ INSERT INTO articles (title, body) VALUES
--source include/restart_mysqld.inc
SELECT * FROM articles WHERE
- MATCH(title, body) AGAINST('MySQL');
+ MATCH(title, body) AGAINST('MySQL') ORDER BY id;
SELECT * FROM articles WHERE
- MATCH(title, body) AGAINST('tutorial');
+ MATCH(title, body) AGAINST('tutorial') ORDER BY id;
SELECT * FROM articles WHERE
- MATCH(title, body) AGAINST('Tricks');
+ MATCH(title, body) AGAINST('Tricks') ORDER BY id;
SELECT * FROM articles WHERE
- MATCH(title, body) AGAINST('full text search');
+ MATCH(title, body) AGAINST('full text search') ORDER BY id;
SELECT COUNT(*) FROM articles;
INSERT INTO articles (title, body) VALUES ('111', '1234 1234 1234');
@@ -195,7 +200,8 @@ UNINSTALL PLUGIN simple_parser;
# Simple term search
SELECT * FROM articles WHERE
- MATCH(title, body) AGAINST('mysql');
+ MATCH(title, body) AGAINST('mysql')
+ ORDER BY id;
# Test stopword and word len less than fts_min_token_size
SELECT * FROM articles WHERE
diff --git a/mysql-test/suite/mariabackup/backup_grants.result b/mysql-test/suite/mariabackup/backup_grants.result
index ed793e7ff1a..56899f8d9c0 100644
--- a/mysql-test/suite/mariabackup/backup_grants.result
+++ b/mysql-test/suite/mariabackup/backup_grants.result
@@ -3,4 +3,12 @@ FOUND 1 /missing required privilege RELOAD/ in backup.log
FOUND 1 /missing required privilege PROCESS/ in backup.log
FOUND 1 /GRANT USAGE ON/ in backup.log
GRANT RELOAD, PROCESS on *.* to backup@localhost;
+NOT FOUND /missing required privilege REPLICA MONITOR/ in backup.log
+GRANT REPLICA MONITOR ON *.* TO backup@localhost;
+REVOKE REPLICA MONITOR ON *.* FROM backup@localhost;
+GRANT CONNECTION ADMIN ON *.* TO backup@localhost;
+FOUND 1 /missing required privilege REPLICATION SLAVE ADMIN/ in backup.log
+NOT FOUND /missing required privilege REPLICA MONITOR/ in backup.log
+GRANT REPLICATION SLAVE ADMIN ON *.* TO backup@localhost;
+GRANT REPLICA MONITOR ON *.* TO backup@localhost;
DROP USER backup@localhost;
diff --git a/mysql-test/suite/mariabackup/backup_grants.test b/mysql-test/suite/mariabackup/backup_grants.test
index eadeedd9b5f..894ae73aeb9 100644
--- a/mysql-test/suite/mariabackup/backup_grants.test
+++ b/mysql-test/suite/mariabackup/backup_grants.test
@@ -25,7 +25,62 @@ GRANT RELOAD, PROCESS on *.* to backup@localhost;
--disable_result_log
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --target-dir=$targetdir;
--enable_result_log
+rmdir $targetdir;
-DROP USER backup@localhost;
-# Cleanup
+# MDEV-23607 Warning: missing required privilege REPLICATION CLIENT
+# --slave-info and galera info require REPLICA MONITOR
+--disable_result_log
+error 1;
+exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --slave-info --target-dir=$targetdir > $MYSQLTEST_VARDIR/tmp/backup.log;
+--enable_result_log
+rmdir $targetdir;
+
+--let SEARCH_PATTERN= missing required privilege REPLICA MONITOR
+--source include/search_pattern_in_file.inc
+
+GRANT REPLICA MONITOR ON *.* TO backup@localhost;
+--disable_result_log
+exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --slave-info --target-dir=$targetdir;
+--enable_result_log
+rmdir $targetdir;
+REVOKE REPLICA MONITOR ON *.* FROM backup@localhost;
+
+# TODO need a query that would delay a BACKUP STAGE START/ BACKUP STAGE BLOCK_COMMIT longer than the kill-long-queries-timeout
+#--send SELECT SLEEP(9) kill_me
+## kill-long-query-type=(not empty) requires CONNECTION ADMIN
+#--disable_result_log
+#error 1;
+#--exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --kill-long-query-type=all --kill-long-queries-timeout=4 --target-dir=$targetdir > $MYSQLTEST_VARDIR/tmp/backup.log;
+#--reap
+#--enable_result_log
+#rmdir $targetdir;
+#
+#--let SEARCH_PATTERN= missing required privilege CONNECTION ADMIN
+#--source include/search_pattern_in_file.inc
+
+GRANT CONNECTION ADMIN ON *.* TO backup@localhost;
+--disable_result_log
+exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --kill-long-query-type=all --kill-long-queries-timeout=1 --target-dir=$targetdir;
+--enable_result_log
+rmdir $targetdir;
+
+# --safe-slave-backup requires REPLICATION SLAVE ADMIN, and REPLICA MONITOR
+--disable_result_log
+error 1;
+exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --safe-slave-backup --target-dir=$targetdir > $MYSQLTEST_VARDIR/tmp/backup.log;
+--enable_result_log
rmdir $targetdir;
+
+--let SEARCH_PATTERN= missing required privilege REPLICATION SLAVE ADMIN
+--source include/search_pattern_in_file.inc
+--let SEARCH_PATTERN= missing required privilege REPLICA MONITOR
+--source include/search_pattern_in_file.inc
+
+GRANT REPLICATION SLAVE ADMIN ON *.* TO backup@localhost;
+GRANT REPLICA MONITOR ON *.* TO backup@localhost;
+--disable_result_log
+exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup -ubackup --safe-slave-backup --target-dir=$targetdir;
+--enable_result_log
+rmdir $targetdir;
+
+DROP USER backup@localhost;
diff --git a/mysql-test/suite/period/r/alter.result b/mysql-test/suite/period/r/alter.result
index 7b9d1fea861..8aa94cee59e 100644
--- a/mysql-test/suite/period/r/alter.result
+++ b/mysql-test/suite/period/r/alter.result
@@ -191,6 +191,20 @@ ERROR 23000: Duplicate entry '1-2020-03-01-2020-03-02' for key 'PRIMARY'
alter table t1 add system versioning;
drop table t1;
#
+# MDEV-18873 Server crashes in Compare_identifiers::operator or in
+# my_strcasecmp_utf8 upon ADD PERIOD IF NOT EXISTS with empty name
+#
+alter table t add period if not exists for `` (s,e);
+ERROR 42000: Incorrect column name ''
+create table t(s DATE, e DATE);
+alter table t add period if not exists for `` (s,e);
+ERROR 42000: Incorrect column name ''
+alter table t add period if not exists for ` ` (s,e);
+ERROR 42000: Incorrect column name ' '
+create table t2 (period for `` (s,e)) select * from t;
+ERROR 42000: Incorrect column name ''
+drop table t;
+#
# MDEV-21941 RENAME doesn't work for system time or period fields
#
create or replace table t1 (
diff --git a/mysql-test/suite/period/t/alter.test b/mysql-test/suite/period/t/alter.test
index 68d9adf476a..fab933eca87 100644
--- a/mysql-test/suite/period/t/alter.test
+++ b/mysql-test/suite/period/t/alter.test
@@ -153,6 +153,29 @@ alter table t1 add system versioning;
drop table t1;
--echo #
+--echo # MDEV-18873 Server crashes in Compare_identifiers::operator or in
+--echo # my_strcasecmp_utf8 upon ADD PERIOD IF NOT EXISTS with empty name
+--echo #
+
+# When there is no table defined.
+--error ER_WRONG_COLUMN_NAME
+alter table t add period if not exists for `` (s,e);
+
+# When there is an actual table.
+create table t(s DATE, e DATE);
+--error ER_WRONG_COLUMN_NAME
+alter table t add period if not exists for `` (s,e);
+
+# When the last character is space
+--error ER_WRONG_COLUMN_NAME
+alter table t add period if not exists for ` ` (s,e);
+
+# Create table with an empty period name
+--error ER_WRONG_COLUMN_NAME
+create table t2 (period for `` (s,e)) select * from t;
+drop table t;
+
+--echo #
--echo # MDEV-21941 RENAME doesn't work for system time or period fields
--echo #
create or replace table t1 (
diff --git a/mysql-test/suite/rpl/r/rpl_filter_set_var_missing_data.result b/mysql-test/suite/rpl/r/rpl_filter_set_var_missing_data.result
new file mode 100644
index 00000000000..e232edae1ed
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_filter_set_var_missing_data.result
@@ -0,0 +1,55 @@
+include/master-slave.inc
+[connection master]
+#
+# Set replica to ignore system mysql tables
+connection slave;
+include/stop_slave.inc
+SET @@GLOBAL.replicate_wild_ignore_table="mysql.%";
+include/start_slave.inc
+#
+# Execute grant-based commands on primary which modify mysql system
+# tables
+connection master;
+CREATE ROLE journalist;
+CREATE USER testuser@localhost IDENTIFIED by '';
+GRANT journalist to testuser@localhost;
+#
+# Execute SET commands which use the previous user/role data
+SET DEFAULT ROLE journalist for testuser@localhost;
+SET PASSWORD for testuser@localhost= PASSWORD('123');
+include/save_master_gtid.inc
+#
+# Verify primary's grant tables have the correct user/role data
+select count(*)=1 from mysql.user where User='testuser';
+count(*)=1
+1
+select count(*)=1 from mysql.roles_mapping where User='testuser';
+count(*)=1
+1
+#
+# Ensure that the replica receives all of the primary's events without
+# error
+connection slave;
+include/sync_with_master_gtid.inc
+Last_SQL_Error =
+Last_SQL_Errno = 0
+#
+# Verify that the replica did not execute the master's commands
+select count(*)=0 from mysql.user where User='testuser';
+count(*)=0
+1
+select count(*)=0 from mysql.roles_mapping where User='testuser';
+count(*)=0
+1
+#
+# Clean up
+connection master;
+DROP ROLE journalist;
+DROP USER testuser@localhost;
+include/save_master_gtid.inc
+connection slave;
+include/sync_with_master_gtid.inc
+include/stop_slave.inc
+SET @@GLOBAL.replicate_wild_ignore_table="";
+include/start_slave.inc
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_filter_set_var_missing_data.test b/mysql-test/suite/rpl/t/rpl_filter_set_var_missing_data.test
new file mode 100644
index 00000000000..25efb6ed662
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_filter_set_var_missing_data.test
@@ -0,0 +1,83 @@
+#
+# Purpose:
+# This test ensures that the SET DEFAULT ROLE and SET PASSWORD commands can
+# be ignored by replica filter rules. MDEV-28294 exposed a bug in which
+# SET DEFAULT ROLE would check for the existence of the given roles/user even
+# when the targeted tables are ignored, resulting in errors if the targeted
+# data does not exist. More specifically, when previously issued
+# CREATE USER/ROLE commands are ignored by the replica because of the
+# replication filtering rules, SET DEFAULT ROLE would result in an error
+# because the targeted data does not exist.
+#
+# Methodology:
+# Using a replica configured with replicate_wild_ignore_table="mysql.%",
+# execute SET DEFAULT ROLE and SET PASSWORD on the primary and ensure that the
+# replica neither errors nor executes the commands which the primary sends.
+#
+# References:
+# MDEV-28294: set default role bypasses Replicate_Wild_Ignore_Table: mysql.%
+#
+
+source include/master-slave.inc;
+source include/have_binlog_format_mixed.inc;
+
+--echo #
+--echo # Set replica to ignore system mysql tables
+connection slave;
+let $old_filter= query_get_value(SHOW SLAVE STATUS, Replicate_Wild_Ignore_Table, 1);
+source include/stop_slave.inc;
+SET @@GLOBAL.replicate_wild_ignore_table="mysql.%";
+source include/start_slave.inc;
+
+--echo #
+--echo # Execute grant-based commands on primary which modify mysql system
+--echo # tables
+connection master;
+CREATE ROLE journalist;
+CREATE USER testuser@localhost IDENTIFIED by '';
+GRANT journalist to testuser@localhost;
+
+--echo #
+--echo # Execute SET commands which use the previous user/role data
+SET DEFAULT ROLE journalist for testuser@localhost;
+SET PASSWORD for testuser@localhost= PASSWORD('123');
+--source include/save_master_gtid.inc
+
+--echo #
+--echo # Verify primary's grant tables have the correct user/role data
+select count(*)=1 from mysql.user where User='testuser';
+select count(*)=1 from mysql.roles_mapping where User='testuser';
+
+--echo #
+--echo # Ensure that the replica receives all of the primary's events without
+--echo # error
+connection slave;
+--source include/sync_with_master_gtid.inc
+let $error= query_get_value(SHOW SLAVE STATUS, Last_SQL_Error, 1);
+--echo Last_SQL_Error = $error
+let $errno= query_get_value(SHOW SLAVE STATUS, Last_SQL_Errno, 1);
+--echo Last_SQL_Errno = $errno
+
+--echo #
+--echo # Verify that the replica did not execute the master's commands
+select count(*)=0 from mysql.user where User='testuser';
+select count(*)=0 from mysql.roles_mapping where User='testuser';
+
+--echo #
+--echo # Clean up
+
+# The master has to drop the role/user combination while the slave still has
+# its filters active; otherwise, the slave would try to drop users/roles that
+# were never replicated.
+--connection master
+DROP ROLE journalist;
+DROP USER testuser@localhost;
+--source include/save_master_gtid.inc
+
+--connection slave
+--source include/sync_with_master_gtid.inc
+source include/stop_slave.inc;
+--eval SET @@GLOBAL.replicate_wild_ignore_table="$old_filter"
+source include/start_slave.inc;
+
+--source include/rpl_end.inc
diff --git a/mysys/my_win_popen.cc b/mysys/my_win_popen.cc
index f41f54100f1..cceb77e9019 100644
--- a/mysys/my_win_popen.cc
+++ b/mysys/my_win_popen.cc
@@ -92,7 +92,7 @@ extern "C" FILE *my_win_popen(const char *cmd, const char *mode)
goto error;
break;
default:
- /* Unknown mode, éxpected "r", "rt", "w", "wt" */
+ /* Unknown mode, expected "r", "rt", "w", "wt" */
abort();
}
if (!SetHandleInformation(parent_pipe_end, HANDLE_FLAG_INHERIT, 0))
diff --git a/plugin/type_uuid/mysql-test/type_uuid/type_uuid.result b/plugin/type_uuid/mysql-test/type_uuid/type_uuid.result
index 2a96b2df592..d388cab45d7 100644
--- a/plugin/type_uuid/mysql-test/type_uuid/type_uuid.result
+++ b/plugin/type_uuid/mysql-test/type_uuid/type_uuid.result
@@ -3171,3 +3171,19 @@ SELECT * FROM companies;
id name
DROP TABLE divisions;
DROP TABLE companies;
+#
+# MDEV-27100 Subquery using the ALL keyword on UUID columns produces a wrong result
+#
+CREATE TABLE t1 (d UUID);
+INSERT INTO t1 VALUES ('00000000-0000-0000-0000-111111111111'), ('11111111-0000-0000-0000-000000000000');
+SELECT * FROM t1 ORDER BY d;
+d
+11111111-0000-0000-0000-000000000000
+00000000-0000-0000-0000-111111111111
+SELECT * FROM t1 WHERE d <= ALL (SELECT * FROM t1);
+d
+11111111-0000-0000-0000-000000000000
+SELECT * FROM t1 WHERE d >= ALL (SELECT * FROM t1);
+d
+00000000-0000-0000-0000-111111111111
+DROP TABLE t1;
diff --git a/plugin/type_uuid/mysql-test/type_uuid/type_uuid.test b/plugin/type_uuid/mysql-test/type_uuid/type_uuid.test
index 91f8f82125b..d4e34c5a74b 100644
--- a/plugin/type_uuid/mysql-test/type_uuid/type_uuid.test
+++ b/plugin/type_uuid/mysql-test/type_uuid/type_uuid.test
@@ -1670,3 +1670,15 @@ DELETE FROM companies WHERE id IN (SELECT company_id FROM divisions);
SELECT * FROM companies;
DROP TABLE divisions;
DROP TABLE companies;
+
+
+--echo #
+--echo # MDEV-27100 Subquery using the ALL keyword on UUID columns produces a wrong result
+--echo #
+
+CREATE TABLE t1 (d UUID);
+INSERT INTO t1 VALUES ('00000000-0000-0000-0000-111111111111'), ('11111111-0000-0000-0000-000000000000');
+SELECT * FROM t1 ORDER BY d;
+SELECT * FROM t1 WHERE d <= ALL (SELECT * FROM t1);
+SELECT * FROM t1 WHERE d >= ALL (SELECT * FROM t1);
+DROP TABLE t1;
diff --git a/sql/field.cc b/sql/field.cc
index 98aeb6d892c..028a1961943 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -983,7 +983,8 @@ bool Field::check_assignability_from(const Type_handler *from,
type_handler_for_item_field());
if (th.aggregate_for_result(from->type_handler_for_item_field()))
{
- bool error= !ignore && get_thd()->is_strict_mode();
+ bool error= (!ignore && get_thd()->is_strict_mode()) ||
+ (type_handler()->is_scalar_type() != from->is_scalar_type());
/*
Display fully qualified column name for table columns.
Display non-qualified names for other things,
@@ -1492,15 +1493,6 @@ bool Field::sp_prepare_and_store_item(THD *thd, Item **value)
if (!(expr_item= thd->sp_fix_func_item_for_assignment(this, value)))
goto error;
- /*
- expr_item is now fixed, it's safe to call cmp_type()
- */
- if (expr_item->cmp_type() == ROW_RESULT)
- {
- my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
- goto error;
- }
-
/* Save the value in the field. Convert the value if needed. */
expr_item->save_in_field(this, 0);
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 2169ac874e5..24c4cc505eb 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -392,7 +392,8 @@ bool Item_subselect::mark_as_eliminated_processor(void *arg)
bool Item_subselect::eliminate_subselect_processor(void *arg)
{
unit->item= NULL;
- unit->exclude();
+ if (!unit->is_excluded())
+ unit->exclude();
eliminated= TRUE;
return FALSE;
}
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 601cae2e945..f75905f07e1 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2018, Oracle and/or its affiliates.
- Copyright (c) 2009, 2021, MariaDB
+ Copyright (c) 2009, 2022, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -1937,10 +1937,17 @@ class Grant_tables
public:
Grant_tables() : p_user_table(&m_user_table_json) { }
- int open_and_lock(THD *thd, int which_tables, enum thr_lock_type lock_type)
+ /**
+ An auxiliary to build a list of involved tables.
+
+ @retval 0 Success
+ @retval -1 A my_error reported error
+ */
+ int build_table_list(THD *thd, TABLE_LIST** ptr_first,
+ int which_tables, enum thr_lock_type lock_type,
+ TABLE_LIST *tables)
{
- DBUG_ENTER("Grant_tables::open_and_lock");
- TABLE_LIST tables[USER_TABLE+1], *first= NULL;
+ DBUG_ENTER("Grant_tables::build_table_list");
DBUG_ASSERT(which_tables); /* At least one table must be opened. */
/*
@@ -1965,12 +1972,23 @@ class Grant_tables
tl->updating= lock_type >= TL_FIRST_WRITE;
if (i >= FIRST_OPTIONAL_TABLE)
tl->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
- tl->next_global= tl->next_local= first;
- first= tl;
+ tl->next_global= tl->next_local= *ptr_first;
+ *ptr_first= tl;
}
else
tl->table= NULL;
}
+ DBUG_RETURN(0);
+ }
+
+ int open_and_lock(THD *thd, int which_tables, enum thr_lock_type lock_type)
+ {
+ DBUG_ENTER("Grant_tables::open_and_lock");
+
+ TABLE_LIST tables[USER_TABLE+1], *first= NULL;
+
+ if (build_table_list(thd, &first, which_tables, lock_type, tables))
+ DBUG_RETURN(-1);
uint counter;
int res= really_open(thd, first, &counter);
@@ -2041,6 +2059,48 @@ class Grant_tables
inline const Roles_mapping_table& roles_mapping_table() const
{ return m_roles_mapping_table; }
+#ifdef HAVE_REPLICATION
+ /**
+ Checks if the tables targeted by a grant command should be ignored because
+ of the configured replication filters
+
+ @retval 1 Tables are excluded for replication
+ @retval 0 tables are included for replication
+ */
+ int rpl_ignore_tables(THD *thd, TABLE_LIST* tables, int which_tables= 0,
+ enum thr_lock_type lock_type= TL_IGNORE)
+ {
+ DBUG_ENTER("Grant_tables::rpl_ignore_tables");
+
+ if (!(thd->slave_thread && !thd->spcont))
+ DBUG_RETURN(0);
+
+ TABLE_LIST all_tables[USER_TABLE+1];
+
+ if (!tables)
+ {
+ int rc __attribute__((unused))=
+ build_table_list(thd, &tables, which_tables, lock_type, all_tables);
+
+ DBUG_ASSERT(!rc); // Grant_tables must be already initialized
+ DBUG_ASSERT(tables);
+ }
+
+ if (tables->lock_type >= TL_FIRST_WRITE)
+ {
+ /*
+ GRANT and REVOKE are applied the slave in/exclusion rules as they are
+ some kind of updates to the mysql.% tables.
+ */
+ Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
+
+ if (rpl_filter->is_on() && !rpl_filter->tables_ok(0, tables))
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+ }
+#endif
+
private:
/* Before any operation is possible on grant tables, they must be opened.
@@ -2054,16 +2114,9 @@ class Grant_tables
{
DBUG_ENTER("Grant_tables::really_open:");
#ifdef HAVE_REPLICATION
- if (tables->lock_type >= TL_FIRST_WRITE &&
- thd->slave_thread && !thd->spcont)
+ if (rpl_ignore_tables(thd, tables))
{
- /*
- GRANT and REVOKE are applied the slave in/exclusion rules as they are
- some kind of updates to the mysql.% tables.
- */
- Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
- if (rpl_filter->is_on() && !rpl_filter->tables_ok(0, tables))
- DBUG_RETURN(1);
+ DBUG_RETURN(1);
}
#endif
if (open_tables(thd, &tables, counter, MYSQL_LOCK_IGNORE_TIMEOUT))
@@ -4080,6 +4133,17 @@ int acl_check_set_default_role(THD *thd, const char *host, const char *user,
const char *role)
{
DBUG_ENTER("acl_check_set_default_role");
+#ifdef HAVE_REPLICATION
+ /*
+ If the roles_mapping table is excluded by the replication filter, we return
+ successful without validating the user/role data because the command will
+ be ignored in a later call to `acl_set_default_role()` for a graceful exit.
+ */
+ Grant_tables tables;
+ TABLE_LIST* first= NULL;
+ if (tables.rpl_ignore_tables(thd, first, Table_roles_mapping, TL_WRITE))
+ DBUG_RETURN(0);
+#endif
DBUG_RETURN(check_alter_user(thd, host, user) ||
check_user_can_set_role(thd, user, host, NULL, role, NULL));
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index e58db8fa502..cbe76849ea7 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -36,6 +36,7 @@
#include "sql_limit.h" // Select_limit_counters
#include "json_table.h" // Json_table_column
#include "sql_schema.h"
+#include "table.h"
/* Used for flags of nesting constructs */
#define SELECT_NESTING_MAP_SIZE 64
@@ -4584,6 +4585,11 @@ public:
int add_period(Lex_ident name, Lex_ident_sys_st start, Lex_ident_sys_st end)
{
+ if (check_period_name(name.str)) {
+ my_error(ER_WRONG_COLUMN_NAME, MYF(0), name.str);
+ return 1;
+ }
+
if (lex_string_cmp(system_charset_info, &start, &end) == 0)
{
my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), start.str);
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 23f647b28df..c934d58087a 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -5096,7 +5096,7 @@ mysql_rename_table(handlerton *base, const LEX_CSTRING *old_db,
length= build_table_filename(to, sizeof(to) - 1, new_db->str,
new_name->str, "", flags & FN_TO_IS_TMP);
// Check if we hit FN_REFLEN bytes along with file extension.
- if (length+reg_ext_length >= FN_REFLEN)
+ if (length+reg_ext_length > FN_REFLEN)
{
my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), (int) sizeof(to)-1, to);
DBUG_RETURN(TRUE);
diff --git a/sql/table.cc b/sql/table.cc
index d5a541b5cc7..5e17eb60b30 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -4945,7 +4945,8 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table)
int
rename_file_ext(const char * from,const char * to,const char * ext)
{
- char from_b[FN_REFLEN],to_b[FN_REFLEN];
+ /* Reserve space for ./databasename/tablename.frm + NUL byte */
+ char from_b[2 + FN_REFLEN + 4 + 1], to_b[2 + FN_REFLEN + 4 + 1];
(void) strxmov(from_b,from,ext,NullS);
(void) strxmov(to_b,to,ext,NullS);
return mysql_file_rename(key_file_frm, from_b, to_b, MYF(0));
@@ -5200,6 +5201,12 @@ bool check_column_name(const char *name)
}
+bool check_period_name(const char *name)
+{
+ return check_column_name(name);
+}
+
+
/**
Checks whether a table is intact. Should be done *just* after the table has
been opened.
diff --git a/sql/table.h b/sql/table.h
index 0622abe9abf..cb917594131 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -3248,6 +3248,7 @@ void open_table_error(TABLE_SHARE *share, enum open_frm_error error,
void update_create_info_from_table(HA_CREATE_INFO *info, TABLE *form);
bool check_db_name(LEX_STRING *db);
bool check_column_name(const char *name);
+bool check_period_name(const char *name);
bool check_table_name(const char *name, size_t length, bool check_for_path_chars);
int rename_file_ext(const char * from,const char * to,const char * ext);
char *get_field(MEM_ROOT *mem, Field *field);
diff --git a/sql/vers_string.h b/sql/vers_string.h
index 4e173f86e6e..67fd421500e 100644
--- a/sql/vers_string.h
+++ b/sql/vers_string.h
@@ -40,6 +40,8 @@ struct Compare_identifiers
{
int operator()(const LEX_CSTRING& a, const LEX_CSTRING& b) const
{
+ DBUG_ASSERT(a.str != NULL);
+ DBUG_ASSERT(b.str != NULL);
DBUG_ASSERT(a.str[a.length] == 0);
DBUG_ASSERT(b.str[b.length] == 0);
return my_strcasecmp(system_charset_info, a.str, b.str);
diff --git a/storage/connect/domdoc.cpp b/storage/connect/domdoc.cpp
index 7d5b87a2640..268ad771ef9 100644
--- a/storage/connect/domdoc.cpp
+++ b/storage/connect/domdoc.cpp
@@ -642,7 +642,6 @@ bool DOMNODELIST::DropItem(PGLOBAL g, int n)
if (Listp == NULL || Listp->length < n)
return true;
-//Listp->item[n] = NULL; La propriété n'a pas de méthode 'set'
return false;
} // end of DeleteItem
diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc
index d337d99d3b5..3b48c0827e7 100644
--- a/storage/innobase/btr/btr0btr.cc
+++ b/storage/innobase/btr/btr0btr.cc
@@ -530,8 +530,7 @@ btr_page_alloc_low(
mtr->u_lock_register(savepoint);
root->page.lock.u_lock();
#ifdef BTR_CUR_HASH_ADAPT
- if (root->index)
- mtr_t::defer_drop_ahi(root, MTR_MEMO_PAGE_SX_FIX);
+ btr_search_drop_page_hash_index(root, true);
#endif
}
@@ -645,8 +644,7 @@ dberr_t btr_page_free(dict_index_t* index, buf_block_t* block, mtr_t* mtr,
mtr->u_lock_register(savepoint);
root->page.lock.u_lock();
#ifdef BTR_CUR_HASH_ADAPT
- if (root->index)
- mtr_t::defer_drop_ahi(root, MTR_MEMO_PAGE_SX_FIX);
+ btr_search_drop_page_hash_index(root, true);
#endif
}
err= fseg_free_page(&root->page.frame[blob ||
diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc
index b038c895721..896db0dde3e 100644
--- a/storage/innobase/btr/btr0cur.cc
+++ b/storage/innobase/btr/btr0cur.cc
@@ -278,10 +278,10 @@ latch_block:
latch_leaves->blocks[1] = block;
}
- mtr->memo_push(block, MTR_MEMO_PAGE_X_FIX);
-
block->page.fix();
block->page.lock.x_lock();
+
+ mtr->memo_push(block, MTR_MEMO_PAGE_X_FIX);
#ifdef BTR_CUR_HASH_ADAPT
ut_ad(!btr_search_check_marked_free_index(block));
#endif
@@ -7015,10 +7015,11 @@ btr_store_big_rec_extern_fields(
mtr.start();
index->set_modified(mtr);
mtr.set_log_mode_sub(*btr_mtr);
- mtr.memo_push(rec_block, MTR_MEMO_PAGE_X_FIX);
rec_block->page.fix();
rec_block->page.lock.x_lock();
+
+ mtr.memo_push(rec_block, MTR_MEMO_PAGE_X_FIX);
#ifdef BTR_CUR_HASH_ADAPT
ut_ad(!btr_search_check_marked_free_index(rec_block));
#endif
@@ -7394,9 +7395,10 @@ skip_free:
/* The buffer pool block containing the BLOB pointer is
exclusively latched by local_mtr. To satisfy some design
constraints, we must recursively latch it in mtr as well. */
- mtr.memo_push(block, MTR_MEMO_PAGE_X_FIX);
block->fix();
block->page.lock.x_lock();
+
+ mtr.memo_push(block, MTR_MEMO_PAGE_X_FIX);
#ifdef BTR_CUR_HASH_ADAPT
ut_ad(!btr_search_check_marked_free_index(block));
#endif
diff --git a/storage/innobase/btr/btr0sea.cc b/storage/innobase/btr/btr0sea.cc
index 04bca37f956..6c6a25f2f7f 100644
--- a/storage/innobase/btr/btr0sea.cc
+++ b/storage/innobase/btr/btr0sea.cc
@@ -1293,10 +1293,6 @@ void btr_search_drop_page_hash_index(buf_block_t* block,
rec_offs* offsets;
retry:
- /* This debug check uses a dirty read that could theoretically cause
- false positives while buf_pool.clear_hash_index() is executing. */
- assert_block_ahi_valid(block);
-
if (!block->index) {
return;
}
diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc
index 3176ac85ea3..98b95239a68 100644
--- a/storage/innobase/buf/buf0buf.cc
+++ b/storage/innobase/buf/buf0buf.cc
@@ -2807,12 +2807,10 @@ get_latch:
goto page_id_mismatch;
}
get_latch_valid:
+ mtr->memo_push(block, fix_type);
#ifdef BTR_CUR_HASH_ADAPT
- if (block->index) {
- mtr_t::defer_drop_ahi(block, fix_type);
- }
+ btr_search_drop_page_hash_index(block, true);
#endif /* BTR_CUR_HASH_ADAPT */
- mtr->memo_push(block, fix_type);
break;
case RW_SX_LATCH:
fix_type = MTR_MEMO_PAGE_SX_FIX;
diff --git a/storage/innobase/dict/dict0crea.cc b/storage/innobase/dict/dict0crea.cc
index f20e5e60154..fab6ff4db8e 100644
--- a/storage/innobase/dict/dict0crea.cc
+++ b/storage/innobase/dict/dict0crea.cc
@@ -1101,7 +1101,7 @@ dict_create_table_step(
}
if (node->state == TABLE_ADD_TO_CACHE) {
- node->table->can_be_evicted = true;
+ node->table->can_be_evicted = !node->table->fts;
node->table->add_to_cache();
err = DB_SUCCESS;
diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc
index 1cfc36eabcf..8c2d720f62e 100644
--- a/storage/innobase/dict/dict0dict.cc
+++ b/storage/innobase/dict/dict0dict.cc
@@ -1665,7 +1665,7 @@ dict_table_rename_in_cache(
in UTF-8 charset. The variable fkid here is used
to store foreign key constraint name in charset
my_charset_filename for comparison further below. */
- char fkid[MAX_TABLE_NAME_LEN+20];
+ char fkid[MAX_TABLE_NAME_LEN * 2 + 20];
/* The old table name in my_charset_filename is stored
in old_name_cs_filename */
@@ -1697,7 +1697,8 @@ dict_table_rename_in_cache(
}
}
- strncpy(fkid, foreign->id, MAX_TABLE_NAME_LEN);
+ strncpy(fkid, foreign->id, (sizeof fkid) - 1);
+ fkid[(sizeof fkid) - 1] = '\0';
const bool on_tmp = dict_table_t::is_temporary_name(
fkid);
@@ -3518,10 +3519,11 @@ dict_table_get_highest_foreign_id(
for (dict_foreign_set::iterator it = table->foreign_set.begin();
it != table->foreign_set.end();
++it) {
- char fkid[MAX_TABLE_NAME_LEN+20];
+ char fkid[MAX_TABLE_NAME_LEN * 2 + 20];
foreign = *it;
- strcpy(fkid, foreign->id);
+ strncpy(fkid, foreign->id, (sizeof fkid) - 1);
+ fkid[(sizeof fkid) - 1] = '\0';
/* Convert foreign key identifier on dictionary memory
cache to filename charset. */
innobase_convert_to_filename_charset(
diff --git a/storage/innobase/dict/drop.cc b/storage/innobase/dict/drop.cc
index 4a4e10b45a8..edb6add0787 100644
--- a/storage/innobase/dict/drop.cc
+++ b/storage/innobase/dict/drop.cc
@@ -245,7 +245,8 @@ void trx_t::commit(std::vector<pfs_os_file_t> &deleted)
ut_ad(ib_vector_is_empty(autoinc_locks));
mem_heap_empty(lock.lock_heap);
lock.table_locks.clear();
- lock.was_chosen_as_deadlock_victim= false;
+ /* commit_persist() already reset this. */
+ ut_ad(!lock.was_chosen_as_deadlock_victim);
lock.n_rec_locks= 0;
while (dict_table_t *table= UT_LIST_GET_FIRST(lock.evicted_tables))
{
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 499dfb55161..50c7c85d5f0 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -14754,9 +14754,11 @@ ha_innobase::info_low(
stats.index_file_length
= ulonglong(stat_sum_of_other_index_sizes)
* size;
+ space->s_lock();
stats.delete_length = 1024
* fsp_get_available_space_in_free_extents(
*space);
+ space->s_unlock();
}
stats.check_time = 0;
stats.mrr_length_per_rec= (uint)ref_length + 8; // 8 = max(sizeof(void *));
diff --git a/storage/innobase/include/mtr0mtr.h b/storage/innobase/include/mtr0mtr.h
index 81ab902f905..21d0cfbb5a1 100644
--- a/storage/innobase/include/mtr0mtr.h
+++ b/storage/innobase/include/mtr0mtr.h
@@ -623,12 +623,6 @@ public:
PAGE_FLUSH_SYNC
};
-#ifdef BTR_CUR_HASH_ADAPT
- /** If a stale adaptive hash index exists on the block, drop it. */
- ATTRIBUTE_COLD
- static void defer_drop_ahi(buf_block_t *block, mtr_memo_type_t fix_type);
-#endif
-
private:
/** Handle any pages that were freed during the mini-transaction. */
void process_freed_pages();
diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h
index bf9a2acd622..505ef96e01d 100644
--- a/storage/innobase/include/trx0trx.h
+++ b/storage/innobase/include/trx0trx.h
@@ -339,27 +339,11 @@ struct trx_lock_t
/** lock wait start time */
Atomic_relaxed<my_hrtime_t> suspend_time;
+#if defined(UNIV_DEBUG) || !defined(DBUG_OFF)
/** 2=high priority WSREP thread has marked this trx to abort;
1=another transaction chose this as a victim in deadlock resolution. */
Atomic_relaxed<byte> was_chosen_as_deadlock_victim;
- /** Clear the deadlock victim status. */
- void clear_deadlock_victim()
- {
-#ifndef WITH_WSREP
- was_chosen_as_deadlock_victim= false;
-#elif defined __GNUC__ && (defined __i386__ || defined __x86_64__)
- /* There is no 8-bit version of the 80386 BTR instruction.
- Technically, this is the wrong addressing mode (16-bit), but
- there are other data members stored after the byte. */
- __asm__ __volatile__("lock btrw $0, %0"
- : "+m" (was_chosen_as_deadlock_victim));
-#else
- was_chosen_as_deadlock_victim.fetch_and(byte(~1));
-#endif
- }
-
-#ifdef WITH_WSREP
/** Flag the lock owner as a victim in Galera conflict resolution. */
void set_wsrep_victim()
{
@@ -373,7 +357,17 @@ struct trx_lock_t
was_chosen_as_deadlock_victim.fetch_or(2);
# endif
}
-#endif
+#else /* defined(UNIV_DEBUG) || !defined(DBUG_OFF) */
+
+ /** High priority WSREP thread has marked this trx to abort or
+ another transaction chose this as a victim in deadlock resolution. */
+ Atomic_relaxed<bool> was_chosen_as_deadlock_victim;
+
+ /** Flag the lock owner as a victim in Galera conflict resolution. */
+ void set_wsrep_victim() {
+ was_chosen_as_deadlock_victim= true;
+ }
+#endif /* defined(UNIV_DEBUG) || !defined(DBUG_OFF) */
/** Next available rec_pool[] entry */
byte rec_cached;
diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc
index 76ad538ea0e..1c11efafc7a 100644
--- a/storage/innobase/lock/lock0lock.cc
+++ b/storage/innobase/lock/lock0lock.cc
@@ -44,6 +44,7 @@ Created 5/7/1996 Heikki Tuuri
#include "row0vers.h"
#include "pars0pars.h"
#include "srv0mon.h"
+#include "scope.h"
#include <set>
@@ -1275,6 +1276,14 @@ lock_rec_enqueue_waiting(
trx_t* trx = thr_get_trx(thr);
ut_ad(xtest() || trx->mutex_is_owner());
ut_ad(!trx->dict_operation_lock_mode);
+ /* Apart from Galera, only transactions that have waiting lock can be
+ chosen as deadlock victim. Only one lock can be waited for at a time,
+ and a transaction is associated with a single thread. That is why there
+ must not be waiting lock requests if the transaction is deadlock victim
+ and it is not WSREP. Galera transaction abort can be invoked from MDL
+ acquisition code when the transaction does not have waiting record
+ lock, that's why we check only deadlock victim bit here. */
+ ut_ad(!(trx->lock.was_chosen_as_deadlock_victim & 1));
if (trx->mysql_thd && thd_lock_wait_timeout(trx->mysql_thd) == 0) {
trx->error_state = DB_LOCK_WAIT_TIMEOUT;
@@ -1292,7 +1301,6 @@ lock_rec_enqueue_waiting(
}
trx->lock.wait_thr = thr;
- trx->lock.clear_deadlock_victim();
DBUG_LOG("ib_lock", "trx " << ib::hex(trx->id)
<< " waits for lock in index " << index->name
@@ -1475,7 +1483,14 @@ lock_rec_lock(
que_thr_t* thr) /*!< in: query thread */
{
trx_t *trx= thr_get_trx(thr);
-
+ /* There must not be lock requests for reads or updates if transaction was
+ chosen as deadlock victim. Apart from Galera, only transactions that have
+ waiting lock may be chosen as deadlock victims. Only one lock can be waited
+ for at a time, and a transaction is associated with a single thread. Galera
+ transaction abort can be invoked from MDL acquisition code when the
+ transaction does not have waiting lock, that's why we check only deadlock
+ victim bit here. */
+ ut_ad(!(trx->lock.was_chosen_as_deadlock_victim & 1));
ut_ad(!srv_read_only_mode);
ut_ad(((LOCK_MODE_MASK | LOCK_TABLE) & mode) == LOCK_S ||
((LOCK_MODE_MASK | LOCK_TABLE) & mode) == LOCK_X);
@@ -1627,7 +1642,9 @@ void lock_sys_t::wait_resume(THD *thd, my_hrtime_t start, my_hrtime_t now)
#ifdef HAVE_REPLICATION
ATTRIBUTE_NOINLINE MY_ATTRIBUTE((nonnull))
-/** Report lock waits to parallel replication.
+/** Report lock waits to parallel replication. Sets
+trx->error_state= DB_DEADLOCK if trx->lock.was_chosen_as_deadlock_victim was
+set when lock_sys.wait_mutex was unlocked.
@param trx transaction that may be waiting for a lock
@param wait_lock lock that is being waited for */
static void lock_wait_rpl_report(trx_t *trx)
@@ -1642,7 +1659,8 @@ static void lock_wait_rpl_report(trx_t *trx)
ut_ad(!(wait_lock->type_mode & LOCK_AUTO_INC));
/* This would likely be too large to attempt to use a memory transaction,
even for wait_lock->is_table(). */
- if (!lock_sys.wr_lock_try())
+ const bool nowait= lock_sys.wr_lock_try();
+ if (!nowait)
{
mysql_mutex_unlock(&lock_sys.wait_mutex);
lock_sys.wr_lock(SRW_LOCK_CALL);
@@ -1652,6 +1670,10 @@ static void lock_wait_rpl_report(trx_t *trx)
{
func_exit:
lock_sys.wr_unlock();
+ /* trx->lock.was_chosen_as_deadlock_victim can be set when
+ lock_sys.wait_mutex was unlocked, let's check it. */
+ if (!nowait && trx->lock.was_chosen_as_deadlock_victim)
+ trx->error_state= DB_DEADLOCK;
return;
}
ut_ad(wait_lock->is_waiting());
@@ -1700,7 +1722,13 @@ dberr_t lock_wait(que_thr_t *thr)
trx_t *trx= thr_get_trx(thr);
if (trx->mysql_thd)
- DEBUG_SYNC_C("lock_wait_suspend_thread_enter");
+ DEBUG_SYNC_C("lock_wait_start");
+
+ /* Create the sync point for any quit from the function. */
+ ut_d(SCOPE_EXIT([trx]() {
+ if (trx->mysql_thd)
+ DEBUG_SYNC_C("lock_wait_end");
+ }));
/* InnoDB system transactions may use the global value of
innodb_lock_wait_timeout, because trx->mysql_thd == NULL. */
@@ -1731,11 +1759,8 @@ dberr_t lock_wait(que_thr_t *thr)
{
/* The lock has already been released or this transaction
was chosen as a deadlock victim: no need to wait */
- if (trx->lock.was_chosen_as_deadlock_victim.fetch_and(byte(~1)))
- trx->error_state= DB_DEADLOCK;
- else
- trx->error_state= DB_SUCCESS;
-
+ trx->error_state=
+ trx->lock.was_chosen_as_deadlock_victim ? DB_DEADLOCK : DB_SUCCESS;
return trx->error_state;
}
@@ -1770,7 +1795,7 @@ dberr_t lock_wait(que_thr_t *thr)
wait_lock->un_member.tab_lock.table->id <= DICT_FIELDS_ID);
thd_wait_begin(trx->mysql_thd, (type_mode & LOCK_TABLE)
? THD_WAIT_TABLE_LOCK : THD_WAIT_ROW_LOCK);
- dberr_t error_state= DB_SUCCESS;
+ trx->error_state= DB_SUCCESS;
mysql_mutex_lock(&lock_sys.wait_mutex);
if (trx->lock.wait_lock)
@@ -1778,23 +1803,28 @@ dberr_t lock_wait(que_thr_t *thr)
if (Deadlock::check_and_resolve(trx))
{
ut_ad(!trx->lock.wait_lock);
- error_state= DB_DEADLOCK;
+ trx->error_state= DB_DEADLOCK;
goto end_wait;
}
}
else
+ {
+ /* trx->lock.was_chosen_as_deadlock_victim can be changed before
+ lock_sys.wait_mutex is acquired, so let's check it once more. */
+ trx->error_state=
+ trx->lock.was_chosen_as_deadlock_victim ? DB_DEADLOCK : DB_SUCCESS;
goto end_wait;
-
+ }
if (row_lock_wait)
lock_sys.wait_start();
+ trx->error_state= DB_SUCCESS;
+
#ifdef HAVE_REPLICATION
if (rpl)
lock_wait_rpl_report(trx);
#endif
- trx->error_state= DB_SUCCESS;
-
while (trx->lock.wait_lock)
{
int err;
@@ -1807,20 +1837,19 @@ dberr_t lock_wait(que_thr_t *thr)
else
err= my_cond_timedwait(&trx->lock.cond, &lock_sys.wait_mutex.m_mutex,
&abstime);
- error_state= trx->error_state;
- switch (error_state) {
+ switch (trx->error_state) {
case DB_DEADLOCK:
case DB_INTERRUPTED:
break;
default:
- ut_ad(error_state != DB_LOCK_WAIT_TIMEOUT);
+ ut_ad(trx->error_state != DB_LOCK_WAIT_TIMEOUT);
/* Dictionary transactions must ignore KILL, because they could
be executed as part of a multi-transaction DDL operation,
such as rollback_inplace_alter_table() or ha_innobase::delete_table(). */
if (!trx->dict_operation && trx_is_interrupted(trx))
/* innobase_kill_query() can only set trx->error_state=DB_INTERRUPTED
for any transaction that is attached to a connection. */
- error_state= DB_INTERRUPTED;
+ trx->error_state= DB_INTERRUPTED;
else if (!err)
continue;
#ifdef WITH_WSREP
@@ -1828,7 +1857,7 @@ dberr_t lock_wait(que_thr_t *thr)
#endif
else
{
- error_state= DB_LOCK_WAIT_TIMEOUT;
+ trx->error_state= DB_LOCK_WAIT_TIMEOUT;
lock_sys.timeouts++;
}
}
@@ -1848,8 +1877,7 @@ end_wait:
mysql_mutex_unlock(&lock_sys.wait_mutex);
thd_wait_end(trx->mysql_thd);
- trx->error_state= error_state;
- return error_state;
+ return trx->error_state;
}
@@ -1862,7 +1890,7 @@ static void lock_wait_end(trx_t *trx)
ut_ad(state == TRX_STATE_ACTIVE || state == TRX_STATE_PREPARED);
ut_ad(trx->lock.wait_thr);
- if (trx->lock.was_chosen_as_deadlock_victim.fetch_and(byte(~1)))
+ if (trx->lock.was_chosen_as_deadlock_victim)
{
ut_ad(state == TRX_STATE_ACTIVE);
trx->error_state= DB_DEADLOCK;
@@ -3401,17 +3429,18 @@ lock_table_enqueue_waiting(
ut_ad(trx->mutex_is_owner());
ut_ad(!trx->dict_operation_lock_mode);
-#ifdef WITH_WSREP
- if (trx->is_wsrep() && trx->lock.was_chosen_as_deadlock_victim) {
- return(DB_DEADLOCK);
- }
-#endif /* WITH_WSREP */
-
/* Enqueue the lock request that will wait to be granted */
lock_table_create(table, mode | LOCK_WAIT, trx, c_lock);
trx->lock.wait_thr = thr;
- trx->lock.clear_deadlock_victim();
+ /* Apart from Galera, only transactions that have waiting lock
+ may be chosen as deadlock victims. Only one lock can be waited for at a
+ time, and a transaction is associated with a single thread. That is why
+ there must not be waiting lock requests if the transaction is deadlock
+ victim and it is not WSREP. Galera transaction abort can be invoked
+ from MDL acquisition code when the transaction does not have waiting
+ lock, that's why we check only deadlock victim bit here. */
+ ut_ad(!(trx->lock.was_chosen_as_deadlock_victim & 1));
MONITOR_INC(MONITOR_TABLELOCK_WAIT);
return(DB_LOCK_WAIT);
@@ -3949,7 +3978,6 @@ released:
mysql_mutex_unlock(&lock_sys.wait_mutex);
}
- trx->lock.was_chosen_as_deadlock_victim= false;
trx->lock.n_rec_locks= 0;
#ifdef UNIV_DEBUG
@@ -5718,10 +5746,12 @@ dberr_t lock_sys_t::cancel(trx_t *trx, lock_t *lock)
lock_sys.rd_lock(SRW_LOCK_CALL);
mysql_mutex_lock(&lock_sys.wait_mutex);
lock= trx->lock.wait_lock;
- if (!lock);
- else if (check_victim && trx->lock.was_chosen_as_deadlock_victim)
+ /* Even if waiting lock was cancelled while lock_sys.wait_mutex was
+ unlocked, we need to return deadlock error if transaction was chosen
+ as deadlock victim to rollback it */
+ if (check_victim && trx->lock.was_chosen_as_deadlock_victim)
err= DB_DEADLOCK;
- else
+ else if (lock)
goto resolve_table_lock;
}
else
@@ -5769,10 +5799,12 @@ retreat:
lock_sys.wr_lock(SRW_LOCK_CALL);
mysql_mutex_lock(&lock_sys.wait_mutex);
lock= trx->lock.wait_lock;
- if (!lock);
- else if (check_victim && trx->lock.was_chosen_as_deadlock_victim)
+ /* Even if waiting lock was cancelled while lock_sys.wait_mutex was
+ unlocked, we need to return deadlock error if transaction was chosen
+ as deadlock victim to rollback it */
+ if (check_victim && trx->lock.was_chosen_as_deadlock_victim)
err= DB_DEADLOCK;
- else
+ else if (lock)
goto resolve_record_lock;
}
else
@@ -5850,6 +5882,7 @@ while holding a clustered index leaf page latch.
lock request was released */
dberr_t lock_trx_handle_wait(trx_t *trx)
{
+ DEBUG_SYNC_C("lock_trx_handle_wait_enter");
if (trx->lock.was_chosen_as_deadlock_victim)
return DB_DEADLOCK;
if (!trx->lock.wait_lock)
diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc
index 5521312f58e..9ab05429184 100644
--- a/storage/innobase/mtr/mtr0mtr.cc
+++ b/storage/innobase/mtr/mtr0mtr.cc
@@ -1680,43 +1680,6 @@ bool mtr_t::memo_contains(const fil_space_t& space, bool shared)
return true;
}
-#ifdef BTR_CUR_HASH_ADAPT
-/** If a stale adaptive hash index exists on the block, drop it. */
-ATTRIBUTE_COLD
-void mtr_t::defer_drop_ahi(buf_block_t *block, mtr_memo_type_t fix_type)
-{
- switch (fix_type) {
- default:
- ut_ad(fix_type == MTR_MEMO_BUF_FIX);
- /* We do not drop the adaptive hash index, because safely doing so
- would require acquiring exclusive block->page.lock, which could
- lead to hangs in some access paths. Those code paths should have
- no business accessing the adaptive hash index anyway. */
- break;
- case MTR_MEMO_PAGE_S_FIX:
- /* Temporarily release our S-latch. */
- block->page.lock.s_unlock();
- block->page.lock.x_lock();
- if (dict_index_t *index= block->index)
- if (index->freed())
- btr_search_drop_page_hash_index(block);
- block->page.lock.x_unlock();
- block->page.lock.s_lock();
- ut_ad(!block->page.is_read_fixed());
- break;
- case MTR_MEMO_PAGE_SX_FIX:
- block->page.lock.u_x_upgrade();
- if (dict_index_t *index= block->index)
- if (index->freed())
- btr_search_drop_page_hash_index(block);
- block->page.lock.x_u_downgrade();
- break;
- case MTR_MEMO_PAGE_X_FIX:
- btr_search_drop_page_hash_index(block);
- }
-}
-#endif /* BTR_CUR_HASH_ADAPT */
-
/** Upgrade U-latched pages to X */
struct UpgradeX
{
@@ -1771,8 +1734,7 @@ void mtr_t::page_lock(buf_block_t *block, ulint rw_latch)
ut_d(const auto state= block->page.state());
ut_ad(state > buf_page_t::FREED);
ut_ad(state > buf_page_t::WRITE_FIX || state < buf_page_t::READ_FIX);
- switch (rw_latch)
- {
+ switch (rw_latch) {
case RW_NO_LATCH:
fix_type= MTR_MEMO_BUF_FIX;
goto done;
@@ -1798,10 +1760,8 @@ void mtr_t::page_lock(buf_block_t *block, ulint rw_latch)
}
#ifdef BTR_CUR_HASH_ADAPT
- if (dict_index_t *index= block->index)
- if (index->freed())
- defer_drop_ahi(block, fix_type);
-#endif /* BTR_CUR_HASH_ADAPT */
+ btr_search_drop_page_hash_index(block, true);
+#endif
done:
ut_ad(state < buf_page_t::UNFIXED ||
@@ -2032,8 +1992,10 @@ void mtr_t::free(const fil_space_t &space, uint32_t offset)
if (is_logged())
{
- m_memo.for_each_block_in_reverse
- (CIterate<MarkFreed>((MarkFreed{{space.id, offset}})));
+ CIterate<MarkFreed> mf{MarkFreed{{space.id, offset}}};
+ m_memo.for_each_block_in_reverse(mf);
+ if (mf.functor.freed && !m_made_dirty)
+ m_made_dirty= is_block_dirtied(mf.functor.freed);
m_log.close(log_write<FREE_PAGE>({space.id, offset}, nullptr));
}
}
diff --git a/storage/innobase/os/os0file.cc b/storage/innobase/os/os0file.cc
index d7f226b9fba..8fc4c5e6f84 100644
--- a/storage/innobase/os/os0file.cc
+++ b/storage/innobase/os/os0file.cc
@@ -112,7 +112,7 @@ public:
size_t pending_io_count()
{
- return (size_t)m_max_aio - m_cache.size();
+ return m_cache.pos();
}
tpool::task_group* get_task_group()
diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc
index bed88b7e374..7c4181da0d7 100644
--- a/storage/innobase/trx/trx0purge.cc
+++ b/storage/innobase/trx/trx0purge.cc
@@ -399,11 +399,11 @@ static dberr_t trx_purge_free_segment(trx_rseg_t *rseg, fil_addr_t hdr_addr)
mtr.commit();
mtr.start();
mtr.flag_modified();
- mtr.memo_push(rseg_hdr, MTR_MEMO_PAGE_X_FIX);
- mtr.memo_push(block, MTR_MEMO_PAGE_X_MODIFY);
rseg->latch.wr_lock(SRW_LOCK_CALL);
rseg_hdr->page.lock.x_lock();
block->page.lock.x_lock();
+ mtr.memo_push(rseg_hdr, MTR_MEMO_PAGE_X_FIX);
+ mtr.memo_push(block, MTR_MEMO_PAGE_X_MODIFY);
}
/* The page list may now be inconsistent, but the length field
diff --git a/storage/innobase/trx/trx0rec.cc b/storage/innobase/trx/trx0rec.cc
index 98954cff684..a64520623d8 100644
--- a/storage/innobase/trx/trx0rec.cc
+++ b/storage/innobase/trx/trx0rec.cc
@@ -1859,9 +1859,9 @@ trx_undo_page_report_rename(trx_t* trx, const dict_table_t* table,
byte* const start = block->page.frame + first_free;
size_t len = strlen(table->name.m_name);
const size_t fixed = 2 + 1 + 11 + 11 + 2;
- ut_ad(len <= NAME_LEN * 2 + 1);
+ ut_ad(len <= NAME_CHAR_LEN * 5 * 2 + 1);
/* The -10 is used in trx_undo_left() */
- compile_time_assert((NAME_LEN * 1) * 2 + fixed
+ compile_time_assert(NAME_CHAR_LEN * 5 * 2 + fixed
+ TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE
< UNIV_PAGE_SIZE_MIN - 10 - FIL_PAGE_DATA_END);
diff --git a/storage/innobase/trx/trx0roll.cc b/storage/innobase/trx/trx0roll.cc
index 59ea0bdcd8f..59c9a319330 100644
--- a/storage/innobase/trx/trx0roll.cc
+++ b/storage/innobase/trx/trx0roll.cc
@@ -135,6 +135,9 @@ inline void trx_t::rollback_low(trx_savept_t *savept)
}
else
{
+ /* There must not be partial rollback if transaction was chosen as deadlock
+ victim. Galera transaction abort can be invoked during partial rollback. */
+ ut_ad(!(lock.was_chosen_as_deadlock_victim & 1));
ut_a(error_state == DB_SUCCESS);
const undo_no_t limit= savept->least_undo_no;
apply_online_log= false;
@@ -211,6 +214,10 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
case TRX_STATE_NOT_STARTED:
trx->will_lock = false;
ut_ad(trx->mysql_thd);
+ /* Galera transaction abort can be invoked from MDL acquision
+ code, so trx->lock.was_chosen_as_deadlock_victim can be set
+ even if trx->state is TRX_STATE_NOT_STARTED. */
+ ut_ad(!(trx->lock.was_chosen_as_deadlock_victim & 1));
#ifdef WITH_WSREP
trx->wsrep= false;
trx->lock.was_chosen_as_deadlock_victim= false;
@@ -418,9 +425,6 @@ trx_rollback_to_savepoint_for_mysql_low(
trx_mark_sql_stat_end(trx);
trx->op_info = "";
-#ifdef WITH_WSREP
- trx->lock.was_chosen_as_deadlock_victim = false;
-#endif
return(err);
}
diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc
index af46150e64a..0bc81ad56da 100644
--- a/storage/innobase/trx/trx0trx.cc
+++ b/storage/innobase/trx/trx0trx.cc
@@ -1370,9 +1370,8 @@ TRANSACTIONAL_INLINE inline void trx_t::commit_in_memory(const mtr_t *mtr)
wsrep= false;
wsrep_commit_ordered(mysql_thd);
}
- ut_ad(!(lock.was_chosen_as_deadlock_victim & byte(~2U)));
- lock.was_chosen_as_deadlock_victim= false;
#endif /* WITH_WSREP */
+ lock.was_chosen_as_deadlock_victim= false;
}
void trx_t::commit_cleanup()
diff --git a/strings/ctype-czech.c b/strings/ctype-czech.c
index ca466c232eb..89eff459215 100644
--- a/strings/ctype-czech.c
+++ b/strings/ctype-czech.c
@@ -23,13 +23,13 @@
solution was needed than the one-to-one conversion table. To
note a few, here is an example of a Czech sorting sequence:
- co < hlaska < hláska < hlava < chlapec < krtek
+ co < hlaska < hlĂĄska < hlava < chlapec < krtek
It because some of the rules are: double char 'ch' is sorted
- between 'h' and 'i'. Accented character 'á' (a with acute) is
+ between 'h' and 'i'. Accented character 'ĂĄ' (a with acute) is
sorted after 'a' and before 'b', but only if the word is
otherwise the same. However, because 's' is sorted before 'v'
- in hlava, the accentness of 'á' is overridden. There are many
+ in hlava, the accentness of 'ĂĄ' is overridden. There are many
more rules.
This file defines functions my_strxfrm and my_strcoll for
@@ -42,8 +42,9 @@
passes, that's why we need four times more space for expanded
string.
- This file also contains the ISO-Latin-2 definitions of
- characters.
+ The non-ASCII literal strings in this file are encoded
+ in the iso-8859-2 / latin-2 character set
+ (https://en.wikipedia.org/wiki/ISO/IEC_8859-2)
Author: (c) 1997--1998 Jan Pazdziora, adelton@fi.muni.cz
Jan Pazdziora has a shared copyright for this code
@@ -112,7 +113,7 @@ static const struct wordvalue doubles[] = {
};
/*
- Unformal description of the algorithm:
+ Informal description of the algorithm:
We walk the string left to right.
@@ -127,7 +128,7 @@ static const struct wordvalue doubles[] = {
End of pass is marked with value 1 on the output.
- For each character, we read it's value from the table.
+ For each character, we read its value from the table.
If the value is ignore (0), we go straight to the next character.
@@ -139,31 +140,6 @@ static const struct wordvalue doubles[] = {
exists behind it, find its value.
We append 0 to the end.
----
- Neformální popis algoritmu:
-
- Procházíme řetězec zleva doprava.
-
- Konec řetězce je předán buď jako parametr, nebo je to *p == 0.
- Toto je ošetřeno makrem IS_END.
-
- Pokud jsme došli na konec řetězce při průchodu 0, nejdeme na
- začátek, ale na uloženou pozici, protože první a druhý průchod
- běží současně.
-
- Konec vstupu (průchodu) označíme na výstupu hodnotou 1.
-
- Pro každý znak řetězce načteme hodnotu z třídící tabulky.
-
- Jde-li o hodnotu ignorovat (0), skočíme ihned na další znak..
-
- Jde-li o hodnotu konec slova (2) a je to průchod 0 nebo 1,
- přeskočíme všechny další 0 -- 2 a prohodíme průchody.
-
- Jde-li o kompozitní znak (255), otestujeme, zda následuje
- správný do dvojice, dohledáme správnou hodnotu.
-
- Na konci připojíme znak 0
*/
#define ADD_TO_RESULT(dest, len, totlen, value) \
@@ -336,24 +312,23 @@ my_strnxfrm_czech(CHARSET_INFO *cs __attribute__((unused)),
/*
- Neformální popis algoritmu:
-
- procházíme řetězec zleva doprava
- konec řetězce poznáme podle *p == 0
- pokud jsme došli na konec řetězce při průchodu 0, nejdeme na
- začátek, ale na uloženou pozici, protože první a druhý
- průchod běží současně
- konec vstupu (průchodu) označíme na výstupu hodnotou 1
-
- načteme hodnotu z třídící tabulky
- jde-li o hodnotu ignorovat (0), skočíme na další průchod
- jde-li o hodnotu konec slova (2) a je to průchod 0 nebo 1,
- přeskočíme všechny další 0 -- 2 a prohodíme
- průchody
- jde-li o kompozitní znak (255), otestujeme, zda následuje
- správný do dvojice, dohledáme správnou hodnotu
-
- na konci připojíme znak 0
+ Informal description of the algorithm:
+
+ we pass the chain from left to right
+ we know the end of the string by *p == 0
+ if we reached the end of the string on transition 0, then we don't go to
+ start, but to the saved position, because the first and second
+ the passage runs concurrently
+ we mark the end of the input (transition) with the value 1 on the output
+
+ then we load the value from the sorting table
+ if the value is ignore (0), we jump to the next pass
+ if the value is the end of the word (2) and it is a 0 or 1 transition,
+ we skip all the other 0 -- 2 and switch transitions
+ if it is a composite character (255), we test whether it follows
+ correct to the pair, we find the correct value
+
+ then we add the character 0 at the end
*/
diff --git a/strings/ctype-latin1.c b/strings/ctype-latin1.c
index 335c4715bf4..ce2e84666bc 100644
--- a/strings/ctype-latin1.c
+++ b/strings/ctype-latin1.c
@@ -504,19 +504,19 @@ struct charset_info_st my_charset_latin1_nopad=
*
* The modern sort order is used, where:
*
- * 'ä' -> "ae"
- * 'ö' -> "oe"
- * 'ü' -> "ue"
- * 'ß' -> "ss"
+ * 'ä' -> "ae"
+ * 'Ăś' -> "oe"
+ * 'Ăź' -> "ue"
+ * 'ß' -> "ss"
*/
/*
* This is a simple latin1 mapping table, which maps all accented
* characters to their non-accented equivalents. Note: in this
- * table, 'ä' is mapped to 'A', '˙' is mapped to 'Y', etc. - all
+ * table, 'ä' is mapped to 'A', 'ÿ' is mapped to 'Y', etc. - all
* accented characters except the following are treated the same way.
- * Ü, ü, Ö, ö, Ä, ä
+ * Ü, ü, Ö, ö, Ä, ä
*/
static const uchar sort_order_latin1_de[] = {
@@ -582,7 +582,7 @@ static const uchar combo2map[]={
my_strnxfrm_latin_de() on both strings and compared the result strings.
This means that:
- Ä must also matches ÁE and Ač, because my_strxn_frm_latin_de() will convert
+ Ä must also matches ÁE and Aè, because my_strxn_frm_latin_de() will convert
both to AE.
The other option would be to not do any accent removal in
@@ -708,7 +708,7 @@ void my_hash_sort_latin1_de(CHARSET_INFO *cs __attribute__((unused)),
/*
Remove end space. We have to do this to be able to compare
- 'AE' and 'Ä' as identical
+ 'AE' and 'Ä' as identical
*/
end= skip_trailing_space(key, len);
diff --git a/tpool/tpool_structs.h b/tpool/tpool_structs.h
index 7b0fb857695..b49204f2d75 100644
--- a/tpool/tpool_structs.h
+++ b/tpool/tpool_structs.h
@@ -40,79 +40,121 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 - 1301 USA*/
namespace tpool
{
-enum cache_notification_mode
-{
- NOTIFY_ONE,
- NOTIFY_ALL
-};
-
/**
Generic "pointer" cache of a fixed size
with fast put/get operations.
- Compared to STL containers, is faster/does not
- do allocations. However, put() operation will wait
- if there is no free items.
+ Compared to STL containers,e.g stack or queue
+ is faster/does not do allocations.
+
+ However, get() operation will wait if there is no free items.
+
+ We assume that put() will only put back the elements that
+ were retrieved previously with get().
*/
template<typename T> class cache
{
+ /** Protects updates of m_pos and m_cache members */
std::mutex m_mtx;
+
+ /**
+ Notify waiting threads about "cache full" or "cache not empty" conditions
+ @see get() and wait()
+ */
std::condition_variable m_cv;
- std::vector<T> m_base;
+
+ /** Cached items vector.Does not change after construction */
+ std::vector<T> m_base;
+
+ /**
+ Pointers to cached items. Protected by m_mtx. Does not grow after
+ construction. Elements in position [0,m_pos-1] are "borrowed",
+ elements in position [m_pos,capacity()-1] are "free"
+ */
std::vector<T*> m_cache;
- cache_notification_mode m_notification_mode;
+
+ /** Number of threads waiting for "cache full" condition (s. wait())
+ Protected by m_mtx */
int m_waiters;
+ /** Current cache size. Protected by m_mtx*/
+ size_t m_pos;
+
+private:
+
+ inline size_t capacity()
+ {
+ return m_base.size();
+ }
+
+ /**
+ @return true if cache is full (no items are borrowed)
+ */
bool is_full()
{
- return m_cache.size() == m_base.size();
+ return m_pos == 0;
+ }
+
+ /**
+ @return true if cache is empty (all items are borrowed)
+ */
+ bool is_empty()
+ {
+ return m_pos == capacity();
}
public:
- cache(size_t count, cache_notification_mode mode= tpool::cache_notification_mode::NOTIFY_ALL):
- m_mtx(), m_cv(), m_base(count),m_cache(count), m_notification_mode(mode),m_waiters()
+ /**
+ Constructor
+ @param size - maximum number of items in cache
+ */
+ cache(size_t size) : m_mtx(), m_cv(), m_base(size), m_cache(size),
+ m_waiters(), m_pos(0)
{
- for(size_t i = 0 ; i < count; i++)
- m_cache[i]=&m_base[i];
+ for(size_t i= 0 ; i < size; i++)
+ m_cache[i]= &m_base[i];
}
- T* get(bool blocking=true)
+ /**
+ Retrieve an item from cache. Waits for free item, if cache is
+ currently empty.
+ @return borrowed item
+ */
+ T* get()
{
std::unique_lock<std::mutex> lk(m_mtx);
- if (blocking)
- {
- while(m_cache.empty())
- m_cv.wait(lk);
- }
- else
- {
- if(m_cache.empty())
- return nullptr;
- }
- T* ret = m_cache.back();
- m_cache.pop_back();
- return ret;
+ while(is_empty())
+ m_cv.wait(lk);
+ assert(m_pos < capacity());
+ // return last element
+ return m_cache[m_pos++];
}
-
+ /**
+ Put back an item to cache.
+ @param item - item to put back
+ */
void put(T *ele)
{
std::unique_lock<std::mutex> lk(m_mtx);
- m_cache.push_back(ele);
- if (m_notification_mode == NOTIFY_ONE)
- m_cv.notify_one();
- else if(m_cache.size() == 1)
- m_cv.notify_all(); // Signal cache is not empty
- else if(m_waiters && is_full())
- m_cv.notify_all(); // Signal cache is full
+ assert(!is_full());
+ // put element to the logical end of the array
+ m_cache[--m_pos] = ele;
+
+ /* Notify waiters when the cache becomes
+ not empty, or when it becomes full */
+ if (m_pos == 1 || (m_waiters && is_full()))
+ m_cv.notify_all();
}
+ /** Check if pointer represents cached element */
bool contains(T* ele)
{
- return ele >= &m_base[0] && ele <= &m_base[m_base.size() -1];
+ // No locking required, m_base does not change after construction.
+ return ele >= &m_base[0] && ele <= &m_base[capacity() - 1];
}
- /* Wait until cache is full.*/
+ /** Wait until cache is full.*/
void wait()
{
std::unique_lock<std::mutex> lk(m_mtx);
@@ -122,9 +164,13 @@ public:
m_waiters--;
}
- TPOOL_SUPPRESS_TSAN size_t size()
+ /**
+ @return approximate number of "borrowed" items.
+ A "dirty" read, not used in any critical functionality.
+ */
+ TPOOL_SUPPRESS_TSAN size_t pos()
{
- return m_cache.size();
+ return m_pos;
}
};