From: Martin Hansson Date: October 7 2010 10:01am Subject: bzr commit into mysql-5.5-bugteam branch (martin.hansson:3215) Bug#56423 List-Archive: http://lists.mysql.com/commits/120221 X-Bug: 56423 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============1858609684==" --===============1858609684== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///data0/martin/bzr/bug56423/5.5bt/ based on revid:magnus.blaudd@stripped 3215 Martin Hansson 2010-10-07 Bug#56423: Different count with SELECT and CREATE SELECT queries This is the 5.5 version of the fix. The 5.1 version was too complicated to merge and was null merged. This is a regression from the fix for bug no 38999. A storage engine capable of reading only a subset of a table's columns updates corresponding bits in the read buffer to signal that it has read NULL values for the corresponding columns. It cannot, and should not, update any other bits. Bug no 38999 occurred because the implementation of UPDATE statements compare the NULL bits using memcmp, inadvertently comparing bits that were never requested from the storage engine. The regression was caused by the storage engine trying to alleviate the situation by writing to all NULL bits, even those that it had no knowledge of. This has devastating effects for the index merge algorithm, which relies on all NULL bits, except those explicitly requested, being left unchanged. The fix reverts the fix for bug no 38999 in both InnoDB and InnoDB plugin and changes the server's method of comparing records. For engines that always read entire rows, we proceed as usual. For engines capable of reading only select columns, the record buffers are now compared on a column by column basis. An assertion was also added so that non comparable buffers are never read. Some relevant copy-pasted code was also consolidated in a new function. modified: mysql-test/include/index_merge2.inc mysql-test/r/index_merge_myisam.result sql/sql_insert.cc sql/sql_update.cc sql/sql_update.h storage/innobase/row/row0sel.c === modified file 'mysql-test/include/index_merge2.inc' --- a/mysql-test/include/index_merge2.inc 2010-10-07 08:24:44 +0000 +++ b/mysql-test/include/index_merge2.inc 2010-10-07 10:01:51 +0000 @@ -351,3 +351,115 @@ explain select * from t1 where (key3 > 3 select * from t1 where (key3 > 30 and key3<35) or (key2 >32 and key2 < 40); drop table t1; +--echo # +--echo # Bug#56423: Different count with SELECT and CREATE SELECT queries +--echo # + +CREATE TABLE t1 ( + a INT, + b INT, + c INT, + d INT, + PRIMARY KEY (a), + KEY (c), + KEY bd (b,d) +); + +INSERT INTO t1 VALUES +(1, 0, 1, 0), +(2, 1, 1, 1), +(3, 1, 1, 1), +(4, 0, 1, 1); + +EXPLAIN +SELECT a +FROM t1 +WHERE c = 1 AND b = 1 AND d = 1; + +CREATE TABLE t2 ( a INT ) +SELECT a +FROM t1 +WHERE c = 1 AND b = 1 AND d = 1; + +SELECT * FROM t2; + +DROP TABLE t1, t2; + +CREATE TABLE t1( a INT, b INT, KEY(a), KEY(b) ); +INSERT INTO t1 VALUES (1, 2), (1, 2), (1, 2), (1, 2); +SELECT * FROM t1 FORCE INDEX(a, b) WHERE a = 1 AND b = 2; + +DROP TABLE t1; + +--echo # Code coverage of fix. +CREATE TABLE t1 ( a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, b INT); +INSERT INTO t1 (b) VALUES (1); +UPDATE t1 SET b = 2 WHERE a = 1; +SELECT * FROM t1; + +CREATE TABLE t2 ( a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, b VARCHAR(1) ); +INSERT INTO t2 (b) VALUES ('a'); +UPDATE t2 SET b = 'b' WHERE a = 1; +SELECT * FROM t2; + +DROP TABLE t1, t2; + +# The test was inactive for InnoDB at the time of pushing. The following is +# expected result for the Bug#56423 test. It can be uncommented and pasted +# into result file when reactivating the test. + +## +## Bug#56423: Different count with SELECT and CREATE SELECT queries +## +#CREATE TABLE t1 ( +#a INT, +#b INT, +#c INT, +#d INT, +#PRIMARY KEY (a), +#KEY (c), +#KEY bd (b,d) +#); +#INSERT INTO t1 VALUES +#(1, 0, 1, 0), +#(2, 1, 1, 1), +#(3, 1, 1, 1), +#(4, 0, 1, 1); +#EXPLAIN +#SELECT a +#FROM t1 +#WHERE c = 1 AND b = 1 AND d = 1; +#id select_type table type possible_keys key key_len ref rows Extra +#1 SIMPLE t1 ref c,bd bd 10 const,const 2 Using where +#CREATE TABLE t2 ( a INT ) +#SELECT a +#FROM t1 +#WHERE c = 1 AND b = 1 AND d = 1; +#SELECT * FROM t2; +#a +#2 +#3 +#DROP TABLE t1, t2; +#CREATE TABLE t1( a INT, b INT, KEY(a), KEY(b) ); +#INSERT INTO t1 VALUES (1, 2), (1, 2), (1, 2), (1, 2); +#SELECT * FROM t1 FORCE INDEX(a, b) WHERE a = 1 AND b = 2; +#a b +#1 2 +#1 2 +#1 2 +#1 2 +#DROP TABLE t1; +## Code coverage of fix. +#CREATE TABLE t1 ( a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, b INT); +#INSERT INTO t1 (b) VALUES (1); +#UPDATE t1 SET b = 2 WHERE a = 1; +#SELECT * FROM t1; +#a b +#1 2 +#CREATE TABLE t2 ( a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, b VARCHAR(1) ); +#INSERT INTO t2 (b) VALUES ('a'); +#UPDATE t2 SET b = 'b' WHERE a = 1; +#SELECT * FROM t2; +#a b +#1 b +#DROP TABLE t1, t2; === modified file 'mysql-test/r/index_merge_myisam.result' --- a/mysql-test/r/index_merge_myisam.result 2010-10-07 08:24:44 +0000 +++ b/mysql-test/r/index_merge_myisam.result 2010-10-07 10:01:51 +0000 @@ -1156,6 +1156,61 @@ key1 key2 key3 38 38 38 39 39 39 drop table t1; +# +# Bug#56423: Different count with SELECT and CREATE SELECT queries +# +CREATE TABLE t1 ( +a INT, +b INT, +c INT, +d INT, +PRIMARY KEY (a), +KEY (c), +KEY bd (b,d) +); +INSERT INTO t1 VALUES +(1, 0, 1, 0), +(2, 1, 1, 1), +(3, 1, 1, 1), +(4, 0, 1, 1); +EXPLAIN +SELECT a +FROM t1 +WHERE c = 1 AND b = 1 AND d = 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref c,bd bd 10 const,const 2 Using where +CREATE TABLE t2 ( a INT ) +SELECT a +FROM t1 +WHERE c = 1 AND b = 1 AND d = 1; +SELECT * FROM t2; +a +2 +3 +DROP TABLE t1, t2; +CREATE TABLE t1( a INT, b INT, KEY(a), KEY(b) ); +INSERT INTO t1 VALUES (1, 2), (1, 2), (1, 2), (1, 2); +SELECT * FROM t1 FORCE INDEX(a, b) WHERE a = 1 AND b = 2; +a b +1 2 +1 2 +1 2 +1 2 +DROP TABLE t1; +# Code coverage of fix. +CREATE TABLE t1 ( a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, b INT); +INSERT INTO t1 (b) VALUES (1); +UPDATE t1 SET b = 2 WHERE a = 1; +SELECT * FROM t1; +a b +1 2 +CREATE TABLE t2 ( a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, b VARCHAR(1) ); +INSERT INTO t2 (b) VALUES ('a'); +UPDATE t2 SET b = 'b' WHERE a = 1; +SELECT * FROM t2; +a b +1 b +DROP TABLE t1, t2; #---------------- 2-sweeps read Index merge test 2 ------------------------------- SET SESSION STORAGE_ENGINE = MyISAM; drop table if exists t1; === modified file 'sql/sql_insert.cc' --- a/sql/sql_insert.cc 2010-10-07 08:24:44 +0000 +++ b/sql/sql_insert.cc 2010-10-07 10:01:51 +0000 @@ -1620,9 +1620,7 @@ int write_record(THD *thd, TABLE *table, table->file->adjust_next_insert_id_after_explicit_value( table->next_number_field->val_int()); info->touched++; - if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ && - !bitmap_is_subset(table->write_set, table->read_set)) || - compare_record(table)) + if (!records_are_comparable(table) || compare_records(table)) { if ((error=table->file->ha_update_row(table->record[1], table->record[0])) && === modified file 'sql/sql_update.cc' --- a/sql/sql_update.cc 2010-10-07 08:24:44 +0000 +++ b/sql/sql_update.cc 2010-10-07 10:01:51 +0000 @@ -42,11 +42,68 @@ // mysql_handle_derived, // mysql_derived_filling -/* Return 0 if row hasn't changed */ -bool compare_record(TABLE *table) +/** + True if the table's input and output record buffers are comparable using + compare_records(TABLE*). + */ +bool records_are_comparable(const TABLE *table) { + return ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) == 0) || + bitmap_is_subset(table->write_set, table->read_set); +} + + +/** + Compares the input and outbut record buffers of the table to see if a row + has changed. The algorithm iterates over updated columns and if they are + nullable compares NULL bits in the buffer before comparing actual + data. Special care must be taken to compare only the relevant NULL bits and + mask out all others as they may be undefined. The storage engine will not + and should not touch them. + + @param table The table to evaluate. + + @return true if row has changed. + @return false otherwise. +*/ +bool compare_records(const TABLE *table) { + DBUG_ASSERT(records_are_comparable(table)); + + if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) != 0) + { + /* + Storage engine may not have read all columns of the record. Fields + (including NULL bits) not in the write_set may not have been read and + can therefore not be compared. + */ + for (Field **ptr= table->field ; *ptr != NULL; ptr++) + { + Field *field= *ptr; + if (bitmap_is_set(table->write_set, field->field_index)) + { + if (field->real_maybe_null()) + { + uchar null_byte_index= field->null_ptr - table->record[0]; + + if (((table->record[0][null_byte_index]) & field->null_bit) != + ((table->record[1][null_byte_index]) & field->null_bit)) + return TRUE; + } + if (field->cmp_binary_offset(table->s->rec_buff_length)) + return TRUE; + } + } + return FALSE; + } + + /* + The storage engine has read all columns, so it's safe to compare all bits + including those not in the write_set. This is cheaper than the field-by-field + comparison done above. + */ if (table->s->blob_fields + table->s->varchar_fields == 0) + // Fixed-size record: do bitwise comparison of the records return cmp_record(table,record[1]); /* Compare null bits */ if (memcmp(table->null_flags, @@ -204,7 +261,6 @@ int mysql_update(THD *thd, bool using_limit= limit != HA_POS_ERROR; bool safe_update= test(thd->variables.option_bits & OPTION_SAFE_UPDATES); bool used_key_is_modified= FALSE, transactional_table, will_batch; - bool can_compare_record; int res; int error, loc_error; uint used_index, dup_key_found; @@ -579,15 +635,6 @@ int mysql_update(THD *thd, if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) table->prepare_for_position(); - /* - We can use compare_record() to optimize away updates if - the table handler is returning all columns OR if - if all updated columns are read - */ - can_compare_record= (!(table->file->ha_table_flags() & - HA_PARTIAL_COLUMN_READ) || - bitmap_is_subset(table->write_set, table->read_set)); - while (!(error=info.read_record(&info)) && !thd->killed) { thd->examined_row_count++; @@ -605,7 +652,7 @@ int mysql_update(THD *thd, found++; - if (!can_compare_record || compare_record(table)) + if (!records_are_comparable(table) || compare_records(table)) { if ((res= table_list->view_check_option(thd, ignore)) != VIEW_CHECK_OK) @@ -1645,18 +1692,8 @@ bool multi_update::send_data(List if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED)) continue; - /* - We can use compare_record() to optimize away updates if - the table handler is returning all columns OR if - if all updated columns are read - */ if (table == table_to_update) { - bool can_compare_record; - can_compare_record= (!(table->file->ha_table_flags() & - HA_PARTIAL_COLUMN_READ) || - bitmap_is_subset(table->write_set, - table->read_set)); table->status|= STATUS_UPDATED; store_record(table,record[1]); if (fill_record_n_invoke_before_triggers(thd, *fields_for_table[offset], @@ -1671,7 +1708,7 @@ bool multi_update::send_data(List */ table->auto_increment_field_not_null= FALSE; found++; - if (!can_compare_record || compare_record(table)) + if (!records_are_comparable(table) || compare_records(table)) { int error; if ((error= cur_table->view_check_option(thd, ignore)) != @@ -1860,7 +1897,6 @@ int multi_update::do_updates() DBUG_RETURN(0); for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local) { - bool can_compare_record; uint offset= cur_table->shared; table = cur_table->table; @@ -1897,11 +1933,6 @@ int multi_update::do_updates() if ((local_error = tmp_table->file->ha_rnd_init(1))) goto err; - can_compare_record= (!(table->file->ha_table_flags() & - HA_PARTIAL_COLUMN_READ) || - bitmap_is_subset(table->write_set, - table->read_set)); - for (;;) { if (thd->killed && trans_safe) @@ -1942,7 +1973,7 @@ int multi_update::do_updates() TRG_ACTION_BEFORE, TRUE)) goto err2; - if (!can_compare_record || compare_record(table)) + if (!records_are_comparable(table) || compare_records(table)) { int error; if ((error= cur_table->view_check_option(thd, ignore)) != === modified file 'sql/sql_update.h' --- a/sql/sql_update.h 2010-04-12 13:17:37 +0000 +++ b/sql/sql_update.h 2010-10-07 10:01:51 +0000 @@ -38,6 +38,7 @@ bool mysql_multi_update(THD *thd, TABLE_ enum enum_duplicates handle_duplicates, bool ignore, SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex, multi_update **result); -bool compare_record(TABLE *table); +bool records_are_comparable(const TABLE *table); +bool compare_records(const TABLE *table); #endif /* SQL_UPDATE_INCLUDED */ === modified file 'storage/innobase/row/row0sel.c' --- a/storage/innobase/row/row0sel.c 2010-10-07 08:24:44 +0000 +++ b/storage/innobase/row/row0sel.c 2010-10-07 10:01:51 +0000 @@ -2688,12 +2688,6 @@ row_sel_store_mysql_rec( prebuilt->blob_heap = NULL; } - /* init null bytes with default values as they might be - left uninitialized in some cases and these uninited bytes - might be copied into mysql record buffer that leads to - valgrind warnings */ - memcpy(mysql_rec, prebuilt->default_rec, prebuilt->null_bitmap_len); - for (i = 0; i < prebuilt->n_template; i++) { templ = prebuilt->mysql_template + i; --===============1858609684== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/martin.hansson@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: martin.hansson@stripped\ # s0zsldqupugpdi64 # target_branch: file:///data0/martin/bzr/bug56423/5.5bt/ # testament_sha1: 6c37e37ac6acf683a29dcac520704dbcfc77bdbf # timestamp: 2010-10-07 12:01:57 +0200 # base_revision_id: magnus.blaudd@stripped\ # a31rwn22pvg1yijq # # Begin bundle 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 l4QqAA== --===============1858609684==--