summaryrefslogtreecommitdiff
path: root/sql/sql_trigger.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_trigger.cc')
-rw-r--r--sql/sql_trigger.cc150
1 files changed, 100 insertions, 50 deletions
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 5802d2c811e..250ff859222 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2004, 2012, Oracle and/or its affiliates.
- Copyright (c) 2010, 2018, MariaDB
+ Copyright (c) 2010, 2021, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -30,11 +30,11 @@
#include "sql_table.h" // build_table_filename,
// check_n_cut_mysql50_prefix
#include "sql_db.h" // get_default_db_collation
-#include "sql_acl.h" // *_ACL
#include "sql_handler.h" // mysql_ha_rm_tables
#include "sp_cache.h" // sp_invalidate_cache
#include <mysys_err.h>
#include "debug_sync.h"
+#include "mysql/psi/mysql_sp.h"
/*************************************************************************/
@@ -393,10 +393,12 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
This is a good candidate for a minor refactoring.
*/
TABLE *table;
- bool result= TRUE;
+ bool result= TRUE, refresh_metadata= FALSE;
String stmt_query;
bool lock_upgrade_done= FALSE;
+ bool backup_of_table_list_done= 0;;
MDL_ticket *mdl_ticket= NULL;
+ MDL_request mdl_request_for_trn;
Query_tables_list backup;
DBUG_ENTER("mysql_create_or_drop_trigger");
@@ -440,12 +442,21 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
*/
if (!trust_function_creators &&
(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()) &&
- !(thd->security_ctx->master_access & SUPER_ACL))
+ !(thd->security_ctx->master_access & PRIV_LOG_BIN_TRUSTED_SP_CREATOR))
{
my_error(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER, MYF(0));
DBUG_RETURN(TRUE);
}
+ /* Protect against concurrent create/drop */
+ MDL_REQUEST_INIT(&mdl_request_for_trn, MDL_key::TABLE,
+ create ? tables->db.str : thd->lex->spname->m_db.str,
+ thd->lex->spname->m_name.str,
+ MDL_EXCLUSIVE, MDL_EXPLICIT);
+ if (thd->mdl_context.acquire_lock(&mdl_request_for_trn,
+ thd->variables.lock_wait_timeout))
+ goto end;
+
if (!create)
{
bool if_exists= thd->lex->if_exists();
@@ -454,6 +465,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
Protect the query table list from the temporary and potentially
destructive changes necessary to open the trigger's table.
*/
+ backup_of_table_list_done= 1;
thd->lex->reset_n_backup_query_tables_list(&backup);
/*
Restore Query_tables_list::sql_command, which was
@@ -463,7 +475,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
*/
thd->lex->sql_command= backup.sql_command;
- if (opt_readonly && !(thd->security_ctx->master_access & SUPER_ACL) &&
+ if (opt_readonly &&
+ !(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) &&
!thd->slave_thread)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
@@ -507,9 +520,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
goto end;
}
-#ifdef WITH_WSREP
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, tables);
-#endif
/* We should have only one table in table list. */
DBUG_ASSERT(tables->next_global == 0);
@@ -550,10 +561,20 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
}
table= tables->table;
+#ifdef WITH_WSREP
+ if (WSREP(thd) &&
+ !wsrep_should_replicate_ddl(thd, table->s->db_type()->db_type))
+ goto end;
+#endif
+
/* Later on we will need it to downgrade the lock */
mdl_ticket= table->mdl_ticket;
- if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
+ /*
+ RENAME ensures that table is flushed properly and locked tables will
+ be removed from the active transaction
+ */
+ if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME))
goto end;
lock_upgrade_done= TRUE;
@@ -585,28 +606,34 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
table->triggers->create_trigger(thd, tables, &stmt_query):
table->triggers->drop_trigger(thd, tables, &stmt_query));
- if (result)
- goto end;
-
- close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
- /*
- Reopen the table if we were under LOCK TABLES.
- Ignore the return value for now. It's better to
- keep master/slave in consistent state.
- */
- if (thd->locked_tables_list.reopen_tables(thd, false))
- thd->clear_error();
-
- /*
- Invalidate SP-cache. That's needed because triggers may change list of
- pre-locking tables.
- */
- sp_cache_invalidate();
+ refresh_metadata= TRUE;
end:
if (!result)
result= write_bin_log(thd, TRUE, stmt_query.ptr(), stmt_query.length());
+ if (mdl_request_for_trn.ticket)
+ thd->mdl_context.release_lock(mdl_request_for_trn.ticket);
+
+ if (refresh_metadata)
+ {
+ close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
+
+ /*
+ Reopen the table if we were under LOCK TABLES.
+ Ignore the return value for now. It's better to
+ keep master/slave in consistent state.
+ */
+ if (thd->locked_tables_list.reopen_tables(thd, false))
+ thd->clear_error();
+
+ /*
+ Invalidate SP-cache. That's needed because triggers may change list of
+ pre-locking tables.
+ */
+ sp_cache_invalidate();
+ }
+
/*
If we are under LOCK TABLES we should restore original state of
meta-data locks. Otherwise all locks will be released along
@@ -616,16 +643,24 @@ end:
mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
/* Restore the query table list. Used only for drop trigger. */
- if (!create)
+ if (backup_of_table_list_done)
thd->lex->restore_backup_query_tables_list(&backup);
if (!result)
+ {
my_ok(thd);
+ /* Drop statistics for this stored program from performance schema. */
+ MYSQL_DROP_SP(SP_TYPE_TRIGGER,
+ thd->lex->spname->m_db.str, static_cast<uint>(thd->lex->spname->m_db.length),
+ thd->lex->spname->m_name.str, static_cast<uint>(thd->lex->spname->m_name.length));
+ }
DBUG_RETURN(result);
+
#ifdef WITH_WSREP
wsrep_error_label:
- DBUG_RETURN(true);
+ DBUG_ASSERT(result == 1);
+ goto end;
#endif
}
@@ -1020,10 +1055,10 @@ bool Trigger::add_to_file_list(void* param_arg)
*/
static bool rm_trigger_file(char *path, const LEX_CSTRING *db,
- const LEX_CSTRING *table_name)
+ const LEX_CSTRING *table_name, myf MyFlags)
{
build_table_filename(path, FN_REFLEN-1, db->str, table_name->str, TRG_EXT, 0);
- return mysql_file_delete(key_file_trg, path, MYF(MY_WME));
+ return mysql_file_delete(key_file_trg, path, MyFlags);
}
@@ -1042,10 +1077,11 @@ static bool rm_trigger_file(char *path, const LEX_CSTRING *db,
*/
static bool rm_trigname_file(char *path, const LEX_CSTRING *db,
- const LEX_CSTRING *trigger_name)
+ const LEX_CSTRING *trigger_name, myf MyFlags)
{
- build_table_filename(path, FN_REFLEN - 1, db->str, trigger_name->str, TRN_EXT, 0);
- return mysql_file_delete(key_file_trn, path, MYF(MY_WME));
+ build_table_filename(path, FN_REFLEN - 1, db->str, trigger_name->str,
+ TRN_EXT, 0);
+ return mysql_file_delete(key_file_trn, path, MyFlags);
}
@@ -1163,7 +1199,7 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
parse_file.cc functionality (because we will need it
elsewhere).
*/
- if (rm_trigger_file(path, &tables->db, &tables->table_name))
+ if (rm_trigger_file(path, &tables->db, &tables->table_name, MYF(MY_WME)))
return 1;
}
else
@@ -1172,7 +1208,7 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
return 1;
}
- if (rm_trigname_file(path, &tables->db, sp_name))
+ if (rm_trigname_file(path, &tables->db, sp_name, MYF(MY_WME)))
return 1;
delete trigger;
@@ -1313,13 +1349,14 @@ bool Table_triggers_list::prepare_record_accessors(TABLE *table)
This could be avoided if there is no triggers for UPDATE and DELETE.
@retval
- False success
+ False no triggers or triggers where correctly loaded
@retval
- True error
+ True error (wrong trigger file)
*/
bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db,
- const LEX_CSTRING *table_name, TABLE *table,
+ const LEX_CSTRING *table_name,
+ TABLE *table,
bool names_only)
{
char path_buff[FN_REFLEN];
@@ -1550,6 +1587,10 @@ bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db,
trigger->definer= *trg_definer;
}
+ sp->m_sp_share= MYSQL_GET_SP_SHARE(SP_TYPE_TRIGGER,
+ sp->m_db.str, static_cast<uint>(sp->m_db.length),
+ sp->m_name.str, static_cast<uint>(sp->m_name.length));
+
#ifndef DBUG_OFF
/*
Let us check that we correctly update trigger definitions when we
@@ -1624,7 +1665,7 @@ err_with_lex_cleanup:
}
error:
- if (unlikely(!thd->is_error()))
+ if (unlikely(!thd->is_error()))
{
/*
We don't care about this error message much because .TRG files will
@@ -1801,20 +1842,23 @@ bool add_table_for_trigger(THD *thd,
*/
bool Table_triggers_list::drop_all_triggers(THD *thd, const LEX_CSTRING *db,
- const LEX_CSTRING *name)
+ const LEX_CSTRING *name,
+ myf MyFlags)
{
TABLE table;
char path[FN_REFLEN];
bool result= 0;
- DBUG_ENTER("Triggers::drop_all_triggers");
+ DBUG_ENTER("Table_triggers_list::drop_all_triggers");
table.reset();
- init_sql_alloc(&table.mem_root, "Triggers::drop_all_triggers", 8192, 0,
- MYF(0));
+ init_sql_alloc(key_memory_Table_trigger_dispatcher,
+ &table.mem_root, 8192, 0, MYF(MY_WME));
if (Table_triggers_list::check_n_load(thd, db, name, &table, 1))
{
result= 1;
+ /* We couldn't parse trigger file, best to just remove it */
+ rm_trigger_file(path, db, name, MyFlags);
goto end;
}
if (table.triggers)
@@ -1834,7 +1878,7 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, const LEX_CSTRING *db,
Such triggers have zero-length name and are skipped here.
*/
if (trigger->name.length &&
- rm_trigname_file(path, db, &trigger->name))
+ rm_trigname_file(path, db, &trigger->name, MyFlags))
{
/*
Instead of immediately bailing out with error if we were unable
@@ -1842,10 +1886,13 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, const LEX_CSTRING *db,
*/
result= 1;
}
+ /* Drop statistics for this stored program from performance schema. */
+ MYSQL_DROP_SP(SP_TYPE_TRIGGER, db->str, static_cast<uint>(db->length),
+ trigger->name.str, static_cast<uint>(trigger->name.length));
}
}
}
- if (rm_trigger_file(path, db, name))
+ if (rm_trigger_file(path, db, name, MyFlags))
result= 1;
delete table.triggers;
}
@@ -1906,9 +1953,10 @@ change_table_name_in_triggers(THD *thd,
if (save_trigger_file(thd, new_db_name, new_table_name))
return TRUE;
- if (rm_trigger_file(path_buff, old_db_name, old_table_name))
+ if (rm_trigger_file(path_buff, old_db_name, old_table_name, MYF(MY_WME)))
{
- (void) rm_trigger_file(path_buff, new_db_name, new_table_name);
+ (void) rm_trigger_file(path_buff, new_db_name, new_table_name,
+ MYF(MY_WME));
return TRUE;
}
return FALSE;
@@ -2017,9 +2065,11 @@ bool Trigger::change_on_table_name(void* param_arg)
/* Remove stale .TRN file in case of database upgrade */
if (param->old_db_name)
{
- if (rm_trigname_file(trigname_buff, param->old_db_name, &name))
+ if (rm_trigname_file(trigname_buff, param->old_db_name, &name,
+ MYF(MY_WME)))
{
- (void) rm_trigname_file(trigname_buff, param->new_db_name, &name);
+ (void) rm_trigname_file(trigname_buff, param->new_db_name, &name,
+ MYF(MY_WME));
return 1;
}
}
@@ -2061,8 +2111,8 @@ bool Table_triggers_list::change_table_name(THD *thd, const LEX_CSTRING *db,
DBUG_ENTER("Triggers::change_table_name");
table.reset();
- init_sql_alloc(&table.mem_root, "Triggers::change_table_name", 8192, 0,
- MYF(0));
+ init_sql_alloc(key_memory_Table_trigger_dispatcher,
+ &table.mem_root, 8192, 0, MYF(0));
/*
This method interfaces the mysql server code protected by