mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
Merge 5.5->mwl248
This commit is contained in:
@ -1233,11 +1233,9 @@ JOIN::optimize()
|
||||
DBUG_RETURN(1); // error == -1
|
||||
}
|
||||
if (const_table_map != found_const_table_map &&
|
||||
!(select_options & SELECT_DESCRIBE) &&
|
||||
(!conds ||
|
||||
!(conds->used_tables() & RAND_TABLE_BIT) ||
|
||||
select_lex->master_unit() == &thd->lex->unit)) // upper level SELECT
|
||||
!(select_options & SELECT_DESCRIBE))
|
||||
{
|
||||
// There is at least one empty const table
|
||||
zero_result_cause= "no matching row in const table";
|
||||
DBUG_PRINT("error",("Error: %s", zero_result_cause));
|
||||
error= 0;
|
||||
@ -5380,7 +5378,8 @@ best_access_path(JOIN *join,
|
||||
!ref_or_null_part)
|
||||
{ /* use eq key */
|
||||
max_key_part= (uint) ~0;
|
||||
if ((key_flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)
|
||||
if ((key_flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME ||
|
||||
test(key_flags & HA_EXT_NOSAME))
|
||||
{
|
||||
tmp = prev_record_reads(join->positions, idx, found_ref);
|
||||
records=1.0;
|
||||
@ -7969,18 +7968,23 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
|
||||
*ref_key=0; // end_marker
|
||||
if (j->type == JT_FT)
|
||||
DBUG_RETURN(0);
|
||||
ulong key_flags= j->table->actual_key_flags(keyinfo);
|
||||
if (j->type == JT_CONST)
|
||||
j->table->const_table= 1;
|
||||
else if (((j->table->actual_key_flags(keyinfo) &
|
||||
(HA_NOSAME | HA_NULL_PART_KEY))
|
||||
!= HA_NOSAME) ||
|
||||
else if (((key_flags & (HA_NOSAME | HA_NULL_PART_KEY))!= HA_NOSAME) ||
|
||||
keyparts != j->table->actual_n_key_parts(keyinfo) ||
|
||||
null_ref_key)
|
||||
{
|
||||
/* Must read with repeat */
|
||||
j->type= null_ref_key ? JT_REF_OR_NULL : JT_REF;
|
||||
j->ref.null_ref_key= null_ref_key;
|
||||
j->ref.null_ref_part= null_ref_part;
|
||||
if (test(key_flags & HA_EXT_NOSAME) && keyparts == keyinfo->ext_key_parts &&
|
||||
!null_ref_key)
|
||||
j->type= JT_EQ_REF;
|
||||
else
|
||||
{
|
||||
/* Must read with repeat */
|
||||
j->type= null_ref_key ? JT_REF_OR_NULL : JT_REF;
|
||||
j->ref.null_ref_key= null_ref_key;
|
||||
j->ref.null_ref_part= null_ref_part;
|
||||
}
|
||||
}
|
||||
else if (keyuse_uses_no_tables)
|
||||
{
|
||||
@ -9140,7 +9144,7 @@ void JOIN::drop_unused_derived_keys()
|
||||
JOIN_TAB *tab;
|
||||
for (tab= first_linear_tab(this, WITHOUT_CONST_TABLES);
|
||||
tab;
|
||||
tab= next_linear_tab(this, tab, WITHOUT_BUSH_ROOTS))
|
||||
tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
|
||||
{
|
||||
|
||||
TABLE *table=tab->table;
|
||||
@ -10606,9 +10610,22 @@ void JOIN::cleanup(bool full)
|
||||
|
||||
if (full)
|
||||
{
|
||||
JOIN_TAB *sort_tab= first_linear_tab(this, WITHOUT_CONST_TABLES);
|
||||
if (pre_sort_join_tab)
|
||||
{
|
||||
if (sort_tab && sort_tab->select == pre_sort_join_tab->select)
|
||||
{
|
||||
pre_sort_join_tab->select= NULL;
|
||||
}
|
||||
else
|
||||
clean_pre_sort_join_tab();
|
||||
}
|
||||
|
||||
for (tab= first_linear_tab(this, WITH_CONST_TABLES); tab;
|
||||
tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
|
||||
{
|
||||
tab->cleanup();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -16488,6 +16505,17 @@ int safe_index_read(JOIN_TAB *tab)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Reads content of constant table
|
||||
|
||||
@param tab table
|
||||
@param pos position of table in query plan
|
||||
|
||||
@retval 0 ok, one row was found or one NULL-complemented row was created
|
||||
@retval -1 ok, no row was found and no NULL-complemented row was created
|
||||
@retval 1 error
|
||||
*/
|
||||
|
||||
static int
|
||||
join_read_const_table(JOIN_TAB *tab, POSITION *pos)
|
||||
{
|
||||
@ -16606,6 +16634,16 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Read a constant table when there is at most one matching row, using a table
|
||||
scan.
|
||||
|
||||
@param tab Table to read
|
||||
|
||||
@retval 0 Row was found
|
||||
@retval -1 Row was not found
|
||||
@retval 1 Got an error (other than row not found) during read
|
||||
*/
|
||||
static int
|
||||
join_read_system(JOIN_TAB *tab)
|
||||
{
|
||||
@ -16638,12 +16676,9 @@ join_read_system(JOIN_TAB *tab)
|
||||
|
||||
@param tab Table to read
|
||||
|
||||
@retval
|
||||
0 Row was found
|
||||
@retval
|
||||
-1 Row was not found
|
||||
@retval
|
||||
1 Got an error (other than row not found) during read
|
||||
@retval 0 Row was found
|
||||
@retval -1 Row was not found
|
||||
@retval 1 Got an error (other than row not found) during read
|
||||
*/
|
||||
|
||||
static int
|
||||
@ -18478,15 +18513,18 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
|
||||
*/
|
||||
|
||||
if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT)
|
||||
goto use_filesort;
|
||||
ref_key= select->quick->index;
|
||||
ref_key_parts= select->quick->used_key_parts;
|
||||
ref_key= MAX_KEY;
|
||||
else
|
||||
{
|
||||
ref_key= select->quick->index;
|
||||
ref_key_parts= select->quick->used_key_parts;
|
||||
}
|
||||
}
|
||||
|
||||
if (ref_key >= 0)
|
||||
if (ref_key >= 0 && ref_key != MAX_KEY)
|
||||
{
|
||||
/*
|
||||
We come here when there is a REF key.
|
||||
@ -18858,6 +18896,8 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
|
||||
TABLE *table;
|
||||
SQL_SELECT *select;
|
||||
JOIN_TAB *tab;
|
||||
int err= 0;
|
||||
bool quick_created= FALSE;
|
||||
DBUG_ENTER("create_sort_index");
|
||||
|
||||
if (join->table_count == join->const_tables)
|
||||
@ -18865,18 +18905,61 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
|
||||
tab= join->join_tab + join->const_tables;
|
||||
table= tab->table;
|
||||
select= tab->select;
|
||||
|
||||
JOIN_TAB *save_pre_sort_join_tab= NULL;
|
||||
if (join->pre_sort_join_tab)
|
||||
{
|
||||
/*
|
||||
we've already been in this function, and stashed away the original access
|
||||
method in join->pre_sort_join_tab, restore it now.
|
||||
*/
|
||||
|
||||
/* First, restore state of the handler */
|
||||
if (join->pre_sort_index != MAX_KEY)
|
||||
{
|
||||
if (table->file->ha_index_or_rnd_end())
|
||||
goto err;
|
||||
if (join->pre_sort_idx_pushed_cond)
|
||||
{
|
||||
table->file->idx_cond_push(join->pre_sort_index,
|
||||
join->pre_sort_idx_pushed_cond);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (table->file->ha_index_or_rnd_end() ||
|
||||
table->file->ha_rnd_init(TRUE))
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Second, restore access method parameters */
|
||||
tab->records= join->pre_sort_join_tab->records;
|
||||
tab->select= join->pre_sort_join_tab->select;
|
||||
tab->select_cond= join->pre_sort_join_tab->select_cond;
|
||||
tab->type= join->pre_sort_join_tab->type;
|
||||
tab->read_first_record= join->pre_sort_join_tab->read_first_record;
|
||||
|
||||
save_pre_sort_join_tab= join->pre_sort_join_tab;
|
||||
join->pre_sort_join_tab= NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Save index #, save index condition. Do it right now, because MRR may
|
||||
*/
|
||||
if (table->file->inited == handler::INDEX)
|
||||
{
|
||||
join->pre_sort_index= table->file->active_index;
|
||||
join->pre_sort_idx_pushed_cond= table->file->pushed_idx_cond;
|
||||
// no need to save key_read
|
||||
}
|
||||
else
|
||||
join->pre_sort_index= MAX_KEY;
|
||||
}
|
||||
|
||||
/* Currently ORDER BY ... LIMIT is not supported in subqueries. */
|
||||
DBUG_ASSERT(join->group_list || !join->is_in_subquery());
|
||||
|
||||
/*
|
||||
If we have a select->quick object that is created outside of
|
||||
create_sort_index() and this is part of a subquery that
|
||||
potentially can be executed multiple times then we should not
|
||||
delete the quick object on exit from this function.
|
||||
*/
|
||||
bool keep_quick= select && select->quick && join->join_tab_save;
|
||||
|
||||
/*
|
||||
When there is SQL_BIG_RESULT do not sort using index for GROUP BY,
|
||||
and thus force sorting on disk unless a group min-max optimization
|
||||
@ -18928,7 +19011,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
|
||||
get_quick_select_for_ref(thd, table, &tab->ref,
|
||||
tab->found_records))))
|
||||
goto err;
|
||||
DBUG_ASSERT(!keep_quick);
|
||||
quick_created= TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -18944,7 +19027,27 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
|
||||
table->sort.found_records=filesort(thd, table,join->sortorder, length,
|
||||
select, filesort_limit, 0,
|
||||
&examined_rows);
|
||||
|
||||
if (quick_created)
|
||||
{
|
||||
/* This will delete the quick select. */
|
||||
select->cleanup();
|
||||
}
|
||||
|
||||
if (!join->pre_sort_join_tab)
|
||||
{
|
||||
if (save_pre_sort_join_tab)
|
||||
join->pre_sort_join_tab= save_pre_sort_join_tab;
|
||||
else if (!(join->pre_sort_join_tab= (JOIN_TAB*)thd->alloc(sizeof(JOIN_TAB))))
|
||||
goto err;
|
||||
}
|
||||
|
||||
*(join->pre_sort_join_tab)= *tab;
|
||||
|
||||
/*TODO: here, close the index scan, cancel index-only read. */
|
||||
tab->records= table->sort.found_records; // For SQL_CALC_ROWS
|
||||
#if 0
|
||||
/* MariaDB doesn't need the following: */
|
||||
if (select)
|
||||
{
|
||||
/*
|
||||
@ -18961,6 +19064,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
|
||||
tablesort_result_cache= table->sort.io_cache;
|
||||
table->sort.io_cache= NULL;
|
||||
|
||||
// select->cleanup(); // filesort did select
|
||||
/*
|
||||
If a quick object was created outside of create_sort_index()
|
||||
that might be reused, then do not call select->cleanup() since
|
||||
@ -18983,18 +19087,61 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
|
||||
// Restore the output resultset
|
||||
table->sort.io_cache= tablesort_result_cache;
|
||||
}
|
||||
#endif
|
||||
tab->select=NULL;
|
||||
tab->set_select_cond(NULL, __LINE__);
|
||||
tab->last_inner= 0;
|
||||
tab->first_unmatched= 0;
|
||||
tab->type=JT_ALL; // Read with normal read_record
|
||||
tab->read_first_record= join_init_read_record;
|
||||
tab->table->file->ha_index_or_rnd_end();
|
||||
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
tab->join->examined_rows+=examined_rows;
|
||||
table->disable_keyread(); // Restore if we used indexes
|
||||
DBUG_RETURN(table->sort.found_records == HA_POS_ERROR);
|
||||
err:
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
|
||||
|
||||
void JOIN::clean_pre_sort_join_tab()
|
||||
{
|
||||
//TABLE *table= pre_sort_join_tab->table;
|
||||
/*
|
||||
Note: we can come here for fake_select_lex object. That object will have
|
||||
the table already deleted by st_select_lex_unit::cleanup().
|
||||
We rely on that fake_select_lex didn't have quick select.
|
||||
*/
|
||||
#if 0
|
||||
if (pre_sort_join_tab->select && pre_sort_join_tab->select->quick)
|
||||
{
|
||||
/*
|
||||
We need to preserve tablesort's output resultset here, because
|
||||
QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT (called by
|
||||
SQL_SELECT::cleanup()) may free it assuming it's the result of the quick
|
||||
select operation that we no longer need. Note that all the other parts of
|
||||
this data structure are cleaned up when
|
||||
QUICK_INDEX_MERGE_SELECT::get_next encounters end of data, so the next
|
||||
SQL_SELECT::cleanup() call changes sort.io_cache alone.
|
||||
*/
|
||||
IO_CACHE *tablesort_result_cache;
|
||||
|
||||
tablesort_result_cache= table->sort.io_cache;
|
||||
table->sort.io_cache= NULL;
|
||||
pre_sort_join_tab->select->cleanup();
|
||||
table->quick_keys.clear_all(); // as far as we cleanup select->quick
|
||||
table->intersect_keys.clear_all();
|
||||
table->sort.io_cache= tablesort_result_cache;
|
||||
}
|
||||
#endif
|
||||
//table->disable_keyread(); // Restore if we used indexes
|
||||
if (pre_sort_join_tab->select && pre_sort_join_tab->select->quick)
|
||||
{
|
||||
pre_sort_join_tab->select->cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
Remove duplicates from tmp table
|
||||
This should be recoded to add a unique index to the table and remove
|
||||
@ -20474,40 +20621,66 @@ change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array,
|
||||
res_selected_fields.empty();
|
||||
res_all_fields.empty();
|
||||
|
||||
uint i, border= all_fields.elements - elements;
|
||||
for (i= 0; (item= it++); i++)
|
||||
uint border= all_fields.elements - elements;
|
||||
for (uint i= 0; (item= it++); i++)
|
||||
{
|
||||
Field *field;
|
||||
|
||||
if ((item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) ||
|
||||
(item->type() == Item::FUNC_ITEM &&
|
||||
((Item_func*)item)->functype() == Item_func::SUSERVAR_FUNC))
|
||||
if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM)
|
||||
item_field= item;
|
||||
else
|
||||
else if (item->type() == Item::FIELD_ITEM)
|
||||
item_field= item->get_tmp_table_item(thd);
|
||||
else if (item->type() == Item::FUNC_ITEM &&
|
||||
((Item_func*)item)->functype() == Item_func::SUSERVAR_FUNC)
|
||||
{
|
||||
if (item->type() == Item::FIELD_ITEM)
|
||||
field= item->get_tmp_table_field();
|
||||
if (field != NULL)
|
||||
{
|
||||
item_field= item->get_tmp_table_item(thd);
|
||||
/*
|
||||
Replace "@:=<expression>" with "@:=<tmp table column>". Otherwise,
|
||||
we would re-evaluate <expression>, and if expression were
|
||||
a subquery, this would access already-unlocked tables.
|
||||
*/
|
||||
Item_func_set_user_var* suv=
|
||||
new Item_func_set_user_var((Item_func_set_user_var*) item);
|
||||
Item_field *new_field= new Item_field(field);
|
||||
if (!suv || !new_field || suv->fix_fields(thd, (Item**)&suv))
|
||||
DBUG_RETURN(true); // Fatal error
|
||||
((Item *)suv)->name= item->name;
|
||||
/*
|
||||
We are replacing the argument of Item_func_set_user_var after its
|
||||
value has been read. The argument's null_value should be set by
|
||||
now, so we must set it explicitly for the replacement argument
|
||||
since the null_value may be read without any preceeding call to
|
||||
val_*().
|
||||
*/
|
||||
new_field->update_null_value();
|
||||
List<Item> list;
|
||||
list.push_back(new_field);
|
||||
suv->set_arguments(list);
|
||||
item_field= suv;
|
||||
}
|
||||
else if ((field= item->get_tmp_table_field()))
|
||||
{
|
||||
if (item->type() == Item::SUM_FUNC_ITEM && field->table->group)
|
||||
item_field= ((Item_sum*) item)->result_item(field);
|
||||
else
|
||||
item_field= (Item*) new Item_field(field);
|
||||
if (!item_field)
|
||||
DBUG_RETURN(TRUE); // Fatal error
|
||||
else
|
||||
item_field= item;
|
||||
}
|
||||
else if ((field= item->get_tmp_table_field()))
|
||||
{
|
||||
if (item->type() == Item::SUM_FUNC_ITEM && field->table->group)
|
||||
item_field= ((Item_sum*) item)->result_item(field);
|
||||
else
|
||||
item_field= (Item*) new Item_field(field);
|
||||
if (!item_field)
|
||||
DBUG_RETURN(true); // Fatal error
|
||||
|
||||
if (item->real_item()->type() != Item::FIELD_ITEM)
|
||||
field->orig_table= 0;
|
||||
item_field->name= item->name;
|
||||
if (item->type() == Item::REF_ITEM)
|
||||
{
|
||||
Item_field *ifield= (Item_field *) item_field;
|
||||
Item_ref *iref= (Item_ref *) item;
|
||||
ifield->table_name= iref->table_name;
|
||||
ifield->db_name= iref->db_name;
|
||||
}
|
||||
if (item->real_item()->type() != Item::FIELD_ITEM)
|
||||
field->orig_table= 0;
|
||||
item_field->name= item->name;
|
||||
if (item->type() == Item::REF_ITEM)
|
||||
{
|
||||
Item_field *ifield= (Item_field *) item_field;
|
||||
Item_ref *iref= (Item_ref *) item;
|
||||
ifield->table_name= iref->table_name;
|
||||
ifield->db_name= iref->db_name;
|
||||
}
|
||||
#ifndef DBUG_OFF
|
||||
if (!item_field->name)
|
||||
{
|
||||
@ -20519,20 +20692,20 @@ change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array,
|
||||
item_field->name= sql_strmake(str.ptr(),str.length());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
item_field= item;
|
||||
}
|
||||
else
|
||||
item_field= item;
|
||||
|
||||
res_all_fields.push_back(item_field);
|
||||
ref_pointer_array[((i < border)? all_fields.elements-i-1 : i-border)]=
|
||||
item_field;
|
||||
}
|
||||
|
||||
List_iterator_fast<Item> itr(res_all_fields);
|
||||
for (i= 0; i < border; i++)
|
||||
for (uint i= 0; i < border; i++)
|
||||
itr++;
|
||||
itr.sublist(res_selected_fields, elements);
|
||||
DBUG_RETURN(FALSE);
|
||||
DBUG_RETURN(false);
|
||||
}
|
||||
|
||||
|
||||
@ -22712,6 +22885,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
|
||||
ha_rows table_records= table->stat_records();
|
||||
bool group= join && join->group && order == join->group_list;
|
||||
ha_rows ref_key_quick_rows= HA_POS_ERROR;
|
||||
const bool has_limit= (select_limit_arg != HA_POS_ERROR);
|
||||
|
||||
/*
|
||||
If not used with LIMIT, only use keys if the whole query can be
|
||||
@ -22736,7 +22910,8 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
|
||||
else
|
||||
keys= usable_keys;
|
||||
|
||||
if (ref_key >= 0 && table->covering_keys.is_set(ref_key))
|
||||
if (ref_key >= 0 && ref_key != MAX_KEY &&
|
||||
table->covering_keys.is_set(ref_key))
|
||||
ref_key_quick_rows= table->quick_rows[ref_key];
|
||||
|
||||
if (join)
|
||||
@ -22843,7 +23018,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
|
||||
be included into the result set.
|
||||
*/
|
||||
if (select_limit > table_records/rec_per_key)
|
||||
select_limit= table_records;
|
||||
select_limit= table_records;
|
||||
else
|
||||
select_limit= (ha_rows) (select_limit*rec_per_key);
|
||||
} /* group */
|
||||
@ -22925,7 +23100,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
|
||||
|
||||
*new_key= best_key;
|
||||
*new_key_direction= best_key_direction;
|
||||
*new_select_limit= best_select_limit;
|
||||
*new_select_limit= has_limit ? best_select_limit : table_records;
|
||||
if (new_used_key_parts != NULL)
|
||||
*new_used_key_parts= best_key_parts;
|
||||
|
||||
|
Reference in New Issue
Block a user