BUG#28722 (Multi-engine statements on has_own_binlogging engine):

WL#3931 (Multi-table statement involving self-logging engines):

Adding logic to generate error if more than one engine is involved in
the statement and at least one engine is self-logging (i.e., has the
HA_HAS_OWN_BINLOGGING table flags set).
parent 61ac88f3
...@@ -10,7 +10,7 @@ UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c; ...@@ -10,7 +10,7 @@ UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c;
UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f; UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f;
ERROR HY000: Binary logging not possible. Message: Statement-based format required for this statement, but not allowed by this combination of engines ERROR HY000: Binary logging not possible. Message: Statement-based format required for this statement, but not allowed by this combination of engines
UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c; UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c;
ERROR HY000: Binary logging not possible. Message: Statement cannot be logged to the binary log in row-based nor statement-based format ERROR HY000: Binary logging not possible. Message: Statement-based format required for this statement, but not allowed by this combination of engines
TRUNCATE t1m; TRUNCATE t1m;
TRUNCATE t1b; TRUNCATE t1b;
TRUNCATE t1n; TRUNCATE t1n;
...@@ -20,8 +20,9 @@ INSERT INTO t1b VALUES (1,1), (1,2), (2,1), (2,2); ...@@ -20,8 +20,9 @@ INSERT INTO t1b VALUES (1,1), (1,2), (2,1), (2,2);
INSERT INTO t1n VALUES (1,1), (1,2), (2,1), (2,2); INSERT INTO t1n VALUES (1,1), (1,2), (2,1), (2,2);
UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c; UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c;
UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f; UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f;
ERROR HY000: Binary logging not possible. Message: Statement cannot be written atomically since more than one engine involved and at least one engine is self-logging
UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c; UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c;
ERROR HY000: Binary logging not possible. Message: Statement cannot be logged to the binary log in row-based nor statement-based format ERROR HY000: Binary logging not possible. Message: Statement cannot be written atomically since more than one engine involved and at least one engine is self-logging
TRUNCATE t1m; TRUNCATE t1m;
TRUNCATE t1b; TRUNCATE t1b;
TRUNCATE t1n; TRUNCATE t1n;
...@@ -33,8 +34,9 @@ INSERT INTO t1n VALUES (1,1), (1,2), (2,1), (2,2); ...@@ -33,8 +34,9 @@ INSERT INTO t1n VALUES (1,1), (1,2), (2,1), (2,2);
UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c; UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c;
ERROR HY000: Binary logging not possible. Message: Row-based format required for this statement, but not allowed by this combination of engines ERROR HY000: Binary logging not possible. Message: Row-based format required for this statement, but not allowed by this combination of engines
UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f; UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f;
ERROR HY000: Binary logging not possible. Message: Statement cannot be written atomically since more than one engine involved and at least one engine is self-logging
UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c; UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c;
ERROR HY000: Binary logging not possible. Message: Statement cannot be logged to the binary log in row-based nor statement-based format ERROR HY000: Binary logging not possible. Message: Row-based format required for this statement, but not allowed by this combination of engines
DROP TABLE t1m, t1b, t1n; DROP TABLE t1m, t1b, t1n;
show binlog events from <binlog_start>; show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info Log_name Pos Event_type Server_id End_log_pos Info
...@@ -50,9 +52,6 @@ master-bin.000001 # Query # # use `test`; TRUNCATE t1n ...@@ -50,9 +52,6 @@ master-bin.000001 # Query # # use `test`; TRUNCATE t1n
master-bin.000001 # Query # # use `test`; INSERT INTO t1m VALUES (1,1), (1,2), (2,1), (2,2) master-bin.000001 # Query # # use `test`; INSERT INTO t1m VALUES (1,1), (1,2), (2,1), (2,2)
master-bin.000001 # Query # # use `test`; INSERT INTO t1b VALUES (1,1), (1,2), (2,1), (2,2) master-bin.000001 # Query # # use `test`; INSERT INTO t1b VALUES (1,1), (1,2), (2,1), (2,2)
master-bin.000001 # Query # # use `test`; UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c master-bin.000001 # Query # # use `test`; UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c
master-bin.000001 # Table_map # # table_id: # (test.t1m)
master-bin.000001 # Table_map # # table_id: # (test.t1n)
master-bin.000001 # Update_rows # # table_id: # flags: STMT_END_F
master-bin.000001 # Query # # use `test`; TRUNCATE t1m master-bin.000001 # Query # # use `test`; TRUNCATE t1m
master-bin.000001 # Query # # use `test`; TRUNCATE t1b master-bin.000001 # Query # # use `test`; TRUNCATE t1b
master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # BEGIN
...@@ -60,23 +59,16 @@ master-bin.000001 # Table_map # # table_id: # (test.t1n) ...@@ -60,23 +59,16 @@ master-bin.000001 # Table_map # # table_id: # (test.t1n)
master-bin.000001 # Table_map # # table_id: # (mysql.ndb_apply_status) master-bin.000001 # Table_map # # table_id: # (mysql.ndb_apply_status)
master-bin.000001 # Write_rows # # table_id: # master-bin.000001 # Write_rows # # table_id: #
master-bin.000001 # Write_rows # # table_id: # master-bin.000001 # Write_rows # # table_id: #
master-bin.000001 # Write_rows # # table_id: # master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F
master-bin.000001 # Update_rows # # table_id: #
master-bin.000001 # Update_rows # # table_id: # flags: STMT_END_F
master-bin.000001 # Query # # COMMIT master-bin.000001 # Query # # COMMIT
master-bin.000001 # Query # # use `test`; TRUNCATE t1n master-bin.000001 # Query # # use `test`; TRUNCATE t1n
master-bin.000001 # Table_map # # table_id: # (test.t1m) master-bin.000001 # Table_map # # table_id: # (test.t1m)
master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F
master-bin.000001 # Table_map # # table_id: # (test.t1m)
master-bin.000001 # Table_map # # table_id: # (test.t1n)
master-bin.000001 # Update_rows # # table_id: # flags: STMT_END_F
master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # BEGIN
master-bin.000001 # Table_map # # table_id: # (test.t1n) master-bin.000001 # Table_map # # table_id: # (test.t1n)
master-bin.000001 # Table_map # # table_id: # (mysql.ndb_apply_status) master-bin.000001 # Table_map # # table_id: # (mysql.ndb_apply_status)
master-bin.000001 # Write_rows # # table_id: # master-bin.000001 # Write_rows # # table_id: #
master-bin.000001 # Write_rows # # table_id: # master-bin.000001 # Write_rows # # table_id: #
master-bin.000001 # Write_rows # # table_id: # master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F
master-bin.000001 # Update_rows # # table_id: #
master-bin.000001 # Update_rows # # table_id: # flags: STMT_END_F
master-bin.000001 # Query # # COMMIT master-bin.000001 # Query # # COMMIT
master-bin.000001 # Query # # use `test`; DROP TABLE t1m, t1b, t1n master-bin.000001 # Query # # use `test`; DROP TABLE t1m, t1b, t1n
# Test to test how logging is done depending on the capabilities of
# the engines. Unfortunately, we don't have a good row-only logging
# engine, and NDB does not really cut is since it is also
# self-logging. I'm using it nevertheless.
source include/have_blackhole.inc; source include/have_blackhole.inc;
source include/have_ndb.inc; source include/have_ndb.inc;
source include/have_binlog_format_mixed_or_row.inc; source include/have_binlog_format_mixed_or_row.inc;
...@@ -30,6 +35,7 @@ INSERT INTO t1b VALUES (1,1), (1,2), (2,1), (2,2); ...@@ -30,6 +35,7 @@ INSERT INTO t1b VALUES (1,1), (1,2), (2,1), (2,2);
INSERT INTO t1n VALUES (1,1), (1,2), (2,1), (2,2); INSERT INTO t1n VALUES (1,1), (1,2), (2,1), (2,2);
UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c; UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c;
error ER_BINLOG_LOGGING_IMPOSSIBLE;
UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f; UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f;
error ER_BINLOG_LOGGING_IMPOSSIBLE; error ER_BINLOG_LOGGING_IMPOSSIBLE;
UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c; UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c;
...@@ -47,6 +53,7 @@ INSERT INTO t1n VALUES (1,1), (1,2), (2,1), (2,2); ...@@ -47,6 +53,7 @@ INSERT INTO t1n VALUES (1,1), (1,2), (2,1), (2,2);
error ER_BINLOG_LOGGING_IMPOSSIBLE; error ER_BINLOG_LOGGING_IMPOSSIBLE;
UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c; UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c;
error ER_BINLOG_LOGGING_IMPOSSIBLE;
UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f; UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f;
error ER_BINLOG_LOGGING_IMPOSSIBLE; error ER_BINLOG_LOGGING_IMPOSSIBLE;
UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c; UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c;
......
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
#include <io.h> #include <io.h>
#endif #endif
#define FLAGSTR(S,F) ((S) & (F) ? #F " " : "")
/** /**
This internal handler is used to trap internally This internal handler is used to trap internally
errors that can occur when executing open table errors that can occur when executing open table
...@@ -3996,36 +3998,47 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables) ...@@ -3996,36 +3998,47 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables)
{ {
if (mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG)) if (mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG))
{ {
handler::Table_flags binlog_flags= ~handler::Table_flags(); handler::Table_flags flags_some_set= handler::Table_flags();
handler::Table_flags flags_all_set= ~handler::Table_flags();
my_bool multi_engine= FALSE;
void* prev_ht= NULL;
for (TABLE_LIST *table= tables; table; table= table->next_global) for (TABLE_LIST *table= tables; table; table= table->next_global)
{
if (!table->placeholder() && table->lock_type >= TL_WRITE_ALLOW_WRITE) if (!table->placeholder() && table->lock_type >= TL_WRITE_ALLOW_WRITE)
{ {
#define FLAGSTR(S,F) ((S) & (F) ? #F " " : "") ulonglong const flags= table->table->file->ha_table_flags();
#ifndef DBUG_OFF
ulonglong flags= table->table->file->ha_table_flags();
DBUG_PRINT("info", ("table: %s; ha_table_flags: %s%s", DBUG_PRINT("info", ("table: %s; ha_table_flags: %s%s",
table->table_name, table->table_name,
FLAGSTR(flags, HA_BINLOG_STMT_CAPABLE), FLAGSTR(flags, HA_BINLOG_STMT_CAPABLE),
FLAGSTR(flags, HA_BINLOG_ROW_CAPABLE))); FLAGSTR(flags, HA_BINLOG_ROW_CAPABLE)));
#endif if (prev_ht && prev_ht != table->table->file->ht)
binlog_flags &= table->table->file->ha_table_flags(); multi_engine= TRUE;
prev_ht= table->table->file->ht;
flags_all_set &= flags;
flags_some_set |= flags;
} }
binlog_flags&= HA_BINLOG_FLAGS; }
DBUG_PRINT("info", ("binlog_flags: %s%s",
FLAGSTR(binlog_flags, HA_BINLOG_STMT_CAPABLE), DBUG_PRINT("info", ("flags_all_set: %s%s",
FLAGSTR(binlog_flags, HA_BINLOG_ROW_CAPABLE))); FLAGSTR(flags_all_set, HA_BINLOG_STMT_CAPABLE),
FLAGSTR(flags_all_set, HA_BINLOG_ROW_CAPABLE)));
DBUG_PRINT("info", ("flags_some_set: %s%s",
FLAGSTR(flags_some_set, HA_BINLOG_STMT_CAPABLE),
FLAGSTR(flags_some_set, HA_BINLOG_ROW_CAPABLE)));
DBUG_PRINT("info", ("thd->variables.binlog_format: %ld", DBUG_PRINT("info", ("thd->variables.binlog_format: %ld",
thd->variables.binlog_format)); thd->variables.binlog_format));
DBUG_PRINT("info", ("multi_engine: %s",
multi_engine ? "TRUE" : "FALSE"));
int error= 0; int error= 0;
if (binlog_flags == 0) if (flags_all_set == 0)
{ {
my_error((error= ER_BINLOG_LOGGING_IMPOSSIBLE), MYF(0), my_error((error= ER_BINLOG_LOGGING_IMPOSSIBLE), MYF(0),
"Statement cannot be logged to the binary log in" "Statement cannot be logged to the binary log in"
" row-based nor statement-based format"); " row-based nor statement-based format");
} }
else if (thd->variables.binlog_format == BINLOG_FORMAT_STMT && else if (thd->variables.binlog_format == BINLOG_FORMAT_STMT &&
(binlog_flags & HA_BINLOG_STMT_CAPABLE) == 0) (flags_all_set & HA_BINLOG_STMT_CAPABLE) == 0)
{ {
my_error((error= ER_BINLOG_LOGGING_IMPOSSIBLE), MYF(0), my_error((error= ER_BINLOG_LOGGING_IMPOSSIBLE), MYF(0),
"Statement-based format required for this statement," "Statement-based format required for this statement,"
...@@ -4033,13 +4046,29 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables) ...@@ -4033,13 +4046,29 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables)
} }
else if ((thd->variables.binlog_format == BINLOG_FORMAT_ROW || else if ((thd->variables.binlog_format == BINLOG_FORMAT_ROW ||
thd->lex->is_stmt_unsafe()) && thd->lex->is_stmt_unsafe()) &&
(binlog_flags & HA_BINLOG_ROW_CAPABLE) == 0) (flags_all_set & HA_BINLOG_ROW_CAPABLE) == 0)
{ {
my_error((error= ER_BINLOG_LOGGING_IMPOSSIBLE), MYF(0), my_error((error= ER_BINLOG_LOGGING_IMPOSSIBLE), MYF(0),
"Row-based format required for this statement," "Row-based format required for this statement,"
" but not allowed by this combination of engines"); " but not allowed by this combination of engines");
} }
/*
If more than one engine is involved in the statement and at
least one is doing it's own logging (is *self-logging*), the
statement cannot be logged atomically, so we generate an error
rather than allowing the binlog to become corrupt.
*/
if (multi_engine &&
(flags_some_set & HA_HAS_OWN_BINLOGGING))
{
error= ER_BINLOG_LOGGING_IMPOSSIBLE;
my_error(error, MYF(0),
"Statement cannot be written atomically since more"
" than one engine involved and at least one engine"
" is self-logging");
}
DBUG_PRINT("info", ("error: %d", error)); DBUG_PRINT("info", ("error: %d", error));
if (error) if (error)
...@@ -4061,7 +4090,7 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables) ...@@ -4061,7 +4090,7 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables)
here. here.
*/ */
if (thd->lex->is_stmt_unsafe() || if (thd->lex->is_stmt_unsafe() ||
(binlog_flags & HA_BINLOG_STMT_CAPABLE) == 0) (flags_all_set & HA_BINLOG_STMT_CAPABLE) == 0)
{ {
thd->set_current_stmt_binlog_row_based_if_mixed(); thd->set_current_stmt_binlog_row_based_if_mixed();
} }
......
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