mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
A 5.1-only version of fix for bug #46947 "Embedded SELECT
without FOR UPDATE is causing a lock". SELECT statements with subqueries referencing InnoDB tables were acquiring shared locks on rows in these tables when they were executed in REPEATABLE-READ mode and with statement or mixed mode binary logging turned on. This was a regression which were introduced when fixing bug 39843. The problem was that for tables belonging to subqueries parser set TL_READ_DEFAULT as a lock type. In cases when statement/mixed binary logging at open_tables() time this type of lock was converted to TL_READ_NO_INSERT lock at open_tables() time and caused InnoDB engine to acquire shared locks on reads from these tables. Although in some cases such behavior was correct (e.g. for subqueries in DELETE) in case of SELECT it has caused unnecessary locking. This patch implements minimal version of the fix for the specific problem described in the bug-report which supposed to be not too risky for pushing into 5.1 tree. The 5.5 tree already contains a more appropriate solution which also addresses other related issues like bug 53921 "Wrong locks for SELECTs used stored functions may lead to broken SBR". This patch tries to solve the problem by ensuring that TL_READ_DEFAULT lock which is set in the parser for tables participating in subqueries at open_tables() time is interpreted as TL_READ_NO_INSERT or TL_READ. TL_READ is used only if we know that this is a SELECT and that this particular table is not used by a stored function. Test coverage is added for both InnoDB and MyISAM. This patch introduces an "incompatible" change in locking scheme for subqueries used in SELECT ... FOR UPDATE and SELECT .. IN SHARE MODE. In 4.1 (as well as in 5.0 and 5.1 before fix for bug 39843) the server would use a snapshot InnoDB read for subqueries in SELECT FOR UPDATE and SELECT .. IN SHARE MODE statements, regardless of whether the binary log is on or off. If the user required a different type of read (i.e. locking read), he/she could request so explicitly by providing FOR UPDATE/IN SHARE MODE clause for each individual subquery. The patch for bug 39843 broke this behaviour (which was not documented or tested), and started to use locking reads for all subqueries in SELECT ... FOR UPDATE/IN SHARE MODE. This patch restores 4.1 behaviour. This patch should be mostly null-merged into 5.5 tree.
This commit is contained in:
@ -4418,7 +4418,8 @@ bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last,
|
||||
Return a appropriate read lock type given a table object.
|
||||
|
||||
@param thd Thread context
|
||||
@param table TABLE object for table to be locked
|
||||
@param lex LEX for the current statement.
|
||||
@param table_list Table list element for table to be locked.
|
||||
|
||||
@remark Due to a statement-based replication limitation, statements such as
|
||||
INSERT INTO .. SELECT FROM .. and CREATE TABLE .. SELECT FROM need
|
||||
@ -4427,19 +4428,32 @@ bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last,
|
||||
source table. If such a statement gets applied on the slave before
|
||||
the INSERT .. SELECT statement finishes, data on the master could
|
||||
differ from data on the slave and end-up with a discrepancy between
|
||||
the binary log and table state. Furthermore, this does not apply to
|
||||
I_S and log tables as it's always unsafe to replicate such tables
|
||||
under statement-based replication as the table on the slave might
|
||||
contain other data (ie: general_log is enabled on the slave). The
|
||||
statement will be marked as unsafe for SBR in decide_logging_format().
|
||||
the binary log and table state.
|
||||
This also applies to SELECT/SET/DO statements which use stored
|
||||
functions. Calls to such functions are going to be logged as a
|
||||
whole and thus should be serialized against concurrent changes
|
||||
to tables used by those functions. This can be avoided if functions
|
||||
only read data but doing so requires more complex analysis than it
|
||||
is done now (unfortunately, due to bug #53921 "Wrong locks for
|
||||
SELECTs used stored functions may lead to broken SBR" this rule
|
||||
is not followed in cases when stored function or trigger use
|
||||
simple SELECT and not a subselect in their body).
|
||||
Furthermore, this does not apply to I_S and log tables as it's
|
||||
always unsafe to replicate such tables under statement-based
|
||||
replication as the table on the slave might contain other data
|
||||
(ie: general_log is enabled on the slave). The statement will
|
||||
be marked as unsafe for SBR in decide_logging_format().
|
||||
*/
|
||||
|
||||
thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table)
|
||||
thr_lock_type read_lock_type_for_table(THD *thd, LEX *lex,
|
||||
TABLE_LIST *table_list)
|
||||
{
|
||||
bool log_on= mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG);
|
||||
ulong binlog_format= thd->variables.binlog_format;
|
||||
if ((log_on == FALSE) || (binlog_format == BINLOG_FORMAT_ROW) ||
|
||||
(table->s->table_category == TABLE_CATEGORY_PERFORMANCE))
|
||||
(table_list->table->s->table_category == TABLE_CATEGORY_PERFORMANCE) ||
|
||||
(lex->sql_command == SQLCOM_SELECT &&
|
||||
! table_list->prelocking_placeholder))
|
||||
return TL_READ;
|
||||
else
|
||||
return TL_READ_NO_INSERT;
|
||||
@ -4735,7 +4749,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
|
||||
tables->table->reginfo.lock_type= thd->update_lock_default;
|
||||
else if (tables->lock_type == TL_READ_DEFAULT)
|
||||
tables->table->reginfo.lock_type=
|
||||
read_lock_type_for_table(thd, tables->table);
|
||||
read_lock_type_for_table(thd, thd->lex, tables);
|
||||
else
|
||||
tables->table->reginfo.lock_type= tables->lock_type;
|
||||
}
|
||||
@ -5389,6 +5403,8 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
|
||||
DEBUG_SYNC(thd, "after_lock_tables_takes_lock");
|
||||
|
||||
if (thd->lex->requires_prelocking() &&
|
||||
thd->lex->sql_command != SQLCOM_LOCK_TABLES)
|
||||
{
|
||||
|
Reference in New Issue
Block a user