1
0
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:
Sergey Petrunya
2010-01-28 16:48:33 +03:00
parent 742afd8eae
commit f47b2d38f6
12 changed files with 1119 additions and 145 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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)
{

View File

@ -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

View File

@ -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; }
};

View File

@ -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

View File

@ -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.

View File

@ -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 ();

View File

@ -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)
{

View File

@ -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);

View File

@ -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())