Commit 99cce189 authored by unknown's avatar unknown

Fixed LP BUG#800696.

The problem was that optimizer removes some outer references (it they are
constant for example) and the list of outer items built during prepare phase is
not actual during execution phase when we need it as the cache parameters.
First solution was use pointer on pointer on outer reference Item and
initialize temporary table on demand. This solved most problem except case
when optimiser also reduce Item which contains outer references ('OR' in
this bug test suite).

The solution is to build the list of outer reference items on execution
phase (after optimization) on demand (just before temporary table creation)
by walking Item tree and finding outer references among Item_ident
(Item_field/Item_ref) and Item_sum items.

Removed depends_on list (because it is not neede any mnore for the cache, in the place where it was used it replaced with upper_refs).

Added processor (collect_outer_ref_processor) and get_cache_parameters() methods to collect outer references (or other expression parameters in future).

mysql-test/r/subselect_cache.result:
  A new test added.
mysql-test/r/subselect_scache.result:
  Changes in creating the cache and its paremeters order or adding arguments of aggregate function (which is a parameter also, but this has no influence on the result).
mysql-test/t/subselect_cache.test:
  Added a new test.
sql/item.cc:
  depends_on removed.
  
  Added processor (collect_outer_ref_processor) and get_cache_parameters() methods to collect outer references.
  
  Item_cache_wrapper collect parameters befor initialization of its cache.
sql/item.h:
  depends_on removed.
  
  Added processor (collect_outer_ref_processor) and get_cache_parameters() methods to collect outer references.
sql/item_cmpfunc.cc:
  depends_on removed.
  
  Added processor (collect_outer_ref_processor) to collect outer references.
sql/item_cmpfunc.h:
  Added processor (collect_outer_ref_processor) to collect outer references.
sql/item_subselect.cc:
  depends_on removed.
  Added processor get_cache_parameters() method to collect outer references.
sql/item_subselect.h:
  depends_on removed.
  Added processor get_cache_parameters() method to collect outer references.
sql/item_sum.cc:
  Added processor (collect_outer_ref_processor) method to collect outer references.
sql/item_sum.h:
  Added processor (collect_outer_ref_processor) and get_cache_parameters() methods to collect outer references.
sql/opt_range.cc:
  depends_on removed.
sql/sql_base.cc:
  depends_on removed.
sql/sql_class.h:
  New iterator added.
sql/sql_expression_cache.cc:
  Build of list of items resolved in outer query done just before creating expression cache on the first execution of the subquery which removes influence of optimizer removing items (all optimization already done).
sql/sql_expression_cache.h:
  Build of list of items resolved in outer query done just before creating expression cache on the first execution of the subquery which removes influence of optimizer removing items (all optimization already done).
sql/sql_lex.cc:
  depends_on removed.
sql/sql_lex.h:
  depends_on removed.
sql/sql_list.h:
  Added add_unique method to add only unique elements to the list.
sql/sql_select.cc:
  Support of new Item list added.
sql/sql_select.h:
  Support of new Item list added.
parent 5df875b0
No related merge requests found
......@@ -3272,6 +3272,7 @@ FROM t2 ) AND table1 .`col_varchar_key` OR table1 .`pk` ;
col_varchar_nokey
drop table t1,t2;
set @@optimizer_switch= default;
set optimizer_switch='subquery_cache=on';
# LP BUG#615378 (incorrect NULL result returning in Item_cache)
CREATE TABLE `t1` (
`pk` int(11) NOT NULL AUTO_INCREMENT,
......@@ -3310,3 +3311,48 @@ GROUP BY field3
HAVING (field3 <= 'h' AND field2 != 4) ;
field2 field3
drop tables t1, t2, t3;
#
# Test aggregate functions as parameters to subquery cache
#
CREATE TABLE t1 ( a INT, b INT, c INT, KEY (a, b));
INSERT INTO t1 VALUES
( 1, 1, 1 ),
( 1, 2, 2 ),
( 1, 3, 3 ),
( 1, 4, 6 ),
( 1, 5, 5 ),
( 1, 9, 13 ),
( 2, 1, 6 ),
( 2, 2, 7 ),
( 2, 3, 8 );
SELECT a, AVG(t1.b),
(SELECT t11.c FROM t1 t11 WHERE t11.a = t1.a AND t11.b = AVG(t1.b)) AS t11c
FROM t1 GROUP BY a;
a AVG(t1.b) t11c
1 4.0000 6
2 2.0000 7
DROP TABLE t1;
#
# Test of LP BUG#800696 (deleting list of Items (OR arguments)
# in optimization)
#
set optimizer_switch='subquery_cache=on,in_to_exists=on';
CREATE TABLE t1 ( f3 int) ;
INSERT INTO t1 VALUES (0),(0);
CREATE TABLE t3 ( f3 int) ;
INSERT INTO t3 VALUES (0),(0);
CREATE TABLE t2 ( f1 int, f2 int, f3 int) ;
INSERT INTO t2 VALUES (7,0,0);
SELECT *
FROM t2, t3
WHERE t2.f2 OR t3.f3 IN
(
SELECT t2.f2
FROM t1
WHERE t2.f1 OR t2.f3 );
f1 f2 f3 f3
7 0 0 0
7 0 0 0
drop tables t1, t2, t3;
# restore default
set @@optimizer_switch= default;
......@@ -232,7 +232,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
3 DEPENDENT SUBQUERY t3 ALL NULL NULL NULL NULL 3 100.00 Using where
Warnings:
Note 1276 Field or reference 'test.t4.a' of SELECT #3 was resolved in SELECT #1
Note 1003 select `test`.`t4`.`b` AS `b`,(select avg((`test`.`t2`.`a` + (select min(`test`.`t3`.`a`) from `test`.`t3` where (`test`.`t3`.`a` >= `test`.`t4`.`a`)))) from `test`.`t2`) AS `(select avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a)) from t2)` from `test`.`t4`
Note 1003 select `test`.`t4`.`b` AS `b`,<expr_cache><`test`.`t4`.`a`>((select avg((`test`.`t2`.`a` + (select min(`test`.`t3`.`a`) from `test`.`t3` where (`test`.`t3`.`a` >= `test`.`t4`.`a`)))) from `test`.`t2`)) AS `(select avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a)) from t2)` from `test`.`t4`
select * from t3 where exists (select * from t2 where t2.b=t3.a);
a
7
......@@ -2835,7 +2835,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 8 100.00
2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 100.00 Using where
Warnings:
Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,<expr_cache><`test`.`t1`.`two`,`test`.`t1`.`one`>(<in_optimizer>((`test`.`t1`.`one`,`test`.`t1`.`two`),<exists>(select `test`.`t2`.`one`,`test`.`t2`.`two` from `test`.`t2` where ((`test`.`t2`.`flag` = '0') and trigcond(((<cache>(`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`))) and trigcond(((<cache>(`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`)))) having (trigcond(<is_not_null_test>(`test`.`t2`.`one`)) and trigcond(<is_not_null_test>(`test`.`t2`.`two`)))))) AS `test` from `test`.`t1`
Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,<expr_cache><`test`.`t1`.`one`,`test`.`t1`.`two`>(<in_optimizer>((`test`.`t1`.`one`,`test`.`t1`.`two`),<exists>(select `test`.`t2`.`one`,`test`.`t2`.`two` from `test`.`t2` where ((`test`.`t2`.`flag` = '0') and trigcond(((<cache>(`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`))) and trigcond(((<cache>(`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`)))) having (trigcond(<is_not_null_test>(`test`.`t2`.`one`)) and trigcond(<is_not_null_test>(`test`.`t2`.`two`)))))) AS `test` from `test`.`t1`
explain extended SELECT one,two from t1 where ROW(one,two) IN (SELECT one,two FROM t2 WHERE flag = 'N');
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 8 100.00
......@@ -2847,7 +2847,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 8 100.00
2 DEPENDENT SUBQUERY t2 ALL NULL NULL NULL NULL 9 100.00 Using where; Using temporary
Warnings:
Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,<expr_cache><`test`.`t1`.`two`,`test`.`t1`.`one`>(<in_optimizer>((`test`.`t1`.`one`,`test`.`t1`.`two`),<exists>(select `test`.`t2`.`one`,`test`.`t2`.`two` from `test`.`t2` where (`test`.`t2`.`flag` = '0') group by `test`.`t2`.`one`,`test`.`t2`.`two` having (trigcond(((<cache>(`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`))) and trigcond(((<cache>(`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`))) and trigcond(<is_not_null_test>(`test`.`t2`.`one`)) and trigcond(<is_not_null_test>(`test`.`t2`.`two`)))))) AS `test` from `test`.`t1`
Note 1003 select `test`.`t1`.`one` AS `one`,`test`.`t1`.`two` AS `two`,<expr_cache><`test`.`t1`.`one`,`test`.`t1`.`two`>(<in_optimizer>((`test`.`t1`.`one`,`test`.`t1`.`two`),<exists>(select `test`.`t2`.`one`,`test`.`t2`.`two` from `test`.`t2` where (`test`.`t2`.`flag` = '0') group by `test`.`t2`.`one`,`test`.`t2`.`two` having (trigcond(((<cache>(`test`.`t1`.`one`) = `test`.`t2`.`one`) or isnull(`test`.`t2`.`one`))) and trigcond(((<cache>(`test`.`t1`.`two`) = `test`.`t2`.`two`) or isnull(`test`.`t2`.`two`))) and trigcond(<is_not_null_test>(`test`.`t2`.`one`)) and trigcond(<is_not_null_test>(`test`.`t2`.`two`)))))) AS `test` from `test`.`t1`
DROP TABLE t1,t2;
CREATE TABLE t1 (a char(5), b char(5));
INSERT INTO t1 VALUES (NULL,'aaa'), ('aaa','aaa');
......
......@@ -1567,6 +1567,7 @@ FROM t2 ) AND table1 .`col_varchar_key` OR table1 .`pk` ;
drop table t1,t2;
set @@optimizer_switch= default;
set optimizer_switch='subquery_cache=on';
#
--echo # LP BUG#615378 (incorrect NULL result returning in Item_cache)
#
......@@ -1614,3 +1615,54 @@ GROUP BY field3
HAVING (field3 <= 'h' AND field2 != 4) ;
--enable_warnings
drop tables t1, t2, t3;
--echo #
--echo # Test aggregate functions as parameters to subquery cache
--echo #
CREATE TABLE t1 ( a INT, b INT, c INT, KEY (a, b));
INSERT INTO t1 VALUES
( 1, 1, 1 ),
( 1, 2, 2 ),
( 1, 3, 3 ),
( 1, 4, 6 ),
( 1, 5, 5 ),
( 1, 9, 13 ),
( 2, 1, 6 ),
( 2, 2, 7 ),
( 2, 3, 8 );
SELECT a, AVG(t1.b),
(SELECT t11.c FROM t1 t11 WHERE t11.a = t1.a AND t11.b = AVG(t1.b)) AS t11c
FROM t1 GROUP BY a;
DROP TABLE t1;
--echo #
--echo # Test of LP BUG#800696 (deleting list of Items (OR arguments)
--echo # in optimization)
--echo #
set optimizer_switch='subquery_cache=on,in_to_exists=on';
CREATE TABLE t1 ( f3 int) ;
INSERT INTO t1 VALUES (0),(0);
CREATE TABLE t3 ( f3 int) ;
INSERT INTO t3 VALUES (0),(0);
CREATE TABLE t2 ( f1 int, f2 int, f3 int) ;
INSERT INTO t2 VALUES (7,0,0);
SELECT *
FROM t2, t3
WHERE t2.f2 OR t3.f3 IN
(
SELECT t2.f2
FROM t1
WHERE t2.f1 OR t2.f3 );
drop tables t1, t2, t3;
--echo # restore default
set @@optimizer_switch= default;
......@@ -31,6 +31,16 @@ const String my_null_string("NULL", 4, default_charset_info);
static int save_field_in_field(Field *from, bool *null_value,
Field *to, bool no_conversions);
/**
Compare two Items for List<Item>::add_unique()
*/
bool cmp_items(Item *a, Item *b)
{
return a->eq(b, FALSE);
}
/****************************************************************************/
/* Hybrid_type_traits {_real} */
......@@ -651,14 +661,14 @@ Item* Item::transform(Item_transformer transformer, uchar *arg)
A pointer to created wrapper item if successful, NULL - otherwise
*/
Item* Item::set_expr_cache(THD *thd, List<Item *> &depends_on)
Item* Item::set_expr_cache(THD *thd)
{
DBUG_ENTER("Item::set_expr_cache");
Item_cache_wrapper *wrapper;
if ((wrapper= new Item_cache_wrapper(this)) &&
!wrapper->fix_fields(thd, (Item**)&wrapper))
{
if (wrapper->set_cache(thd, depends_on))
if (wrapper->set_cache(thd))
DBUG_RETURN(NULL);
DBUG_RETURN(wrapper);
}
......@@ -742,6 +752,15 @@ bool Item_ident::remove_dependence_processor(uchar * arg)
}
bool Item_ident::collect_outer_ref_processor(uchar *param)
{
Collect_deps_prm *prm= (Collect_deps_prm *)param;
if (depended_from && depended_from->nest_level < prm->nest_level)
prm->parameters->add_unique(this, &cmp_items);
return FALSE;
}
/**
Store the pointer to this item field into a list if not already there.
......@@ -4357,9 +4376,6 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
((ref_type == REF_ITEM || ref_type == FIELD_ITEM) ?
(Item_ident*) (*reference) :
0));
context->select_lex->
register_dependency_item(last_checked_context->select_lex,
reference);
/*
A reference to a view field had been found and we
substituted it instead of this Item (find_field_in_tables
......@@ -4460,9 +4476,6 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
mark_as_dependent(thd, last_checked_context->select_lex,
context->select_lex, rf,
rf);
context->select_lex->
register_dependency_item(last_checked_context->select_lex,
reference);
return 0;
}
......@@ -4471,9 +4484,6 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
mark_as_dependent(thd, last_checked_context->select_lex,
context->select_lex,
this, (Item_ident*)*reference);
context->select_lex->
register_dependency_item(last_checked_context->select_lex,
reference);
if (last_checked_context->select_lex->having_fix_field)
{
Item_ref *rf;
......@@ -6304,9 +6314,6 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
refer_type == FIELD_ITEM) ?
(Item_ident*) (*reference) :
0));
context->select_lex->
register_dependency_item(last_checked_context->select_lex,
reference);
/*
view reference found, we substituted it instead of this
Item, so can quit
......@@ -6357,9 +6364,6 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
thd->change_item_tree(reference, fld);
mark_as_dependent(thd, last_checked_context->select_lex,
thd->lex->current_select, fld, fld);
context->select_lex->
register_dependency_item(last_checked_context->select_lex,
reference);
/*
A reference is resolved to a nest level that's outer or the same as
the nest level of the enclosing set function : adjust the value of
......@@ -6383,9 +6387,6 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
DBUG_ASSERT(*ref && (*ref)->fixed);
mark_as_dependent(thd, last_checked_context->select_lex,
context->select_lex, this, this);
context->select_lex->
register_dependency_item(last_checked_context->select_lex,
reference);
/*
A reference is resolved to a nest level that's outer or the same as
the nest level of the enclosing set function : adjust the value of
......@@ -6400,12 +6401,6 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
}
else if (ref_type() != VIEW_REF)
{
if (depended_from && reference)
{
DBUG_ASSERT(context->select_lex != get_depended_from());
context->select_lex->register_dependency_item(get_depended_from(),
reference);
}
/*
It could be that we're referring to something that's in ancestor selects.
We must make an appropriate mark_as_dependent() call for each such
......@@ -6914,6 +6909,7 @@ Item_cache_wrapper::Item_cache_wrapper(Item *item_arg)
unsigned_flag= orig_item->unsigned_flag;
name= item_arg->name;
name_length= item_arg->name_length;
with_subselect= orig_item->with_subselect;
if ((expr_value= Item_cache::get_cache(orig_item)))
expr_value->setup(orig_item);
......@@ -6922,11 +6918,28 @@ Item_cache_wrapper::Item_cache_wrapper(Item *item_arg)
}
/**
Initialize the cache if it is needed
*/
void Item_cache_wrapper::init_on_demand()
{
if (!expr_cache->is_inited())
{
orig_item->get_cache_parameters(parameters);
expr_cache->init();
}
}
void Item_cache_wrapper::print(String *str, enum_query_type query_type)
{
str->append(func_name());
if (expr_cache)
{
init_on_demand();
expr_cache->print(str, query_type);
}
else
str->append(STRING_WITH_LEN("<<DISABLED>>"));
str->append('(');
......@@ -6962,6 +6975,7 @@ void Item_cache_wrapper::cleanup()
expr_cache= 0;
/* expr_value is Item so it will be destroyed from list of Items */
expr_value= 0;
parameters.empty();
DBUG_VOID_RETURN;
}
......@@ -6982,11 +6996,11 @@ void Item_cache_wrapper::cleanup()
@retval TRUE Error
*/
bool Item_cache_wrapper::set_cache(THD *thd, List<Item*> &depends_on)
bool Item_cache_wrapper::set_cache(THD *thd)
{
DBUG_ENTER("Item_cache_wrapper::set_cache");
DBUG_ASSERT(expr_cache == 0);
expr_cache= new Expression_cache_tmptable(thd, depends_on, expr_value);
expr_cache= new Expression_cache_tmptable(thd, parameters, expr_value);
DBUG_RETURN(expr_cache == NULL);
}
......@@ -7011,6 +7025,7 @@ Item *Item_cache_wrapper::check_cache()
{
Expression_cache_tmptable::result res;
Item *cached_value;
init_on_demand();
res= expr_cache->check_value(&cached_value);
if (res == Expression_cache_tmptable::HIT)
DBUG_RETURN(cached_value);
......
......@@ -1155,6 +1155,15 @@ public:
{
return FALSE;
}
struct Collect_deps_prm
{
int nest_level;
List<Item> *parameters;
};
/**
Collect outer references
*/
virtual bool collect_outer_ref_processor(uchar *arg) {return FALSE; }
/**
Find a function of a given type
......@@ -1249,7 +1258,7 @@ public:
{ return Field::GEOM_GEOMETRY; };
String *check_well_formed_result(String *str, bool send_error= 0);
bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs);
Item* set_expr_cache(THD *thd, List<Item*> &depends_on);
Item* set_expr_cache(THD *thd);
virtual Item *get_cached_item() { return NULL; }
virtual Item_equal *get_item_equal() { return NULL; }
......@@ -1273,9 +1282,25 @@ public:
walk(&Item::view_used_tables_processor, 0, (uchar *) view);
return view->view_used_tables;
}
/**
Collect and add to the list cache parameters for this Item.
@note Now implemented only for subqueries and in_optimizer,
if we need it for general function then this method should
be defined for Item_func.
*/
virtual void get_cache_parameters(List<Item> &parameters) { };
};
/**
Compare two Items for List<Item>::add_unique()
*/
bool cmp_items(Item *a, Item *b);
/*
Class to be used to enumerate all field references in an item tree. This
includes references to outside but not fields of the tables within a
......@@ -1677,6 +1702,10 @@ public:
virtual void print(String *str, enum_query_type query_type);
virtual bool change_context_processor(uchar *cntx)
{ context= (Name_resolution_context *)cntx; return FALSE; }
/**
Collect outer references
*/
virtual bool collect_outer_ref_processor(uchar *arg);
friend bool insert_fields(THD *thd, Name_resolution_context *context,
const char *db_name,
const char *table_name, List_iterator<Item> *it,
......@@ -2739,8 +2768,11 @@ private:
*/
Item_cache *expr_value;
List<Item> parameters;
Item *check_cache();
inline void cache();
void cache();
void init_on_demand();
public:
Item_cache_wrapper(Item *item_arg);
......@@ -2750,7 +2782,7 @@ public:
enum Type type() const { return EXPR_CACHE_ITEM; }
enum Type real_type() const { return orig_item->type(); }
bool set_cache(THD *thd, List<Item*> &depends_on);
bool set_cache(THD *thd);
bool fix_fields(THD *thd, Item **it);
void fix_length_and_dec() {}
......@@ -2832,6 +2864,9 @@ public:
if (result_type() == ROW_RESULT)
orig_item->bring_value();
}
virtual bool is_expensive() { return orig_item->is_expensive(); }
bool is_expensive_processor(uchar *arg)
{ return orig_item->is_expensive_processor(arg); }
bool check_vcol_func_processor(uchar *arg)
{
return trace_unsupported_by_check_vcol_func_processor("cache");
......
......@@ -1464,32 +1464,41 @@ Item *Item_in_optimizer::expr_cache_insert_transformer(uchar *thd_arg)
DBUG_ENTER("Item_in_optimizer::expr_cache_insert_transformer");
if (args[1]->type() != Item::SUBSELECT_ITEM)
DBUG_RETURN(this); // MAX/MIN transformed => do nothing
List<Item*> &depends_on= ((Item_subselect *)args[1])->depends_on;
if (expr_cache)
DBUG_RETURN(expr_cache);
if (args[1]->expr_cache_is_needed(thd) &&
(expr_cache= set_expr_cache(thd)))
DBUG_RETURN(expr_cache);
DBUG_RETURN(this);
}
/**
Collect and add to the list cache parameters for this Item.
@param parameters The list where to add parameters
*/
void Item_in_optimizer::get_cache_parameters(List<Item> &parameters)
{
/* Add left expression to the list of the parameters of the subquery */
if (args[0]->cols() == 1)
depends_on.push_front((Item**)args);
parameters.add_unique(args[0], &cmp_items);
else
{
for (uint i= 0; i < args[0]->cols(); i++)
{
depends_on.push_front(args[0]->addr(i));
parameters.add_unique(args[0]->element_index(i), &cmp_items);
}
}
if (args[1]->expr_cache_is_needed(thd) &&
(expr_cache= set_expr_cache(thd, depends_on)))
DBUG_RETURN(expr_cache);
/* no cache => return list in original state just to be safe */
for (uint i= 0; i < args[0]->cols(); i++)
depends_on.pop();
DBUG_RETURN(this);
args[1]->get_cache_parameters(parameters);
}
/*
The implementation of optimized \<outer expression\> [NOT] IN \<subquery\>
predicates. The implementation works as follows.
......
......@@ -259,6 +259,7 @@ public:
bool is_expensive();
void set_join_tab_idx(uint join_tab_idx_arg)
{ args[1]->set_join_tab_idx(join_tab_idx_arg); }
virtual void get_cache_parameters(List<Item> &parameters);
};
class Comp_creator
......
......@@ -128,7 +128,6 @@ void Item_subselect::cleanup()
}
if (engine)
engine->cleanup();
depends_on.empty();
reset();
value_assigned= 0;
expr_cache= 0;
......@@ -583,6 +582,12 @@ bool Item_subselect::exec()
}
void Item_subselect::get_cache_parameters(List<Item> &parameters)
{
Collect_deps_prm prm= { unit->first_select()->nest_level, &parameters };
walk(&Item::collect_outer_ref_processor, TRUE, (uchar*)&prm);
}
int Item_in_subselect::optimize(double *out_rows, double *cost)
{
int res;
......@@ -647,7 +652,7 @@ int Item_in_subselect::optimize(double *out_rows, double *cost)
bool Item_subselect::expr_cache_is_needed(THD *thd)
{
return (depends_on.elements &&
return ((engine->uncacheable() & UNCACHEABLE_DEPENDENT) &&
engine->cols() == 1 &&
optimizer_flag(thd, OPTIMIZER_SWITCH_SUBQUERY_CACHE) &&
!(engine->uncacheable() & (UNCACHEABLE_RAND |
......@@ -675,8 +680,7 @@ bool Item_subselect::expr_cache_is_needed(THD *thd)
bool Item_in_subselect::expr_cache_is_needed(THD *thd)
{
return (depends_on.elements &&
optimizer_flag(thd, OPTIMIZER_SWITCH_SUBQUERY_CACHE) &&
return (optimizer_flag(thd, OPTIMIZER_SWITCH_SUBQUERY_CACHE) &&
!(engine->uncacheable() & (UNCACHEABLE_RAND |
UNCACHEABLE_SIDEEFFECT)));
}
......@@ -1009,7 +1013,7 @@ Item* Item_singlerow_subselect::expr_cache_insert_transformer(uchar *thd_arg)
DBUG_RETURN(expr_cache);
if (expr_cache_is_needed(thd) &&
(expr_cache= set_expr_cache(thd, depends_on)))
(expr_cache= set_expr_cache(thd)))
DBUG_RETURN(expr_cache);
DBUG_RETURN(this);
}
......@@ -1270,7 +1274,7 @@ Item* Item_exists_subselect::expr_cache_insert_transformer(uchar *thd_arg)
DBUG_RETURN(expr_cache);
if (substype() == EXISTS_SUBS && expr_cache_is_needed(thd) &&
(expr_cache= set_expr_cache(thd, depends_on)))
(expr_cache= set_expr_cache(thd)))
DBUG_RETURN(expr_cache);
DBUG_RETURN(this);
}
......@@ -5725,3 +5729,4 @@ end:
void subselect_table_scan_engine::cleanup()
{
}
......@@ -102,14 +102,6 @@ public:
List<Ref_to_outside> upper_refs;
st_select_lex *parent_select;
/**
List of references on items subquery depends on (externally resolved);
@note We can't store direct links on Items because it could be
substituted with other item (for example for grouping).
*/
List<Item*> depends_on;
/*
TRUE<=>Table Elimination has made it redundant to evaluate this select
(and so it is not part of QEP, etc)
......@@ -225,6 +217,7 @@ public:
@retval FALSE otherwise
*/
bool is_expensive_processor(uchar *arg) { return TRUE; }
/**
Get the SELECT_LEX structure associated with this Item.
@return the SELECT_LEX structure associated with this Item
......@@ -232,6 +225,7 @@ public:
st_select_lex* get_select_lex();
const char *func_name() const { DBUG_ASSERT(0); return "subselect"; }
virtual bool expr_cache_is_needed(THD *);
virtual void get_cache_parameters(List<Item> &parameters);
friend class select_result_interceptor;
friend class Item_in_optimizer;
......
......@@ -319,7 +319,6 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref)
if (aggr_level >= 0)
{
ref_by= ref;
thd->lex->current_select->register_dependency_item(aggr_sel, ref);
/* Add the object to the list of registered objects assigned to aggr_sel */
if (!aggr_sel->inner_sum_func_list)
next= this;
......@@ -356,6 +355,16 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref)
}
bool Item_sum::collect_outer_ref_processor(uchar *param)
{
Collect_deps_prm *prm= (Collect_deps_prm *)param;
SELECT_LEX *ds;
if ((ds= depended_from()) && ds->nest_level < prm->nest_level)
prm->parameters->add_unique(this, &cmp_items);
return FALSE;
}
Item_sum::Item_sum(List<Item> &list) :arg_count(list.elements),
forced_const(FALSE)
{
......
......@@ -374,6 +374,7 @@ public:
virtual Field *create_tmp_field(bool group, TABLE *table,
uint convert_blob_length);
bool walk(Item_processor processor, bool walk_subquery, uchar *argument);
virtual bool collect_outer_ref_processor(uchar *param);
bool init_sum_func_check(THD *thd);
bool check_sum_func(THD *thd, Item **ref);
bool register_sum_func(THD *thd, Item **ref);
......
......@@ -11731,17 +11731,19 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
Disallow loose index scan if the MIN/MAX argument field is referenced by
a subquery in the WHERE clause.
*/
if (cond_type == Item::SUBSELECT_ITEM)
{
Item_subselect *subs_cond= (Item_subselect*) cond;
if (subs_cond->is_correlated)
{
DBUG_ASSERT(subs_cond->depends_on.elements > 0);
List_iterator_fast<Item*> li(subs_cond->depends_on);
Item **dep;
DBUG_ASSERT(subs_cond->upper_refs.elements > 0);
List_iterator_fast<Item_subselect::Ref_to_outside>
li(subs_cond->upper_refs);
Item_subselect::Ref_to_outside *dep;
while ((dep= li++))
{
if ((*dep)->eq(min_max_arg_item, FALSE))
if (dep->item->eq(min_max_arg_item, FALSE))
DBUG_RETURN(FALSE);
}
}
......
......@@ -6454,11 +6454,6 @@ find_field_in_tables(THD *thd, Item_ident *item,
{
mark_select_range_as_dependent(thd, last_select, current_sel,
found, *ref, item);
if (item->can_be_depended)
{
DBUG_ASSERT((*ref) == (Item*)item);
current_sel->register_dependency_item(last_select, ref);
}
}
}
return found;
......
......@@ -73,6 +73,22 @@ public:
};
/**
Item iterator over List_iterator_fast for Items
*/
class Item_iterator_list: public Item_iterator
{
List_iterator<Item> list;
public:
Item_iterator_list(List_iterator<Item> &arg_list):
list(arg_list) {}
void open() { list.rewind(); }
Item *next() { return (list++); }
void close() {}
};
/**
Item iterator over Item interface for rows
*/
......
......@@ -23,9 +23,9 @@
ulong subquery_cache_miss, subquery_cache_hit;
Expression_cache_tmptable::Expression_cache_tmptable(THD *thd,
List<Item*> &dependants,
Item *value)
:cache_table(NULL), table_thd(thd), list(&dependants), val(value),
List<Item> &dependants,
Item *value)
:cache_table(NULL), table_thd(thd), items(dependants), val(value),
inited (0)
{
DBUG_ENTER("Expression_cache_tmptable::Expression_cache_tmptable");
......@@ -60,50 +60,32 @@ static uint field_enumerator(uchar *arg)
void Expression_cache_tmptable::init()
{
List_iterator<Item*> li(*list);
Item_iterator_ref_list it(li);
Item **item;
List_iterator<Item> li(items);
Item_iterator_list it(li);
uint field_counter;
DBUG_ENTER("Expression_cache_tmptable::init");
DBUG_ASSERT(!inited);
inited= TRUE;
cache_table= NULL;
while ((item= li++))
{
DBUG_ASSERT(item);
if (*item)
{
DBUG_ASSERT((*item)->fixed);
items.push_back((*item));
}
else
{
/*
This is possible when optimizer already executed this subquery and
optimized out the condition predicate.
*/
li.remove();
}
}
if (list->elements == 0)
if (items.elements == 0)
{
DBUG_PRINT("info", ("All parameters were removed by optimizer."));
DBUG_VOID_RETURN;
}
/* add result field */
items.push_front(val);
cache_table_param.init();
/* dependent items and result */
cache_table_param.field_count= list->elements + 1;
cache_table_param.field_count= items.elements;
/* postpone table creation to index description */
cache_table_param.skip_create_table= 1;
items.push_front(val);
if (!(cache_table= create_tmp_table(table_thd, &cache_table_param,
items, (ORDER*) NULL,
FALSE, FALSE,
FALSE, TRUE,
((table_thd->options |
TMP_TABLE_ALL_COLUMNS) &
~(OPTION_BIG_TABLES |
......@@ -122,16 +104,13 @@ void Expression_cache_tmptable::init()
goto error;
}
/* This list do not contain result field */
it.open();
field_counter=1;
field_counter= 1;
if (cache_table->alloc_keys(1) ||
cache_table->add_tmp_key(0, items.elements - 1, &field_enumerator,
(uchar*)&field_counter, TRUE) ||
ref.tmp_table_index_lookup_init(table_thd, cache_table->key_info, it,
TRUE))
TRUE, 1 /* skip result field*/))
{
DBUG_PRINT("error", ("creating index failed"));
goto error;
......@@ -192,13 +171,6 @@ Expression_cache::result Expression_cache_tmptable::check_value(Item **value)
int res;
DBUG_ENTER("Expression_cache_tmptable::check_value");
/*
We defer cache initialization to get item references that are
used at the execution phase.
*/
if (!inited)
init();
if (cache_table)
{
DBUG_PRINT("info", ("status: %u has_record %u",
......@@ -274,16 +246,17 @@ err:
void Expression_cache_tmptable::print(String *str, enum_query_type query_type)
{
List_iterator<Item*> li(*list);
Item **item;
List_iterator<Item> li(items);
Item *item;
bool is_first= TRUE;
str->append('<');
li++; // skip result field
while ((item= li++))
{
if (!is_first)
str->append(',');
(*item)->print(str, query_type);
item->print(str, query_type);
is_first= FALSE;
}
str->append('>');
......
......@@ -37,6 +37,15 @@ public:
Print cache parameters
*/
virtual void print(String *str, enum_query_type query_type)= 0;
/**
Is this cache initialized
*/
virtual bool is_inited()= 0;
/**
Initialize this cache
*/
virtual void init()= 0;
};
struct st_table_ref;
......@@ -51,15 +60,16 @@ class Item_field;
class Expression_cache_tmptable :public Expression_cache
{
public:
Expression_cache_tmptable(THD *thd, List<Item*> &dependants, Item *value);
Expression_cache_tmptable(THD *thd, List<Item> &dependants, Item *value);
virtual ~Expression_cache_tmptable();
virtual result check_value(Item **value);
virtual my_bool put_value(Item *value);
void print(String *str, enum_query_type query_type);
bool is_inited() { return inited; };
void init();
private:
void init();
/* tmp table parameters */
TMP_TABLE_PARAM cache_table_param;
......@@ -71,10 +81,8 @@ private:
struct st_table_ref ref;
/* Cached result */
Item_field *cached_result;
/* List of references to the parameters of the expression */
List<Item*> *list;
/* List of items */
List<Item> items;
/* List of parameter items */
List<Item> &items;
/* Value Item example */
Item *val;
/* Set on if the object has been succesfully initialized with init() */
......
......@@ -1879,59 +1879,6 @@ void st_select_lex_unit::exclude_tree()
}
/**
Register reference to an item which the subqueries depends on
@param def_sel select against which the item is resolved
@param dependency reference to the item
@details
This function puts the reference dependency to an item that is either an
outer field or an aggregate function resolved against an outer select into
the list 'depends_on'. It adds it to the 'depends_on' lists for each
subquery between this one and 'def_sel' - the subquery against which the
item is resolved.
*/
void st_select_lex::register_dependency_item(st_select_lex *def_sel,
Item **dependency)
{
SELECT_LEX *s= this;
DBUG_ENTER("st_select_lex::register_dependency_item");
DBUG_ASSERT(this != def_sel);
DBUG_ASSERT(*dependency);
//psergey-add-stupid:
while (def_sel->merged_into)
def_sel= def_sel->merged_into;
//:eof
do
{
/* check duplicates */
List_iterator_fast<Item*> li(s->master_unit()->item->depends_on);
Item **dep;
while ((dep= li++))
{
if ((*dep)->eq(*dependency, FALSE))
{
DBUG_PRINT("info", ("dependency %s already present",
((*dependency)->name ?
(*dependency)->name :
"<no name>")));
DBUG_VOID_RETURN;
}
}
s->master_unit()->item->depends_on.push_back(dependency);
DBUG_PRINT("info", ("depends_on: Select: %d added: %s",
s->select_number,
((*dependency)->name ?
(*dependency)->name :
"<no name>")));
} while ((s= s->outer_select()) != def_sel);
DBUG_VOID_RETURN;
}
/*
st_select_lex_node::mark_as_dependent mark all st_select_lex struct from
this to 'last' as dependent
......
......@@ -793,7 +793,6 @@ public:
inline bool is_subquery_function() { return master_unit()->item != 0; }
bool mark_as_dependent(THD *thd, st_select_lex *last, Item *dependency);
void register_dependency_item(st_select_lex *last, Item **dependency);
bool set_braces(bool value);
bool inc_in_sum_expr();
......
......@@ -152,6 +152,7 @@ struct list_node :public Sql_alloc
}
};
typedef bool List_eq(void *a, void *b);
extern MYSQL_PLUGIN_IMPORT list_node end_of_list;
......@@ -295,6 +296,16 @@ public:
inline void **head_ref() { return first != &end_of_list ? &first->info : 0; }
inline bool is_empty() { return first == &end_of_list ; }
inline list_node *last_ref() { return &end_of_list; }
inline bool add_unique(void *info, List_eq *eq)
{
list_node *node= first;
for (;
node != &end_of_list && (!(*eq)(node->info, info));
node= node->next) ;
if (node == &end_of_list)
return push_back(info);
return 1;
}
friend class base_list_iterator;
friend class error_list;
friend class error_list_iterator;
......@@ -465,6 +476,8 @@ public:
inline void concat(List<T> *list) { base_list::concat(list); }
inline void disjoin(List<T> *list) { base_list::disjoin(list); }
inline void prepand(List<T> *list) { base_list::prepand(list); }
inline bool add_unique(T *a, bool (*eq)(T *a, T *b))
{ return base_list::add_unique(a, (List_eq *)eq); }
void delete_elements(void)
{
list_node *element,*next;
......
......@@ -9613,6 +9613,7 @@ bool JOIN_TAB::preread_init()
@param thd Thread handle
@param tmp_key The temporary table key
@param it The iterator of items for lookup in the key
@param skip Number of fields from the beginning to skip
@details
Build TABLE_REF object for lookup in the key 'tmp_key' using items
......@@ -9625,9 +9626,11 @@ bool JOIN_TAB::preread_init()
bool TABLE_REF::tmp_table_index_lookup_init(THD *thd,
KEY *tmp_key,
Item_iterator &it,
bool value)
bool value,
uint skip)
{
uint tmp_key_parts= tmp_key->key_parts;
uint i;
DBUG_ENTER("TABLE_REF::tmp_table_index_lookup_init");
key= 0; /* The only temp table index. */
......@@ -9648,7 +9651,8 @@ bool TABLE_REF::tmp_table_index_lookup_init(THD *thd,
uchar *cur_ref_buff= key_buff;
it.open();
for (uint i= 0; i < tmp_key_parts; i++, cur_key_part++, ref_key++)
for (i= 0; i < skip; i++) it.next();
for (i= 0; i < tmp_key_parts; i++, cur_key_part++, ref_key++)
{
Item *item= it.next();
DBUG_ASSERT(item);
......
......@@ -135,7 +135,7 @@ typedef struct st_table_ref
bool disable_cache;
bool tmp_table_index_lookup_init(THD *thd, KEY *tmp_key, Item_iterator &it,
bool value);
bool value, uint skip= 0);
} TABLE_REF;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment