Commit 2fcf2ec2 authored by sjaakola's avatar sjaakola Committed by Monty

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
parent 4987b5e3
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-`;
#
# 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`
--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
......@@ -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);
......
......@@ -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};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment