summaryrefslogtreecommitdiff
path: root/sql/sql_select.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_select.cc')
-rw-r--r--sql/sql_select.cc1845
1 files changed, 1197 insertions, 648 deletions
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 8644acefad8..f88a14c3312 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -29,11 +29,30 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "mysql_priv.h"
+#include "sql_priv.h"
+#include "unireg.h"
#include "sql_select.h"
-#include "sql_cursor.h"
+#include "sql_cache.h" // query_cache_*
+#include "sql_table.h" // primary_key_name
+#include "probes_mysql.h"
+#include "key.h" // key_copy, key_cmp, key_cmp_if_same
+#include "lock.h" // mysql_unlock_some_tables,
+ // mysql_unlock_read_tables
+#include "sql_show.h" // append_identifier
+#include "sql_base.h" // setup_wild, setup_fields, fill_record
+#include "sql_parse.h" // check_stack_overrun
+#include "sql_partition.h" // make_used_partitions_str
+#include "sql_acl.h" // *_ACL
+#include "sql_test.h" // print_where, print_keyuse_array,
+ // print_sjm, print_plan, TEST_join
+#include "records.h" // init_read_record, end_read_record
+#include "filesort.h" // filesort_free_buffers
+#include "sql_union.h" // mysql_union
#include "opt_subselect.h"
+#include "log_slow.h"
+#include "sql_derived.h"
+#include "debug_sync.h" // DEBUG_SYNC
#include <m_ctype.h>
#include <my_bit.h>
#include <hash.h>
@@ -76,9 +95,11 @@ static bool best_extension_by_limited_search(JOIN *join,
double read_time, uint depth,
uint prune_level);
static uint determine_search_depth(JOIN* join);
+C_MODE_START
static int join_tab_cmp(const void *dummy, const void* ptr1, const void* ptr2);
static int join_tab_cmp_straight(const void *dummy, const void* ptr1, const void* ptr2);
static int join_tab_cmp_embedded_first(const void *emb, const void* ptr1, const void *ptr2);
+C_MODE_END
/*
TODO: 'find_best' is here only temporarily until 'greedy_search' is
tested and approved.
@@ -126,32 +147,26 @@ 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,
+ Item::cond_result *cond_value,
COND_EQUAL **cond_equal);
-static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item);
+bool const_expression_in_where(COND *conds,Item *item, Item **comp_item);
static bool create_internal_tmp_table_from_heap2(THD *, TABLE *,
ENGINE_COLUMNDEF *, ENGINE_COLUMNDEF **,
int, bool, handlerton *, const char *);
static int do_select(JOIN *join,List<Item> *fields,TABLE *tmp_table,
Procedure *proc);
-static enum_nested_loop_state
-evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
- int error);
+static enum_nested_loop_state evaluate_join_record(JOIN *, JOIN_TAB *, int);
static enum_nested_loop_state
evaluate_null_complemented_join_record(JOIN *join, JOIN_TAB *join_tab);
static enum_nested_loop_state
end_send(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
-enum_nested_loop_state
-end_send_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
static enum_nested_loop_state
end_write(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
static enum_nested_loop_state
end_update(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
static enum_nested_loop_state
end_unique_update(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
-enum_nested_loop_state
-end_write_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
static int test_if_group_changed(List<Cached_item> &list);
static int join_read_const_table(JOIN_TAB *tab, POSITION *pos);
@@ -191,6 +206,14 @@ static COND *make_cond_for_table_from_pred(THD *thd, Item *root_cond,
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,
const key_map *map);
@@ -235,6 +258,7 @@ static void update_tmptable_sum_func(Item_sum **func,TABLE *tmp_table);
static void copy_sum_funcs(Item_sum **func_ptr, Item_sum **end);
static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab);
static bool setup_sum_funcs(THD *thd, Item_sum **func_ptr);
+static bool prepare_sum_aggregators(Item_sum **func_ptr, bool need_distinct);
static bool init_sum_functions(Item_sum **func, Item_sum **end);
static bool update_sum_func(Item_sum **func);
static void select_describe(JOIN *join, bool need_tmp_table,bool need_order,
@@ -258,6 +282,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
bool res;
register SELECT_LEX *select_lex = &lex->select_lex;
DBUG_ENTER("handle_select");
+ MYSQL_SELECT_START(thd->query());
if (select_lex->master_unit()->is_union() ||
select_lex->master_unit()->fake_select_lex)
@@ -281,7 +306,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
select_lex->group_list.first,
select_lex->having,
lex->proc_list.first,
- select_lex->options | thd->options |
+ select_lex->options | thd->variables.option_bits |
setup_tables_done_option,
result, unit, select_lex);
}
@@ -289,8 +314,24 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
thd->is_error()));
res|= thd->is_error();
if (unlikely(res))
- result->abort();
+ result->abort_result_set();
+ if (thd->killed == ABORT_QUERY)
+ {
+ /*
+ If LIMIT ROWS EXAMINED interrupted query execution, issue a warning,
+ continue with normal processing and produce an incomplete query result.
+ */
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT,
+ ER(ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT),
+ thd->accessed_rows_and_keys,
+ thd->lex->limit_rows_examined->val_uint());
+ thd->killed= NOT_KILLED;
+ }
+ /* Disable LIMIT ROWS EXAMINED after query execution. */
+ thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;
+ MYSQL_SELECT_DONE((int) res, (ulong) thd->limit_found_rows);
DBUG_RETURN(res);
}
@@ -327,7 +368,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
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.
+ grouping by a subquery that references this outer field.
The resolution is done here and not at the fix_fields() stage as
it can be done only after aggregate functions are fixed and pulled up to
@@ -603,7 +644,7 @@ JOIN::prepare(Item ***rref_pointer_array,
*/
if (select_lex->master_unit()->item && // 1)
select_lex->first_cond_optimization && // 2)
- !(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW)) // 3)
+ !thd->lex->is_view_context_analysis()) // 3)
{
remove_redundant_subquery_clauses(select_lex);
}
@@ -904,16 +945,19 @@ JOIN::optimize()
{
ulonglong select_opts_for_readinfo;
uint no_jbuf_after;
-
DBUG_ENTER("JOIN::optimize");
+
do_send_rows = (unit->select_limit_cnt) ? 1 : 0;
// to prevent double initialization on EXPLAIN
if (optimized)
DBUG_RETURN(0);
optimized= 1;
+ DEBUG_SYNC(thd, "before_join_optimize");
+
thd_proc_info(thd, "optimizing");
set_allowed_join_cache_types();
+ need_distinct= TRUE;
/* Run optimize phase for all derived tables/views used in this SELECT. */
if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE))
@@ -1189,7 +1233,7 @@ JOIN::optimize()
error= 0;
goto setup_subq_exit;
}
- if (!(thd->options & OPTION_BIG_SELECTS) &&
+ if (!(thd->variables.option_bits & OPTION_BIG_SELECTS) &&
best_read > (double) thd->variables.max_join_size &&
!(select_options & SELECT_DESCRIBE))
{ /* purecov: inspected */
@@ -1197,7 +1241,7 @@ JOIN::optimize()
error= -1;
DBUG_RETURN(1);
}
- if (const_tables && !thd->locked_tables &&
+ if (const_tables && !thd->locked_tables_mode &&
!(select_options & SELECT_NO_UNLOCK))
mysql_unlock_some_tables(thd, table, const_tables);
if (!conds && outer_join)
@@ -1315,6 +1359,9 @@ JOIN::optimize()
conds=new Item_int((longlong) 0,1); // Always false
}
+ /* Cache constant expressions in WHERE, HAVING, ON clauses. */
+ cache_const_exprs();
+
if (make_join_select(this, select, conds))
{
zero_result_cause=
@@ -1507,7 +1554,11 @@ JOIN::optimize()
if (test_if_subpart(group_list, order) ||
(!group_list && tmp_table_param.sum_func_count))
+ {
order=0;
+ if (is_indexed_agg_distinct(this, NULL))
+ sort_and_group= 0;
+ }
// Can't use sort on head table if using join buffering
if (full_join || hash_join)
@@ -1539,11 +1590,11 @@ JOIN::optimize()
*/
no_jbuf_after= 1 ? table_count : make_join_orderinfo(this);
+ // Don't use join buffering when we use MATCH
select_opts_for_readinfo=
(select_options & (SELECT_DESCRIBE | SELECT_NO_JOIN_CACHE)) |
(select_lex->ftfunc_list->elements ? SELECT_NO_JOIN_CACHE : 0);
- // No cache for MATCH == 'Don't use join buffering when we use MATCH'.
if (make_join_readinfo(this, select_opts_for_readinfo, no_jbuf_after))
DBUG_RETURN(1);
@@ -1656,7 +1707,14 @@ JOIN::optimize()
join_tab element of the plan for its access method.
*/
if (join_tab->is_using_loose_index_scan())
+ {
tmp_table_param.precomputed_group_by= TRUE;
+ if (join_tab->is_using_agg_loose_index_scan())
+ {
+ need_distinct= FALSE;
+ tmp_table_param.precomputed_group_by= FALSE;
+ }
+ }
error= 0;
@@ -1693,6 +1751,19 @@ int JOIN::init_execution()
DBUG_ASSERT(!(select_options & SELECT_DESCRIBE));
initialized= true;
+ /*
+ Enable LIMIT ROWS EXAMINED during query execution if:
+ (1) This JOIN is the outermost query (not a subquery or derived table)
+ This ensures that the limit is enabled when actual execution begins, and
+ not if a subquery is evaluated during optimization of the outer query.
+ (2) This JOIN is not the result of a UNION. In this case do not apply the
+ limit in order to produce the partial query result stored in the
+ UNION temp table.
+ */
+ if (!select_lex->outer_select() && // (1)
+ select_lex != select_lex->master_unit()->fake_select_lex) // (2)
+ thd->lex->set_limit_rows_examined();
+
/* Create a tmp table if distinct or if the sort is too complicated */
if (need_tmp)
{
@@ -1719,15 +1790,10 @@ int JOIN::init_execution()
if (!(exec_tmp_table1=
create_tmp_table(thd, &tmp_table_param, all_fields,
- tmp_group,
- group_list ? 0 : select_distinct,
+ tmp_group, group_list ? 0 : select_distinct,
group_list && simple_group,
- select_options,
- tmp_rows_limit,
- (char *) "")))
- {
+ select_options, tmp_rows_limit, "")))
DBUG_RETURN(1);
- }
/*
We don't have to store rows in temp table that doesn't match HAVING if:
@@ -1752,6 +1818,7 @@ int JOIN::init_execution()
HA_POS_ERROR, HA_POS_ERROR, FALSE) ||
alloc_group_fields(this, group_list) ||
make_sum_func_list(all_fields, fields_list, 1) ||
+ prepare_sum_aggregators(sum_funcs, need_distinct) ||
setup_sum_funcs(thd, sum_funcs))
{
DBUG_RETURN(1);
@@ -1761,6 +1828,7 @@ int JOIN::init_execution()
else
{
if (make_sum_func_list(all_fields, fields_list, 0) ||
+ prepare_sum_aggregators(sum_funcs, need_distinct) ||
setup_sum_funcs(thd, sum_funcs))
{
DBUG_RETURN(1);
@@ -2114,8 +2182,8 @@ JOIN::exec()
(zero_result_cause?zero_result_cause:"No tables used"));
else
{
- if (result->send_fields(*columns_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ if (result->send_result_set_metadata(*columns_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
{
DBUG_VOID_RETURN;
}
@@ -2422,8 +2490,7 @@ JOIN::exec()
curr_join->select_distinct &&
!curr_join->group_list,
1, curr_join->select_options,
- HA_POS_ERROR,
- (char *) "")))
+ HA_POS_ERROR, "")))
DBUG_VOID_RETURN;
curr_join->exec_tmp_table2= exec_tmp_table2;
}
@@ -2460,7 +2527,9 @@ JOIN::exec()
}
}
if (curr_join->make_sum_func_list(*curr_all_fields, *curr_fields_list,
- 1, TRUE))
+ 1, TRUE) ||
+ prepare_sum_aggregators(curr_join->sum_funcs,
+ !curr_join->join_tab->is_using_agg_loose_index_scan()))
DBUG_VOID_RETURN;
curr_join->group_list= 0;
if (!curr_join->sort_and_group &&
@@ -2576,13 +2645,17 @@ JOIN::exec()
if (curr_join->make_sum_func_list(*curr_all_fields, *curr_fields_list,
1, TRUE) ||
+ prepare_sum_aggregators(curr_join->sum_funcs,
+ !curr_join->join_tab ||
+ !curr_join->join_tab->
+ is_using_agg_loose_index_scan()) ||
setup_sum_funcs(curr_join->thd, curr_join->sum_funcs) ||
thd->is_fatal_error)
DBUG_VOID_RETURN;
}
if (curr_join->group_list || curr_join->order)
{
- DBUG_PRINT("info",("Sorting for send_fields"));
+ DBUG_PRINT("info",("Sorting for send_result_set_metadata"));
thd_proc_info(thd, "Sorting result");
/* If we have already done the group, add HAVING to sorted table */
if (curr_join->tmp_having && ! curr_join->group_list &&
@@ -2713,35 +2786,13 @@ JOIN::exec()
curr_join->fields= curr_fields_list;
curr_join->procedure= procedure;
- if (is_top_level_join() && thd->cursor && table_count != const_tables)
- {
- /*
- We are here if this is JOIN::exec for the last select of the main unit
- and the client requested to open a cursor.
- We check that not all tables are constant because this case is not
- handled by do_select() separately, and this case is not implemented
- for cursors yet.
- */
- DBUG_ASSERT(error == 0);
- /*
- curr_join is used only for reusable joins - that is,
- to perform SELECT for each outer row (like in subselects).
- This join is main, so we know for sure that curr_join == join.
- */
- DBUG_ASSERT(curr_join == this);
- /* Open cursor for the last join sweep */
- error= thd->cursor->open(this);
- }
- else
- {
- thd_proc_info(thd, "Sending data");
- DBUG_PRINT("info", ("%s", thd->proc_info));
- result->send_fields((procedure ? curr_join->procedure_fields_list :
- *curr_fields_list),
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
- error= do_select(curr_join, curr_fields_list, NULL, procedure);
- thd->limit_found_rows= curr_join->send_records;
- }
+ thd_proc_info(thd, "Sending data");
+ DBUG_PRINT("info", ("%s", thd->proc_info));
+ result->send_result_set_metadata((procedure ? curr_join->procedure_fields_list :
+ *curr_fields_list),
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
+ error= do_select(curr_join, curr_fields_list, NULL, procedure);
+ thd->limit_found_rows= curr_join->send_records;
/* Accumulate the counts from all join iterations of all join parts. */
thd->examined_row_count+= curr_join->examined_rows;
@@ -2962,16 +3013,6 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
join->exec();
- if (thd->cursor && thd->cursor->is_open())
- {
- /*
- A cursor was opened for the last sweep in exec().
- We are here only if this is mysql_select for top-level SELECT_LEX_UNIT
- and there were no error.
- */
- free_join= 0;
- }
-
if (thd->lex->describe & DESCRIBE_EXTENDED)
{
select_lex->where= join->conds_history;
@@ -3000,9 +3041,7 @@ static ha_rows get_quick_record_count(THD *thd, SQL_SELECT *select,
{
int error;
DBUG_ENTER("get_quick_record_count");
-#ifndef EMBEDDED_LIBRARY // Avoid compiler warning
uchar buff[STACK_BUFF_ALLOC];
-#endif
if (check_stack_overrun(thd, STACK_MIN_SIZE, buff))
DBUG_RETURN(0); // Fatal error flag is set
if (select)
@@ -3300,7 +3339,10 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
goto error; // Fatal error
}
else
+ {
found_const_table_map|= s->table->map;
+ s->table->pos_in_table_list->optimized_away= TRUE;
+ }
}
}
@@ -3427,11 +3469,13 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
The effect of this is that we don't do const substitution for
such tables.
*/
- if (eq_part.is_prefix(table->key_info[key].key_parts) &&
+ KEY *keyinfo= table->key_info + key;
+ uint key_parts= table->actual_n_key_parts(keyinfo);
+ if (eq_part.is_prefix(key_parts) &&
!table->fulltext_searched &&
(!embedding || (embedding->sj_on_expr && !embedding->embedding)))
{
- if (table->key_info[key].flags & HA_NOSAME)
+ if (table->actual_key_flags(keyinfo) & HA_NOSAME)
{
if (const_ref == eq_part &&
!has_expensive_keyparts &&
@@ -4001,14 +4045,14 @@ add_key_field(JOIN *join,
(*sargables)->arg_value= value;
(*sargables)->num_values= num_values;
}
+ if (!eq_func) // eq_func is NEVER true when num_values > 1
+ return;
/*
We can't use indexes when comparing a string index to a
number or two strings if the effective collation
of the operation differ from the field collation.
*/
- if (!eq_func)
- return;
if (field->cmp_type() == STRING_RESULT)
{
@@ -4465,7 +4509,8 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field)
if (form->key_info[key].flags & (HA_FULLTEXT | HA_SPATIAL))
continue; // ToDo: ft-keys in non-ft queries. SerG
- uint key_parts= (uint) form->key_info[key].key_parts;
+ KEY *keyinfo= form->key_info+key;
+ uint key_parts= form->actual_n_key_parts(keyinfo);
for (uint part=0 ; part < key_parts ; part++)
{
if (field->eq(form->key_info[key].key_part[part].field))
@@ -4512,19 +4557,19 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array,
cond_func=(Item_func_match *)cond;
else if (func->arg_count == 2)
{
- Item *arg0= func->arguments()[0],
- *arg1= func->arguments()[1];
+ Item *arg0=(Item *)(func->arguments()[0]),
+ *arg1=(Item *)(func->arguments()[1]);
if (arg1->const_item() && arg1->cols() == 1 &&
arg0->type() == Item::FUNC_ITEM &&
((Item_func *) arg0)->functype() == Item_func::FT_FUNC &&
((functype == Item_func::GE_FUNC && arg1->val_real() > 0) ||
- (functype == Item_func::GT_FUNC && arg1->val_real() >= 0)))
+ (functype == Item_func::GT_FUNC && arg1->val_real() >=0)))
cond_func= (Item_func_match *) arg0;
else if (arg0->const_item() && arg0->cols() == 1 &&
arg1->type() == Item::FUNC_ITEM &&
((Item_func *) arg1)->functype() == Item_func::FT_FUNC &&
((functype == Item_func::LE_FUNC && arg0->val_real() > 0) ||
- (functype == Item_func::LT_FUNC && arg0->val_real() >= 0)))
+ (functype == Item_func::LT_FUNC && arg0->val_real() >=0)))
cond_func= (Item_func_match *) arg1;
}
}
@@ -4850,7 +4895,7 @@ static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse,
save_pos++;
}
i= (uint) (save_pos-(KEYUSE*) keyuse->buffer);
- VOID(set_dynamic(keyuse,(uchar*) &key_end,i));
+ (void) set_dynamic(keyuse,(uchar*) &key_end,i);
keyuse->elements= i;
return FALSE;
@@ -4903,6 +4948,82 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array)
/**
+ Check for the presence of AGGFN(DISTINCT a) queries that may be subject
+ to loose index scan.
+
+
+ Check if the query is a subject to AGGFN(DISTINCT) using loose index scan
+ (QUICK_GROUP_MIN_MAX_SELECT).
+ Optionally (if out_args is supplied) will push the arguments of
+ AGGFN(DISTINCT) to the list
+
+ @param join the join to check
+ @param[out] out_args list of aggregate function arguments
+ @return does the query qualify for indexed AGGFN(DISTINCT)
+ @retval true it does
+ @retval false AGGFN(DISTINCT) must apply distinct in it.
+*/
+
+bool
+is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args)
+{
+ Item_sum **sum_item_ptr;
+ bool result= false;
+
+ if (join->table_count != 1 || /* reference more than 1 table */
+ join->select_distinct || /* or a DISTINCT */
+ join->select_lex->olap == ROLLUP_TYPE) /* Check (B3) for ROLLUP */
+ return false;
+
+ if (join->make_sum_func_list(join->all_fields, join->fields_list, true))
+ return false;
+
+ for (sum_item_ptr= join->sum_funcs; *sum_item_ptr; sum_item_ptr++)
+ {
+ Item_sum *sum_item= *sum_item_ptr;
+ Item *expr;
+ /* aggregate is not AGGFN(DISTINCT) or more than 1 argument to it */
+ switch (sum_item->sum_func())
+ {
+ case Item_sum::MIN_FUNC:
+ case Item_sum::MAX_FUNC:
+ continue;
+ case Item_sum::COUNT_DISTINCT_FUNC:
+ break;
+ case Item_sum::AVG_DISTINCT_FUNC:
+ case Item_sum::SUM_DISTINCT_FUNC:
+ if (sum_item->get_arg_count() == 1)
+ break;
+ /* fall through */
+ default: return false;
+ }
+ /*
+ We arrive here for every COUNT(DISTINCT),AVG(DISTINCT) or SUM(DISTINCT).
+ Collect the arguments of the aggregate functions to a list.
+ We don't worry about duplicates as these will be sorted out later in
+ get_best_group_min_max
+ */
+ for (uint i= 0; i < sum_item->get_arg_count(); i++)
+ {
+ expr= sum_item->get_arg(i);
+ /* The AGGFN(DISTINCT) arg is not an attribute? */
+ if (expr->real_item()->type() != Item::FIELD_ITEM)
+ return false;
+
+ /*
+ If we came to this point the AGGFN(DISTINCT) loose index scan
+ optimization is applicable
+ */
+ if (out_args)
+ out_args->push_back((Item_field *) expr->real_item());
+ result= true;
+ }
+ }
+ return result;
+}
+
+
+/**
Discover the indexes that can be used for GROUP BY or DISTINCT queries.
If the query has a GROUP BY clause, find all indexes that contain all
@@ -4944,6 +5065,10 @@ add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab)
item->walk(&Item::collect_item_field_processor, 0,
(uchar*) &indexed_fields);
}
+ else if (is_indexed_agg_distinct(join, &indexed_fields))
+ {
+ join->sort_and_group= 1;
+ }
else
return;
@@ -5097,6 +5222,8 @@ best_access_path(JOIN *join,
for (keyuse=s->keyuse ; keyuse->table == table ;)
{
KEY *keyinfo;
+ ulong key_flags;
+ uint key_parts;
key_part_map found_part= 0;
table_map found_ref= 0;
uint key= keyuse->key;
@@ -5123,6 +5250,8 @@ best_access_path(JOIN *join,
}
keyinfo= table->key_info+key;
+ key_parts= table->actual_n_key_parts(keyinfo);
+ key_flags= table->actual_key_flags(keyinfo);
/* Calculate how many key segments of the current key we can use */
start_key= keyuse;
@@ -5201,11 +5330,11 @@ best_access_path(JOIN *join,
loose_scan_opt.check_ref_access_part1(s, key, start_key, found_part);
/* Check if we found full key */
- if (found_part == PREV_BITS(uint,keyinfo->key_parts) &&
+ if (found_part == PREV_BITS(uint, key_parts) &&
!ref_or_null_part)
{ /* use eq key */
max_key_part= (uint) ~0;
- if ((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)
+ if ((key_flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)
{
tmp = prev_record_reads(join->positions, idx, found_ref);
records=1.0;
@@ -5241,7 +5370,8 @@ best_access_path(JOIN *join,
}
else
{
- if (!(records=keyinfo->rec_per_key[keyinfo->key_parts-1]))
+ uint key_parts= table->actual_n_key_parts(keyinfo);
+ if (!(records=keyinfo->rec_per_key[key_parts-1]))
{ /* Prefer longer keys */
records=
((double) s->records / (double) rec *
@@ -5448,7 +5578,6 @@ best_access_path(JOIN *join,
tmp= best_time; // Do nothing
}
- DBUG_ASSERT(tmp > 0 || record_count == 0);
tmp += s->startup_cost;
loose_scan_opt.check_ref_access_part2(key, start_key, records, tmp);
} /* not ft_key */
@@ -6297,7 +6426,7 @@ greedy_search(JOIN *join,
on exit.
*/
bool is_interleave_error __attribute__((unused))=
- check_interleaving_with_nj(best_table);
+ check_interleaving_with_nj (best_table);
/* This has been already checked by best_extension_by_limited_search */
DBUG_ASSERT(!is_interleave_error);
@@ -7512,6 +7641,7 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab,
key_parts)))
DBUG_RETURN(TRUE);
keyinfo->usable_key_parts= keyinfo->key_parts = key_parts;
+ keyinfo->ext_key_parts= keyinfo->key_parts;
keyinfo->key_part= key_part_info;
keyinfo->key_length=0;
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
@@ -7556,6 +7686,10 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab,
keyuse++;
} while (keyuse->table == table && keyuse->is_for_hash_join());
+ keyinfo->ext_key_parts= keyinfo->key_parts;
+ keyinfo->ext_key_flags= keyinfo->flags;
+ keyinfo->ext_key_part_map= 0;
+
join_tab->hj_key= keyinfo;
DBUG_RETURN(FALSE);
@@ -7750,8 +7884,11 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
DBUG_RETURN(0);
if (j->type == JT_CONST)
j->table->const_table= 1;
- else if (((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) != HA_NOSAME) ||
- keyparts != keyinfo->key_parts || null_ref_key)
+ else if (((j->table->actual_key_flags(keyinfo) &
+ (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;
@@ -8508,7 +8645,11 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
if (tab->table)
{
tab->table->file->pushed_cond= NULL;
- if (thd->variables.engine_condition_pushdown && !first_inner_tab)
+ if (((thd->variables.optimizer_switch &
+ OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN) ||
+ (tab->table->file->ha_table_flags() &
+ HA_MUST_USE_TABLE_CONDITION_PUSHDOWN)) &&
+ !first_inner_tab)
{
COND *push_cond=
make_cond_for_table(thd, tmp, current_map, current_map,
@@ -8538,11 +8679,12 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
{
/* Use quick key read if it's a constant and it's not used
with key reading */
- if ((tab->needed_reg.is_clear_all() && tab->type != JT_EQ_REF
- && tab->type != JT_FT &&
- ((tab->type != JT_REF && tab->type != JT_CONST) ||
- (uint) tab->ref.key == tab->quick->index)) || is_hj)
- {
+ if ((tab->needed_reg.is_clear_all() && tab->type != JT_EQ_REF &&
+ tab->type != JT_FT &&
+ ((tab->type != JT_CONST && tab->type != JT_REF) ||
+ (uint) tab->ref.key == tab->quick->index)) || is_hj)
+ {
+ DBUG_ASSERT(tab->quick->is_valid());
sel->quick=tab->quick; // Use value from get_quick_...
sel->quick_keys.clear_all();
sel->needed_reg.clear_all();
@@ -9986,12 +10128,14 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
{
join->need_tmp= 1;
join->simple_order= join->simple_group= 0;
- if (sort_by_tab->type == JT_NEXT)
+ if (sort_by_tab->type == JT_NEXT &&
+ !sort_by_tab->table->covering_keys.is_set(sort_by_tab->index))
{
sort_by_tab->type= JT_ALL;
sort_by_tab->read_first_record= join_init_read_record;
}
- else if (sort_by_tab->type == JT_HASH_NEXT)
+ else if (sort_by_tab->type == JT_HASH_NEXT &&
+ !sort_by_tab->table->covering_keys.is_set(sort_by_tab->index))
{
sort_by_tab->type= JT_HASH;
sort_by_tab->read_first_record= join_init_read_record;
@@ -10327,7 +10471,7 @@ void JOIN::join_free()
We are not using tables anymore
Unlock all tables. We may be in an INSERT .... SELECT statement.
*/
- if (can_unlock && lock && thd->lock &&
+ if (can_unlock && lock && thd->lock && ! thd->locked_tables_mode &&
!(select_options & SELECT_NO_UNLOCK) &&
!select_lex->subquery_in_having &&
(select_lex == (thd->lex->unit.fake_select_lex ?
@@ -10371,18 +10515,19 @@ void JOIN::cleanup(bool full)
*/
if (table_count > const_tables) // Test for not-const tables
{
- free_io_cache(table[const_tables]);
- filesort_free_buffers(table[const_tables],full);
+ JOIN_TAB *first_tab= first_top_level_tab(this, WITHOUT_CONST_TABLES);
+ if (first_tab->table)
+ {
+ free_io_cache(first_tab->table);
+ filesort_free_buffers(first_tab->table, full);
+ }
}
if (full)
{
for (tab= first_linear_tab(this, WITH_CONST_TABLES); tab;
tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
- {
tab->cleanup();
- }
- table= 0;
}
else
{
@@ -10671,8 +10816,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;
@@ -10702,6 +10846,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, List<TABLE_LIST> &tables,
List<Item> &fields, bool send_row, ulonglong select_options,
@@ -10746,7 +10930,7 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables,
if (having && having->val_int() == 0)
send_row=0;
}
- if (!(result->send_fields(fields,
+ if (!(result->send_result_set_metadata(fields,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)))
{
bool send_error= FALSE;
@@ -11064,7 +11248,7 @@ static bool check_simple_equality(Item *left_item, Item *right_item,
{
bool copyfl;
- if (field_item->result_type() == STRING_RESULT)
+ if (field_item->cmp_type() == STRING_RESULT)
{
CHARSET_INFO *cs= ((Field_str*) field_item->field)->charset();
if (!item)
@@ -11515,8 +11699,7 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
pointer to the transformed condition containing multiple equalities
*/
-static COND *build_equal_items(THD *thd, COND *cond,
- COND_EQUAL *inherited,
+static COND *build_equal_items(THD *thd, COND *cond, COND_EQUAL *inherited,
List<TABLE_LIST> *join_list,
COND_EQUAL **cond_equal_ref)
{
@@ -12141,7 +12324,6 @@ 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();
-
if (tmp)
{
tmp->collation.set(right_item->collation);
@@ -12165,7 +12347,6 @@ 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();
-
if (tmp)
{
tmp->collation.set(left_item->collation);
@@ -13014,18 +13195,26 @@ optimize_cond(JOIN *join, COND *conds, List<TABLE_LIST> *join_list,
/**
- Remove const and eq items.
+ Handles the recursive job remove_eq_conds()
- @return
- Return new item, or NULL if no condition @n
- cond_value is set to according:
- - COND_OK : query is possible (field = constant)
- - COND_TRUE : always true ( 1 = 1 )
- - COND_FALSE : always false ( 1 = 2 )
+ Remove const and eq items. Return new item, or NULL if no condition
+ cond_value is set to according:
+ COND_OK query is possible (field = constant)
+ COND_TRUE always true ( 1 = 1 )
+ COND_FALSE always false ( 1 = 2 )
+
+ SYNOPSIS
+ internal_remove_eq_conds()
+ thd THD environment
+ cond the condition to handle
+ cond_value the resulting value of the condition
+
+ RETURN
+ *COND with the simplified condition
*/
-COND *
-remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
+static COND *
+internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
{
if (cond->type() == Item::COND_ITEM)
{
@@ -13039,12 +13228,12 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
Item *item;
while ((item=li++))
{
- Item *new_item=remove_eq_conds(thd, item, &tmp_cond_value);
+ Item *new_item=internal_remove_eq_conds(thd, item, &tmp_cond_value);
if (!new_item)
li.remove();
else if (item != new_item)
{
- VOID(li.replace(new_item));
+ (void) li.replace(new_item);
should_fix_fields=1;
}
if (*cond_value == Item::COND_UNDEF)
@@ -13088,6 +13277,102 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
else if (cond->type() == Item::FUNC_ITEM &&
((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC)
{
+ Item_func_isnull *func=(Item_func_isnull*) cond;
+ Item **args= func->arguments();
+ if (args[0]->type() == Item::FIELD_ITEM)
+ {
+ Field *field=((Item_field*) args[0])->field;
+ /* fix to replace 'NULL' dates with '0' (shreeve@uci.edu) */
+ /*
+ See BUG#12594011
+ Documentation says that
+ SELECT datetime_notnull d FROM t1 WHERE d IS NULL
+ shall return rows where d=='0000-00-00'
+
+ Thus, for DATE and DATETIME columns defined as NOT NULL,
+ "date_notnull IS NULL" has to be modified to
+ "date_notnull IS NULL OR date_notnull == 0" (if outer join)
+ "date_notnull == 0" (otherwise)
+
+ */
+ if (((field->type() == MYSQL_TYPE_DATE) ||
+ (field->type() == MYSQL_TYPE_DATETIME)) &&
+ (field->flags & NOT_NULL_FLAG))
+ {
+ Item *item0= new(thd->mem_root) Item_int((longlong)0, 1);
+ Item *eq_cond= new(thd->mem_root) Item_func_eq(args[0], item0);
+ if (!eq_cond)
+ return cond;
+
+ if (field->table->pos_in_table_list->outer_join)
+ {
+ // outer join: transform "col IS NULL" to "col IS NULL or col=0"
+ Item *or_cond= new(thd->mem_root) Item_cond_or(eq_cond, cond);
+ if (!or_cond)
+ return cond;
+ cond= or_cond;
+ }
+ else
+ {
+ // not outer join: transform "col IS NULL" to "col=0"
+ cond= eq_cond;
+ }
+
+ cond->fix_fields(thd, &cond);
+ }
+ }
+ if (cond->const_item() && !cond->is_expensive())
+ {
+ *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
+ return (COND*) 0;
+ }
+ }
+ else if (cond->const_item() && !cond->is_expensive())
+ {
+ *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
+ return (COND*) 0;
+ }
+ else if ((*cond_value= cond->eq_cmp_result()) != Item::COND_OK)
+ { // boolan compare function
+ Item *left_item= ((Item_func*) cond)->arguments()[0];
+ Item *right_item= ((Item_func*) cond)->arguments()[1];
+ if (left_item->eq(right_item,1))
+ {
+ if (!left_item->maybe_null ||
+ ((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC)
+ return (COND*) 0; // Compare of identical items
+ }
+ }
+ *cond_value=Item::COND_OK;
+ return cond; // Point at next and level
+}
+
+/**
+ Remove const and eq items. Return new item, or NULL if no condition
+ cond_value is set to according:
+ COND_OK query is possible (field = constant)
+ COND_TRUE always true ( 1 = 1 )
+ COND_FALSE always false ( 1 = 2 )
+
+ SYNPOSIS
+ remove_eq_conds()
+ thd THD environment
+ cond the condition to handle
+ cond_value the resulting value of the condition
+
+ NOTES
+ calls the inner_remove_eq_conds to check all the tree reqursively
+
+ RETURN
+ *COND with the simplified condition
+*/
+
+COND *
+remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
+{
+ if (cond->type() == Item::FUNC_ITEM &&
+ ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC)
+ {
/*
Handles this special case for some ODBC applications:
The are requesting the row that was just updated with a auto_increment
@@ -13104,12 +13389,12 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
{
Field *field=((Item_field*) args[0])->field;
if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null &&
- (thd->options & OPTION_AUTO_IS_NULL) &&
+ (thd->variables.option_bits & OPTION_AUTO_IS_NULL) &&
(thd->first_successful_insert_id_in_prev_stmt > 0 &&
thd->substitute_null_with_insert_id))
{
#ifdef HAVE_QUERY_CACHE
- query_cache_abort(&thd->net);
+ query_cache_abort(&thd->query_cache_tls);
#endif
COND *new_cond;
if ((new_cond= new Item_func_eq(args[0],
@@ -13130,53 +13415,17 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
clear for next row
*/
thd->substitute_null_with_insert_id= FALSE;
- }
- /* fix to replace 'NULL' dates with '0' (shreeve@uci.edu) */
- else if (((field->type() == MYSQL_TYPE_DATE) ||
- (field->type() == MYSQL_TYPE_DATETIME)) &&
- (field->flags & NOT_NULL_FLAG) &&
- !field->table->maybe_null)
- {
- COND *new_cond;
- if ((new_cond= new Item_func_eq(args[0],new Item_int("0", 0, 2))))
- {
- cond=new_cond;
- /*
- Item_func_eq can't be fixed after creation so we do not check
- cond->fixed, also it do not need tables so we use 0 as second
- argument.
- */
- cond->fix_fields(thd, &cond);
- }
+
+ *cond_value= Item::COND_OK;
+ return cond;
}
}
- if (cond->const_item() && !cond->is_expensive())
- {
- *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
- return (COND*) 0;
- }
}
- else if (cond->const_item() && !cond->is_expensive())
- {
- *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
- return (COND*) 0;
- }
- else if ((*cond_value= cond->eq_cmp_result()) != Item::COND_OK)
- { // boolan compare function
- Item *left_item= ((Item_func*) cond)->arguments()[0];
- Item *right_item= ((Item_func*) cond)->arguments()[1];
- if (left_item->eq(right_item,1))
- {
- if (!left_item->maybe_null ||
- ((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC)
- return (COND*) 0; // Compare of identical items
- }
- }
- *cond_value=Item::COND_OK;
- return cond; // Point at next and level
+ return internal_remove_eq_conds(thd, cond, cond_value); // Scan all the condition
}
-/**
+
+/*
Check if equality can be used in removing components of GROUP BY/DISTINCT
@param l the left comparison argument (a field if any)
@@ -13208,13 +13457,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()
@@ -13223,7 +13509,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)
@@ -13235,14 +13522,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))
{
@@ -13252,7 +13539,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))
{
@@ -13266,6 +13553,7 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
return 0;
}
+
/****************************************************************************
Create internal temporary table
****************************************************************************/
@@ -13519,7 +13807,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
Item_sum *item_sum=(Item_sum*) item;
result= item_sum->create_tmp_field(group, table, convert_blob_length);
if (!result)
- thd->fatal_error();
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
return result;
}
case Item::FIELD_ITEM:
@@ -13684,7 +13972,7 @@ void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps)
Create a temp table according to a field list.
Given field pointers are changed to point at tmp_table for
- send_fields. The table object is self contained: it's
+ send_result_set_metadata. The table object is self contained: it's
allocated in its own memory root, as well as Field objects
created for table columns.
This function will replace Item_sum items in 'fields' list with
@@ -13710,7 +13998,7 @@ TABLE *
create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
ORDER *group, bool distinct, bool save_sum_fields,
ulonglong select_options, ha_rows rows_limit,
- char *table_alias, bool do_not_open)
+ const char *table_alias, bool do_not_open)
{
MEM_ROOT *mem_root_save, own_root;
TABLE *table;
@@ -13876,6 +14164,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
share->primary_key= MAX_KEY; // Indicate no primary key
share->keys_for_keyread.init();
share->keys_in_use.init();
+ if (param->schema_table)
+ share->db= INFORMATION_SCHEMA_NAME;
/* Calculate which type of fields we will store in the temporary table */
@@ -14088,10 +14378,10 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
/* If result table is small; use a heap */
/* future: storage engine selection can be made dynamic? */
- if (blob_count || using_unique_constraint ||
- (select_options & (OPTION_BIG_TABLES | SELECT_SMALL_RESULT)) ==
- OPTION_BIG_TABLES || (select_options & TMP_TABLE_FORCE_MYISAM) ||
- !thd->variables.tmp_table_size)
+ if (blob_count || using_unique_constraint
+ || (thd->variables.big_tables && !(select_options & SELECT_SMALL_RESULT))
+ || (select_options & TMP_TABLE_FORCE_MYISAM)
+ || thd->variables.tmp_table_size == 0)
{
share->db_plugin= ha_lock_engine(0, TMP_ENGINE_HTON);
table->file= get_new_handler(share, &table->mem_root,
@@ -14297,7 +14587,9 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
share->keys_in_use.set_bit(0);
keyinfo->key_part=key_part_info;
keyinfo->flags=HA_NOSAME | HA_BINARY_PACK_KEY | HA_PACK_KEY;
+ keyinfo->ext_key_flags= keyinfo->flags;
keyinfo->usable_key_parts=keyinfo->key_parts= param->group_parts;
+ keyinfo->ext_key_parts= keyinfo->key_parts;
keyinfo->key_length=0;
keyinfo->rec_per_key=0;
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
@@ -14321,7 +14613,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
(ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT1 ||
(ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT2) ?
0 : FIELDFLAG_BINARY;
-
+ key_part_info->key_part_flag= 0;
if (!using_unique_constraint)
{
cur_group->buff=(char*) group_buff;
@@ -14396,6 +14688,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
null_pack_length-=hidden_null_pack_length;
keyinfo->key_parts= ((field_count-param->hidden_field_count)+
(share->uniques ? test(null_pack_length) : 0));
+ keyinfo->ext_key_parts= keyinfo->key_parts;
table->distinct= 1;
share->keys= 1;
if (!(key_part_info= (KEY_PART_INFO*)
@@ -14408,6 +14701,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
table->key_info= table->s->key_info= keyinfo;
keyinfo->key_part=key_part_info;
keyinfo->flags=HA_NOSAME | HA_NULL_ARE_EQUAL | HA_BINARY_PACK_KEY | HA_PACK_KEY;
+ keyinfo->ext_key_flags= keyinfo->flags;
keyinfo->key_length= 0; // Will compute the sum of the parts below.
keyinfo->name= (char*) "distinct_key";
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
@@ -14503,7 +14797,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
if (share->db_type() == TMP_ENGINE_HTON)
{
if (create_internal_tmp_table(table, param->keyinfo, param->start_recinfo,
- &param->recinfo, select_options))
+ &param->recinfo, select_options,
+ thd->variables.big_tables))
goto err;
}
if (open_tmp_table(table))
@@ -14575,6 +14870,7 @@ TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list)
bzero(share, sizeof(*share));
table->field= field;
table->s= share;
+ table->temp_pool_slot= MY_BIT_NONE;
share->blob_field= blob_field;
share->fields= field_count;
share->blob_ptr_size= portable_sizeof_char_ptr;
@@ -14642,6 +14938,18 @@ TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list)
null_bit= 1;
}
}
+ if (cur_field->type() == MYSQL_TYPE_BIT &&
+ cur_field->key_type() == HA_KEYTYPE_BIT)
+ {
+ /* This is a Field_bit since key_type is HA_KEYTYPE_BIT */
+ static_cast<Field_bit*>(cur_field)->set_bit_ptr(null_pos, null_bit);
+ null_bit+= cur_field->field_length & 7;
+ if (null_bit > 7)
+ {
+ null_pos++;
+ null_bit-= 8;
+ }
+ }
cur_field->reset();
field_pos+= cur_field->pack_length();
@@ -14673,7 +14981,7 @@ bool open_tmp_table(TABLE *table)
}
-#if defined(WITH_ARIA_STORAGE_ENGINE) && defined(USE_MARIA_FOR_TMP_TABLES)
+#if defined(WITH_ARIA_STORAGE_ENGINE) && defined(USE_ARIA_FOR_TMP_TABLES)
/*
Create internal (MyISAM or Maria) temporary table
@@ -14709,7 +15017,7 @@ bool open_tmp_table(TABLE *table)
bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
ENGINE_COLUMNDEF *start_recinfo,
ENGINE_COLUMNDEF **recinfo,
- ulonglong options)
+ ulonglong options, my_bool big_tables)
{
int error;
MARIA_KEYDEF keydef;
@@ -14802,8 +15110,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
}
bzero((char*) &create_info,sizeof(create_info));
- if ((options & (OPTION_BIG_TABLES | SELECT_SMALL_RESULT)) ==
- OPTION_BIG_TABLES)
+ if (big_tables && !(options & SELECT_SMALL_RESULT))
create_info.data_file_length= ~(ulonglong) 0;
/*
@@ -14893,7 +15200,7 @@ bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
ENGINE_COLUMNDEF *start_recinfo,
ENGINE_COLUMNDEF **recinfo,
- ulonglong options)
+ ulonglong options, my_bool big_tables)
{
int error;
MI_KEYDEF keydef;
@@ -14980,8 +15287,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
MI_CREATE_INFO create_info;
bzero((char*) &create_info,sizeof(create_info));
- if ((options & (OPTION_BIG_TABLES | SELECT_SMALL_RESULT)) ==
- OPTION_BIG_TABLES)
+ if (big_tables && !(options & SELECT_SMALL_RESULT))
create_info.data_file_length= ~(ulonglong) 0;
if ((error=mi_create(share->table_name.str, share->keys, &keydef,
@@ -15053,8 +15359,7 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
We don't want this error to be converted to a warning, e.g. in case of
INSERT IGNORE ... SELECT.
*/
- thd->fatal_error();
- table->file->print_error(error,MYF(0));
+ table->file->print_error(error, MYF(ME_FATALERROR));
DBUG_RETURN(1);
}
new_table= *table;
@@ -15072,7 +15377,8 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
if (create_internal_tmp_table(&new_table, table->key_info, start_recinfo,
recinfo,
thd->lex->select_lex.options |
- thd->options))
+ thd->variables.option_bits,
+ thd->variables.big_tables))
goto err2;
if (open_tmp_table(&new_table))
goto err1;
@@ -15165,6 +15471,7 @@ free_tmp_table(THD *thd, TABLE *entry)
if (entry->file && entry->created)
{
+ entry->file->ha_index_or_rnd_end();
if (entry->db_stat)
entry->file->ha_drop_table(entry->s->table_name.str);
else
@@ -15181,6 +15488,7 @@ free_tmp_table(THD *thd, TABLE *entry)
bitmap_lock_clear_bit(&temp_pool, entry->temp_pool_slot);
plugin_unlock(0, entry->s->db_plugin);
+ entry->alias.free();
free_root(&own_root, MYF(0)); /* the table is allocated in its own root */
thd_proc_info(thd, save_proc_info);
@@ -15293,7 +15601,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
if (table)
{
- VOID(table->file->extra(HA_EXTRA_WRITE_CACHE));
+ (void) table->file->extra(HA_EXTRA_WRITE_CACHE);
empty_record(table);
if (table->group && join->tmp_table_param.sum_func_count &&
table->s->keys && !table->file->inited)
@@ -15337,7 +15645,6 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
so we don't touch it here.
*/
join->examined_rows++;
- join->thd->row_count++;
DBUG_ASSERT(join->examined_rows <= 1);
}
else if (join->send_row_on_empty_set())
@@ -15349,6 +15656,13 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
rc= join->result->send_data(*columns_list) > 0;
}
}
+ /*
+ An error can happen when evaluating the conds
+ (the join condition and piece of where clause
+ relevant to this join table).
+ */
+ if (join->thd->is_error())
+ error= NESTED_LOOP_ERROR;
}
else
{
@@ -15357,34 +15671,15 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
error= NESTED_LOOP_NO_MORE_ROWS;
else
error= sub_select(join,join_tab,0);
- if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS)
+ if ((error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS) &&
+ join->thd->killed != ABORT_QUERY)
error= sub_select(join,join_tab,1);
if (error == NESTED_LOOP_QUERY_LIMIT)
error= NESTED_LOOP_OK; /* select_limit used */
}
- if (error == NESTED_LOOP_NO_MORE_ROWS)
+ if (error == NESTED_LOOP_NO_MORE_ROWS || join->thd->killed == ABORT_QUERY)
error= NESTED_LOOP_OK;
- if (table == NULL) // If sending data to client
- {
- /*
- The following will unlock all cursors if the command wasn't an
- update command
- */
- join->join_free(); // Unlock all cursors
- }
- if (error == NESTED_LOOP_OK)
- {
- /*
- Sic: this branch works even if rc != 0, e.g. when
- send_data above returns an error.
- */
- if (table == NULL && join->result->send_eof()) // If sending data to client
- rc= 1; // Don't send error
- DBUG_PRINT("info",("%ld records output", (long) join->send_records));
- }
- else
- rc= -1;
if (table)
{
int tmp, new_errno= 0;
@@ -15401,6 +15696,29 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
if (new_errno)
table->file->print_error(new_errno,MYF(0));
}
+ else
+ {
+ /*
+ The following will unlock all cursors if the command wasn't an
+ update command
+ */
+ join->join_free(); // Unlock all cursors
+ }
+ if (error == NESTED_LOOP_OK)
+ {
+ /*
+ Sic: this branch works even if rc != 0, e.g. when
+ send_data above returns an error.
+ */
+ if (!table) // If sending data to client
+ {
+ if (join->result->send_eof())
+ rc= 1; // Don't send error
+ }
+ DBUG_PRINT("info",("%ld records output", (long) join->send_records));
+ }
+ else
+ rc= -1;
#ifndef DBUG_OFF
if (rc)
{
@@ -15653,52 +15971,38 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
if (!join_tab->preread_init_done && join_tab->preread_init())
DBUG_RETURN(NESTED_LOOP_ERROR);
- if (join->resume_nested_loop)
- {
- /* If not the last table, plunge down the nested loop */
- if (join_tab < join->join_tab + join->top_join_tab_count - 1)
- rc= (*join_tab->next_select)(join, join_tab + 1, 0);
- else
- {
- join->resume_nested_loop= FALSE;
- rc= NESTED_LOOP_OK;
- }
- }
- else
- {
- join->return_tab= join_tab;
+ join->return_tab= join_tab;
- if (join_tab->last_inner)
- {
- /* join_tab is the first inner table for an outer join operation. */
+ if (join_tab->last_inner)
+ {
+ /* join_tab is the first inner table for an outer join operation. */
- /* Set initial state of guard variables for this table.*/
- join_tab->found=0;
- join_tab->not_null_compl= 1;
+ /* Set initial state of guard variables for this table.*/
+ join_tab->found=0;
+ join_tab->not_null_compl= 1;
- /* Set first_unmatched for the last inner table of this group */
- join_tab->last_inner->first_unmatched= join_tab;
- if (join_tab->on_precond && !join_tab->on_precond->val_int())
- rc= NESTED_LOOP_NO_MORE_ROWS;
- }
- join->thd->row_count= 0;
-
- if (rc != NESTED_LOOP_NO_MORE_ROWS &&
- (rc= join_tab_execution_startup(join_tab)) < 0)
- DBUG_RETURN(rc);
+ /* Set first_unmatched for the last inner table of this group */
+ join_tab->last_inner->first_unmatched= join_tab;
+ if (join_tab->on_precond && !join_tab->on_precond->val_int())
+ rc= NESTED_LOOP_NO_MORE_ROWS;
+ }
+ join->thd->warning_info->reset_current_row_for_warning();
- if (join_tab->loosescan_match_tab)
- join_tab->loosescan_match_tab->found_match= FALSE;
+ if (rc != NESTED_LOOP_NO_MORE_ROWS &&
+ (rc= join_tab_execution_startup(join_tab)) < 0)
+ DBUG_RETURN(rc);
+
+ if (join_tab->loosescan_match_tab)
+ join_tab->loosescan_match_tab->found_match= FALSE;
- if (rc != NESTED_LOOP_NO_MORE_ROWS)
- {
- error= (*join_tab->read_first_record)(join_tab);
- if (join_tab->keep_current_rowid)
- join_tab->table->file->position(join_tab->table->record[0]);
- rc= evaluate_join_record(join, join_tab, error);
- }
+ if (rc != NESTED_LOOP_NO_MORE_ROWS)
+ {
+ error= (*join_tab->read_first_record)(join_tab);
+ if (join_tab->keep_current_rowid)
+ join_tab->table->file->position(join_tab->table->record[0]);
+ rc= evaluate_join_record(join, join_tab, error);
}
-
+
/*
Note: psergey has added the 2nd part of the following condition; the
change should probably be made in 5.1, too.
@@ -15826,14 +16130,26 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
will be re-evaluated again. It could be fixed, but, probably,
it's not worth doing now.
*/
+ /*
+ not_exists_optimize has been created from a
+ select_cond containing 'is_null'. This 'is_null'
+ predicate is still present on any 'tab' with
+ 'not_exists_optimize'. Furthermore, the usual rules
+ for condition guards also applies for
+ 'not_exists_optimize' -> When 'is_null==false' we
+ know all cond. guards are open and we can apply
+ the 'not_exists_optimize'.
+ */
+ DBUG_ASSERT(!(tab->table->reginfo.not_exists_optimize &&
+ !tab->select_cond));
+
if (tab->select_cond && !tab->select_cond->val_int())
{
/* The condition attached to table tab is false */
+
if (tab == join_tab)
{
found= 0;
- if (tab->table->reginfo.not_exists_optimize)
- DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
}
else
{
@@ -15842,10 +16158,21 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
not to the last table of the current nest level.
*/
join->return_tab= tab;
- if (tab->table->reginfo.not_exists_optimize)
- DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
- else
- DBUG_RETURN(NESTED_LOOP_OK);
+ }
+
+ if (tab->table->reginfo.not_exists_optimize)
+ {
+ /*
+ When not_exists_optimize is set: No need to further
+ explore more rows of 'tab' for this partial result.
+ Any found 'tab' matches are known to evaluate to 'false'.
+ Returning .._NO_MORE_ROWS will skip rem. 'tab' rows.
+ */
+ DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
+ }
+ else if (tab != join_tab)
+ {
+ DBUG_RETURN(NESTED_LOOP_OK);
}
}
}
@@ -15885,7 +16212,6 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
(See above join->return_tab= tab).
*/
join->examined_rows++;
- join->thd->row_count++;
DBUG_PRINT("counts", ("join->examined_rows++: %lu",
(ulong) join->examined_rows));
@@ -15894,6 +16220,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
enum enum_nested_loop_state rc;
/* A match from join_tab is found for the current partial join. */
rc= (*join_tab->next_select)(join, join_tab+1, 0);
+ join->thd->warning_info->inc_current_row_for_warning();
if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS)
DBUG_RETURN(rc);
if (return_tab < join->return_tab)
@@ -15910,7 +16237,10 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
}
else
+ {
+ join->thd->warning_info->inc_current_row_for_warning();
join_tab->read_record.unlock_row(join_tab);
+ }
}
else
{
@@ -15919,7 +16249,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
with the beginning coinciding with the current partial join.
*/
join->examined_rows++;
- join->thd->row_count++;
+ join->thd->warning_info->inc_current_row_for_warning();
join_tab->read_record.unlock_row(join_tab);
}
DBUG_RETURN(NESTED_LOOP_OK);
@@ -16031,7 +16361,7 @@ int report_error(TABLE *table, int error)
*/
if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT)
{
- push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_ERROR, error,
+ push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, error,
"Got error %d when reading table `%s`.`%s`",
error, table->s->db.str, table->s->table_name.str);
sql_print_error("Got error %d when reading table '%s'",
@@ -16092,7 +16422,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
/* Mark for EXPLAIN that the row was not found */
pos->records_read=0.0;
pos->ref_depend_map= 0;
- if (!table->maybe_null || error > 0)
+ if (!table->pos_in_table_list->outer_join || error > 0)
DBUG_RETURN(error);
}
/*
@@ -16118,7 +16448,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
/* Mark for EXPLAIN that the row was not found */
pos->records_read=0.0;
pos->ref_depend_map= 0;
- if (!table->maybe_null || error > 0)
+ if (!table->pos_in_table_list->outer_join || error > 0)
DBUG_RETURN(error);
}
}
@@ -16488,9 +16818,8 @@ join_init_quick_read_record(JOIN_TAB *tab)
}
-int init_read_record_seq(JOIN_TAB *tab)
+int read_first_record_seq(JOIN_TAB *tab)
{
- tab->read_record.read_record= rr_sequential;
if (tab->read_record.table->file->ha_rnd_init_with_error(1))
return 1;
return (*tab->read_record.read_record)(&tab->read_record);
@@ -16631,11 +16960,6 @@ join_ft_read_first(JOIN_TAB *tab)
table->file->print_error(error, MYF(0)); /* purecov: inspected */
return(1); /* purecov: inspected */
}
-#if NOT_USED_YET
- /* as ft-key doesn't use store_key's, see also FT_SELECT::init() */
- if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref))
- return -1;
-#endif
table->file->ft_init();
if ((error= table->file->ha_ft_read(table->record[0])))
@@ -16722,6 +17046,12 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_ENTER("end_send");
if (!end_of_records)
{
+ if (join->table_count &&
+ join->join_tab->is_using_loose_index_scan())
+ {
+ /* Copy non-aggregated fields when loose index scan is used. */
+ copy_fields(&join->tmp_table_param);
+ }
if (join->having && join->having->val_int() == 0)
DBUG_RETURN(NESTED_LOOP_OK); // Didn't match having
if (join->procedure)
@@ -16894,7 +17224,7 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
join->first_record=1;
- VOID(test_if_group_changed(join->group_fields));
+ (void) test_if_group_changed(join->group_fields);
}
if (idx < (int) join->send_group_parts)
{
@@ -16926,33 +17256,12 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
TABLE *table=join->tmp_table;
DBUG_ENTER("end_write");
- if (join->thd->killed) // Aborted by user
- {
- join->thd->send_kill_message();
- DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
- }
if (!end_of_records)
{
copy_fields(&join->tmp_table_param);
if (copy_funcs(join->tmp_table_param.items_to_copy, join->thd))
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
-#ifdef TO_BE_DELETED
- if (!table->uniques) // If not unique handling
- {
- /* Copy null values from group to row */
- ORDER *group;
- for (group=table->group ; group ; group=group->next)
- {
- Item *item= *group->item;
- if (item->maybe_null)
- {
- Field *field=item->get_tmp_table_field();
- field->ptr[-1]= (uchar) (field->is_null() ? 1 : 0);
- }
- }
- }
-#endif
if (!join->having || join->having->val_int())
{
int error;
@@ -16975,11 +17284,15 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
join->do_send_rows=0;
join->unit->select_limit_cnt = HA_POS_ERROR;
- DBUG_RETURN(NESTED_LOOP_OK);
}
}
}
end:
+ if (join->thd->killed)
+ {
+ join->thd->send_kill_message();
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
+ }
DBUG_RETURN(NESTED_LOOP_OK);
}
@@ -16997,11 +17310,6 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
- if (join->thd->killed) // Aborted by user
- {
- join->thd->send_kill_message();
- DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
- }
join->found_records++;
copy_fields(&join->tmp_table_param); // Groups are copied twice.
@@ -17027,7 +17335,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
- DBUG_RETURN(NESTED_LOOP_OK);
+ goto end;
}
/*
@@ -17062,6 +17370,12 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
join->join_tab[join->top_join_tab_count-1].next_select=end_unique_update;
}
join->send_records++;
+end:
+ if (join->thd->killed)
+ {
+ join->thd->send_kill_message();
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
+ }
DBUG_RETURN(NESTED_LOOP_OK);
}
@@ -17078,11 +17392,6 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
- if (join->thd->killed) // Aborted by user
- {
- join->thd->send_kill_message();
- DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
- }
init_tmptable_sum_functions(join->sum_funcs);
copy_fields(&join->tmp_table_param); // Groups are copied twice.
@@ -17112,6 +17421,11 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
}
+ if (join->thd->killed)
+ {
+ join->thd->send_kill_message();
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
+ }
DBUG_RETURN(NESTED_LOOP_OK);
}
@@ -17125,11 +17439,6 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
int idx= -1;
DBUG_ENTER("end_write_group");
- if (join->thd->killed)
- { // Aborted by user
- join->thd->send_kill_message();
- DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
- }
if (!join->first_record || end_of_records ||
(idx=test_if_group_changed(join->group_fields)) >= 0)
{
@@ -17163,15 +17472,15 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR);
}
if (end_of_records)
- DBUG_RETURN(NESTED_LOOP_OK);
+ goto end;
}
}
else
{
if (end_of_records)
- DBUG_RETURN(NESTED_LOOP_OK);
+ goto end;
join->first_record=1;
- VOID(test_if_group_changed(join->group_fields));
+ (void) test_if_group_changed(join->group_fields);
}
if (idx < (int) join->send_group_parts)
{
@@ -17182,13 +17491,19 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR);
if (join->procedure)
join->procedure->add();
- DBUG_RETURN(NESTED_LOOP_OK);
+ goto end;
}
}
if (update_sum_func(join->sum_funcs))
DBUG_RETURN(NESTED_LOOP_ERROR);
if (join->procedure)
join->procedure->add();
+end:
+ if (join->thd->killed)
+ {
+ join->thd->send_kill_message();
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
+ }
DBUG_RETURN(NESTED_LOOP_OK);
}
@@ -17525,7 +17840,7 @@ make_cond_after_sjm(Item *root_cond, Item *cond, table_map tables,
new_cond->argument_list()->push_back(fix);
}
/*
- Item_cond_and do not need fix_fields for execution, its parameters
+ Item_cond_or do not need fix_fields for execution, its parameters
are fixed or do not need fix_fields, too
*/
new_cond->quick_fix_field();
@@ -17638,7 +17953,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
@@ -17657,13 +17973,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");
@@ -17704,15 +18021,16 @@ 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
DBUG_RETURN(0);
}
- if (key_part->field != field)
+ if (key_part->field != field || !field->part_of_sortkey.is_set(idx))
DBUG_RETURN(0);
/* set flag to 1 if we can use read-next on key, else to -1 */
@@ -17728,7 +18046,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) &
@@ -17739,11 +18057,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);
}
@@ -17826,7 +18147,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;
@@ -17837,7 +18157,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;
@@ -17997,11 +18317,11 @@ find_field_in_item_list (Field *field, void *data)
*/
static bool
-test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit_arg,
+test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
bool no_changes, const key_map *map)
{
int ref_key;
- uint ref_key_parts;
+ uint UNINIT_VAR(ref_key_parts);
int order_direction= 0;
uint used_key_parts= 0;
TABLE *table=tab->table;
@@ -18012,12 +18332,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit_arg,
bool orig_cond_saved= false;
int best_key= -1;
bool changed_key= false;
- ha_rows best_select_limit;
DBUG_ENTER("test_if_skip_sort_order");
- LINT_INIT(ref_key_parts);
- LINT_INIT(best_select_limit);
-
/* Check that we are always called with first non-const table */
DBUG_ASSERT(tab == tab->join->join_tab + tab->join->const_tables);
@@ -18162,229 +18478,17 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit_arg,
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;
+ uint UNINIT_VAR(best_key_parts);
uint saved_best_key_parts= 0;
- int best_key_direction;
- ha_rows best_records;
- double read_time;
- bool is_best_covering= FALSE;
- double fanout= 1;
+ int best_key_direction= 0;
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;
- LINT_INIT(best_key_parts);
- LINT_INIT(best_key_direction);
- LINT_INIT(best_records);
- /*
- 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_arg >= 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->table_count; i++)
- fanout*= join->best_positions[i].records_read; // fanout is always >= 1
-
- for (nr=0; nr < table->s->keys ; nr++)
- {
- int direction;
- ha_rows select_limit= select_limit_arg;
-
- 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) ||
- (table->file->index_flags(nr, 0, 1) &
- HA_CLUSTERED_INDEX));
-
- /*
- 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.
- */
- uint used_index_parts= keyinfo->key_parts;
- uint used_pk_parts= 0;
- if (used_key_parts > used_index_parts)
- used_pk_parts= used_key_parts-used_index_parts;
- rec_per_key= used_key_parts ?
- keyinfo->rec_per_key[used_key_parts-1] : 1;
- /* Take into account the selectivity of the used pk prefix */
- if (used_pk_parts)
- {
- KEY *pkinfo=tab->table->key_info+table->s->primary_key;
- /*
- If the values of of records per key for the prefixes
- of the primary key are considered unknown we assume
- they are equal to 1.
- */
- if (used_key_parts == pkinfo->key_parts ||
- pkinfo->rec_per_key[0] == 0)
- rec_per_key= 1;
- if (rec_per_key > 1)
- {
- rec_per_key*= pkinfo->rec_per_key[used_pk_parts-1];
- rec_per_key/= pkinfo->rec_per_key[0];
- /*
- The value of rec_per_key for the extended key has
- to be adjusted accordingly if some components of
- the secondary key are included in the primary key.
- */
- for(uint i= 0; i < used_pk_parts; i++)
- {
- if (pkinfo->key_part[i].field->key_start.is_set(nr))
- {
- /*
- We presume here that for any index rec_per_key[i] != 0
- if rec_per_key[0] != 0.
- */
- DBUG_ASSERT(pkinfo->rec_per_key[i]);
- rec_per_key*= pkinfo->rec_per_key[i-1];
- rec_per_key/= pkinfo->rec_per_key[i];
- }
- }
- }
- }
- 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);
- } /* group */
-
- /*
- 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 && (group || table->force_index || is_covering)) ||
- 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) ||
- (!is_best_covering && is_covering))
- {
- 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;
- best_select_limit= select_limit;
- }
- }
- }
- }
- }
+ 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
@@ -18392,7 +18496,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit_arg,
index is clustered key.
*/
if (best_key < 0 ||
- ((select_limit_arg >= table_records) &&
+ ((select_limit >= table_records) &&
(tab->type == JT_ALL &&
tab->join->table_count > tab->join->const_tables + 1) &&
!(table->file->index_flags(best_key, 0, 1) & HA_CLUSTERED_INDEX)))
@@ -18494,8 +18598,8 @@ check_reverse_order:
{
tab->ref.key= -1;
tab->ref.key_parts= 0;
- if (best_select_limit < table->file->stats.records)
- tab->limit= best_select_limit;
+ if (select_limit < table->file->stats.records)
+ tab->limit= select_limit;
}
}
else if (tab->type != JT_ALL)
@@ -18533,22 +18637,16 @@ check_reverse_order:
{
if (select && select->quick)
{
- QUICK_SELECT_DESC *tmp;
- bool error= FALSE;
-
/* ORDER BY range_key DESC */
- tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick),
- used_key_parts, &error);
- if (tmp && select->quick == save_quick)
- save_quick= 0; // ::QUICK_SELECT_DESC consumed it
-
- if (!tmp || error)
+ QUICK_SELECT_I *tmp= select->quick->make_reverse(used_key_parts);
+ if (!tmp)
{
- delete tmp;
tab->limit= 0;
goto use_filesort; // Reverse sort failed -> filesort
}
- select->quick= tmp;
+ if (select->quick == save_quick)
+ save_quick= 0; // make_reverse() consumed it
+ select->set_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)
@@ -18608,7 +18706,7 @@ use_filesort:
SYNOPSIS
create_sort_index()
thd Thread handler
- tab Table to sort (in join structure)
+ join Join with table to sort
order How table should be sorted
filesort_limit Max number of rows that needs to be sorted
select_limit Max number of rows in final output
@@ -18618,8 +18716,8 @@ use_filesort:
IMPLEMENTATION
- - If there is an index that can be used, 'tab' is modified to use
- this index.
+ - If there is an index that can be used, the first non-const join_tab in
+ 'join' is modified to use this index.
- If no index, create with filesort() an index file that can be used to
retrieve rows in order (should be done with 'read_record').
The sorted data is stored in tab->table and will be freed when calling
@@ -18752,49 +18850,6 @@ err:
DBUG_RETURN(-1);
}
-#ifdef NOT_YET
-/**
- Add the HAVING criteria to table->select.
-*/
-
-static bool fix_having(JOIN *join, Item **having)
-{
- (*having)->update_used_tables(); // Some tables may have been const
- JOIN_TAB *table=&join->join_tab[join->const_tables];
- table_map used_tables= join->const_table_map | table->table->map;
-
- DBUG_EXECUTE("where",print_where(*having,"having", QT_ORDINARY););
- Item* sort_table_cond= make_cond_for_table(join->thd, *having, used_tables,
- used_tables, MAX_TABLES,
- FALSE, FALSE);
- if (sort_table_cond)
- {
- if (!table->select)
- if (!(table->select=new SQL_SELECT))
- return 1;
- if (!table->select->cond)
- table->select->cond=sort_table_cond;
- else // This should never happen
- if (!(table->select->cond= new Item_cond_and(table->select->cond,
- sort_table_cond)) ||
- table->select->cond->fix_fields(join->thd, &table->select->cond))
- return 1;
- table->set_select_cond(table->select->cond, __LINE__);
- table->select_cond->top_level_item();
- DBUG_EXECUTE("where",print_where(table->select_cond,
- "select and having",
- QT_ORDINARY););
- *having= make_cond_for_table(join->thd, *having,
- ~ (table_map) 0,~used_tables,
- MAX_TABLES, FALSE, FALSE);
- DBUG_EXECUTE("where",
- print_where(*having,"having after make_cond", QT_ORDINARY););
- }
- return 0;
-}
-#endif
-
-
/*****************************************************************************
Remove duplicates from tmp table
This should be recoded to add a unique index to the table and remove
@@ -18841,6 +18896,7 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
ulong reclength,offset;
uint field_count;
THD *thd= join->thd;
+
DBUG_ENTER("remove_duplicates");
entry->reginfo.lock_type=TL_WRITE;
@@ -18866,6 +18922,13 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
offset(entry->record[0]) : 0);
reclength=entry->s->reclength-offset;
+ /*
+ Disable LIMIT ROWS EXAMINED in order to avoid interrupting prematurely
+ duplicate removal, and produce a possibly incomplete query result.
+ */
+ thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;
+ if (thd->killed == ABORT_QUERY)
+ thd->killed= NOT_KILLED;
free_io_cache(entry); // Safety
entry->file->info(HA_STATUS_VARIABLE);
if (entry->s->db_type() == heap_hton ||
@@ -18879,6 +18942,8 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
error=remove_dup_with_compare(join->thd, entry, first_field, offset,
having);
+ if (join->select_lex != join->select_lex->master_unit()->fake_select_lex)
+ thd->lex->set_limit_rows_examined();
free_blobs(first_field);
DBUG_RETURN(error);
}
@@ -19021,10 +19086,10 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
extra_length= ALIGN_SIZE(key_length)-key_length;
}
- if (hash_init(&hash, &my_charset_bin, (uint) file->stats.records, 0,
- key_length, (hash_get_key) 0, 0, 0))
+ 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);
}
@@ -19065,7 +19130,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
key_pos+= *field_length++;
}
/* Check if it exists before */
- if (hash_search(&hash, org_key_pos, key_length))
+ if (my_hash_search(&hash, org_key_pos, key_length))
{
/* Duplicated found ; Remove the row */
if ((error= file->ha_delete_row(record)))
@@ -19078,15 +19143,15 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
}
key_pos+=extra_length;
}
- my_free((char*) key_buffer,MYF(0));
- hash_free(&hash);
+ 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));
- hash_free(&hash);
+ my_free(key_buffer);
+ my_hash_free(&hash);
file->extra(HA_EXTRA_NO_CACHE);
(void) file->ha_rnd_end();
if (error)
@@ -19134,7 +19199,6 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length,
}
-
/*
eq_ref: Create the lookup key and check if it is the same as saved key
@@ -19556,7 +19620,7 @@ setup_new_fields(THD *thd, List<Item> &fields,
optimize away 'order by'.
*/
-static ORDER *
+ORDER *
create_distinct_group(THD *thd, Item **ref_pointer_array,
ORDER *order_list, List<Item> &fields,
List<Item> &all_fields,
@@ -19822,8 +19886,7 @@ calc_group_buffer(JOIN *join,ORDER *group)
default:
/* This case should never be choosen */
DBUG_ASSERT(0);
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
- join->thd->fatal_error();
+ my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
}
}
parts++;
@@ -19947,7 +20010,7 @@ test_if_group_changed(List<Cached_item> &list)
Only FIELD_ITEM:s and FUNC_ITEM:s needs to be saved between groups.
Change old item_field to use a new field with points at saved fieldvalue
- This function is only called before use of send_fields.
+ This function is only called before use of send_result_set_metadata.
@param thd THD pointer
@param param temporary table parameters
@@ -19978,7 +20041,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
Item *pos;
List_iterator_fast<Item> li(all_fields);
Copy_field *copy= NULL;
- IF_DBUG(Copy_field *copy_start);
+ Copy_field *copy_start __attribute__((unused));
res_selected_fields.empty();
res_all_fields.empty();
List_iterator_fast<Item> itr(res_all_fields);
@@ -19991,7 +20054,7 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
goto err2;
param->copy_funcs.empty();
- IF_DBUG(copy_start= copy);
+ copy_start= copy;
for (i= 0; (pos= li++); i++)
{
Field *field;
@@ -20187,7 +20250,7 @@ bool JOIN::alloc_func_list()
Initialize 'sum_funcs' array with all Item_sum objects.
@param field_list All items
- @param send_fields Items in select list
+ @param send_result_set_metadata Items in select list
@param before_group_by Set to 1 if this is called before GROUP BY handling
@param recompute Set to TRUE if sum_funcs must be recomputed
@@ -20197,7 +20260,7 @@ bool JOIN::alloc_func_list()
1 error
*/
-bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_fields,
+bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_result_set_metadata,
bool before_group_by, bool recompute)
{
List_iterator_fast<Item> it(field_list);
@@ -20219,7 +20282,7 @@ bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_fields,
if (before_group_by && rollup.state == ROLLUP::STATE_INITED)
{
rollup.state= ROLLUP::STATE_READY;
- if (rollup_make_fields(field_list, send_fields, &func))
+ if (rollup_make_fields(field_list, send_result_set_metadata, &func))
DBUG_RETURN(TRUE); // Should never happen
}
else if (rollup.state == ROLLUP::STATE_NONE)
@@ -20395,7 +20458,22 @@ static bool setup_sum_funcs(THD *thd, Item_sum **func_ptr)
DBUG_ENTER("setup_sum_funcs");
while ((func= *(func_ptr++)))
{
- if (func->setup(thd))
+ if (func->aggregator_setup(thd))
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+static bool prepare_sum_aggregators(Item_sum **func_ptr, bool need_distinct)
+{
+ Item_sum *func;
+ DBUG_ENTER("prepare_sum_aggregators");
+ while ((func= *(func_ptr++)))
+ {
+ if (func->set_aggregator(need_distinct && func->has_with_distinct() ?
+ Aggregator::DISTINCT_AGGREGATOR :
+ Aggregator::SIMPLE_AGGREGATOR))
DBUG_RETURN(TRUE);
}
DBUG_RETURN(FALSE);
@@ -20439,13 +20517,13 @@ init_sum_functions(Item_sum **func_ptr, Item_sum **end_ptr)
{
for (; func_ptr != end_ptr ;func_ptr++)
{
- if ((*func_ptr)->reset())
+ if ((*func_ptr)->reset_and_add())
return 1;
}
/* If rollup, calculate the upper sum levels */
for ( ; *func_ptr ; func_ptr++)
{
- if ((*func_ptr)->add())
+ if ((*func_ptr)->aggregator_add())
return 1;
}
return 0;
@@ -20457,7 +20535,7 @@ update_sum_func(Item_sum **func_ptr)
{
Item_sum *func;
for (; (func= (Item_sum*) *func_ptr) ; func_ptr++)
- if (func->add())
+ if (func->aggregator_add())
return 1;
return 0;
}
@@ -21340,6 +21418,11 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
length= (longlong10_to_str(key_len, keylen_str_buf, 10) -
keylen_str_buf);
tmp3.append(keylen_str_buf, length, cs);
+/*<<<<<<< TREE
+ }
+ if ((is_hj || tab->type==JT_RANGE || tab->type == JT_INDEX_MERGE) &&
+ tab->select && tab->select->quick)
+=======*/
}
if (tab->type != JT_CONST && tab->select && tab->select->quick)
tab->select->quick->add_keys_and_lengths(&tmp2, &tmp3);
@@ -21509,7 +21592,11 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
{
const COND *pushed_cond= tab->table->file->pushed_cond;
- if (thd->variables.engine_condition_pushdown && pushed_cond)
+ if (((thd->variables.optimizer_switch &
+ OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN) ||
+ (tab->table->file->ha_table_flags() &
+ HA_MUST_USE_TABLE_CONDITION_PUSHDOWN)) &&
+ pushed_cond)
{
extra.append(STRING_WITH_LEN("; Using where with pushed "
"condition"));
@@ -21545,7 +21632,12 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
if (key_read)
{
if (quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
+ {
+ QUICK_GROUP_MIN_MAX_SELECT *qgs=
+ (QUICK_GROUP_MIN_MAX_SELECT *) tab->select->quick;
extra.append(STRING_WITH_LEN("; Using index for group-by"));
+ qgs->append_loose_scan_type(&extra);
+ }
else
extra.append(STRING_WITH_LEN("; Using index"));
}
@@ -21722,7 +21814,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
first->group_list.first,
first->having,
thd->lex->proc_list.first,
- first->options | thd->options | SELECT_DESCRIBE,
+ first->options | thd->variables.option_bits | SELECT_DESCRIBE,
result, unit, first);
}
DBUG_RETURN(res || thd->is_error());
@@ -21751,6 +21843,8 @@ static void print_table_array(THD *thd,
(curr->nested_join && !(curr->nested_join->used_tables &
~eliminated_tables))))
{
+ /* as of 5.5, print_join doesnt put eliminated elements into array */
+ DBUG_ASSERT(0);
continue;
}
@@ -21776,6 +21870,21 @@ static void print_table_array(THD *thd,
}
+/*
+ Check if the passed table is
+ - a base table which was eliminated, or
+ - a join nest which only contained eliminated tables (and so was eliminated,
+ too)
+*/
+
+static bool is_eliminated_table(table_map eliminated_tables, TABLE_LIST *tbl)
+{
+ return eliminated_tables &&
+ ((tbl->table && (tbl->table->map & eliminated_tables)) ||
+ (tbl->nested_join && !(tbl->nested_join->used_tables &
+ ~eliminated_tables)));
+}
+
/**
Print joins from the FROM clause.
@@ -21793,13 +21902,36 @@ static void print_join(THD *thd,
{
/* List is reversed => we should reverse it before using */
List_iterator_fast<TABLE_LIST> ti(*tables);
- TABLE_LIST **table= (TABLE_LIST **)thd->alloc(sizeof(TABLE_LIST*) *
- tables->elements);
- if (table == 0)
+ TABLE_LIST **table;
+ uint non_const_tables= 0;
+
+ for (TABLE_LIST *t= ti++; t ; t= ti++)
+ {
+ /*
+ See comment in print_table_array() about the second part of the
+ condition
+ */
+ if (!t->optimized_away && !is_eliminated_table(eliminated_tables, t))
+ non_const_tables++;
+ }
+ if (!non_const_tables)
+ {
+ str->append(STRING_WITH_LEN("dual"));
+ return; // all tables were optimized away
+ }
+ ti.rewind();
+
+ if (!(table= (TABLE_LIST **)thd->alloc(sizeof(TABLE_LIST*) *
+ non_const_tables)))
return; // out of memory
- for (TABLE_LIST **t= table + (tables->elements - 1); t >= table; t--)
- *t= ti++;
+ TABLE_LIST *tmp, **t= table + (non_const_tables - 1);
+ while ((tmp= ti++))
+ {
+ if (tmp->optimized_away || is_eliminated_table(eliminated_tables, tmp))
+ continue;
+ *t--= tmp;
+ }
DBUG_ASSERT(tables->elements >= 1);
/*
@@ -21816,7 +21948,7 @@ static void print_join(THD *thd,
*/
if ((*table)->sj_inner_tables)
{
- TABLE_LIST **end= table + tables->elements;
+ TABLE_LIST **end= table + non_const_tables;
for (TABLE_LIST **t2= table; t2!=end; t2++)
{
if (!(*t2)->sj_inner_tables)
@@ -21829,7 +21961,7 @@ static void print_join(THD *thd,
}
}
print_table_array(thd, eliminated_tables, str, table,
- table + tables->elements, query_type);
+ table + non_const_tables, query_type);
}
/**
@@ -21999,8 +22131,7 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
/* First add options */
if (options & SELECT_STRAIGHT_JOIN)
str->append(STRING_WITH_LEN("straight_join "));
- if ((thd->lex->lock_option == TL_READ_HIGH_PRIORITY) &&
- (this == &thd->lex->select_lex))
+ if (options & SELECT_HIGH_PRIORITY)
str->append(STRING_WITH_LEN("high_priority "));
if (options & SELECT_DISTINCT)
str->append(STRING_WITH_LEN("distinct "));
@@ -22371,5 +22502,423 @@ JOIN::reoptimize(Item *added_where, table_map join_tables,
/**
+ Cache constant expressions in WHERE, HAVING, ON conditions.
+*/
+
+void JOIN::cache_const_exprs()
+{
+ bool cache_flag= FALSE;
+ bool *analyzer_arg= &cache_flag;
+
+ /* No need in cache if all tables are constant. */
+ if (const_tables == table_count)
+ return;
+
+ if (conds)
+ conds->compile(&Item::cache_const_expr_analyzer, (uchar **)&analyzer_arg,
+ &Item::cache_const_expr_transformer, (uchar *)&cache_flag);
+ cache_flag= FALSE;
+ if (having)
+ having->compile(&Item::cache_const_expr_analyzer, (uchar **)&analyzer_arg,
+ &Item::cache_const_expr_transformer, (uchar *)&cache_flag);
+
+ for (JOIN_TAB *tab= first_depth_first_tab(this); tab;
+ tab= next_depth_first_tab(this, tab))
+ {
+ if (*tab->on_expr_ref)
+ {
+ cache_flag= FALSE;
+ (*tab->on_expr_ref)->compile(&Item::cache_const_expr_analyzer,
+ (uchar **)&analyzer_arg,
+ &Item::cache_const_expr_transformer,
+ (uchar *)&cache_flag);
+ }
+ }
+}
+
+
+/**
+ 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_arg,
+ 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_arg >= 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->table_count; 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;
+ ha_rows select_limit= select_limit_arg;
+ 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) ||
+ (table->file->index_flags(nr, 0, 1) &
+ HA_CLUSTERED_INDEX));
+ /*
+ 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.
+ */
+ uint used_index_parts= keyinfo->key_parts;
+ uint used_pk_parts= 0;
+ if (used_key_parts > used_index_parts)
+ used_pk_parts= used_key_parts-used_index_parts;
+ rec_per_key= used_key_parts ?
+ keyinfo->rec_per_key[used_key_parts-1] : 1;
+ /* Take into account the selectivity of the used pk prefix */
+ if (used_pk_parts)
+ {
+ KEY *pkinfo=tab->table->key_info+table->s->primary_key;
+ /*
+ If the values of of records per key for the prefixes
+ of the primary key are considered unknown we assume
+ they are equal to 1.
+ */
+ if (used_key_parts == pkinfo->key_parts ||
+ pkinfo->rec_per_key[0] == 0)
+ rec_per_key= 1;
+ if (rec_per_key > 1)
+ {
+ rec_per_key*= pkinfo->rec_per_key[used_pk_parts-1];
+ rec_per_key/= pkinfo->rec_per_key[0];
+ /*
+ The value of rec_per_key for the extended key has
+ to be adjusted accordingly if some components of
+ the secondary key are included in the primary key.
+ */
+ for(uint i= 0; i < used_pk_parts; i++)
+ {
+ if (pkinfo->key_part[i].field->key_start.is_set(nr))
+ {
+ /*
+ We presume here that for any index rec_per_key[i] != 0
+ if rec_per_key[0] != 0.
+ */
+ DBUG_ASSERT(pkinfo->rec_per_key[i]);
+ rec_per_key*= pkinfo->rec_per_key[i-1];
+ rec_per_key/= pkinfo->rec_per_key[i];
+ }
+ }
+ }
+ }
+ 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);
+ } /* group */
+
+ /*
+ 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 && (group || table->force_index || is_covering)) ||
+ 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) ||
+ (!is_best_covering && is_covering))
+ {
+ 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 (!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)
*/