From: Martin Hansson Date: October 4 2010 10:40am Subject: bzr commit into mysql-5.1-bugteam branch (martin.hansson:3516) Bug#56423 List-Archive: http://lists.mysql.com/commits/119814 X-Bug: 56423 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============1305382256==" --===============1305382256== 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.1bt/ based on revid:alexey.kopytov@stripped 3516 Martin Hansson 2010-10-04 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 === 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-04 10:40:16 +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-04 10:40:16 +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-04 10:40:16 +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-04 10:40:16 +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-04 10:40:16 +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-04 10:40:16 +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-06-09 12:07:34 +0000 +++ b/storage/innobase/row/row0sel.c 2010-10-04 10:40:16 +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-08-18 11:01:10 +0000 +++ b/storage/innodb_plugin/row/row0sel.c 2010-10-04 10:40:16 +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; --===============1305382256== 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\ # 0iouyq9odo834cip # target_branch: file:///data0/martin/bzr/bug56423/5.1bt/ # testament_sha1: 57c5e96e93c883886b7a14b6d5b873883266106d # timestamp: 2010-10-04 12:40:41 +0200 # base_revision_id: alexey.kopytov@stripped\ # 2z1rzbvj0tagqazc # # Begin bundle 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 mg== --===============1305382256==--