Commit 9377e9ba authored by sjaakola's avatar sjaakola Committed by Jan Lindström

MDEV-21153 Replica nodes crash due to indexed virtual columns and FK cascading delete

Fix for MDEV-23033 fixes a problem in replication applying of transactions, which contain cascading foreign key delete for a table, which has indexed virtual column.
This fix adds slave_fk_event_map flag for table, to mark when the prelocking is needed for applying of a transaction.
See commit 608b0ee5 for more details.
However, this fix is targeted for async replication only, Rows_log_event::do_apply_event() has condition to rule out galera replication from the fix domain, and use cases suffering from MDEV-23033 and related MDEV-21153 will fail in galera cluster.

The fix in this commit removes the condition to rule out the setting of slave_fk_event_map flag from galera replication, and makes the fix in MDEV-23033 effective for galera replication as well.

However, the above fix has caused regressions for some galera_sr suite tests, which run tests for streaming replication.
This regression can be observed e.g. by: /mtr galera_sr.galera_sr_multirow_rollback  --mysqld=--slave_run_triggers_for_rbr=yes
These galera_sr suite tests were failing in last phase of replication applying, where actual transaction is already applied, and streaming replication related meta data needs to be updated in wsrep system tables.
Opening the wsrep system tables failed for corrupt data in THD::lex:query_tables_list. The fix in this commit uses back query table list for the duration of fragment update operation.

Finally, a mtr test for virtual column support has been added. galera.galera_virtual_column.test has as first test a scenario from MDEV-21153

new fix
Reviewed-by: default avatarJan Lindström <jan.lindstrom@mariadb.com>
parent 8bcddb02
connection node_2;
connection node_1;
connection node_1;
CREATE TABLE p (id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT) ENGINE = InnoDB;
CREATE TABLE c (id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, pid INT UNSIGNED, bitmap TINYINT UNSIGNED NOT NULL DEFAULT 0, bitmap5 TINYINT UNSIGNED GENERATED ALWAYS AS (bitmap&(1<<5)) VIRTUAL, FOREIGN KEY (pid) REFERENCES p (id) ON DELETE CASCADE ON UPDATE CASCADE);
CREATE INDEX bitmap5 ON c(bitmap5) USING BTREE;
INSERT INTO p VALUES(1);
INSERT INTO c(pid) VALUES(1);
connection node_2;
connection node_1;
DELETE FROM p WHERE id=1;
SELECT * FROM p;
id
SELECT * FROM c;
id pid bitmap bitmap5
connection node_2;
connection node_1;
DROP TABLE c;
DROP TABLE p;
#
# This test is for testing virtual columnm support in galera cluster
#
--source include/galera_cluster.inc
--source include/have_innodb.inc
#
# test case for verifying that cascaded delete in a table with virtual column does not crash slave node
#
--connection node_1
CREATE TABLE p (id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT) ENGINE = InnoDB;
CREATE TABLE c (id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, pid INT UNSIGNED, bitmap TINYINT UNSIGNED NOT NULL DEFAULT 0, bitmap5 TINYINT UNSIGNED GENERATED ALWAYS AS (bitmap&(1<<5)) VIRTUAL, FOREIGN KEY (pid) REFERENCES p (id) ON DELETE CASCADE ON UPDATE CASCADE);
# not sure of this index is needed for the test
CREATE INDEX bitmap5 ON c(bitmap5) USING BTREE;
INSERT INTO p VALUES(1);
INSERT INTO c(pid) VALUES(1);
--connection node_2
# wait until both INSERTS have arrived in node_2
--let $wait_condition = SELECT COUNT(*) = 1 FROM c
--source include/wait_condition.inc
--connection node_1
# delete from parent table, it will cascade into child table
# node_2 might have problem in applying this cascaded delete
DELETE FROM p WHERE id=1;
SELECT * FROM p;
SELECT * FROM c;
--connection node_2
--let $wait_condition = SELECT COUNT(*) = 0 FROM c;
--source include/wait_condition.inc
--connection node_1
DROP TABLE c;
DROP TABLE p;
...@@ -11392,7 +11392,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) ...@@ -11392,7 +11392,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
tables->trg_event_map= new_trg_event_map; tables->trg_event_map= new_trg_event_map;
lex->query_tables_last= &tables->next_global; lex->query_tables_last= &tables->next_global;
} }
else if (!WSREP_ON) else
{ {
tables->slave_fk_event_map= new_trg_event_map; tables->slave_fk_event_map= new_trg_event_map;
lex->query_tables_last= &tables->next_global; lex->query_tables_last= &tables->next_global;
......
...@@ -899,6 +899,13 @@ int Wsrep_schema::append_fragment(THD* thd, ...@@ -899,6 +899,13 @@ int Wsrep_schema::append_fragment(THD* thd,
thd->thread_id, thd->thread_id,
os.str().c_str(), os.str().c_str(),
transaction_id.get()); transaction_id.get());
/* use private query table list for the duration of fragment storing,
populated query table list from "parent DML" may cause problems .e.g
for virtual column handling
*/
Query_tables_list query_tables_list_backup;
thd->lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
Wsrep_schema_impl::binlog_off binlog_off(thd); Wsrep_schema_impl::binlog_off binlog_off(thd);
Wsrep_schema_impl::init_stmt(thd); Wsrep_schema_impl::init_stmt(thd);
...@@ -906,6 +913,7 @@ int Wsrep_schema::append_fragment(THD* thd, ...@@ -906,6 +913,7 @@ int Wsrep_schema::append_fragment(THD* thd,
if (Wsrep_schema_impl::open_for_write(thd, sr_table_str.c_str(), &frag_table)) if (Wsrep_schema_impl::open_for_write(thd, sr_table_str.c_str(), &frag_table))
{ {
trans_rollback_stmt(thd); trans_rollback_stmt(thd);
thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
DBUG_RETURN(1); DBUG_RETURN(1);
} }
...@@ -919,9 +927,11 @@ int Wsrep_schema::append_fragment(THD* thd, ...@@ -919,9 +927,11 @@ int Wsrep_schema::append_fragment(THD* thd,
if ((error= Wsrep_schema_impl::insert(frag_table))) { if ((error= Wsrep_schema_impl::insert(frag_table))) {
WSREP_ERROR("Failed to write to frag table: %d", error); WSREP_ERROR("Failed to write to frag table: %d", error);
trans_rollback_stmt(thd); trans_rollback_stmt(thd);
thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
DBUG_RETURN(1); DBUG_RETURN(1);
} }
Wsrep_schema_impl::finish_stmt(thd); Wsrep_schema_impl::finish_stmt(thd);
thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -938,6 +948,13 @@ int Wsrep_schema::update_fragment_meta(THD* thd, ...@@ -938,6 +948,13 @@ int Wsrep_schema::update_fragment_meta(THD* thd,
ws_meta.seqno().get()); ws_meta.seqno().get());
DBUG_ASSERT(ws_meta.seqno().is_undefined() == false); DBUG_ASSERT(ws_meta.seqno().is_undefined() == false);
/* use private query table list for the duration of fragment storing,
populated query table list from "parent DML" may cause problems .e.g
for virtual column handling
*/
Query_tables_list query_tables_list_backup;
thd->lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
Wsrep_schema_impl::binlog_off binlog_off(thd); Wsrep_schema_impl::binlog_off binlog_off(thd);
int error; int error;
uchar key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; uchar key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH];
...@@ -947,6 +964,7 @@ int Wsrep_schema::update_fragment_meta(THD* thd, ...@@ -947,6 +964,7 @@ int Wsrep_schema::update_fragment_meta(THD* thd,
Wsrep_schema_impl::init_stmt(thd); Wsrep_schema_impl::init_stmt(thd);
if (Wsrep_schema_impl::open_for_write(thd, sr_table_str.c_str(), &frag_table)) if (Wsrep_schema_impl::open_for_write(thd, sr_table_str.c_str(), &frag_table))
{ {
thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
DBUG_RETURN(1); DBUG_RETURN(1);
} }
...@@ -967,6 +985,7 @@ int Wsrep_schema::update_fragment_meta(THD* thd, ...@@ -967,6 +985,7 @@ int Wsrep_schema::update_fragment_meta(THD* thd,
error); error);
} }
Wsrep_schema_impl::finish_stmt(thd); Wsrep_schema_impl::finish_stmt(thd);
thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
DBUG_RETURN(1); DBUG_RETURN(1);
} }
...@@ -982,11 +1001,13 @@ int Wsrep_schema::update_fragment_meta(THD* thd, ...@@ -982,11 +1001,13 @@ int Wsrep_schema::update_fragment_meta(THD* thd,
frag_table->s->table_name.str, frag_table->s->table_name.str,
error); error);
Wsrep_schema_impl::finish_stmt(thd); Wsrep_schema_impl::finish_stmt(thd);
thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
DBUG_RETURN(1); DBUG_RETURN(1);
} }
int ret= Wsrep_schema_impl::end_index_scan(frag_table); int ret= Wsrep_schema_impl::end_index_scan(frag_table);
Wsrep_schema_impl::finish_stmt(thd); Wsrep_schema_impl::finish_stmt(thd);
thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
DBUG_RETURN(ret); DBUG_RETURN(ret);
} }
......
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