diff options
author | Davi Arnaut <Davi.Arnaut@Sun.COM> | 2009-04-03 16:11:54 -0300 |
---|---|---|
committer | Davi Arnaut <Davi.Arnaut@Sun.COM> | 2009-04-03 16:11:54 -0300 |
commit | aebaf079d1611532767dd0c9b183ca73a07b6ac9 (patch) | |
tree | b11bced0612f7135d287c919aace0baf06344d69 /sql/sql_parse.cc | |
parent | 668d988c71a7980b3f131feb51c24427cfd78082 (diff) | |
download | mariadb-git-aebaf079d1611532767dd0c9b183ca73a07b6ac9.tar.gz |
Bug#43230: SELECT ... FOR UPDATE can hang with FLUSH TABLES WITH READ LOCK indefinitely
The problem is that a SELECT .. FOR UPDATE statement might open
a table and later wait for a impeding global read lock without
noticing whether it is holding a table that is being waited upon
the the flush phase of the process that took the global read
lock.
The same problem also affected the following statements:
LOCK TABLES .. WRITE
UPDATE .. SET (update and multi-table update)
TRUNCATE TABLE ..
LOAD DATA ..
The solution is to make the above statements wait for a impending
global read lock before opening the tables. If there is no
impending global read lock, the statement raises a temporary
protection against global read locks and progresses smoothly
towards completion.
Important notice: the patch does not try to address all possible
cases, only those which are common and can be fixed unintrusively
enough for 5.0.
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r-- | sql/sql_parse.cc | 26 |
1 files changed, 25 insertions, 1 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ba887486aa1..02030ac2e19 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2800,6 +2800,10 @@ mysql_execute_command(THD *thd) if (res) goto error; + if (!thd->locked_tables && lex->protect_against_global_read_lock && + !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + goto error; + if (!(res= open_and_lock_tables(thd, all_tables))) { if (lex->describe) @@ -3660,6 +3664,9 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); if (update_precheck(thd, all_tables)) break; + if (!thd->locked_tables && + !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + goto error; DBUG_ASSERT(select_lex->offset_limit == 0); unit->set_limit(select_lex); res= (up_result= mysql_update(thd, all_tables, @@ -3686,6 +3693,15 @@ end_with_restore_list: else res= 0; + /* + Protection might have already been risen if its a fall through + from the SQLCOM_UPDATE case above. + */ + if (!thd->locked_tables && + lex->sql_command == SQLCOM_UPDATE_MULTI && + !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + goto error; + res= mysql_multi_update_prepare(thd); #ifdef HAVE_REPLICATION @@ -3853,7 +3869,8 @@ end_with_restore_list: ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); goto error; } - + if (!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + goto error; res= mysql_truncate(thd, first_table, 0); break; case SQLCOM_DELETE: @@ -4027,6 +4044,10 @@ end_with_restore_list: if (check_one_table_access(thd, privilege, all_tables)) goto error; + if (!thd->locked_tables && + !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + goto error; + res= mysql_load(thd, lex->exchange, first_table, lex->field_list, lex->update_list, lex->value_list, lex->duplicates, lex->ignore, (bool) lex->local_file); @@ -4082,6 +4103,9 @@ end_with_restore_list: goto error; if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0)) goto error; + if (lex->protect_against_global_read_lock && + !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + goto error; thd->in_lock_tables=1; thd->options|= OPTION_TABLE_LOCK; |