#ifndef ITEM_SUBSELECT_INCLUDED #define ITEM_SUBSELECT_INCLUDED /* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ /* subselect Item */ #include "item.h" // Item_result_field class st_select_lex; class st_select_lex_unit; class JOIN; class Query_result_interceptor; class Query_result_subquery; class subselect_engine; class subselect_hash_sj_engine; class Item_bool_func2; class Cached_item; class Comp_creator; class PT_subselect; class Item_in_optimizer; class Item_func_not_all; typedef class st_select_lex SELECT_LEX; /** Convenience typedef used in this file, and further used by any files including this file. @retval NULL In case of semantic errors. */ typedef Comp_creator* (*chooser_compare_func_creator)(bool invert); /* base class for subselects */ class Item_subselect :public Item_result_field { typedef Item_result_field super; private: bool value_assigned; /* value already assigned to subselect */ /** Whether or not execution of this subselect has been traced by optimizer tracing already. If optimizer trace option REPEATED_SUBSELECT is disabled, this is used to disable tracing after the first one. */ bool traced_before; public: /* Used inside Item_subselect::fix_fields() according to this scenario: > Item_subselect::fix_fields > engine->prepare > query_block->prepare (Here we realize we need to do the rewrite and set substitution= some new Item, eg. Item_in_optimizer ) < query_block->prepare < engine->prepare *ref= substitution; < Item_subselect::fix_fields */ Item *substitution; public: /* unit of subquery */ st_select_lex_unit *unit; /** If !=NO_PLAN_IDX: this Item is in the condition attached to the JOIN_TAB having this index in the parent JOIN. */ int in_cond_of_tab; /// EXPLAIN needs read-only access to the engine const subselect_engine *get_engine_for_explain() const { return engine; } protected: /* engine that perform execution of subselect (single select or union) */ subselect_engine *engine; /* old engine if engine was changed */ subselect_engine *old_engine; /* cache of used external tables */ table_map used_tables_cache; /* allowed number of columns (1 for single value subqueries) */ uint max_columns; /* where subquery is placed */ enum_parsing_context parsing_place; /* work with 'substitution' */ bool have_to_be_excluded; /* cache of constant state */ bool const_item_cache; public: /* subquery is transformed */ bool changed; enum trans_res {RES_OK, RES_REDUCE, RES_ERROR}; enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS, EXISTS_SUBS, IN_SUBS, ALL_SUBS, ANY_SUBS}; Item_subselect(); explicit Item_subselect(const POS &pos); virtual subs_type substype() { return UNKNOWN_SUBS; } /* We need this method, because some compilers do not allow 'this' pointer in constructor initialization list, but we need to pass a pointer to subselect Item class to Query_result_interceptor's constructor. */ void init(st_select_lex *select, Query_result_subquery *result); ~Item_subselect(); virtual void cleanup(); virtual void reset() { null_value= 1; } virtual trans_res select_transformer(st_select_lex *select) = 0; bool assigned() const { return value_assigned; } void assigned(bool a) { value_assigned= a; } enum Type type() const; bool is_null() { update_null_value(); return null_value; } bool fix_fields(THD *thd, Item **ref); void fix_after_pullout(st_select_lex *parent_select, st_select_lex *removed_select); virtual bool exec(); virtual void fix_length_and_dec(); table_map used_tables() const; table_map not_null_tables() const { return 0; } bool const_item() const; inline table_map get_used_tables_cache() { return used_tables_cache; } inline bool get_const_item_cache() { return const_item_cache; } Item *get_tmp_table_item(THD *thd); void update_used_tables(); virtual void print(String *str, enum_query_type query_type); virtual bool have_guarded_conds() { return FALSE; } bool change_engine(subselect_engine *eng) { old_engine= engine; engine= eng; return eng == 0; } /* True if this subquery has been already evaluated. Implemented only for single select and union subqueries only. */ bool is_evaluated() const; bool is_uncacheable() const; /* Used by max/min subquery to initialize value presence registration mechanism. Engine call this method before rexecution query. */ virtual void reset_value_registration() {} enum_parsing_context place() { return parsing_place; } bool walk_body(Item_processor processor, enum_walk walk, uchar *arg); bool walk(Item_processor processor, enum_walk walk, uchar *arg); virtual bool explain_subquery_checker(uchar **arg); bool inform_item_in_cond_of_tab(uchar *arg); virtual bool clean_up_after_removal(uchar *arg); const char *func_name() const { DBUG_ASSERT(0); return "subselect"; } friend class Query_result_interceptor; friend class Item_in_optimizer; friend bool Item_field::fix_fields(THD *, Item **); friend int Item_field::fix_outer_field(THD *, Field **, Item **); friend bool Item_ref::fix_fields(THD *, Item **); friend void Item_ident::fix_after_pullout(st_select_lex *parent_select, st_select_lex *removed_selec); friend void mark_select_range_as_dependent(THD*, st_select_lex*, st_select_lex*, Field*, Item*, Item_ident*); private: virtual bool subq_opt_away_processor(uchar *arg); }; /* single value subselect */ class Item_cache; class Item_singlerow_subselect :public Item_subselect { protected: Item_cache *value, **row; bool no_rows; ///< @c no_rows_in_result public: Item_singlerow_subselect(st_select_lex *select_lex); Item_singlerow_subselect() : Item_subselect(), value(0), row (0), no_rows(false) {} virtual void cleanup(); subs_type substype() { return SINGLEROW_SUBS; } virtual void reset(); trans_res select_transformer(st_select_lex *select); void store(uint i, Item* item); double val_real(); longlong val_int (); String *val_str (String *); my_decimal *val_decimal(my_decimal *); bool val_json(Json_wrapper *result); bool get_date(MYSQL_TIME *ltime, my_time_flags_t fuzzydate); bool get_time(MYSQL_TIME *ltime); bool val_bool(); enum Item_result result_type() const; enum_field_types field_type() const; void fix_length_and_dec(); /* Mark the subquery as having no rows. If there are aggregate functions (in the outer query), we need to generate a NULL row. @c return_zero_rows(). */ virtual void no_rows_in_result(); uint cols(); /** @note that this returns the i-th element of the SELECT list. To check for nullability, look at this->maybe_null and not element_index[i]->maybe_null, since the selected expressions are always NULL if the subquery is empty. */ Item* element_index(uint i) { return reinterpret_cast(row[i]); } Item** addr(uint i) { return (Item**)row + i; } bool check_cols(uint c); bool null_inside(); void bring_value(); /** This method is used to implement a special case of semantic tree rewriting, mandated by a SQL:2003 exception in the specification. The only caller of this method is handle_sql2003_note184_exception(), see the code there for more details. Note that this method breaks the object internal integrity, by removing it's association with the corresponding SELECT_LEX, making this object orphan from the parse tree. No other method, beside the destructor, should be called on this object, as it is now invalid. @return the SELECT_LEX structure that was given in the constructor. */ st_select_lex* invalidate_and_restore_select_lex(); friend class Query_result_scalar_subquery; }; /* used in static ALL/ANY optimization */ class Item_maxmin_subselect :public Item_singlerow_subselect { protected: bool max; bool was_values; // Set if we have found at least one row public: Item_maxmin_subselect(THD *thd, Item_subselect *parent, st_select_lex *select_lex, bool max, bool ignore_nulls); virtual void print(String *str, enum_query_type query_type); virtual void cleanup(); bool any_value() { return was_values; } void register_value() { was_values= TRUE; } void reset_value_registration() { was_values= FALSE; } }; /* exists subselect */ class Item_exists_subselect :public Item_subselect { typedef Item_subselect super; protected: bool value; /* value of this item (boolean: exists/not-exists) */ public: /** The method chosen to execute the predicate, currently used for IN, =ANY and EXISTS predicates. */ enum enum_exec_method { EXEC_UNSPECIFIED, ///< No execution method specified yet. EXEC_SEMI_JOIN, ///< Predicate is converted to semi-join nest. /// IN was converted to correlated EXISTS, and this is a final decision. EXEC_EXISTS, /** Decision between EXEC_EXISTS and EXEC_MATERIALIZATION is not yet taken. IN was temporarily converted to correlated EXISTS. All descendants of Item_in_subselect must go through this method before they can reach EXEC_EXISTS. */ EXEC_EXISTS_OR_MAT, /// Predicate executed via materialization, and this is a final decision. EXEC_MATERIALIZATION }; enum_exec_method exec_method; /// Priority of this predicate in the convert-to-semi-join-nest process. int sj_convert_priority; /// True if this predicate is chosen for semi-join transformation bool sj_chosen; /** Used by subquery optimizations to keep track about where this subquery predicate is located, and whether it is a candidate for transformation. (TABLE_LIST*) 1 - the predicate is an AND-part of the WHERE join nest pointer - the predicate is an AND-part of ON expression of a join nest NULL - for all other locations. It also means that the predicate is not a candidate for transformation. See also THD::emb_on_expr_nest. */ TABLE_LIST *embedding_join_nest; Item_exists_subselect(SELECT_LEX *select); Item_exists_subselect() :Item_subselect(), value(false), exec_method(EXEC_UNSPECIFIED), sj_convert_priority(0), sj_chosen(false), embedding_join_nest(NULL) {} explicit Item_exists_subselect(const POS &pos) :super(pos), value(false), exec_method(EXEC_UNSPECIFIED), sj_convert_priority(0), sj_chosen(false), embedding_join_nest(NULL) {} virtual trans_res select_transformer(st_select_lex *select) { exec_method= EXEC_EXISTS; return RES_OK; } subs_type substype() { return EXISTS_SUBS; } virtual void reset() { value= 0; } enum Item_result result_type() const { return INT_RESULT;} longlong val_int(); double val_real(); String *val_str(String*); my_decimal *val_decimal(my_decimal *); bool val_bool(); bool get_date(MYSQL_TIME *ltime, my_time_flags_t fuzzydate) { return get_date_from_int(ltime, fuzzydate); } bool get_time(MYSQL_TIME *ltime) { return get_time_from_int(ltime); } void fix_length_and_dec(); virtual void print(String *str, enum_query_type query_type); friend class Query_result_exists_subquery; friend class subselect_indexsubquery_engine; }; /** Representation of IN subquery predicates of the form "left_expr IN (SELECT ...)". @detail This class has: - A "subquery execution engine" (as a subclass of Item_subselect) that allows it to evaluate subqueries. (and this class participates in execution by having was_null variable where part of execution result is stored. - Transformation methods (todo: more on this). This class is not used directly, it is "wrapped" into Item_in_optimizer which provides some small bits of subquery evaluation. */ class Item_in_subselect :public Item_exists_subselect { typedef Item_exists_subselect super; public: Item *left_expr; protected: /** Cache of the left operand of the subquery predicate. Allocated in the runtime memory root, for each execution, thus need not be freed. */ List *left_expr_cache; bool left_expr_cache_filled; ///< Whether left_expr_cache holds a value /** The need for expr cache may be optimized away, @sa init_left_expr_cache. */ bool need_expr_cache; private: /** In the case of x COMP_OP (SELECT1 UNION SELECT2 ...) - the subquery transformation is done on SELECT1; this requires wrapping 'x' with more Item layers, and injecting that in a condition in SELECT1. - the same transformation is done on SELECT2; but the wrapped 'x' doesn't need to be created again, the one created for SELECT1 could be reused - to achieve this, the wrapped 'x' is stored in member 'm_injected_left_expr' when it is created for SELECT1, and is later reused for SELECT2. This will refer to a cached value which is reevaluated once for each candidate row, cf. setup in ::single_value_transformer. */ Item_direct_ref *m_injected_left_expr; /** Pointer to the created Item_in_optimizer; it is stored for the same reasons as 'm_injected_left_expr'. */ Item_in_optimizer *optimizer; bool was_null; protected: bool abort_on_null; private: /** This bundles several pieces of information useful when doing the IN->EXISTS transform. If this transform has not been done, pointer is NULL. */ struct In2exists_info: public Sql_alloc { /** True: if IN->EXISTS has been done and has added a condition to the subquery's WHERE clause. */ bool added_to_where; /** True: if subquery was dependent (correlated) before IN->EXISTS was done. */ bool dependent_before; /** True: if subquery was dependent (correlated) after IN->EXISTS was done. */ bool dependent_after; } *in2exists_info; Item *remove_in2exists_conds(Item* conds); bool mark_as_outer(Item *left_row, size_t col); public: /* Used to trigger on/off conditions that were pushed down to subselect */ bool *pushed_cond_guards; Item_func_not_all *upper_item; // point on NOT/NOP before ALL/SOME subquery /* Location of the subquery predicate. It is either - pointer to join nest if the subquery predicate is in the ON expression - (TABLE_LIST*)1 if the predicate is in the WHERE. */ TABLE_LIST *expr_join_nest; private: PT_subselect *pt_subselect; public: bool in2exists_added_to_where() const { return in2exists_info && in2exists_info->added_to_where; } /// Is reliable only if IN->EXISTS has been done. bool dependent_before_in2exists() const { return in2exists_info->dependent_before; } bool *get_cond_guard(int i) { return pushed_cond_guards ? pushed_cond_guards + i : NULL; } void set_cond_guard_var(int i, bool v) { if ( pushed_cond_guards) pushed_cond_guards[i]= v; } bool have_guarded_conds() { return MY_TEST(pushed_cond_guards); } explicit Item_in_subselect(Item * left_expr, SELECT_LEX *select_lex); Item_in_subselect(const POS &pos, Item * left_expr, PT_subselect *pt_subselect_arg); Item_in_subselect() :Item_exists_subselect(), left_expr(NULL), left_expr_cache(NULL), left_expr_cache_filled(false), need_expr_cache(TRUE), m_injected_left_expr(NULL), optimizer(NULL), was_null(FALSE), abort_on_null(FALSE), in2exists_info(NULL), pushed_cond_guards(NULL), upper_item(NULL) {} bool itemize(Parse_context *pc, Item **res); virtual void cleanup(); subs_type substype() { return IN_SUBS; } virtual void reset() { value= 0; null_value= 0; was_null= 0; } trans_res select_transformer(st_select_lex *select); trans_res select_in_like_transformer(st_select_lex *select, Comp_creator *func); trans_res single_value_transformer(st_select_lex *select, Comp_creator *func); trans_res row_value_transformer(st_select_lex *select); trans_res single_value_in_to_exists_transformer(st_select_lex *select, Comp_creator *func); trans_res row_value_in_to_exists_transformer(st_select_lex *select); bool walk(Item_processor processor, enum_walk walk, uchar *arg); virtual bool exec(); longlong val_int(); double val_real(); String *val_str(String*); my_decimal *val_decimal(my_decimal *); void update_null_value () { (void) val_bool(); } bool val_bool(); void top_level_item() { abort_on_null=1; } bool is_top_level_item() const { return abort_on_null; } bool test_limit(); virtual void print(String *str, enum_query_type query_type); bool fix_fields(THD *thd, Item **ref); void fix_after_pullout(st_select_lex *parent_select, st_select_lex *removed_select); bool init_left_expr_cache(); /** Once the decision to use IN->EXISTS has been taken, performs some last steps of this transformation. */ bool finalize_exists_transform(st_select_lex *select); /** Once the decision to use materialization has been taken, performs some last steps of this transformation. */ bool finalize_materialization_transform(JOIN *join); friend class Item_ref_null_helper; friend class Item_is_not_null_test; friend class Item_in_optimizer; friend class subselect_indexsubquery_engine; friend class subselect_hash_sj_engine; }; /* ALL/ANY/SOME subselect */ class Item_allany_subselect :public Item_in_subselect { public: chooser_compare_func_creator func_creator; Comp_creator *func; bool all; Item_allany_subselect(Item * left_expr, chooser_compare_func_creator fc, st_select_lex *select, bool all); // only ALL subquery has upper not subs_type substype() { return all?ALL_SUBS:ANY_SUBS; } trans_res select_transformer(st_select_lex *select); virtual void print(String *str, enum_query_type query_type); }; class subselect_engine: public Sql_alloc { protected: Query_result_interceptor *result; /* results storage class */ Item_subselect *item; /* item, that use this engine */ enum Item_result res_type; /* type of results */ enum_field_types res_field_type; /* column type of the results */ /** True if at least one of the columns returned by the subquery may be null, or if a single-row subquery may return zero rows. */ bool maybe_null; public: enum enum_engine_type {ABSTRACT_ENGINE, SINGLE_SELECT_ENGINE, UNION_ENGINE, UNIQUESUBQUERY_ENGINE, INDEXSUBQUERY_ENGINE, HASH_SJ_ENGINE}; subselect_engine(Item_subselect *si, Query_result_interceptor *res) :result(res), item(si), res_type(STRING_RESULT), res_field_type(MYSQL_TYPE_VAR_STRING), maybe_null(false) {} virtual ~subselect_engine() {}; // to satisfy compiler /** Cleanup engine after complete query execution, free all resources. */ virtual void cleanup()= 0; /// Sets "thd" for 'result'. Should be called before prepare() void set_thd_for_result(); virtual bool prepare()= 0; virtual void fix_length_and_dec(Item_cache** row)= 0; /* Execute the engine SYNOPSIS exec() DESCRIPTION Execute the engine. The result of execution is subquery value that is either captured by previously set up Query_result-based 'sink' or stored somewhere by the exec() method itself. RETURN 0 - OK 1 - Either an execution error, or the engine was "changed", and the caller should call exec() again for the new engine. */ virtual bool exec()= 0; virtual uint cols() const = 0; /* return number of columns in select */ virtual uint8 uncacheable() const = 0; /* query is uncacheable */ virtual enum Item_result type() const { return res_type; } virtual enum_field_types field_type() const { return res_field_type; } virtual void exclude()= 0; bool may_be_null() const { return maybe_null; } virtual table_map upper_select_const_tables() const = 0; static table_map calc_const_tables(TABLE_LIST *); virtual void print(String *str, enum_query_type query_type)= 0; virtual bool change_query_result(Item_subselect *si, Query_result_subquery *result)= 0; virtual enum_engine_type engine_type() const { return ABSTRACT_ENGINE; } #ifndef DBUG_OFF /** @returns the internal Item. Defined only in debug builds, because should be used only for debug asserts. */ const Item_subselect *get_item() const { return item; } #endif protected: void set_row(List &item_list, Item_cache **row, bool never_empty); }; class subselect_single_select_engine: public subselect_engine { private: st_select_lex *select_lex; /* corresponding select_lex */ public: subselect_single_select_engine(st_select_lex *select, Query_result_interceptor *result, Item_subselect *item); virtual void cleanup(); virtual bool prepare(); virtual void fix_length_and_dec(Item_cache** row); virtual bool exec(); virtual uint cols() const; virtual uint8 uncacheable() const; virtual void exclude(); virtual table_map upper_select_const_tables() const; virtual void print (String *str, enum_query_type query_type); virtual bool change_query_result(Item_subselect *si, Query_result_subquery *result); virtual enum_engine_type engine_type() const { return SINGLE_SELECT_ENGINE; } friend class subselect_hash_sj_engine; friend class Item_in_subselect; }; class subselect_union_engine: public subselect_engine { public: subselect_union_engine(st_select_lex_unit *u, Query_result_interceptor *result, Item_subselect *item); virtual void cleanup(); virtual bool prepare(); virtual void fix_length_and_dec(Item_cache** row); virtual bool exec(); virtual uint cols() const; virtual uint8 uncacheable() const; virtual void exclude(); virtual table_map upper_select_const_tables() const; virtual void print (String *str, enum_query_type query_type); virtual bool change_query_result(Item_subselect *si, Query_result_subquery *result); virtual enum_engine_type engine_type() const { return UNION_ENGINE; } private: st_select_lex_unit *unit; /* corresponding unit structure */ }; struct st_join_table; /** A subquery execution engine that evaluates the subquery by doing index lookups in a single table's index. This engine is used to resolve subqueries in forms outer_expr IN (SELECT tbl.key FROM tbl WHERE subq_where) or, row-based: (oe1, .. oeN) IN (SELECT key_part1, ... key_partK FROM tbl WHERE subqwhere) i.e. the subquery is a single table SELECT without GROUP BY, aggregate functions, etc. */ class subselect_indexsubquery_engine : public subselect_engine { protected: QEP_TAB *tab; Item *cond; /* The WHERE condition of subselect */ ulonglong hash; /* Hash value calculated by copy_ref_key, when needed. */ /** Whether a lookup for key value (x0,y0) (x0 and/or y0 being NULL or not NULL) will find at most one row. */ bool unique; private: /* FALSE for 'ref', TRUE for 'ref-or-null'. */ bool check_null; /* The "having" clause. This clause (further referred 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. */ Item *having; public: /* constructor can assign THD because it will be called after SELECT_LEX::prepare */ subselect_indexsubquery_engine(THD *thd_arg, QEP_TAB *tab_arg, Item_subselect *subs, Item *where, Item *having_arg, bool chk_null, bool unique_arg) :subselect_engine(subs, 0), tab(tab_arg), cond(where), unique(unique_arg), check_null(chk_null), having(having_arg) {}; virtual bool exec(); virtual void print (String *str, enum_query_type query_type); virtual enum_engine_type engine_type() const { return unique ? UNIQUESUBQUERY_ENGINE : INDEXSUBQUERY_ENGINE; } virtual void cleanup() {} virtual bool prepare(); virtual void fix_length_and_dec(Item_cache** row); virtual uint cols() const { return 1; } virtual uint8 uncacheable() const { return UNCACHEABLE_DEPENDENT; } virtual void exclude(); virtual table_map upper_select_const_tables() const { return 0; } virtual bool change_query_result(Item_subselect *si, Query_result_subquery *result); bool scan_table(); void copy_ref_key(bool *require_scan, bool *convert_error); }; /* This function is actually defined in sql_parse.cc, but it depends on chooser_compare_func_creator defined in this file. */ Item * all_any_subquery_creator(Item *left_expr, chooser_compare_func_creator cmp, bool all, st_select_lex *select); inline bool Item_subselect::is_uncacheable() const { return engine->uncacheable(); } /** Compute an IN predicate via a hash semi-join. The subquery is materialized during the first evaluation of the IN predicate. The IN predicate is executed via the functionality inherited from subselect_indexsubquery_engine. */ class subselect_hash_sj_engine: public subselect_indexsubquery_engine { private: /* TRUE if the subquery was materialized into a temp table. */ bool is_materialized; /** Existence of inner NULLs in materialized table: By design, other values than IRRELEVANT_OR_FALSE are possible only if the subquery has only one inner expression. */ enum nulls_exist { /// none, or they don't matter NEX_IRRELEVANT_OR_FALSE= 0, /// they matter, and we don't know yet if they exists NEX_UNKNOWN= 1, /// they matter, and we know there exists at least one. NEX_TRUE= 2 }; enum nulls_exist mat_table_has_nulls; /* The old engine already chosen at parse time and stored in permanent memory. Through this member we can re-create and re-prepare the join object used to materialize the subquery for each execution of a prepared statement. We also reuse the functionality of subselect_single_select_engine::[prepare | cols]. */ subselect_single_select_engine *materialize_engine; /* Temp table context of the outer select's JOIN. */ Temp_table_param *tmp_param; public: subselect_hash_sj_engine(THD *thd, Item_subselect *in_predicate, subselect_single_select_engine *old_engine) :subselect_indexsubquery_engine(thd, NULL, in_predicate, NULL, NULL, false, true), is_materialized(false), materialize_engine(old_engine), tmp_param(NULL) {} ~subselect_hash_sj_engine(); bool setup(List *tmp_columns); virtual void cleanup(); virtual bool prepare() { return materialize_engine->prepare(); } virtual bool exec(); virtual void print (String *str, enum_query_type query_type); virtual uint cols() const { return materialize_engine->cols(); } virtual enum_engine_type engine_type() const { return HASH_SJ_ENGINE; } const QEP_TAB *get_qep_tab() const { return tab; } Item *get_cond_for_explain() const { return cond; } }; #endif /* ITEM_SUBSELECT_INCLUDED */