From: Ole John Aske Date: March 24 2011 9:27am Subject: bzr commit into mysql-5.1-telco-7.0 branch (ole.john.aske:4253) Bug#51070 List-Archive: http://lists.mysql.com/commits/133736 X-Bug: 51070 Message-Id: <20110324092709.9E211218@fimafeng09.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============6425505138201263605==" --===============6425505138201263605== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///net/fimafeng09/export/home/tmp/oleja/mysql/mysql-5.1-telco-7.0/ based on revid:ole.john.aske@stripped 4253 Ole John Aske 2011-03-24 Cherry picked fix for bug#51070 to mysql-5.1-telco-7.0. Required as this fix has already been cherrypicked to the SPJ branch, and we want to minimize the diff between SPJ and main telco branches. See: http://lists.mysql.com/commits/117686 for original commit message. modified: mysql-test/r/subselect4.result mysql-test/t/subselect4.test sql/item_cmpfunc.cc sql/item_subselect.cc === modified file 'mysql-test/r/subselect4.result' --- a/mysql-test/r/subselect4.result 2010-08-05 10:42:14 +0000 +++ b/mysql-test/r/subselect4.result 2011-03-24 09:27:05 +0000 @@ -77,6 +77,92 @@ Note 1249 Select 2 was reduced during op CREATE VIEW v1 AS SELECT 1 LIKE ( 1 IN ( SELECT 1 ) ); CREATE VIEW v2 AS SELECT 1 LIKE '%' ESCAPE ( 1 IN ( SELECT 1 ) ); DROP VIEW v1, v2; +# +# Bug#51070: Query with a NOT IN subquery predicate returns a wrong +# result set +# +CREATE TABLE t1 ( a INT, b INT ); +INSERT INTO t1 VALUES ( 1, NULL ), ( 2, NULL ); +CREATE TABLE t2 ( c INT, d INT ); +INSERT INTO t2 VALUES ( NULL, 3 ), ( NULL, 4 ); +CREATE TABLE t3 ( e INT, f INT ); +INSERT INTO t3 VALUES ( NULL, NULL ), ( NULL, NULL ); +CREATE TABLE t4 ( a INT ); +INSERT INTO t4 VALUES (1), (2), (3); +CREATE TABLE t5 ( a INT ); +INSERT INTO t5 VALUES (NULL), (2); +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 +x PRIMARY x x x x x x x x +x DEPENDENT SUBQUERY x x x x x x x x +SELECT * FROM t1 WHERE ( a, b ) NOT IN ( SELECT c, d FROM t2 ); +a b +EXPLAIN +SELECT * FROM t1 WHERE ( a, b ) NOT IN ( SELECT c, d FROM t2 ) IS 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 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 2 Using where +SELECT * FROM t1 WHERE ( a, b ) NOT IN ( SELECT c, d FROM t2 ) IS NULL; +a b +1 NULL +2 NULL +SELECT * FROM t1 WHERE ( a, b ) IN ( SELECT c, d FROM t2 ) IS NULL; +a b +1 NULL +2 NULL +SELECT * FROM t1 WHERE ( a, b ) NOT IN ( SELECT c, d FROM t2 ) IS UNKNOWN; +a b +1 NULL +2 NULL +SELECT * FROM t1 WHERE (( a, b ) NOT IN ( SELECT c, d FROM t2 )) IS UNKNOWN; +a b +1 NULL +2 NULL +SELECT * FROM t1 WHERE 1 = 1 AND ( a, b ) NOT IN ( SELECT c, d FROM t2 ); +a b +EXPLAIN +SELECT * FROM t1 WHERE ( a, b ) NOT IN ( SELECT e, f FROM t3 ); +id select_type table type possible_keys key key_len ref rows Extra +x PRIMARY x x x x x x x x +x DEPENDENT SUBQUERY x x x x x x x x +SELECT * FROM t1 WHERE ( a, b ) NOT IN ( SELECT e, f FROM t3 ); +a b +EXPLAIN +SELECT * FROM t2 WHERE ( c, d ) NOT IN ( SELECT a, b FROM t1 ); +id select_type table type possible_keys key key_len ref rows Extra +x PRIMARY x x x x x x x x +x DEPENDENT SUBQUERY x x x x x x x x +SELECT * FROM t2 WHERE ( c, d ) NOT IN ( SELECT a, b FROM t1 ); +c d +EXPLAIN +SELECT * FROM t3 WHERE ( e, f ) NOT IN ( SELECT c, d FROM t2 ); +id select_type table type possible_keys key key_len ref rows Extra +x PRIMARY x x x x x x x x +x DEPENDENT SUBQUERY x x x x x x x x +SELECT * FROM t3 WHERE ( e, f ) NOT IN ( SELECT c, d FROM t2 ); +e f +EXPLAIN +SELECT * FROM t2 WHERE ( c, d ) NOT IN ( SELECT e, f FROM t3 ); +id select_type table type possible_keys key key_len ref rows Extra +x PRIMARY x x x x x x x x +x DEPENDENT SUBQUERY x x x x x x x x +SELECT * FROM t2 WHERE ( c, d ) NOT IN ( SELECT e, f FROM t3 ); +c d +SELECT * FROM t1 WHERE ( a, b ) NOT IN +( SELECT c, d FROM t2 WHERE c = 1 AND c <> 1 ); +a b +1 NULL +2 NULL +SELECT * FROM t1 WHERE b NOT IN ( SELECT c FROM t2 WHERE c = 1 ); +a b +1 NULL +2 NULL +SELECT * FROM t1 WHERE NULL NOT IN ( SELECT c FROM t2 WHERE c = 1 AND c <> 1 ); +a b +1 NULL +2 NULL +DROP TABLE t1, t2, t3, t4, t5; # # End of 5.1 tests. # === modified file 'mysql-test/t/subselect4.test' --- a/mysql-test/t/subselect4.test 2010-08-05 10:42:14 +0000 +++ b/mysql-test/t/subselect4.test 2011-03-24 09:27:05 +0000 @@ -74,6 +74,68 @@ CREATE VIEW v1 AS SELECT 1 LIKE ( 1 IN ( CREATE VIEW v2 AS SELECT 1 LIKE '%' ESCAPE ( 1 IN ( SELECT 1 ) ); DROP VIEW v1, v2; +--echo # +--echo # Bug#51070: Query with a NOT IN subquery predicate returns a wrong +--echo # result set +--echo # +CREATE TABLE t1 ( a INT, b INT ); +INSERT INTO t1 VALUES ( 1, NULL ), ( 2, NULL ); + +CREATE TABLE t2 ( c INT, d INT ); +INSERT INTO t2 VALUES ( NULL, 3 ), ( NULL, 4 ); + +CREATE TABLE t3 ( e INT, f INT ); +INSERT INTO t3 VALUES ( NULL, NULL ), ( NULL, NULL ); + +CREATE TABLE t4 ( a INT ); +INSERT INTO t4 VALUES (1), (2), (3); + +CREATE TABLE t5 ( a INT ); +INSERT INTO t5 VALUES (NULL), (2); + +--replace_column 1 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x 10 x +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 ); + +EXPLAIN +SELECT * FROM t1 WHERE ( a, b ) NOT IN ( SELECT c, d FROM t2 ) IS NULL; +SELECT * FROM t1 WHERE ( a, b ) NOT IN ( SELECT c, d FROM t2 ) IS NULL; +SELECT * FROM t1 WHERE ( a, b ) IN ( SELECT c, d FROM t2 ) IS NULL; +SELECT * FROM t1 WHERE ( a, b ) NOT IN ( SELECT c, d FROM t2 ) IS UNKNOWN; +SELECT * FROM t1 WHERE (( a, b ) NOT IN ( SELECT c, d FROM t2 )) IS UNKNOWN; + +SELECT * FROM t1 WHERE 1 = 1 AND ( a, b ) NOT IN ( SELECT c, d FROM t2 ); + +--replace_column 1 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x 10 x +EXPLAIN +SELECT * FROM t1 WHERE ( a, b ) NOT IN ( SELECT e, f FROM t3 ); +SELECT * FROM t1 WHERE ( a, b ) NOT IN ( SELECT e, f FROM t3 ); + +--replace_column 1 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x 10 x +EXPLAIN +SELECT * FROM t2 WHERE ( c, d ) NOT IN ( SELECT a, b FROM t1 ); +SELECT * FROM t2 WHERE ( c, d ) NOT IN ( SELECT a, b FROM t1 ); + +--replace_column 1 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x 10 x +EXPLAIN +SELECT * FROM t3 WHERE ( e, f ) NOT IN ( SELECT c, d FROM t2 ); +SELECT * FROM t3 WHERE ( e, f ) NOT IN ( SELECT c, d FROM t2 ); + +--replace_column 1 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x 10 x +EXPLAIN +SELECT * FROM t2 WHERE ( c, d ) NOT IN ( SELECT e, f FROM t3 ); +SELECT * FROM t2 WHERE ( c, d ) NOT IN ( SELECT e, f FROM t3 ); + +SELECT * FROM t1 WHERE ( a, b ) NOT IN + ( SELECT c, d FROM t2 WHERE c = 1 AND c <> 1 ); + +SELECT * FROM t1 WHERE b NOT IN ( SELECT c FROM t2 WHERE c = 1 ); + +SELECT * FROM t1 WHERE NULL NOT IN ( SELECT c FROM t2 WHERE c = 1 AND c <> 1 ); + +DROP TABLE t1, t2, t3, t4, t5; + --echo # --echo # End of 5.1 tests. === modified file 'sql/item_cmpfunc.cc' --- a/sql/item_cmpfunc.cc 2011-01-14 08:54:47 +0000 +++ b/sql/item_cmpfunc.cc 2011-03-24 09:27:05 +0000 @@ -1752,6 +1752,76 @@ bool Item_in_optimizer::fix_fields(THD * } +/** + The implementation of optimized \ [NOT] IN \ + predicates. The implementation works as follows. + + For the current value of the outer expression + + - If it contains only NULL values, the original (before rewrite by the + Item_in_subselect rewrite methods) inner subquery is non-correlated and + was previously executed, there is no need to re-execute it, and the + previous return value is returned. + + - If it contains NULL values, check if there is a partial match for the + inner query block by evaluating it. For clarity we repeat here the + transformation previously performed on the sub-query. The expression + + + ( oc_1, ..., oc_n ) + \ + ( SELECT ic_1, ..., ic_n + FROM \ + WHERE \ + ) + + + was transformed into + + + ( oc_1, ..., oc_n ) + \ + ( SELECT ic_1, ..., ic_n + FROM \ + WHERE \ AND ... ( ic_k = oc_k OR ic_k IS NULL ) + HAVING ... NOT ic_k IS NULL + ) + + + The evaluation will now proceed according to special rules set up + elsewhere. These rules include: + + - The HAVING NOT \ IS NULL conditions added by the + aforementioned rewrite methods will detect whether they evaluated (and + rejected) a NULL value and if so, will cause the subquery to evaluate + to NULL. + + - The added WHERE and HAVING conditions are present only for those inner + columns that correspond to outer column that are not NULL at the moment. + + - If there is an eligible index for executing the subquery, the special + access method "Full scan on NULL key" is employed which ensures that + the inner query will detect if there are NULL values resulting from the + inner query. This access method will quietly resort to table scan if it + needs to find NULL values as well. + + - Under these conditions, the sub-query need only be evaluated in order to + find out whether it produced any rows. + + - If it did, we know that there was a partial match since there are + NULL values in the outer row expression. + + - If it did not, the result is FALSE or UNKNOWN. If at least one of the + HAVING sub-predicates rejected a NULL value corresponding to an outer + non-NULL, and hence the inner query block returns UNKNOWN upon + evaluation, there was a partial match and the result is UNKNOWN. + + - If it contains no NULL values, the call is forwarded to the inner query + block. + + @see Item_in_subselect::val_bool() + @see Item_is_not_null_test::val_int() + */ longlong Item_in_optimizer::val_int() { bool tmp; @@ -1805,7 +1875,7 @@ longlong Item_in_optimizer::val_int() all_left_cols_null= false; } - if (!((Item_in_subselect*)args[1])->is_correlated && + if (!item_subs->is_correlated && all_left_cols_null && result_for_null_param != UNKNOWN) { /* @@ -1819,8 +1889,11 @@ longlong Item_in_optimizer::val_int() else { /* The subquery has to be evaluated */ - (void) args[1]->val_bool_result(); - null_value= !item_subs->engine->no_rows(); + (void) item_subs->val_bool_result(); + if (item_subs->engine->no_rows()) + null_value= item_subs->null_value; + else + null_value= TRUE; if (all_left_cols_null) result_for_null_param= null_value; } === modified file 'sql/item_subselect.cc' --- a/sql/item_subselect.cc 2011-01-14 13:36:47 +0000 +++ b/sql/item_subselect.cc 2011-03-24 09:27:05 +0000 @@ -50,7 +50,7 @@ Item_subselect::Item_subselect(): item value is NULL if select_subselect not changed this value (i.e. some rows will be found returned) */ - null_value= 1; + null_value= TRUE; } @@ -430,9 +430,9 @@ void Item_maxmin_subselect::print(String void Item_singlerow_subselect::reset() { - null_value= 1; + null_value= TRUE; if (value) - value->null_value= 1; + value->null_value= TRUE; } @@ -577,7 +577,7 @@ double Item_singlerow_subselect::val_rea DBUG_ASSERT(fixed == 1); if (!exec() && !value->null_value) { - null_value= 0; + null_value= FALSE; return value->val_real(); } else @@ -592,7 +592,7 @@ longlong Item_singlerow_subselect::val_i DBUG_ASSERT(fixed == 1); if (!exec() && !value->null_value) { - null_value= 0; + null_value= FALSE; return value->val_int(); } else @@ -606,7 +606,7 @@ String *Item_singlerow_subselect::val_st { if (!exec() && !value->null_value) { - null_value= 0; + null_value= FALSE; return value->val_str(str); } else @@ -621,7 +621,7 @@ my_decimal *Item_singlerow_subselect::va { if (!exec() && !value->null_value) { - null_value= 0; + null_value= FALSE; return value->val_decimal(decimal_value); } else @@ -636,7 +636,7 @@ bool Item_singlerow_subselect::val_bool( { if (!exec() && !value->null_value) { - null_value= 0; + null_value= FALSE; return value->val_bool(); } else @@ -654,7 +654,7 @@ Item_exists_subselect::Item_exists_subse bool val_bool(); init(select_lex, new select_exists_subselect(this)); max_columns= UINT_MAX; - null_value= 0; //can't be NULL + null_value= FALSE; //can't be NULL maybe_null= 0; //can't be NULL value= 0; DBUG_VOID_RETURN; @@ -817,15 +817,14 @@ double Item_in_subselect::val_real() */ DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); - null_value= 0; + null_value= was_null= FALSE; if (exec()) { reset(); - null_value= 1; return 0; } if (was_null && !value) - null_value= 1; + null_value= TRUE; return (double) value; } @@ -838,15 +837,14 @@ longlong Item_in_subselect::val_int() */ DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); - null_value= 0; + null_value= was_null= FALSE; if (exec()) { reset(); - null_value= 1; return 0; } if (was_null && !value) - null_value= 1; + null_value= TRUE; return value; } @@ -859,16 +857,15 @@ String *Item_in_subselect::val_str(Strin */ DBUG_ASSERT(0); DBUG_ASSERT(fixed == 1); - null_value= 0; + null_value= was_null= FALSE; if (exec()) { reset(); - null_value= 1; return 0; } if (was_null && !value) { - null_value= 1; + null_value= TRUE; return 0; } str->set((ulonglong)value, &my_charset_bin); @@ -879,20 +876,14 @@ String *Item_in_subselect::val_str(Strin bool Item_in_subselect::val_bool() { DBUG_ASSERT(fixed == 1); - null_value= 0; + null_value= was_null= FALSE; if (exec()) { reset(); - /* - Must mark the IN predicate as NULL so as to make sure an enclosing NOT - predicate will return FALSE. See the comments in - subselect_uniquesubquery_engine::copy_ref_key for further details. - */ - null_value= 1; return 0; } if (was_null && !value) - null_value= 1; + null_value= TRUE; return value; } @@ -903,16 +894,15 @@ my_decimal *Item_in_subselect::val_decim method should not be used */ DBUG_ASSERT(0); - null_value= 0; + null_value= was_null= FALSE; DBUG_ASSERT(fixed == 1); if (exec()) { reset(); - null_value= 1; return 0; } if (was_null && !value) - null_value= 1; + null_value= TRUE; int2my_decimal(E_DEC_FATAL_ERROR, value, 0, decimal_value); return decimal_value; } --===============6425505138201263605== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/ole.john.aske@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: ole.john.aske@stripped\ # l0fi0mvqklv358z1 # target_branch: file:///net/fimafeng09/export/home/tmp/oleja/mysql\ # /mysql-5.1-telco-7.0/ # testament_sha1: a1f71182473f8844eb5e61880b9bdd43caf0466d # timestamp: 2011-03-24 10:27:09 +0100 # base_revision_id: ole.john.aske@stripped\ # b31agywhlki5f0gb # # Begin bundle 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 --===============6425505138201263605==--