From: Martin Hansson Date: October 7 2010 8:15am Subject: bzr push into mysql-5.1-bugteam branch (martin.hansson:3522 to 3523) Bug#56423 List-Archive: http://lists.mysql.com/commits/120194 X-Bug: 56423 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============1680364335==" --===============1680364335== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline 3523 Martin Hansson 2010-10-07 Bug#56423: Different count with SELECT and CREATE SELECT queries 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_innodb.result mysql-test/r/index_merge_myisam.result sql/mysql_priv.h sql/sql_insert.cc sql/sql_update.cc storage/innobase/row/row0sel.c storage/innodb_plugin/row/row0sel.c 3522 Georgi Kodinov 2010-10-05 [merge] merge modified: include/sha1.h mysys/sha1.c === modified file 'mysql-test/include/index_merge2.inc' --- a/mysql-test/include/index_merge2.inc 2006-09-14 19:44:17 +0000 +++ b/mysql-test/include/index_merge2.inc 2010-10-07 08:13:11 +0000 @@ -343,3 +343,55 @@ 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; === modified file 'mysql-test/r/index_merge_innodb.result' --- a/mysql-test/r/index_merge_innodb.result 2010-09-16 12:13:53 +0000 +++ b/mysql-test/r/index_merge_innodb.result 2010-10-07 08:13:11 +0000 @@ -324,6 +324,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 index_merge c,bd c,bd 5,10 NULL 1 Using intersect(c,bd); Using where; Using index +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 = InnoDB; drop table if exists t1; === modified file 'mysql-test/r/index_merge_myisam.result' --- a/mysql-test/r/index_merge_myisam.result 2010-09-16 12:13:53 +0000 +++ b/mysql-test/r/index_merge_myisam.result 2010-10-07 08:13:11 +0000 @@ -1158,6 +1158,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/mysql_priv.h' --- a/sql/mysql_priv.h 2010-07-29 03:00:57 +0000 +++ b/sql/mysql_priv.h 2010-10-07 08:13:11 +0000 @@ -1047,7 +1047,8 @@ bool dispatch_command(enum enum_server_c char* packet, uint packet_length); void log_slow_statement(THD *thd); bool check_dup(const char *db, const char *name, TABLE_LIST *tables); -bool compare_record(TABLE *table); +bool records_are_comparable(const TABLE *table); +bool compare_records(const TABLE *table); bool append_file_to_dir(THD *thd, const char **filename_ptr, const char *table_name); void wait_while_table_is_used(THD *thd, TABLE *table, === modified file 'sql/sql_insert.cc' --- a/sql/sql_insert.cc 2010-08-20 09:09:17 +0000 +++ b/sql/sql_insert.cc 2010-10-07 08:13:11 +0000 @@ -1483,9 +1483,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-08-09 11:39:59 +0000 +++ b/sql/sql_update.cc 2010-10-07 08:13:11 +0000 @@ -25,11 +25,68 @@ #include "sql_trigger.h" #include "debug_sync.h" -/* 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, @@ -186,7 +243,6 @@ int mysql_update(THD *thd, bool using_limit= limit != HA_POS_ERROR; bool safe_update= test(thd->options & OPTION_SAFE_UPDATES); bool used_key_is_modified, transactional_table, will_batch; - bool can_compare_record; int res; int error, loc_error; uint used_index= MAX_KEY, dup_key_found; @@ -575,15 +631,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++; @@ -601,7 +648,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) @@ -1695,18 +1742,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], @@ -1721,7 +1758,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)) != @@ -1908,7 +1945,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; @@ -1945,11 +1981,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) @@ -1990,7 +2021,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 'storage/innobase/row/row0sel.c' --- a/storage/innobase/row/row0sel.c 2010-10-04 10:05:21 +0000 +++ b/storage/innobase/row/row0sel.c 2010-10-07 08:13:11 +0000 @@ -2621,12 +2621,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 this 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; === modified file 'storage/innodb_plugin/row/row0sel.c' --- a/storage/innodb_plugin/row/row0sel.c 2010-10-04 10:06:41 +0000 +++ b/storage/innodb_plugin/row/row0sel.c 2010-10-07 08:13:11 +0000 @@ -2696,12 +2696,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; --===============1680364335== 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\ # zb72jgqjx2rs831z # target_branch: file:///data0/martin/bzr/bug56423/5.1bt/ # testament_sha1: 72fcbd3546b8e490bb4b3fffb39a3abf6113ca47 # timestamp: 2010-10-07 10:15:25 +0200 # base_revision_id: georgi.kodinov@stripped\ # qqa7a03c3dhnvo9d # # Begin bundle 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 --===============1680364335==--