summaryrefslogtreecommitdiff
path: root/sql/sql_parse.cc
diff options
context:
space:
mode:
authorunknown <ingo@mysql.com>2006-06-26 19:14:35 +0200
committerunknown <ingo@mysql.com>2006-06-26 19:14:35 +0200
commit1c2a13b894ce26712316dfe5a62174b433e959f1 (patch)
treef72941273e5da9bb2f5853ff33357f7b0c0a118c /sql/sql_parse.cc
parent41b9884db0db0b3e16562161a86e94a998bcce2d (diff)
downloadmariadb-git-1c2a13b894ce26712316dfe5a62174b433e959f1.tar.gz
Bug#16986 - Deadlock condition with MyISAM tables
Addendum fixes after changing the condition variable for the global read lock. The stress test suite revealed some deadlocks. Some were related to the new condition variable (COND_global_read_lock) and some were general problems with the global read lock. It is now necessary to signal COND_global_read_lock whenever COND_refresh is signalled. We need to wait for the release of a global read lock if one is set before every operation that requires a write lock. But we must not wait if we have locked tables by LOCK TABLES. After setting a global read lock a thread waits until all write locks are released. mysql-test/r/lock_multi.result: Bug#16986 - Deadlock condition with MyISAM tables Addendum fixes after changing the condition variable for the global read lock. Added test results. mysql-test/t/lock_multi.test: Bug#16986 - Deadlock condition with MyISAM tables Addendum fixes after changing the condition variable for the global read lock. Added tests for possible deadlocks that did not occur with the stress test suite. mysys/thr_lock.c: Bug#16986 - Deadlock condition with MyISAM tables Addendum fixes after changing the condition variable for the global read lock. Added a protection against an infinite loop that occurs with the test case for Bug #20662. sql/lock.cc: Bug#16986 - Deadlock condition with MyISAM tables Addendum fixes after changing the condition variable for the global read lock. Signal COND_global_read_lock whenever COND_refresh is signalled by using the new function broadcast_refresh(). Added the definition of a new function that signals COND_global_read_lock whenever COND_refresh is signalled. sql/mysql_priv.h: Bug#16986 - Deadlock condition with MyISAM tables Addendum fixes after changing the condition variable for the global read lock. Added a declaration for a new function that signals COND_global_read_lock whenever COND_refresh is signalled. sql/sql_base.cc: Bug#16986 - Deadlock condition with MyISAM tables Addendum fixes after changing the condition variable for the global read lock. Signal COND_global_read_lock whenever COND_refresh is signalled by using the new function broadcast_refresh(). sql/sql_handler.cc: Bug#16986 - Deadlock condition with MyISAM tables Addendum fixes after changing the condition variable for the global read lock. Signal COND_global_read_lock whenever COND_refresh is signalled by using the new function broadcast_refresh(). sql/sql_insert.cc: Bug#16986 - Deadlock condition with MyISAM tables Addendum fixes after changing the condition variable for the global read lock. Removed global read lock handling from inside of INSERT DELAYED. It is handled on a higher level now. sql/sql_parse.cc: Bug#16986 - Deadlock condition with MyISAM tables Addendum fixes after changing the condition variable for the global read lock. Wait for the release of a global read lock if one is set before every operation that requires a write lock. But don't wait if locked tables exist already. sql/sql_table.cc: Bug#16986 - Deadlock condition with MyISAM tables Addendum fixes after changing the condition variable for the global read lock. Removed global read lock handling from inside of CREATE TABLE. It is handled on a higher level now. Signal COND_global_read_lock whenever COND_refresh is signalled by using the new function broadcast_refresh().
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r--sql/sql_parse.cc102
1 files changed, 83 insertions, 19 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index ba5c2ebf484..169fe219263 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2337,17 +2337,37 @@ static void reset_one_shot_variables(THD *thd)
}
-/****************************************************************************
-** mysql_execute_command
-** Execute command saved in thd and current_lex->sql_command
-****************************************************************************/
+/*
+ Execute command saved in thd and current_lex->sql_command
+
+ SYNOPSIS
+ mysql_execute_command()
+ thd Thread handle
+
+ IMPLEMENTATION
+
+ Before every operation that can request a write lock for a table
+ wait if a global read lock exists. However do not wait if this
+ thread has locked tables already. No new locks can be requested
+ until the other locks are released. The thread that requests the
+ global read lock waits for write locked tables to become unlocked.
+
+ Note that wait_if_global_read_lock() sets a protection against a new
+ global read lock when it succeeds. This needs to be released by
+ start_waiting_global_read_lock() after the operation.
+
+ RETURN
+ FALSE OK
+ TRUE Error
+*/
bool
mysql_execute_command(THD *thd)
{
- bool res= FALSE;
- int result= 0;
- LEX *lex= thd->lex;
+ bool res= FALSE;
+ bool need_start_waiting= FALSE; // have protection against global read lock
+ int result= 0;
+ LEX *lex= thd->lex;
/* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
SELECT_LEX *select_lex= &lex->select_lex;
/* first table of first SELECT_LEX */
@@ -2832,7 +2852,8 @@ mysql_execute_command(THD *thd)
TABLE in the same way. That way we avoid that a new table is
created during a gobal read lock.
*/
- if (wait_if_global_read_lock(thd, 0, 1))
+ if (!thd->locked_tables &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
{
res= 1;
goto end_with_restore_list;
@@ -2857,7 +2878,7 @@ mysql_execute_command(THD *thd)
{
update_non_unique_table_error(create_table, "CREATE", duplicate);
res= 1;
- goto end_with_restart_wait;
+ goto end_with_restore_list;
}
}
/* If we create merge table, we have to test tables in merge, too */
@@ -2873,7 +2894,7 @@ mysql_execute_command(THD *thd)
{
update_non_unique_table_error(tab, "CREATE", duplicate);
res= 1;
- goto end_with_restart_wait;
+ goto end_with_restore_list;
}
}
}
@@ -2915,13 +2936,6 @@ mysql_execute_command(THD *thd)
send_ok(thd);
}
-end_with_restart_wait:
- /*
- Release the protection against the global read lock and wake
- everyone, who might want to set a global read lock.
- */
- start_waiting_global_read_lock(thd);
-
/* put tables back for PS rexecuting */
end_with_restore_list:
lex->link_first_table_back(create_table, link_to_local);
@@ -3039,6 +3053,13 @@ end_with_restore_list:
goto error;
else
{
+ if (!thd->locked_tables &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ {
+ res= 1;
+ break;
+ }
+
thd->enable_slow_log= opt_log_slow_admin_statements;
res= mysql_alter_table(thd, select_lex->db, lex->name,
&lex->create_info,
@@ -3296,6 +3317,14 @@ end_with_restore_list:
break;
/* Skip first table, which is the table we are inserting in */
select_lex->context.table_list= first_table->next_local;
+
+ if (!thd->locked_tables &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ {
+ res= 1;
+ break;
+ }
+
res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
lex->update_list, lex->value_list,
lex->duplicates, lex->ignore);
@@ -3319,6 +3348,14 @@ end_with_restore_list:
select_lex->options|= SELECT_NO_UNLOCK;
unit->set_limit(select_lex);
+
+ if (! thd->locked_tables &&
+ ! (need_start_waiting= ! wait_if_global_read_lock(thd, 0, 1)))
+ {
+ res= 1;
+ break;
+ }
+
if (!(res= open_and_lock_tables(thd, all_tables)))
{
/* Skip first table, which is the table we are inserting in */
@@ -3395,6 +3432,14 @@ end_with_restore_list:
break;
DBUG_ASSERT(select_lex->offset_limit == 0);
unit->set_limit(select_lex);
+
+ if (!thd->locked_tables &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ {
+ res= 1;
+ break;
+ }
+
res = mysql_delete(thd, all_tables, select_lex->where,
&select_lex->order_list,
unit->select_limit_cnt, select_lex->options,
@@ -3408,6 +3453,13 @@ end_with_restore_list:
(TABLE_LIST *)thd->lex->auxilliary_table_list.first;
multi_delete *result;
+ if (!thd->locked_tables &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ {
+ res= 1;
+ break;
+ }
+
if ((res= multi_delete_precheck(thd, all_tables)))
break;
@@ -4974,10 +5026,22 @@ end_with_restore_list:
if (lex->sql_command != SQLCOM_CALL && lex->sql_command != SQLCOM_EXECUTE &&
uc_update_queries[lex->sql_command]<2)
thd->row_count_func= -1;
- DBUG_RETURN(res || thd->net.report_error);
+
+ goto end;
error:
- DBUG_RETURN(1);
+ res= TRUE;
+
+end:
+ if (need_start_waiting)
+ {
+ /*
+ Release the protection against the global read lock and wake
+ everyone, who might want to set a global read lock.
+ */
+ start_waiting_global_read_lock(thd);
+ }
+ DBUG_RETURN(res || thd->net.report_error);
}