summaryrefslogtreecommitdiffstats
path: root/sql/sql_union.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sql/sql_union.cc373
1 files changed, 234 insertions, 139 deletions
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 3413b840..eca490e8 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -32,6 +32,9 @@
#include "sql_cte.h"
#include "item_windowfunc.h"
+select_handler *find_partial_select_handler(THD *thd, SELECT_LEX *select_lex,
+ SELECT_LEX_UNIT *lex_unit);
+
bool mysql_union(THD *thd, LEX *lex, select_result *result,
SELECT_LEX_UNIT *unit, ulonglong setup_tables_done_option)
{
@@ -355,8 +358,6 @@ select_unit::create_result_table(THD *thd_arg, List<Item> *column_types,
return TRUE;
table->keys_in_use_for_query.clear_all();
- for (uint i=0; i < table->s->fields; i++)
- table->field[i]->flags &= ~(PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG);
if (create_table)
{
@@ -395,9 +396,6 @@ select_union_recursive::create_result_table(THD *thd_arg,
return true;
incr_table->keys_in_use_for_query.clear_all();
- for (uint i=0; i < table->s->fields; i++)
- incr_table->field[i]->flags &= ~(PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG);
-
return false;
}
@@ -1282,6 +1280,94 @@ static bool init_item_int(THD* thd, Item_int* &item)
return false;
}
+/**
+ @brief
+ Recursive subroutine to be called from find_unit_handler() (see below).
+ Must not be called directly, only from find_unit_handler().
+*/
+static select_handler *find_unit_handler_for_lex(THD *thd,
+ SELECT_LEX *sel_lex,
+ SELECT_LEX_UNIT* unit)
+{
+ if (!(sel_lex->join))
+ return nullptr;
+ for (TABLE_LIST *tbl= sel_lex->join->tables_list; tbl; tbl= tbl->next_local)
+ {
+ if (!tbl->table)
+ continue;
+ if (tbl->derived)
+ {
+ /*
+ Skip derived table for now as they will be checked
+ in the subsequent loop
+ */
+ continue;
+ }
+ handlerton *ht= tbl->table->file->partition_ht();
+ if (!ht->create_unit)
+ continue;
+ select_handler *sh= ht->create_unit(thd, unit);
+ if (sh)
+ return sh;
+ }
+
+ for (SELECT_LEX_UNIT *un= sel_lex->first_inner_unit(); un;
+ un= un->next_unit())
+ {
+ for (SELECT_LEX *sl= un->first_select(); sl; sl= sl->next_select())
+ {
+ select_handler *uh= find_unit_handler_for_lex(thd, sl, unit);
+ if (uh)
+ return uh;
+ }
+ }
+ return nullptr;
+}
+
+
+/**
+ @brief
+ Look for provision of the select_handler interface by a foreign engine.
+ This interface must support processing UNITs (multiple SELECTs combined
+ with UNION/EXCEPT/INTERSECT operators)
+
+ @param
+ thd The thread handler
+ unit UNIT (one or more SELECTs combined with UNION/EXCEPT/INTERSECT
+
+ @details
+ The function checks that this is an upper level UNIT and if so looks
+ through its tables searching for one whose handlerton owns a
+ create_unit call-back function. If the call of this function returns
+ a select_handler interface object then the server will push the
+ query into this engine.
+ This is a responsibility of the create_unit call-back function to
+ check whether the engine can execute the query.
+
+ The function recursively scans subqueries (see find_unit_handler_for_lex())
+ to get down to real tables and process queries like this:
+ (SELECT a FROM t1 UNION SELECT b FROM t2) UNION
+ (SELECT c FROM t3 UNION select d FROM t4)
+
+ @retval the found select_handler if the search is successful
+ nullptr otherwise
+*/
+
+static select_handler *find_unit_handler(THD *thd,
+ SELECT_LEX_UNIT *unit)
+{
+ if (unit->outer_select())
+ return nullptr;
+
+ for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
+ {
+ select_handler *uh= find_unit_handler_for_lex(thd, sl, unit);
+ if (uh)
+ return uh;
+ }
+ return nullptr;
+}
+
bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
select_result *sel_result,
@@ -1297,8 +1383,10 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
bool have_except= false, have_intersect= false,
have_except_all_or_intersect_all= false;
bool instantiate_tmp_table= false;
+ bool use_direct_union_result= false;
bool single_tvc= !first_sl->next_select() && first_sl->tvc;
bool single_tvc_wo_order= single_tvc && !first_sl->order_list.elements;
+ bool distinct_key= 0;
DBUG_ENTER("st_select_lex_unit::prepare");
DBUG_ASSERT(thd == current_thd);
@@ -1402,38 +1490,35 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
{
case INTERSECT_TYPE:
have_intersect= TRUE;
- if (!s->distinct){
- have_except_all_or_intersect_all= true;
- }
+ if (!s->distinct)
+ have_except_all_or_intersect_all= TRUE;
break;
case EXCEPT_TYPE:
have_except= TRUE;
- if (!s->distinct){
+ if (!s->distinct)
have_except_all_or_intersect_all= TRUE;
- }
+ break;
+ case DERIVED_TABLE_TYPE:
+ if (s->distinct)
+ distinct_key= 1;
break;
default:
break;
}
}
- /* Global option */
-
if (is_union_select || is_recursive)
{
if ((single_tvc_wo_order && !fake_select_lex) ||
(is_unit_op() && !union_needs_tmp_table() &&
- !have_except && !have_intersect && !single_tvc))
- {
- SELECT_LEX *last= first_select();
- while (last->next_select())
- last= last->next_select();
- if (!(tmp_result= union_result=
- new (thd->mem_root) select_union_direct(thd, sel_result,
- last)))
- goto err; /* purecov: inspected */
+ !have_except && !have_intersect && !single_tvc))
+ {
+ if (unlikely(set_direct_union_result(sel_result)))
+ goto err;
+ tmp_result= union_result;
fake_select_lex= NULL;
instantiate_tmp_table= false;
+ use_direct_union_result= true;
}
else
{
@@ -1617,7 +1702,8 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
if (join_union_item_types(thd, types, union_part_count + 1))
goto err;
if (union_result->create_result_table(thd, &types,
- MY_TEST(union_distinct),
+ (MY_TEST(union_distinct) ||
+ distinct_key),
create_options,
&derived_arg->alias, false,
instantiate_tmp_table, false,
@@ -1640,7 +1726,7 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
res= derived_arg->derived_result->create_result_table(thd,
&types,
- FALSE,
+ distinct_key,
create_options,
&derived_arg->alias,
FALSE, FALSE,
@@ -1768,9 +1854,9 @@ cont:
union_result->create_result_table(thd, &types,
MY_TEST(union_distinct) ||
have_except_all_or_intersect_all ||
- have_intersect,
- create_options, &empty_clex_str, false,
- instantiate_tmp_table, false,
+ have_intersect || distinct_key,
+ create_options, &empty_clex_str,
+ false, instantiate_tmp_table, false,
hidden);
union_result->addon_cnt= hidden;
for (uint i= 0; i < hidden; i++)
@@ -1814,37 +1900,6 @@ cont:
if (unlikely(saved_error))
goto err;
-
- if (fake_select_lex != NULL &&
- (thd->stmt_arena->is_stmt_prepare() ||
- (thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW)))
- {
- /* Validate the global parameters of this union */
-
- init_prepare_fake_select_lex(thd, TRUE);
- /* Should be done only once (the only item_list per statement) */
- DBUG_ASSERT(fake_select_lex->join == 0);
- if (!(fake_select_lex->join= new JOIN(thd, item_list, thd->variables.option_bits,
- result)))
- {
- fake_select_lex->table_list.empty();
- DBUG_RETURN(TRUE);
- }
-
- /*
- Fake st_select_lex should have item list for correct ref_array
- allocation.
- */
- fake_select_lex->item_list= item_list;
-
- thd->lex->current_select= fake_select_lex;
-
- /*
- 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;
- }
}
else
{
@@ -1854,23 +1909,45 @@ cont:
*/
table->reset_item_list(&item_list, hidden);
}
- if (fake_select_lex != NULL &&
- (thd->stmt_arena->is_stmt_prepare() ||
- (thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW)))
+
+ if (fake_select_lex != NULL)
{
- if (!fake_select_lex->join &&
- !(fake_select_lex->join=
- new JOIN(thd, item_list, thd->variables.option_bits, result)))
+ init_prepare_fake_select_lex(thd, TRUE);
+
+ DBUG_ASSERT(fake_select_lex->join == 0);
+ if (!(fake_select_lex->join= new JOIN(thd, item_list, options, result)))
{
- fake_select_lex->table_list.empty();
- DBUG_RETURN(TRUE);
+ fake_select_lex->table_list.empty();
+ DBUG_RETURN(TRUE);
}
- saved_error= fake_select_lex->join->
- prepare(fake_select_lex->table_list.first, 0,
+
+ /*
+ Fake st_select_lex should have item list for correct ref_array
+ allocation.
+ */
+ fake_select_lex->item_list= item_list;
+
+ thd->lex->current_select= fake_select_lex;
+
+ /*
+ 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->join->no_const_tables= TRUE;
+ saved_error= fake_select_lex->join->prepare(
+ fake_select_lex->table_list.first, 0,
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();
+ }
+
+ if (!thd->lex->is_view_context_analysis())
+ pushdown_unit= find_unit_handler(thd, this);
+ if (pushdown_unit)
+ {
+ if (prepare_pushdown(use_direct_union_result, sel_result))
+ goto err;
}
}
@@ -1884,6 +1961,45 @@ err:
DBUG_RETURN(TRUE);
}
+/**
+ @brief
+ Prepare st_select_lex_unit for the pushdown handler processing
+ @details
+ Creates and initializes data structures required for processing of the
+ pushdown handler. Validates fake_select_lex then discards it and sets
+ direct union result which is necessary for pushed down statements
+ @return
+ false - success
+ true - failure
+*/
+bool st_select_lex_unit::prepare_pushdown(bool use_direct_union_result,
+ select_result *sel_result)
+{
+ if (unlikely(pushdown_unit->prepare()))
+ return true;
+
+ if(!use_direct_union_result)
+ {
+ /*
+ Always use select_union_direct result for pushed down units, overwrite
+ the previous union_result unless select_union_direct is already used
+ */
+ if (unlikely(set_direct_union_result(sel_result)))
+ return true;
+ }
+ return false;
+}
+
+
+bool st_select_lex_unit::set_direct_union_result(select_result *sel_result)
+{
+ SELECT_LEX *last= first_select();
+ while (last->next_select())
+ last= last->next_select();
+ union_result= new (thd->mem_root) select_union_direct(thd, sel_result,
+ last);
+ return (union_result == nullptr);
+}
/**
@brief
@@ -2133,6 +2249,15 @@ bool st_select_lex_unit::optimize()
(lim.is_unlimited() || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
+ if (!this->pushdown_unit)
+ {
+ /*
+ If the UNIT hasn't been pushed down to the engine as a whole,
+ try to push down partial SELECTs of this UNIT separately
+ */
+ sl->pushdown_select= find_partial_select_handler(thd, sl, this);
+ }
+
saved_error= sl->join->optimize();
}
@@ -2152,16 +2277,29 @@ bool st_select_lex_unit::optimize()
bool st_select_lex_unit::exec()
{
+ DBUG_ENTER("st_select_lex_unit::exec");
+ if (executed && !uncacheable && !describe)
+ DBUG_RETURN(FALSE);
+
+ if (pushdown_unit)
+ {
+ create_explain_query_if_not_exists(thd->lex, thd->mem_root);
+ if (!executed)
+ save_union_explain(thd->lex->explain);
+ DBUG_RETURN(pushdown_unit->execute());
+ }
+ DBUG_RETURN(exec_inner());
+}
+
+
+bool st_select_lex_unit::exec_inner()
+{
SELECT_LEX *lex_select_save= thd->lex->current_select;
SELECT_LEX *select_cursor=first_select();
ulonglong add_rows=0;
- ha_rows examined_rows= 0;
bool first_execution= !executed;
- DBUG_ENTER("st_select_lex_unit::exec");
bool was_executed= executed;
- if (executed && !uncacheable && !describe)
- DBUG_RETURN(FALSE);
executed= 1;
if (!(uncacheable & ~UNCACHEABLE_EXPLAIN) && item &&
!item->with_recursive_reference)
@@ -2175,7 +2313,7 @@ bool st_select_lex_unit::exec()
save_union_explain(thd->lex->explain);
if (unlikely(saved_error))
- DBUG_RETURN(saved_error);
+ return saved_error;
if (union_result)
{
@@ -2192,6 +2330,7 @@ bool st_select_lex_unit::exec()
{
if (!fake_select_lex && !(with_element && with_element->is_recursive))
union_result->cleanup();
+
for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select())
{
ha_rows records_at_start= 0;
@@ -2245,33 +2384,29 @@ bool st_select_lex_unit::exec()
if (sl->tvc)
sl->tvc->exec(sl);
else
- sl->join->exec();
+ saved_error= sl->join->exec();
if (sl == union_distinct && !have_except_all_or_intersect_all &&
!(with_element && with_element->is_recursive))
{
// This is UNION DISTINCT, so there should be a fake_select_lex
DBUG_ASSERT(fake_select_lex != NULL);
if (table->file->ha_disable_indexes(key_map(0), false))
- DBUG_RETURN(TRUE);
+ return true;
table->no_keyread=1;
}
- if (!sl->tvc)
- saved_error= sl->join->error;
if (likely(!saved_error))
{
- examined_rows+= thd->get_examined_row_count();
- thd->set_examined_row_count(0);
if (union_result->flush())
{
thd->lex->current_select= lex_select_save;
- DBUG_RETURN(1);
+ return true;
}
}
}
if (unlikely(saved_error))
{
thd->lex->current_select= lex_select_save;
- DBUG_RETURN(saved_error);
+ return saved_error;
}
if (fake_select_lex != NULL)
{
@@ -2280,7 +2415,7 @@ bool st_select_lex_unit::exec()
if (unlikely(error))
{
table->file->print_error(error, MYF(0));
- DBUG_RETURN(1);
+ return true;
}
}
if (found_rows_for_union && !sl->braces &&
@@ -2330,45 +2465,13 @@ bool st_select_lex_unit::exec()
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)
+ if (!(thd->stmt_arena->is_stmt_prepare() ||
+ (thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW)) &&
+ first_execution)
{
- /*
- allocate JOIN for fake select only once (prevent
- mysql_select automatic allocation)
- TODO: The above is nonsense. mysql_select() will not allocate the
- join if one already exists. There must be some other reason why we
- don't let it allocate the join. Perhaps this is because we need
- some special parameter values passed to join constructor?
- */
- if (unlikely(!(fake_select_lex->join=
- new JOIN(thd, item_list, fake_select_lex->options,
- result))))
- {
- fake_select_lex->table_list.empty();
- goto err;
- }
- fake_select_lex->join->no_const_tables= TRUE;
-
- /*
- Fake st_select_lex should have item list for correct ref_array
- allocation.
- */
- fake_select_lex->item_list= item_list;
-
- /*
- We need to add up n_sum_items in order to make the correct
- allocation in setup_ref_array().
- Don't add more sum_items if we have already done JOIN::prepare
- for this (with a different join object)
- */
- if (fake_select_lex->ref_pointer_array.is_null())
- fake_select_lex->n_child_sum_items+= global_parameters()->n_sum_items;
-
- if (!was_executed)
- save_union_explain_part2(thd->lex->explain);
+ save_union_explain_part2(thd->lex->explain);
saved_error= mysql_select(thd, &result_table_list,
item_list, NULL,
@@ -2391,7 +2494,6 @@ bool st_select_lex_unit::exec()
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, &result_table_list, item_list, NULL,
global_parameters()->order_list.elements,
global_parameters()->order_list.first,
@@ -2401,9 +2503,9 @@ bool st_select_lex_unit::exec()
}
else
{
- join->join_examined_rows= 0;
saved_error= join->reinit();
- join->exec();
+ if (join->exec())
+ saved_error= 1;
}
}
@@ -2411,7 +2513,6 @@ bool st_select_lex_unit::exec()
if (likely(!saved_error))
{
thd->limit_found_rows = (ulonglong)table->file->stats.records + add_rows;
- thd->inc_examined_row_count(examined_rows);
}
/*
Mark for slow query log if any of the union parts didn't use
@@ -2420,9 +2521,8 @@ bool st_select_lex_unit::exec()
}
}
thd->lex->current_select= lex_select_save;
-err:
thd->lex->set_limit_rows_examined();
- DBUG_RETURN(saved_error);
+ return saved_error;
}
@@ -2458,7 +2558,6 @@ bool st_select_lex_unit::exec_recursive()
bool is_unrestricted= with_element->is_unrestricted();
List_iterator_fast<TABLE_LIST> li(with_element->rec_result->rec_table_refs);
TMP_TABLE_PARAM *tmp_table_param= &with_element->rec_result->tmp_table_param;
- ha_rows examined_rows= 0;
bool was_executed= executed;
TABLE_LIST *rec_tbl;
@@ -2509,29 +2608,23 @@ bool st_select_lex_unit::exec_recursive()
sl->tvc->exec(sl);
else
{
- sl->join->exec();
- saved_error= sl->join->error;
+ saved_error= sl->join->exec();
}
if (likely(!saved_error))
{
- examined_rows+= thd->get_examined_row_count();
- thd->set_examined_row_count(0);
if (unlikely(union_result->flush()))
{
thd->lex->current_select= lex_select_save;
DBUG_RETURN(1);
}
}
- if (unlikely(saved_error))
+ else
{
thd->lex->current_select= lex_select_save;
goto err;
-
}
}
- thd->inc_examined_row_count(examined_rows);
-
incr_table->file->info(HA_STATUS_VARIABLE);
if (with_element->level && incr_table->file->stats.records == 0)
with_element->set_as_stabilized();
@@ -2651,6 +2744,9 @@ bool st_select_lex_unit::cleanup()
}
}
+ delete pushdown_unit;
+ pushdown_unit= nullptr;
+
DBUG_RETURN(error);
}
@@ -2684,12 +2780,9 @@ bool st_select_lex_unit::change_result(select_result_interceptor *new_result,
if (sl->join->change_result(new_result, old_result))
return true; /* purecov: inspected */
}
- /*
- 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);
+
+ if (fake_select_lex && fake_select_lex->join)
+ fake_select_lex->join->change_result(new_result, old_result);
return false;
}
@@ -2814,6 +2907,8 @@ bool st_select_lex::cleanup()
inner_refs_list.empty();
exclude_from_table_unique_test= FALSE;
hidden_bit_fields= 0;
+ delete pushdown_select;
+ pushdown_select= nullptr;
DBUG_RETURN(error);
}