1
0
mirror of https://github.com/MariaDB/server.git synced 2026-01-06 05:22:24 +03:00

Merge of mysql-trunk-bugfixing into mysql-trunk-merge.

This commit is contained in:
Davi Arnaut
2010-07-15 10:47:50 -03:00
488 changed files with 12061 additions and 12284 deletions

View File

@@ -137,7 +137,6 @@ static uint build_bitmap_for_nested_joins(List<TABLE_LIST> *join_list,
static COND *optimize_cond(JOIN *join, COND *conds,
List<TABLE_LIST> *join_list,
Item::cond_result *cond_value);
static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item);
static bool open_tmp_table(TABLE *table);
static bool create_myisam_tmp_table(TABLE *,TMP_TABLE_PARAM *, ulonglong, my_bool);
static int do_select(JOIN *join,List<Item> *fields,TABLE *tmp_table,
@@ -190,6 +189,14 @@ static COND *make_cond_for_table(COND *cond,table_map table,
table_map used_table);
static Item* part_of_refkey(TABLE *form,Field *field);
uint find_shortest_key(TABLE *table, const key_map *usable_keys);
static bool test_if_cheaper_ordering(const JOIN_TAB *tab,
ORDER *order, TABLE *table,
key_map usable_keys, int key,
ha_rows select_limit,
int *new_key, int *new_key_direction,
ha_rows *new_select_limit,
uint *new_used_key_parts= NULL,
uint *saved_best_key_parts= NULL);
static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,
ha_rows select_limit, bool no_changes,
key_map *map);
@@ -6959,7 +6966,7 @@ void JOIN_TAB::cleanup()
select= 0;
delete quick;
quick= 0;
x_free(cache.buff);
my_free(cache.buff);
cache.buff= 0;
limit= 0;
if (table)
@@ -7370,8 +7377,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
*simple_order=0;
else
{
Item *comp_item=0;
if (cond && const_expression_in_where(cond,order->item[0], &comp_item))
if (cond && const_expression_in_where(cond,order->item[0]))
{
DBUG_PRINT("info",("removing: %s", order->item[0]->full_name()));
continue;
@@ -7401,6 +7407,46 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond,
}
/**
Filter out ORDER items those are equal to constants in WHERE
This function is a limited version of remove_const() for use
with non-JOIN statements (i.e. single-table UPDATE and DELETE).
@param order Linked list of ORDER BY arguments
@param cond WHERE expression
@return pointer to new filtered ORDER list or NULL if whole list eliminated
@note
This function overwrites input order list.
*/
ORDER *simple_remove_const(ORDER *order, COND *where)
{
if (!order || !where)
return order;
ORDER *first= NULL, *prev= NULL;
for (; order; order= order->next)
{
DBUG_ASSERT(!order->item[0]->with_sum_func); // should never happen
if (!const_expression_in_where(where, order->item[0]))
{
if (!first)
first= order;
if (prev)
prev->next= order;
prev= order;
}
}
if (prev)
prev->next= NULL;
return first;
}
static int
return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables,
List<Item> &fields, bool send_row, ulonglong select_options,
@@ -8593,10 +8639,9 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
left_item->collation.collation == value->collation.collation))
{
Item *tmp=value->clone_item();
tmp->collation.set(right_item->collation);
if (tmp)
{
tmp->collation.set(right_item->collation);
thd->change_item_tree(args + 1, tmp);
func->update_used_tables();
if ((functype == Item_func::EQ_FUNC || functype == Item_func::EQUAL_FUNC)
@@ -8617,10 +8662,9 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
right_item->collation.collation == value->collation.collation))
{
Item *tmp= value->clone_item();
tmp->collation.set(left_item->collation);
if (tmp)
{
tmp->collation.set(left_item->collation);
thd->change_item_tree(args, tmp);
value= tmp;
func->update_used_tables();
@@ -9602,13 +9646,50 @@ test_if_equality_guarantees_uniqueness(Item *l, Item *r)
l->collation.collation == r->collation.collation)));
}
/**
Return TRUE if the item is a const value in all the WHERE clause.
/*
Return TRUE if i1 and i2 (if any) are equal items,
or if i1 is a wrapper item around the f2 field.
*/
static bool
const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
static bool equal(Item *i1, Item *i2, Field *f2)
{
DBUG_ASSERT((i2 == NULL) ^ (f2 == NULL));
if (i2 != NULL)
return i1->eq(i2, 1);
else if (i1->type() == Item::FIELD_ITEM)
return f2->eq(((Item_field *) i1)->field);
else
return FALSE;
}
/**
Test if a field or an item is equal to a constant value in WHERE
@param cond WHERE clause expression
@param comp_item Item to find in WHERE expression
(if comp_field != NULL)
@param comp_field Field to find in WHERE expression
(if comp_item != NULL)
@param[out] const_item intermediate arg, set to Item pointer to NULL
@return TRUE if the field is a constant value in WHERE
@note
comp_item and comp_field parameters are mutually exclusive.
*/
bool
const_expression_in_where(COND *cond, Item *comp_item, Field *comp_field,
Item **const_item)
{
DBUG_ASSERT((comp_item == NULL) ^ (comp_field == NULL));
Item *intermediate= NULL;
if (const_item == NULL)
const_item= &intermediate;
if (cond->type() == Item::COND_ITEM)
{
bool and_level= (((Item_cond*) cond)->functype()
@@ -9617,7 +9698,8 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
Item *item;
while ((item=li++))
{
bool res=const_expression_in_where(item, comp_item, const_item);
bool res=const_expression_in_where(item, comp_item, comp_field,
const_item);
if (res) // Is a const value
{
if (and_level)
@@ -9629,14 +9711,14 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
return and_level ? 0 : 1;
}
else if (cond->eq_cmp_result() != Item::COND_OK)
{ // boolan compare function
{ // boolean compare function
Item_func* func= (Item_func*) cond;
if (func->functype() != Item_func::EQUAL_FUNC &&
func->functype() != Item_func::EQ_FUNC)
return 0;
Item *left_item= ((Item_func*) cond)->arguments()[0];
Item *right_item= ((Item_func*) cond)->arguments()[1];
if (left_item->eq(comp_item,1))
if (equal(left_item, comp_item, comp_field))
{
if (test_if_equality_guarantees_uniqueness (left_item, right_item))
{
@@ -9646,7 +9728,7 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
return 1;
}
}
else if (right_item->eq(comp_item,1))
else if (equal(right_item, comp_item, comp_field))
{
if (test_if_equality_guarantees_uniqueness (right_item, left_item))
{
@@ -9660,6 +9742,7 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
return 0;
}
/****************************************************************************
Create internal temporary table
****************************************************************************/
@@ -13113,7 +13196,8 @@ part_of_refkey(TABLE *table,Field *field)
@param order Sort order
@param table Table to sort
@param idx Index to check
@param used_key_parts Return value for used key parts.
@param used_key_parts [out] NULL by default, otherwise return value for
used key parts.
@note
@@ -13132,13 +13216,14 @@ part_of_refkey(TABLE *table,Field *field)
*/
static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
uint *used_key_parts)
uint *used_key_parts= NULL)
{
KEY_PART_INFO *key_part,*key_part_end;
key_part=table->key_info[idx].key_part;
key_part_end=key_part+table->key_info[idx].key_parts;
key_part_map const_key_parts=table->const_key_parts[idx];
int reverse=0;
uint key_parts;
my_bool on_pk_suffix= FALSE;
DBUG_ENTER("test_if_order_by_key");
@@ -13179,8 +13264,9 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
*/
if (key_part == key_part_end && reverse == 0)
{
*used_key_parts= 0;
DBUG_RETURN(1);
key_parts= 0;
reverse= 1;
goto ok;
}
}
else
@@ -13203,7 +13289,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
uint used_key_parts_secondary= table->key_info[idx].key_parts;
uint used_key_parts_pk=
(uint) (key_part - table->key_info[table->s->primary_key].key_part);
*used_key_parts= used_key_parts_pk + used_key_parts_secondary;
key_parts= used_key_parts_pk + used_key_parts_secondary;
if (reverse == -1 &&
(!(table->file->index_flags(idx, used_key_parts_secondary - 1, 1) &
@@ -13214,11 +13300,14 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
}
else
{
*used_key_parts= (uint) (key_part - table->key_info[idx].key_part);
key_parts= (uint) (key_part - table->key_info[idx].key_part);
if (reverse == -1 &&
!(table->file->index_flags(idx, *used_key_parts-1, 1) & HA_READ_PREV))
!(table->file->index_flags(idx, key_parts-1, 1) & HA_READ_PREV))
reverse= 0; // Index can't be used
}
ok:
if (used_key_parts != NULL)
*used_key_parts= key_parts;
DBUG_RETURN(reverse);
}
@@ -13312,7 +13401,6 @@ test_if_subkey(ORDER *order, TABLE *table, uint ref, uint ref_key_parts,
uint nr;
uint min_length= (uint) ~0;
uint best= MAX_KEY;
uint not_used;
KEY_PART_INFO *ref_key_part= table->key_info[ref].key_part;
KEY_PART_INFO *ref_key_part_end= ref_key_part + ref_key_parts;
@@ -13323,7 +13411,7 @@ test_if_subkey(ORDER *order, TABLE *table, uint ref, uint ref_key_parts,
table->key_info[nr].key_parts >= ref_key_parts &&
is_subkey(table->key_info[nr].key_part, ref_key_part,
ref_key_part_end) &&
test_if_order_by_key(order, table, nr, &not_used))
test_if_order_by_key(order, table, nr))
{
min_length= table->key_info[nr].key_length;
best= nr;
@@ -13615,185 +13703,18 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
goto check_reverse_order;
}
{
/*
Check whether there is an index compatible with the given order
usage of which is cheaper than usage of the ref_key index (ref_key>=0)
or a table scan.
It may be the case if ORDER/GROUP BY is used with LIMIT.
*/
uint nr;
key_map keys;
uint best_key_parts= 0;
uint saved_best_key_parts= 0;
int best_key_direction= 0;
ha_rows best_records= 0;
double read_time;
int best_key= -1;
bool is_best_covering= FALSE;
double fanout= 1;
JOIN *join= tab->join;
uint tablenr= tab - join->join_tab;
ha_rows table_records= table->file->stats.records;
bool group= join->group && order == join->group_list;
ha_rows ref_key_quick_rows= HA_POS_ERROR;
/*
If not used with LIMIT, only use keys if the whole query can be
resolved with a key; This is because filesort() is usually faster than
retrieving all rows through an index.
*/
if (select_limit >= table_records)
{
keys= *table->file->keys_to_use_for_scanning();
keys.merge(table->covering_keys);
/*
We are adding here also the index specified in FORCE INDEX clause,
if any.
This is to allow users to use index in ORDER BY.
*/
if (table->force_index)
keys.merge(group ? table->keys_in_use_for_group_by :
table->keys_in_use_for_order_by);
keys.intersect(usable_keys);
}
else
keys= usable_keys;
if (ref_key >= 0 && table->covering_keys.is_set(ref_key))
ref_key_quick_rows= table->quick_rows[ref_key];
read_time= join->best_positions[tablenr].read_time;
for (uint i= tablenr+1; i < join->tables; i++)
fanout*= join->best_positions[i].records_read; // fanout is always >= 1
for (nr=0; nr < table->s->keys ; nr++)
{
int direction;
if (keys.is_set(nr) &&
(direction= test_if_order_by_key(order, table, nr, &used_key_parts)))
{
/*
At this point we are sure that ref_key is a non-ordering
key (where "ordering key" is a key that will return rows
in the order required by ORDER BY).
*/
DBUG_ASSERT (ref_key != (int) nr);
bool is_covering= table->covering_keys.is_set(nr) ||
(nr == table->s->primary_key &&
table->file->primary_key_is_clustered());
/*
Don't use an index scan with ORDER BY without limit.
For GROUP BY without limit always use index scan
if there is a suitable index.
Why we hold to this asymmetry hardly can be explained
rationally. It's easy to demonstrate that using
temporary table + filesort could be cheaper for grouping
queries too.
*/
if (is_covering ||
select_limit != HA_POS_ERROR ||
(ref_key < 0 && (group || table->force_index)))
{
double rec_per_key;
double index_scan_time;
KEY *keyinfo= tab->table->key_info+nr;
if (select_limit == HA_POS_ERROR)
select_limit= table_records;
if (group)
{
/*
Used_key_parts can be larger than keyinfo->key_parts
when using a secondary index clustered with a primary
key (e.g. as in Innodb).
See Bug #28591 for details.
*/
rec_per_key= used_key_parts &&
used_key_parts <= keyinfo->key_parts ?
keyinfo->rec_per_key[used_key_parts-1] : 1;
set_if_bigger(rec_per_key, 1);
/*
With a grouping query each group containing on average
rec_per_key records produces only one row that will
be included into the result set.
*/
if (select_limit > table_records/rec_per_key)
select_limit= table_records;
else
select_limit= (ha_rows) (select_limit*rec_per_key);
}
/*
If tab=tk is not the last joined table tn then to get first
L records from the result set we can expect to retrieve
only L/fanout(tk,tn) where fanout(tk,tn) says how many
rows in the record set on average will match each row tk.
Usually our estimates for fanouts are too pessimistic.
So the estimate for L/fanout(tk,tn) will be too optimistic
and as result we'll choose an index scan when using ref/range
access + filesort will be cheaper.
*/
select_limit= (ha_rows) (select_limit < fanout ?
1 : select_limit/fanout);
/*
We assume that each of the tested indexes is not correlated
with ref_key. Thus, to select first N records we have to scan
N/selectivity(ref_key) index entries.
selectivity(ref_key) = #scanned_records/#table_records =
table->quick_condition_rows/table_records.
In any case we can't select more than #table_records.
N/(table->quick_condition_rows/table_records) > table_records
<=> N > table->quick_condition_rows.
*/
if (select_limit > table->quick_condition_rows)
select_limit= table_records;
else
select_limit= (ha_rows) (select_limit *
(double) table_records /
table->quick_condition_rows);
rec_per_key= keyinfo->rec_per_key[keyinfo->key_parts-1];
set_if_bigger(rec_per_key, 1);
/*
Here we take into account the fact that rows are
accessed in sequences rec_per_key records in each.
Rows in such a sequence are supposed to be ordered
by rowid/primary key. When reading the data
in a sequence we'll touch not more pages than the
table file contains.
TODO. Use the formula for a disk sweep sequential access
to calculate the cost of accessing data rows for one
index entry.
*/
index_scan_time= select_limit/rec_per_key *
min(rec_per_key, table->file->scan_time());
if ((ref_key < 0 && is_covering) ||
(ref_key < 0 && (group || table->force_index)) ||
index_scan_time < read_time)
{
ha_rows quick_records= table_records;
if ((is_best_covering && !is_covering) ||
(is_covering && ref_key_quick_rows < select_limit))
continue;
if (table->quick_keys.is_set(nr))
quick_records= table->quick_rows[nr];
if (best_key < 0 ||
(select_limit <= min(quick_records,best_records) ?
keyinfo->key_parts < best_key_parts :
quick_records < best_records))
{
best_key= nr;
best_key_parts= keyinfo->key_parts;
saved_best_key_parts= used_key_parts;
best_records= quick_records;
is_best_covering= is_covering;
best_key_direction= direction;
}
}
}
}
}
test_if_cheaper_ordering(tab, order, table, usable_keys,
ref_key, select_limit,
&best_key, &best_key_direction,
&select_limit, &best_key_parts,
&saved_best_key_parts);
/*
filesort() and join cache are usually faster than reading in
@@ -13897,7 +13818,7 @@ check_reverse_order:
*/
if (!select->quick->reverse_sorted())
{
QUICK_SELECT_DESC *tmp;
QUICK_SELECT_I *tmp;
int quick_type= select->quick->get_type();
if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE ||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
@@ -13910,16 +13831,14 @@ check_reverse_order:
}
/* ORDER BY range_key DESC */
tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick),
used_key_parts);
if (!tmp || tmp->error)
tmp= select->quick->make_reverse(used_key_parts);
if (!tmp)
{
delete tmp;
select->quick= save_quick;
tab->limit= 0;
DBUG_RETURN(0); // Reverse sort not supported
}
select->quick=tmp;
select->set_quick(tmp);
}
}
else if (tab->type != JT_NEXT && tab->type != JT_REF_OR_NULL &&
@@ -14351,7 +14270,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
if (my_hash_init(&hash, &my_charset_bin, (uint) file->stats.records, 0,
key_length, (my_hash_get_key) 0, 0, 0))
{
my_free((char*) key_buffer,MYF(0));
my_free(key_buffer);
DBUG_RETURN(1);
}
@@ -14403,14 +14322,14 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
}
key_pos+=extra_length;
}
my_free((char*) key_buffer,MYF(0));
my_free(key_buffer);
my_hash_free(&hash);
file->extra(HA_EXTRA_NO_CACHE);
(void) file->ha_rnd_end();
DBUG_RETURN(0);
err:
my_free((char*) key_buffer,MYF(0));
my_free(key_buffer);
my_hash_free(&hash);
file->extra(HA_EXTRA_NO_CACHE);
(void) file->ha_rnd_end();
@@ -14493,7 +14412,7 @@ join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count)
sizeof(CACHE_FIELD*))))
{
my_free((uchar*) cache->buff,MYF(0)); /* purecov: inspected */
my_free(cache->buff); /* purecov: inspected */
cache->buff=0; /* purecov: inspected */
DBUG_RETURN(1); /* purecov: inspected */
}
@@ -17456,6 +17375,358 @@ void JOIN::cache_const_exprs()
}
/**
Find a cheaper access key than a given @a key
@param tab NULL or JOIN_TAB of the accessed table
@param order Linked list of ORDER BY arguments
@param table Table if tab == NULL or tab->table
@param usable_keys Key map to find a cheaper key in
@param ref_key
* 0 <= key < MAX_KEY - key number (hint) to start the search
* -1 - no key number provided
@param select_limit LIMIT value
@param [out] new_key Key number if success, otherwise undefined
@param [out] new_key_direction Return -1 (reverse) or +1 if success,
otherwise undefined
@param [out] new_select_limit Return adjusted LIMIT
@param [out] new_used_key_parts NULL by default, otherwise return number
of new_key prefix columns if success
or undefined if the function fails
@param [out] saved_best_key_parts NULL by default, otherwise preserve the
value for further use in QUICK_SELECT_DESC
@note
This function takes into account table->quick_condition_rows statistic
(that is calculated by the make_join_statistics function).
However, single table procedures such as mysql_update() and mysql_delete()
never call make_join_statistics, so they have to update it manually
(@see get_index_for_order()).
*/
static bool
test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
key_map usable_keys, int ref_key,
ha_rows select_limit,
int *new_key, int *new_key_direction,
ha_rows *new_select_limit, uint *new_used_key_parts,
uint *saved_best_key_parts)
{
DBUG_ENTER("test_if_cheaper_ordering");
/*
Check whether there is an index compatible with the given order
usage of which is cheaper than usage of the ref_key index (ref_key>=0)
or a table scan.
It may be the case if ORDER/GROUP BY is used with LIMIT.
*/
ha_rows best_select_limit= HA_POS_ERROR;
JOIN *join= tab ? tab->join : NULL;
uint nr;
key_map keys;
uint best_key_parts= 0;
int best_key_direction= 0;
ha_rows best_records= 0;
double read_time;
int best_key= -1;
bool is_best_covering= FALSE;
double fanout= 1;
ha_rows table_records= table->file->stats.records;
bool group= join && join->group && order == join->group_list;
ha_rows ref_key_quick_rows= HA_POS_ERROR;
/*
If not used with LIMIT, only use keys if the whole query can be
resolved with a key; This is because filesort() is usually faster than
retrieving all rows through an index.
*/
if (select_limit >= table_records)
{
keys= *table->file->keys_to_use_for_scanning();
keys.merge(table->covering_keys);
/*
We are adding here also the index specified in FORCE INDEX clause,
if any.
This is to allow users to use index in ORDER BY.
*/
if (table->force_index)
keys.merge(group ? table->keys_in_use_for_group_by :
table->keys_in_use_for_order_by);
keys.intersect(usable_keys);
}
else
keys= usable_keys;
if (ref_key >= 0 && table->covering_keys.is_set(ref_key))
ref_key_quick_rows= table->quick_rows[ref_key];
if (join)
{
uint tablenr= tab - join->join_tab;
read_time= join->best_positions[tablenr].read_time;
for (uint i= tablenr+1; i < join->tables; i++)
fanout*= join->best_positions[i].records_read; // fanout is always >= 1
}
else
read_time= table->file->scan_time();
for (nr=0; nr < table->s->keys ; nr++)
{
int direction;
uint used_key_parts;
if (keys.is_set(nr) &&
(direction= test_if_order_by_key(order, table, nr, &used_key_parts)))
{
/*
At this point we are sure that ref_key is a non-ordering
key (where "ordering key" is a key that will return rows
in the order required by ORDER BY).
*/
DBUG_ASSERT (ref_key != (int) nr);
bool is_covering= table->covering_keys.is_set(nr) ||
(nr == table->s->primary_key &&
table->file->primary_key_is_clustered());
/*
Don't use an index scan with ORDER BY without limit.
For GROUP BY without limit always use index scan
if there is a suitable index.
Why we hold to this asymmetry hardly can be explained
rationally. It's easy to demonstrate that using
temporary table + filesort could be cheaper for grouping
queries too.
*/
if (is_covering ||
select_limit != HA_POS_ERROR ||
(ref_key < 0 && (group || table->force_index)))
{
double rec_per_key;
double index_scan_time;
KEY *keyinfo= table->key_info+nr;
if (select_limit == HA_POS_ERROR)
select_limit= table_records;
if (group)
{
/*
Used_key_parts can be larger than keyinfo->key_parts
when using a secondary index clustered with a primary
key (e.g. as in Innodb).
See Bug #28591 for details.
*/
rec_per_key= used_key_parts &&
used_key_parts <= keyinfo->key_parts ?
keyinfo->rec_per_key[used_key_parts-1] : 1;
set_if_bigger(rec_per_key, 1);
/*
With a grouping query each group containing on average
rec_per_key records produces only one row that will
be included into the result set.
*/
if (select_limit > table_records/rec_per_key)
select_limit= table_records;
else
select_limit= (ha_rows) (select_limit*rec_per_key);
}
/*
If tab=tk is not the last joined table tn then to get first
L records from the result set we can expect to retrieve
only L/fanout(tk,tn) where fanout(tk,tn) says how many
rows in the record set on average will match each row tk.
Usually our estimates for fanouts are too pessimistic.
So the estimate for L/fanout(tk,tn) will be too optimistic
and as result we'll choose an index scan when using ref/range
access + filesort will be cheaper.
*/
select_limit= (ha_rows) (select_limit < fanout ?
1 : select_limit/fanout);
/*
We assume that each of the tested indexes is not correlated
with ref_key. Thus, to select first N records we have to scan
N/selectivity(ref_key) index entries.
selectivity(ref_key) = #scanned_records/#table_records =
table->quick_condition_rows/table_records.
In any case we can't select more than #table_records.
N/(table->quick_condition_rows/table_records) > table_records
<=> N > table->quick_condition_rows.
*/
if (select_limit > table->quick_condition_rows)
select_limit= table_records;
else
select_limit= (ha_rows) (select_limit *
(double) table_records /
table->quick_condition_rows);
rec_per_key= keyinfo->rec_per_key[keyinfo->key_parts-1];
set_if_bigger(rec_per_key, 1);
/*
Here we take into account the fact that rows are
accessed in sequences rec_per_key records in each.
Rows in such a sequence are supposed to be ordered
by rowid/primary key. When reading the data
in a sequence we'll touch not more pages than the
table file contains.
TODO. Use the formula for a disk sweep sequential access
to calculate the cost of accessing data rows for one
index entry.
*/
index_scan_time= select_limit/rec_per_key *
min(rec_per_key, table->file->scan_time());
if ((ref_key < 0 && is_covering) ||
(ref_key < 0 && (group || table->force_index)) ||
index_scan_time < read_time)
{
ha_rows quick_records= table_records;
if ((is_best_covering && !is_covering) ||
(is_covering && ref_key_quick_rows < select_limit))
continue;
if (table->quick_keys.is_set(nr))
quick_records= table->quick_rows[nr];
if (best_key < 0 ||
(select_limit <= min(quick_records,best_records) ?
keyinfo->key_parts < best_key_parts :
quick_records < best_records))
{
best_key= nr;
best_key_parts= keyinfo->key_parts;
if (saved_best_key_parts)
*saved_best_key_parts= used_key_parts;
best_records= quick_records;
is_best_covering= is_covering;
best_key_direction= direction;
best_select_limit= select_limit;
}
}
}
}
}
if (best_key < 0 || best_key == ref_key)
DBUG_RETURN(FALSE);
*new_key= best_key;
*new_key_direction= best_key_direction;
*new_select_limit= best_select_limit;
if (new_used_key_parts != NULL)
*new_used_key_parts= best_key_parts;
DBUG_RETURN(TRUE);
}
/**
Find a key to apply single table UPDATE/DELETE by a given ORDER
@param order Linked list of ORDER BY arguments
@param table Table to find a key
@param select Pointer to access/update select->quick (if any)
@param limit LIMIT clause parameter
@param [out] need_sort TRUE if filesort needed
@param [out] reverse
TRUE if the key is reversed again given ORDER (undefined if key == MAX_KEY)
@return
- MAX_KEY if no key found (need_sort == TRUE)
- MAX_KEY if quick select result order is OK (need_sort == FALSE)
- key number (either index scan or quick select) (need_sort == FALSE)
@note
Side effects:
- may deallocate or deallocate and replace select->quick;
- may set table->quick_condition_rows and table->quick_rows[...]
to table->file->stats.records.
*/
uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
ha_rows limit, bool *need_sort, bool *reverse)
{
if (select && select->quick && select->quick->unique_key_range())
{ // Single row select (always "ordered"): Ok to use with key field UPDATE
*need_sort= FALSE;
/*
Returning of MAX_KEY here prevents updating of used_key_is_modified
in mysql_update(). Use quick select "as is".
*/
return MAX_KEY;
}
if (!order)
{
*need_sort= FALSE;
if (select && select->quick)
return select->quick->index; // index or MAX_KEY, use quick select as is
else
return table->file->key_used_on_scan; // MAX_KEY or index for some engines
}
if (!is_simple_order(order)) // just to cut further expensive checks
{
*need_sort= TRUE;
return MAX_KEY;
}
if (select && select->quick)
{
if (select->quick->index == MAX_KEY)
{
*need_sort= TRUE;
return MAX_KEY;
}
uint used_key_parts;
switch (test_if_order_by_key(order, table, select->quick->index,
&used_key_parts)) {
case 1: // desired order
*need_sort= FALSE;
return select->quick->index;
case 0: // unacceptable order
*need_sort= TRUE;
return MAX_KEY;
case -1: // desired order, but opposite direction
{
QUICK_SELECT_I *reverse_quick;
if ((reverse_quick=
select->quick->make_reverse(used_key_parts)))
{
select->set_quick(reverse_quick);
*need_sort= FALSE;
return select->quick->index;
}
else
{
*need_sort= TRUE;
return MAX_KEY;
}
}
}
DBUG_ASSERT(0);
}
else if (limit != HA_POS_ERROR)
{ // check if some index scan & LIMIT is more efficient than filesort
/*
Update quick_condition_rows since single table UPDATE/DELETE procedures
don't call make_join_statistics() and leave this variable uninitialized.
*/
table->quick_condition_rows= table->file->stats.records;
int key, direction;
if (test_if_cheaper_ordering(NULL, order, table,
table->keys_in_use_for_order_by, -1,
limit,
&key, &direction, &limit) &&
!is_key_used(table, key, table->write_set))
{
*need_sort= FALSE;
*reverse= (direction < 0);
return key;
}
}
*need_sort= TRUE;
return MAX_KEY;
}
/**
@} (end of group Query_Optimizer)
*/