From eb1f21f8b656e858cfef3daa72d9a5bee7951ce2 Mon Sep 17 00:00:00 2001 From: "gshchepa/uchum@gleb.loc" <> Date: Tue, 8 May 2007 00:24:25 +0500 Subject: [PATCH] Fixed bug #27954. This bug affects multi-row INSERT ... ON DUPLICATE into table with PRIMARY KEY of AUTO_INCREMENT field and some additional UNIQUE indices. If the first row in multi-row INSERT contains duplicated values of UNIQUE indices, then following rows of multi-row INSERT (with either duplicated or unique key field values) may me applied to _arbitrary_ records of table as updates. This bug was introduced in 5.0. Related code was widely rewritten in 5.1, and 5.1 is already free of this problem. 4.1 was not affected too. When updating the row during INSERT ON DUPLICATE KEY UPDATE, we called restore_auto_increment(), which set next_insert_id back to 0, but we forgot to set clear_next_insert_id back to 0. restore_auto_increment() function has been fixed. --- mysql-test/r/insert_update.result | 22 ++++++++++++++++++++++ mysql-test/t/insert_update.test | 17 +++++++++++++++++ sql/handler.cc | 7 +++++++ sql/sql_class.h | 4 ++++ 4 files changed, 50 insertions(+) diff --git a/mysql-test/r/insert_update.result b/mysql-test/r/insert_update.result index fd70fcb9084..4a3e87d9d48 100644 --- a/mysql-test/r/insert_update.result +++ b/mysql-test/r/insert_update.result @@ -336,3 +336,25 @@ id f1 0 test1 DROP TABLE t1; SET SQL_MODE=''; +CREATE TABLE t1 ( +id INT AUTO_INCREMENT PRIMARY KEY, +c1 CHAR(1) UNIQUE KEY, +cnt INT DEFAULT 1 +); +INSERT INTO t1 (c1) VALUES ('A'), ('B'), ('C'); +SELECT * FROM t1; +id c1 cnt +1 A 1 +2 B 1 +3 C 1 +INSERT INTO t1 (c1) VALUES ('A'), ('X'), ('Y'), ('Z') +ON DUPLICATE KEY UPDATE cnt=cnt+1; +SELECT * FROM t1; +id c1 cnt +1 A 2 +2 B 1 +3 C 1 +4 X 1 +5 Y 1 +6 Z 1 +DROP TABLE t1; diff --git a/mysql-test/t/insert_update.test b/mysql-test/t/insert_update.test index 76df4502769..0e199dab4bd 100644 --- a/mysql-test/t/insert_update.test +++ b/mysql-test/t/insert_update.test @@ -247,3 +247,20 @@ REPLACE INTO t1 VALUES (0,"test1",null); SELECT id, f1 FROM t1; DROP TABLE t1; SET SQL_MODE=''; + +# +# Bug#27954: multi-row INSERT ... ON DUPLICATE with duplicated +# row at the first place into table with AUTO_INCREMENT and +# additional UNIQUE key. +# +CREATE TABLE t1 ( + id INT AUTO_INCREMENT PRIMARY KEY, + c1 CHAR(1) UNIQUE KEY, + cnt INT DEFAULT 1 +); +INSERT INTO t1 (c1) VALUES ('A'), ('B'), ('C'); +SELECT * FROM t1; +INSERT INTO t1 (c1) VALUES ('A'), ('X'), ('Y'), ('Z') + ON DUPLICATE KEY UPDATE cnt=cnt+1; +SELECT * FROM t1; +DROP TABLE t1; diff --git a/sql/handler.cc b/sql/handler.cc index c6c31593a5f..867ac7ff778 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1685,7 +1685,14 @@ void handler::restore_auto_increment() { THD *thd= table->in_use; if (thd->next_insert_id) + { thd->next_insert_id= thd->prev_insert_id; + if (thd->next_insert_id == 0) + { + /* we didn't generate a value, engine will be called again */ + thd->clear_next_insert_id= 0; + } + } } diff --git a/sql/sql_class.h b/sql/sql_class.h index 12d7cb2368f..7dd46e2efe7 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1431,6 +1431,10 @@ public: */ bool insert_id_used; + /* + clear_next_insert_id is set if engine was called at least once + for this statement to generate auto_increment value. + */ bool clear_next_insert_id; /* for IS NULL => = last_insert_id() fix in remove_eq_conds() */ bool substitute_null_with_insert_id;