Skip to content

Commit d334766

Browse files
committed
Bug#22810883: ASSERTION FAILED: !(USED_TABS & (~READ_TABLES & ~FILTER_FOR_TABLE))
If the generation expression of an indexed generated column contains an AND or OR expression, the optimizer may choose that index too often and create execution plans that give wrong results. The problem is that Item_cond::eq() does not inspect the arguments of the AND or OR expression. It inspects the "args" member, but it is always empty for Item_cond objects. They instead keep their arguments in the member "list". The result is that it always considers two AND expressions as equal. Similarly, it always considers two OR expressions as equal. Because of this, the optimizer may choose to replace an AND or OR expression, for example in a WHERE clause, with a generated column that is not equivalent to the replaced expression. The fix is to change Item_cond::eq() to inspect the "list" member instead of the "args" member.
1 parent 83f46ae commit d334766

File tree

4 files changed

+136
-3
lines changed

4 files changed

+136
-3
lines changed

mysql-test/suite/gcol/inc/gcol_keys.inc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,13 @@ SELECT a FROM t WHERE (NOT a) = 1;
732732
EXPLAIN SELECT a FROM t WHERE (CASE WHEN (a AND b) THEN a ELSE b END) = 1;
733733
--sorted_result
734734
SELECT a FROM t WHERE (CASE WHEN (a AND b) THEN a ELSE b END) = 1;
735+
# The expression must be exactly the same as the generated expression.
736+
# (b AND a) is not recognized as equivalent to (a AND b).
737+
EXPLAIN SELECT a, b FROM t WHERE 1 = (b AND a);
738+
SELECT a, b FROM t WHERE 1 = (b AND a);
739+
--sorted_result
740+
EXPLAIN SELECT a, b FROM t WHERE 1 = (b OR a);
741+
SELECT a, b FROM t WHERE 1 = (b OR a);
735742
DROP TABLE t;
736743

737744
--echo #
@@ -808,4 +815,25 @@ ALTER TABLE c1 drop vc2;
808815
ALTER TABLE c1 ADD vc4 INT(11);
809816
DROP TABLE c1;
810817
}
818+
819+
--echo #
820+
--echo # Bug#22810883: ASSERTION FAILED:
821+
--echo # !(USED_TABS & (~READ_TABLES & ~FILTER_FOR_TABLE))
822+
--echo #
823+
CREATE TABLE t1 (a1 INTEGER GENERATED ALWAYS AS (1 AND 0) STORED,
824+
a2 INTEGER, KEY (a1));
825+
INSERT INTO t1 VALUES ();
826+
CREATE TABLE t2 (b INTEGER);
827+
INSERT INTO t2 VALUES (1);
828+
ANALYZE TABLE t1, t2;
829+
--echo # Used to choose the index on a1 and get wrong results.
830+
--let $query= SELECT * FROM t1 WHERE (a2 AND a2) = 0
831+
--eval EXPLAIN $query
832+
--eval $query
833+
--echo # Used to get assertion or wrong results.
834+
--let $query= SELECT * FROM t1 STRAIGHT_JOIN t2 ON b WHERE (b AND b) = 1
835+
--eval EXPLAIN $query
836+
--eval $query
837+
DROP TABLE t1, t2;
838+
811839
--echo #

mysql-test/suite/gcol/r/gcol_keys_innodb.result

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,6 +1317,24 @@ SELECT a FROM t WHERE (CASE WHEN (a AND b) THEN a ELSE b END) = 1;
13171317
a
13181318
0
13191319
1
1320+
EXPLAIN SELECT a, b FROM t WHERE 1 = (b AND a);
1321+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1322+
1 SIMPLE t NULL ALL NULL NULL NULL NULL 4 100.00 Using where
1323+
Warnings:
1324+
Note 1003 /* select#1 */ select `test`.`t`.`a` AS `a`,`test`.`t`.`b` AS `b` from `test`.`t` where (1 = (`test`.`t`.`b` and `test`.`t`.`a`))
1325+
SELECT a, b FROM t WHERE 1 = (b AND a);
1326+
a b
1327+
1 1
1328+
EXPLAIN SELECT a, b FROM t WHERE 1 = (b OR a);
1329+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1330+
1 SIMPLE t NULL ALL NULL NULL NULL NULL 4 100.00 Using where
1331+
Note 1003 /* select#1 */ select `test`.`t`.`a` AS `a`,`test`.`t`.`b` AS `b` from `test`.`t` where (1 = (`test`.`t`.`b` or `test`.`t`.`a`))
1332+
Warnings:
1333+
SELECT a, b FROM t WHERE 1 = (b OR a);
1334+
a b
1335+
0 1
1336+
1 0
1337+
1 1
13201338
DROP TABLE t;
13211339
#
13221340
# Bug#21854241: QUERY USING JSON_EXTRACT() RETURNS WRONG RESULT
@@ -1459,6 +1477,38 @@ ALTER TABLE c1 drop vc2;
14591477
ALTER TABLE c1 ADD vc4 INT(11);
14601478
DROP TABLE c1;
14611479
#
1480+
# Bug#22810883: ASSERTION FAILED:
1481+
# !(USED_TABS & (~READ_TABLES & ~FILTER_FOR_TABLE))
1482+
#
1483+
CREATE TABLE t1 (a1 INTEGER GENERATED ALWAYS AS (1 AND 0) STORED,
1484+
a2 INTEGER, KEY (a1));
1485+
INSERT INTO t1 VALUES ();
1486+
CREATE TABLE t2 (b INTEGER);
1487+
INSERT INTO t2 VALUES (1);
1488+
ANALYZE TABLE t1, t2;
1489+
Table Op Msg_type Msg_text
1490+
test.t1 analyze status OK
1491+
test.t2 analyze status OK
1492+
# Used to choose the index on a1 and get wrong results.
1493+
EXPLAIN SELECT * FROM t1 WHERE (a2 AND a2) = 0;
1494+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1495+
1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 1 100.00 Using where
1496+
Warnings:
1497+
Note 1003 /* select#1 */ select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2` from `test`.`t1` where ((`test`.`t1`.`a2` and `test`.`t1`.`a2`) = 0)
1498+
SELECT * FROM t1 WHERE (a2 AND a2) = 0;
1499+
a1 a2
1500+
# Used to get assertion or wrong results.
1501+
EXPLAIN SELECT * FROM t1 STRAIGHT_JOIN t2 ON b WHERE (b AND b) = 1;
1502+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1503+
1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 1 100.00 NULL
1504+
1 SIMPLE t2 NULL ALL NULL NULL NULL NULL 1 100.00 Using where; Using join buffer (Block Nested Loop)
1505+
Warnings:
1506+
Note 1003 /* select#1 */ select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2`,`test`.`t2`.`b` AS `b` from `test`.`t1` straight_join `test`.`t2` where (((`test`.`t2`.`b` and `test`.`t2`.`b`) = 1) and `test`.`t2`.`b`)
1507+
SELECT * FROM t1 STRAIGHT_JOIN t2 ON b WHERE (b AND b) = 1;
1508+
a1 a2 b
1509+
0 NULL 1
1510+
DROP TABLE t1, t2;
1511+
#
14621512
#
14631513
# BUG#21365158 WL8149:ASSERTION `!TABLE || (!TABLE->WRITE_SET
14641514
#

mysql-test/suite/gcol/r/gcol_keys_myisam.result

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,24 @@ SELECT a FROM t WHERE (CASE WHEN (a AND b) THEN a ELSE b END) = 1;
887887
a
888888
0
889889
1
890+
EXPLAIN SELECT a, b FROM t WHERE 1 = (b AND a);
891+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
892+
1 SIMPLE t NULL ALL NULL NULL NULL NULL 4 100.00 Using where
893+
Warnings:
894+
Note 1003 /* select#1 */ select `test`.`t`.`a` AS `a`,`test`.`t`.`b` AS `b` from `test`.`t` where (1 = (`test`.`t`.`b` and `test`.`t`.`a`))
895+
SELECT a, b FROM t WHERE 1 = (b AND a);
896+
a b
897+
1 1
898+
EXPLAIN SELECT a, b FROM t WHERE 1 = (b OR a);
899+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
900+
1 SIMPLE t NULL ALL NULL NULL NULL NULL 4 100.00 Using where
901+
Note 1003 /* select#1 */ select `test`.`t`.`a` AS `a`,`test`.`t`.`b` AS `b` from `test`.`t` where (1 = (`test`.`t`.`b` or `test`.`t`.`a`))
902+
Warnings:
903+
SELECT a, b FROM t WHERE 1 = (b OR a);
904+
a b
905+
0 1
906+
1 0
907+
1 1
890908
DROP TABLE t;
891909
#
892910
# Bug#21854241: QUERY USING JSON_EXTRACT() RETURNS WRONG RESULT
@@ -1014,6 +1032,38 @@ a b gc
10141032
5 NULL 6
10151033
DROP TABLE t;
10161034
#
1035+
# Bug#22810883: ASSERTION FAILED:
1036+
# !(USED_TABS & (~READ_TABLES & ~FILTER_FOR_TABLE))
1037+
#
1038+
CREATE TABLE t1 (a1 INTEGER GENERATED ALWAYS AS (1 AND 0) STORED,
1039+
a2 INTEGER, KEY (a1));
1040+
INSERT INTO t1 VALUES ();
1041+
CREATE TABLE t2 (b INTEGER);
1042+
INSERT INTO t2 VALUES (1);
1043+
ANALYZE TABLE t1, t2;
1044+
Table Op Msg_type Msg_text
1045+
test.t1 analyze status OK
1046+
test.t2 analyze status OK
1047+
# Used to choose the index on a1 and get wrong results.
1048+
EXPLAIN SELECT * FROM t1 WHERE (a2 AND a2) = 0;
1049+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1050+
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
1051+
Warnings:
1052+
Note 1003 /* select#1 */ select '0' AS `a1`,NULL AS `a2` from dual where ((NULL and NULL) = 0)
1053+
SELECT * FROM t1 WHERE (a2 AND a2) = 0;
1054+
a1 a2
1055+
# Used to get assertion or wrong results.
1056+
EXPLAIN SELECT * FROM t1 STRAIGHT_JOIN t2 ON b WHERE (b AND b) = 1;
1057+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1058+
1 SIMPLE t1 NULL system NULL NULL NULL NULL 1 100.00 NULL
1059+
1 SIMPLE t2 NULL system NULL NULL NULL NULL 1 100.00 NULL
1060+
Warnings:
1061+
Note 1003 /* select#1 */ select '0' AS `a1`,NULL AS `a2`,'1' AS `b` from `test`.`t2` where ((('1' and '1') = 1) and '1')
1062+
SELECT * FROM t1 STRAIGHT_JOIN t2 ON b WHERE (b AND b) = 1;
1063+
a1 a2 b
1064+
0 NULL 1
1065+
DROP TABLE t1, t2;
1066+
#
10171067
DROP VIEW IF EXISTS v1,v2;
10181068
DROP TABLE IF EXISTS t1,t2,t3;
10191069
DROP PROCEDURE IF EXISTS p1;

sql/item_cmpfunc.cc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5871,11 +5871,16 @@ bool Item_cond::eq(const Item *item, bool binary_cmp) const
58715871
return false;
58725872
const Item_cond *item_cond= down_cast<const Item_cond *>(item);
58735873
if (functype() != item_cond->functype() ||
5874-
arg_count != item_cond->arg_count ||
5874+
list.elements != item_cond->list.elements ||
58755875
func_name() != item_cond->func_name())
58765876
return false;
5877-
for (size_t i= 0; i < arg_count; ++i)
5878-
if (!args[i]->eq(item_cond->args[i], binary_cmp))
5877+
// Item_cond never uses "args". Inspect "list" instead.
5878+
DBUG_ASSERT(arg_count == 0 && item_cond->arg_count == 0);
5879+
List_iterator_fast<Item> it1(const_cast<Item_cond *>(this)->list);
5880+
List_iterator_fast<Item> it2(const_cast<Item_cond *>(item_cond)->list);
5881+
Item *i;
5882+
while ((i= it1++))
5883+
if (!i->eq(it2++, binary_cmp))
58795884
return false;
58805885
return true;
58815886
}

0 commit comments

Comments
 (0)