A review comment for the fix for Bug#46672.
Remove unnecessary need_reopen loops.
This commit is contained in:
parent
ea70b6a20a
commit
7116431a8a
@ -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';
|
|||||||
# 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';
|
|||||||
# 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;
|
|||||||
# 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=
|
|||||||
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;
|
|||||||
--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=
|
|||||||
# 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;
|
|||||||
--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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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.
|
||||||
|
136
sql/lock.cc
136
sql/lock.cc
@ -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[]=
|
|||||||
@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,
|
|||||||
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,
|
|||||||
|
|
||||||
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)
|
|||||||
@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)
|
|||||||
@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);
|
||||||
|
|
||||||
|
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= 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);
|
|
||||||
|
|
||||||
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)
|
|||||||
*/
|
*/
|
||||||
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)
|
|||||||
*/
|
*/
|
||||||
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)
|
||||||
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,
|
||||||
uint actual_error= thd->net.last_errno;
|
"Error '%s' in %s event: when locking tables",
|
||||||
rli->report(ERROR_LEVEL, actual_error,
|
(actual_error ? thd->net.last_error :
|
||||||
"Error '%s' in %s event: when locking tables",
|
"unexpected success or fatal error"),
|
||||||
(actual_error ? thd->net.last_error :
|
get_type_str());
|
||||||
"unexpected success or fatal error"),
|
thd->is_fatal_error= 1;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
/*
|
|
||||||
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,
|
rli->report(ERROR_LEVEL, error,
|
||||||
ER(ER_SLAVE_FATAL_ERROR),
|
"Error in %s event: when locking tables",
|
||||||
"call to binlog_flush_pending_rows_event() failed");
|
get_type_str());
|
||||||
thd->is_slave_error= 1;
|
|
||||||
DBUG_RETURN(error);
|
|
||||||
}
|
|
||||||
TABLE_LIST *tables= rli->tables_to_lock;
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
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,
|
|||||||
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,
|
|||||||
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
|
|||||||
|
|
||||||
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"
|
||||||
|
119
sql/sql_base.cc
119
sql/sql_base.cc
@ -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
|
|||||||
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)
|
|||||||
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, <_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,
|
|||||||
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,
|
|||||||
/* 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 @@ retry:
|
|||||||
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,
|
|||||||
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,
|
|||||||
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))
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
if (!lock_tables(thd, tables, counter, flags,
|
|
||||||
&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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
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,
|
|||||||
*/
|
*/
|
||||||
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,
|
|||||||
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)
|
|||||||
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 @@ public:
|
|||||||
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 @@ public:
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
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,
|
|||||||
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 @@ retry:
|
|||||||
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 @@ retry:
|
|||||||
/* 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)
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
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, ¬_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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
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 @@ reopen_tables:
|
|||||||
|
|
||||||
/* 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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user