Commit d1c2e850 authored by Sergey Glukhov's avatar Sergey Glukhov

Bug#51598 Inconsistent behaviour with a COALESCE statement inside an IN comparison

Optimizer erroneously translated LEFT JOIN into INNER JOIN.
It leads to cutting rows with NULL right side. It happens
because Item_row uses not_null_tables() method form the
base(Item) class and does not calculate 'null tables'
properly. The fix is adding calculation of 'not null tables'
to Item_row.


mysql-test/r/join_outer.result:
  test result
mysql-test/t/join_outer.test:
  test case
sql/item_row.cc:
  adding calculation of 'not null tables' to Item_row.
sql/item_row.h:
  adding calculation of 'not null tables' to Item_row.
parent caa1ccb0
...@@ -1289,3 +1289,23 @@ a COUNT( t2.b ) SUM( t2.b ) MAX( t2.b ) ...@@ -1289,3 +1289,23 @@ a COUNT( t2.b ) SUM( t2.b ) MAX( t2.b )
1 3 6 3 1 3 6 3
NULL 3 6 3 NULL 3 6 3
DROP TABLE t1, t2; DROP TABLE t1, t2;
#
# Bug#51598 Inconsistent behaviour with a COALESCE statement inside an IN comparison
#
CREATE TABLE t1(f1 INT, f2 INT, f3 INT);
INSERT INTO t1 VALUES (1, NULL, 3);
CREATE TABLE t2(f1 INT, f2 INT);
INSERT INTO t2 VALUES (2, 1);
EXPLAIN EXTENDED SELECT * FROM t1 LEFT JOIN t2 ON t1.f2 = t2.f2
WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2));
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 system NULL NULL NULL NULL 1 100.00
1 SIMPLE t2 system NULL NULL NULL NULL 1 100.00
Warnings:
Note 1003 select '1' AS `f1`,NULL AS `f2`,'3' AS `f3`,NULL AS `f1`,NULL AS `f2` from `test`.`t1` left join `test`.`t2` on(multiple equal(NULL)) where ((coalesce('1',NULL),'3') in ((1,3),(2,2)))
SELECT * FROM t1 LEFT JOIN t2 ON t1.f2 = t2.f2
WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2));
f1 f2 f3 f1 f2
1 NULL 3 NULL NULL
DROP TABLE t1, t2;
End of 5.1 tests
...@@ -896,3 +896,21 @@ FROM t1 JOIN t2 USING( a ) ...@@ -896,3 +896,21 @@ FROM t1 JOIN t2 USING( a )
GROUP BY t1.a WITH ROLLUP; GROUP BY t1.a WITH ROLLUP;
DROP TABLE t1, t2; DROP TABLE t1, t2;
--echo #
--echo # Bug#51598 Inconsistent behaviour with a COALESCE statement inside an IN comparison
--echo #
CREATE TABLE t1(f1 INT, f2 INT, f3 INT);
INSERT INTO t1 VALUES (1, NULL, 3);
CREATE TABLE t2(f1 INT, f2 INT);
INSERT INTO t2 VALUES (2, 1);
EXPLAIN EXTENDED SELECT * FROM t1 LEFT JOIN t2 ON t1.f2 = t2.f2
WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2));
SELECT * FROM t1 LEFT JOIN t2 ON t1.f2 = t2.f2
WHERE (COALESCE(t1.f1, t2.f1), f3) IN ((1, 3), (2, 2));
DROP TABLE t1, t2;
--echo End of 5.1 tests
...@@ -30,7 +30,8 @@ ...@@ -30,7 +30,8 @@
*/ */
Item_row::Item_row(List<Item> &arg): Item_row::Item_row(List<Item> &arg):
Item(), used_tables_cache(0), const_item_cache(1), with_null(0) Item(), used_tables_cache(0), not_null_tables_cache(0),
const_item_cache(1), with_null(0)
{ {
//TODO: think placing 2-3 component items in item (as it done for function) //TODO: think placing 2-3 component items in item (as it done for function)
...@@ -71,6 +72,7 @@ bool Item_row::fix_fields(THD *thd, Item **ref) ...@@ -71,6 +72,7 @@ bool Item_row::fix_fields(THD *thd, Item **ref)
Item *item= *arg; Item *item= *arg;
used_tables_cache |= item->used_tables(); used_tables_cache |= item->used_tables();
const_item_cache&= item->const_item() && !with_null; const_item_cache&= item->const_item() && !with_null;
not_null_tables_cache|= item->not_null_tables();
/* /*
Some subqueries transformations aren't done in the view_prepare_mode thus Some subqueries transformations aren't done in the view_prepare_mode thus
is_null() will fail. So we skip is_null() calculation for CREATE VIEW as is_null() will fail. So we skip is_null() calculation for CREATE VIEW as
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
class Item_row: public Item class Item_row: public Item
{ {
Item **items; Item **items;
table_map used_tables_cache; table_map used_tables_cache, not_null_tables_cache;
uint arg_count; uint arg_count;
bool const_item_cache; bool const_item_cache;
bool with_null; bool with_null;
...@@ -26,6 +26,7 @@ public: ...@@ -26,6 +26,7 @@ public:
Item(), Item(),
items(item->items), items(item->items),
used_tables_cache(item->used_tables_cache), used_tables_cache(item->used_tables_cache),
not_null_tables_cache(0),
arg_count(item->arg_count), arg_count(item->arg_count),
const_item_cache(item->const_item_cache), const_item_cache(item->const_item_cache),
with_null(0) with_null(0)
...@@ -65,6 +66,7 @@ public: ...@@ -65,6 +66,7 @@ public:
bool const_item() const { return const_item_cache; }; bool const_item() const { return const_item_cache; };
enum Item_result result_type() const { return ROW_RESULT; } enum Item_result result_type() const { return ROW_RESULT; }
void update_used_tables(); void update_used_tables();
table_map not_null_tables() const { return not_null_tables_cache; }
virtual void print(String *str, enum_query_type query_type); virtual void print(String *str, enum_query_type query_type);
bool walk(Item_processor processor, bool walk_subquery, uchar *arg); bool walk(Item_processor processor, bool walk_subquery, uchar *arg);
......
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