summaryrefslogtreecommitdiff
path: root/sql/sql_union.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_union.cc')
-rw-r--r--sql/sql_union.cc280
1 files changed, 221 insertions, 59 deletions
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index cfa907b9ddc..2086d1d6c03 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -188,6 +188,104 @@ void select_union::cleanup()
}
+
+/**
+ Replace the current result with new_result and prepare it.
+
+ @param new_result New result pointer
+
+ @retval FALSE Success
+ @retval TRUE Error
+*/
+
+bool select_union_direct::change_result(select_result *new_result)
+{
+ result= new_result;
+ return (result->prepare(unit->types, unit) || result->prepare2());
+}
+
+
+bool select_union_direct::postponed_prepare(List<Item> &types)
+{
+ if (result != NULL)
+ return (result->prepare(types, unit) || result->prepare2());
+ else
+ return false;
+}
+
+
+bool select_union_direct::send_result_set_metadata(List<Item> &list, uint flags)
+{
+ if (done_send_result_set_metadata)
+ return false;
+ done_send_result_set_metadata= true;
+
+ /*
+ Set global offset and limit to be used in send_data(). These can
+ be variables in prepared statements or stored programs, so they
+ must be reevaluated for each execution.
+ */
+ offset= unit->global_parameters()->get_offset();
+ limit= unit->global_parameters()->get_limit();
+ if (limit + offset >= limit)
+ limit+= offset;
+ else
+ limit= HA_POS_ERROR; /* purecov: inspected */
+
+ return result->send_result_set_metadata(unit->types, flags);
+}
+
+
+int select_union_direct::send_data(List<Item> &items)
+{
+ if (!limit)
+ return false;
+ limit--;
+ if (offset)
+ {
+ offset--;
+ return false;
+ }
+
+ send_records++;
+ fill_record(thd, table, table->field, items, true, false);
+ if (thd->is_error())
+ return true; /* purecov: inspected */
+
+ return result->send_data(unit->item_list);
+}
+
+
+bool select_union_direct::initialize_tables (JOIN *join)
+{
+ if (done_initialize_tables)
+ return false;
+ done_initialize_tables= true;
+
+ return result->initialize_tables(join);
+}
+
+
+bool select_union_direct::send_eof()
+{
+ // Reset for each SELECT_LEX, so accumulate here
+ limit_found_rows+= thd->limit_found_rows;
+
+ if (unit->thd->lex->current_select == last_select_lex)
+ {
+ thd->limit_found_rows= limit_found_rows;
+
+ // Reset and make ready for re-execution
+ done_send_result_set_metadata= false;
+ done_initialize_tables= false;
+
+ return result->send_eof();
+ }
+ else
+ return false;
+}
+
+
/*
initialization procedures before fake_select_lex preparation()
@@ -218,12 +316,12 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd_arg,
*/
if (!fake_select_lex->first_execution && first_execution)
{
- for (ORDER *order= global_parameters->order_list.first;
+ for (ORDER *order= global_parameters()->order_list.first;
order;
order= order->next)
order->item= &order->item_ptr;
}
- for (ORDER *order= global_parameters->order_list.first;
+ for (ORDER *order= global_parameters()->order_list.first;
order;
order=order->next)
{
@@ -242,11 +340,20 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
SELECT_LEX *sl, *first_sl= first_select();
select_result *tmp_result;
bool is_union_select;
+ bool instantiate_tmp_table= false;
DBUG_ENTER("st_select_lex_unit::prepare");
+ DBUG_ASSERT(thd == thd_arg && thd == current_thd);
describe= MY_TEST(additional_options & SELECT_DESCRIBE);
/*
+ Save fake_select_lex in case we don't need it for anything but
+ global parameters.
+ */
+ if (saved_fake_select_lex == NULL) // Don't overwrite on PS second prepare
+ saved_fake_select_lex= fake_select_lex;
+
+ /*
result object should be reassigned even if preparing already done for
max/min subquery (ALL/ANY optimization)
*/
@@ -284,8 +391,25 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
if (is_union_select)
{
- if (!(tmp_result= union_result= new select_union))
- goto err;
+ if (is_union() && !union_needs_tmp_table())
+ {
+ SELECT_LEX *last= first_select();
+ while (last->next_select())
+ last= last->next_select();
+ if (!(tmp_result= union_result=
+ new (thd_arg->mem_root) select_union_direct(thd_arg, sel_result,
+ last)))
+ goto err; /* purecov: inspected */
+ fake_select_lex= NULL;
+ instantiate_tmp_table= false;
+ }
+ else
+ {
+ if (!(tmp_result= union_result=
+ new (thd_arg->mem_root) select_union(thd_arg)))
+ goto err; /* purecov: inspected */
+ instantiate_tmp_table= true;
+ }
}
else
tmp_result= sel_result;
@@ -361,7 +485,8 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
while ((item_tmp= it++))
{
/* Error's in 'new' will be detected after loop */
- types.push_back(new Item_type_holder(thd_arg, item_tmp));
+ types.push_back(new (thd_arg->mem_root)
+ Item_type_holder(thd_arg, item_tmp));
}
if (thd_arg->is_fatal_error)
@@ -372,7 +497,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
if (types.elements != sl->item_list.elements)
{
my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
- ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0));
+ ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0));
goto err;
}
List_iterator_fast<Item> it(sl->item_list);
@@ -386,6 +511,14 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
}
}
+ /*
+ If the query is using select_union_direct, we have postponed
+ preparation of the underlying select_result until column types
+ are known.
+ */
+ if (union_result != NULL && union_result->postponed_prepare(types))
+ DBUG_RETURN(true);
+
if (is_union_select)
{
/*
@@ -422,13 +555,13 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
the meaning of these accumulated flags and what to carry over to the
recipient query (SELECT_LEX).
*/
- if (global_parameters->ftfunc_list->elements &&
- global_parameters->order_list.elements &&
- global_parameters != fake_select_lex)
+ if (global_parameters()->ftfunc_list->elements &&
+ global_parameters()->order_list.elements &&
+ global_parameters() != fake_select_lex)
{
ORDER *ord;
Item_func::Functype ft= Item_func::FT_FUNC;
- for (ord= global_parameters->order_list.first; ord; ord= ord->next)
+ for (ord= global_parameters()->order_list.first; ord; ord= ord->next)
if ((*ord->item)->walk (&Item::find_function_processor, FALSE,
(uchar *) &ft))
{
@@ -446,11 +579,12 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
from it (this should be removed in 5.2 when fulltext search is moved
out of MyISAM).
*/
- if (global_parameters->ftfunc_list->elements)
+ if (global_parameters()->ftfunc_list->elements)
create_options= create_options | TMP_TABLE_FORCE_MYISAM;
if (union_result->create_result_table(thd, &types, MY_TEST(union_distinct),
- create_options, "", FALSE, TRUE))
+ create_options, "", false,
+ instantiate_tmp_table))
goto err;
if (fake_select_lex && !fake_select_lex->first_cond_optimization)
{
@@ -484,7 +618,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
if (saved_error)
goto err;
- if (thd->stmt_arena->is_stmt_prepare())
+ if (fake_select_lex != NULL && thd->stmt_arena->is_stmt_prepare())
{
/* Validate the global parameters of this union */
@@ -510,14 +644,14 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
We need to add up n_sum_items in order to make the correct
allocation in setup_ref_array().
*/
- fake_select_lex->n_child_sum_items+= global_parameters->n_sum_items;
+ fake_select_lex->n_child_sum_items+= global_parameters()->n_sum_items;
saved_error= fake_select_lex->join->
prepare(&fake_select_lex->ref_pointer_array,
fake_select_lex->table_list.first,
0, 0,
- global_parameters->order_list.elements, // og_num
- global_parameters->order_list.first, // order
+ global_parameters()->order_list.elements, // og_num
+ global_parameters()->order_list.first, // order
false, NULL, NULL, NULL,
fake_select_lex, this);
fake_select_lex->table_list.empty();
@@ -569,7 +703,11 @@ bool st_select_lex_unit::optimize()
{
item->assigned(0); // We will reinit & rexecute unit
item->reset();
- table->file->ha_delete_all_rows();
+ if (table->created)
+ {
+ table->file->ha_delete_all_rows();
+ table->file->info(HA_STATUS_VARIABLE);
+ }
}
/* re-enabling indexes for next subselect iteration */
if (union_distinct && table->file->ha_enable_indexes(HA_KEY_SWITCH_ALL))
@@ -586,7 +724,7 @@ bool st_select_lex_unit::optimize()
else
{
set_limit(sl);
- if (sl == global_parameters || describe)
+ if (sl == global_parameters() || describe)
{
offset_limit_cnt= 0;
/*
@@ -651,18 +789,23 @@ bool st_select_lex_unit::exec()
if (uncacheable || !item || !item->assigned() || describe)
{
+ if (!fake_select_lex)
+ union_result->cleanup();
for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select())
{
ha_rows records_at_start= 0;
thd->lex->current_select= sl;
- if (sl != &thd->lex->select_lex)
- fake_select_lex->uncacheable|= sl->uncacheable;
- else
- fake_select_lex->uncacheable= 0;
+ if (fake_select_lex)
+ {
+ if (sl != &thd->lex->select_lex)
+ fake_select_lex->uncacheable|= sl->uncacheable;
+ else
+ fake_select_lex->uncacheable= 0;
+ }
{
set_limit(sl);
- if (sl == global_parameters || describe)
+ if (sl == global_parameters() || describe)
{
offset_limit_cnt= 0;
/*
@@ -689,6 +832,8 @@ bool st_select_lex_unit::exec()
sl->join->exec();
if (sl == union_distinct)
{
+ // This is UNION DISTINCT, so there should be a fake_select_lex
+ DBUG_ASSERT(fake_select_lex != NULL);
if (table->file->ha_disable_indexes(HA_KEY_SWITCH_ALL))
DBUG_RETURN(TRUE);
table->no_keyread=1;
@@ -713,12 +858,15 @@ bool st_select_lex_unit::exec()
thd->lex->current_select= lex_select_save;
DBUG_RETURN(saved_error);
}
- /* Needed for the following test and for records_at_start in next loop */
- int error= table->file->info(HA_STATUS_VARIABLE);
- if(error)
+ if (fake_select_lex != NULL)
{
- table->file->print_error(error, MYF(0));
- DBUG_RETURN(1);
+ /* Needed for the following test and for records_at_start in next loop */
+ int error= table->file->info(HA_STATUS_VARIABLE);
+ if(error)
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(1);
+ }
}
if (found_rows_for_union && !sl->braces &&
select_limit_cnt != HA_POS_ERROR)
@@ -740,7 +888,7 @@ bool st_select_lex_unit::exec()
*/
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT,
- ER(ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT),
+ ER_THD(thd, ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT),
thd->accessed_rows_and_keys,
thd->lex->limit_rows_examined->val_uint());
thd->reset_killed();
@@ -751,8 +899,6 @@ bool st_select_lex_unit::exec()
DBUG_EXECUTE_IF("show_explain_probe_union_read",
dbug_serve_apcs(thd, 1););
- /* Send result to 'result' */
- saved_error= TRUE;
{
List<Item_func_match> empty_list;
empty_list.empty();
@@ -762,11 +908,15 @@ bool st_select_lex_unit::exec()
*/
thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;
- if (!thd->is_fatal_error) // Check if EOM
+ if (fake_select_lex != NULL && !thd->is_fatal_error) // Check if EOM
{
- set_limit(global_parameters);
+ /* Send result to 'result' */
+ saved_error= true;
+
+ set_limit(global_parameters());
init_prepare_fake_select_lex(thd, first_execution);
JOIN *join= fake_select_lex->join;
+ saved_error= false;
if (!join)
{
/*
@@ -798,7 +948,7 @@ bool st_select_lex_unit::exec()
for this (with a different join object)
*/
if (!fake_select_lex->ref_pointer_array)
- fake_select_lex->n_child_sum_items+= global_parameters->n_sum_items;
+ fake_select_lex->n_child_sum_items+= global_parameters()->n_sum_items;
if (!was_executed)
save_union_explain_part2(thd->lex->explain);
@@ -806,8 +956,8 @@ bool st_select_lex_unit::exec()
saved_error= mysql_select(thd, &fake_select_lex->ref_pointer_array,
&result_table_list,
0, item_list, NULL,
- global_parameters->order_list.elements,
- global_parameters->order_list.first,
+ global_parameters()->order_list.elements,
+ global_parameters()->order_list.first,
NULL, NULL, NULL,
fake_select_lex->options | SELECT_NO_UNLOCK,
result, this, fake_select_lex);
@@ -824,20 +974,20 @@ bool st_select_lex_unit::exec()
1st execution sets certain members (e.g. select_result) to perform
subquery execution rather than EXPLAIN line production. In order
to reset them back, we re-do all of the actions (yes it is ugly):
- */
+ */ // psergey-todo: is the above really necessary anymore??
join->init(thd, item_list, fake_select_lex->options, result);
saved_error= mysql_select(thd, &fake_select_lex->ref_pointer_array,
&result_table_list,
0, item_list, NULL,
- global_parameters->order_list.elements,
- global_parameters->order_list.first,
+ global_parameters()->order_list.elements,
+ global_parameters()->order_list.first,
NULL, NULL, NULL,
fake_select_lex->options | SELECT_NO_UNLOCK,
result, this, fake_select_lex);
}
else
{
- join->examined_rows= 0;
+ join->join_examined_rows= 0;
saved_error= join->reinit();
join->exec();
}
@@ -912,11 +1062,11 @@ bool st_select_lex_unit::cleanup()
Note: global_parameters and fake_select_lex are always
initialized for UNION
*/
- DBUG_ASSERT(global_parameters);
- if (global_parameters->order_list.elements)
+ DBUG_ASSERT(global_parameters());
+ if (global_parameters()->order_list.elements)
{
ORDER *ord;
- for (ord= global_parameters->order_list.first; ord; ord= ord->next)
+ for (ord= global_parameters()->order_list.first; ord; ord= ord->next)
(*ord->item)->walk (&Item::cleanup_processor, 0, 0);
}
}
@@ -947,32 +1097,33 @@ void st_select_lex_unit::reinit_exec_mechanism()
}
-/*
- change select_result object of unit
+/**
+ Change the select_result object used to return the final result of
+ the unit, replacing occurences of old_result with new_result.
- SYNOPSIS
- st_select_lex_unit::change_result()
- result new select_result object
- old_result old select_result object
+ @param new_result New select_result object
+ @param old_result Old select_result object
- RETURN
- FALSE - OK
- TRUE - error
+ @retval false Success
+ @retval true Error
*/
bool st_select_lex_unit::change_result(select_result_interceptor *new_result,
select_result_interceptor *old_result)
{
- bool res= FALSE;
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
{
- if (sl->join && sl->join->result == old_result)
- if (sl->join->change_result(new_result))
- return TRUE;
+ if (sl->join)
+ if (sl->join->change_result(new_result, old_result))
+ return true; /* purecov: inspected */
}
- if (fake_select_lex && fake_select_lex->join)
- res= fake_select_lex->join->change_result(new_result);
- return (res);
+ /*
+ If there were a fake_select_lex->join, we would have to change the
+ result of that also, but change_result() is called before such an
+ object is created.
+ */
+ DBUG_ASSERT(fake_select_lex == NULL || fake_select_lex->join == NULL);
+ return false;
}
/*
@@ -1016,11 +1167,22 @@ List<Item> *st_select_lex_unit::get_unit_column_types()
return &sl->item_list;
}
+
+static void cleanup_order(ORDER *order)
+{
+ for (; order; order= order->next)
+ order->counter_used= 0;
+}
+
+
bool st_select_lex::cleanup()
{
bool error= FALSE;
DBUG_ENTER("st_select_lex::cleanup()");
+ cleanup_order(order_list.first);
+ cleanup_order(group_list.first);
+
if (join)
{
DBUG_ASSERT((st_select_lex*)join->select_lex == this);