summaryrefslogtreecommitdiff
path: root/sql/sql_trigger.cc
diff options
context:
space:
mode:
authorunknown <dlenev@mysql.com>2005-10-17 22:37:24 +0400
committerunknown <dlenev@mysql.com>2005-10-17 22:37:24 +0400
commitdd02b98d14393e02442a4e7ad41a8b6c0b429d85 (patch)
tree6ab27efff544719f937826ca792589038dab8878 /sql/sql_trigger.cc
parent63e7824fc2cb5fec43cafbc59d17e5cfcc2195b3 (diff)
downloadmariadb-git-dd02b98d14393e02442a4e7ad41a8b6c0b429d85.tar.gz
Fix for bug #12739 "Deadlock in multithreaded environment during creating/
droping trigger on InnoDB table". Deadlock occured in cases when we were trying to create two triggers for the same InnoDB table concurrently and both threads were able to reach close_cached_table() simultaneously. Bugfix implements new approach to table locking and table cache invalidation during creation/dropping of trigger. No testcase is supplied since bug was repeatable only under high concurrency. sql/mysql_priv.h: reopen_name_locked_table(): Changed function signature to make it more robust against erroneous usage. sql/sql_base.cc: reopen_name_locked_table(): Changed function signature to make it more robust against erroneous usage. Obtaining LOCK_open lock is now resposibility of caller. When searching for the table to open we should not prefer connection's current database over database which was explicitly specified in TABLE_LIST::db member (even if database is not explicitly specified for table in original query TABLE_LIST::db will be set properly at parsing stage). Fixed behavior of function in cases when error occurs during opening of table. sql/sql_table.cc: prepare_for_restore()/prepare_for_repair(): We should not prefer connection's current database over database which was specified in TABLE_LIST::db member (even if database is not explicitly specified for table in original query TABLE_LIST::db will be set properly at parsing stage). Fixed behavior in unlikely case when we are unable to open table which we are restoring/reparing at the end of preparation stage. sql/sql_trigger.cc: mysql_create_or_drop_trigger(): Now instead of opening and locking table, creating trigger, and then trying to invalidate all instances of this table in table cache, we obtain name lock on table first (thus ensuring that no other thread has this table open), open it, create trigger and then close table therefore releasing lock. New approach is more in line with other places where change .frm files (i.e. change table meta-data). With this change we also get rid of deadlock which occured in cases when we were trying to create two triggers for the same InnoDB table concurrently and both threads were able to reach close_cached_table() simultaneously. (Alternative was to forbid to InnoDB downgrade locks for CREATE/DROP TRIGGER statements in one way or another but I think that proposed solution is better long term).
Diffstat (limited to 'sql/sql_trigger.cc')
-rw-r--r--sql/sql_trigger.cc76
1 files changed, 39 insertions, 37 deletions
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index df8de59508d..dbad8dcffb5 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -103,8 +103,7 @@ static TABLE_LIST *add_table_for_trigger(THD *thd, sp_name *trig);
bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
{
TABLE *table;
- bool result= 0;
-
+ bool result= TRUE;
DBUG_ENTER("mysql_create_or_drop_trigger");
/*
@@ -119,9 +118,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
/* We should have only one table in table list. */
DBUG_ASSERT(tables->next_global == 0);
- if (!(table= open_ltable(thd, tables, tables->lock_type)))
- DBUG_RETURN(TRUE);
-
/*
TODO: We should check if user has TRIGGER privilege for table here.
Now we just require SUPER privilege for creating/dropping because
@@ -131,28 +127,24 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
DBUG_RETURN(TRUE);
/*
- We do not allow creation of triggers on temporary tables. We also don't
- allow creation of triggers on views but fulfilment of this restriction
- is guaranteed by open_ltable(). It is better to have this check here
- than do it in Table_triggers_list::create_trigger() and mess with table
- cache.
+ There is no DETERMINISTIC clause for triggers, so can't check it.
+ But a trigger can in theory be used to do nasty things (if it supported
+ DROP for example) so we do the check for privileges. For now there is
+ already a stronger test right above; but when this stronger test will
+ be removed, the test below will hold.
*/
- if (table->s->tmp_table != NO_TMP_TABLE)
+ if (!trust_routine_creators && mysql_bin_log.is_open() &&
+ !(thd->security_ctx->master_access & SUPER_ACL))
{
- my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias);
+ my_error(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER, MYF(0));
DBUG_RETURN(TRUE);
}
- if (!table->triggers)
+ /* We do not allow creation of triggers on temporary tables. */
+ if (create && find_temporary_table(thd, tables->db, tables->table_name))
{
- if (!create)
- {
- my_message(ER_TRG_DOES_NOT_EXIST, ER(ER_TRG_DOES_NOT_EXIST), MYF(0));
- DBUG_RETURN(TRUE);
- }
-
- if (!(table->triggers= new (&table->mem_root) Table_triggers_list(table)))
- DBUG_RETURN(TRUE);
+ my_error(ER_TRG_ON_VIEW_OR_TEMP_TABLE, MYF(0), tables->alias);
+ DBUG_RETURN(TRUE);
}
/*
@@ -161,31 +153,41 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
again until we are done. (Acquiring LOCK_open is not enough because
global read lock is held without helding LOCK_open).
*/
- if (wait_if_global_read_lock(thd, 0, 0))
+ if (wait_if_global_read_lock(thd, 0, 1))
DBUG_RETURN(TRUE);
- /*
- There is no DETERMINISTIC clause for triggers, so can't check it.
- But a trigger can in theory be used to do nasty things (if it supported
- DROP for example) so we do the check for privileges. For now there is
- already a stronger test above (see start of the function); but when this
- stronger test will be removed, the test below will hold.
- */
- if (!trust_routine_creators && mysql_bin_log.is_open() &&
- !(thd->security_ctx->master_access & SUPER_ACL))
+ VOID(pthread_mutex_lock(&LOCK_open));
+
+ if (lock_table_names(thd, tables))
+ goto end;
+
+ /* We also don't allow creation of triggers on views. */
+ tables->required_type= FRMTYPE_TABLE;
+
+ if (reopen_name_locked_table(thd, tables))
{
- my_message(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER,
- ER(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER), MYF(0));
- DBUG_RETURN(TRUE);
+ unlock_table_name(thd, tables);
+ goto end;
+ }
+ table= tables->table;
+
+ if (!table->triggers)
+ {
+ if (!create)
+ {
+ my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
+ goto end;
+ }
+
+ if (!(table->triggers= new (&table->mem_root) Table_triggers_list(table)))
+ goto end;
}
- VOID(pthread_mutex_lock(&LOCK_open));
result= (create ?
table->triggers->create_trigger(thd, tables):
table->triggers->drop_trigger(thd, tables));
- /* It is sensible to invalidate table in any case */
- close_cached_table(thd, table);
+end:
VOID(pthread_mutex_unlock(&LOCK_open));
start_waiting_global_read_lock(thd);