From: Ole John Aske Date: January 14 2011 12:50pm Subject: bzr push into mysql-5.1-telco-7.0 branch (ole.john.aske:4110 to 4111) List-Archive: http://lists.mysql.com/commits/128739 Message-Id: <20110114125011.5CB46223@fimafeng09.norway.sun.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 4111 Ole John Aske 2011-01-14 Backport to mysql-5.1-telco-7.0: Fix for bug#58628: Incorrect result for 'WHERE NULL NOT IN () create_ref_for_key() allowed any constant part of a REF key to be evaluated and stored into the 'key_buff' during ::optimize(). These 'store_key*' was *not* kept in ref.key_copy[] as they where constant and we assumed we would not have to reevaluate them during JOIN::exec() However, during execute NULL values in REF key has to be detected as they may need special attention - as in 'Full scan on NULL key'. This is done by subselect_uniquesubquery_engine::copy_ref_key() which check if any keyparts evaluated to a NULL-value. As we didn't keep a store_key for a constant value, a NULL-constant was not detected by subselect_uniquesubquery_engine::copy_ref_key() ! This fixs modifies create_ref_for_key() to check if a NULL-value constant was produced - In these cases it keeps the store_key, which then will be reevaluated in JOIN::exec() and trigger correct handling of NULL-valued keys. @ mysql-test/r/update.result Update of result file due to changed (improved) behaviour when constructing a REF key from 'out of bound' values. As the main fix will cause failing const key value ::copy() to be reevaluate inside JOIN::exec(), we will detect out of bound values (which will form an illegal/undefined REF key) before executing the access request. Previously these const REF keys was produced by JOIN::optimize() and any conversion/range errors was ignored. - Possibly causing undefined keys to be produced. modified: mysql-test/r/func_in.result mysql-test/r/update.result mysql-test/t/func_in.test sql/sql_select.cc sql/sql_select.h 4110 Jonas Oreland 2011-01-14 [merge] ndb - merge 63 to 70 modified: storage/ndb/src/kernel/blocks/ERROR_codes.txt storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp storage/ndb/test/ndbapi/testBasic.cpp storage/ndb/test/run-test/daily-basic-tests.txt === modified file 'mysql-test/r/func_in.result' --- a/mysql-test/r/func_in.result 2010-06-22 18:53:08 +0000 +++ b/mysql-test/r/func_in.result 2011-01-14 12:49:25 +0000 @@ -770,4 +770,40 @@ CASE a WHEN a THEN a END NULL DROP TABLE t1; # +# Bug#58628: Incorrect result for 'WHERE NULL NOT IN () +# +CREATE TABLE t1 (pk INT NOT NULL, i INT); +INSERT INTO t1 VALUES (0,NULL), (1,NULL), (2,NULL), (3,NULL); +CREATE TABLE subq (pk INT NOT NULL, i INT NOT NULL, PRIMARY KEY(i,pk)); +INSERT INTO subq VALUES (0,0), (1,1), (2,2), (3,3); +SELECT * FROM t1 +WHERE t1.i NOT IN +(SELECT i FROM subq WHERE subq.pk = t1.pk); +pk i +SELECT * FROM t1 +WHERE t1.i IN +(SELECT i FROM subq WHERE subq.pk = t1.pk) IS UNKNOWN; +pk i +0 NULL +1 NULL +2 NULL +3 NULL +SELECT * FROM t1 +WHERE NULL NOT IN +(SELECT i FROM subq WHERE subq.pk = t1.pk); +pk i +SELECT * FROM t1 +WHERE NULL IN +(SELECT i FROM subq WHERE subq.pk = t1.pk) IS UNKNOWN; +pk i +0 NULL +1 NULL +2 NULL +3 NULL +SELECT * FROM t1 +WHERE 1+NULL NOT IN +(SELECT i FROM subq WHERE subq.pk = t1.pk); +pk i +DROP TABLE t1,subq; +# End of 5.1 tests === modified file 'mysql-test/r/update.result' --- a/mysql-test/r/update.result 2010-07-19 09:03:52 +0000 +++ b/mysql-test/r/update.result 2011-01-14 12:49:25 +0000 @@ -404,7 +404,7 @@ user_id show status like '%Handler_read%'; Variable_name Value Handler_read_first 0 -Handler_read_key 1 +Handler_read_key 0 Handler_read_next 0 Handler_read_prev 0 Handler_read_rnd 0 @@ -414,7 +414,7 @@ user_id show status like '%Handler_read%'; Variable_name Value Handler_read_first 0 -Handler_read_key 2 +Handler_read_key 0 Handler_read_next 0 Handler_read_prev 0 Handler_read_rnd 0 @@ -423,7 +423,7 @@ UPDATE t1 SET user_id=null WHERE request show status like '%Handler_read%'; Variable_name Value Handler_read_first 0 -Handler_read_key 3 +Handler_read_key 1 Handler_read_next 0 Handler_read_prev 0 Handler_read_rnd 0 @@ -432,7 +432,7 @@ UPDATE t1 SET user_id=null WHERE request show status like '%Handler_read%'; Variable_name Value Handler_read_first 0 -Handler_read_key 3 +Handler_read_key 1 Handler_read_next 0 Handler_read_prev 0 Handler_read_rnd 0 === modified file 'mysql-test/t/func_in.test' --- a/mysql-test/t/func_in.test 2010-06-22 18:53:08 +0000 +++ b/mysql-test/t/func_in.test 2011-01-14 12:49:25 +0000 @@ -555,5 +555,50 @@ SELECT CASE a WHEN a THEN a END FROM t1 DROP TABLE t1; --echo # +--echo # Bug#58628: Incorrect result for 'WHERE NULL NOT IN () +--echo # + +CREATE TABLE t1 (pk INT NOT NULL, i INT); +INSERT INTO t1 VALUES (0,NULL), (1,NULL), (2,NULL), (3,NULL); + +CREATE TABLE subq (pk INT NOT NULL, i INT NOT NULL, PRIMARY KEY(i,pk)); +INSERT INTO subq VALUES (0,0), (1,1), (2,2), (3,3); + +## Baseline queries: t1.i contains only NULL and should effectively +## be evaluated as 'WHERE NULL IN' +## .. These return correct resultset ! + +--sorted_result +SELECT * FROM t1 + WHERE t1.i NOT IN + (SELECT i FROM subq WHERE subq.pk = t1.pk); + +--sorted_result +SELECT * FROM t1 + WHERE t1.i IN + (SELECT i FROM subq WHERE subq.pk = t1.pk) IS UNKNOWN; + +## Replaced 't1.i' with some constant expression which +## also evaluates to NULL. Expected to return same result as above: + +--sorted_result +SELECT * FROM t1 + WHERE NULL NOT IN + (SELECT i FROM subq WHERE subq.pk = t1.pk); + +--sorted_result +SELECT * FROM t1 + WHERE NULL IN + (SELECT i FROM subq WHERE subq.pk = t1.pk) IS UNKNOWN; + +--sorted_result +SELECT * FROM t1 + WHERE 1+NULL NOT IN + (SELECT i FROM subq WHERE subq.pk = t1.pk); + + +DROP TABLE t1,subq; + +--echo # --echo End of 5.1 tests === modified file 'sql/sql_select.cc' --- a/sql/sql_select.cc 2011-01-14 11:14:26 +0000 +++ b/sql/sql_select.cc 2011-01-14 12:49:25 +0000 @@ -5780,6 +5780,38 @@ static bool create_ref_for_key(JOIN *joi if (keyuse->null_rejecting) j->ref.null_rejecting |= 1 << i; keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables; + +#ifndef MCP_BUG58628 + store_key* key= get_store_key(thd, + keyuse,join->const_table_map, + &keyinfo->key_part[i], + key_buff, maybe_null); + if (unlikely(!key || thd->is_fatal_error)) + DBUG_RETURN(TRUE); + + if (keyuse->used_tables || join->select_options & SELECT_DESCRIBE) + { + *ref_key++= key; // Always evaluate/explain in JOIN::exec() + } + else + { + /* key is constant, copy value now and possibly skip it while ::exec() */ + enum store_key::store_key_result result= key->copy(); + + /* Depending on 'result' it should be reevaluated in ::exec(), if either: + * 1) '::copy()' failed, in case we reevaluate - and refail in + * JOIN::exec() where the error can be handled. + * 2) Constant evaluated to NULL value which we might need to + * handle as a special case during JOIN::exec() + * (As in : 'Full scan on NULL key') + */ + if (result!=store_key::STORE_KEY_OK || // 1) + key->null_key) // 2) + { + *ref_key++= key; // Reevaluate in JOIN::exec() + } + } +#else if (!keyuse->used_tables && !(join->select_options & SELECT_DESCRIBE)) { // Compare against constant @@ -5796,6 +5828,8 @@ static bool create_ref_for_key(JOIN *joi keyuse,join->const_table_map, &keyinfo->key_part[i], key_buff, maybe_null); +#endif + /* Remember if we are going to use REF_OR_NULL But only if field _really_ can be null i.e. we force JT_REF === modified file 'sql/sql_select.h' --- a/sql/sql_select.h 2010-10-06 10:06:47 +0000 +++ b/sql/sql_select.h 2011-01-14 12:49:25 +0000 @@ -756,9 +756,14 @@ public: store_key_const_item(THD *thd, Field *to_field_arg, uchar *ptr, uchar *null_ptr_arg, uint length, Item *item_arg) +#ifndef MCP_BUG58628 + :store_key_item(thd, to_field_arg, ptr, + null_ptr_arg, length, item_arg), inited(0) +#else :store_key_item(thd, to_field_arg,ptr, - null_ptr_arg ? null_ptr_arg : item_arg->maybe_null ? - &err : (uchar*) 0, length, item_arg), inited(0) + null_ptr_arg ? null_ptr_arg : item_arg->maybe_null ? + &err : (uchar*) 0, length, item_arg), inited(0) +#endif { } const char *name() const { return "const"; } @@ -766,12 +771,21 @@ public: protected: enum store_key_result copy_inner() { +#ifndef MCP_BUG58628 + if (!inited) + { + inited=1; + int res= store_key_item::copy_inner(); + if (res && !err) + err= res; + } +#else int res; if (!inited) { inited=1; if ((res= item->save_in_field(to_field, 1))) - { + { if (!err) err= res < 0 ? 1 : res; /* 1=STORE_KEY_FATAL */ } @@ -783,6 +797,7 @@ protected: err= 1; /* STORE_KEY_FATAL */ } null_key= to_field->is_null() || item->null_value; +#endif return (err > 2 ? STORE_KEY_FATAL : (store_key_result) err); } }; No bundle (reason: useless for push emails).