From: Jorgen Loland Date: February 21 2011 3:49pm Subject: bzr commit into mysql-5.5 branch (jorgen.loland:3352) Bug#11762751 Bug#11764529 List-Archive: http://lists.mysql.com/commits/131785 X-Bug: 11762751,11764529 Message-Id: <20110221154906.BDD31B8B@atum21.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============7212993780391402677==" --===============7212993780391402677== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///export/home/jl208045/mysql/mysql-5.5/ based on revid:magnus.blaudd@stripped 3352 Jorgen Loland 2011-02-21 BUG#11762751: UPDATE STATEMENT THROWS AN ERROR, BUT STILL UPDATES THE TABLE ENTRIES (formerly 55385) BUG#11764529: MULTI UPDATE+INNODB REPORTS ER_KEY_NOT_FOUND IF A TABLE IS UPDATED TWICE (formerly 57373) If multiple-table update updates a row through two aliases and the first update physically moves the row, the second update will fail to locate the row. This results in different errors depending on storage engine: * MyISAM: Got error 134 from storage engine * InnoDB: Can't find record in 'tbl' None of these errors accurately describe the problem. Furthermore, since MyISAM is non-transactional, the update executed first will be performed while the second will not. In addition, for two equal multiple-table update statements, one could succeed and the other fail based on whether or not the record actually moved or not. This was inconsistent. Two update operations may physically move a row: 1) Update of a column in a clustered primary key 2) Update of a column used to calculate which partition the row belongs to BUG#11764529 is about case 1) above, BUG#11762751 was about case 2). The fix for these bugs is to return with an error if multiple-table update is about to: a) Update a table through multiple aliases, and b) Perform an update that may physically more the row in at least one of these aliases This avoids * partial updates as described for MyISAM above, * provides the same error message that describes the actual problem for all SEs * inconsistent behavior where a statement fails or succeeds based on e.g. the partitioning algorithm of the table. @ mysql-test/r/multi_update.result Add test for bug#57373 @ mysql-test/r/multi_update_innodb.result Add test for bug#57373 @ mysql-test/r/partition.result Add test for bug#55385 @ mysql-test/t/multi_update.test Add test for bug#57373 @ mysql-test/t/multi_update_innodb.test Add test for bug#57373 @ mysql-test/t/partition.test Add test for bug#55385 @ sql/handler.cc Translate handler error HA_ERR_RECORD_DELETED to server error @ sql/share/errmsg-utf8.txt New error message for multi-table update where the same table is updated multiple times. @ sql/sql_update.cc Add function unsafe_key_update() added: mysql-test/r/multi_update_innodb.result mysql-test/t/multi_update_innodb.test modified: mysql-test/r/multi_update.result mysql-test/r/partition.result mysql-test/t/multi_update.test mysql-test/t/partition.test sql/handler.cc sql/share/errmsg-utf8.txt sql/sql_update.cc === modified file 'mysql-test/r/multi_update.result' --- a/mysql-test/r/multi_update.result 2010-12-14 10:46:00 +0000 +++ b/mysql-test/r/multi_update.result 2011-02-21 15:49:03 +0000 @@ -680,4 +680,21 @@ Warnings: Warning 1292 Truncated incorrect datetime value: '1' DROP FUNCTION f1; DROP TABLE t1; +# +# BUG#57373: Multi update+InnoDB reports ER_KEY_NOT_FOUND if a +# table is updated twice +# +CREATE TABLE t1( +pk INT, +a INT, +PRIMARY KEY (pk) +) ENGINE=MyISAM; +INSERT INTO t1 VALUES (0,0); +UPDATE t1 AS A, t1 AS B SET A.pk = 1, B.a = 2; + +# Should be (1,2) +SELECT * FROM t1; +pk a +1 2 +DROP TABLE t1; end of tests === added file 'mysql-test/r/multi_update_innodb.result' --- a/mysql-test/r/multi_update_innodb.result 1970-01-01 00:00:00 +0000 +++ b/mysql-test/r/multi_update_innodb.result 2011-02-21 15:49:03 +0000 @@ -0,0 +1,29 @@ +# +# BUG#57373: Multi update+InnoDB reports ER_KEY_NOT_FOUND if a +# table is updated twice +# +CREATE TABLE t1( +pk INT, +a INT, +b INT, +PRIMARY KEY (pk) +) ENGINE=InnoDB; +INSERT INTO t1 VALUES (0,0,0); +UPDATE t1 AS A, t1 AS B SET A.pk = 1, B.a = 2; +ERROR HY000: Primary key/partition key update is not allowed since the table is updated both as 'A' and 'B'. +SELECT * FROM t1; +pk a b +0 0 0 +CREATE VIEW v1 AS SELECT * FROM t1; +UPDATE v1 AS A, t1 AS B SET A.pk = 1, B.a = 2; +ERROR HY000: Primary key/partition key update is not allowed since the table is updated both as 'A' and 'B'. +SELECT * FROM t1; +pk a b +0 0 0 +UPDATE t1 AS A, t1 AS B SET A.a = 1, B.b = 2; +# Should be (0,1,2) +SELECT * FROM t1; +pk a b +0 1 2 +DROP VIEW v1; +DROP TABLE t1; === modified file 'mysql-test/r/partition.result' --- a/mysql-test/r/partition.result 2011-01-10 14:08:31 +0000 +++ b/mysql-test/r/partition.result 2011-02-21 15:49:03 +0000 @@ -2264,3 +2264,51 @@ INSERT INTO t1 VALUES(0); DROP TABLE t1; SET GLOBAL myisam_use_mmap=default; End of 5.1 tests +# +# BUG#55385: UPDATE statement throws an error, but still updates +# the table entries +CREATE TABLE t1_part ( +partkey int, +nokey int +) PARTITION BY LINEAR HASH(partkey) PARTITIONS 3; +INSERT INTO t1_part VALUES (1, 1) , (10, 10); +CREATE VIEW v1 AS SELECT * FROM t1_part; + +# Should be (1,1),(10,10) +SELECT * FROM t1_part; +partkey nokey +1 1 +10 10 + +# Case 1 +# Update is refused because partitioning key is updated +UPDATE t1_part AS A NATURAL JOIN t1_part B SET A.partkey = 2, B.nokey = 3; +ERROR HY000: Primary key/partition key update is not allowed since the table is updated both as 'A' and 'B'. +UPDATE t1_part AS A NATURAL JOIN t1_part B SET A.nokey = 2, B.partkey = 3; +ERROR HY000: Primary key/partition key update is not allowed since the table is updated both as 'A' and 'B'. + +# Case 2 +# Like 1, but partition accessed through a view +UPDATE t1_part AS A NATURAL JOIN v1 as B SET A.nokey = 2 , B.partkey = 3; +ERROR HY000: Primary key/partition key update is not allowed since the table is updated both as 'A' and 'B'. +UPDATE v1 AS A NATURAL JOIN t1_part as B SET A.nokey = 2 , B.partkey = 3; +ERROR HY000: Primary key/partition key update is not allowed since the table is updated both as 'A' and 'B'. + +# Should be (1,1),(10,10) +SELECT * FROM t1_part; +partkey nokey +1 1 +10 10 + +# Case 3 +# Update is accepted because partitioning key is not updated +UPDATE t1_part AS A NATURAL JOIN t1_part B SET A.nokey = 2 , B.nokey = 3; + +# Should be (1,3),(10,3) +SELECT * FROM t1_part; +partkey nokey +1 3 +10 3 + +DROP VIEW v1; +DROP TABLE t1_part; === modified file 'mysql-test/t/multi_update.test' --- a/mysql-test/t/multi_update.test 2010-12-14 10:46:00 +0000 +++ b/mysql-test/t/multi_update.test 2011-02-21 15:49:03 +0000 @@ -683,4 +683,24 @@ UPDATE (SELECT 1 FROM t1 WHERE f1 = (SEL DROP FUNCTION f1; DROP TABLE t1; +--echo # +--echo # BUG#57373: Multi update+InnoDB reports ER_KEY_NOT_FOUND if a +--echo # table is updated twice +--echo # + +# Results differ between storage engines. +# See multi_update_innodb.test for the InnoDB variant of this test +CREATE TABLE t1( + pk INT, + a INT, + PRIMARY KEY (pk) +) ENGINE=MyISAM; + +INSERT INTO t1 VALUES (0,0); +UPDATE t1 AS A, t1 AS B SET A.pk = 1, B.a = 2; +--echo +--echo # Should be (1,2) +SELECT * FROM t1; +DROP TABLE t1; + --echo end of tests === added file 'mysql-test/t/multi_update_innodb.test' --- a/mysql-test/t/multi_update_innodb.test 1970-01-01 00:00:00 +0000 +++ b/mysql-test/t/multi_update_innodb.test 2011-02-21 15:49:03 +0000 @@ -0,0 +1,33 @@ +--source include/have_innodb.inc + +--echo # +--echo # BUG#57373: Multi update+InnoDB reports ER_KEY_NOT_FOUND if a +--echo # table is updated twice +--echo # + +# Results differ between storage engines. +# See multi_update.test for the MyISAM variant of this test +CREATE TABLE t1( + pk INT, + a INT, + b INT, + PRIMARY KEY (pk) +) ENGINE=InnoDB; + +INSERT INTO t1 VALUES (0,0,0); +--error ER_MULTI_UPDATE_KEY_CONFLICT +UPDATE t1 AS A, t1 AS B SET A.pk = 1, B.a = 2; +SELECT * FROM t1; + +CREATE VIEW v1 AS SELECT * FROM t1; +--error ER_MULTI_UPDATE_KEY_CONFLICT +UPDATE v1 AS A, t1 AS B SET A.pk = 1, B.a = 2; +SELECT * FROM t1; + +UPDATE t1 AS A, t1 AS B SET A.a = 1, B.b = 2; +--echo # Should be (0,1,2) +SELECT * FROM t1; + +DROP VIEW v1; +DROP TABLE t1; + === modified file 'mysql-test/t/partition.test' --- a/mysql-test/t/partition.test 2011-01-10 14:08:31 +0000 +++ b/mysql-test/t/partition.test 2011-02-21 15:49:03 +0000 @@ -2267,3 +2267,53 @@ DROP TABLE t1; SET GLOBAL myisam_use_mmap=default; --echo End of 5.1 tests + +--echo # +--echo # BUG#55385: UPDATE statement throws an error, but still updates +--echo # the table entries + +CREATE TABLE t1_part ( + partkey int, + nokey int +) PARTITION BY LINEAR HASH(partkey) PARTITIONS 3; + +INSERT INTO t1_part VALUES (1, 1) , (10, 10); +CREATE VIEW v1 AS SELECT * FROM t1_part; + +--echo +--echo # Should be (1,1),(10,10) +SELECT * FROM t1_part; + +--echo +--echo # Case 1 +--echo # Update is refused because partitioning key is updated +--error ER_MULTI_UPDATE_KEY_CONFLICT +UPDATE t1_part AS A NATURAL JOIN t1_part B SET A.partkey = 2, B.nokey = 3; +--error ER_MULTI_UPDATE_KEY_CONFLICT +UPDATE t1_part AS A NATURAL JOIN t1_part B SET A.nokey = 2, B.partkey = 3; + +--echo +--echo # Case 2 +--echo # Like 1, but partition accessed through a view +--error ER_MULTI_UPDATE_KEY_CONFLICT +UPDATE t1_part AS A NATURAL JOIN v1 as B SET A.nokey = 2 , B.partkey = 3; +--error ER_MULTI_UPDATE_KEY_CONFLICT +UPDATE v1 AS A NATURAL JOIN t1_part as B SET A.nokey = 2 , B.partkey = 3; + +--echo +--echo # Should be (1,1),(10,10) +SELECT * FROM t1_part; + +--echo +--echo # Case 3 +--echo # Update is accepted because partitioning key is not updated +UPDATE t1_part AS A NATURAL JOIN t1_part B SET A.nokey = 2 , B.nokey = 3; + +--echo +--echo # Should be (1,3),(10,3) +SELECT * FROM t1_part; + +--echo +# Cleanup +DROP VIEW v1; +DROP TABLE t1_part; === modified file 'sql/handler.cc' --- a/sql/handler.cc 2011-02-08 15:47:33 +0000 +++ b/sql/handler.cc 2011-02-21 15:49:03 +0000 @@ -2700,6 +2700,7 @@ void handler::print_error(int error, myf break; case HA_ERR_KEY_NOT_FOUND: case HA_ERR_NO_ACTIVE_RECORD: + case HA_ERR_RECORD_DELETED: case HA_ERR_END_OF_FILE: textno=ER_KEY_NOT_FOUND; break; === modified file 'sql/share/errmsg-utf8.txt' --- a/sql/share/errmsg-utf8.txt 2011-02-02 11:54:49 +0000 +++ b/sql/share/errmsg-utf8.txt 2011-02-21 15:49:03 +0000 @@ -6394,3 +6394,6 @@ ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MA ER_STMT_CACHE_FULL eng "Multi-row statements required more than 'max_binlog_stmt_cache_size' bytes of storage; increase this mysqld variable and try again" + +ER_MULTI_UPDATE_KEY_CONFLICT + eng "Primary key/partition key update is not allowed since the table is updated both as '%-.192s' and '%-.192s'." === modified file 'sql/sql_update.cc' --- a/sql/sql_update.cc 2010-12-29 00:26:31 +0000 +++ b/sql/sql_update.cc 2011-02-21 15:49:03 +0000 @@ -998,6 +998,98 @@ static table_map get_table_map(Listnext_leaf) + { + if (tl->table->map & tables_for_update) + { + TABLE *table1= tl->table; + bool primkey_clustered= (table1->file->primary_key_is_clustered() && + table1->s->primary_key != MAX_KEY); + + bool table_partitioned= false; +#ifdef WITH_PARTITION_STORAGE_ENGINE + table_partitioned= (table1->part_info != NULL); +#endif + + if (!table_partitioned && !primkey_clustered) + continue; + + for (TABLE_LIST* tl2= tl->next_leaf; tl2 ; tl2= tl2->next_leaf) + { + /* + Look at "next" tables only since all previous tables have + already been checked + */ + TABLE *table2= tl2->table; + if (table2->map & tables_for_update && table1->s == table2->s) + { + // A table is updated through two aliases + if (table_partitioned && + (partition_key_modified(table1, table1->write_set) || + partition_key_modified(table2, table2->write_set))) + { + // Partitioned key is updated + my_error(ER_MULTI_UPDATE_KEY_CONFLICT, MYF(0), + tl->belong_to_view ? tl->belong_to_view->alias + : tl->alias, + tl2->belong_to_view ? tl2->belong_to_view->alias + : tl2->alias); + return true; + } + + if (primkey_clustered && + (bitmap_is_set(table1->write_set, table1->s->primary_key) || + bitmap_is_set(table2->write_set, table2->s->primary_key))) + { + // Clustered primary key is updated + my_error(ER_MULTI_UPDATE_KEY_CONFLICT, MYF(0), + tl->belong_to_view ? tl->belong_to_view->alias + : tl->alias, + tl2->belong_to_view ? tl2->belong_to_view->alias + : tl2->alias); + return true; + } + } + } + } + } + return false; +} + /* make update specific preparation and checks after opening tables @@ -1077,10 +1169,14 @@ int mysql_multi_update_prepare(THD *thd) thd->table_map_for_update= tables_for_update= get_table_map(fields); + leaves= lex->select_lex.leaf_tables; + + if (unsafe_key_update(leaves, tables_for_update)) + DBUG_RETURN(true); + /* Setup timestamp handling and locking mode */ - leaves= lex->select_lex.leaf_tables; for (tl= leaves; tl; tl= tl->next_leaf) { TABLE *table= tl->table; --===============7212993780391402677== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/jorgen.loland@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: jorgen.loland@stripped\ # lbyzzk52hiht7i5j # target_branch: file:///export/home/jl208045/mysql/mysql-5.5/ # testament_sha1: b1414e57b3ceb654ef5ba0bfaa5145a4e31e7fd7 # timestamp: 2011-02-21 16:49:06 +0100 # source_branch: file:///export/home/jl208045/mysql/mysql-trunk-55385/ # base_revision_id: magnus.blaudd@stripped\ # 47jpb70yoy45f43j # # Begin bundle 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 --===============7212993780391402677==--