Commit 1a02c266 authored by Rohit Kalhans's avatar Rohit Kalhans

BUG#11765650 - 58637: MARK UPDATES THAT DEPEND ON ORDER OF TWO KEYS UNSAFE

      
 Description: When the table has more than one unique or primary key, 
 INSERT... ON DUP KEY UPDATE statement is sensitive to the order in which
 the storage engines checks the keys. Depending on this order, the storage
 engine may determine different rows to mysql, and hence mysql can update
 different rows on master and slave.
      
 Solution: We mark INSERT...ON DUP KEY UPDATE on a table with more than on unique
 key as unsafe therefore the event will be logged in row format if it is available
 (ROW/MIXED). If only STATEMENT format is available, a warning will be thrown. 

mysql-test/suite/binlog/r/binlog_unsafe.result:
  Updated result file
mysql-test/suite/binlog/t/binlog_unsafe.test:
  Added test to check  for warning being thrown when the unsafe statement is executed
mysql-test/suite/rpl/r/rpl_known_bugs_detection.result:
  Updated result file
sql/share/errmsg-utf8.txt:
  Added new warning message
sql/sql_base.cc:
  check for tables in the query with more than one UNIQUE KEY and INSERT ON DUPLICATE KEY UPDATE, and mark such statements unsafe.
parent a4a5580e
...@@ -2679,6 +2679,8 @@ CREATE TABLE replace_table (a INT, b INT, PRIMARY KEY(a)); ...@@ -2679,6 +2679,8 @@ CREATE TABLE replace_table (a INT, b INT, PRIMARY KEY(a));
INSERT INTO replace_table values (1,1),(2,2); INSERT INTO replace_table values (1,1),(2,2);
CREATE TABLE update_table (a INT, b INT, PRIMARY KEY(a)); CREATE TABLE update_table (a INT, b INT, PRIMARY KEY(a));
INSERT INTO update_table values (1,1),(2,2); 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; INSERT IGNORE INTO insert_table SELECT * FROM filler_table;
Warnings: 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. 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 ...@@ -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; CREATE TEMPORARY TABLE temp1 (a INT, b INT, PRIMARY KEY(b)) REPLACE SELECT * FROM filler_table;
Warnings: 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. 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 filler_table;
DROP TABLE insert_table; DROP TABLE insert_table;
DROP TABLE update_table; DROP TABLE update_table;
DROP TABLE replace_table; DROP TABLE replace_table;
DROP TABLE create_ignore_test; DROP TABLE create_ignore_test;
DROP TABLE create_replace_test; DROP TABLE create_replace_test;
DROP TABLE insert_2_keys;
"End of tests" "End of tests"
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
# - CREATE TABLE [IGNORE/REPLACE] SELECT # - CREATE TABLE [IGNORE/REPLACE] SELECT
# - INSERT IGNORE...SELECT # - INSERT IGNORE...SELECT
# - UPDATE IGNORE # - UPDATE IGNORE
# - INSERT... ON DUPLICATE KEY UPDATE on a table with two UNIQUE KEYS
# #
# Note that statements that use stored functions, stored procedures, # Note that statements that use stored functions, stored procedures,
# triggers, views, or prepared statements that invoke unsafe # triggers, views, or prepared statements that invoke unsafe
...@@ -714,6 +715,9 @@ DROP TABLE t1; ...@@ -714,6 +715,9 @@ DROP TABLE t1;
#UPDATE IGNORE #UPDATE IGNORE
#CREATE TABLE... IGNORE SELECT #CREATE TABLE... IGNORE SELECT
#CREATE TABLE... REPLACE 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 #setup tables
CREATE TABLE filler_table (a INT, b INT); CREATE TABLE filler_table (a INT, b INT);
...@@ -723,6 +727,8 @@ CREATE TABLE replace_table (a INT, b INT, PRIMARY KEY(a)); ...@@ -723,6 +727,8 @@ CREATE TABLE replace_table (a INT, b INT, PRIMARY KEY(a));
INSERT INTO replace_table values (1,1),(2,2); INSERT INTO replace_table values (1,1),(2,2);
CREATE TABLE update_table (a INT, b INT, PRIMARY KEY(a)); CREATE TABLE update_table (a INT, b INT, PRIMARY KEY(a));
INSERT INTO update_table values (1,1),(2,2); 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... SELECT
INSERT IGNORE INTO insert_table SELECT * FROM filler_table; 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 * ...@@ -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. #temporary tables should not throw the warning.
CREATE TEMPORARY TABLE temp1 (a INT, b INT, PRIMARY KEY(b)) REPLACE SELECT * FROM filler_table; 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 ###clean up
DROP TABLE filler_table; DROP TABLE filler_table;
DROP TABLE insert_table; DROP TABLE insert_table;
...@@ -747,5 +757,6 @@ DROP TABLE update_table; ...@@ -747,5 +757,6 @@ DROP TABLE update_table;
DROP TABLE replace_table; DROP TABLE replace_table;
DROP TABLE create_ignore_test; DROP TABLE create_ignore_test;
DROP TABLE create_replace_test; DROP TABLE create_replace_test;
DROP TABLE insert_2_keys;
--echo "End of tests" --echo "End of tests"
...@@ -4,6 +4,8 @@ call mtr.add_suppression("Unsafe statement written to the binary log using state ...@@ -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, CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b INT,
UNIQUE(b)); UNIQUE(b));
INSERT INTO t1(b) VALUES(1),(1),(2) ON DUPLICATE KEY UPDATE t1.b=10; 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; SELECT * FROM t1;
a b a b
1 10 1 10
......
...@@ -6494,6 +6494,9 @@ ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT ...@@ -6494,6 +6494,9 @@ ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT
ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC 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." 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. # End of 5.5 error messages.
# #
...@@ -5691,6 +5691,30 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, ...@@ -5691,6 +5691,30 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
has_write_table_with_auto_increment_and_select(tables)) has_write_table_with_auto_increment_and_select(tables))
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_WRITE_AUTOINC_SELECT); 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. */ /* We have to emulate LOCK TABLES if we are statement needs prelocking. */
if (thd->lex->requires_prelocking()) if (thd->lex->requires_prelocking())
{ {
......
...@@ -66,7 +66,8 @@ Query_tables_list::binlog_stmt_unsafe_errcode[BINLOG_STMT_UNSAFE_COUNT] = ...@@ -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_IGNORE_SELECT,
ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT, ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT,
ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC, ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC,
ER_BINLOG_UNSAFE_UPDATE_IGNORE ER_BINLOG_UNSAFE_UPDATE_IGNORE,
ER_BINLOG_UNSAFE_INSERT_TWO_KEYS
}; };
......
...@@ -1323,6 +1323,12 @@ class Query_tables_list ...@@ -1323,6 +1323,12 @@ class Query_tables_list
*/ */
BINLOG_STMT_UNSAFE_UPDATE_IGNORE, 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. */ /* The last element of this enumeration type. */
BINLOG_STMT_UNSAFE_COUNT BINLOG_STMT_UNSAFE_COUNT
}; };
......
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