MDEV-35731 Assertion `(mem_root->flags & 4) == 0' failed upon 2nd exec of CHECK

... TABLE with FK

The normal order of things in prepared statement is:
1. open tables during prepare
2. use them during all the executions

Currently, tables are opened in the body of CHECK TABLE's execution part.
This is motivated by the nature of this statement: we have to run the checks for
all the tables specified in the list, event if some of them fail to open.

Rewriting it as in normal order is possible, but is out of scope of this task.

That is, we shouldn't activate stmt_arena when constructing the referential
tables list.

It's not enough, since the next statement execution will try to reuse all the
TABLE_LIST's in lex->query_list, but the referential tables are allocated on the
normal query arena, so they should be cleaned up from that list.

Given how specific the open_only_one_table work is, we only have to re-link
table lists as they were before the tables are opened. It's enough to just
assign next_local to next_global for each table specified by
user, i.e. in lex->first_select_lex().

lex->query_tables_last and other lex fields are already maintained by that
function.

Base task: MDEV-34309
This commit is contained in:
Nikita Malyavin 2025-01-14 02:16:53 +01:00
parent 2af2808f57
commit e6862c0620
4 changed files with 68 additions and 1 deletions

View file

@ -186,3 +186,41 @@ Table Op Msg_type Msg_text
test.t check Warning Cannot add or update a child row: a foreign key constraint fails (Key: t_ibfk_1, record: '\x00')
test.t check error Corrupt
DROP TABLE t;
# MDEV-35731 Assertion `(mem_root->flags & 4) == 0' failed upon 2nd
# execution of CHECK TABLE with FK
create table t1 (a int) engine=innodb;
set foreign_key_checks = off;
alter table t1 add foreign key (a) references x(x);
set foreign_key_checks = on;
prepare stmt from 'check table t1 extended';
execute stmt;
Table Op Msg_type Msg_text
test.t1 check Warning Table test.x is not found. Needed for a foreign key t1_ibfk_1
test.t1 check error Corrupt
execute stmt;
Table Op Msg_type Msg_text
test.t1 check Warning Table test.x is not found. Needed for a foreign key t1_ibfk_1
test.t1 check error Corrupt
create table t2 (a int) engine=innodb;
set foreign_key_checks = off;
alter table t2 add foreign key (a) references x(x);
set foreign_key_checks = on;
prepare stmt from 'check table t1, t2, nosuchtable extended';
execute stmt;
Table Op Msg_type Msg_text
test.t1 check Warning Table test.x is not found. Needed for a foreign key t1_ibfk_1
test.t1 check error Corrupt
test.t2 check Warning Table test.x is not found. Needed for a foreign key t2_ibfk_1
test.t2 check error Corrupt
test.nosuchtable check Error Table 'test.nosuchtable' doesn't exist
test.nosuchtable check status Operation failed
execute stmt;
Table Op Msg_type Msg_text
test.t1 check Warning Table test.x is not found. Needed for a foreign key t1_ibfk_1
test.t1 check error Corrupt
test.t2 check Warning Table test.x is not found. Needed for a foreign key t2_ibfk_1
test.t2 check error Corrupt
test.nosuchtable check Error Table 'test.nosuchtable' doesn't exist
test.nosuchtable check status Operation failed
drop table t2;
drop table t1;

View file

@ -126,3 +126,26 @@ ALTER TABLE t ADD FOREIGN KEY (b) REFERENCES t (a);
SET FOREIGN_KEY_CHECKS = ON;
CHECK TABLE t EXTENDED;
DROP TABLE t;
--echo # MDEV-35731 Assertion `(mem_root->flags & 4) == 0' failed upon 2nd
--echo # execution of CHECK TABLE with FK
create table t1 (a int) engine=innodb;
set foreign_key_checks = off;
alter table t1 add foreign key (a) references x(x);
set foreign_key_checks = on;
prepare stmt from 'check table t1 extended';
execute stmt;
execute stmt;
create table t2 (a int) engine=innodb;
set foreign_key_checks = off;
alter table t2 add foreign key (a) references x(x);
set foreign_key_checks = on;
prepare stmt from 'check table t1, t2, nosuchtable extended';
execute stmt;
execute stmt;
drop table t2;
drop table t1;

View file

@ -1468,6 +1468,13 @@ send_result_message:
}
close_thread_tables(thd);
/*
Opening tables and extending table list doesn't happen on the prepare
stage here, so we have to make sure that Prepared_statement won't try to
reuse table lists, that have a statement execution lifetime.
*/
table->next_global= table->next_local;
if (storage_engine_name[0])
{
/* Table was changed (repair, optimize or something similar) */

View file

@ -5200,7 +5200,6 @@ Check_table_prelocking_strategy::handle_table(THD *thd,
TABLE_LIST *tables,
bool *need_prelocking)
{
Query_arena_stmt stmt_ctx(thd);
List <FOREIGN_KEY_INFO> fk_child_list, fk_parent_list;
tables->table->file->get_foreign_key_list(thd, &fk_child_list);
tables->table->file->get_parent_foreign_key_list(thd, &fk_parent_list);