summaryrefslogtreecommitdiff
path: root/sql/item_subselect.h
diff options
context:
space:
mode:
authorunknown <sergefp@mysql.com>2007-01-12 23:22:41 +0300
committerunknown <sergefp@mysql.com>2007-01-12 23:22:41 +0300
commit5f97dc6e9e0b21df249f3c2fa26d8f7616797097 (patch)
tree89dced7ceab3b8f0b6983f1ecea0d1c59eedf0a1 /sql/item_subselect.h
parentb671815c95bf6c86145c16bde011a4abdd35093d (diff)
downloadmariadb-git-5f97dc6e9e0b21df249f3c2fa26d8f7616797097.tar.gz
BUG#24127: (a,b) IN (SELECT c,d ...) can produce wrong results if a and/or b are NULLs:
- Make the code produce correct result: use an array of triggers to turn on/off equalities for each compared column. Also turn on/off optimizations based on those equalities. - Make EXPLAIN output show "Full scan on NULL key" for tables for which we switch between ref/unique_subquery/index_subquery and ALL access. - index_subquery engine now has HAVING clause when it is needed, and it is displayed in EXPLAIN EXTENDED - Fix incorrect presense of "Using index" for index/unique-based subqueries (BUG#22930) // bk trigger note: this commit refers to BUG#24127 mysql-test/r/ndb_subquery.result: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Updated test results (checked) mysql-test/r/subselect.result: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Updated test results (checked) mysql-test/r/subselect2.result: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Updated test results (checked) mysql-test/r/subselect3.result: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Testcases mysql-test/t/subselect3.test: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Testcases sql/item_cmpfunc.cc: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - For row-based IN subqueries, use one flag per each column. Set the flags appropriately before running the subquery. sql/item_cmpfunc.h: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Added Item_func_trig_cond::get_triv_var() sql/item_subselect.cc: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Item_subselect::exec() and subselect_*_engine::exec() don't have parameter anymore - now Item_subselect owns the pushed down predicates guard flags. - A correct set of conditional predicates is now pushed into row-based IN subquery. - select_indexsubquery_engine now has "HAVING clause" (needed for correct query results), and it is shown in EXPLAIN EXTENDED sql/item_subselect.h: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Item_subselect::exec() and subselect_*_engine::exec() don't have parameter anymore - now Item_subselect owns the pushed down predicates guard flags. - A correct set of conditional predicates is now pushed into row-based IN subquery. - select_indexsubquery_engine now has "HAVING clause" (needed for correct query results), and it is shown in EXPLAIN EXTENDED sql/mysql_priv.h: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Added "in_having_cond" special Item name sql/mysqld.cc: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Added "in_having_cond" special Item name sql/sql_lex.h: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) sql/sql_select.cc: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Make "ref" analyzer be able to work with conditional equalities - Fix subquery optimization code to match the changes in what kinds of conditions are pushed down into subqueries - Fix wrong EXPLAIN output in some queries with subquery (BUG#22390) sql/sql_select.h: BUG#24127: wrong result for (null,not-null) IN (SELECT a,b ...) - Make "ref" analyzer be able to work with conditional equalities - Fix wrong EXPLAIN output in some queries with subquery (BUG#22390)
Diffstat (limited to 'sql/item_subselect.h')
-rw-r--r--sql/item_subselect.h92
1 files changed, 75 insertions, 17 deletions
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 69ac78d859b..25a0326f8b6 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -94,7 +94,7 @@ public:
return null_value;
}
bool fix_fields(THD *thd, Item **ref);
- virtual bool exec(bool full_scan);
+ virtual bool exec();
virtual void fix_length_and_dec();
table_map used_tables() const;
table_map not_null_tables() const { return 0; }
@@ -104,6 +104,7 @@ public:
Item *get_tmp_table_item(THD *thd);
void update_used_tables();
void print(String *str);
+ virtual bool have_guarded_conds() { return FALSE; }
bool change_engine(subselect_engine *eng)
{
old_engine= engine;
@@ -249,13 +250,21 @@ protected:
bool transformed;
public:
/* Used to trigger on/off conditions that were pushed down to subselect */
- bool enable_pushed_conds;
+ bool *pushed_cond_guards;
+
+ bool *get_cond_guard(int i)
+ {
+ return pushed_cond_guards ? pushed_cond_guards + i : NULL;
+ }
+ void set_cond_guard_var(int i, bool v) { pushed_cond_guards[i]= v; }
+ bool have_guarded_conds() { return test(pushed_cond_guards); }
+
Item_func_not_all *upper_item; // point on NOT/NOP before ALL/SOME subquery
Item_in_subselect(Item * left_expr, st_select_lex *select_lex);
Item_in_subselect()
:Item_exists_subselect(), optimizer(0), abort_on_null(0), transformed(0),
- enable_pushed_conds(TRUE), upper_item(0)
+ pushed_cond_guards(NULL), upper_item(0)
{}
subs_type substype() { return IN_SUBS; }
@@ -340,23 +349,22 @@ public:
SYNOPSIS
exec()
- full_scan TRUE - Pushed-down predicates are disabled, the engine
- must disable made based on those predicates.
- FALSE - Pushed-down predicates are in effect.
+
DESCRIPTION
Execute the engine. The result of execution is subquery value that is
either captured by previously set up select_result-based 'sink' or
stored somewhere by the exec() method itself.
- A required side effect: if full_scan==TRUE, subselect_engine->no_rows()
- should return correct result.
+ A required side effect: If at least one pushed-down predicate is
+ disabled, subselect_engine->no_rows() must return correct result after
+ the exec() call.
RETURN
0 - OK
- 1 - Either an execution error, or the engine was be "changed", and
+ 1 - Either an execution error, or the engine was "changed", and the
caller should call exec() again for the new engine.
*/
- virtual int exec(bool full_scan)= 0;
+ virtual int exec()= 0;
virtual uint cols()= 0; /* return number of columns in select */
virtual uint8 uncacheable()= 0; /* query is uncacheable */
enum Item_result type() { return res_type; }
@@ -391,7 +399,7 @@ public:
void cleanup();
int prepare();
void fix_length_and_dec(Item_cache** row);
- int exec(bool full_scan);
+ int exec();
uint cols();
uint8 uncacheable();
void exclude();
@@ -415,7 +423,7 @@ public:
void cleanup();
int prepare();
void fix_length_and_dec(Item_cache** row);
- int exec(bool full_scan);
+ int exec();
uint cols();
uint8 uncacheable();
void exclude();
@@ -429,11 +437,30 @@ public:
struct st_join_table;
+
+
+/*
+ A subquery execution engine that evaluates the subquery by doing one index
+ lookup in a unique index.
+
+ This engine is used to resolve subqueries in forms
+
+ outer_expr IN (SELECT tbl.unique_key FROM tbl WHERE subq_where)
+
+ or, tuple-based:
+
+ (oe1, .. oeN) IN (SELECT uniq_key_part1, ... uniq_key_partK
+ FROM tbl WHERE subqwhere)
+
+ i.e. the subquery is a single table SELECT without GROUP BY, aggregate
+ functions, etc.
+*/
+
class subselect_uniquesubquery_engine: public subselect_engine
{
protected:
st_join_table *tab;
- Item *cond;
+ Item *cond; /* The WHERE condition of subselect */
/*
TRUE<=> last execution produced empty set. Valid only when left
expression is NULL.
@@ -453,7 +480,7 @@ public:
void cleanup();
int prepare();
void fix_length_and_dec(Item_cache** row);
- int exec(bool full_scan);
+ int exec();
uint cols() { return 1; }
uint8 uncacheable() { return UNCACHEABLE_DEPENDENT; }
void exclude();
@@ -471,16 +498,47 @@ class subselect_indexsubquery_engine: public subselect_uniquesubquery_engine
{
/* FALSE for 'ref', TRUE for 'ref-or-null'. */
bool check_null;
+ /*
+ The "having" clause. This clause (further reffered to as "artificial
+ having") was inserted by subquery transformation code. It contains
+ Item(s) that have a side-effect: they record whether the subquery has
+ produced a row with NULL certain components. We need to use it for cases
+ like
+ (oe1, oe2) IN (SELECT t.key, t.no_key FROM t1)
+ where we do index lookup on t.key=oe1 but need also to check if there
+ was a row such that t.no_key IS NULL.
+
+ NOTE: This is currently here and not in the uniquesubquery_engine. Ideally
+ it should have been in uniquesubquery_engine in order to allow execution of
+ subqueries like
+
+ (oe1, oe2) IN (SELECT primary_key, non_key_maybe_null_field FROM tbl)
+
+ We could use uniquesubquery_engine for the first component and let
+ Item_is_not_null_test( non_key_maybe_null_field) to handle the second.
+
+ However, subqueries like the above are currently not handled by index
+ lookup-based subquery engines, the engine applicability check misses
+ them: it doesn't switch the engine for case of artificial having and
+ [eq_]ref access (only for artifical having + ref_or_null or no having).
+ The above example subquery is handled as a full-blown SELECT with eq_ref
+ access to one table.
+
+ Due to this limitation, the "artificial having" currently needs to be
+ checked by only in indexsubquery_engine.
+ */
+ Item *having;
public:
// constructor can assign THD because it will be called after JOIN::prepare
subselect_indexsubquery_engine(THD *thd, st_join_table *tab_arg,
Item_subselect *subs, Item *where,
- bool chk_null)
+ Item *having_arg, bool chk_null)
:subselect_uniquesubquery_engine(thd, tab_arg, subs, where),
- check_null(chk_null)
+ check_null(chk_null),
+ having(having_arg)
{}
- int exec(bool full_scan);
+ int exec();
void print (String *str);
};