1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-01 03:47:19 +03:00

Subquery cache (MWL#66) added.

libmysqld/Makefile.am:
  The new file added.
mysql-test/r/index_merge_myisam.result:
  subquery_cache optimization option added.
mysql-test/r/myisam_mrr.result:
  subquery_cache optimization option added.
mysql-test/r/subquery_cache.result:
  The subquery cache tests added.
mysql-test/r/subselect3.result:
  Subquery cache switched off to avoid changing read statistics.
mysql-test/r/subselect3_jcl6.result:
  Subquery cache switched off to avoid changing read statistics.
mysql-test/r/subselect_no_mat.result:
  subquery_cache optimization option added.
mysql-test/r/subselect_no_opts.result:
  subquery_cache optimization option added.
mysql-test/r/subselect_no_semijoin.result:
  subquery_cache optimization option added.
mysql-test/r/subselect_sj.result:
  subquery_cache optimization option added.
mysql-test/r/subselect_sj_jcl6.result:
  subquery_cache optimization option added.
mysql-test/t/subquery_cache.test:
  The subquery cache tests added.
mysql-test/t/subselect3.test:
  Subquery cache switched off to avoid changing read statistics.
sql/CMakeLists.txt:
  The new file added.
sql/Makefile.am:
  The new files added.
sql/item.cc:
  Expression cache item (Item_cache_wrapper) added.
  Item_ref and Item_field fixed for correct usage of result field and fast resolwing in SP.
sql/item.h:
  Expression cache item (Item_cache_wrapper) added.
  Item_ref and Item_field fixed for correct usage of result field and fast resolwing in SP.
sql/item_cmpfunc.cc:
  Subquery cache added.
sql/item_cmpfunc.h:
  Subquery cache added.
sql/item_subselect.cc:
  Subquery cache added.
sql/item_subselect.h:
  Subquery cache added.
sql/item_sum.cc:
  Registration of subquery parameters added.
sql/mysql_priv.h:
  subquery_cache optimization option added.
sql/mysqld.cc:
  subquery_cache optimization option added.
sql/opt_range.cc:
  Fix due to subquery cache.
sql/opt_subselect.cc:
  Parameters of the function cahnged.
sql/procedure.h:
  .h file guard added.
sql/sql_base.cc:
  Registration of subquery parameters added.
sql/sql_class.cc:
  Option to allow add indeces to temporary table.
sql/sql_class.h:
  Item iterators added.
  Option to allow add indeces to temporary table.
sql/sql_expression_cache.cc:
  Expression cache for caching subqueries added.
sql/sql_expression_cache.h:
  Expression cache for caching subqueries added.
sql/sql_lex.cc:
  Registration of subquery parameters added.
sql/sql_lex.h:
  Registration of subqueries and subquery parameters added.
sql/sql_select.cc:
  Subquery cache added.
sql/sql_select.h:
  Subquery cache added.
sql/sql_union.cc:
  A new parameter to the function added.
sql/sql_update.cc:
  A new parameter to the function added.
sql/table.cc:
  Procedures to manage temporarty tables index added.
sql/table.h:
  Procedures to manage temporarty tables index added.
storage/maria/ha_maria.cc:
  Fix of handler to allow destoy a table in case of error during the table creation.
storage/maria/ha_maria.h:
  .h file guard added.
storage/myisam/ha_myisam.cc:
  Fix of handler to allow destoy a table in case of error during the table creation.
This commit is contained in:
unknown
2010-07-10 13:37:30 +03:00
parent e5f238a051
commit ceb5468fd8
43 changed files with 4255 additions and 164 deletions

View File

@ -152,7 +152,6 @@ static int join_read_const_table(JOIN_TAB *tab, POSITION *pos);
static int join_read_system(JOIN_TAB *tab);
static int join_read_const(JOIN_TAB *tab);
static int join_read_key(JOIN_TAB *tab);
static int join_read_key2(JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref);
static void join_read_key_unlock_row(st_join_table *tab);
static int join_read_always_key(JOIN_TAB *tab);
static int join_read_last_key(JOIN_TAB *tab);
@ -1293,6 +1292,9 @@ JOIN::optimize()
int res;
if ((res= rewrite_to_index_subquery_engine(this)) != -1)
DBUG_RETURN(res);
if (setup_subquery_caches())
DBUG_RETURN(-1);
/*
Need to tell handlers that to play it safe, it should fetch all
columns of the primary key of the tables: this is because MySQL may
@ -1514,6 +1516,152 @@ setup_subq_exit:
}
/**
Transform given item with changing it in all_fields if it is needed.
@param expr Reference on expression to transform
@param transformer Transformer to apply to the expression
@details
The function transforms the expression expr and, if the top item of the
expression has changed, the function looks for the item in
JOIN::all_fields and replaces it with the result of transformation.
*/
void JOIN::transform_and_change_in_all_fields(Item** expr,
Item_transformer transformer)
{
DBUG_ENTER("JOIN::transform_and_change_in_all_fields");
Item *new_item= (*expr)->transform(transformer,
(uchar*) thd);
if (new_item != (*expr))
{
List_iterator<Item> li(all_fields);
Item *it;
/*
Check if this item already has expression cache and if it has then use
that cache instead of the cache we have just created
*/
while ((it= li++))
{
if (((*expr) == it->get_cached_item()))
{
/*
We have to forget about the created cache, but this situation is
really rare.
*/
new_item->cleanup();
new_item= it;
DBUG_PRINT("info", ("Other cache found"));
break;
}
}
li.rewind();
while ((it= li++))
{
if (it == (*expr))
{
li.replace(new_item);
DBUG_PRINT("info", ("Cache Added"));
}
}
*expr= new_item;
}
DBUG_VOID_RETURN;
}
/**
Setup expression caches for subqueries that need them
@details
The function wraps correlated subquery expressions that return one value
into objects of the class Item_cache_wrapper setting up an expression
cache for each of them. The result values of the subqueries are to be
cached together with the corresponding sets of the parameters - outer
references of the subqueries.
@retval FALSE OK
@retval TRUE Error
*/
bool JOIN::setup_subquery_caches()
{
DBUG_ENTER("JOIN::setup_subquery_caches");
/*
We have to check all this condition together because items created in
one of this clauses can be moved to another one by optimizer
*/
if (select_lex->expr_cache_may_be_used[IN_WHERE] ||
select_lex->expr_cache_may_be_used[IN_HAVING] ||
select_lex->expr_cache_may_be_used[IN_ON] ||
select_lex->expr_cache_may_be_used[NO_MATTER])
{
if (conds)
conds= conds->transform(&Item::expr_cache_insert_transformer,
(uchar*) thd);
for (JOIN_TAB *tab= join_tab + const_tables;
tab < join_tab + tables ;
tab++)
{
if (tab->select_cond)
tab->select_cond=
tab->select_cond->transform(&Item::expr_cache_insert_transformer,
(uchar*) thd);
if (tab->cache_select && tab->cache_select->cond)
tab->cache_select->cond=
tab->cache_select->
cond->transform(&Item::expr_cache_insert_transformer,
(uchar*) thd);
}
if (having)
having= having->transform(&Item::expr_cache_insert_transformer,
(uchar*) thd);
if (tmp_having)
{
DBUG_ASSERT(having == NULL);
tmp_having= tmp_having->transform(&Item::expr_cache_insert_transformer,
(uchar*) thd);
}
}
if (select_lex->expr_cache_may_be_used[SELECT_LIST] ||
select_lex->expr_cache_may_be_used[IN_GROUP_BY] ||
select_lex->expr_cache_may_be_used[NO_MATTER])
{
List_iterator<Item> li(fields_list);
Item *item;
while ((item= li++))
{
Item *new_item=
item->transform(&Item::expr_cache_insert_transformer, (uchar*) thd);
if (new_item != item)
{
thd->change_item_tree(li.ref(), new_item);
}
}
for (ORDER *group= group_list; group ; group= group->next)
{
transform_and_change_in_all_fields(group->item,
&Item::expr_cache_insert_transformer);
}
}
if (select_lex->expr_cache_may_be_used[NO_MATTER])
{
for (ORDER *ord= order; ord; ord= ord->next)
{
transform_and_change_in_all_fields(ord->item,
&Item::expr_cache_insert_transformer);
}
}
DBUG_RETURN(FALSE);
}
/**
Restore values in temporary join.
*/
@ -5244,7 +5392,7 @@ greedy_search(JOIN *join,
'join->best_positions' contains a complete optimal extension of the
current partial QEP.
*/
DBUG_EXECUTE("opt", print_plan(join, join->tables,
DBUG_EXECUTE("opt", print_plan(join, n_tables,
record_count, read_time, read_time,
"optimal"););
DBUG_RETURN(FALSE);
@ -6035,7 +6183,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
store_key_item tmp(thd, keyinfo->key_part[i].field,
key_buff + maybe_null,
maybe_null ? key_buff : 0,
keyinfo->key_part[i].length, keyuse->val);
keyinfo->key_part[i].length, keyuse->val,
FALSE);
if (thd->is_fatal_error)
DBUG_RETURN(TRUE);
tmp.copy();
@ -6117,7 +6266,7 @@ get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables,
key_buff + maybe_null,
maybe_null ? key_buff : 0,
key_part->length,
keyuse->val);
keyuse->val, FALSE);
}
/**
@ -7156,7 +7305,7 @@ end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
if (item->is_null())
DBUG_RETURN(NESTED_LOOP_OK);
}
fill_record(thd, table->field, sjm->sjm_table_cols, 1);
fill_record(thd, table->field, sjm->sjm_table_cols, TRUE, FALSE);
if (thd->is_error())
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
if ((error= table->file->ha_write_row(table->record[0])))
@ -7721,6 +7870,72 @@ void JOIN_TAB::cleanup()
}
/**
Build a TABLE_REF structure for index lookup in the temporary table
@param thd Thread handle
@param tmp_key The temporary table key
@param it The iterator of items for lookup in the key
@details
Build TABLE_REF object for lookup in the key 'tmp_key' using items
accessible via item iterator 'it'.
@retval TRUE Error
@retval FALSE OK
*/
bool TABLE_REF::tmp_table_index_lookup_init(THD *thd,
KEY *tmp_key,
Item_iterator &it,
bool value)
{
uint tmp_key_parts= tmp_key->key_parts;
DBUG_ENTER("TABLE_REF::tmp_table_index_lookup_init");
key= 0; /* The only temp table index. */
key_length= tmp_key->key_length;
if (!(key_buff=
(uchar*) thd->calloc(ALIGN_SIZE(tmp_key->key_length) * 2)) ||
!(key_copy=
(store_key**) thd->alloc((sizeof(store_key*) *
(tmp_key_parts + 1)))) ||
!(items=
(Item**) thd->alloc(sizeof(Item*) * tmp_key_parts)))
DBUG_RETURN(TRUE);
key_buff2= key_buff + ALIGN_SIZE(tmp_key->key_length);
KEY_PART_INFO *cur_key_part= tmp_key->key_part;
store_key **ref_key= key_copy;
uchar *cur_ref_buff= key_buff;
it.open();
for (uint i= 0; i < tmp_key_parts; i++, cur_key_part++, ref_key++)
{
Item *item= it.next();
DBUG_ASSERT(item);
items[i]= item;
int null_count= test(cur_key_part->field->real_maybe_null());
*ref_key= new store_key_item(thd, cur_key_part->field,
/* TIMOUR:
the NULL byte is taken into account in
cur_key_part->store_length, so instead of
cur_ref_buff + test(maybe_null), we could
use that information instead.
*/
cur_ref_buff + null_count,
null_count ? key_buff : 0,
cur_key_part->length, items[i], value);
cur_ref_buff+= cur_key_part->store_length;
}
*ref_key= NULL; /* End marker. */
key_err= 1;
key_parts= tmp_key_parts;
DBUG_RETURN(FALSE);
}
/**
Partially cleanup JOIN after it has executed: close index or rnd read
(table cursors), free quick selects.
@ -10829,6 +11044,8 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
case Item::REF_ITEM:
case Item::NULL_ITEM:
case Item::VARBIN_ITEM:
case Item::CACHE_ITEM:
case Item::EXPR_CACHE_ITEM:
if (make_copy_field)
{
DBUG_ASSERT(((Item_result_field*)item)->result_field);
@ -11089,6 +11306,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
if (item->used_tables() & OUTER_REF_TABLE_BIT)
item->update_used_tables();
if (type == Item::SUBSELECT_ITEM ||
(item->get_cached_item() &&
item->get_cached_item()->type() == Item::SUBSELECT_ITEM ) ||
(item->used_tables() & ~OUTER_REF_TABLE_BIT))
{
/*
@ -11616,7 +11835,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
&param->recinfo, select_options))
goto err;
}
if (open_tmp_table(table))
DBUG_PRINT("info", ("skip_create_table: %d", (int)param->skip_create_table));
if (!param->skip_create_table && open_tmp_table(table))
goto err;
thd->mem_root= mem_root_save;
@ -12609,7 +12829,8 @@ sub_select_sjm(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
else
{
/* Do index lookup in the materialized table */
if ((res= join_read_key2(join_tab, sjm->table, sjm->tab_ref)) == 1)
if ((res= join_read_key2(join_tab->join->thd, join_tab,
sjm->table, sjm->tab_ref)) == 1)
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
if (res || !sjm->in_equality->val_int())
DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
@ -13409,25 +13630,24 @@ join_read_const(JOIN_TAB *tab)
static int
join_read_key(JOIN_TAB *tab)
{
return join_read_key2(tab, tab->table, &tab->ref);
return join_read_key2(tab->join->thd, tab, tab->table, &tab->ref);
}
/*
/*
eq_ref access handler but generalized a bit to support TABLE and TABLE_REF
not from the join_tab. See join_read_key for detailed synopsis.
*/
static int
join_read_key2(JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref)
int join_read_key2(THD *thd, JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref)
{
int error;
if (!table->file->inited)
{
table->file->ha_index_init(table_ref->key, tab->sorted);
table->file->ha_index_init(table_ref->key, (tab ? tab->sorted : TRUE));
}
/* TODO: Why don't we do "Late NULLs Filtering" here? */
if (cmp_buffer_with_ref(tab->join->thd, table, table_ref) ||
if (cmp_buffer_with_ref(thd, table, table_ref) ||
(table->status & (STATUS_GARBAGE | STATUS_NO_PARENT | STATUS_NULL_ROW)))
{
if (table_ref->key_err)
@ -13439,10 +13659,10 @@ join_read_key2(JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref)
Moving away from the current record. Unlock the row
in the handler if it did not match the partial WHERE.
*/
if (tab->ref.has_record && tab->ref.use_count == 0)
if (tab && tab->ref.has_record && tab->ref.use_count == 0)
{
tab->read_record.file->unlock_row();
tab->ref.has_record= FALSE;
table_ref->has_record= FALSE;
}
error=table->file->ha_index_read_map(table->record[0],
table_ref->key_buff,
@ -13453,14 +13673,14 @@ join_read_key2(JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref)
if (! error)
{
tab->ref.has_record= TRUE;
tab->ref.use_count= 1;
table_ref->has_record= TRUE;
table_ref->use_count= 1;
}
}
else if (table->status == 0)
{
DBUG_ASSERT(tab->ref.has_record);
tab->ref.use_count++;
DBUG_ASSERT(table_ref->has_record);
table_ref->use_count++;
}
table->null_row=0;
return table->status ? -1 : 0;
@ -16960,6 +17180,8 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
}
else if ((real_pos->type() == Item::FUNC_ITEM ||
real_pos->type() == Item::SUBSELECT_ITEM ||
(real_pos->get_cached_item() &&
real_pos->get_cached_item()->type() == Item::SUBSELECT_ITEM) ||
real_pos->type() == Item::CACHE_ITEM ||
real_pos->type() == Item::COND_ITEM) &&
!real_pos->with_sum_func)