1
0
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:
Monty
2021-03-21 17:47:56 +02:00
committed by Sergei Golubchik
parent ffe7f19fa6
commit c844a76b0a
6 changed files with 195 additions and 16 deletions

View File

@ -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.
*/
@ -396,6 +402,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
*/
TABLE *table;
bool result= TRUE;
bool add_if_exists_to_binlog= 0, action_executed= 0;
String stmt_query;
bool lock_upgrade_done= FALSE;
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;
Query_tables_list backup;
DDL_LOG_STATE ddl_log_state, ddl_log_state_tmp_file;
char trn_path_buff[FN_REFLEN];
DBUG_ENTER("mysql_create_or_drop_trigger");
/* 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;
}
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;
if (!tables)
@ -505,7 +514,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
*/
result= FALSE;
/* 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;
}
}
@ -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,
TL_READ_NO_INSERT, 0);
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;
}
tables->table->use_all_columns();
}
table= tables->table;
@ -588,11 +606,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
if (!table->triggers)
{
if (!create)
{
my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
goto end;
}
goto drop_orphan_trn;
if (!(table->triggers= new (&table->mem_root) Table_triggers_list(table)))
goto end;
}
@ -618,7 +632,13 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
&thd->lex->spname->m_name,
&stmt_query,
&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);
@ -637,13 +657,19 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
sp_cache_invalidate();
end:
if (!result)
if (!result && action_executed)
{
ulonglong save_option_bits= thd->variables.option_bits;
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;
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->variables.option_bits= save_option_bits;
debug_crash_here("ddl_log_drop_after_binlog");
}
ddl_log_complete(&ddl_log_state);
@ -679,6 +705,16 @@ end:
wsrep_error_label:
DBUG_RETURN(true);
#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.
*/
bool add_table_for_trigger(THD *thd,
const sp_name *trg_name,
bool if_exists,
TABLE_LIST **table)
static bool add_table_for_trigger_internal(THD *thd,
const sp_name *trg_name,
bool if_exists,
TABLE_LIST **table,
char *trn_path_buff)
{
LEX *lex= thd->lex;
char trn_path_buff[FN_REFLEN];
LEX_CSTRING trn_path= { trn_path_buff, 0 };
LEX_CSTRING tbl_name= null_clex_str;
DBUG_ENTER("add_table_for_trigger");
DBUG_ENTER("add_table_for_trigger_internal");
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.