diff --git a/mysql-test/suite/binlog/r/binlog_unsafe.result b/mysql-test/suite/binlog/r/binlog_unsafe.result index 9c4330a96c07b0ff1a96138bc4509a7812d75951..8c18f0ff9633117d2d31117f5a4b25b601bd9ac3 100644 --- a/mysql-test/suite/binlog/r/binlog_unsafe.result +++ b/mysql-test/suite/binlog/r/binlog_unsafe.result @@ -2679,6 +2679,8 @@ CREATE TABLE replace_table (a INT, b INT, PRIMARY KEY(a)); INSERT INTO replace_table values (1,1),(2,2); CREATE TABLE update_table (a INT, b INT, PRIMARY KEY(a)); INSERT INTO update_table values (1,1),(2,2); +CREATE TABLE insert_2_keys (a INT UNIQUE KEY, b INT UNIQUE KEY) ENGINE = InnoDB; +INSERT INTO insert_2_keys values (1, 1); INSERT IGNORE INTO insert_table SELECT * FROM filler_table; Warnings: Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT IGNORE... SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are ignored. This order cannot be predicted and may differ on master and the slave. @@ -2702,10 +2704,15 @@ Note 1592 Unsafe statement written to the binary log using statement format sinc CREATE TEMPORARY TABLE temp1 (a INT, b INT, PRIMARY KEY(b)) REPLACE SELECT * FROM filler_table; Warnings: Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. CREATE... REPLACE SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are replaced. This order cannot be predicted and may differ on master and the slave. +INSERT INTO insert_2_keys VALUES (1, 2) +ON DUPLICATE KEY UPDATE a=VALUES(a)+10, b=VALUES(b)+10; +Warnings: +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe DROP TABLE filler_table; DROP TABLE insert_table; DROP TABLE update_table; DROP TABLE replace_table; DROP TABLE create_ignore_test; DROP TABLE create_replace_test; +DROP TABLE insert_2_keys; "End of tests" diff --git a/mysql-test/suite/binlog/t/binlog_unsafe.test b/mysql-test/suite/binlog/t/binlog_unsafe.test index 6b342f24ab5cdeacdf4fbf7445dd36a1bf1b8288..7781eec12fc7f9a34c7803c1e9a2da0d22c3c044 100644 --- a/mysql-test/suite/binlog/t/binlog_unsafe.test +++ b/mysql-test/suite/binlog/t/binlog_unsafe.test @@ -17,6 +17,7 @@ # - CREATE TABLE [IGNORE/REPLACE] SELECT # - INSERT IGNORE...SELECT # - UPDATE IGNORE +# - INSERT... ON DUPLICATE KEY UPDATE on a table with two UNIQUE KEYS # # Note that statements that use stored functions, stored procedures, # triggers, views, or prepared statements that invoke unsafe @@ -714,6 +715,9 @@ DROP TABLE t1; #UPDATE IGNORE #CREATE TABLE... IGNORE SELECT #CREATE TABLE... REPLACE SELECT +# +###BUG 11765650 - 58637: MARK UPDATES THAT DEPEND ON ORDER OF TWO KEYS UNSAFE +#INSERT.... ON DUP KEY UPDATE on a table with more than one UNIQUE KEY #setup tables CREATE TABLE filler_table (a INT, b INT); @@ -723,6 +727,8 @@ CREATE TABLE replace_table (a INT, b INT, PRIMARY KEY(a)); INSERT INTO replace_table values (1,1),(2,2); CREATE TABLE update_table (a INT, b INT, PRIMARY KEY(a)); INSERT INTO update_table values (1,1),(2,2); +CREATE TABLE insert_2_keys (a INT UNIQUE KEY, b INT UNIQUE KEY) ENGINE = InnoDB; +INSERT INTO insert_2_keys values (1, 1); #INSERT IGNORE... SELECT INSERT IGNORE INTO insert_table SELECT * FROM filler_table; @@ -740,6 +746,10 @@ CREATE TABLE create_replace_test (a INT, b INT, PRIMARY KEY(b)) REPLACE SELECT * #temporary tables should not throw the warning. CREATE TEMPORARY TABLE temp1 (a INT, b INT, PRIMARY KEY(b)) REPLACE SELECT * FROM filler_table; +#INSERT.... ON DUP KEY UPDATE on a table with more than one UNIQUE KEY +INSERT INTO insert_2_keys VALUES (1, 2) + ON DUPLICATE KEY UPDATE a=VALUES(a)+10, b=VALUES(b)+10; + ###clean up DROP TABLE filler_table; DROP TABLE insert_table; @@ -747,5 +757,6 @@ DROP TABLE update_table; DROP TABLE replace_table; DROP TABLE create_ignore_test; DROP TABLE create_replace_test; +DROP TABLE insert_2_keys; --echo "End of tests" diff --git a/mysql-test/suite/rpl/r/rpl_known_bugs_detection.result b/mysql-test/suite/rpl/r/rpl_known_bugs_detection.result index 295ce7f58898c0f0f02f8335e8f2638fb0a648de..e28981fba83d902456173c3ca7a08416c4ff6f4f 100644 --- a/mysql-test/suite/rpl/r/rpl_known_bugs_detection.result +++ b/mysql-test/suite/rpl/r/rpl_known_bugs_detection.result @@ -4,6 +4,8 @@ call mtr.add_suppression("Unsafe statement written to the binary log using state CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT, UNIQUE(b)); INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10; +Warnings: +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe SELECT * FROM t1; a b 1 10 diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index f0e9c210e622d4d5d6ef516a3f2bbaa6e3108393..587a70d50bd028083c3dc64b4a4f15c8294f21b2 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6494,6 +6494,9 @@ ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC eng "CREATE TABLE... SELECT... on a table with an auto-increment column is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are inserted. This order cannot be predicted and may differ on master and the slave." +ER_BINLOG_UNSAFE_INSERT_TWO_KEYS + eng "INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe" + # # End of 5.5 error messages. # diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 45e1b3a0f12265fe47a80e21591bfe6b15c49418..6784bacdea606c2f207097e3966c8cee382783f4 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5691,6 +5691,30 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, has_write_table_with_auto_increment_and_select(tables)) thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_WRITE_AUTOINC_SELECT); + /* + INSERT...ON DUPLICATE KEY UPDATE on a table with more than one unique keys + can be unsafe. + */ + uint unique_keys= 0; + for (TABLE_LIST *query_table= tables; query_table && unique_keys <= 1; + query_table= query_table->next_global) + if(query_table->table) + { + uint keys= query_table->table->s->keys, i= 0; + unique_keys= 0; + for (KEY* keyinfo= query_table->table->s->key_info; + i < keys && unique_keys <= 1; i++, keyinfo++) + { + if (keyinfo->flags & HA_NOSAME) + unique_keys++; + } + if (!query_table->placeholder() && + query_table->lock_type >= TL_WRITE_ALLOW_WRITE && + unique_keys > 1 && thd->lex->sql_command == SQLCOM_INSERT && + thd->lex->duplicates == DUP_UPDATE) + thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS); + } + /* We have to emulate LOCK TABLES if we are statement needs prelocking. */ if (thd->lex->requires_prelocking()) { diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 4692d1154a2c10ee1b0ef82b5cd3440344232ccc..7e548ff11c70ca41c8ca1054058df073319e3330 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -66,7 +66,8 @@ Query_tables_list::binlog_stmt_unsafe_errcode[BINLOG_STMT_UNSAFE_COUNT] = ER_BINLOG_UNSAFE_CREATE_IGNORE_SELECT, ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT, ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC, - ER_BINLOG_UNSAFE_UPDATE_IGNORE + ER_BINLOG_UNSAFE_UPDATE_IGNORE, + ER_BINLOG_UNSAFE_INSERT_TWO_KEYS }; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 6d004d0fa24c7ab582b111547997e49bc51d9c11..1ce8bd3f8fa167ed4b3ca925e75e77e69fbe9150 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1323,6 +1323,12 @@ class Query_tables_list */ BINLOG_STMT_UNSAFE_UPDATE_IGNORE, + /** + INSERT... ON DUPLICATE KEY UPDATE on a table with more than one + UNIQUE KEYS is unsafe. + */ + BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS, + /* The last element of this enumeration type. */ BINLOG_STMT_UNSAFE_COUNT };