mirror of
https://github.com/MariaDB/server.git
synced 2025-07-20 16:56:36 +00:00
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:
@ -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";
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
|
Reference in New Issue
Block a user