MDEV-26048 Table 'test._test_new' doesn't exist when add trigger (use pt-osc)

There is inconsistency happens between two INSERTs. First INSERT
happens after only tr1 was created. It enters
DML_prelocking_strategy::handle_table() and sets need_prelocking as
there is table->triggers. But there is no relevant triggers: tr1 is
TRG_EVENT_DELETE, so nothing was added for prelocking. Nonetheless,
extend_table_list() calls mark_as_requiring_prelocking() based on
need_prelocking. It sets lex->query_tables_own_last to
save_query_tables_last which is double pointer to NULL. Then
reset_lex_and_exec_core() based on that value (double pointer is
non-NULL itself) propagates that to SP's m_lex.

After tr2 created, next execution of INSERT thinks it has prelocking
list. extend_table_list() receives has_prelocking_list:true and
skips handle_table() (and therefore any prelocking list updates).

As a result when process_triggers() is trying to open t2 it does not
see it in the list of open (and prelocked) tables and fails with
ER_NO_SUCH_TABLE (which is misleading error code as t2 exists).

The fix avoids setting need_prelocking for the cases when no triggers
executed (i.e. no matching event type).

Note that we can not disable prelocking for triggers without any
tables like that:

  *need_prelocking|= trigger->add_used_tables_to_table_list(thd,
	     &prelocking_ctx->query_tables_last,
	     table_list->belong_to_view);

because trigger must be executed under LTM_PRELOCKED, otherwise it
will close not own tables (this can be further improved, see TODO in
the patch).

Thanks to Chǔ Huáxīng <15381158111@163.com> for the patch idea.
This commit is contained in:
Aleksey Midenkov
2025-07-08 01:31:47 +03:00
parent 6205d39958
commit 5689d1275f
7 changed files with 97 additions and 11 deletions

View File

@ -117,3 +117,35 @@ ERROR 42000: Unknown database 's1'
connection default;
disconnect con3;
SET DEBUG_SYNC = 'RESET';
#
# MDEV-26048 Table 'test._test_new' doesn't exist when add trigger (use pt-osc)
#
create table t1 (x int);
create procedure sp()
begin
declare a int default 1;
while a <= 2 do
set debug_sync= concat("now wait_for created_", a);
insert into t1 values(a);
set debug_sync= "now signal inserted";
set a= a + 1;
end while;
end$$
connect con1,localhost,root,,test;
call sp();
connection default;
create table t2 (y int);
create trigger tr1 after delete on t1 for each row delete from t2;
set debug_sync= "now signal created_1";
set debug_sync= "now wait_for inserted";
create trigger tr2 after insert on t1 for each row insert t2 values (0);
set debug_sync= "now signal created_2";
connection con1;
disconnect con1;
connection default;
drop trigger tr1;
drop trigger tr2;
drop table t2;
drop procedure sp;
drop table t1;
set debug_sync= "reset";

View File

@ -168,3 +168,49 @@ SET DEBUG_SYNC = 'RESET';
# Check that all connections opened by test cases in this file are really
# gone so execution of other tests won't be affected by their presence.
--source include/wait_until_count_sessions.inc
--echo #
--echo # MDEV-26048 Table 'test._test_new' doesn't exist when add trigger (use pt-osc)
--echo #
create table t1 (x int);
--delimiter $$
# Note: it's important to do INSERT in a loop as
# the bug reproduces only on the same statetement
# (has_prelocking_list depends on it)
create procedure sp()
begin
declare a int default 1;
while a <= 2 do
set debug_sync= concat("now wait_for created_", a);
insert into t1 values(a);
set debug_sync= "now signal inserted";
set a= a + 1;
end while;
end$$
--delimiter ;
--connect (con1,localhost,root,,test)
send call sp();
--connection default
create table t2 (y int);
create trigger tr1 after delete on t1 for each row delete from t2;
set debug_sync= "now signal created_1";
set debug_sync= "now wait_for inserted";
create trigger tr2 after insert on t1 for each row insert t2 values (0);
set debug_sync= "now signal created_2";
--connection con1
--reap
--disconnect con1
--connection default
drop trigger tr1;
drop trigger tr2;
drop table t2;
drop procedure sp;
drop table t1;
set debug_sync= "reset";
# End of 10.11 tests

View File

@ -3572,6 +3572,8 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
We've already entered/left prelocked mode with this statement.
Attach the list of tables that need to be prelocked and mark m_lex
as having such list attached.
TODO: DBUG_ASSERT(prelocking_tables);
*/
*lex_query_tables_own_last= prelocking_tables;
m_lex->mark_as_requiring_prelocking(lex_query_tables_own_last);

View File

@ -1269,7 +1269,8 @@ class sp_lex_keeper
public:
sp_lex_keeper(LEX *lex, bool lex_resp)
: m_lex(lex), m_lex_resp(lex_resp),
: m_lex(lex), m_lex_resp(lex_resp),
prelocking_tables(NULL),
lex_query_tables_own_last(NULL)
{
lex->sp_lex_in_use= TRUE;

View File

@ -3709,7 +3709,12 @@ bool extend_table_list(THD *thd, TABLE_LIST *tables,
&need_prelocking);
if (need_prelocking && ! lex->requires_prelocking())
{
/*
TODO: DBUG_ASSERT(save_query_tables_last != lex->query_tables_last);
*/
lex->mark_as_requiring_prelocking(save_query_tables_last);
}
}
return error;
}
@ -4912,14 +4917,10 @@ bool DML_prelocking_strategy::handle_table(THD *thd,
if (table_list->trg_event_map)
{
if (table->triggers)
{
*need_prelocking= TRUE;
if (table->triggers->
add_tables_and_routines_for_triggers(thd, prelocking_ctx, table_list))
return TRUE;
}
Table_triggers_list *tr= table->triggers;
if (tr && tr->add_tables_and_routines_for_triggers(thd, prelocking_ctx,
table_list, need_prelocking))
return true;
if (prepare_fk_prelocking_list(thd, prelocking_ctx, table_list,
need_prelocking,

View File

@ -2532,7 +2532,8 @@ bool
Table_triggers_list::
add_tables_and_routines_for_triggers(THD *thd,
Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list)
TABLE_LIST *table_list,
bool *need_prelocking)
{
DBUG_ASSERT(static_cast<int>(table_list->lock_type) >=
static_cast<int>(TL_FIRST_WRITE));
@ -2552,6 +2553,8 @@ add_tables_and_routines_for_triggers(THD *thd,
if (unlikely(!triggers->body)) // Parse error
continue;
*need_prelocking= true;
MDL_key key(MDL_key::TRIGGER, trigger->m_db.str, trigger->m_name.str);
if (sp_add_used_routine(prelocking_ctx,

View File

@ -309,7 +309,8 @@ public:
bool add_tables_and_routines_for_triggers(THD *thd,
Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list);
TABLE_LIST *table_list,
bool *need_prelocking);
Field **nullable_fields() { return record0_field; }
void clear_extra_null_bitmap()