mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
Ensure that one can drop a trigger with an orphan .TRN file
Before this fix, one would get a 'Trigger ... already exists' when trying to create a trigger matching the original name and 'Trigger ... does not exists" when trying to drop it. Fixes a reported bug in MDEV-25180 Atomic ALTER TABLE MDEV-25517 Atomic DDL: Assertion `query_arg' in THD::binlog_query upon DROP TRIGGER The bug was that the stmt_query variable was not populated with the query in case of DROP TRIGGER of an orphan trigger (.TRN file exists & table exists, but the trigger was not in table->triggers).
This commit is contained in:
@ -2450,3 +2450,41 @@ DROP TABLE t1;
|
|||||||
#
|
#
|
||||||
# End of 10.3 tests
|
# End of 10.3 tests
|
||||||
#
|
#
|
||||||
|
#
|
||||||
|
# Test dropping orphan .trn file
|
||||||
|
#
|
||||||
|
create table t1 (a int);
|
||||||
|
create trigger t1_trg before insert on t1 for each row
|
||||||
|
begin
|
||||||
|
if isnull(new.a) then
|
||||||
|
set new.a:= 1000;
|
||||||
|
end if;
|
||||||
|
end|
|
||||||
|
insert into t1 values (null);
|
||||||
|
select * from t1;
|
||||||
|
a
|
||||||
|
1000
|
||||||
|
drop table t1;
|
||||||
|
drop trigger t1_trg;
|
||||||
|
Warnings:
|
||||||
|
Error 1146 Table 'test.t1' doesn't exist
|
||||||
|
Warning 4181 Dropped orphan trigger 't1_trg', originally created for table: 't1'
|
||||||
|
create table t1 (a int);
|
||||||
|
drop trigger t1_trg;
|
||||||
|
Warnings:
|
||||||
|
Warning 4181 Dropped orphan trigger 't1_trg', originally created for table: 't1'
|
||||||
|
create trigger t1_trg_2 before insert on t1 for each row
|
||||||
|
begin
|
||||||
|
if isnull(new.a) then
|
||||||
|
set new.a:= 1000;
|
||||||
|
end if;
|
||||||
|
end|
|
||||||
|
drop trigger t1_trg;
|
||||||
|
Warnings:
|
||||||
|
Error 1360 Trigger does not exist
|
||||||
|
Warning 4181 Dropped orphan trigger 't1_trg', originally created for table: 't1'
|
||||||
|
drop trigger t1_trg_2;
|
||||||
|
drop table t1;
|
||||||
|
#
|
||||||
|
# End of 10.6 tests
|
||||||
|
#
|
||||||
|
@ -2782,3 +2782,48 @@ DROP TABLE t1;
|
|||||||
--echo #
|
--echo #
|
||||||
--echo # End of 10.3 tests
|
--echo # End of 10.3 tests
|
||||||
--echo #
|
--echo #
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Test dropping orphan .trn file
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
let $MYSQLD_DATADIR= `select @@datadir`;
|
||||||
|
|
||||||
|
create table t1 (a int);
|
||||||
|
delimiter |;
|
||||||
|
create trigger t1_trg before insert on t1 for each row
|
||||||
|
begin
|
||||||
|
if isnull(new.a) then
|
||||||
|
set new.a:= 1000;
|
||||||
|
end if;
|
||||||
|
end|
|
||||||
|
delimiter ;|
|
||||||
|
insert into t1 values (null);
|
||||||
|
select * from t1;
|
||||||
|
|
||||||
|
--copy_file $MYSQLD_DATADIR/test/t1_trg.TRN $MYSQLD_DATADIR/test/t1_trg.TMP
|
||||||
|
drop table t1;
|
||||||
|
--copy_file $MYSQLD_DATADIR/test/t1_trg.TMP $MYSQLD_DATADIR/test/t1_trg.TRN
|
||||||
|
drop trigger t1_trg;
|
||||||
|
create table t1 (a int);
|
||||||
|
--copy_file $MYSQLD_DATADIR/test/t1_trg.TMP $MYSQLD_DATADIR/test/t1_trg.TRN
|
||||||
|
drop trigger t1_trg;
|
||||||
|
|
||||||
|
# Test creating an additonal trigger for t1, but with different names
|
||||||
|
delimiter |;
|
||||||
|
create trigger t1_trg_2 before insert on t1 for each row
|
||||||
|
begin
|
||||||
|
if isnull(new.a) then
|
||||||
|
set new.a:= 1000;
|
||||||
|
end if;
|
||||||
|
end|
|
||||||
|
delimiter ;|
|
||||||
|
--copy_file $MYSQLD_DATADIR/test/t1_trg.TMP $MYSQLD_DATADIR/test/t1_trg.TRN
|
||||||
|
drop trigger t1_trg;
|
||||||
|
drop trigger t1_trg_2;
|
||||||
|
drop table t1;
|
||||||
|
--remove_file $MYSQLD_DATADIR/test/t1_trg.TMP
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # End of 10.6 tests
|
||||||
|
--echo #
|
||||||
|
@ -22,3 +22,17 @@ master-bin.000001 # Gtid # # GTID #-#-#
|
|||||||
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` TRIGGER tr3_bi BEFORE INSERT ON t1 FOR EACH ROW precedes tr4_bi INSERT INTO t2 (a) VALUES (NEW.a + 400)
|
master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` TRIGGER tr3_bi BEFORE INSERT ON t1 FOR EACH ROW precedes tr4_bi INSERT INTO t2 (a) VALUES (NEW.a + 400)
|
||||||
master-bin.000001 # Gtid # # GTID #-#-#
|
master-bin.000001 # Gtid # # GTID #-#-#
|
||||||
master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */
|
master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */
|
||||||
|
#
|
||||||
|
# MDEV-25517 Atomic DDL: Assertion `query_arg' in THD::binlog_query
|
||||||
|
# upon DROP TRIGGER
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (a INT);
|
||||||
|
CREATE TRIGGER trg AFTER INSERT ON t1 FOR EACH ROW SET @x = 1;
|
||||||
|
connect con1,localhost,root,,test;
|
||||||
|
DROP TRIGGER trg;
|
||||||
|
connection default;
|
||||||
|
DROP TRIGGER trg;
|
||||||
|
connection con1;
|
||||||
|
disconnect con1;
|
||||||
|
connection default;
|
||||||
|
DROP TABLE t1;
|
||||||
|
@ -21,3 +21,31 @@ DROP TABLE t1;
|
|||||||
|
|
||||||
--let $binlog_file = LAST
|
--let $binlog_file = LAST
|
||||||
source include/show_binlog_events.inc;
|
source include/show_binlog_events.inc;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-25517 Atomic DDL: Assertion `query_arg' in THD::binlog_query
|
||||||
|
--echo # upon DROP TRIGGER
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
# This test case is 'random' by design. For most cases the second DROP TRIGGER
|
||||||
|
# will generate a warning "Dropped orphan trigger...", but if there is a timing
|
||||||
|
# issue, we may get another error or warning later. This is ok as it enables
|
||||||
|
# us to have more code paths tested over time.
|
||||||
|
|
||||||
|
CREATE TABLE t1 (a INT);
|
||||||
|
CREATE TRIGGER trg AFTER INSERT ON t1 FOR EACH ROW SET @x = 1;
|
||||||
|
--disable_warnings
|
||||||
|
--connect (con1,localhost,root,,test)
|
||||||
|
--send
|
||||||
|
DROP TRIGGER trg;
|
||||||
|
--connection default
|
||||||
|
--error 0,ER_TRG_DOES_NOT_EXIST
|
||||||
|
DROP TRIGGER trg;
|
||||||
|
# Cleanup
|
||||||
|
--connection con1
|
||||||
|
--error 0,ER_TRG_DOES_NOT_EXIST
|
||||||
|
--reap
|
||||||
|
--disconnect con1
|
||||||
|
--connection default
|
||||||
|
--enable_warnings
|
||||||
|
DROP TABLE t1;
|
||||||
|
@ -7988,3 +7988,5 @@ ER_JSON_TABLE_MULTIPLE_MATCHES
|
|||||||
eng "Can't store multiple matches of the path in the column '%s' of JSON_TABLE '%s'."
|
eng "Can't store multiple matches of the path in the column '%s' of JSON_TABLE '%s'."
|
||||||
ER_WITH_TIES_NEEDS_ORDER
|
ER_WITH_TIES_NEEDS_ORDER
|
||||||
eng "FETCH ... WITH TIES requires ORDER BY clause to be present"
|
eng "FETCH ... WITH TIES requires ORDER BY clause to be present"
|
||||||
|
ER_REMOVED_ORPHAN_TRIGGER
|
||||||
|
eng "Dropped orphan trigger '%-.64s', originally created for table: '%-.192s'"
|
||||||
|
@ -39,6 +39,12 @@
|
|||||||
|
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
|
||||||
|
static bool add_table_for_trigger_internal(THD *thd,
|
||||||
|
const sp_name *trg_name,
|
||||||
|
bool if_exists,
|
||||||
|
TABLE_LIST **table,
|
||||||
|
char *trn_path_buff);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Trigger_creation_ctx -- creation context of triggers.
|
Trigger_creation_ctx -- creation context of triggers.
|
||||||
*/
|
*/
|
||||||
@ -396,6 +402,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||||||
*/
|
*/
|
||||||
TABLE *table;
|
TABLE *table;
|
||||||
bool result= TRUE;
|
bool result= TRUE;
|
||||||
|
bool add_if_exists_to_binlog= 0, action_executed= 0;
|
||||||
String stmt_query;
|
String stmt_query;
|
||||||
bool lock_upgrade_done= FALSE;
|
bool lock_upgrade_done= FALSE;
|
||||||
bool backup_of_table_list_done= 0;;
|
bool backup_of_table_list_done= 0;;
|
||||||
@ -403,6 +410,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||||||
MDL_request mdl_request_for_trn;
|
MDL_request mdl_request_for_trn;
|
||||||
Query_tables_list backup;
|
Query_tables_list backup;
|
||||||
DDL_LOG_STATE ddl_log_state, ddl_log_state_tmp_file;
|
DDL_LOG_STATE ddl_log_state, ddl_log_state_tmp_file;
|
||||||
|
char trn_path_buff[FN_REFLEN];
|
||||||
DBUG_ENTER("mysql_create_or_drop_trigger");
|
DBUG_ENTER("mysql_create_or_drop_trigger");
|
||||||
|
|
||||||
/* Charset of the buffer for statement must be system one. */
|
/* Charset of the buffer for statement must be system one. */
|
||||||
@ -489,7 +497,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (add_table_for_trigger(thd, thd->lex->spname, if_exists, & tables))
|
if (add_table_for_trigger_internal(thd, thd->lex->spname, if_exists, &tables,
|
||||||
|
trn_path_buff))
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
if (!tables)
|
if (!tables)
|
||||||
@ -505,7 +514,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||||||
*/
|
*/
|
||||||
result= FALSE;
|
result= FALSE;
|
||||||
/* Still, we need to log the query ... */
|
/* Still, we need to log the query ... */
|
||||||
stmt_query.append(thd->query(), thd->query_length());
|
stmt_query.set(thd->query(), thd->query_length(), system_charset_info);
|
||||||
|
action_executed= 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -562,7 +572,15 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||||||
tables->table= open_n_lock_single_table(thd, tables,
|
tables->table= open_n_lock_single_table(thd, tables,
|
||||||
TL_READ_NO_INSERT, 0);
|
TL_READ_NO_INSERT, 0);
|
||||||
if (! tables->table)
|
if (! tables->table)
|
||||||
|
{
|
||||||
|
if (!create && thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE)
|
||||||
|
{
|
||||||
|
/* TRN file exists but table does not. Drop the orphan trigger */
|
||||||
|
thd->clear_error(); // Remove error from open
|
||||||
|
goto drop_orphan_trn;
|
||||||
|
}
|
||||||
goto end;
|
goto end;
|
||||||
|
}
|
||||||
tables->table->use_all_columns();
|
tables->table->use_all_columns();
|
||||||
}
|
}
|
||||||
table= tables->table;
|
table= tables->table;
|
||||||
@ -588,11 +606,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||||||
if (!table->triggers)
|
if (!table->triggers)
|
||||||
{
|
{
|
||||||
if (!create)
|
if (!create)
|
||||||
{
|
goto drop_orphan_trn;
|
||||||
my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(table->triggers= new (&table->mem_root) Table_triggers_list(table)))
|
if (!(table->triggers= new (&table->mem_root) Table_triggers_list(table)))
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
@ -618,7 +632,13 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||||||
&thd->lex->spname->m_name,
|
&thd->lex->spname->m_name,
|
||||||
&stmt_query,
|
&stmt_query,
|
||||||
&ddl_log_state);
|
&ddl_log_state);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
thd->clear_error(); // Remove error from drop trigger
|
||||||
|
goto drop_orphan_trn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
action_executed= 1;
|
||||||
|
|
||||||
close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
|
close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
|
||||||
|
|
||||||
@ -637,13 +657,19 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||||||
sp_cache_invalidate();
|
sp_cache_invalidate();
|
||||||
|
|
||||||
end:
|
end:
|
||||||
if (!result)
|
if (!result && action_executed)
|
||||||
{
|
{
|
||||||
|
ulonglong save_option_bits= thd->variables.option_bits;
|
||||||
|
|
||||||
debug_crash_here("ddl_log_drop_before_binlog");
|
debug_crash_here("ddl_log_drop_before_binlog");
|
||||||
|
if (add_if_exists_to_binlog)
|
||||||
|
thd->variables.option_bits|= OPTION_IF_EXISTS;
|
||||||
thd->binlog_xid= thd->query_id;
|
thd->binlog_xid= thd->query_id;
|
||||||
ddl_log_update_xid(&ddl_log_state, thd->binlog_xid);
|
ddl_log_update_xid(&ddl_log_state, thd->binlog_xid);
|
||||||
result= write_bin_log(thd, TRUE, stmt_query.ptr(), stmt_query.length());
|
result= write_bin_log(thd, TRUE, stmt_query.ptr(),
|
||||||
|
stmt_query.length());
|
||||||
thd->binlog_xid= 0;
|
thd->binlog_xid= 0;
|
||||||
|
thd->variables.option_bits= save_option_bits;
|
||||||
debug_crash_here("ddl_log_drop_after_binlog");
|
debug_crash_here("ddl_log_drop_after_binlog");
|
||||||
}
|
}
|
||||||
ddl_log_complete(&ddl_log_state);
|
ddl_log_complete(&ddl_log_state);
|
||||||
@ -679,6 +705,16 @@ end:
|
|||||||
wsrep_error_label:
|
wsrep_error_label:
|
||||||
DBUG_RETURN(true);
|
DBUG_RETURN(true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
drop_orphan_trn:
|
||||||
|
my_error(ER_REMOVED_ORPHAN_TRIGGER, MYF(ME_WARNING),
|
||||||
|
thd->lex->spname->m_name.str, tables->table_name.str);
|
||||||
|
mysql_file_delete(key_file_trg, trn_path_buff, MYF(0));
|
||||||
|
result= thd->is_error();
|
||||||
|
add_if_exists_to_binlog= 1;
|
||||||
|
action_executed= 1; // Ensure query is binlogged
|
||||||
|
stmt_query.set(thd->query(), thd->query_length(), system_charset_info);
|
||||||
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1862,17 +1898,16 @@ void Trigger::get_trigger_info(LEX_CSTRING *trigger_stmt,
|
|||||||
@retval TRUE Otherwise.
|
@retval TRUE Otherwise.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool add_table_for_trigger(THD *thd,
|
static bool add_table_for_trigger_internal(THD *thd,
|
||||||
const sp_name *trg_name,
|
const sp_name *trg_name,
|
||||||
bool if_exists,
|
bool if_exists,
|
||||||
TABLE_LIST **table)
|
TABLE_LIST **table,
|
||||||
|
char *trn_path_buff)
|
||||||
{
|
{
|
||||||
LEX *lex= thd->lex;
|
LEX *lex= thd->lex;
|
||||||
char trn_path_buff[FN_REFLEN];
|
|
||||||
LEX_CSTRING trn_path= { trn_path_buff, 0 };
|
LEX_CSTRING trn_path= { trn_path_buff, 0 };
|
||||||
LEX_CSTRING tbl_name= null_clex_str;
|
LEX_CSTRING tbl_name= null_clex_str;
|
||||||
|
DBUG_ENTER("add_table_for_trigger_internal");
|
||||||
DBUG_ENTER("add_table_for_trigger");
|
|
||||||
|
|
||||||
build_trn_path(thd, trg_name, (LEX_STRING*) &trn_path);
|
build_trn_path(thd, trg_name, (LEX_STRING*) &trn_path);
|
||||||
|
|
||||||
@ -1905,6 +1940,23 @@ bool add_table_for_trigger(THD *thd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Same as above, but with an allocated buffer.
|
||||||
|
This is called by mysql_excute_command() in is here to keep stack
|
||||||
|
space down in the caller.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool add_table_for_trigger(THD *thd,
|
||||||
|
const sp_name *trg_name,
|
||||||
|
bool if_exists,
|
||||||
|
TABLE_LIST **table)
|
||||||
|
{
|
||||||
|
char trn_path_buff[FN_REFLEN];
|
||||||
|
return add_table_for_trigger_internal(thd, trg_name, if_exists,
|
||||||
|
table, trn_path_buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Drop all triggers for table.
|
Drop all triggers for table.
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user