summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <timour@askmonty.org>2011-11-23 23:13:51 +0200
committerunknown <timour@askmonty.org>2011-11-23 23:13:51 +0200
commitfd3295e0acb782790eb185352a401473fd4eba99 (patch)
tree99f01617965395f0184abcc0410635d3bdf706fc
parent12e60c4989ce0214da88faad7c08d2f046885327 (diff)
downloadmariadb-git-fd3295e0acb782790eb185352a401473fd4eba99.tar.gz
Fix bug lp:893486
Analysis: The bug is a result of an incomplete fix for bug lp:869036. That fix didn't take into account that there may be a case when ther are no NULLs in the materialized subquery, however all columns without NULLs may not be grouped in the only non-null index. This is the case when the left subquery expression has nullable columns. Solution: The patch handles two missing sub-cases of the case when there are no value (non-null matches) for any outer expression, and there are both NULLs and non-NUll values in the outer reference. a) If the materialized subquery contains no NULLs there cannot be a partial match, because there are no NULLs in those columns where the outer reference has no NULLs. b) If the materialized subquery contains NULLs, but there exists a column, such that its corresponding outer expression has no NULL, and this column also has no NULL. Then there cannot be a partial match either.
-rw-r--r--mysql-test/r/subselect_partial_match.result31
-rw-r--r--mysql-test/t/subselect_partial_match.test22
-rw-r--r--sql/item_subselect.cc26
3 files changed, 67 insertions, 12 deletions
diff --git a/mysql-test/r/subselect_partial_match.result b/mysql-test/r/subselect_partial_match.result
index dddf5e2d8d6..d23f9885f44 100644
--- a/mysql-test/r/subselect_partial_match.result
+++ b/mysql-test/r/subselect_partial_match.result
@@ -948,4 +948,35 @@ SELECT * from outer_sq where (f1, f2) NOT IN (select * from inner_sq);
f1 f2
g c
drop table outer_sq, inner_sq;
+#
+# LP BUG#893486 Wrong result with partial_match_rowid_merge , NOT IN , NULLs
+#
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 VALUES (0,NULL),(2,NULL);
+CREATE TABLE t2 (c int, d int);
+INSERT INTO t2 VALUES (2,3),(4,5),(6, NULL);
+set @@optimizer_switch='materialization=on,partial_match_rowid_merge=on,partial_match_table_scan=off,in_to_exists=off';
+EXPLAIN SELECT * FROM t1 WHERE (a, b) NOT IN (SELECT c, d FROM t2);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 2 Using where
+2 SUBQUERY t2 ALL NULL NULL NULL NULL 3
+SELECT * FROM t1 WHERE (a, b) NOT IN (SELECT c, d FROM t2);
+a b
+0 NULL
+SELECT a, b, (a, b) NOT IN (SELECT c, d FROM t2) subq_res FROM t1;
+a b subq_res
+0 NULL 1
+2 NULL NULL
+EXPLAIN SELECT * FROM t1 WHERE (a, b) NOT IN (SELECT c, d FROM t2 WHERE d is not NULL);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 2 Using where
+2 SUBQUERY t2 ALL NULL NULL NULL NULL 3 Using where
+SELECT * FROM t1 WHERE (a, b) NOT IN (SELECT c, d FROM t2 WHERE d is not NULL);
+a b
+0 NULL
+SELECT a, b, (a, b) NOT IN (SELECT c, d FROM t2 WHERE d is not NULL) subq_res FROM t1;
+a b subq_res
+0 NULL 1
+2 NULL NULL
+drop table t1,t2;
set @@optimizer_switch=@save_optimizer_switch;
diff --git a/mysql-test/t/subselect_partial_match.test b/mysql-test/t/subselect_partial_match.test
index be78360c76b..45386efd266 100644
--- a/mysql-test/t/subselect_partial_match.test
+++ b/mysql-test/t/subselect_partial_match.test
@@ -775,4 +775,26 @@ SELECT * from outer_sq where (f1, f2) NOT IN (select * from inner_sq);
drop table outer_sq, inner_sq;
+--echo #
+--echo # LP BUG#893486 Wrong result with partial_match_rowid_merge , NOT IN , NULLs
+--echo #
+
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 VALUES (0,NULL),(2,NULL);
+
+CREATE TABLE t2 (c int, d int);
+INSERT INTO t2 VALUES (2,3),(4,5),(6, NULL);
+
+set @@optimizer_switch='materialization=on,partial_match_rowid_merge=on,partial_match_table_scan=off,in_to_exists=off';
+
+EXPLAIN SELECT * FROM t1 WHERE (a, b) NOT IN (SELECT c, d FROM t2);
+SELECT * FROM t1 WHERE (a, b) NOT IN (SELECT c, d FROM t2);
+SELECT a, b, (a, b) NOT IN (SELECT c, d FROM t2) subq_res FROM t1;
+
+EXPLAIN SELECT * FROM t1 WHERE (a, b) NOT IN (SELECT c, d FROM t2 WHERE d is not NULL);
+SELECT * FROM t1 WHERE (a, b) NOT IN (SELECT c, d FROM t2 WHERE d is not NULL);
+SELECT a, b, (a, b) NOT IN (SELECT c, d FROM t2 WHERE d is not NULL) subq_res FROM t1;
+
+drop table t1,t2;
+
set @@optimizer_switch=@save_optimizer_switch;
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 18374000dff..16b23045cca 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -5542,6 +5542,8 @@ bool subselect_rowid_merge_engine::test_null_row(rownum_t row_num)
/**
Test if a subset of NULL-able columns contains a row of NULLs.
+ @retval TRUE if such a row exists
+ @retval FALSE no complementing null row
*/
bool subselect_rowid_merge_engine::
@@ -5549,34 +5551,34 @@ exists_complementing_null_row(MY_BITMAP *keys_to_complement)
{
rownum_t highest_min_row= 0;
rownum_t lowest_max_row= UINT_MAX;
- uint count_null_keys, i, j;
+ uint count_null_keys, i;
Ordered_key *cur_key;
- count_null_keys= keys_to_complement->n_bits -
- bitmap_bits_set(keys_to_complement);
- if (count_null_keys == 1)
+ if (!count_columns_with_nulls)
{
/*
- The caller guarantees that the complement to keys_to_complement
- contains only columns with NULLs. Therefore if there is only one column,
- it is guaranteed to contain NULLs.
+ If there are both NULLs and non-NUll values in the outer reference, and
+ the subquery contains no NULLs, a complementing NULL row cannot exist.
*/
- return TRUE;
+ return FALSE;
}
- for (i= (non_null_key ? 1 : 0), j= 0; i < merge_keys_count; i++)
+ for (i= (non_null_key ? 1 : 0), count_null_keys= 0; i < merge_keys_count; i++)
{
cur_key= merge_keys[i];
if (bitmap_is_set(keys_to_complement, cur_key->get_keyid()))
continue;
- DBUG_ASSERT(cur_key->get_null_count());
+ if (!cur_key->get_null_count())
+ {
+ /* If there is column without NULLs, there cannot be a partial match. */
+ return FALSE;
+ }
if (cur_key->get_min_null_row() > highest_min_row)
highest_min_row= cur_key->get_min_null_row();
if (cur_key->get_max_null_row() < lowest_max_row)
lowest_max_row= cur_key->get_max_null_row();
- null_bitmaps[j++]= cur_key->get_null_key();
+ null_bitmaps[count_null_keys++]= cur_key->get_null_key();
}
- DBUG_ASSERT(count_null_keys == j);
if (lowest_max_row < highest_min_row)
{