mirror of
https://github.com/MariaDB/server.git
synced 2025-12-24 11:21:21 +03:00
Merge with MariaDB 5.1
This commit is contained in:
@@ -289,57 +289,60 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
Fix fields referenced from inner selects.
|
||||
|
||||
SYNOPSIS
|
||||
fix_inner_refs()
|
||||
thd Thread handle
|
||||
all_fields List of all fields used in select
|
||||
select Current select
|
||||
ref_pointer_array Array of references to Items used in current select
|
||||
group_list GROUP BY list (is NULL by default)
|
||||
@param thd Thread handle
|
||||
@param all_fields List of all fields used in select
|
||||
@param select Current select
|
||||
@param ref_pointer_array Array of references to Items used in current select
|
||||
@param group_list GROUP BY list (is NULL by default)
|
||||
|
||||
DESCRIPTION
|
||||
The function serves 3 purposes - adds fields referenced from inner
|
||||
selects to the current select list, resolves which class to use
|
||||
to access referenced item (Item_ref of Item_direct_ref) and fixes
|
||||
references (Item_ref objects) to these fields.
|
||||
@details
|
||||
The function serves 3 purposes
|
||||
|
||||
If a field isn't already in the select list and the ref_pointer_array
|
||||
- adds fields referenced from inner query blocks to the current select list
|
||||
|
||||
- Decides which class to use to reference the items (Item_ref or
|
||||
Item_direct_ref)
|
||||
|
||||
- fixes references (Item_ref objects) to these fields.
|
||||
|
||||
If a field isn't already on the select list and the ref_pointer_array
|
||||
is provided then it is added to the all_fields list and the pointer to
|
||||
it is saved in the ref_pointer_array.
|
||||
|
||||
The class to access the outer field is determined by the following rules:
|
||||
1. If the outer field isn't used under an aggregate function
|
||||
then the Item_ref class should be used.
|
||||
2. If the outer field is used under an aggregate function and this
|
||||
function is aggregated in the select where the outer field was
|
||||
resolved or in some more inner select then the Item_direct_ref
|
||||
class should be used.
|
||||
It used used also if we are grouping by a subquery that refers
|
||||
this outer field.
|
||||
|
||||
-#. If the outer field isn't used under an aggregate function then the
|
||||
Item_ref class should be used.
|
||||
|
||||
-#. If the outer field is used under an aggregate function and this
|
||||
function is, in turn, aggregated in the query block where the outer
|
||||
field was resolved or some query nested therein, then the
|
||||
Item_direct_ref class should be used. Also it should be used if we are
|
||||
grouping by a subquery containing the outer field.
|
||||
|
||||
The resolution is done here and not at the fix_fields() stage as
|
||||
it can be done only after sum functions are fixed and pulled up to
|
||||
selects where they are have to be aggregated.
|
||||
it can be done only after aggregate functions are fixed and pulled up to
|
||||
selects where they are to be aggregated.
|
||||
|
||||
When the class is chosen it substitutes the original field in the
|
||||
Item_outer_ref object.
|
||||
|
||||
After this we proceed with fixing references (Item_outer_ref objects) to
|
||||
this field from inner subqueries.
|
||||
|
||||
RETURN
|
||||
TRUE an error occured
|
||||
FALSE ok
|
||||
*/
|
||||
@return Status
|
||||
@retval true An error occured.
|
||||
@retval false OK.
|
||||
*/
|
||||
|
||||
bool
|
||||
fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
|
||||
Item **ref_pointer_array)
|
||||
{
|
||||
Item_outer_ref *ref;
|
||||
bool res= FALSE;
|
||||
bool direct_ref= FALSE;
|
||||
|
||||
/*
|
||||
Mark the references from the inner_refs_list that are occurred in
|
||||
@@ -356,6 +359,7 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
|
||||
|
||||
while ((ref= ref_it++))
|
||||
{
|
||||
bool direct_ref= false;
|
||||
Item *item= ref->outer_ref;
|
||||
Item **item_ref= ref->ref;
|
||||
Item_ref *new_ref;
|
||||
@@ -414,7 +418,7 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
|
||||
return TRUE;
|
||||
thd->used_tables|= item->used_tables();
|
||||
}
|
||||
return res;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -966,7 +970,7 @@ JOIN::optimize()
|
||||
If all items were resolved by opt_sum_query, there is no need to
|
||||
open any tables.
|
||||
*/
|
||||
if ((res=opt_sum_query(select_lex->leaf_tables, all_fields, conds)))
|
||||
if ((res=opt_sum_query(thd, select_lex->leaf_tables, all_fields, conds)))
|
||||
{
|
||||
if (res == HA_ERR_KEY_NOT_FOUND)
|
||||
{
|
||||
@@ -1920,7 +1924,11 @@ JOIN::exec()
|
||||
if (!curr_join->sort_and_group &&
|
||||
curr_join->const_tables != curr_join->tables)
|
||||
curr_join->join_tab[curr_join->const_tables].sorted= 0;
|
||||
if ((tmp_error= do_select(curr_join, (List<Item> *) 0, curr_tmp_table, 0)))
|
||||
|
||||
Procedure *save_proc= curr_join->procedure;
|
||||
tmp_error= do_select(curr_join, (List<Item> *) 0, curr_tmp_table, 0);
|
||||
curr_join->procedure= save_proc;
|
||||
if (tmp_error)
|
||||
{
|
||||
error= tmp_error;
|
||||
DBUG_VOID_RETURN;
|
||||
@@ -3304,6 +3312,7 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
|
||||
@param field Field used in comparision
|
||||
@param eq_func True if we used =, <=> or IS NULL
|
||||
@param value Value used for comparison with field
|
||||
@param num_values Number of values[] that we are comparing against
|
||||
@param usable_tables Tables which can be used for key optimization
|
||||
@param sargables IN/OUT Array of found sargable candidates
|
||||
|
||||
@@ -3396,26 +3405,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond,
|
||||
eq_func is NEVER true when num_values > 1
|
||||
*/
|
||||
if (!eq_func)
|
||||
{
|
||||
/*
|
||||
Additional optimization: if we're processing
|
||||
"t.key BETWEEN c1 AND c1" then proceed as if we were processing
|
||||
"t.key = c1".
|
||||
TODO: This is a very limited fix. A more generic fix is possible.
|
||||
There are 2 options:
|
||||
A) Make equality propagation code be able to handle BETWEEN
|
||||
(including cases like t1.key BETWEEN t2.key AND t3.key)
|
||||
B) Make range optimizer to infer additional "t.key = c" equalities
|
||||
and use them in equality propagation process (see details in
|
||||
OptimizerKBAndTodo)
|
||||
*/
|
||||
if ((cond->functype() != Item_func::BETWEEN) ||
|
||||
((Item_func_between*) cond)->negated ||
|
||||
!value[0]->eq(value[1], field->binary()))
|
||||
return;
|
||||
eq_func= TRUE;
|
||||
}
|
||||
|
||||
return;
|
||||
if (field->result_type() == STRING_RESULT)
|
||||
{
|
||||
if ((*value)->result_type() != STRING_RESULT)
|
||||
@@ -3630,9 +3620,65 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
|
||||
case Item_func::OPTIMIZE_KEY:
|
||||
{
|
||||
Item **values;
|
||||
// BETWEEN, IN, NE
|
||||
if (is_local_field (cond_func->key_item()) &&
|
||||
!(cond_func->used_tables() & OUTER_REF_TABLE_BIT))
|
||||
/*
|
||||
Build list of possible keys for 'a BETWEEN low AND high'.
|
||||
It is handled similar to the equivalent condition
|
||||
'a >= low AND a <= high':
|
||||
*/
|
||||
if (cond_func->functype() == Item_func::BETWEEN)
|
||||
{
|
||||
Item_field *field_item;
|
||||
bool equal_func= FALSE;
|
||||
uint num_values= 2;
|
||||
values= cond_func->arguments();
|
||||
|
||||
bool binary_cmp= (values[0]->real_item()->type() == Item::FIELD_ITEM)
|
||||
? ((Item_field*)values[0]->real_item())->field->binary()
|
||||
: TRUE;
|
||||
|
||||
/*
|
||||
Additional optimization: If 'low = high':
|
||||
Handle as if the condition was "t.key = low".
|
||||
*/
|
||||
if (!((Item_func_between*)cond_func)->negated &&
|
||||
values[1]->eq(values[2], binary_cmp))
|
||||
{
|
||||
equal_func= TRUE;
|
||||
num_values= 1;
|
||||
}
|
||||
|
||||
/*
|
||||
Append keys for 'field <cmp> value[]' if the
|
||||
condition is of the form::
|
||||
'<field> BETWEEN value[1] AND value[2]'
|
||||
*/
|
||||
if (is_local_field(values[0]))
|
||||
{
|
||||
field_item= (Item_field *) (values[0]->real_item());
|
||||
add_key_equal_fields(key_fields, *and_level, cond_func,
|
||||
field_item, equal_func, &values[1],
|
||||
num_values, usable_tables, sargables);
|
||||
}
|
||||
/*
|
||||
Append keys for 'value[0] <cmp> field' if the
|
||||
condition is of the form:
|
||||
'value[0] BETWEEN field1 AND field2'
|
||||
*/
|
||||
for (uint i= 1; i <= num_values; i++)
|
||||
{
|
||||
if (is_local_field(values[i]))
|
||||
{
|
||||
field_item= (Item_field *) (values[i]->real_item());
|
||||
add_key_equal_fields(key_fields, *and_level, cond_func,
|
||||
field_item, equal_func, values,
|
||||
1, usable_tables, sargables);
|
||||
}
|
||||
}
|
||||
} // if ( ... Item_func::BETWEEN)
|
||||
|
||||
// IN, NE
|
||||
else if (is_local_field (cond_func->key_item()) &&
|
||||
!(cond_func->used_tables() & OUTER_REF_TABLE_BIT))
|
||||
{
|
||||
values= cond_func->arguments()+1;
|
||||
if (cond_func->functype() == Item_func::NE_FUNC &&
|
||||
@@ -3646,21 +3692,6 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
|
||||
cond_func->argument_count()-1,
|
||||
usable_tables, sargables);
|
||||
}
|
||||
if (cond_func->functype() == Item_func::BETWEEN)
|
||||
{
|
||||
values= cond_func->arguments();
|
||||
for (uint i= 1 ; i < cond_func->argument_count() ; i++)
|
||||
{
|
||||
Item_field *field_item;
|
||||
if (is_local_field (cond_func->arguments()[i]))
|
||||
{
|
||||
field_item= (Item_field *) (cond_func->arguments()[i]->real_item());
|
||||
add_key_equal_fields(key_fields, *and_level, cond_func,
|
||||
field_item, 0, values, 1, usable_tables,
|
||||
sargables);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Item_func::OPTIMIZE_OP:
|
||||
@@ -12680,22 +12711,21 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
|
||||
DBUG_ENTER("end_send");
|
||||
if (!end_of_records)
|
||||
{
|
||||
int error;
|
||||
if (join->having && join->having->val_int() == 0)
|
||||
DBUG_RETURN(NESTED_LOOP_OK); // Didn't match having
|
||||
error=0;
|
||||
if (join->procedure)
|
||||
error=join->procedure->send_row(join->procedure_fields_list);
|
||||
else if (join->do_send_rows)
|
||||
{
|
||||
if ((error= join->result->send_data(*join->fields)) < 0)
|
||||
{
|
||||
/* row was not accepted. Don't count it */
|
||||
DBUG_RETURN(NESTED_LOOP_OK);
|
||||
}
|
||||
if (join->procedure->send_row(join->procedure_fields_list))
|
||||
DBUG_RETURN(NESTED_LOOP_ERROR);
|
||||
DBUG_RETURN(NESTED_LOOP_OK);
|
||||
}
|
||||
if (join->do_send_rows)
|
||||
{
|
||||
int error;
|
||||
/* result < 0 if row was not accepted and should not be counted */
|
||||
if ((error= join->result->send_data(*join->fields)))
|
||||
DBUG_RETURN(error < 0 ? NESTED_LOOP_OK : NESTED_LOOP_ERROR);
|
||||
}
|
||||
if (error)
|
||||
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
|
||||
if (++join->send_records >= join->unit->select_limit_cnt &&
|
||||
join->do_send_rows)
|
||||
{
|
||||
@@ -13195,6 +13225,42 @@ static bool test_if_ref(Item_field *left_item,Item *right_item)
|
||||
return 0; // keep test
|
||||
}
|
||||
|
||||
/**
|
||||
Extract a condition that can be checked after reading given table
|
||||
|
||||
@param cond Condition to analyze
|
||||
@param tables Tables for which "current field values" are available
|
||||
@param used_table Table that we're extracting the condition for (may
|
||||
also include PSEUDO_TABLE_BITS, and may be zero)
|
||||
@param exclude_expensive_cond Do not push expensive conditions
|
||||
|
||||
@retval <>NULL Generated condition
|
||||
@retval =NULL Already checked, OR error
|
||||
|
||||
@details
|
||||
Extract the condition that can be checked after reading the table
|
||||
specified in 'used_table', given that current-field values for tables
|
||||
specified in 'tables' bitmap are available.
|
||||
If 'used_table' is 0
|
||||
- extract conditions for all tables in 'tables'.
|
||||
- extract conditions are unrelated to any tables
|
||||
in the same query block/level(i.e. conditions
|
||||
which have used_tables == 0).
|
||||
|
||||
The function assumes that
|
||||
- Constant parts of the condition has already been checked.
|
||||
- Condition that could be checked for tables in 'tables' has already
|
||||
been checked.
|
||||
|
||||
The function takes into account that some parts of the condition are
|
||||
guaranteed to be true by employed 'ref' access methods (the code that
|
||||
does this is located at the end, search down for "EQ_FUNC").
|
||||
|
||||
@note
|
||||
Make sure to keep the implementations of make_cond_for_table() and
|
||||
make_cond_after_sjm() synchronized.
|
||||
make_cond_for_info_schema() uses similar algorithm as well.
|
||||
*/
|
||||
|
||||
static COND *
|
||||
make_cond_for_table(COND *cond, table_map tables, table_map used_table)
|
||||
@@ -13682,12 +13748,13 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
|
||||
{
|
||||
int ref_key;
|
||||
uint ref_key_parts;
|
||||
int order_direction;
|
||||
int order_direction= 0;
|
||||
uint used_key_parts;
|
||||
TABLE *table=tab->table;
|
||||
SQL_SELECT *select=tab->select;
|
||||
key_map usable_keys;
|
||||
QUICK_SELECT_I *save_quick= 0;
|
||||
int best_key= -1;
|
||||
DBUG_ENTER("test_if_skip_sort_order");
|
||||
LINT_INIT(ref_key_parts);
|
||||
|
||||
@@ -13791,13 +13858,15 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
|
||||
new_ref_key_map.clear_all(); // Force the creation of quick select
|
||||
new_ref_key_map.set_bit(new_ref_key); // only for new_ref_key.
|
||||
|
||||
/* Reset quick; This will be restored in 'use_filesort' if needed */
|
||||
select->quick= 0;
|
||||
if (select->test_quick_select(tab->join->thd, new_ref_key_map, 0,
|
||||
(tab->join->select_options &
|
||||
OPTION_FOUND_ROWS) ?
|
||||
HA_POS_ERROR :
|
||||
tab->join->unit->select_limit_cnt,0) <=
|
||||
0)
|
||||
DBUG_RETURN(0);
|
||||
goto use_filesort;
|
||||
}
|
||||
ref_key= new_ref_key;
|
||||
}
|
||||
@@ -13822,7 +13891,6 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
|
||||
int best_key_direction;
|
||||
ha_rows best_records;
|
||||
double read_time;
|
||||
int best_key= -1;
|
||||
bool is_best_covering= FALSE;
|
||||
double fanout= 1;
|
||||
JOIN *join= tab->join;
|
||||
@@ -14040,72 +14108,21 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
|
||||
tab->join->tables > tab->join->const_tables + 1) &&
|
||||
((unsigned) best_key != table->s->primary_key ||
|
||||
!table->file->primary_key_is_clustered()))
|
||||
DBUG_RETURN(0);
|
||||
goto use_filesort;
|
||||
|
||||
if (best_key >= 0)
|
||||
{
|
||||
bool quick_created= FALSE;
|
||||
if (table->quick_keys.is_set(best_key) && best_key != ref_key)
|
||||
{
|
||||
key_map map;
|
||||
map.clear_all(); // Force the creation of quick select
|
||||
map.set_bit(best_key); // only best_key.
|
||||
quick_created=
|
||||
select->test_quick_select(join->thd, map, 0,
|
||||
join->select_options & OPTION_FOUND_ROWS ?
|
||||
HA_POS_ERROR :
|
||||
join->unit->select_limit_cnt,
|
||||
0) > 0;
|
||||
}
|
||||
if (!no_changes)
|
||||
{
|
||||
/*
|
||||
If ref_key used index tree reading only ('Using index' in EXPLAIN),
|
||||
and best_key doesn't, then revert the decision.
|
||||
*/
|
||||
if (!table->covering_keys.is_set(best_key))
|
||||
table->disable_keyread();
|
||||
if (!quick_created)
|
||||
{
|
||||
tab->index= best_key;
|
||||
tab->read_first_record= best_key_direction > 0 ?
|
||||
join_read_first:join_read_last;
|
||||
tab->type=JT_NEXT; // Read with index_first(), index_next()
|
||||
if (select && select->quick)
|
||||
{
|
||||
delete select->quick;
|
||||
select->quick= 0;
|
||||
}
|
||||
if (table->covering_keys.is_set(best_key) && ! table->key_read)
|
||||
table->enable_keyread();
|
||||
table->file->ha_index_or_rnd_end();
|
||||
if (join->select_options & SELECT_DESCRIBE)
|
||||
{
|
||||
tab->ref.key= -1;
|
||||
tab->ref.key_parts= 0;
|
||||
if (select_limit < table_records)
|
||||
tab->limit= select_limit;
|
||||
}
|
||||
}
|
||||
else if (tab->type != JT_ALL)
|
||||
{
|
||||
/*
|
||||
We're about to use a quick access to the table.
|
||||
We need to change the access method so as the quick access
|
||||
method is actually used.
|
||||
*/
|
||||
DBUG_ASSERT(tab->select->quick);
|
||||
tab->type=JT_ALL;
|
||||
tab->use_quick=1;
|
||||
tab->ref.key= -1;
|
||||
tab->ref.key_parts=0; // Don't use ref key.
|
||||
tab->read_first_record= join_init_read_record;
|
||||
if (tab->is_using_loose_index_scan())
|
||||
join->tmp_table_param.precomputed_group_by= TRUE;
|
||||
/*
|
||||
TODO: update the number of records in join->best_positions[tablenr]
|
||||
*/
|
||||
}
|
||||
select->quick= 0;
|
||||
select->test_quick_select(join->thd, map, 0,
|
||||
join->select_options & OPTION_FOUND_ROWS ?
|
||||
HA_POS_ERROR :
|
||||
join->unit->select_limit_cnt,
|
||||
0);
|
||||
}
|
||||
order_direction= best_key_direction;
|
||||
/*
|
||||
@@ -14118,61 +14135,155 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
|
||||
saved_best_key_parts : best_key_parts;
|
||||
}
|
||||
else
|
||||
DBUG_RETURN(0);
|
||||
goto use_filesort;
|
||||
}
|
||||
|
||||
check_reverse_order:
|
||||
DBUG_ASSERT(order_direction != 0);
|
||||
|
||||
if (order_direction == -1) // If ORDER BY ... DESC
|
||||
{
|
||||
int quick_type;
|
||||
if (select && select->quick)
|
||||
{
|
||||
/*
|
||||
Don't reverse the sort order, if it's already done.
|
||||
(In some cases test_if_order_by_key() can be called multiple times
|
||||
*/
|
||||
if (!select->quick->reverse_sorted())
|
||||
if (select->quick->reverse_sorted())
|
||||
goto skipped_filesort;
|
||||
|
||||
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 ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
|
||||
{
|
||||
QUICK_SELECT_DESC *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 ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
|
||||
quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
|
||||
{
|
||||
tab->limit= 0;
|
||||
select->quick= save_quick;
|
||||
DBUG_RETURN(0); // Use filesort
|
||||
}
|
||||
|
||||
/* ORDER BY range_key DESC */
|
||||
tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick),
|
||||
used_key_parts);
|
||||
if (!tmp || tmp->error)
|
||||
{
|
||||
delete tmp;
|
||||
select->quick= save_quick;
|
||||
tab->limit= 0;
|
||||
DBUG_RETURN(0); // Reverse sort not supported
|
||||
}
|
||||
select->quick=tmp;
|
||||
tab->limit= 0;
|
||||
goto use_filesort; // Use filesort
|
||||
}
|
||||
}
|
||||
else if (tab->type != JT_NEXT && tab->type != JT_REF_OR_NULL &&
|
||||
tab->ref.key >= 0 && tab->ref.key_parts <= used_key_parts)
|
||||
{
|
||||
/*
|
||||
SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC
|
||||
|
||||
Use a traversal function that starts by reading the last row
|
||||
with key part (A) and then traverse the index backwards.
|
||||
*/
|
||||
tab->read_first_record= join_read_last_key;
|
||||
tab->read_record.read_record= join_read_prev_same;
|
||||
}
|
||||
}
|
||||
else if (select && select->quick)
|
||||
select->quick->sorted= 1;
|
||||
|
||||
/*
|
||||
Update query plan with access pattern for doing ordered access
|
||||
according to what we have decided above.
|
||||
*/
|
||||
if (!no_changes) // We are allowed to update QEP
|
||||
{
|
||||
if (best_key >= 0)
|
||||
{
|
||||
bool quick_created=
|
||||
(select && select->quick && select->quick!=save_quick);
|
||||
|
||||
/*
|
||||
If ref_key used index tree reading only ('Using index' in EXPLAIN),
|
||||
and best_key doesn't, then revert the decision.
|
||||
*/
|
||||
if (!table->covering_keys.is_set(best_key))
|
||||
table->disable_keyread();
|
||||
if (!quick_created)
|
||||
{
|
||||
if (select) // Throw any existing quick select
|
||||
select->quick= 0; // Cleanup either reset to save_quick,
|
||||
// or 'delete save_quick'
|
||||
tab->index= best_key;
|
||||
tab->read_first_record= order_direction > 0 ?
|
||||
join_read_first:join_read_last;
|
||||
tab->type=JT_NEXT; // Read with index_first(), index_next()
|
||||
|
||||
if (table->covering_keys.is_set(best_key) && ! table->key_read)
|
||||
table->enable_keyread();
|
||||
table->file->ha_index_or_rnd_end();
|
||||
if (tab->join->select_options & SELECT_DESCRIBE)
|
||||
{
|
||||
tab->ref.key= -1;
|
||||
tab->ref.key_parts= 0;
|
||||
if (select_limit < table->file->stats.records)
|
||||
tab->limit= select_limit;
|
||||
}
|
||||
}
|
||||
else if (tab->type != JT_ALL)
|
||||
{
|
||||
/*
|
||||
We're about to use a quick access to the table.
|
||||
We need to change the access method so as the quick access
|
||||
method is actually used.
|
||||
*/
|
||||
DBUG_ASSERT(tab->select->quick);
|
||||
tab->type=JT_ALL;
|
||||
tab->use_quick=1;
|
||||
tab->ref.key= -1;
|
||||
tab->ref.key_parts=0; // Don't use ref key.
|
||||
tab->read_first_record= join_init_read_record;
|
||||
if (tab->is_using_loose_index_scan())
|
||||
tab->join->tmp_table_param.precomputed_group_by= TRUE;
|
||||
/*
|
||||
TODO: update the number of records in join->best_positions[tablenr]
|
||||
*/
|
||||
}
|
||||
} // best_key >= 0
|
||||
|
||||
if (order_direction == -1) // If ORDER BY ... DESC
|
||||
{
|
||||
if (select && select->quick)
|
||||
{
|
||||
QUICK_SELECT_DESC *tmp;
|
||||
/* ORDER BY range_key DESC */
|
||||
tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick),
|
||||
used_key_parts);
|
||||
if (tmp && select->quick == save_quick)
|
||||
save_quick= 0; // ::QUICK_SELECT_DESC consumed it
|
||||
|
||||
if (!tmp || tmp->error)
|
||||
{
|
||||
delete tmp;
|
||||
tab->limit= 0;
|
||||
goto use_filesort; // Reverse sort failed -> filesort
|
||||
}
|
||||
select->quick= tmp;
|
||||
}
|
||||
else if (tab->type != JT_NEXT && tab->type != JT_REF_OR_NULL &&
|
||||
tab->ref.key >= 0 && tab->ref.key_parts <= used_key_parts)
|
||||
{
|
||||
/*
|
||||
SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC
|
||||
|
||||
Use a traversal function that starts by reading the last row
|
||||
with key part (A) and then traverse the index backwards.
|
||||
*/
|
||||
tab->read_first_record= join_read_last_key;
|
||||
tab->read_record.read_record= join_read_prev_same;
|
||||
}
|
||||
}
|
||||
else if (select && select->quick)
|
||||
select->quick->sorted= 1;
|
||||
|
||||
} // QEP has been modified
|
||||
|
||||
/*
|
||||
Cleanup:
|
||||
We may have both a 'select->quick' and 'save_quick' (original)
|
||||
at this point. Delete the one that we wan't use.
|
||||
*/
|
||||
|
||||
skipped_filesort:
|
||||
// Keep current (ordered) select->quick
|
||||
if (select && save_quick != select->quick)
|
||||
{
|
||||
delete save_quick;
|
||||
save_quick= NULL;
|
||||
}
|
||||
DBUG_RETURN(1);
|
||||
|
||||
use_filesort:
|
||||
// Restore original save_quick
|
||||
if (select && select->quick != save_quick)
|
||||
{
|
||||
delete select->quick;
|
||||
select->quick= save_quick;
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user