mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Bug#34306: Can't make copy of log tables when server binary log is enabled
The problem is that when statement-based replication was enabled, statements such as INSERT INTO .. SELECT FROM .. and CREATE TABLE .. SELECT FROM need to grab a read lock on the source table that does not permit concurrent inserts, which would in turn be denied if the source table is a log table because log tables can't be locked exclusively. The solution is to not take such a lock when the source table is a log table as it is unsafe to replicate log tables under statement based replication. Furthermore, the read lock that does not permits concurrent inserts is now only taken if statement-based replication is enabled and if the source table is not a log table. include/thr_lock.h: Introduce yet another lock type that my get upgraded depending on the binary log format. This is not a optimal solution but can be easily improved later. mysql-test/r/log_tables.result: Add test case result for Bug#34306 mysql-test/suite/binlog/r/binlog_stm_row.result: Add test case result for Bug#34306 mysql-test/suite/binlog/t/binlog_stm_row.test: Add test case for Bug#34306 mysql-test/t/log_tables.test: Add test case for Bug#34306 sql/lock.cc: Assert that TL_READ_DEFAULT is not a real lock type. sql/mysql_priv.h: Export new function. sql/mysqld.cc: Remove using_update_log. sql/sql_base.cc: Introduce function that returns the appropriate read lock type depending on how the statement is going to be replicated. It will only take a TL_READ_NO_INSERT log if the binary is enabled and the binary log format is statement-based and the table is not a log table. sql/sql_parse.cc: Remove using_update_log. sql/sql_update.cc: Use new function to choose read lock type. sql/sql_yacc.yy: The lock type is now decided at open_tables time. This old behavior was actually misleading as the binary log format can be dynamically switched and this would not change for statements that have already been parsed when the binary log format is changed (ie: prepared statements).
This commit is contained in:
@ -4355,6 +4355,38 @@ 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
|
||||
|
||||
@remark Due to a statement-based replication limitation, statements such as
|
||||
INSERT INTO .. SELECT FROM .. and CREATE TABLE .. SELECT FROM need
|
||||
to grab a TL_READ_NO_INSERT lock on the source table in order to
|
||||
prevent the replication of a concurrent statement that modifies the
|
||||
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().
|
||||
*/
|
||||
|
||||
thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table)
|
||||
{
|
||||
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))
|
||||
return TL_READ;
|
||||
else
|
||||
return TL_READ_NO_INSERT;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Open all tables in list
|
||||
|
||||
@ -4629,6 +4661,9 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
|
||||
{
|
||||
if (tables->lock_type == TL_WRITE_DEFAULT)
|
||||
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);
|
||||
else if (tables->table->s->tmp_table == NO_TMP_TABLE)
|
||||
tables->table->reginfo.lock_type= tables->lock_type;
|
||||
}
|
||||
@ -5036,7 +5071,11 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables)
|
||||
void* prev_ht= NULL;
|
||||
for (TABLE_LIST *table= tables; table; table= table->next_global)
|
||||
{
|
||||
if (!table->placeholder() && table->lock_type >= TL_WRITE_ALLOW_WRITE)
|
||||
if (table->placeholder())
|
||||
continue;
|
||||
if (table->table->s->table_category == TABLE_CATEGORY_PERFORMANCE)
|
||||
thd->lex->set_stmt_unsafe();
|
||||
if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
|
||||
{
|
||||
ulonglong const flags= table->table->file->ha_table_flags();
|
||||
DBUG_PRINT("info", ("table: %s; ha_table_flags: %s%s",
|
||||
|
Reference in New Issue
Block a user