From d2d630c2ee4cfb7bd72531882f31386167f5f857 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 21 Jun 2007 13:34:06 +0200 Subject: [PATCH] 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). mysql-test/r/binlog_multi_engine.result: Result change. mysql-test/t/binlog_multi_engine.test: Errors now generated for some statements due to NDB setting the HA_HAS_OWN_BINLOGGING flag. sql/sql_base.cc: Computing both the intersection and union of all table flags in the logic to decide logging format. Calculating if there are more than one engine involved in the statement. For this case, we only consider engines that hold tables that data is written to. 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). --- Renaming variable. --- mysql-test/r/binlog_multi_engine.result | 22 +++------- mysql-test/t/binlog_multi_engine.test | 7 +++ sql/sql_base.cc | 57 +++++++++++++++++++------ 3 files changed, 57 insertions(+), 29 deletions(-) diff --git a/mysql-test/r/binlog_multi_engine.result b/mysql-test/r/binlog_multi_engine.result index 18e43e2a3d6..541c015c4fe 100644 --- a/mysql-test/r/binlog_multi_engine.result +++ b/mysql-test/r/binlog_multi_engine.result @@ -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; 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; -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 t1b; TRUNCATE t1n; @@ -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); UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c; 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; -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 t1b; TRUNCATE t1n; @@ -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; 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; +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; -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; show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info @@ -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 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 # 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 t1b master-bin.000001 # Query # # BEGIN @@ -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 # Write_rows # # table_id: # master-bin.000001 # Write_rows # # table_id: # -master-bin.000001 # Write_rows # # table_id: # -master-bin.000001 # Update_rows # # table_id: # -master-bin.000001 # Update_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F master-bin.000001 # Query # # COMMIT master-bin.000001 # Query # # use `test`; TRUNCATE t1n master-bin.000001 # Table_map # # table_id: # (test.t1m) 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 # Table_map # # table_id: # (test.t1n) 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 # Update_rows # # table_id: # -master-bin.000001 # Update_rows # # table_id: # flags: STMT_END_F +master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F master-bin.000001 # Query # # COMMIT master-bin.000001 # Query # # use `test`; DROP TABLE t1m, t1b, t1n diff --git a/mysql-test/t/binlog_multi_engine.test b/mysql-test/t/binlog_multi_engine.test index acfb8b66b8a..5ab52ca9015 100644 --- a/mysql-test/t/binlog_multi_engine.test +++ b/mysql-test/t/binlog_multi_engine.test @@ -1,3 +1,8 @@ +# 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_ndb.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); INSERT INTO t1n VALUES (1,1), (1,2), (2,1), (2,2); 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; error ER_BINLOG_LOGGING_IMPOSSIBLE; 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); error ER_BINLOG_LOGGING_IMPOSSIBLE; 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; error ER_BINLOG_LOGGING_IMPOSSIBLE; UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8de3beb2a24..48c876d6d89 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -28,6 +28,8 @@ #include #endif +#define FLAGSTR(S,F) ((S) & (F) ? #F " " : "") + /** This internal handler is used to trap internally errors that can occur when executing open table @@ -3996,36 +3998,47 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables) { 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) + { if (!table->placeholder() && table->lock_type >= TL_WRITE_ALLOW_WRITE) { -#define FLAGSTR(S,F) ((S) & (F) ? #F " " : "") -#ifndef DBUG_OFF - ulonglong flags= table->table->file->ha_table_flags(); + ulonglong const flags= table->table->file->ha_table_flags(); DBUG_PRINT("info", ("table: %s; ha_table_flags: %s%s", table->table_name, FLAGSTR(flags, HA_BINLOG_STMT_CAPABLE), FLAGSTR(flags, HA_BINLOG_ROW_CAPABLE))); -#endif - binlog_flags &= table->table->file->ha_table_flags(); + if (prev_ht && prev_ht != table->table->file->ht) + 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), - FLAGSTR(binlog_flags, HA_BINLOG_ROW_CAPABLE))); + } + + DBUG_PRINT("info", ("flags_all_set: %s%s", + 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", thd->variables.binlog_format)); + DBUG_PRINT("info", ("multi_engine: %s", + multi_engine ? "TRUE" : "FALSE")); int error= 0; - if (binlog_flags == 0) + if (flags_all_set == 0) { my_error((error= ER_BINLOG_LOGGING_IMPOSSIBLE), MYF(0), "Statement cannot be logged to the binary log in" " row-based nor statement-based format"); } 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), "Statement-based format required for this statement," @@ -4033,13 +4046,29 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables) } else if ((thd->variables.binlog_format == BINLOG_FORMAT_ROW || 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), "Row-based format required for this statement," " 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)); if (error) @@ -4061,7 +4090,7 @@ int decide_logging_format(THD *thd, TABLE_LIST *tables) here. */ 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(); }