Commit 1d0acc77 authored by Alexey Kopytov's avatar Alexey Kopytov

Bug #53830: !table || (!table->read_set ||

             bitmap_is_set(table->read_set, field_index))

UPDATE on an InnoDB table modifying the same index that is used
to satisfy the WHERE condition could trigger a debug assertion
under some circumstances.

Since for engines with the HA_PRIMARY_KEY_IN_READ_INDEX flag
set results of an index scan on a secondary index are appended
by the primary key value, if a query involves only columns from
the primary key and a secondary index, the latter is considered
to be covering.

That tricks mysql_update() to mark for reading only columns
from the secondary index when it does an index scan to retrieve
rows to update in case a part of that key is also being
updated. However, there may be other columns in WHERE that are
part of the primary key, but not the secondary one.

What we actually want to do in this case is to add index
columns to the existing WHERE columns bitmap rather than
replace it.

mysql-test/r/innodb_mysql.result:
  Test case for bug #53830.
mysql-test/t/innodb_mysql.test:
  Test case for bug #53830.
sql/sql_update.cc:
  Add index columns to the read_set bitmap, don't replace it.
sql/table.cc:
  Added a new add_read_columns_used_by_index() function to 
  st_table.
sql/table.h:
  Added a new add_read_columns_used_by_index() function to 
  st_table.
parent d72a4710
...@@ -2408,4 +2408,13 @@ id select_type table type possible_keys key key_len ref rows Extra ...@@ -2408,4 +2408,13 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY NULL NULL NULL 1 Impossible ON condition 1 SIMPLE t1 const PRIMARY NULL NULL NULL 1 Impossible ON condition
1 SIMPLE t2 ALL NULL NULL NULL NULL 3 Using where 1 SIMPLE t2 ALL NULL NULL NULL NULL 3 Using where
DROP TABLE t1,t2; DROP TABLE t1,t2;
#
# Bug #53830: !table || (!table->read_set || bitmap_is_set(table->read_set, field_index))
#
CREATE TABLE t1 (a INT, b INT, c INT, d INT,
PRIMARY KEY(a,b,c), KEY(b,d))
ENGINE=InnoDB;
INSERT INTO t1 VALUES (0, 77, 1, 3);
UPDATE t1 SET d = 0 WHERE b = 77 AND c = 25;
DROP TABLE t1;
End of 5.1 tests End of 5.1 tests
...@@ -649,4 +649,18 @@ EXPLAIN SELECT t1.id,t2.id FROM t2 LEFT JOIN t1 ON t1.id>=74 AND t1.id<=0 ...@@ -649,4 +649,18 @@ EXPLAIN SELECT t1.id,t2.id FROM t2 LEFT JOIN t1 ON t1.id>=74 AND t1.id<=0
DROP TABLE t1,t2; DROP TABLE t1,t2;
--echo #
--echo # Bug #53830: !table || (!table->read_set || bitmap_is_set(table->read_set, field_index))
--echo #
CREATE TABLE t1 (a INT, b INT, c INT, d INT,
PRIMARY KEY(a,b,c), KEY(b,d))
ENGINE=InnoDB;
INSERT INTO t1 VALUES (0, 77, 1, 3);
UPDATE t1 SET d = 0 WHERE b = 77 AND c = 25;
DROP TABLE t1;
--echo End of 5.1 tests --echo End of 5.1 tests
...@@ -397,7 +397,7 @@ int mysql_update(THD *thd, ...@@ -397,7 +397,7 @@ int mysql_update(THD *thd,
matching rows before updating the table! matching rows before updating the table!
*/ */
if (used_index < MAX_KEY && old_covering_keys.is_set(used_index)) if (used_index < MAX_KEY && old_covering_keys.is_set(used_index))
table->mark_columns_used_by_index(used_index); table->add_read_columns_used_by_index(used_index);
else else
{ {
table->use_all_columns(); table->use_all_columns();
......
...@@ -4377,6 +4377,27 @@ void st_table::mark_columns_used_by_index(uint index) ...@@ -4377,6 +4377,27 @@ void st_table::mark_columns_used_by_index(uint index)
} }
/*
Add fields used by a specified index to the table's read_set.
NOTE:
The original state can be restored with
restore_column_maps_after_mark_index().
*/
void st_table::add_read_columns_used_by_index(uint index)
{
MY_BITMAP *bitmap= &tmp_set;
DBUG_ENTER("st_table::add_read_columns_used_by_index");
set_keyread(TRUE);
bitmap_copy(bitmap, read_set);
mark_columns_used_by_index_no_reset(index, bitmap);
column_bitmaps_set(bitmap, write_set);
DBUG_VOID_RETURN;
}
/* /*
Restore to use normal column maps after key read Restore to use normal column maps after key read
......
...@@ -865,6 +865,7 @@ struct st_table { ...@@ -865,6 +865,7 @@ struct st_table {
void prepare_for_position(void); void prepare_for_position(void);
void mark_columns_used_by_index_no_reset(uint index, MY_BITMAP *map); void mark_columns_used_by_index_no_reset(uint index, MY_BITMAP *map);
void mark_columns_used_by_index(uint index); void mark_columns_used_by_index(uint index);
void add_read_columns_used_by_index(uint index);
void restore_column_maps_after_mark_index(); void restore_column_maps_after_mark_index();
void mark_auto_increment_column(void); void mark_auto_increment_column(void);
void mark_columns_needed_for_update(void); void mark_columns_needed_for_update(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment