mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
Subquery optimizations: non-semijoin materialization
- Backport into Maria DB 5.3, part 1
This commit is contained in:
34
sql/item.h
34
sql/item.h
@ -878,6 +878,9 @@ public:
|
||||
static CHARSET_INFO *default_charset();
|
||||
virtual CHARSET_INFO *compare_collation() { return NULL; }
|
||||
|
||||
/* Cache of the result of is_expensive(). */
|
||||
int8 is_expensive_cache;
|
||||
|
||||
virtual bool walk(Item_processor processor, bool walk_subquery, uchar *arg)
|
||||
{
|
||||
return (this->*processor)(arg);
|
||||
@ -1081,6 +1084,29 @@ public:
|
||||
*/
|
||||
virtual bool result_as_longlong() { return FALSE; }
|
||||
bool is_datetime();
|
||||
|
||||
/*
|
||||
Test whether an expression is expensive to compute. Used during
|
||||
optimization to avoid computing expensive expressions during this
|
||||
phase. Also used to force temp tables when sorting on expensive
|
||||
functions.
|
||||
TODO:
|
||||
Normally we should have a method:
|
||||
cost Item::execution_cost(),
|
||||
where 'cost' is either 'double' or some structure of various cost
|
||||
parameters.
|
||||
|
||||
NOTE
|
||||
This function is now used to prevent evaluation of materialized IN
|
||||
subquery predicates before it is allowed. grep for
|
||||
DontEvaluateMaterializedSubqueryTooEarly to see the uses.
|
||||
*/
|
||||
virtual bool is_expensive()
|
||||
{
|
||||
if (is_expensive_cache < 0)
|
||||
is_expensive_cache= walk(&Item::is_expensive_processor, 0, (uchar*)0);
|
||||
return test(is_expensive_cache);
|
||||
}
|
||||
virtual Field::geometry_type get_geometry_type() const
|
||||
{ return Field::GEOM_GEOMETRY; };
|
||||
String *check_well_formed_result(String *str, bool send_error= 0);
|
||||
@ -2842,9 +2868,10 @@ class Cached_item_field :public Cached_item
|
||||
uint length;
|
||||
|
||||
public:
|
||||
Cached_item_field(Item_field *item)
|
||||
Cached_item_field(Field *arg_field) : field(arg_field)
|
||||
{
|
||||
field= item->field;
|
||||
field= arg_field;
|
||||
/* TODO: take the memory allocation below out of the constructor. */
|
||||
buff= (uchar*) sql_calloc(length=field->pack_length());
|
||||
}
|
||||
bool cmp(void);
|
||||
@ -3271,7 +3298,8 @@ void mark_select_range_as_dependent(THD *thd,
|
||||
Field *found_field, Item *found_item,
|
||||
Item_ident *resolved_item);
|
||||
|
||||
extern Cached_item *new_Cached_item(THD *thd, Item *item);
|
||||
extern Cached_item *new_Cached_item(THD *thd, Item *item,
|
||||
bool use_result_field);
|
||||
extern Item_result item_cmp_type(Item_result a,Item_result b);
|
||||
extern void resolve_const_item(THD *thd, Item **ref, Item *cmp_item);
|
||||
extern int stored_field_cmp_to_item(Field *field, Item *item);
|
||||
|
@ -27,11 +27,16 @@
|
||||
Create right type of Cached_item for an item.
|
||||
*/
|
||||
|
||||
Cached_item *new_Cached_item(THD *thd, Item *item)
|
||||
Cached_item *new_Cached_item(THD *thd, Item *item, bool use_result_field)
|
||||
{
|
||||
if (item->real_item()->type() == Item::FIELD_ITEM &&
|
||||
!(((Item_field *) (item->real_item()))->field->flags & BLOB_FLAG))
|
||||
return new Cached_item_field((Item_field *) (item->real_item()));
|
||||
{
|
||||
Item_field *real_item= (Item_field *) item->real_item();
|
||||
Field *cached_field= use_result_field ? real_item->result_field :
|
||||
real_item->field;
|
||||
return new Cached_item_field(cached_field);
|
||||
}
|
||||
switch (item->result_type()) {
|
||||
case STRING_RESULT:
|
||||
return new Cached_item_str(thd, (Item_field *) item);
|
||||
|
@ -490,12 +490,12 @@ Field *Item_func::tmp_table_field(TABLE *table)
|
||||
return field;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
bool Item_func::is_expensive_processor(uchar *arg)
|
||||
{
|
||||
return is_expensive();
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
my_decimal *Item_func::val_decimal(my_decimal *decimal_value)
|
||||
{
|
||||
|
@ -192,8 +192,8 @@ public:
|
||||
Item_transformer transformer, uchar *arg_t);
|
||||
void traverse_cond(Cond_traverser traverser,
|
||||
void * arg, traverse_order order);
|
||||
bool is_expensive_processor(uchar *arg);
|
||||
virtual bool is_expensive() { return 0; }
|
||||
// bool is_expensive_processor(uchar *arg);
|
||||
// virtual bool is_expensive() { return 0; }
|
||||
inline double fix_result(double value)
|
||||
{
|
||||
if (isfinite(value))
|
||||
@ -1053,6 +1053,7 @@ class Item_udf_func :public Item_func
|
||||
{
|
||||
protected:
|
||||
udf_handler udf;
|
||||
bool is_expensive_processor(uchar *arg) { return TRUE; }
|
||||
|
||||
public:
|
||||
Item_udf_func(udf_func *udf_arg)
|
||||
@ -1677,6 +1678,9 @@ private:
|
||||
bool execute_impl(THD *thd);
|
||||
bool init_result_field(THD *thd);
|
||||
|
||||
protected:
|
||||
bool is_expensive_processor(uchar *arg) { return TRUE; }
|
||||
|
||||
public:
|
||||
|
||||
Item_func_sp(Name_resolution_context *context_arg, sp_name *name);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,9 +22,11 @@
|
||||
class st_select_lex;
|
||||
class st_select_lex_unit;
|
||||
class JOIN;
|
||||
class select_subselect;
|
||||
class select_result_interceptor;
|
||||
class subselect_engine;
|
||||
class subselect_hash_sj_engine;
|
||||
class Item_bool_func2;
|
||||
class Cached_item;
|
||||
|
||||
/* base class for subselects */
|
||||
|
||||
@ -34,7 +36,18 @@ class Item_subselect :public Item_result_field
|
||||
public:
|
||||
/* thread handler, will be assigned in fix_fields only */
|
||||
THD *thd;
|
||||
/* substitution instead of subselect in case of optimization */
|
||||
/*
|
||||
Used inside Item_subselect::fix_fields() according to this scenario:
|
||||
> Item_subselect::fix_fields
|
||||
> engine->prepare
|
||||
> child_join->prepare
|
||||
(Here we realize we need to do the rewrite and set
|
||||
substitution= some new Item, eg. Item_in_optimizer )
|
||||
< child_join->prepare
|
||||
< engine->prepare
|
||||
*ref= substitution;
|
||||
< Item_subselect::fix_fields
|
||||
*/
|
||||
Item *substitution;
|
||||
public:
|
||||
/* unit of subquery */
|
||||
@ -81,12 +94,12 @@ public:
|
||||
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 pass pointer
|
||||
to subselect Item class to select_subselect classes constructor.
|
||||
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 select_result_interceptor's constructor.
|
||||
*/
|
||||
virtual void init (st_select_lex *select_lex,
|
||||
select_subselect *result);
|
||||
select_result_interceptor *result);
|
||||
|
||||
~Item_subselect();
|
||||
void cleanup();
|
||||
@ -148,8 +161,9 @@ public:
|
||||
@return the SELECT_LEX structure associated with this Item
|
||||
*/
|
||||
st_select_lex* get_select_lex();
|
||||
const char *func_name() const { DBUG_ASSERT(0); return "subselect"; }
|
||||
|
||||
friend class select_subselect;
|
||||
friend class select_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 **);
|
||||
@ -258,14 +272,15 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
IN subselect: this represents "left_exr IN (SELECT ...)"
|
||||
/**
|
||||
Representation of IN subquery predicates of the form
|
||||
"left_expr IN (SELECT ...)".
|
||||
|
||||
@detail
|
||||
This class has:
|
||||
- (as a descendant of Item_subselect) a "subquery execution engine" which
|
||||
allows it to evaluate subqueries. (and this class participates in
|
||||
execution by having was_null variable where part of execution result
|
||||
is stored.
|
||||
- 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
|
||||
@ -277,6 +292,13 @@ class Item_in_subselect :public Item_exists_subselect
|
||||
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<Cached_item> *left_expr_cache;
|
||||
bool first_execution;
|
||||
|
||||
/*
|
||||
expr & optimizer used in subselect rewriting to store Item for
|
||||
all JOIN in UNION
|
||||
@ -285,7 +307,6 @@ protected:
|
||||
Item_in_optimizer *optimizer;
|
||||
bool was_null;
|
||||
bool abort_on_null;
|
||||
bool transformed;
|
||||
public:
|
||||
/* Used to trigger on/off conditions that were pushed down to subselect */
|
||||
bool *pushed_cond_guards;
|
||||
@ -344,10 +365,11 @@ public:
|
||||
|
||||
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),
|
||||
pushed_cond_guards(NULL), exec_method(NOT_TRANSFORMED), upper_item(0)
|
||||
:Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE),
|
||||
optimizer(0), abort_on_null(0), pushed_cond_guards(NULL),
|
||||
exec_method(NOT_TRANSFORMED), upper_item(0)
|
||||
{}
|
||||
|
||||
void cleanup();
|
||||
subs_type substype() { return IN_SUBS; }
|
||||
void reset()
|
||||
{
|
||||
@ -359,6 +381,10 @@ public:
|
||||
trans_res select_in_like_transformer(JOIN *join, Comp_creator *func);
|
||||
trans_res single_value_transformer(JOIN *join, Comp_creator *func);
|
||||
trans_res row_value_transformer(JOIN * join);
|
||||
trans_res single_value_in_to_exists_transformer(JOIN * join,
|
||||
Comp_creator *func);
|
||||
trans_res row_value_in_to_exists_transformer(JOIN * join);
|
||||
virtual bool exec();
|
||||
longlong val_int();
|
||||
double val_real();
|
||||
String *val_str(String*);
|
||||
@ -370,10 +396,15 @@ public:
|
||||
bool test_limit(st_select_lex_unit *unit);
|
||||
virtual void print(String *str, enum_query_type query_type);
|
||||
bool fix_fields(THD *thd, Item **ref);
|
||||
bool setup_engine();
|
||||
bool init_left_expr_cache();
|
||||
bool is_expensive_processor(uchar *arg);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
@ -398,7 +429,7 @@ public:
|
||||
class subselect_engine: public Sql_alloc
|
||||
{
|
||||
protected:
|
||||
select_subselect *result; /* results storage class */
|
||||
select_result_interceptor *result; /* results storage class */
|
||||
THD *thd; /* pointer to current THD */
|
||||
Item_subselect *item; /* item, that use this engine */
|
||||
enum Item_result res_type; /* type of results */
|
||||
@ -406,7 +437,11 @@ protected:
|
||||
bool maybe_null; /* may be null (first item in select) */
|
||||
public:
|
||||
|
||||
subselect_engine(Item_subselect *si, select_subselect *res)
|
||||
enum enum_engine_type {ABSTRACT_ENGINE, SINGLE_SELECT_ENGINE,
|
||||
UNION_ENGINE, UNIQUESUBQUERY_ENGINE,
|
||||
INDEXSUBQUERY_ENGINE, HASH_SJ_ENGINE};
|
||||
|
||||
subselect_engine(Item_subselect *si, select_result_interceptor *res)
|
||||
:thd(0)
|
||||
{
|
||||
result= res;
|
||||
@ -456,11 +491,13 @@ public:
|
||||
virtual table_map upper_select_const_tables()= 0;
|
||||
static table_map calc_const_tables(TABLE_LIST *);
|
||||
virtual void print(String *str, enum_query_type query_type)= 0;
|
||||
virtual bool change_result(Item_subselect *si, select_subselect *result)= 0;
|
||||
virtual bool change_result(Item_subselect *si,
|
||||
select_result_interceptor *result)= 0;
|
||||
virtual bool no_tables()= 0;
|
||||
virtual bool is_executed() const { return FALSE; }
|
||||
/* Check if subquery produced any rows during last query execution */
|
||||
virtual bool no_rows() = 0;
|
||||
virtual enum_engine_type engine_type() { return ABSTRACT_ENGINE; }
|
||||
|
||||
protected:
|
||||
void set_row(List<Item> &item_list, Item_cache **row);
|
||||
@ -476,7 +513,7 @@ class subselect_single_select_engine: public subselect_engine
|
||||
JOIN * join; /* corresponding JOIN structure */
|
||||
public:
|
||||
subselect_single_select_engine(st_select_lex *select,
|
||||
select_subselect *result,
|
||||
select_result_interceptor *result,
|
||||
Item_subselect *item);
|
||||
void cleanup();
|
||||
int prepare();
|
||||
@ -487,11 +524,15 @@ public:
|
||||
void exclude();
|
||||
table_map upper_select_const_tables();
|
||||
virtual void print (String *str, enum_query_type query_type);
|
||||
bool change_result(Item_subselect *si, select_subselect *result);
|
||||
bool change_result(Item_subselect *si, select_result_interceptor *result);
|
||||
bool no_tables();
|
||||
bool may_be_null();
|
||||
bool is_executed() const { return executed; }
|
||||
bool no_rows();
|
||||
virtual enum_engine_type engine_type() { return SINGLE_SELECT_ENGINE; }
|
||||
|
||||
friend class subselect_hash_sj_engine;
|
||||
friend class Item_in_subselect;
|
||||
};
|
||||
|
||||
|
||||
@ -500,7 +541,7 @@ class subselect_union_engine: public subselect_engine
|
||||
st_select_lex_unit *unit; /* corresponding unit structure */
|
||||
public:
|
||||
subselect_union_engine(st_select_lex_unit *u,
|
||||
select_subselect *result,
|
||||
select_result_interceptor *result,
|
||||
Item_subselect *item);
|
||||
void cleanup();
|
||||
int prepare();
|
||||
@ -511,10 +552,11 @@ public:
|
||||
void exclude();
|
||||
table_map upper_select_const_tables();
|
||||
virtual void print (String *str, enum_query_type query_type);
|
||||
bool change_result(Item_subselect *si, select_subselect *result);
|
||||
bool change_result(Item_subselect *si, select_result_interceptor *result);
|
||||
bool no_tables();
|
||||
bool is_executed() const;
|
||||
bool no_rows();
|
||||
virtual enum_engine_type engine_type() { return UNION_ENGINE; }
|
||||
};
|
||||
|
||||
|
||||
@ -568,11 +610,12 @@ public:
|
||||
void exclude();
|
||||
table_map upper_select_const_tables() { return 0; }
|
||||
virtual void print (String *str, enum_query_type query_type);
|
||||
bool change_result(Item_subselect *si, select_subselect *result);
|
||||
bool change_result(Item_subselect *si, select_result_interceptor *result);
|
||||
bool no_tables();
|
||||
int scan_table();
|
||||
bool copy_ref_key();
|
||||
bool no_rows() { return empty_result_set; }
|
||||
virtual enum_engine_type engine_type() { return UNIQUESUBQUERY_ENGINE; }
|
||||
};
|
||||
|
||||
|
||||
@ -622,6 +665,7 @@ public:
|
||||
{}
|
||||
int exec();
|
||||
virtual void print (String *str, enum_query_type query_type);
|
||||
virtual enum_engine_type engine_type() { return INDEXSUBQUERY_ENGINE; }
|
||||
};
|
||||
|
||||
|
||||
@ -630,9 +674,58 @@ inline bool Item_subselect::is_evaluated() const
|
||||
return engine->is_executed();
|
||||
}
|
||||
|
||||
|
||||
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_uniquesubquery_engine.
|
||||
*/
|
||||
|
||||
class subselect_hash_sj_engine: public subselect_uniquesubquery_engine
|
||||
{
|
||||
protected:
|
||||
/* TRUE if the subquery was materialized into a temp table. */
|
||||
bool is_materialized;
|
||||
/*
|
||||
The old engine already chosen at parse time and stored in permanent memory.
|
||||
Through this member we can re-create and re-prepare materialize_join for
|
||||
each execution of a prepared statement. We akso resuse the functionality
|
||||
of subselect_single_select_engine::[prepare | cols].
|
||||
*/
|
||||
subselect_single_select_engine *materialize_engine;
|
||||
/*
|
||||
QEP to execute the subquery and materialize its result into a
|
||||
temporary table. Created during the first call to exec().
|
||||
*/
|
||||
JOIN *materialize_join;
|
||||
/* Temp table context of the outer select's JOIN. */
|
||||
TMP_TABLE_PARAM *tmp_param;
|
||||
|
||||
public:
|
||||
subselect_hash_sj_engine(THD *thd, Item_subselect *in_predicate,
|
||||
subselect_single_select_engine *old_engine)
|
||||
:subselect_uniquesubquery_engine(thd, NULL, in_predicate, NULL),
|
||||
is_materialized(FALSE), materialize_engine(old_engine),
|
||||
materialize_join(NULL), tmp_param(NULL)
|
||||
{}
|
||||
~subselect_hash_sj_engine();
|
||||
|
||||
bool init_permanent(List<Item> *tmp_columns);
|
||||
bool init_runtime();
|
||||
void cleanup();
|
||||
int prepare() { return 0; }
|
||||
int exec();
|
||||
virtual void print (String *str, enum_query_type query_type);
|
||||
uint cols()
|
||||
{
|
||||
return materialize_engine->cols();
|
||||
}
|
||||
virtual enum_engine_type engine_type() { return HASH_SJ_ENGINE; }
|
||||
};
|
||||
|
||||
|
@ -5493,7 +5493,7 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond)
|
||||
DBUG_RETURN(tree);
|
||||
}
|
||||
/* Here when simple cond */
|
||||
if (cond->const_item())
|
||||
if (cond->const_item() && !cond->is_expensive())
|
||||
{
|
||||
/*
|
||||
During the cond->val_int() evaluation we can come across a subselect
|
||||
|
@ -2076,16 +2076,28 @@ void st_select_lex::print_limit(THD *thd,
|
||||
{
|
||||
SELECT_LEX_UNIT *unit= master_unit();
|
||||
Item_subselect *item= unit->item;
|
||||
if (item && unit->global_parameters == this &&
|
||||
(item->substype() == Item_subselect::EXISTS_SUBS ||
|
||||
item->substype() == Item_subselect::IN_SUBS ||
|
||||
item->substype() == Item_subselect::ALL_SUBS))
|
||||
{
|
||||
DBUG_ASSERT(!item->fixed ||
|
||||
(select_limit->val_int() == LL(1) && offset_limit == 0));
|
||||
return;
|
||||
}
|
||||
|
||||
if (item && unit->global_parameters == this)
|
||||
{
|
||||
Item_subselect::subs_type subs_type= item->substype();
|
||||
if (subs_type == Item_subselect::EXISTS_SUBS ||
|
||||
subs_type == Item_subselect::IN_SUBS ||
|
||||
subs_type == Item_subselect::ALL_SUBS)
|
||||
{
|
||||
DBUG_ASSERT(!item->fixed ||
|
||||
/*
|
||||
If not using materialization both:
|
||||
select_limit == 1, and there should be no offset_limit.
|
||||
*/
|
||||
(((subs_type == Item_subselect::IN_SUBS) &&
|
||||
((Item_in_subselect*)item)->exec_method ==
|
||||
Item_in_subselect::MATERIALIZATION) ?
|
||||
TRUE :
|
||||
(select_limit->val_int() == 1LL) &&
|
||||
offset_limit == 0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (explicit_limit)
|
||||
{
|
||||
str->append(STRING_WITH_LEN(" limit "));
|
||||
@ -2098,6 +2110,7 @@ void st_select_lex::print_limit(THD *thd,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Restore the LEX and THD in case of a parse error.
|
||||
|
||||
|
@ -559,7 +559,8 @@ public:
|
||||
bool add_fake_select_lex(THD *thd);
|
||||
void init_prepare_fake_select_lex(THD *thd);
|
||||
inline bool is_prepared() { return prepared; }
|
||||
bool change_result(select_subselect *result, select_subselect *old_result);
|
||||
bool change_result(select_result_interceptor *result,
|
||||
select_result_interceptor *old_result);
|
||||
void set_limit(st_select_lex *values);
|
||||
void set_thd(THD *thd_arg) { thd= thd_arg; }
|
||||
inline bool is_union ();
|
||||
|
@ -158,7 +158,7 @@ static enum_nested_loop_state
|
||||
evaluate_null_complemented_join_record(JOIN *join, JOIN_TAB *join_tab);
|
||||
static enum_nested_loop_state
|
||||
end_send(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
|
||||
static enum_nested_loop_state
|
||||
enum_nested_loop_state
|
||||
end_send_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
|
||||
static enum_nested_loop_state
|
||||
end_write(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
|
||||
@ -166,7 +166,7 @@ static enum_nested_loop_state
|
||||
end_update(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
|
||||
static enum_nested_loop_state
|
||||
end_unique_update(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
|
||||
static enum_nested_loop_state
|
||||
enum_nested_loop_state
|
||||
end_write_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
|
||||
|
||||
static int test_if_group_changed(List<Cached_item> &list);
|
||||
@ -192,11 +192,14 @@ static int join_ft_read_first(JOIN_TAB *tab);
|
||||
static int join_ft_read_next(READ_RECORD *info);
|
||||
int join_read_always_key_or_null(JOIN_TAB *tab);
|
||||
int join_read_next_same_or_null(READ_RECORD *info);
|
||||
static COND *make_cond_for_table(COND *cond,table_map table,
|
||||
table_map used_table);
|
||||
static COND *make_cond_for_table_from_pred(COND *root_cond, COND *cond,
|
||||
static COND *make_cond_for_table(Item *cond,table_map table,
|
||||
table_map used_table,
|
||||
bool exclude_expensive_cond);
|
||||
static COND *make_cond_for_table_from_pred(Item *root_cond, Item *cond,
|
||||
table_map tables,
|
||||
table_map used_table);
|
||||
table_map used_table,
|
||||
bool exclude_expensive_cond);
|
||||
|
||||
static Item* part_of_refkey(TABLE *form,Field *field);
|
||||
uint find_shortest_key(TABLE *table, const key_map *usable_keys);
|
||||
static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,
|
||||
@ -707,8 +710,7 @@ JOIN::prepare(Item ***rref_pointer_array,
|
||||
// psergey-todo: duplicated_subselect_card_check: where it's done?
|
||||
if (in_subs->is_top_level_item() && // 4
|
||||
!in_subs->is_correlated && // 5
|
||||
in_subs->exec_method == Item_in_subselect::NOT_TRANSFORMED // 6
|
||||
&& FALSE ) // psergey-merge: disable non-sj materialization
|
||||
in_subs->exec_method == Item_in_subselect::NOT_TRANSFORMED) // 6
|
||||
in_subs->exec_method= Item_in_subselect::MATERIALIZATION;
|
||||
}
|
||||
|
||||
@ -1501,7 +1503,7 @@ JOIN::optimize()
|
||||
"Impossible HAVING" : "Impossible WHERE";
|
||||
tables= 0;
|
||||
error= 0;
|
||||
DBUG_RETURN(0);
|
||||
goto setup_subq_exit;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1543,7 +1545,7 @@ JOIN::optimize()
|
||||
zero_result_cause= "No matching min/max row";
|
||||
tables= 0;
|
||||
error=0;
|
||||
DBUG_RETURN(0);
|
||||
goto setup_subq_exit;
|
||||
}
|
||||
if (res > 1)
|
||||
{
|
||||
@ -1557,7 +1559,7 @@ JOIN::optimize()
|
||||
zero_result_cause= "No matching min/max row";
|
||||
tables= 0;
|
||||
error=0;
|
||||
DBUG_RETURN(0);
|
||||
goto setup_subq_exit;
|
||||
}
|
||||
DBUG_PRINT("info",("Select tables optimized away"));
|
||||
zero_result_cause= "Select tables optimized away";
|
||||
@ -1575,13 +1577,14 @@ JOIN::optimize()
|
||||
if (conds && !(thd->lex->describe & DESCRIBE_EXTENDED))
|
||||
{
|
||||
COND *table_independent_conds=
|
||||
make_cond_for_table(conds, PSEUDO_TABLE_BITS, 0);
|
||||
make_cond_for_table(conds, PSEUDO_TABLE_BITS, 0, FALSE);
|
||||
DBUG_EXECUTE("where",
|
||||
print_where(table_independent_conds,
|
||||
"where after opt_sum_query()",
|
||||
QT_ORDINARY););
|
||||
conds= table_independent_conds;
|
||||
}
|
||||
goto setup_subq_exit;
|
||||
}
|
||||
}
|
||||
if (!tables_list)
|
||||
@ -1619,7 +1622,7 @@ JOIN::optimize()
|
||||
zero_result_cause= "no matching row in const table";
|
||||
DBUG_PRINT("error",("Error: %s", zero_result_cause));
|
||||
error= 0;
|
||||
DBUG_RETURN(0);
|
||||
goto setup_subq_exit;
|
||||
}
|
||||
if (!(thd->options & OPTION_BIG_SELECTS) &&
|
||||
best_read > (double) thd->variables.max_join_size &&
|
||||
@ -1690,7 +1693,7 @@ JOIN::optimize()
|
||||
{
|
||||
zero_result_cause=
|
||||
"Impossible WHERE noticed after reading const tables";
|
||||
DBUG_RETURN(0); // error == 0
|
||||
goto setup_subq_exit;
|
||||
}
|
||||
|
||||
error= -1; /* if goto err */
|
||||
@ -1920,6 +1923,10 @@ JOIN::optimize()
|
||||
if (!(select_options & SELECT_DESCRIBE))
|
||||
init_ftfuncs(thd, select_lex, test(order));
|
||||
|
||||
/* Create all structures needed for materialized subquery execution. */
|
||||
if (setup_subquery_materialization())
|
||||
DBUG_RETURN(1);
|
||||
|
||||
/*
|
||||
is this simple IN subquery?
|
||||
*/
|
||||
@ -2033,7 +2040,7 @@ JOIN::optimize()
|
||||
for (ORDER *tmp_order= order; tmp_order ; tmp_order=tmp_order->next)
|
||||
{
|
||||
Item *item= *tmp_order->item;
|
||||
if (item->walk(&Item::is_expensive_processor, 0, (uchar*)0))
|
||||
if (item->is_expensive())
|
||||
{
|
||||
/* Force tmp table without sort */
|
||||
need_tmp=1; simple_order=simple_group=0;
|
||||
@ -2185,6 +2192,18 @@ JOIN::optimize()
|
||||
|
||||
error= 0;
|
||||
DBUG_RETURN(0);
|
||||
|
||||
setup_subq_exit:
|
||||
/*
|
||||
Even with zero matching rows, subqueries in the HAVING clause may
|
||||
need to be evaluated if there are aggregate functions in the
|
||||
query. If we have planned to materialize the subquery, we need to
|
||||
set it up properly before prematurely leaving optimize().
|
||||
*/
|
||||
if (setup_subquery_materialization())
|
||||
DBUG_RETURN(1);
|
||||
error= 0;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
@ -2746,7 +2765,7 @@ JOIN::exec()
|
||||
|
||||
Item* sort_table_cond= make_cond_for_table(curr_join->tmp_having,
|
||||
used_tables,
|
||||
used_tables);
|
||||
used_tables, FALSE);
|
||||
if (sort_table_cond)
|
||||
{
|
||||
if (!curr_table->select)
|
||||
@ -2773,7 +2792,7 @@ JOIN::exec()
|
||||
QT_ORDINARY););
|
||||
curr_join->tmp_having= make_cond_for_table(curr_join->tmp_having,
|
||||
~ (table_map) 0,
|
||||
~used_tables);
|
||||
~used_tables, FALSE);
|
||||
DBUG_EXECUTE("where",print_where(curr_join->tmp_having,
|
||||
"having after sort",
|
||||
QT_ORDINARY););
|
||||
@ -3656,7 +3675,6 @@ skip_conversion:
|
||||
bool JOIN::setup_subquery_materialization()
|
||||
{
|
||||
//psergey-merge: we don't have materialization so dont need that:
|
||||
#if 0
|
||||
for (SELECT_LEX_UNIT *un= select_lex->first_inner_unit(); un;
|
||||
un= un->next_unit())
|
||||
{
|
||||
@ -3673,7 +3691,6 @@ bool JOIN::setup_subquery_materialization()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -8685,7 +8702,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
COND *const_cond=
|
||||
make_cond_for_table(cond,
|
||||
join->const_table_map,
|
||||
(table_map) 0);
|
||||
(table_map) 0, TRUE);
|
||||
DBUG_EXECUTE("where",print_where(const_cond,"constants", QT_ORDINARY););
|
||||
for (JOIN_TAB *tab= join->join_tab+join->const_tables;
|
||||
tab < join->join_tab+join->tables ; tab++)
|
||||
@ -8695,7 +8712,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
JOIN_TAB *cond_tab= tab->first_inner;
|
||||
COND *tmp= make_cond_for_table(*tab->on_expr_ref,
|
||||
join->const_table_map,
|
||||
( table_map) 0);
|
||||
( table_map) 0, FALSE);
|
||||
if (!tmp)
|
||||
continue;
|
||||
tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl);
|
||||
@ -8783,7 +8800,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
|
||||
tmp= NULL;
|
||||
if (cond)
|
||||
tmp= make_cond_for_table(cond,used_tables,current_map);
|
||||
tmp= make_cond_for_table(cond, used_tables, current_map, FALSE);
|
||||
if (cond && !tmp && tab->quick)
|
||||
{ // Outer join
|
||||
if (tab->type != JT_ALL)
|
||||
@ -8839,7 +8856,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
if (thd->variables.engine_condition_pushdown && !first_inner_tab)
|
||||
{
|
||||
COND *push_cond=
|
||||
make_cond_for_table(tmp, current_map, current_map);
|
||||
make_cond_for_table(tmp, current_map, current_map, FALSE);
|
||||
if (push_cond)
|
||||
{
|
||||
/* Push condition to handler */
|
||||
@ -8968,7 +8985,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
(tmp=make_cond_for_table(cond,
|
||||
join->const_table_map |
|
||||
current_map,
|
||||
current_map)))
|
||||
current_map, FALSE)))
|
||||
{
|
||||
DBUG_EXECUTE("where",print_where(tmp,"cache", QT_ORDINARY););
|
||||
tab->cache_select=(SQL_SELECT*)
|
||||
@ -8998,7 +9015,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
JOIN_TAB *cond_tab= join_tab->first_inner;
|
||||
COND *tmp= make_cond_for_table(*join_tab->on_expr_ref,
|
||||
join->const_table_map,
|
||||
(table_map) 0);
|
||||
(table_map) 0, FALSE);
|
||||
if (!tmp)
|
||||
continue;
|
||||
tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl);
|
||||
@ -9033,7 +9050,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
current_map= tab->table->map;
|
||||
used_tables2|= current_map;
|
||||
COND *tmp_cond= make_cond_for_table(on_expr, used_tables2,
|
||||
current_map);
|
||||
current_map, FALSE);
|
||||
if (tmp_cond)
|
||||
{
|
||||
JOIN_TAB *cond_tab= tab < first_inner_tab ? first_inner_tab : tab;
|
||||
@ -13299,7 +13316,17 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
|
||||
return (COND*) 0;
|
||||
}
|
||||
}
|
||||
else if (cond->const_item())
|
||||
else if (cond->const_item() && !cond->is_expensive())
|
||||
/*
|
||||
DontEvaluateMaterializedSubqueryTooEarly:
|
||||
TODO:
|
||||
Excluding all expensive functions is too restritive we should exclude only
|
||||
materialized IN subquery predicates because they can't yet be evaluated
|
||||
here (they need additional initialization that is done later on).
|
||||
|
||||
The proper way to exclude the subqueries would be to walk the cond tree and
|
||||
check for materialized subqueries there.
|
||||
*/
|
||||
{
|
||||
*cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
|
||||
return (COND*) 0;
|
||||
@ -16164,6 +16191,8 @@ int do_sj_dups_weedout(THD *thd, SJ_TMP_TABLE *sjtbl)
|
||||
int error;
|
||||
SJ_TMP_TABLE::TAB *tab= sjtbl->tabs;
|
||||
SJ_TMP_TABLE::TAB *tab_end= sjtbl->tabs_end;
|
||||
uchar *ptr;
|
||||
uchar *nulls_ptr;
|
||||
|
||||
DBUG_ENTER("do_sj_dups_weedout");
|
||||
|
||||
@ -16171,15 +16200,13 @@ int do_sj_dups_weedout(THD *thd, SJ_TMP_TABLE *sjtbl)
|
||||
{
|
||||
if (sjtbl->have_confluent_row)
|
||||
DBUG_RETURN(1);
|
||||
else
|
||||
{
|
||||
sjtbl->have_confluent_row= TRUE;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
sjtbl->have_confluent_row= TRUE;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
uchar *ptr= sjtbl->tmp_table->record[0] + 1;
|
||||
uchar *nulls_ptr= ptr;
|
||||
ptr= sjtbl->tmp_table->record[0] + 1;
|
||||
nulls_ptr= ptr;
|
||||
|
||||
/* Put the the rowids tuple into table->record[0]: */
|
||||
|
||||
@ -17196,7 +17223,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static enum_nested_loop_state
|
||||
enum_nested_loop_state
|
||||
end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
|
||||
bool end_of_records)
|
||||
{
|
||||
@ -17504,7 +17531,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
|
||||
|
||||
|
||||
/* ARGSUSED */
|
||||
static enum_nested_loop_state
|
||||
enum_nested_loop_state
|
||||
end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
|
||||
bool end_of_records)
|
||||
{
|
||||
@ -17724,17 +17751,33 @@ static bool replace_where_subcondition(JOIN *join, Item **expr,
|
||||
Extracted condition
|
||||
*/
|
||||
|
||||
static COND *
|
||||
make_cond_for_table(COND *cond, table_map tables, table_map used_table)
|
||||
static Item *
|
||||
make_cond_for_table(Item *cond, table_map tables, table_map used_table,
|
||||
bool exclude_expensive_cond)
|
||||
{
|
||||
return make_cond_for_table_from_pred(cond, cond, tables, used_table);
|
||||
return make_cond_for_table_from_pred(cond, cond, tables, used_table,
|
||||
exclude_expensive_cond);
|
||||
}
|
||||
|
||||
static COND *
|
||||
make_cond_for_table_from_pred(COND *root_cond, COND *cond,
|
||||
table_map tables, table_map used_table)
|
||||
static Item *
|
||||
make_cond_for_table_from_pred(Item *root_cond, Item *cond,
|
||||
table_map tables, table_map used_table,
|
||||
bool exclude_expensive_cond)
|
||||
|
||||
{
|
||||
if (used_table && !(cond->used_tables() & used_table))
|
||||
if (used_table && !(cond->used_tables() & used_table) &&
|
||||
/*
|
||||
Exclude constant conditions not checked at optimization time if
|
||||
the table we are pushing conditions to is the first one.
|
||||
As a result, such conditions are not considered as already checked
|
||||
and will be checked at execution time, attached to the first table.
|
||||
|
||||
psergey: TODO: "used_table & 1" doesn't make sense in nearly any
|
||||
context. Look at setup_table_map(), table bits reflect the order
|
||||
the tables were encountered by the parser. Check what we should
|
||||
replace this condition with.
|
||||
*/
|
||||
!((used_table & 1) && cond->is_expensive()))
|
||||
return (COND*) 0; // Already checked
|
||||
if (cond->type() == Item::COND_ITEM)
|
||||
{
|
||||
@ -17748,7 +17791,9 @@ make_cond_for_table_from_pred(COND *root_cond, COND *cond,
|
||||
Item *item;
|
||||
while ((item=li++))
|
||||
{
|
||||
Item *fix=make_cond_for_table_from_pred(root_cond, item, tables, used_table);
|
||||
Item *fix=make_cond_for_table_from_pred(root_cond, item,
|
||||
tables, used_table,
|
||||
exclude_expensive_cond);
|
||||
if (fix)
|
||||
new_cond->argument_list()->push_back(fix);
|
||||
}
|
||||
@ -17778,7 +17823,9 @@ make_cond_for_table_from_pred(COND *root_cond, COND *cond,
|
||||
Item *item;
|
||||
while ((item=li++))
|
||||
{
|
||||
Item *fix=make_cond_for_table_from_pred(root_cond, item, tables, 0L);
|
||||
Item *fix=make_cond_for_table_from_pred(root_cond, item,
|
||||
tables, 0L,
|
||||
exclude_expensive_cond);
|
||||
if (!fix)
|
||||
return (COND*) 0; // Always true
|
||||
new_cond->argument_list()->push_back(fix);
|
||||
@ -17799,8 +17846,12 @@ make_cond_for_table_from_pred(COND *root_cond, COND *cond,
|
||||
table_count times, we mark each item that we have examined with the result
|
||||
of the test
|
||||
*/
|
||||
|
||||
if (cond->marker == 3 || (cond->used_tables() & ~tables))
|
||||
if (cond->marker == 3 || (cond->used_tables() & ~tables) ||
|
||||
/*
|
||||
When extracting constant conditions, treat expensive conditions as
|
||||
non-constant, so that they are not evaluated at optimization time.
|
||||
*/
|
||||
(!used_table && exclude_expensive_cond && cond->is_expensive()))
|
||||
return (COND*) 0; // Can't check this yet
|
||||
if (cond->marker == 2 || cond->eq_cmp_result() == Item::COND_OK)
|
||||
return cond; // Not boolean op
|
||||
@ -18937,7 +18988,8 @@ static bool fix_having(JOIN *join, Item **having)
|
||||
table_map used_tables= join->const_table_map | table->table->map;
|
||||
|
||||
DBUG_EXECUTE("where",print_where(*having,"having", QT_ORDINARY););
|
||||
Item* sort_table_cond=make_cond_for_table(*having,used_tables,used_tables);
|
||||
Item* sort_table_cond=make_cond_for_table(*having, used_tables, used_tables,
|
||||
FALSE);
|
||||
if (sort_table_cond)
|
||||
{
|
||||
if (!table->select)
|
||||
@ -18955,7 +19007,7 @@ static bool fix_having(JOIN *join, Item **having)
|
||||
DBUG_EXECUTE("where",print_where(table->select_cond,
|
||||
"select and having",
|
||||
QT_ORDINARY););
|
||||
*having=make_cond_for_table(*having,~ (table_map) 0,~used_tables);
|
||||
*having= make_cond_for_table(*having,~ (table_map) 0,~used_tables, FALSE);
|
||||
DBUG_EXECUTE("where",
|
||||
print_where(*having,"having after make_cond", QT_ORDINARY););
|
||||
}
|
||||
@ -20027,7 +20079,7 @@ alloc_group_fields(JOIN *join,ORDER *group)
|
||||
{
|
||||
for (; group ; group=group->next)
|
||||
{
|
||||
Cached_item *tmp=new_Cached_item(join->thd, *group->item);
|
||||
Cached_item *tmp=new_Cached_item(join->thd, *group->item, FALSE);
|
||||
if (!tmp || join->group_fields.push_front(tmp))
|
||||
return TRUE;
|
||||
}
|
||||
@ -20037,6 +20089,38 @@ alloc_group_fields(JOIN *join,ORDER *group)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Test if a single-row cache of items changed, and update the cache.
|
||||
|
||||
@details Test if a list of items that typically represents a result
|
||||
row has changed. If the value of some item changed, update the cached
|
||||
value for this item.
|
||||
|
||||
@param list list of <item, cached_value> pairs stored as Cached_item.
|
||||
|
||||
@return -1 if no item changed
|
||||
@return index of the first item that changed
|
||||
*/
|
||||
|
||||
int test_if_item_cache_changed(List<Cached_item> &list)
|
||||
{
|
||||
DBUG_ENTER("test_if_item_cache_changed");
|
||||
List_iterator<Cached_item> li(list);
|
||||
int idx= -1,i;
|
||||
Cached_item *buff;
|
||||
|
||||
for (i=(int) list.elements-1 ; (buff=li++) ; i--)
|
||||
{
|
||||
if (buff->cmp())
|
||||
idx=i;
|
||||
}
|
||||
DBUG_PRINT("info", ("idx: %d", idx));
|
||||
DBUG_RETURN(idx);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
test_if_group_changed(List<Cached_item> &list)
|
||||
{
|
||||
|
@ -1199,6 +1199,14 @@ enum_nested_loop_state sub_select_sjm(JOIN *join, JOIN_TAB *join_tab,
|
||||
bool end_of_records);
|
||||
int do_sj_dups_weedout(THD *thd, SJ_TMP_TABLE *sjtbl);
|
||||
|
||||
enum_nested_loop_state
|
||||
end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
|
||||
bool end_of_records);
|
||||
enum_nested_loop_state
|
||||
end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
|
||||
bool end_of_records);
|
||||
|
||||
|
||||
/**
|
||||
Information about a position of table within a join order. Used in join
|
||||
optimization.
|
||||
@ -1945,6 +1953,7 @@ bool error_if_full_join(JOIN *join);
|
||||
int report_error(TABLE *table, int error);
|
||||
int safe_index_read(JOIN_TAB *tab);
|
||||
COND *remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value);
|
||||
int test_if_item_cache_changed(List<Cached_item> &list);
|
||||
void calc_used_field_length(THD *thd, JOIN_TAB *join_tab);
|
||||
int join_init_read_record(JOIN_TAB *tab);
|
||||
void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key);
|
||||
|
@ -715,8 +715,8 @@ void st_select_lex_unit::reinit_exec_mechanism()
|
||||
TRUE - error
|
||||
*/
|
||||
|
||||
bool st_select_lex_unit::change_result(select_subselect *new_result,
|
||||
select_subselect *old_result)
|
||||
bool st_select_lex_unit::change_result(select_result_interceptor *new_result,
|
||||
select_result_interceptor *old_result)
|
||||
{
|
||||
bool res= FALSE;
|
||||
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
|
||||
|
Reference in New Issue
Block a user