mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 12:32:27 +01:00
99cce18955
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.
263 lines
7.3 KiB
C++
263 lines
7.3 KiB
C++
/* Copyright (C) 2010-2011 Monty Program Ab & Oleksandr Byelkin
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
|
#include "mysql_priv.h"
|
|
#include "sql_select.h"
|
|
|
|
/*
|
|
Expression cache is used only for caching subqueries now, so its statistic
|
|
variables we call subquery_cache*.
|
|
*/
|
|
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), items(dependants), val(value),
|
|
inited (0)
|
|
{
|
|
DBUG_ENTER("Expression_cache_tmptable::Expression_cache_tmptable");
|
|
DBUG_VOID_RETURN;
|
|
};
|
|
|
|
|
|
/**
|
|
Field enumerator for TABLE::add_tmp_key
|
|
|
|
@param arg reference variable with current field number
|
|
|
|
@return field number
|
|
*/
|
|
|
|
static uint field_enumerator(uchar *arg)
|
|
{
|
|
return ((uint*)arg)[0]++;
|
|
}
|
|
|
|
|
|
/**
|
|
Initialize temporary table and auxiliary structures for the expression
|
|
cache
|
|
|
|
@details
|
|
The function creates a temporary table for the expression cache, defines
|
|
the search index and initializes auxiliary search structures used to check
|
|
whether a given set of of values of the expression parameters is in some
|
|
cache entry.
|
|
*/
|
|
|
|
void Expression_cache_tmptable::init()
|
|
{
|
|
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;
|
|
|
|
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= items.elements;
|
|
/* postpone table creation to index description */
|
|
cache_table_param.skip_create_table= 1;
|
|
|
|
if (!(cache_table= create_tmp_table(table_thd, &cache_table_param,
|
|
items, (ORDER*) NULL,
|
|
FALSE, TRUE,
|
|
((table_thd->options |
|
|
TMP_TABLE_ALL_COLUMNS) &
|
|
~(OPTION_BIG_TABLES |
|
|
TMP_TABLE_FORCE_MYISAM)),
|
|
HA_POS_ERROR,
|
|
(char *)"subquery-cache-table",
|
|
TRUE)))
|
|
{
|
|
DBUG_PRINT("error", ("create_tmp_table failed, caching switched off"));
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
if (cache_table->s->db_type() != heap_hton)
|
|
{
|
|
DBUG_PRINT("error", ("we need only heap table"));
|
|
goto error;
|
|
}
|
|
|
|
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, 1 /* skip result field*/))
|
|
{
|
|
DBUG_PRINT("error", ("creating index failed"));
|
|
goto error;
|
|
}
|
|
cache_table->s->keys= 1;
|
|
ref.null_rejecting= 1;
|
|
ref.disable_cache= FALSE;
|
|
ref.has_record= 0;
|
|
ref.use_count= 0;
|
|
|
|
|
|
if (open_tmp_table(cache_table))
|
|
{
|
|
DBUG_PRINT("error", ("Opening (creating) temporary table failed"));
|
|
goto error;
|
|
}
|
|
|
|
if (!(cached_result= new Item_field(cache_table->field[0])))
|
|
{
|
|
DBUG_PRINT("error", ("Creating Item_field failed"));
|
|
goto error;
|
|
}
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
error:
|
|
/* switch off cache */
|
|
free_tmp_table(table_thd, cache_table);
|
|
cache_table= NULL;
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
|
|
Expression_cache_tmptable::~Expression_cache_tmptable()
|
|
{
|
|
if (cache_table)
|
|
free_tmp_table(table_thd, cache_table);
|
|
}
|
|
|
|
|
|
/**
|
|
Check if a given set of parameters of the expression is in the cache
|
|
|
|
@param [out] value the expression value found in the cache if any
|
|
|
|
@details
|
|
For a given set of the parameters of the expression the function
|
|
checks whether it can be found in some entry of the cache. If so
|
|
the function returns the result of the expression extracted from
|
|
the cache.
|
|
|
|
@retval Expression_cache::HIT if the set of parameters is in the cache
|
|
@retval Expression_cache::MISS - otherwise
|
|
*/
|
|
|
|
Expression_cache::result Expression_cache_tmptable::check_value(Item **value)
|
|
{
|
|
int res;
|
|
DBUG_ENTER("Expression_cache_tmptable::check_value");
|
|
|
|
if (cache_table)
|
|
{
|
|
DBUG_PRINT("info", ("status: %u has_record %u",
|
|
(uint)cache_table->status, (uint)ref.has_record));
|
|
if ((res= join_read_key2(table_thd, NULL, cache_table, &ref)) == 1)
|
|
DBUG_RETURN(ERROR);
|
|
if (res)
|
|
{
|
|
subquery_cache_miss++;
|
|
DBUG_RETURN(MISS);
|
|
}
|
|
|
|
subquery_cache_hit++;
|
|
*value= cached_result;
|
|
DBUG_RETURN(Expression_cache::HIT);
|
|
}
|
|
DBUG_RETURN(Expression_cache::MISS);
|
|
}
|
|
|
|
|
|
/**
|
|
Put a new entry into the expression cache
|
|
|
|
@param value the result of the expression to be put into the cache
|
|
|
|
@details
|
|
The function evaluates 'value' and puts the result into the cache as the
|
|
result of the expression for the current set of parameters.
|
|
|
|
@retval FALSE OK
|
|
@retval TRUE Error
|
|
*/
|
|
|
|
my_bool Expression_cache_tmptable::put_value(Item *value)
|
|
{
|
|
int error;
|
|
DBUG_ENTER("Expression_cache_tmptable::put_value");
|
|
DBUG_ASSERT(inited);
|
|
|
|
if (!cache_table)
|
|
{
|
|
DBUG_PRINT("info", ("No table so behave as we successfully put value"));
|
|
DBUG_RETURN(FALSE);
|
|
}
|
|
|
|
*(items.head_ref())= value;
|
|
fill_record(table_thd, cache_table->field, items, TRUE, TRUE);
|
|
if (table_thd->is_error())
|
|
goto err;;
|
|
|
|
if ((error= cache_table->file->ha_write_tmp_row(cache_table->record[0])))
|
|
{
|
|
/* create_myisam_from_heap will generate error if needed */
|
|
if (cache_table->file->is_fatal_error(error, HA_CHECK_DUP) &&
|
|
create_internal_tmp_table_from_heap(table_thd, cache_table,
|
|
cache_table_param.start_recinfo,
|
|
&cache_table_param.recinfo,
|
|
error, 1))
|
|
goto err;
|
|
}
|
|
cache_table->status= 0; /* cache_table->record contains an existed record */
|
|
ref.has_record= TRUE; /* the same as above */
|
|
DBUG_PRINT("info", ("has_record: TRUE status: 0"));
|
|
|
|
DBUG_RETURN(FALSE);
|
|
|
|
err:
|
|
free_tmp_table(table_thd, cache_table);
|
|
cache_table= NULL;
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
|
|
|
|
void Expression_cache_tmptable::print(String *str, enum_query_type query_type)
|
|
{
|
|
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);
|
|
is_first= FALSE;
|
|
}
|
|
str->append('>');
|
|
}
|