From 2fcf2ec229862fe24a97c54db4e62f1ae8f9ed63 Mon Sep 17 00:00:00 2001 From: sjaakola Date: Thu, 4 Apr 2024 17:12:09 +0300 Subject: [PATCH] MDEV-33749 hyphen in table name can cause galera certification failures Fix in this commit handles foreign key value appending into write set so that db and table names are converted from the filepath format to tablename format. This is compatible with key values appended from elsewhere in the code base There is a mtr test galera.galera_table_with_hyphen for regression testing Reviewer: monty@mariadb.com --- .../galera/r/galera_table_with_hyphen.result | 52 +++++++++++++++++++ .../galera/t/galera_table_with_hyphen.inc | 48 +++++++++++++++++ .../galera/t/galera_table_with_hyphen.test | 34 ++++++++++++ sql/handler.cc | 11 +++- storage/innobase/handler/ha_innodb.cc | 43 ++++++++++----- 5 files changed, 173 insertions(+), 15 deletions(-) create mode 100644 mysql-test/suite/galera/r/galera_table_with_hyphen.result create mode 100644 mysql-test/suite/galera/t/galera_table_with_hyphen.inc create mode 100644 mysql-test/suite/galera/t/galera_table_with_hyphen.test diff --git a/mysql-test/suite/galera/r/galera_table_with_hyphen.result b/mysql-test/suite/galera/r/galera_table_with_hyphen.result new file mode 100644 index 00000000000..c9993004b53 --- /dev/null +++ b/mysql-test/suite/galera/r/galera_table_with_hyphen.result @@ -0,0 +1,52 @@ +connection node_2; +connection node_1; +connection node_1; +set wsrep_sync_wait=0; +connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1; +SET SESSION wsrep_sync_wait = 0; +connection node_1; +SET GLOBAL wsrep_slave_threads=2; +CREATE TABLE `par-ent` ( id INT AUTO_INCREMENT PRIMARY KEY, j INT) ENGINE=InnoDB; +CREATE TABLE `child` (id INT AUTO_INCREMENT PRIMARY KEY, parent_id INT, j INT, FOREIGN KEY (parent_id) REFERENCES `par-ent`(id)) ENGINE=InnoDB; +INSERT INTO `par-ent` VALUES (23,0); +connection node_2; +connection node_1a; +SET GLOBAL DEBUG_DBUG='+d,wsrep_ha_write_row'; +connection node_2; +INSERT INTO `child` VALUES (21,23,0),(22,23,0),(23,23,0); +connection node_1a; +SET DEBUG_SYNC='now WAIT_FOR wsrep_ha_write_row_reached'; +connection node_2; +UPDATE `par-ent` SET j=2 WHERE id=23; +connection node_1a; +SET GLOBAL DEBUG_DBUG='-d,wsrep_ha_write_row'; +SET DEBUG_SYNC='now SIGNAL wsrep_ha_write_row_continue'; +SET GLOBAL DEBUG_DBUG="RESET"; +SET DEBUG_SYNC = 'RESET'; +SET GLOBAL wsrep_slave_threads=DEFAULT; +connection node_2; +drop table `child`; +drop table `par-ent`; +connection node_1; +SET GLOBAL wsrep_slave_threads=2; +CREATE TABLE `p-arent-` ( id INT AUTO_INCREMENT PRIMARY KEY, j INT) ENGINE=InnoDB; +CREATE TABLE `c-hild` (id INT AUTO_INCREMENT PRIMARY KEY, parent_id INT, j INT, FOREIGN KEY (parent_id) REFERENCES `p-arent-`(id)) ENGINE=InnoDB; +INSERT INTO `p-arent-` VALUES (23,0); +connection node_2; +connection node_1a; +SET GLOBAL DEBUG_DBUG='+d,wsrep_ha_write_row'; +connection node_2; +INSERT INTO `c-hild` VALUES (21,23,0),(22,23,0),(23,23,0); +connection node_1a; +SET DEBUG_SYNC='now WAIT_FOR wsrep_ha_write_row_reached'; +connection node_2; +UPDATE `p-arent-` SET j=2 WHERE id=23; +connection node_1a; +SET GLOBAL DEBUG_DBUG='-d,wsrep_ha_write_row'; +SET DEBUG_SYNC='now SIGNAL wsrep_ha_write_row_continue'; +SET GLOBAL DEBUG_DBUG="RESET"; +SET DEBUG_SYNC = 'RESET'; +SET GLOBAL wsrep_slave_threads=DEFAULT; +connection node_2; +drop table `c-hild`; +drop table `p-arent-`; diff --git a/mysql-test/suite/galera/t/galera_table_with_hyphen.inc b/mysql-test/suite/galera/t/galera_table_with_hyphen.inc new file mode 100644 index 00000000000..ac79d864e82 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_table_with_hyphen.inc @@ -0,0 +1,48 @@ +# +# parameters: +# $fk_child - child table name +# $fk_parent - parent table name +# +--connection node_1 +SET GLOBAL wsrep_slave_threads=2; + +--eval CREATE TABLE `$fk_parent` ( id INT AUTO_INCREMENT PRIMARY KEY, j INT) ENGINE=InnoDB + +--eval CREATE TABLE `$fk_child` (id INT AUTO_INCREMENT PRIMARY KEY, parent_id INT, j INT, FOREIGN KEY (parent_id) REFERENCES `$fk_parent`(id)) ENGINE=InnoDB + +--eval INSERT INTO `$fk_parent` VALUES (23,0) + +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 1 FROM `$fk_parent`; +--source include/wait_condition.inc + +--connection node_1a +SET GLOBAL DEBUG_DBUG='+d,wsrep_ha_write_row'; + +--connection node_2 +--eval INSERT INTO `$fk_child` VALUES (21,23,0),(22,23,0),(23,23,0) + +--connection node_1a +SET DEBUG_SYNC='now WAIT_FOR wsrep_ha_write_row_reached'; + +--let $wsrep_received_before = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'wsrep_received'` + +--connection node_2 +--eval UPDATE `$fk_parent` SET j=2 WHERE id=23 + +--connection node_1a +--let $wait_condition = SELECT VARIABLE_VALUE = $wsrep_received_before + 1 FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME = 'wsrep_received' +--source include/wait_condition.inc + +SET GLOBAL DEBUG_DBUG='-d,wsrep_ha_write_row'; +SET DEBUG_SYNC='now SIGNAL wsrep_ha_write_row_continue'; + +SET GLOBAL DEBUG_DBUG="RESET"; +SET DEBUG_SYNC = 'RESET'; + +SET GLOBAL wsrep_slave_threads=DEFAULT; + +--connection node_2 +--eval drop table `$fk_child` +--eval drop table `$fk_parent` + diff --git a/mysql-test/suite/galera/t/galera_table_with_hyphen.test b/mysql-test/suite/galera/t/galera_table_with_hyphen.test new file mode 100644 index 00000000000..1b28bdeb3ca --- /dev/null +++ b/mysql-test/suite/galera/t/galera_table_with_hyphen.test @@ -0,0 +1,34 @@ +--source include/galera_cluster.inc +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc + +# +# Testing how tables and databases with special characters +# are treated in certification +# +# The test creates two tables having foreign key constraint +# reference and executes two transactions which modify +# same rows. The same test is executed with different names +# containin special characters to see if the certification +# can detect the conflicts +# +# Actual test is in include file galera_table_with_hyphen.inc +# It create the test tables from parameters $fk_child and +# $fk_parent +# +--connection node_1 +set wsrep_sync_wait=0; + +--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1 +SET SESSION wsrep_sync_wait = 0; + +--let $fk_child = child +--let $fk_parent = par-ent + +--source galera_table_with_hyphen.inc + +--let $fk_child = c-hild +--let $fk_parent = p-arent- + +--source galera_table_with_hyphen.inc diff --git a/sql/handler.cc b/sql/handler.cc index 352076a9e21..24bc845c694 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -7270,7 +7270,16 @@ int handler::ha_write_row(const uchar *buf) m_lock_type == F_WRLCK); DBUG_ENTER("handler::ha_write_row"); DEBUG_SYNC_C("ha_write_row_start"); - +#ifdef WITH_WSREP + DBUG_EXECUTE_IF("wsrep_ha_write_row", + { + const char act[]= + "now " + "SIGNAL wsrep_ha_write_row_reached " + "WAIT_FOR wsrep_ha_write_row_continue"; + DBUG_ASSERT(!debug_sync_set_action(ha_thd(), STRING_WITH_LEN(act))); + }); +#endif /* WITH_WSREP */ if ((error= ha_check_overlaps(NULL, buf))) DBUG_RETURN(error); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 19cfa21c84e..01c881e1bb8 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -9786,7 +9786,8 @@ wsrep_append_foreign_key( } ulint rcode = DB_SUCCESS; - char cache_key[513] = {'\0'}; + char cache_key[MAX_FULL_NAME_LEN] = {'\0'}; + char db_name[MAX_DATABASE_NAME_LEN+1] = {'\0'}; size_t cache_key_len = 0; if ( !((referenced) ? @@ -9871,14 +9872,38 @@ wsrep_append_foreign_key( return DB_ERROR; } - strncpy(cache_key, + char * fk_table = (wsrep_protocol_version > 1) ? ((referenced) ? foreign->referenced_table->name.m_name : foreign->foreign_table->name.m_name) : - foreign->foreign_table->name.m_name, sizeof(cache_key) - 1); - cache_key_len = strlen(cache_key); + foreign->foreign_table->name.m_name; + /* convert db and table name parts separately to system charset */ + ulint db_name_len = dict_get_db_name_len(fk_table); + strmake(db_name, fk_table, db_name_len); + uint errors; + cache_key_len= innobase_convert_to_system_charset(cache_key, + db_name, sizeof(cache_key), &errors); + if (errors) { + WSREP_WARN("unexpected foreign key table %s %s", + foreign->referenced_table->name.m_name, + foreign->foreign_table->name.m_name); + return DB_ERROR; + } + + /* after db name adding 0 and then converted table name */ + cache_key[db_name_len]= '\0'; + cache_key_len++; + + cache_key_len+= innobase_convert_to_system_charset(cache_key+cache_key_len, + fk_table+db_name_len+1, sizeof(cache_key), &errors); + if (errors) { + WSREP_WARN("unexpected foreign key table %s %s", + foreign->referenced_table->name.m_name, + foreign->foreign_table->name.m_name); + return DB_ERROR; + } #ifdef WSREP_DEBUG_PRINT ulint j; fprintf(stderr, "FK parent key, table: %s %s len: %lu ", @@ -9888,16 +9913,6 @@ wsrep_append_foreign_key( } fprintf(stderr, "\n"); #endif - char *p = strchr(cache_key, '/'); - - if (p) { - *p = '\0'; - } else { - WSREP_WARN("unexpected foreign key table %s %s", - foreign->referenced_table->name.m_name, - foreign->foreign_table->name.m_name); - } - wsrep_buf_t wkey_part[3]; wsrep_key_t wkey = {wkey_part, 3};