Follow-up for the fix for bug #46947 "Embedded SELECT without
FOR UPDATE is causing a lock". This patch tries to address problems which were exposed during backporting of original patch to 5.1 tree. - It ensures that we don't change locking behavior of simple SELECT statements on InnoDB tables when they are executed under LOCK TABLES ... READ and with @@innodb_table_locks=0. Also we no longer pass TL_READ_DEFAULT/TL_WRITE_DEFAULT lock types, which are supposed to be parser-only, to handler::start_stmt() method. - It makes check_/no_concurrent_insert.inc auxiliary scripts more robust against changes in test cases that use them and also ensures that they don't unnecessarily change environment of caller.
This commit is contained in:
parent
c09eb2afc3
commit
6ceacd4fb9
@ -20,6 +20,9 @@
|
|||||||
--disable_result_log
|
--disable_result_log
|
||||||
--disable_query_log
|
--disable_query_log
|
||||||
|
|
||||||
|
# Reset DEBUG_SYNC facility for safety.
|
||||||
|
set debug_sync= "RESET";
|
||||||
|
|
||||||
if (`SELECT '$restore_table' <> ''`)
|
if (`SELECT '$restore_table' <> ''`)
|
||||||
{
|
{
|
||||||
--eval create table t_backup select * from $restore_table;
|
--eval create table t_backup select * from $restore_table;
|
||||||
@ -86,5 +89,8 @@ if (`SELECT '$restore_table' <> ''`)
|
|||||||
drop table t_backup;
|
drop table t_backup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Clean-up. Reset DEBUG_SYNC facility after use.
|
||||||
|
set debug_sync= "RESET";
|
||||||
|
|
||||||
--enable_result_log
|
--enable_result_log
|
||||||
--enable_query_log
|
--enable_query_log
|
||||||
|
@ -20,6 +20,9 @@
|
|||||||
--disable_result_log
|
--disable_result_log
|
||||||
--disable_query_log
|
--disable_query_log
|
||||||
|
|
||||||
|
# Reset DEBUG_SYNC facility for safety.
|
||||||
|
set debug_sync= "RESET";
|
||||||
|
|
||||||
if (`SELECT '$restore_table' <> ''`)
|
if (`SELECT '$restore_table' <> ''`)
|
||||||
{
|
{
|
||||||
--eval create table t_backup select * from $restore_table;
|
--eval create table t_backup select * from $restore_table;
|
||||||
@ -71,5 +74,8 @@ if (`SELECT '$restore_table' <> ''`)
|
|||||||
drop table t_backup;
|
drop table t_backup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Clean-up. Reset DEBUG_SYNC facility after use.
|
||||||
|
set debug_sync= "RESET";
|
||||||
|
|
||||||
--enable_result_log
|
--enable_result_log
|
||||||
--enable_query_log
|
--enable_query_log
|
||||||
|
@ -27,9 +27,10 @@ commit;
|
|||||||
drop table t1;
|
drop table t1;
|
||||||
#
|
#
|
||||||
# Old lock method (where LOCK TABLE was ignored by InnoDB) no longer
|
# Old lock method (where LOCK TABLE was ignored by InnoDB) no longer
|
||||||
# works due to fix for bugs #46272 "MySQL 5.4.4, new MDL: unnecessary
|
# works when LOCK TABLE ... WRITE is used due to fix for bugs #46272
|
||||||
# deadlock" and bug #37346 "innodb does not detect deadlock between
|
# "MySQL 5.4.4, new MDL: unnecessary and bug #37346 "innodb does not
|
||||||
# update and alter table".
|
# detect deadlock between update and alter table". But it still works
|
||||||
|
# for LOCK TABLE ... READ.
|
||||||
#
|
#
|
||||||
set @@innodb_table_locks=0;
|
set @@innodb_table_locks=0;
|
||||||
create table t1 (id integer primary key, x integer) engine=INNODB;
|
create table t1 (id integer primary key, x integer) engine=INNODB;
|
||||||
@ -61,4 +62,30 @@ commit;
|
|||||||
# Reap LOCK TABLE.
|
# Reap LOCK TABLE.
|
||||||
unlock tables;
|
unlock tables;
|
||||||
# Connection 'con1'.
|
# Connection 'con1'.
|
||||||
|
select * from t1 where id = 0 for update;
|
||||||
|
id x
|
||||||
|
0 1
|
||||||
|
# Connection 'con2'.
|
||||||
|
# The below statement should not be blocked as LOCK TABLES ... READ
|
||||||
|
# does not take strong SQL-level lock on t1. SELECTs which do not
|
||||||
|
# conflict with transaction in the first connections should not be
|
||||||
|
# blocked.
|
||||||
|
lock table t1 read;
|
||||||
|
select * from t1;
|
||||||
|
id x
|
||||||
|
0 1
|
||||||
|
1 1
|
||||||
|
2 2
|
||||||
|
select * from t1 where id = 1 lock in share mode;
|
||||||
|
id x
|
||||||
|
1 1
|
||||||
|
unlock tables;
|
||||||
|
select * from t1;
|
||||||
|
id x
|
||||||
|
0 1
|
||||||
|
1 1
|
||||||
|
2 2
|
||||||
|
commit;
|
||||||
|
# Connection 'con1'.
|
||||||
|
commit;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
@ -58,9 +58,10 @@ drop table t1;
|
|||||||
|
|
||||||
--echo #
|
--echo #
|
||||||
--echo # Old lock method (where LOCK TABLE was ignored by InnoDB) no longer
|
--echo # Old lock method (where LOCK TABLE was ignored by InnoDB) no longer
|
||||||
--echo # works due to fix for bugs #46272 "MySQL 5.4.4, new MDL: unnecessary
|
--echo # works when LOCK TABLE ... WRITE is used due to fix for bugs #46272
|
||||||
--echo # deadlock" and bug #37346 "innodb does not detect deadlock between
|
--echo # "MySQL 5.4.4, new MDL: unnecessary and bug #37346 "innodb does not
|
||||||
--echo # update and alter table".
|
--echo # detect deadlock between update and alter table". But it still works
|
||||||
|
--echo # for LOCK TABLE ... READ.
|
||||||
--echo #
|
--echo #
|
||||||
|
|
||||||
set @@innodb_table_locks=0;
|
set @@innodb_table_locks=0;
|
||||||
@ -102,6 +103,26 @@ unlock tables;
|
|||||||
|
|
||||||
--echo # Connection 'con1'.
|
--echo # Connection 'con1'.
|
||||||
connection con1;
|
connection con1;
|
||||||
|
|
||||||
|
select * from t1 where id = 0 for update;
|
||||||
|
|
||||||
|
--echo # Connection 'con2'.
|
||||||
|
connection con2;
|
||||||
|
--echo # The below statement should not be blocked as LOCK TABLES ... READ
|
||||||
|
--echo # does not take strong SQL-level lock on t1. SELECTs which do not
|
||||||
|
--echo # conflict with transaction in the first connections should not be
|
||||||
|
--echo # blocked.
|
||||||
|
lock table t1 read;
|
||||||
|
select * from t1;
|
||||||
|
select * from t1 where id = 1 lock in share mode;
|
||||||
|
unlock tables;
|
||||||
|
select * from t1;
|
||||||
|
commit;
|
||||||
|
|
||||||
|
--echo # Connection 'con1'.
|
||||||
|
connection con1;
|
||||||
|
commit;
|
||||||
|
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
# End of 4.1 tests
|
# End of 4.1 tests
|
||||||
|
@ -3985,19 +3985,32 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request,
|
|||||||
replication as the table on the slave might contain other data
|
replication as the table on the slave might contain other data
|
||||||
(ie: general_log is enabled on the slave). The statement will
|
(ie: general_log is enabled on the slave). The statement will
|
||||||
be marked as unsafe for SBR in decide_logging_format().
|
be marked as unsafe for SBR in decide_logging_format().
|
||||||
|
@remark Note that even in prelocked mode it is important to correctly
|
||||||
|
determine lock type value. In this mode lock type is passed to
|
||||||
|
handler::start_stmt() method and can be used by storage engine,
|
||||||
|
for example, to determine what kind of row locks it should acquire
|
||||||
|
when reading data from the table.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
thr_lock_type read_lock_type_for_table(THD *thd,
|
thr_lock_type read_lock_type_for_table(THD *thd,
|
||||||
Query_tables_list *prelocking_ctx,
|
Query_tables_list *prelocking_ctx,
|
||||||
TABLE_LIST *table_list)
|
TABLE_LIST *table_list)
|
||||||
{
|
{
|
||||||
bool log_on= mysql_bin_log.is_open() && (thd->variables.option_bits & OPTION_BIN_LOG);
|
/*
|
||||||
|
In cases when this function is called for a sub-statement executed in
|
||||||
|
prelocked mode we can't rely on OPTION_BIN_LOG flag in THD::options
|
||||||
|
bitmap to determine that binary logging is turned on as this bit can
|
||||||
|
be cleared before executing sub-statement. So instead we have to look
|
||||||
|
at THD::sql_log_bin_toplevel member.
|
||||||
|
*/
|
||||||
|
bool log_on= mysql_bin_log.is_open() && thd->sql_log_bin_toplevel;
|
||||||
ulong binlog_format= thd->variables.binlog_format;
|
ulong binlog_format= thd->variables.binlog_format;
|
||||||
if ((log_on == FALSE) || (binlog_format == BINLOG_FORMAT_ROW) ||
|
if ((log_on == FALSE) || (binlog_format == BINLOG_FORMAT_ROW) ||
|
||||||
(table_list->table->s->table_category == TABLE_CATEGORY_LOG) ||
|
(table_list->table->s->table_category == TABLE_CATEGORY_LOG) ||
|
||||||
(table_list->table->s->table_category == TABLE_CATEGORY_PERFORMANCE) ||
|
(table_list->table->s->table_category == TABLE_CATEGORY_PERFORMANCE) ||
|
||||||
!(is_update_query(prelocking_ctx->sql_command) ||
|
!(is_update_query(prelocking_ctx->sql_command) ||
|
||||||
table_list->prelocking_placeholder))
|
table_list->prelocking_placeholder ||
|
||||||
|
(thd->locked_tables_mode > LTM_LOCK_TABLES)))
|
||||||
return TL_READ;
|
return TL_READ;
|
||||||
else
|
else
|
||||||
return TL_READ_NO_INSERT;
|
return TL_READ_NO_INSERT;
|
||||||
@ -5001,35 +5014,49 @@ handle_view(THD *thd, Query_tables_list *prelocking_ctx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Check that lock is ok for tables; Call start stmt if ok
|
Check that lock is ok for tables; Call start stmt if ok
|
||||||
|
|
||||||
SYNOPSIS
|
@param thd Thread handle.
|
||||||
check_lock_and_start_stmt()
|
@param prelocking_ctx Prelocking context.
|
||||||
thd Thread handle
|
@param table_list Table list element for table to be checked.
|
||||||
table_list Table to check
|
|
||||||
lock_type Lock used for table
|
|
||||||
|
|
||||||
RETURN VALUES
|
@retval FALSE - Ok.
|
||||||
0 ok
|
@retval TRUE - Error.
|
||||||
1 error
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static bool check_lock_and_start_stmt(THD *thd, TABLE *table,
|
static bool check_lock_and_start_stmt(THD *thd,
|
||||||
thr_lock_type lock_type)
|
Query_tables_list *prelocking_ctx,
|
||||||
|
TABLE_LIST *table_list)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
|
thr_lock_type lock_type;
|
||||||
DBUG_ENTER("check_lock_and_start_stmt");
|
DBUG_ENTER("check_lock_and_start_stmt");
|
||||||
|
|
||||||
|
/*
|
||||||
|
TL_WRITE_DEFAULT and TL_READ_DEFAULT are supposed to be parser only
|
||||||
|
types of locks so they should be converted to appropriate other types
|
||||||
|
to be passed to storage engine. The exact lock type passed to the
|
||||||
|
engine is important as, for example, InnoDB uses it to determine
|
||||||
|
what kind of row locks should be acquired when executing statement
|
||||||
|
in prelocked mode or under LOCK TABLES with @@innodb_table_locks = 0.
|
||||||
|
*/
|
||||||
|
if (table_list->lock_type == TL_WRITE_DEFAULT)
|
||||||
|
lock_type= thd->update_lock_default;
|
||||||
|
else if (table_list->lock_type == TL_READ_DEFAULT)
|
||||||
|
lock_type= read_lock_type_for_table(thd, prelocking_ctx, table_list);
|
||||||
|
else
|
||||||
|
lock_type= table_list->lock_type;
|
||||||
|
|
||||||
if ((int) lock_type >= (int) TL_WRITE_ALLOW_READ &&
|
if ((int) lock_type >= (int) TL_WRITE_ALLOW_READ &&
|
||||||
(int) table->reginfo.lock_type < (int) TL_WRITE_ALLOW_READ)
|
(int) table_list->table->reginfo.lock_type < (int) TL_WRITE_ALLOW_READ)
|
||||||
{
|
{
|
||||||
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),table->alias);
|
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_list->alias);
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
if ((error=table->file->start_stmt(thd, lock_type)))
|
if ((error= table_list->table->file->start_stmt(thd, lock_type)))
|
||||||
{
|
{
|
||||||
table->file->print_error(error,MYF(0));
|
table_list->table->file->print_error(error, MYF(0));
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
@ -5174,7 +5201,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
|
|||||||
table->grant= table_list->grant;
|
table->grant= table_list->grant;
|
||||||
if (thd->locked_tables_mode)
|
if (thd->locked_tables_mode)
|
||||||
{
|
{
|
||||||
if (check_lock_and_start_stmt(thd, table, lock_type))
|
if (check_lock_and_start_stmt(thd, thd->lex, table_list))
|
||||||
table= 0;
|
table= 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -5402,7 +5429,7 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
|
|||||||
if (!table->placeholder())
|
if (!table->placeholder())
|
||||||
{
|
{
|
||||||
table->table->query_id= thd->query_id;
|
table->table->query_id= thd->query_id;
|
||||||
if (check_lock_and_start_stmt(thd, table->table, table->lock_type))
|
if (check_lock_and_start_stmt(thd, thd->lex, table))
|
||||||
{
|
{
|
||||||
mysql_unlock_tables(thd, thd->lock);
|
mysql_unlock_tables(thd, thd->lock);
|
||||||
thd->lock= 0;
|
thd->lock= 0;
|
||||||
@ -5456,7 +5483,7 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_lock_and_start_stmt(thd, table->table, table->lock_type))
|
if (check_lock_and_start_stmt(thd, thd->lex, table))
|
||||||
{
|
{
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user