From 3d3d5f1d431f2631d57a4f28fb5bd5fff9cd6c53 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Mon, 7 Mar 2011 22:09:21 -0800 Subject: [PATCH] Fixed LP bug #729039. If join condition is of the form = then the server performs no index look-ups when looking for matching rows of t2 for the rows from t1 with t1.no_key=NULL. It happens because the function add_not_null_conds() injects an additional condition of the form IS NOT NULL() into the WHERE condition. However if the join condition was of the form = no additional null rejecting predicate was generated. This could lead to extra records in the result set if the value of happened to be NULL. The new code injects null rejecting predicates of the form IS NOT NULL() and evaluates them before the first row the subquery is constructed. --- mysql-test/r/subselect.result | 28 +++++++++++++++++++++++ mysql-test/r/subselect_no_mat.result | 28 +++++++++++++++++++++++ mysql-test/r/subselect_no_opts.result | 28 +++++++++++++++++++++++ mysql-test/r/subselect_no_semijoin.result | 28 +++++++++++++++++++++++ mysql-test/t/subselect.test | 23 +++++++++++++++++++ sql/sql_select.cc | 25 ++++++++++++++++---- sql/sql_select.h | 2 ++ 7 files changed, 158 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 73d14acd532..69ac9b1b086 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -5018,3 +5018,31 @@ id select_type table type possible_keys key key_len ref rows Extra 2 SUBQUERY it2 ALL NULL NULL NULL NULL 4 2 SUBQUERY it3 ALL NULL NULL NULL NULL 6 Using join buffer (flat, BNL join) DROP TABLE IF EXISTS ot1, ot4, it2, it3; +# +# Bug#729039: NULL keys used to evaluate subquery +# +CREATE TABLE t1 (a int) ; +INSERT INTO t1 VALUES (NULL), (1), (NULL), (2); +CREATE TABLE t2 (a int, INDEX idx(a)) ; +INSERT INTO t2 VALUES (NULL), (1), (NULL); +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 USE INDEX () WHERE t2.a = t1.a); +a +1 +EXPLAIN +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 USE INDEX() WHERE t2.a = t1.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where +2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 3 Using where +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 WHERE t2.a = t1.a); +a +1 +EXPLAIN +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 WHERE t2.a = t1.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where +2 DEPENDENT SUBQUERY t2 ref idx idx 5 test.t1.a 2 Using index +DROP TABLE t1,t2; diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result index 486bb03373e..86714f359af 100644 --- a/mysql-test/r/subselect_no_mat.result +++ b/mysql-test/r/subselect_no_mat.result @@ -5020,6 +5020,34 @@ id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY it3 ALL NULL NULL NULL NULL 6 Using join buffer (flat, BNL join) 1 PRIMARY ot4 ALL NULL NULL NULL NULL 8 Using where; End temporary; Using join buffer (flat, BNL join) DROP TABLE IF EXISTS ot1, ot4, it2, it3; +# +# Bug#729039: NULL keys used to evaluate subquery +# +CREATE TABLE t1 (a int) ; +INSERT INTO t1 VALUES (NULL), (1), (NULL), (2); +CREATE TABLE t2 (a int, INDEX idx(a)) ; +INSERT INTO t2 VALUES (NULL), (1), (NULL); +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 USE INDEX () WHERE t2.a = t1.a); +a +1 +EXPLAIN +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 USE INDEX() WHERE t2.a = t1.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where +2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 3 Using where +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 WHERE t2.a = t1.a); +a +1 +EXPLAIN +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 WHERE t2.a = t1.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where +2 DEPENDENT SUBQUERY t2 ref idx idx 5 test.t1.a 2 Using index +DROP TABLE t1,t2; set optimizer_switch=default; select @@optimizer_switch like '%materialization=on%'; @@optimizer_switch like '%materialization=on%' diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result index 07412a0c72c..c1aa6d1e5ad 100644 --- a/mysql-test/r/subselect_no_opts.result +++ b/mysql-test/r/subselect_no_opts.result @@ -5017,4 +5017,32 @@ id select_type table type possible_keys key key_len ref rows Extra 2 DEPENDENT SUBQUERY it2 ALL NULL NULL NULL NULL 4 Using where 2 DEPENDENT SUBQUERY it3 ALL NULL NULL NULL NULL 6 Using where; Using join buffer (flat, BNL join) DROP TABLE IF EXISTS ot1, ot4, it2, it3; +# +# Bug#729039: NULL keys used to evaluate subquery +# +CREATE TABLE t1 (a int) ; +INSERT INTO t1 VALUES (NULL), (1), (NULL), (2); +CREATE TABLE t2 (a int, INDEX idx(a)) ; +INSERT INTO t2 VALUES (NULL), (1), (NULL); +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 USE INDEX () WHERE t2.a = t1.a); +a +1 +EXPLAIN +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 USE INDEX() WHERE t2.a = t1.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where +2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 3 Using where +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 WHERE t2.a = t1.a); +a +1 +EXPLAIN +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 WHERE t2.a = t1.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where +2 DEPENDENT SUBQUERY t2 ref idx idx 5 test.t1.a 2 Using index +DROP TABLE t1,t2; set optimizer_switch=default; diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result index 737fd5f2eb5..907fb6558cb 100644 --- a/mysql-test/r/subselect_no_semijoin.result +++ b/mysql-test/r/subselect_no_semijoin.result @@ -5017,4 +5017,32 @@ id select_type table type possible_keys key key_len ref rows Extra 2 SUBQUERY it2 ALL NULL NULL NULL NULL 4 2 SUBQUERY it3 ALL NULL NULL NULL NULL 6 Using join buffer (flat, BNL join) DROP TABLE IF EXISTS ot1, ot4, it2, it3; +# +# Bug#729039: NULL keys used to evaluate subquery +# +CREATE TABLE t1 (a int) ; +INSERT INTO t1 VALUES (NULL), (1), (NULL), (2); +CREATE TABLE t2 (a int, INDEX idx(a)) ; +INSERT INTO t2 VALUES (NULL), (1), (NULL); +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 USE INDEX () WHERE t2.a = t1.a); +a +1 +EXPLAIN +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 USE INDEX() WHERE t2.a = t1.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where +2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 3 Using where +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 WHERE t2.a = t1.a); +a +1 +EXPLAIN +SELECT * FROM t1 +WHERE EXISTS (SELECT a FROM t2 WHERE t2.a = t1.a); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where +2 DEPENDENT SUBQUERY t2 ref idx idx 5 test.t1.a 2 Using index +DROP TABLE t1,t2; set optimizer_switch=default; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 9ff8dd63473..965dfc6072e 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -4293,3 +4293,26 @@ eval explain $query; DROP TABLE IF EXISTS ot1, ot4, it2, it3; +--echo # +--echo # Bug#729039: NULL keys used to evaluate subquery +--echo # + +CREATE TABLE t1 (a int) ; +INSERT INTO t1 VALUES (NULL), (1), (NULL), (2); + +CREATE TABLE t2 (a int, INDEX idx(a)) ; +INSERT INTO t2 VALUES (NULL), (1), (NULL); + +SELECT * FROM t1 + WHERE EXISTS (SELECT a FROM t2 USE INDEX () WHERE t2.a = t1.a); +EXPLAIN +SELECT * FROM t1 + WHERE EXISTS (SELECT a FROM t2 USE INDEX() WHERE t2.a = t1.a); + +SELECT * FROM t1 + WHERE EXISTS (SELECT a FROM t2 WHERE t2.a = t1.a); +EXPLAIN +SELECT * FROM t1 + WHERE EXISTS (SELECT a FROM t2 WHERE t2.a = t1.a); + +DROP TABLE t1,t2; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 9e496cde197..4912bd2355e 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -6891,7 +6891,7 @@ static void add_not_null_conds(JOIN *join) UPDATE t1 SET t1.f2=(SELECT MAX(t2.f4) FROM t2 WHERE t2.f3=t1.f1); not_null_item is the t1.f1, but it's referred_tab is 0. */ - if (!referred_tab || referred_tab->join != join) + if (!referred_tab) continue; if (!(notnull= new Item_func_isnotnull(not_null_item))) DBUG_VOID_RETURN; @@ -6908,9 +6908,14 @@ static void add_not_null_conds(JOIN *join) QT_ORDINARY);); if (!tab->first_inner) { - COND *new_cond= referred_tab->select_cond; + COND *new_cond= referred_tab->join == join ? + referred_tab->select_cond : + join->outer_ref_cond; add_cond_and_fix(&new_cond, notnull); - referred_tab->set_select_cond(new_cond, __LINE__); + if (referred_tab->join == join) + referred_tab->set_select_cond(new_cond, __LINE__); + else + join->outer_ref_cond= new_cond; } else add_cond_and_fix(tab->first_inner->on_expr_ref, notnull); @@ -7127,6 +7132,15 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) DBUG_PRINT("info",("Found impossible WHERE condition")); DBUG_RETURN(1); // Impossible const condition } + + COND *outer_ref_cond= make_cond_for_table(cond, + OUTER_REF_TABLE_BIT, + (table_map) 0, FALSE, FALSE); + if (outer_ref_cond) + { + add_cond_and_fix(&outer_ref_cond, join->outer_ref_cond); + join->outer_ref_cond= outer_ref_cond; + } } } @@ -13393,7 +13407,10 @@ do_select(JOIN *join,List *fields,TABLE *table,Procedure *procedure) else { DBUG_ASSERT(join->tables); - error= join->first_select(join,join_tab,0); + if (join->outer_ref_cond && !join->outer_ref_cond->val_int()) + error= NESTED_LOOP_NO_MORE_ROWS; + else + error= join->first_select(join,join_tab,0); if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS) error= join->first_select(join,join_tab,1); if (error == NESTED_LOOP_QUERY_LIMIT) diff --git a/sql/sql_select.h b/sql/sql_select.h index 8a6a75cb80a..37e33abd9dd 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -798,6 +798,7 @@ public: ORDER *order, *group_list, *proc_param; //hold parameters of mysql_select COND *conds; // ---"--- Item *conds_history; // store WHERE for explain + COND *outer_ref_cond; /// *join_list; ///< list of joined tables in reverse order COND_EQUAL *cond_equal; @@ -903,6 +904,7 @@ public: no_const_tables= FALSE; first_select= sub_select; + outer_ref_cond= 0; } int prepare(Item ***rref_pointer_array, TABLE_LIST *tables, uint wind_num,