Commit 7116431a authored by Konstantin Osipov's avatar Konstantin Osipov

A review comment for the fix for Bug#46672.

Remove unnecessary need_reopen loops.
parent ea70b6a2
...@@ -1678,14 +1678,21 @@ insert into t2 values (1);; ...@@ -1678,14 +1678,21 @@ insert into t2 values (1);;
# #
# Switching to connection 'handler_con1'. # Switching to connection 'handler_con1'.
# Wait until INSERT is blocked on table-level lock. # Wait until INSERT is blocked on table-level lock.
# The below statement should not cause deadlock. # Sending 'alter table t1 drop column j'. It should not cause
# deadlock.
alter table t1 drop column j; alter table t1 drop column j;
unlock tables; # Switching to connection 'handler_con2'.
# Wait until ALTER is blocked during upgrade.
# #
# Switching to connection 'default'. # Switching to connection 'default'.
# Reap INSERT. # Reap INSERT.
ERROR HY000: Wait on a lock was aborted due to a pending exclusive lock
handler t1 close; handler t1 close;
# #
# Switching to connection 'handler_con1'.
# Reaping 'alter table t1 drop column j'
unlock tables;
# Switching to connection 'default'.
# Then, check the scenario in which upgrade of SNRW lock to X # Then, check the scenario in which upgrade of SNRW lock to X
# lock is blocked by HANDLER which is open in connection currently # lock is blocked by HANDLER which is open in connection currently
# waiting to get SW lock on the same table. # waiting to get SW lock on the same table.
...@@ -2248,6 +2255,8 @@ SET DEBUG_SYNC= 'RESET'; ...@@ -2248,6 +2255,8 @@ SET DEBUG_SYNC= 'RESET';
# Bug#50786 Assertion `thd->mdl_context.trans_sentinel() == __null' # Bug#50786 Assertion `thd->mdl_context.trans_sentinel() == __null'
# failed in open_ltable() # failed in open_ltable()
# #
# Supress warnings written to the log file
call mtr.add_suppression("Wait on a lock was aborted due to a pending exclusive lock");
DROP TABLE IF EXISTS t1, t2; DROP TABLE IF EXISTS t1, t2;
CREATE TABLE t1 (i INT); CREATE TABLE t1 (i INT);
CREATE TABLE t2 (i INT); CREATE TABLE t2 (i INT);
...@@ -2271,7 +2280,6 @@ SET DEBUG_SYNC= 'now WAIT_FOR parked'; ...@@ -2271,7 +2280,6 @@ SET DEBUG_SYNC= 'now WAIT_FOR parked';
# Sending: # Sending:
SELECT 1; SELECT 1;
# connection: con3 # connection: con3
# Sending:
ALTER TABLE t1 ADD COLUMN j INT; ALTER TABLE t1 ADD COLUMN j INT;
# connection: default # connection: default
SET DEBUG_SYNC= 'now SIGNAL go'; SET DEBUG_SYNC= 'now SIGNAL go';
...@@ -2284,8 +2292,6 @@ HANDLER t1 CLOSE; ...@@ -2284,8 +2292,6 @@ HANDLER t1 CLOSE;
# Reaping SELECT 1 # Reaping SELECT 1
1 1
1 1
# connection: con3
# Reaping ALTER TABLE t1 ADD COLUMN j INT
# connection: default # connection: default
DROP TABLE t1, t2; DROP TABLE t1, t2;
SET DEBUG_SYNC= 'RESET'; SET DEBUG_SYNC= 'RESET';
......
...@@ -2277,17 +2277,32 @@ let $wait_condition= ...@@ -2277,17 +2277,32 @@ let $wait_condition=
select count(*) = 1 from information_schema.processlist select count(*) = 1 from information_schema.processlist
where state = "Table lock" and info = "insert into t2 values (1)"; where state = "Table lock" and info = "insert into t2 values (1)";
--source include/wait_condition.inc --source include/wait_condition.inc
--echo # The below statement should not cause deadlock. --echo # Sending 'alter table t1 drop column j'. It should not cause
alter table t1 drop column j; --echo # deadlock.
unlock tables; send alter table t1 drop column j;
--echo # Switching to connection 'handler_con2'.
connection handler_con2;
--echo # Wait until ALTER is blocked during upgrade.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table" and info = "alter table t1 drop column j";
--source include/wait_condition.inc
--echo # --echo #
--echo # Switching to connection 'default'. --echo # Switching to connection 'default'.
connection default; connection default;
--echo # Reap INSERT. --echo # Reap INSERT.
--error ER_LOCK_ABORTED
--reap --reap
handler t1 close; handler t1 close;
--echo # --echo #
--echo # Switching to connection 'handler_con1'.
connection handler_con1;
--echo # Reaping 'alter table t1 drop column j'
--reap
unlock tables;
--echo # Switching to connection 'default'.
connection default;
--echo # Then, check the scenario in which upgrade of SNRW lock to X --echo # Then, check the scenario in which upgrade of SNRW lock to X
--echo # lock is blocked by HANDLER which is open in connection currently --echo # lock is blocked by HANDLER which is open in connection currently
--echo # waiting to get SW lock on the same table. --echo # waiting to get SW lock on the same table.
...@@ -3220,6 +3235,8 @@ disconnect con2; ...@@ -3220,6 +3235,8 @@ disconnect con2;
--echo # failed in open_ltable() --echo # failed in open_ltable()
--echo # --echo #
--echo # Supress warnings written to the log file
call mtr.add_suppression("Wait on a lock was aborted due to a pending exclusive lock");
--disable_warnings --disable_warnings
DROP TABLE IF EXISTS t1, t2; DROP TABLE IF EXISTS t1, t2;
--enable_warnings --enable_warnings
...@@ -3279,16 +3296,10 @@ let $wait_condition= ...@@ -3279,16 +3296,10 @@ let $wait_condition=
# since the latter waits on a table-level lock while having a HANDLER # since the latter waits on a table-level lock while having a HANDLER
# open. This will cause mysql_lock_tables() in con1 fail which before # open. This will cause mysql_lock_tables() in con1 fail which before
# triggered the assert. # triggered the assert.
--echo # Sending: ALTER TABLE t1 ADD COLUMN j INT;
--send ALTER TABLE t1 ADD COLUMN j INT
--echo # connection: default --echo # connection: default
connection default; connection default;
let $wait_condition=
SELECT COUNT(*) = 1 FROM information_schema.processlist
WHERE state = "Waiting for table"
AND info = "ALTER TABLE t1 ADD COLUMN j INT";
--source include/wait_condition.inc
SET DEBUG_SYNC= 'now SIGNAL go'; SET DEBUG_SYNC= 'now SIGNAL go';
--echo # connection: con1 --echo # connection: con1
...@@ -3302,11 +3313,6 @@ connection con2; ...@@ -3302,11 +3313,6 @@ connection con2;
--echo # Reaping SELECT 1 --echo # Reaping SELECT 1
--reap --reap
--echo # connection: con3
connection con3;
--echo # Reaping ALTER TABLE t1 ADD COLUMN j INT
--reap
--echo # connection: default --echo # connection: default
connection default; connection default;
DROP TABLE t1, t2; DROP TABLE t1, t2;
......
...@@ -2346,7 +2346,6 @@ static int open_ndb_binlog_index(THD *thd, TABLE **ndb_binlog_index) ...@@ -2346,7 +2346,6 @@ static int open_ndb_binlog_index(THD *thd, TABLE **ndb_binlog_index)
thd->proc_info= "Opening " NDB_REP_DB "." NDB_REP_TABLE; thd->proc_info= "Opening " NDB_REP_DB "." NDB_REP_TABLE;
tables->required_type= FRMTYPE_TABLE; tables->required_type= FRMTYPE_TABLE;
uint counter;
thd->clear_error(); thd->clear_error();
if (open_and_lock_tables(thd, tables, FALSE, 0)) if (open_and_lock_tables(thd, tables, FALSE, 0))
{ {
...@@ -2374,7 +2373,6 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row) ...@@ -2374,7 +2373,6 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row)
{ {
ndb_binlog_index_row &row= *(ndb_binlog_index_row *) _row; ndb_binlog_index_row &row= *(ndb_binlog_index_row *) _row;
int error= 0; int error= 0;
bool need_reopen;
/* /*
Turn of binlogging to prevent the table changes to be written to Turn of binlogging to prevent the table changes to be written to
the binary log. the binary log.
......
...@@ -97,7 +97,7 @@ static void print_lock_error(int error, const char *); ...@@ -97,7 +97,7 @@ static void print_lock_error(int error, const char *);
/* Map the return value of thr_lock to an error from errmsg.txt */ /* Map the return value of thr_lock to an error from errmsg.txt */
static int thr_lock_errno_to_mysql[]= static int thr_lock_errno_to_mysql[]=
{ 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK }; { 0, ER_LOCK_ABORTED, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK };
/** /**
Perform semantic checks for mysql_lock_tables. Perform semantic checks for mysql_lock_tables.
...@@ -108,8 +108,7 @@ static int thr_lock_errno_to_mysql[]= ...@@ -108,8 +108,7 @@ static int thr_lock_errno_to_mysql[]=
@return 0 if all the check passed, non zero if a check failed. @return 0 if all the check passed, non zero if a check failed.
*/ */
static int static int
lock_tables_check(THD *thd, TABLE **tables, uint count, lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
bool *write_lock_used, uint flags)
{ {
uint system_count, i; uint system_count, i;
bool is_superuser, log_table_write_query; bool is_superuser, log_table_write_query;
...@@ -117,7 +116,6 @@ lock_tables_check(THD *thd, TABLE **tables, uint count, ...@@ -117,7 +116,6 @@ lock_tables_check(THD *thd, TABLE **tables, uint count,
DBUG_ENTER("lock_tables_check"); DBUG_ENTER("lock_tables_check");
system_count= 0; system_count= 0;
*write_lock_used= FALSE;
is_superuser= thd->security_ctx->master_access & SUPER_ACL; is_superuser= thd->security_ctx->master_access & SUPER_ACL;
log_table_write_query= (is_log_table_write_query(thd->lex->sql_command) log_table_write_query= (is_log_table_write_query(thd->lex->sql_command)
|| ((flags & MYSQL_LOCK_PERF_SCHEMA) != 0)); || ((flags & MYSQL_LOCK_PERF_SCHEMA) != 0));
...@@ -153,8 +151,6 @@ lock_tables_check(THD *thd, TABLE **tables, uint count, ...@@ -153,8 +151,6 @@ lock_tables_check(THD *thd, TABLE **tables, uint count,
if (t->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE) if (t->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE)
{ {
*write_lock_used= TRUE;
if (t->s->table_category == TABLE_CATEGORY_SYSTEM) if (t->s->table_category == TABLE_CATEGORY_SYSTEM)
system_count++; system_count++;
...@@ -273,12 +269,8 @@ static void reset_lock_data_and_free(MYSQL_LOCK **mysql_lock) ...@@ -273,12 +269,8 @@ static void reset_lock_data_and_free(MYSQL_LOCK **mysql_lock)
@param tables An array of pointers to the tables to lock. @param tables An array of pointers to the tables to lock.
@param count The number of tables to lock. @param count The number of tables to lock.
@param flags Options: @param flags Options:
MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock
MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY Ignore SET GLOBAL READ_ONLY MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY Ignore SET GLOBAL READ_ONLY
MYSQL_LOCK_IGNORE_FLUSH Ignore a flush tables.
MYSQL_LOCK_IGNORE_TIMEOUT Use maximum timeout value. MYSQL_LOCK_IGNORE_TIMEOUT Use maximum timeout value.
@param need_reopen Out parameter, TRUE if some tables were altered
or deleted and should be reopened by caller.
@note Caller of this function should always be ready to handle request to @note Caller of this function should always be ready to handle request to
reopen table unless there are external invariants which guarantee reopen table unless there are external invariants which guarantee
...@@ -289,125 +281,63 @@ static void reset_lock_data_and_free(MYSQL_LOCK **mysql_lock) ...@@ -289,125 +281,63 @@ static void reset_lock_data_and_free(MYSQL_LOCK **mysql_lock)
@retval NULL on error or if some tables should be reopen. @retval NULL on error or if some tables should be reopen.
*/ */
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
uint flags, bool *need_reopen)
{ {
int rc; int rc;
MYSQL_LOCK *sql_lock; MYSQL_LOCK *sql_lock;
bool write_lock_used;
DBUG_ENTER("mysql_lock_tables"); DBUG_ENTER("mysql_lock_tables");
*need_reopen= FALSE; if (lock_tables_check(thd, tables, count, flags))
if (lock_tables_check(thd, tables, count, &write_lock_used, flags))
DBUG_RETURN (NULL); DBUG_RETURN (NULL);
ulong timeout= (flags & MYSQL_LOCK_IGNORE_TIMEOUT) ? ulong timeout= (flags & MYSQL_LOCK_IGNORE_TIMEOUT) ?
LONG_TIMEOUT : thd->variables.lock_wait_timeout; LONG_TIMEOUT : thd->variables.lock_wait_timeout;
for (;;) if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS)))
{ DBUG_RETURN(NULL);
if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS)))
break;
if (global_read_lock && write_lock_used &&
! (flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK))
{
/*
Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
Wait until the lock is gone
*/
if (thd->global_read_lock.wait_if_global_read_lock(thd, 1, 1))
{
/* Clear the lock type of all lock data to avoid reusage. */
reset_lock_data_and_free(&sql_lock);
break;
}
if (thd->version != refresh_version)
{
/* Clear the lock type of all lock data to avoid reusage. */
reset_lock_data_and_free(&sql_lock);
goto retry;
}
}
thd_proc_info(thd, "System lock");
DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
if (sql_lock->table_count && lock_external(thd, sql_lock->table,
sql_lock->table_count))
{
/* Clear the lock type of all lock data to avoid reusage. */
reset_lock_data_and_free(&sql_lock);
break;
}
DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
/* Copy the lock data array. thr_multi_lock() reorders its contens. */
memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
sql_lock->lock_count * sizeof(*sql_lock->locks));
/* Lock on the copied half of the lock data array. */
rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks +
sql_lock->lock_count,
sql_lock->lock_count,
thd->lock_id, timeout)];
if (rc > 1) /* a timeout or a deadlock */
{
if (sql_lock->table_count)
(void) unlock_external(thd, sql_lock->table, sql_lock->table_count);
reset_lock_data_and_free(&sql_lock);
my_error(rc, MYF(0));
break;
}
else if (rc == 1) /* aborted or killed */
{
/*
reset_lock_data is required here. If thr_multi_lock fails it
resets lock type for tables, which were locked before (and
including) one that caused error. Lock type for other tables
preserved.
*/
reset_lock_data(sql_lock);
sql_lock->lock_count= 0; // Locks are already freed
// Fall through: unlock, reset lock data, free and retry
}
else
{
/* Success */
break;
}
thd_proc_info(thd, 0);
/* going to retry, unlock all tables */
if (sql_lock->lock_count)
thr_multi_unlock(sql_lock->locks, sql_lock->lock_count);
thd_proc_info(thd, "System lock");
DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
if (sql_lock->table_count && lock_external(thd, sql_lock->table,
sql_lock->table_count))
{
/* Clear the lock type of all lock data to avoid reusage. */
reset_lock_data_and_free(&sql_lock);
goto end;
}
DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
/* Copy the lock data array. thr_multi_lock() reorders its contens. */
memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
sql_lock->lock_count * sizeof(*sql_lock->locks));
/* Lock on the copied half of the lock data array. */
rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks +
sql_lock->lock_count,
sql_lock->lock_count,
thd->lock_id, timeout)];
if (rc)
{
if (sql_lock->table_count) if (sql_lock->table_count)
(void) unlock_external(thd, sql_lock->table, sql_lock->table_count); (void) unlock_external(thd, sql_lock->table, sql_lock->table_count);
/*
If thr_multi_lock fails it resets lock type for tables, which
were locked before (and including) one that caused error. Lock
type for other tables preserved.
*/
reset_lock_data_and_free(&sql_lock); reset_lock_data_and_free(&sql_lock);
retry: if (! thd->killed)
/* Let upper level close all used tables and retry or give error. */ my_error(rc, MYF(0));
*need_reopen= TRUE;
break;
} }
end:
thd_proc_info(thd, 0); thd_proc_info(thd, 0);
if (thd->killed) if (thd->killed)
{ {
thd->send_kill_message(); thd->send_kill_message();
if (sql_lock) if (sql_lock)
{ {
mysql_unlock_tables(thd,sql_lock); mysql_unlock_tables(thd, sql_lock);
sql_lock=0; sql_lock= 0;
} }
} }
thd->set_time_after_lock(); thd->set_time_after_lock();
DBUG_RETURN (sql_lock); DBUG_RETURN(sql_lock);
} }
......
...@@ -1455,8 +1455,6 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) ...@@ -1455,8 +1455,6 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
*/ */
if (!thd->lock) if (!thd->lock)
{ {
bool need_reopen= 1; /* To execute the first lap of the loop below */
/* /*
lock_tables() reads the contents of thd->lex, so they must be lock_tables() reads the contents of thd->lex, so they must be
initialized. Contrary to in initialized. Contrary to in
...@@ -1465,80 +1463,31 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli) ...@@ -1465,80 +1463,31 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
*/ */
lex_start(thd); lex_start(thd);
while ((error= lock_tables(thd, rli->tables_to_lock, if ((error= lock_tables(thd, rli->tables_to_lock,
rli->tables_to_lock_count, 0, rli->tables_to_lock_count, 0)))
&need_reopen)))
{ {
if (!need_reopen) if (thd->is_slave_error || thd->is_fatal_error)
{
if (thd->is_slave_error || thd->is_fatal_error)
{
/*
Error reporting borrowed from Query_log_event with many excessive
simplifications (we don't honour --slave-skip-errors)
*/
uint actual_error= thd->net.last_errno;
rli->report(ERROR_LEVEL, actual_error,
"Error '%s' in %s event: when locking tables",
(actual_error ? thd->net.last_error :
"unexpected success or fatal error"),
get_type_str());
thd->is_fatal_error= 1;
}
else
{
rli->report(ERROR_LEVEL, error,
"Error in %s event: when locking tables",
get_type_str());
}
const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
DBUG_RETURN(error);
}
/*
So we need to reopen the tables.
We need to flush the pending RBR event, since it keeps a
pointer to an open table.
ALTERNATIVE SOLUTION (not implemented): Extract a pointer to
the pending RBR event and reset the table pointer after the
tables has been reopened.
NOTE: For this new scheme there should be no pending event:
need to add code to assert that is the case.
*/
error= thd->binlog_flush_pending_rows_event(FALSE);
if (error)
{ {
rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, /*
ER(ER_SLAVE_FATAL_ERROR), Error reporting borrowed from Query_log_event with many excessive
"call to binlog_flush_pending_rows_event() failed"); simplifications (we don't honour --slave-skip-errors)
thd->is_slave_error= 1; */
DBUG_RETURN(error); uint actual_error= thd->net.last_errno;
rli->report(ERROR_LEVEL, actual_error,
"Error '%s' in %s event: when locking tables",
(actual_error ? thd->net.last_error :
"unexpected success or fatal error"),
get_type_str());
thd->is_fatal_error= 1;
} }
TABLE_LIST *tables= rli->tables_to_lock; else
close_tables_for_reopen(thd, &tables, NULL);
uint tables_count= rli->tables_to_lock_count;
if ((error= open_tables(thd, &tables, &tables_count, 0)))
{ {
if (thd->is_slave_error || thd->is_fatal_error) rli->report(ERROR_LEVEL, error,
{ "Error in %s event: when locking tables",
/* get_type_str());
Error reporting borrowed from Query_log_event with many excessive
simplifications (we don't honour --slave-skip-errors)
*/
uint actual_error= thd->net.last_errno;
rli->report(ERROR_LEVEL, actual_error,
"Error '%s' on reopening tables",
(actual_error ? thd->net.last_error :
"unexpected success or fatal error"));
thd->is_slave_error= 1;
}
const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
DBUG_RETURN(error);
} }
const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
DBUG_RETURN(error);
} }
/* /*
......
...@@ -1590,8 +1590,7 @@ inline bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, ...@@ -1590,8 +1590,7 @@ inline bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
thr_lock_type lock_type, uint flags); thr_lock_type lock_type, uint flags);
bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags); bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags);
bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags, bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags);
bool *need_reopen);
TABLE *open_temporary_table(THD *thd, const char *path, const char *db, TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
const char *table_name, bool link_in_list); const char *table_name, bool link_in_list);
bool rm_temporary_table(handlerton *base, char *path); bool rm_temporary_table(handlerton *base, char *path);
...@@ -2145,8 +2144,7 @@ extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher, ...@@ -2145,8 +2144,7 @@ extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher,
extern struct st_VioSSLFd * ssl_acceptor_fd; extern struct st_VioSSLFd * ssl_acceptor_fd;
#endif /* HAVE_OPENSSL */ #endif /* HAVE_OPENSSL */
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, uint flags);
uint flags, bool *need_reopen);
/* mysql_lock_tables() and open_table() flags bits */ /* mysql_lock_tables() and open_table() flags bits */
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK 0x0001 #define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK 0x0001
#define MYSQL_LOCK_IGNORE_FLUSH 0x0002 #define MYSQL_LOCK_IGNORE_FLUSH 0x0002
......
...@@ -6321,3 +6321,6 @@ ER_SPATIAL_MUST_HAVE_GEOM_COL 42000 ...@@ -6321,3 +6321,6 @@ ER_SPATIAL_MUST_HAVE_GEOM_COL 42000
ER_TOO_LONG_INDEX_COMMENT ER_TOO_LONG_INDEX_COMMENT
eng "Comment for index '%-.64s' is too long (max = %lu)" eng "Comment for index '%-.64s' is too long (max = %lu)"
ER_LOCK_ABORTED
eng "Wait on a lock was aborted due to a pending exclusive lock"
...@@ -2479,6 +2479,31 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, ...@@ -2479,6 +2479,31 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
key_length= (create_table_def_key(thd, key, table_list, 1) - key_length= (create_table_def_key(thd, key, table_list, 1) -
TMP_TABLE_KEY_EXTRA); TMP_TABLE_KEY_EXTRA);
/*
We need this to work for all tables, including temporary tables,
for backwards compatibility. But not under LOCK TABLES,
since under LOCK TABLES one can't use a non-prelocked table.
This works for DO/SELECT f1() statements.
@todo: what about tmp tables used under LOCK TABLES? We used to
allow them if mysql_lock_tables() IS NOT called for them?
*/
if (global_read_lock && table_list->lock_type >= TL_WRITE_ALLOW_WRITE &&
! (flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK) &&
! thd->locked_tables_mode)
{
/*
Someone has issued FLUSH TABLES WITH READ LOCK and we want
a write lock. Wait until the lock is gone.
*/
if (thd->global_read_lock.wait_if_global_read_lock(thd, 1, 1))
DBUG_RETURN(TRUE);
if (thd->version != refresh_version)
{
(void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC);
DBUG_RETURN(TRUE);
}
}
/* /*
Unless requested otherwise, try to resolve this table in the list Unless requested otherwise, try to resolve this table in the list
of temporary tables of this thread. In MySQL temporary tables of temporary tables of this thread. In MySQL temporary tables
...@@ -3293,7 +3318,6 @@ bool ...@@ -3293,7 +3318,6 @@ bool
Locked_tables_list::reopen_tables(THD *thd) Locked_tables_list::reopen_tables(THD *thd)
{ {
Open_table_context ot_ctx_unused(thd, LONG_TIMEOUT); Open_table_context ot_ctx_unused(thd, LONG_TIMEOUT);
bool lt_refresh_unused;
size_t reopen_count= 0; size_t reopen_count= 0;
MYSQL_LOCK *lock; MYSQL_LOCK *lock;
MYSQL_LOCK *merged_lock; MYSQL_LOCK *merged_lock;
...@@ -3333,7 +3357,7 @@ Locked_tables_list::reopen_tables(THD *thd) ...@@ -3333,7 +3357,7 @@ Locked_tables_list::reopen_tables(THD *thd)
break something else. break something else.
*/ */
lock= mysql_lock_tables(thd, m_reopen_array, reopen_count, lock= mysql_lock_tables(thd, m_reopen_array, reopen_count,
MYSQL_OPEN_REOPEN, &lt_refresh_unused); MYSQL_OPEN_REOPEN);
thd->in_lock_tables= 0; thd->in_lock_tables= 0;
if (lock == NULL || (merged_lock= if (lock == NULL || (merged_lock=
mysql_lock_merge(thd->lock, lock)) == NULL) mysql_lock_merge(thd->lock, lock)) == NULL)
...@@ -5061,7 +5085,6 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, ...@@ -5061,7 +5085,6 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
TABLE *table; TABLE *table;
Open_table_context ot_ctx(thd, (lock_flags & MYSQL_LOCK_IGNORE_TIMEOUT) ? Open_table_context ot_ctx(thd, (lock_flags & MYSQL_LOCK_IGNORE_TIMEOUT) ?
LONG_TIMEOUT : thd->variables.lock_wait_timeout); LONG_TIMEOUT : thd->variables.lock_wait_timeout);
bool refresh;
bool error; bool error;
DBUG_ENTER("open_ltable"); DBUG_ENTER("open_ltable");
...@@ -5073,8 +5096,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, ...@@ -5073,8 +5096,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
/* open_ltable can be used only for BASIC TABLEs */ /* open_ltable can be used only for BASIC TABLEs */
table_list->required_type= FRMTYPE_TABLE; table_list->required_type= FRMTYPE_TABLE;
retry: while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx, lock_flags)) &&
while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx, 0)) &&
ot_ctx.can_recover_from_failed_open()) ot_ctx.can_recover_from_failed_open())
{ {
/* /*
...@@ -5120,18 +5142,9 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, ...@@ -5120,18 +5142,9 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
DBUG_ASSERT(thd->lock == 0); // You must lock everything at once DBUG_ASSERT(thd->lock == 0); // You must lock everything at once
if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK) if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK)
if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1, if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1,
lock_flags, &refresh))) lock_flags)))
{ {
if (refresh) table= 0;
{
close_thread_tables(thd);
table_list->table= NULL;
table_list->mdl_request.ticket= NULL;
thd->mdl_context.rollback_to_savepoint(ot_ctx.start_of_statement_svp());
goto retry;
}
else
table= 0;
} }
} }
} }
...@@ -5168,7 +5181,6 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, ...@@ -5168,7 +5181,6 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
Prelocking_strategy *prelocking_strategy) Prelocking_strategy *prelocking_strategy)
{ {
uint counter; uint counter;
bool need_reopen;
/* /*
Remember the set of metadata locks which this connection Remember the set of metadata locks which this connection
managed to acquire before the start of the current statement. managed to acquire before the start of the current statement.
...@@ -5182,28 +5194,24 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables, ...@@ -5182,28 +5194,24 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
DBUG_ENTER("open_and_lock_tables"); DBUG_ENTER("open_and_lock_tables");
DBUG_PRINT("enter", ("derived handling: %d", derived)); DBUG_PRINT("enter", ("derived handling: %d", derived));
for ( ; ; ) if (open_tables(thd, &tables, &counter, flags, prelocking_strategy))
{ DBUG_RETURN(TRUE);
if (open_tables(thd, &tables, &counter, flags, prelocking_strategy))
DBUG_RETURN(TRUE); DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", {
DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", { const char *old_proc_info= thd->proc_info;
const char *old_proc_info= thd->proc_info; thd->proc_info= "DBUG sleep";
thd->proc_info= "DBUG sleep"; my_sleep(6000000);
my_sleep(6000000); thd->proc_info= old_proc_info;});
thd->proc_info= old_proc_info;});
if (lock_tables(thd, tables, counter, flags))
if (!lock_tables(thd, tables, counter, flags, DBUG_RETURN(TRUE);
&need_reopen))
break;
if (!need_reopen)
DBUG_RETURN(TRUE);
close_tables_for_reopen(thd, &tables, start_of_statement_svp);
}
if (derived && if (derived &&
(mysql_handle_derived(thd->lex, &mysql_derived_prepare) || (mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
(thd->fill_derived_tables() && (thd->fill_derived_tables() &&
mysql_handle_derived(thd->lex, &mysql_derived_filling)))) mysql_handle_derived(thd->lex, &mysql_derived_filling))))
DBUG_RETURN(TRUE); /* purecov: inspected */ DBUG_RETURN(TRUE); /* purecov: inspected */
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
} }
...@@ -5261,37 +5269,28 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table) ...@@ -5261,37 +5269,28 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table)
} }
/* /**
Lock all tables in list Lock all tables in a list.
SYNOPSIS @param thd Thread handler
lock_tables() @param tables Tables to lock
thd Thread handler @param count Number of opened tables
tables Tables to lock @param flags Options (see mysql_lock_tables() for details)
count Number of opened tables
flags Options (see mysql_lock_tables() for details)
need_reopen Out parameter which if TRUE indicates that some
tables were dropped or altered during this call
and therefore invoker should reopen tables and
try to lock them once again (in this case
lock_tables() will also return error).
NOTES You can't call lock_tables() while holding thr_lock locks, as
You can't call lock_tables twice, as this would break the dead-lock-free this would break the dead-lock-free handling thr_lock gives us.
handling thr_lock gives us. You most always get all needed locks at You most always get all needed locks at once.
once.
If query for which we are calling this function marked as requiring If the query for which we are calling this function is marked as
prelocking, this function will change locked_tables_mode to requiring prelocking, this function will change
LTM_PRELOCKED. locked_tables_mode to LTM_PRELOCKED.
RETURN VALUES @retval FALSE Success.
0 ok @retval TRUE A lock wait timeout, deadlock or out of memory.
-1 Error
*/ */
bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
uint flags, bool *need_reopen) uint flags)
{ {
TABLE_LIST *table; TABLE_LIST *table;
...@@ -5302,7 +5301,6 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, ...@@ -5302,7 +5301,6 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
*/ */
DBUG_ASSERT(thd->locked_tables_mode <= LTM_LOCK_TABLES || DBUG_ASSERT(thd->locked_tables_mode <= LTM_LOCK_TABLES ||
!thd->lex->requires_prelocking()); !thd->lex->requires_prelocking());
*need_reopen= FALSE;
if (!tables && !thd->lex->requires_prelocking()) if (!tables && !thd->lex->requires_prelocking())
DBUG_RETURN(thd->decide_logging_format(tables)); DBUG_RETURN(thd->decide_logging_format(tables));
...@@ -5347,7 +5345,7 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, ...@@ -5347,7 +5345,7 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
DEBUG_SYNC(thd, "before_lock_tables_takes_lock"); DEBUG_SYNC(thd, "before_lock_tables_takes_lock");
if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start), if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start),
flags, need_reopen))) flags)))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
DEBUG_SYNC(thd, "after_lock_tables_takes_lock"); DEBUG_SYNC(thd, "after_lock_tables_takes_lock");
...@@ -8989,8 +8987,7 @@ open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup) ...@@ -8989,8 +8987,7 @@ open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup)
open tables cannot be accepted when restoring the open tables open tables cannot be accepted when restoring the open tables
state. state.
*/ */
if (thd->killed) close_thread_tables(thd);
close_thread_tables(thd);
thd->restore_backup_open_tables_state(backup); thd->restore_backup_open_tables_state(backup);
} }
......
...@@ -1161,9 +1161,7 @@ class Dummy_error_handler : public Internal_error_handler ...@@ -1161,9 +1161,7 @@ class Dummy_error_handler : public Internal_error_handler
class Drop_table_error_handler : public Internal_error_handler class Drop_table_error_handler : public Internal_error_handler
{ {
public: public:
Drop_table_error_handler(Internal_error_handler *err_handler) Drop_table_error_handler() {}
:m_err_handler(err_handler)
{ }
public: public:
bool handle_condition(THD *thd, bool handle_condition(THD *thd,
...@@ -1174,7 +1172,6 @@ class Drop_table_error_handler : public Internal_error_handler ...@@ -1174,7 +1172,6 @@ class Drop_table_error_handler : public Internal_error_handler
MYSQL_ERROR ** cond_hdl); MYSQL_ERROR ** cond_hdl);
private: private:
Internal_error_handler *m_err_handler;
}; };
......
...@@ -934,7 +934,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) ...@@ -934,7 +934,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
} }
else else
{ {
Drop_table_error_handler err_handler(thd->get_internal_handler()); Drop_table_error_handler err_handler;
thd->push_internal_handler(&err_handler); thd->push_internal_handler(&err_handler);
error= -1; error= -1;
......
...@@ -405,6 +405,56 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables) ...@@ -405,6 +405,56 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
} }
/**
A helper class to process an error from mysql_lock_tables().
HANDLER READ statement's attempt to lock the subject table
may get aborted if there is a pending DDL. In that case
we close the table, reopen it, and try to read again.
This is implicit and obscure, since HANDLER position
is lost in the process, but it's the legacy server
behaviour we should preserve.
*/
class Sql_handler_lock_error_handler: public Internal_error_handler
{
public:
virtual
bool handle_condition(THD *thd,
uint sql_errno,
const char *sqlstate,
MYSQL_ERROR::enum_warning_level level,
const char* msg,
MYSQL_ERROR **cond_hdl);
bool need_reopen() const { return m_need_reopen; };
void init() { m_need_reopen= FALSE; };
private:
bool m_need_reopen;
};
/**
Handle an error from mysql_lock_tables().
Ignore ER_LOCK_ABORTED errors.
*/
bool
Sql_handler_lock_error_handler::
handle_condition(THD *thd,
uint sql_errno,
const char *sqlstate,
MYSQL_ERROR::enum_warning_level level,
const char* msg,
MYSQL_ERROR **cond_hdl)
{
*cond_hdl= NULL;
if (sql_errno == ER_LOCK_ABORTED)
m_need_reopen= TRUE;
return m_need_reopen;
}
/* /*
Read from a HANDLER table. Read from a HANDLER table.
...@@ -442,7 +492,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, ...@@ -442,7 +492,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
uint num_rows; uint num_rows;
uchar *UNINIT_VAR(key); uchar *UNINIT_VAR(key);
uint UNINIT_VAR(key_len); uint UNINIT_VAR(key_len);
bool need_reopen; Sql_handler_lock_error_handler sql_handler_lock_error;
DBUG_ENTER("mysql_ha_read"); DBUG_ENTER("mysql_ha_read");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'", DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias)); tables->db, tables->table_name, tables->alias));
...@@ -506,8 +556,12 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, ...@@ -506,8 +556,12 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
thd->open_tables= hash_tables->table; thd->open_tables= hash_tables->table;
lock= mysql_lock_tables(thd, &thd->open_tables, 1, 0, &need_reopen); sql_handler_lock_error.init();
thd->push_internal_handler(&sql_handler_lock_error);
lock= mysql_lock_tables(thd, &thd->open_tables, 1, 0);
thd->pop_internal_handler();
/* /*
In 5.1 and earlier, mysql_lock_tables() could replace the TABLE In 5.1 and earlier, mysql_lock_tables() could replace the TABLE
object with another one (reopen it). This is no longer the case object with another one (reopen it). This is no longer the case
...@@ -517,7 +571,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, ...@@ -517,7 +571,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
/* Restore previous context. */ /* Restore previous context. */
thd->open_tables= backup_open_tables; thd->open_tables= backup_open_tables;
if (need_reopen) if (sql_handler_lock_error.need_reopen())
{ {
mysql_ha_close_table(thd, hash_tables); mysql_ha_close_table(thd, hash_tables);
goto retry; goto retry;
......
...@@ -2396,7 +2396,8 @@ void kill_delayed_threads(void) ...@@ -2396,7 +2396,8 @@ void kill_delayed_threads(void)
bool Delayed_insert::open_and_lock_table() bool Delayed_insert::open_and_lock_table()
{ {
if (!(table= open_n_lock_single_table(&thd, &table_list, if (!(table= open_n_lock_single_table(&thd, &table_list,
TL_WRITE_DELAYED, 0))) TL_WRITE_DELAYED,
MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK)))
{ {
thd.fatal_error(); // Abort waiting inserts thd.fatal_error(); // Abort waiting inserts
return TRUE; return TRUE;
...@@ -2557,7 +2558,6 @@ pthread_handler_t handle_delayed_insert(void *arg) ...@@ -2557,7 +2558,6 @@ pthread_handler_t handle_delayed_insert(void *arg)
if (di->tables_in_use && ! thd->lock && !thd->killed) if (di->tables_in_use && ! thd->lock && !thd->killed)
{ {
bool need_reopen;
/* /*
Request for new delayed insert. Request for new delayed insert.
Lock the table, but avoid to be blocked by a global read lock. Lock the table, but avoid to be blocked by a global read lock.
...@@ -2568,30 +2568,10 @@ pthread_handler_t handle_delayed_insert(void *arg) ...@@ -2568,30 +2568,10 @@ pthread_handler_t handle_delayed_insert(void *arg)
handler will close the table and finish when the outstanding handler will close the table and finish when the outstanding
inserts are done. inserts are done.
*/ */
if (! (thd->lock= mysql_lock_tables(thd, &di->table, 1, if (! (thd->lock= mysql_lock_tables(thd, &di->table, 1, 0)))
MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK,
&need_reopen)))
{ {
if (need_reopen && !thd->killed) /* Fatal error */
{ thd->killed= THD::KILL_CONNECTION;
/*
We were waiting to obtain TL_WRITE_DELAYED (probably due to
someone having or requesting TL_WRITE_ALLOW_READ) and got
aborted. Try to reopen table and if it fails die.
*/
TABLE_LIST *tl_ptr = &di->table_list;
close_tables_for_reopen(thd, &tl_ptr, NULL);
di->table= 0;
if (di->open_and_lock_table())
{
thd->killed= THD::KILL_CONNECTION;
}
}
else
{
/* Fatal error */
thd->killed= THD::KILL_CONNECTION;
}
} }
mysql_cond_broadcast(&di->cond_client); mysql_cond_broadcast(&di->cond_client);
} }
...@@ -3542,7 +3522,6 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, ...@@ -3542,7 +3522,6 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
List_iterator_fast<Item> it(*items); List_iterator_fast<Item> it(*items);
Item *item; Item *item;
Field *tmp_field; Field *tmp_field;
bool not_used;
DBUG_ENTER("create_table_from_items"); DBUG_ENTER("create_table_from_items");
tmp_table.alias= 0; tmp_table.alias= 0;
...@@ -3667,7 +3646,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, ...@@ -3667,7 +3646,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
the table) and thus can't get aborted. the table) and thus can't get aborted.
*/ */
if (! ((*lock)= mysql_lock_tables(thd, &table, 1, if (! ((*lock)= mysql_lock_tables(thd, &table, 1,
MYSQL_LOCK_IGNORE_FLUSH, &not_used)) || MYSQL_LOCK_IGNORE_FLUSH)) ||
hooks->postlock(&table, 1)) hooks->postlock(&table, 1))
{ {
if (*lock) if (*lock)
......
...@@ -1811,7 +1811,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, ...@@ -1811,7 +1811,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
my_bool drop_temporary) my_bool drop_temporary)
{ {
bool error; bool error;
Drop_table_error_handler err_handler(thd->get_internal_handler()); Drop_table_error_handler err_handler;
DBUG_ENTER("mysql_rm_table"); DBUG_ENTER("mysql_rm_table");
......
...@@ -203,33 +203,26 @@ int mysql_update(THD *thd, ...@@ -203,33 +203,26 @@ int mysql_update(THD *thd,
SQL_SELECT *select; SQL_SELECT *select;
READ_RECORD info; READ_RECORD info;
SELECT_LEX *select_lex= &thd->lex->select_lex; SELECT_LEX *select_lex= &thd->lex->select_lex;
bool need_reopen;
ulonglong id; ulonglong id;
List<Item> all_fields; List<Item> all_fields;
THD::killed_state killed_status= THD::NOT_KILLED; THD::killed_state killed_status= THD::NOT_KILLED;
MDL_ticket *start_of_statement_svp= thd->mdl_context.mdl_savepoint(); MDL_ticket *start_of_statement_svp= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("mysql_update"); DBUG_ENTER("mysql_update");
for ( ; ; ) if (open_tables(thd, &table_list, &table_count, 0))
{ DBUG_RETURN(1);
if (open_tables(thd, &table_list, &table_count, 0))
DBUG_RETURN(1);
if (table_list->multitable_view) if (table_list->multitable_view)
{ {
DBUG_ASSERT(table_list->view != 0); DBUG_ASSERT(table_list->view != 0);
DBUG_PRINT("info", ("Switch to multi-update")); DBUG_PRINT("info", ("Switch to multi-update"));
/* pass counter value */ /* pass counter value */
thd->lex->table_count= table_count; thd->lex->table_count= table_count;
/* convert to multiupdate */ /* convert to multiupdate */
DBUG_RETURN(2); DBUG_RETURN(2);
}
if (!lock_tables(thd, table_list, table_count, 0, &need_reopen))
break;
if (!need_reopen)
DBUG_RETURN(1);
close_tables_for_reopen(thd, &table_list, start_of_statement_svp);
} }
if (lock_tables(thd, table_list, table_count, 0))
DBUG_RETURN(1);
if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) || if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
(thd->fill_derived_tables() && (thd->fill_derived_tables() &&
...@@ -963,17 +956,14 @@ int mysql_multi_update_prepare(THD *thd) ...@@ -963,17 +956,14 @@ int mysql_multi_update_prepare(THD *thd)
uint table_count= lex->table_count; uint table_count= lex->table_count;
const bool using_lock_tables= thd->locked_tables_mode != LTM_NONE; const bool using_lock_tables= thd->locked_tables_mode != LTM_NONE;
bool original_multiupdate= (thd->lex->sql_command == SQLCOM_UPDATE_MULTI); bool original_multiupdate= (thd->lex->sql_command == SQLCOM_UPDATE_MULTI);
bool need_reopen= FALSE;
MDL_ticket *start_of_statement_svp= thd->mdl_context.mdl_savepoint(); MDL_ticket *start_of_statement_svp= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("mysql_multi_update_prepare"); DBUG_ENTER("mysql_multi_update_prepare");
/* following need for prepared statements, to run next time multi-update */ /* following need for prepared statements, to run next time multi-update */
thd->lex->sql_command= SQLCOM_UPDATE_MULTI; thd->lex->sql_command= SQLCOM_UPDATE_MULTI;
reopen_tables:
/* open tables and create derived ones, but do not lock and fill them */ /* open tables and create derived ones, but do not lock and fill them */
if (((original_multiupdate || need_reopen) && if ((original_multiupdate &&
open_tables(thd, &table_list, &table_count, 0)) || open_tables(thd, &table_list, &table_count, 0)) ||
mysql_handle_derived(lex, &mysql_derived_prepare)) mysql_handle_derived(lex, &mysql_derived_prepare))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
...@@ -1089,58 +1079,11 @@ int mysql_multi_update_prepare(THD *thd) ...@@ -1089,58 +1079,11 @@ int mysql_multi_update_prepare(THD *thd)
/* now lock and fill tables */ /* now lock and fill tables */
if (!thd->stmt_arena->is_stmt_prepare() && if (!thd->stmt_arena->is_stmt_prepare() &&
lock_tables(thd, table_list, table_count, 0, &need_reopen)) lock_tables(thd, table_list, table_count, 0))
{ {
if (!need_reopen) DBUG_RETURN(TRUE);
DBUG_RETURN(TRUE);
DBUG_PRINT("info", ("lock_tables failed, reopening"));
/*
We have to reopen tables since some of them were altered or dropped
during lock_tables() or something was done with their triggers.
Let us do some cleanups to be able do setup_table() and setup_fields()
once again.
*/
List_iterator_fast<Item> it(*fields);
Item *item;
while ((item= it++))
item->cleanup();
/*
To not to hog memory (as a result of the
unit->reinit_exec_mechanism() call below):
*/
lex->unit.cleanup();
for (SELECT_LEX *sl= lex->all_selects_list;
sl;
sl= sl->next_select_in_list())
{
SELECT_LEX_UNIT *unit= sl->master_unit();
unit->reinit_exec_mechanism(); // reset unit->prepared flags
/*
Reset 'clean' flag back to force normal execution of
unit->cleanup() in Prepared_statement::cleanup_stmt()
(call to lex->unit.cleanup() above sets this flag to TRUE).
*/
unit->unclean();
}
/*
Also we need to cleanup Natural_join_column::table_field items.
To not to traverse a join tree we will cleanup whole
thd->free_list (in PS execution mode that list may not contain
items from 'fields' list, so the cleanup above is necessary to.
*/
cleanup_items(thd->free_list);
cleanup_items(thd->stmt_arena->free_list);
close_tables_for_reopen(thd, &table_list, start_of_statement_svp);
DEBUG_SYNC(thd, "multi_update_reopen_tables");
goto reopen_tables;
} }
/* @todo: downgrade the metadata locks here. */
/* /*
Check that we are not using table that we are updating, but we should Check that we are not using table that we are updating, but we should
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment