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.cc966
1 files changed, 748 insertions, 218 deletions
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 7e4d06b03f2..b71b62b35ed 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -20,7 +20,7 @@
UNION's were introduced by Monty and Sinisa <sinisa@mysql.com>
*/
-#include <my_global.h>
+#include "mariadb.h"
#include "sql_priv.h"
#include "unireg.h"
#include "sql_union.h"
@@ -36,7 +36,7 @@ bool mysql_union(THD *thd, LEX *lex, select_result *result,
{
DBUG_ENTER("mysql_union");
bool res;
- if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK |
+ if (!(res= unit->prepare(unit->derived, result, SELECT_NO_UNLOCK |
setup_tables_done_option)))
res= unit->exec();
res|= unit->cleanup();
@@ -48,15 +48,68 @@ bool mysql_union(THD *thd, LEX *lex, select_result *result,
** store records in temporary table for UNION
***************************************************************************/
-int select_union::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
+int select_unit::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
{
unit= u;
return 0;
}
+/**
+ This called by SELECT_LEX_UNIT::exec when select changed
+*/
-int select_union::send_data(List<Item> &values)
+void select_unit::change_select()
+{
+ uint current_select_number= thd->lex->current_select->select_number;
+ DBUG_ENTER("select_unit::change_select");
+ DBUG_PRINT("enter", ("select in unit change: %u -> %u",
+ curr_sel, current_select_number));
+ DBUG_ASSERT(curr_sel != current_select_number);
+ curr_sel= current_select_number;
+ /* New SELECT processing starts */
+ DBUG_ASSERT(table->file->inited == 0);
+ step= thd->lex->current_select->linkage;
+ switch (step)
+ {
+ case INTERSECT_TYPE:
+ intersect_mark->value= prev_step= curr_step;
+ curr_step= current_select_number;
+ break;
+ case EXCEPT_TYPE:
+ break;
+ default:
+ step= UNION_TYPE;
+ break;
+ }
+ DBUG_VOID_RETURN;
+}
+/**
+ Fill temporary tables for UNION/EXCEPT/INTERSECT
+
+ @Note
+UNION:
+ just add records to the table (with 'counter' field first if INTERSECT
+ present in the sequence).
+EXCEPT:
+ looks for the record in the table (with 'counter' field first if
+ INTERSECT present in the sequence) and delete it if found
+INTESECT:
+ looks for the same record with 'counter' field of previous operation,
+ put as a 'counter' number of the current SELECT.
+ We scan the table and remove all records which marked with not last
+ 'counter' after processing all records in send_eof and only if it last
+ SELECT of sequence of INTERSECTS.
+
+ @param values List of record items to process.
+
+ @retval 0 - OK
+ @retval -1 - duplicate
+ @retval 1 - error
+*/
+int select_unit::send_data(List<Item> &values)
{
+ int rc;
+ int not_reported_error= 0;
if (unit->offset_limit_cnt)
{ // using limit offset,count
unit->offset_limit_cnt--;
@@ -66,46 +119,187 @@ int select_union::send_data(List<Item> &values)
return 0;
if (table->no_rows_with_nulls)
table->null_catch_flags= CHECK_ROW_FOR_NULLS_TO_REJECT;
- fill_record(thd, table, table->field, values, TRUE, FALSE);
- if (thd->is_error())
- return 1;
+ if (intersect_mark)
+ {
+ fill_record(thd, table, table->field + 1, values, TRUE, FALSE);
+ table->field[0]->store((ulonglong) curr_step, 1);
+ }
+ else
+ fill_record(thd, table, table->field, values, TRUE, FALSE);
+ if (unlikely(thd->is_error()))
+ {
+ rc= 1;
+ goto end;
+ }
if (table->no_rows_with_nulls)
{
table->null_catch_flags&= ~CHECK_ROW_FOR_NULLS_TO_REJECT;
if (table->null_catch_flags)
- return 0;
+ {
+ rc= 0;
+ goto end;
+ }
}
- if ((write_err= table->file->ha_write_tmp_row(table->record[0])))
+ // select_unit::change_select() change step & Co correctly for each SELECT
+ switch (step)
{
- if (write_err == HA_ERR_FOUND_DUPP_KEY)
+ case UNION_TYPE:
{
+ if (unlikely((write_err=
+ table->file->ha_write_tmp_row(table->record[0]))))
+ {
+ if (write_err == HA_ERR_FOUND_DUPP_KEY)
+ {
+ /*
+ Inform upper level that we found a duplicate key, that should not
+ be counted as part of limit
+ */
+ rc= -1;
+ goto end;
+ }
+ bool is_duplicate= FALSE;
+ /* create_internal_tmp_table_from_heap will generate error if needed */
+ if (table->file->is_fatal_error(write_err, HA_CHECK_DUP) &&
+ create_internal_tmp_table_from_heap(thd, table,
+ tmp_table_param.start_recinfo,
+ &tmp_table_param.recinfo,
+ write_err, 1, &is_duplicate))
+ {
+ rc= 1;
+ goto end;
+ }
+
+ if (is_duplicate)
+ {
+ rc= -1;
+ goto end;
+ }
+ }
+ break;
+ }
+ case EXCEPT_TYPE:
+ {
+ int find_res;
/*
- Inform upper level that we found a duplicate key, that should not
- be counted as part of limit
+ The temporary table uses very first index or constrain for
+ checking unique constrain.
*/
- return -1;
+ if (!(find_res= table->file->find_unique_row(table->record[0], 0)))
+ {
+ DBUG_ASSERT(!table->triggers);
+ table->status|= STATUS_DELETED;
+ not_reported_error= table->file->ha_delete_tmp_row(table->record[0]);
+ rc= MY_TEST(not_reported_error);
+ goto end;
+ }
+ else
+ {
+ if ((rc= not_reported_error= (find_res != 1)))
+ goto end;
+ }
+ break;
}
- bool is_duplicate= FALSE;
- /* create_internal_tmp_table_from_heap will generate error if needed */
- if (table->file->is_fatal_error(write_err, HA_CHECK_DUP) &&
- create_internal_tmp_table_from_heap(thd, table,
- tmp_table_param.start_recinfo,
- &tmp_table_param.recinfo,
- write_err, 1, &is_duplicate))
- return 1;
- if (is_duplicate)
- return -1;
+ case INTERSECT_TYPE:
+ {
+ int find_res;
+ /*
+ The temporary table uses very first index or constrain for
+ checking unique constrain.
+ */
+ if (!(find_res= table->file->find_unique_row(table->record[0], 0)))
+ {
+ DBUG_ASSERT(!table->triggers);
+ if (table->field[0]->val_int() != prev_step)
+ {
+ rc= 0;
+ goto end;
+ }
+ store_record(table, record[1]);
+ table->field[0]->store(curr_step, 0);
+ not_reported_error= table->file->ha_update_tmp_row(table->record[1],
+ table->record[0]);
+ rc= MY_TEST(not_reported_error);
+ DBUG_ASSERT(rc != HA_ERR_RECORD_IS_THE_SAME);
+ goto end;
+ }
+ else
+ {
+ if ((rc= not_reported_error= (find_res != 1)))
+ goto end;
+ }
+ break;
+ }
+ default:
+ DBUG_ASSERT(0);
}
- return 0;
+ rc= 0;
+
+end:
+ if (unlikely(not_reported_error))
+ {
+ DBUG_ASSERT(rc);
+ table->file->print_error(not_reported_error, MYF(0));
+ }
+ return rc;
+}
+
+bool select_unit::send_eof()
+{
+ if (step != INTERSECT_TYPE ||
+ (thd->lex->current_select->next_select() &&
+ thd->lex->current_select->next_select()->linkage == INTERSECT_TYPE))
+ {
+ /*
+ it is not INTESECT or next SELECT in the sequence is INTERSECT so no
+ need filtering (the last INTERSECT in this sequence of intersects will
+ filter).
+ */
+ return 0;
+ }
+
+ /*
+ It is last select in the sequence of INTERSECTs so we should filter out
+ all records except marked with actual counter.
+
+ TODO: as optimization for simple case this could be moved to
+ 'fake_select' WHERE condition
+ */
+ handler *file= table->file;
+ int error;
+
+ if (unlikely(file->ha_rnd_init_with_error(1)))
+ return 1;
+
+ do
+ {
+ if (unlikely(error= file->ha_rnd_next(table->record[0])))
+ {
+ if (error == HA_ERR_END_OF_FILE)
+ {
+ error= 0;
+ break;
+ }
+ break;
+ }
+ if (table->field[0]->val_int() != curr_step)
+ error= file->ha_delete_tmp_row(table->record[0]);
+ } while (likely(!error));
+ file->ha_rnd_end();
+
+ if (unlikely(error))
+ table->file->print_error(error, MYF(0));
+
+ return(MY_TEST(error));
}
int select_union_recursive::send_data(List<Item> &values)
{
- int rc= select_union::send_data(values);
+ int rc= select_unit::send_data(values);
- if (write_err != HA_ERR_FOUND_DUPP_KEY &&
+ if (rc == 0 &&
+ write_err != HA_ERR_FOUND_DUPP_KEY &&
write_err != HA_ERR_FOUND_DUPP_UNIQUE)
{
int err;
@@ -123,16 +317,10 @@ int select_union_recursive::send_data(List<Item> &values)
}
-bool select_union::send_eof()
-{
- return 0;
-}
-
-
-bool select_union::flush()
+bool select_unit::flush()
{
int error;
- if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
+ if (unlikely((error=table->file->extra(HA_EXTRA_NO_CACHE))))
{
table->file->print_error(error, MYF(0));
return 1;
@@ -140,11 +328,12 @@ bool select_union::flush()
return 0;
}
+
/*
Create a temporary table to store the result of select_union.
SYNOPSIS
- select_union::create_result_table()
+ select_unit::create_result_table()
thd thread handle
column_types a list of items used to define columns of the
temporary table
@@ -155,6 +344,7 @@ bool select_union::flush()
bit_fields_as_long convert bit fields to ulonglong
create_table whether to physically create result table
keep_row_order keep rows in order as they were inserted
+ hidden number of hidden fields (for INTERSECT)
DESCRIPTION
Create a temporary table that is used to store the result of a UNION,
@@ -166,16 +356,18 @@ bool select_union::flush()
*/
bool
-select_union::create_result_table(THD *thd_arg, List<Item> *column_types,
+select_unit::create_result_table(THD *thd_arg, List<Item> *column_types,
bool is_union_distinct, ulonglong options,
- const char *alias,
+ const LEX_CSTRING *alias,
bool bit_fields_as_long, bool create_table,
- bool keep_row_order)
+ bool keep_row_order,
+ uint hidden)
{
DBUG_ASSERT(table == 0);
tmp_table_param.init();
tmp_table_param.field_count= column_types->elements;
tmp_table_param.bit_fields_as_long= bit_fields_as_long;
+ tmp_table_param.hidden_field_count= hidden;
if (! (table= create_tmp_table(thd_arg, &tmp_table_param, *column_types,
(ORDER*) 0, is_union_distinct, 1,
@@ -200,20 +392,22 @@ select_union_recursive::create_result_table(THD *thd_arg,
List<Item> *column_types,
bool is_union_distinct,
ulonglong options,
- const char *alias,
+ const LEX_CSTRING *alias,
bool bit_fields_as_long,
bool create_table,
- bool keep_row_order)
+ bool keep_row_order,
+ uint hidden)
{
- if (select_union::create_result_table(thd_arg, column_types,
- is_union_distinct, options,
- "", bit_fields_as_long,
- create_table, keep_row_order))
+ if (select_unit::create_result_table(thd_arg, column_types,
+ is_union_distinct, options,
+ &empty_clex_str, bit_fields_as_long,
+ create_table, keep_row_order,
+ hidden))
return true;
if (! (incr_table= create_tmp_table(thd_arg, &tmp_table_param, *column_types,
(ORDER*) 0, false, 1,
- options, HA_POS_ERROR, "",
+ options, HA_POS_ERROR, &empty_clex_str,
true, keep_row_order)))
return true;
@@ -247,7 +441,7 @@ select_union_recursive::create_result_table(THD *thd_arg,
tables of JOIN - exec_tmp_table_[1 | 2].
*/
-void select_union::cleanup()
+void select_unit::cleanup()
{
table->file->extra(HA_EXTRA_RESET_STATE);
table->file->ha_delete_all_rows();
@@ -258,7 +452,7 @@ void select_union_recursive::cleanup()
{
if (table)
{
- select_union::cleanup();
+ select_unit::cleanup();
free_tmp_table(thd, table);
}
@@ -305,14 +499,14 @@ void select_union_recursive::cleanup()
bool select_union_direct::change_result(select_result *new_result)
{
result= new_result;
- return (result->prepare(unit->types, unit) || result->prepare2());
+ return (result->prepare(unit->types, unit) || result->prepare2(NULL));
}
bool select_union_direct::postponed_prepare(List<Item> &types)
{
if (result != NULL)
- return (result->prepare(types, unit) || result->prepare2());
+ return (result->prepare(types, unit) || result->prepare2(NULL));
else
return false;
}
@@ -353,7 +547,7 @@ int select_union_direct::send_data(List<Item> &items)
send_records++;
fill_record(thd, table, table->field, items, true, false);
- if (thd->is_error())
+ if (unlikely(thd->is_error()))
return true; /* purecov: inspected */
return result->send_data(unit->item_list);
@@ -438,20 +632,208 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd_arg,
}
+bool st_select_lex_unit::prepare_join(THD *thd_arg, SELECT_LEX *sl,
+ select_result *tmp_result,
+ ulong additional_options,
+ bool is_union_select)
+{
+ DBUG_ENTER("st_select_lex_unit::prepare_join");
+ TABLE_LIST *derived= sl->master_unit()->derived;
+ bool can_skip_order_by;
+ sl->options|= SELECT_NO_UNLOCK;
+ JOIN *join= new JOIN(thd_arg, sl->item_list,
+ (sl->options | thd_arg->variables.option_bits |
+ additional_options),
+ tmp_result);
+ if (!join)
+ DBUG_RETURN(true);
+
+ thd_arg->lex->current_select= sl;
+
+ can_skip_order_by= is_union_select && !(sl->braces && sl->explicit_limit);
+
+ saved_error= join->prepare(sl->table_list.first,
+ sl->with_wild,
+ (derived && derived->merged ? NULL : sl->where),
+ (can_skip_order_by ? 0 :
+ sl->order_list.elements) +
+ sl->group_list.elements,
+ can_skip_order_by ?
+ NULL : sl->order_list.first,
+ can_skip_order_by,
+ sl->group_list.first,
+ sl->having,
+ (is_union_select ? NULL :
+ thd_arg->lex->proc_list.first),
+ sl, this);
+
+ /* There are no * in the statement anymore (for PS) */
+ sl->with_wild= 0;
+ last_procedure= join->procedure;
+
+ if (unlikely(saved_error || (saved_error= thd_arg->is_fatal_error)))
+ DBUG_RETURN(true);
+ /*
+ Remove all references from the select_lex_units to the subqueries that
+ are inside the ORDER BY clause.
+ */
+ if (can_skip_order_by)
+ {
+ for (ORDER *ord= (ORDER *)sl->order_list.first; ord; ord= ord->next)
+ {
+ (*ord->item)->walk(&Item::eliminate_subselect_processor, FALSE, NULL);
+ }
+ }
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Aggregate data type handlers for the "count" leftmost UNION parts.
+*/
+bool st_select_lex_unit::join_union_type_handlers(THD *thd_arg,
+ Type_holder *holders,
+ uint count)
+{
+ DBUG_ENTER("st_select_lex_unit::join_union_type_handlers");
+ SELECT_LEX *first_sl= first_select(), *sl= first_sl;
+ for (uint i= 0; i < count ; sl= sl->next_select(), i++)
+ {
+ Item *item;
+ List_iterator_fast<Item> it(sl->item_list);
+ for (uint pos= 0; (item= it++); pos++)
+ {
+ const Type_handler *item_type_handler= item->real_type_handler();
+ if (sl == first_sl)
+ holders[pos].set_handler(item_type_handler);
+ else
+ {
+ DBUG_ASSERT(first_sl->item_list.elements == sl->item_list.elements);
+ if (holders[pos].aggregate_for_result(item_type_handler))
+ {
+ my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0),
+ holders[pos].type_handler()->name().ptr(),
+ item_type_handler->name().ptr(),
+ "UNION");
+ DBUG_RETURN(true);
+ }
+ }
+ }
+ }
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Aggregate data type attributes for the "count" leftmost UNION parts.
+*/
+bool st_select_lex_unit::join_union_type_attributes(THD *thd_arg,
+ Type_holder *holders,
+ uint count)
+{
+ DBUG_ENTER("st_select_lex_unit::join_union_type_attributes");
+ SELECT_LEX *sl, *first_sl= first_select();
+ uint item_pos;
+ for (uint pos= 0; pos < first_sl->item_list.elements; pos++)
+ {
+ if (holders[pos].alloc_arguments(thd_arg, count))
+ DBUG_RETURN(true);
+ }
+ for (item_pos= 0, sl= first_sl ;
+ item_pos < count;
+ sl= sl->next_select(), item_pos++)
+ {
+ Item *item_tmp;
+ List_iterator_fast<Item> itx(sl->item_list);
+ for (uint holder_pos= 0 ; (item_tmp= itx++); holder_pos++)
+ {
+ /*
+ If the outer query has a GROUP BY clause, an outer reference to this
+ query block may have been wrapped in a Item_outer_ref, which has not
+ been fixed yet. An Item_type_holder must be created based on a fixed
+ Item, so use the inner Item instead.
+ */
+ DBUG_ASSERT(item_tmp->fixed ||
+ (item_tmp->type() == Item::REF_ITEM &&
+ ((Item_ref *)(item_tmp))->ref_type() ==
+ Item_ref::OUTER_REF));
+ if (!item_tmp->fixed)
+ item_tmp= item_tmp->real_item();
+ holders[holder_pos].add_argument(item_tmp);
+ }
+ }
+ for (uint pos= 0; pos < first_sl->item_list.elements; pos++)
+ {
+ if (holders[pos].aggregate_attributes(thd_arg))
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(false);
+}
+
+/**
+ Join data types for the leftmost "count" UNION parts
+ and store corresponding Item_type_holder's into "types".
+*/
+bool st_select_lex_unit::join_union_item_types(THD *thd_arg,
+ List<Item> &types,
+ uint count)
+{
+ DBUG_ENTER("st_select_lex_unit::join_union_select_list_types");
+ SELECT_LEX *first_sl= first_select();
+ Type_holder *holders;
+
+ if (!(holders= new (thd_arg->mem_root)
+ Type_holder[first_sl->item_list.elements]) ||
+ join_union_type_handlers(thd_arg, holders, count) ||
+ join_union_type_attributes(thd_arg, holders, count))
+ DBUG_RETURN(true);
-bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
+ bool is_recursive= with_element && with_element->is_recursive;
+ types.empty();
+ List_iterator_fast<Item> it(first_sl->item_list);
+ Item *item_tmp;
+ for (uint pos= 0; (item_tmp= it++); pos++)
+ {
+ /*
+ SQL standard requires forced nullability only for
+ recursive columns. However type aggregation in our
+ implementation so far does not differentiate between
+ recursive and non-recursive columns of a recursive CTE.
+ TODO: this should be fixed.
+ */
+ bool pos_maybe_null= is_recursive ? true : holders[pos].get_maybe_null();
+
+ /* Error's in 'new' will be detected after loop */
+ types.push_back(new (thd_arg->mem_root)
+ Item_type_holder(thd_arg,
+ item_tmp,
+ holders[pos].type_handler(),
+ &holders[pos]/*Type_all_attributes*/,
+ pos_maybe_null));
+ }
+ if (unlikely(thd_arg->is_fatal_error))
+ DBUG_RETURN(true); // out of memory
+ DBUG_RETURN(false);
+}
+
+
+bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
+ select_result *sel_result,
ulong additional_options)
{
- SELECT_LEX *lex_select_save= thd_arg->lex->current_select;
+ SELECT_LEX *lex_select_save= thd->lex->current_select;
SELECT_LEX *sl, *first_sl= first_select();
bool is_recursive= with_element && with_element->is_recursive;
bool is_rec_result_table_created= false;
+ uint union_part_count= 0;
select_result *tmp_result;
bool is_union_select;
+ bool have_except= FALSE, have_intersect= FALSE;
bool instantiate_tmp_table= false;
+ bool single_tvc= !first_sl->next_select() && first_sl->tvc &&
+ !fake_select_lex;
DBUG_ENTER("st_select_lex_unit::prepare");
- DBUG_ASSERT(thd == thd_arg);
DBUG_ASSERT(thd == current_thd);
if (is_recursive && (sl= first_sl->next_select()))
@@ -493,16 +875,26 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
/* fast reinit for EXPLAIN */
for (sl= first_sl; sl; sl= sl->next_select())
{
- sl->join->result= result;
- select_limit_cnt= HA_POS_ERROR;
- offset_limit_cnt= 0;
- if (!sl->join->procedure &&
- result->prepare(sl->join->fields_list, this))
+ if (sl->tvc)
{
- DBUG_RETURN(TRUE);
+ sl->tvc->result= result;
+ if (result->prepare(sl->item_list, this))
+ DBUG_RETURN(TRUE);
+ sl->tvc->select_options|= SELECT_DESCRIBE;
+ }
+ else
+ {
+ sl->join->result= result;
+ select_limit_cnt= HA_POS_ERROR;
+ offset_limit_cnt= 0;
+ if (!sl->join->procedure &&
+ result->prepare(sl->join->fields_list, this))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ sl->join->select_options|= SELECT_DESCRIBE;
+ sl->join->reinit();
}
- sl->join->select_options|= SELECT_DESCRIBE;
- sl->join->reinit();
}
}
DBUG_RETURN(FALSE);
@@ -510,21 +902,36 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
prepared= 1;
saved_error= FALSE;
- thd_arg->lex->current_select= sl= first_sl;
+ thd->lex->current_select= sl= first_sl;
found_rows_for_union= first_sl->options & OPTION_FOUND_ROWS;
- is_union_select= is_union() || fake_select_lex;
+ is_union_select= is_unit_op() || fake_select_lex || single_tvc;
+ for (SELECT_LEX *s= first_sl; s; s= s->next_select())
+ {
+ switch (s->linkage)
+ {
+ case INTERSECT_TYPE:
+ have_intersect= TRUE;
+ break;
+ case EXCEPT_TYPE:
+ have_except= TRUE;
+ break;
+ default:
+ break;
+ }
+ }
/* Global option */
if (is_union_select || is_recursive)
{
- if (is_union() && !union_needs_tmp_table())
+ if ((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_arg->mem_root) select_union_direct(thd_arg, sel_result,
+ new (thd->mem_root) select_union_direct(thd, sel_result,
last)))
goto err; /* purecov: inspected */
fake_select_lex= NULL;
@@ -533,11 +940,11 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
else
{
if (!is_recursive)
- union_result= new (thd_arg->mem_root) select_union(thd_arg);
+ union_result= new (thd->mem_root) select_unit(thd);
else
{
with_element->rec_result=
- new (thd_arg->mem_root) select_union_recursive(thd_arg);
+ new (thd->mem_root) select_union_recursive(thd);
union_result= with_element->rec_result;
if (fake_select_lex)
{
@@ -561,14 +968,60 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
tmp_result= sel_result;
sl->context.resolve_in_select_list= TRUE;
+
+ if (!is_union_select && !is_recursive)
+ {
+ if (sl->tvc)
+ {
+ if (sl->tvc->prepare(thd, sl, tmp_result, this))
+ goto err;
+ }
+ else
+ {
+ if (prepare_join(thd, first_sl, tmp_result, additional_options,
+ is_union_select))
+ goto err;
+
+ if (derived_arg && derived_arg->table &&
+ derived_arg->derived_type == VIEW_ALGORITHM_MERGE &&
+ derived_arg->table->versioned())
+ {
+ /* Got versioning conditions (see vers_setup_conds()), need to update
+ derived_arg. */
+ derived_arg->where= first_sl->where;
+ }
+ }
+ types= first_sl->item_list;
+ goto cont;
+ }
- for (;sl; sl= sl->next_select())
- {
- bool can_skip_order_by;
- sl->options|= SELECT_NO_UNLOCK;
- JOIN *join= new JOIN(thd_arg, sl->item_list,
- sl->options | thd_arg->variables.option_bits | additional_options,
- tmp_result);
+ for (;sl; sl= sl->next_select(), union_part_count++)
+ {
+ if (sl->tvc)
+ {
+ if (sl->tvc->to_be_wrapped_as_with_tail() &&
+ !(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW))
+
+ {
+ st_select_lex *wrapper_sl= wrap_tvc_with_tail(thd, sl);
+ if (!wrapper_sl)
+ goto err;
+
+ if (sl == first_sl)
+ first_sl= wrapper_sl;
+ sl= wrapper_sl;
+
+ if (prepare_join(thd, sl, tmp_result, additional_options,
+ is_union_select))
+ goto err;
+ }
+ else if (sl->tvc->prepare(thd, sl, tmp_result, this))
+ goto err;
+ }
+ else if (prepare_join(thd, sl, tmp_result, additional_options,
+ is_union_select))
+ goto err;
+
/*
setup_tables_done_option should be set only for very first SELECT,
because it protect from secont setup_tables call for select-like non
@@ -576,136 +1029,68 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
SELECT (for union it can be only INSERT ... SELECT).
*/
additional_options&= ~OPTION_SETUP_TABLES_DONE;
- if (!join)
- goto err;
-
- thd_arg->lex->current_select= sl;
-
- can_skip_order_by= is_union_select && !(sl->braces && sl->explicit_limit);
-
- saved_error= join->prepare(sl->table_list.first,
- sl->with_wild,
- sl->where,
- (can_skip_order_by ? 0 :
- sl->order_list.elements) +
- sl->group_list.elements,
- can_skip_order_by ?
- NULL : sl->order_list.first,
- can_skip_order_by,
- sl->group_list.first,
- sl->having,
- (is_union_select ? NULL :
- thd_arg->lex->proc_list.first),
- sl, this);
-
- /* There are no * in the statement anymore (for PS) */
- sl->with_wild= 0;
- last_procedure= join->procedure;
-
- if (saved_error || (saved_error= thd_arg->is_fatal_error))
- goto err;
- /*
- Remove all references from the select_lex_units to the subqueries that
- are inside the ORDER BY clause.
- */
- if (can_skip_order_by)
- {
- for (ORDER *ord= (ORDER *)sl->order_list.first; ord; ord= ord->next)
- {
- (*ord->item)->walk(&Item::eliminate_subselect_processor, FALSE, NULL);
- }
- }
/*
Use items list of underlaid select for derived tables to preserve
information about fields lengths and exact types
*/
- if (!is_union_select && !is_recursive)
- types= first_sl->item_list;
- else if (sl == first_sl)
+ if (sl == first_sl)
{
if (with_element)
{
if (with_element->rename_columns_of_derived_unit(thd, this))
- goto err;
+ goto err;
if (check_duplicate_names(thd, sl->item_list, 0))
goto err;
}
- types.empty();
- List_iterator_fast<Item> it(sl->item_list);
- Item *item_tmp;
- while ((item_tmp= it++))
- {
- /*
- If the outer query has a GROUP BY clause, an outer reference to this
- query block may have been wrapped in a Item_outer_ref, which has not
- been fixed yet. An Item_type_holder must be created based on a fixed
- Item, so use the inner Item instead.
- */
- DBUG_ASSERT(item_tmp->fixed ||
- (item_tmp->type() == Item::REF_ITEM &&
- ((Item_ref *)(item_tmp))->ref_type() ==
- Item_ref::OUTER_REF));
- if (!item_tmp->fixed)
- item_tmp= item_tmp->real_item();
-
- /* Error's in 'new' will be detected after loop */
- types.push_back(new (thd_arg->mem_root)
- Item_type_holder(thd_arg, item_tmp));
- }
-
- if (thd_arg->is_fatal_error)
- goto err; // out of memory
}
else
{
- if (types.elements != sl->item_list.elements)
+ if (first_sl->item_list.elements != sl->item_list.elements)
{
- my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
- ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0));
- goto err;
- }
- if (!is_rec_result_table_created)
- {
- List_iterator_fast<Item> it(sl->item_list);
- List_iterator_fast<Item> tp(types);
- Item *type, *item_tmp;
- while ((type= tp++, item_tmp= it++))
- {
- if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp))
- DBUG_RETURN(TRUE);
- }
+ my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
+ ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),
+ MYF(0));
+ goto err;
}
}
if (is_recursive)
{
if (!with_element->is_anchor(sl))
sl->uncacheable|= UNCACHEABLE_UNITED;
- if(!is_rec_result_table_created &&
- (!sl->next_select() ||
- sl->next_select() == with_element->first_recursive))
+ if (!is_rec_result_table_created &&
+ (!sl->next_select() ||
+ sl->next_select() == with_element->first_recursive))
{
ulonglong create_options;
- create_options= (first_sl->options | thd_arg->variables.option_bits |
+ create_options= (first_sl->options | thd->variables.option_bits |
TMP_TABLE_ALL_COLUMNS);
+ // Join data types for all non-recursive parts of a recursive UNION
+ 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),
- create_options, derived->alias,
- false,
- instantiate_tmp_table, false))
+ create_options,
+ &derived_arg->alias, false,
+ instantiate_tmp_table, false,
+ 0))
goto err;
- if (!derived->table)
+ if (!derived_arg->table)
{
- derived->table= with_element->rec_result->rec_tables.head();
- if (derived->derived_result)
- derived->derived_result->table= derived->table;
+ derived_arg->table= with_element->rec_result->rec_tables.head();
+ if (derived_arg->derived_result)
+ derived_arg->derived_result->table= derived_arg->table;
}
with_element->mark_as_with_prepared_anchor();
is_rec_result_table_created= true;
}
}
}
+ // In case of a non-recursive UNION, join data types for all UNION parts.
+ if (!is_recursive && join_union_item_types(thd, types, union_part_count))
+ goto err;
+cont:
/*
If the query is using select_union_direct, we have postponed
preparation of the underlying select_result until column types
@@ -765,7 +1150,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
}
- create_options= (first_sl->options | thd_arg->variables.option_bits |
+ create_options= (first_sl->options | thd->variables.option_bits |
TMP_TABLE_ALL_COLUMNS);
/*
Force the temporary table to be a MyISAM table if we're going to use
@@ -776,12 +1161,48 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
if (global_parameters()->ftfunc_list->elements)
create_options= create_options | TMP_TABLE_FORCE_MYISAM;
-
- if (!is_recursive &&
- union_result->create_result_table(thd, &types, MY_TEST(union_distinct),
- create_options, "", false,
- instantiate_tmp_table, false))
- goto err;
+ if (!is_recursive)
+ {
+ uint hidden= 0;
+ if (have_intersect)
+ {
+ hidden= 1;
+ if (!intersect_mark)
+ {
+ /*
+ For intersect we add a hidden column first that contains
+ the current select number of the time when the row was
+ added to the temporary table
+ */
+
+ Query_arena *arena, backup_arena;
+ arena= thd->activate_stmt_arena_if_needed(&backup_arena);
+
+ intersect_mark= new (thd->mem_root) Item_int(thd, 0);
+
+ if (arena)
+ thd->restore_active_arena(arena, &backup_arena);
+
+ if (!intersect_mark)
+ goto err;
+ }
+ else
+ intersect_mark->value= 0; //reset
+ types.push_front(union_result->intersect_mark= intersect_mark);
+ union_result->intersect_mark->name.str= "___";
+ union_result->intersect_mark->name.length= 3;
+ }
+ bool error=
+ union_result->create_result_table(thd, &types,
+ MY_TEST(union_distinct),
+ create_options, &empty_clex_str, false,
+ instantiate_tmp_table, false,
+ hidden);
+ if (intersect_mark)
+ types.pop();
+ if (unlikely(error))
+ goto err;
+ }
if (fake_select_lex && !fake_select_lex->first_cond_optimization)
{
save_tablenr= result_table_list.tablenr_exec;
@@ -789,8 +1210,10 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
save_maybe_null= result_table_list.maybe_null_exec;
}
bzero((char*) &result_table_list, sizeof(result_table_list));
- result_table_list.db= (char*) "";
- result_table_list.table_name= result_table_list.alias= (char*) "union";
+ result_table_list.db.str= (char*) "";
+ result_table_list.db.length= 0;
+ result_table_list.table_name.str= result_table_list.alias.str= "union";
+ result_table_list.table_name.length= result_table_list.alias.length= sizeof("union")-1;
result_table_list.table= table= union_result->table;
if (fake_select_lex && !fake_select_lex->first_cond_optimization)
{
@@ -799,7 +1222,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
result_table_list.maybe_null_exec= save_maybe_null;
}
- thd_arg->lex->current_select= lex_select_save;
+ thd->lex->current_select= lex_select_save;
if (!item_list.elements)
{
Query_arena *arena, backup_arena;
@@ -807,11 +1230,14 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
arena= thd->activate_stmt_arena_if_needed(&backup_arena);
saved_error= table->fill_item_list(&item_list);
+ // Item_list is inherited from 'types', so there could be the counter
+ if (intersect_mark)
+ item_list.pop(); // remove intersect counter
if (arena)
thd->restore_active_arena(arena, &backup_arena);
- if (saved_error)
+ if (unlikely(saved_error))
goto err;
if (fake_select_lex != NULL &&
@@ -836,7 +1262,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
*/
fake_select_lex->item_list= item_list;
- thd_arg->lex->current_select= fake_select_lex;
+ thd->lex->current_select= fake_select_lex;
/*
We need to add up n_sum_items in order to make the correct
@@ -851,7 +1277,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
We're in execution of a prepared statement or stored procedure:
reset field items to point at fields from the created temporary table.
*/
- table->reset_item_list(&item_list);
+ table->reset_item_list(&item_list, intersect_mark ? 1 : 0);
}
if (fake_select_lex != NULL &&
(thd->stmt_arena->is_stmt_prepare() ||
@@ -873,12 +1299,12 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
}
}
- thd_arg->lex->current_select= lex_select_save;
+ thd->lex->current_select= lex_select_save;
- DBUG_RETURN(saved_error || thd_arg->is_fatal_error);
+ DBUG_RETURN(saved_error || thd->is_fatal_error);
err:
- thd_arg->lex->current_select= lex_select_save;
+ thd->lex->current_select= lex_select_save;
(void) cleanup();
DBUG_RETURN(TRUE);
}
@@ -913,11 +1339,11 @@ bool st_select_lex_unit::optimize()
{
item->assigned(0); // We will reinit & rexecute unit
item->reset();
- if (table->is_created())
- {
- table->file->ha_delete_all_rows();
- table->file->info(HA_STATUS_VARIABLE);
- }
+ }
+ if (table->is_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))
@@ -927,6 +1353,20 @@ bool st_select_lex_unit::optimize()
}
for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select())
{
+ if (sl->tvc)
+ {
+ sl->tvc->select_options=
+ (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
+ if (sl->tvc->optimize(thd))
+ {
+ thd->lex->current_select= lex_select_save;
+ DBUG_RETURN(TRUE);
+ }
+ if (derived)
+ sl->increase_derived_records(sl->tvc->get_records());
+ continue;
+ }
thd->lex->current_select= sl;
if (optimized)
@@ -950,14 +1390,14 @@ bool st_select_lex_unit::optimize()
we don't calculate found_rows() per union part.
Otherwise, SQL_CALC_FOUND_ROWS should be done on all sub parts.
*/
- sl->join->select_options=
+ sl->join->select_options=
(select_limit_cnt == HA_POS_ERROR || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
saved_error= sl->join->optimize();
}
- if (saved_error)
+ if (unlikely(saved_error))
{
thd->lex->current_select= lex_select_save;
DBUG_RETURN(saved_error);
@@ -995,9 +1435,20 @@ bool st_select_lex_unit::exec()
if (!saved_error && !was_executed)
save_union_explain(thd->lex->explain);
- if (saved_error)
+ if (unlikely(saved_error))
DBUG_RETURN(saved_error);
+ if (union_result)
+ {
+ union_result->init();
+ if (uncacheable & UNCACHEABLE_DEPENDENT &&
+ union_result->table && union_result->table->is_created())
+ {
+ union_result->table->file->ha_delete_all_rows();
+ union_result->table->file->ha_enable_indexes(HA_KEY_SWITCH_ALL);
+ }
+ }
+
if (uncacheable || !item || !item->assigned() || describe)
{
if (!fake_select_lex && !(with_element && with_element->is_recursive))
@@ -1006,6 +1457,8 @@ bool st_select_lex_unit::exec()
{
ha_rows records_at_start= 0;
thd->lex->current_select= sl;
+ if (union_result)
+ union_result->change_select();
if (fake_select_lex)
{
if (sl != &thd->lex->select_lex)
@@ -1032,28 +1485,42 @@ bool st_select_lex_unit::exec()
we don't calculate found_rows() per union part.
Otherwise, SQL_CALC_FOUND_ROWS should be done on all sub parts.
*/
- sl->join->select_options=
- (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
- sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
- saved_error= sl->join->optimize();
+ if (sl->tvc)
+ {
+ sl->tvc->select_options=
+ (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
+ saved_error= sl->tvc->optimize(thd);
+ }
+ else
+ {
+ sl->join->select_options=
+ (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
+ saved_error= sl->join->optimize();
+ }
}
- if (!saved_error)
+ if (likely(!saved_error))
{
records_at_start= table->file->stats.records;
- sl->join->exec();
+ if (sl->tvc)
+ sl->tvc->exec(sl);
+ else
+ sl->join->exec();
if (sl == union_distinct && !(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(HA_KEY_SWITCH_ALL))
+ if (unlikely(table->file->ha_disable_indexes(HA_KEY_SWITCH_ALL)))
DBUG_RETURN(TRUE);
table->no_keyread=1;
}
- saved_error= sl->join->error;
+ if (!sl->tvc)
+ saved_error= sl->join->error;
offset_limit_cnt= (ha_rows)(sl->offset_limit ?
sl->offset_limit->val_uint() :
0);
- if (!saved_error)
+ if (likely(!saved_error))
{
examined_rows+= thd->get_examined_row_count();
thd->set_examined_row_count(0);
@@ -1064,7 +1531,7 @@ bool st_select_lex_unit::exec()
}
}
}
- if (saved_error)
+ if (unlikely(saved_error))
{
thd->lex->current_select= lex_select_save;
DBUG_RETURN(saved_error);
@@ -1073,7 +1540,7 @@ bool st_select_lex_unit::exec()
{
/* Needed for the following test and for records_at_start in next loop */
int error= table->file->info(HA_STATUS_VARIABLE);
- if(error)
+ if (unlikely(error))
{
table->file->print_error(error, MYF(0));
DBUG_RETURN(1);
@@ -1119,7 +1586,8 @@ bool st_select_lex_unit::exec()
*/
thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;
- if (fake_select_lex != NULL && !thd->is_fatal_error) // Check if EOM
+ // Check if EOM
+ if (fake_select_lex != NULL && likely(!thd->is_fatal_error))
{
/* Send result to 'result' */
saved_error= true;
@@ -1138,8 +1606,9 @@ bool st_select_lex_unit::exec()
don't let it allocate the join. Perhaps this is because we need
some special parameter values passed to join constructor?
*/
- if (!(fake_select_lex->join= new JOIN(thd, item_list,
- fake_select_lex->options, result)))
+ if (unlikely(!(fake_select_lex->join=
+ new JOIN(thd, item_list, fake_select_lex->options,
+ result))))
{
fake_select_lex->table_list.empty();
goto err;
@@ -1205,7 +1674,7 @@ bool st_select_lex_unit::exec()
}
fake_select_lex->table_list.empty();
- if (!saved_error)
+ if (likely(!saved_error))
{
thd->limit_found_rows = (ulonglong)table->file->stats.records + add_rows;
thd->inc_examined_row_count(examined_rows);
@@ -1281,26 +1750,45 @@ bool st_select_lex_unit::exec_recursive()
if (with_element->with_anchor)
end= with_element->first_recursive;
}
- else if ((saved_error= incr_table->file->ha_delete_all_rows()))
+ else if (unlikely((saved_error= incr_table->file->ha_delete_all_rows())))
goto err;
for (st_select_lex *sl= start ; sl != end; sl= sl->next_select())
{
+ if (with_element->level)
+ {
+ for (TABLE_LIST *derived= with_element->derived_with_rec_ref.first;
+ derived;
+ derived= derived->next_with_rec_ref)
+ {
+ if (derived->is_materialized_derived())
+ {
+ if (derived->table->is_created())
+ derived->table->file->ha_delete_all_rows();
+ derived->table->reginfo.join_tab->preread_init_done= false;
+ }
+ }
+ }
thd->lex->current_select= sl;
set_limit(sl);
- sl->join->exec();
- saved_error= sl->join->error;
- if (!saved_error)
+ if (sl->tvc)
+ sl->tvc->exec(sl);
+ else
+ {
+ sl->join->exec();
+ 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())
+ if (unlikely(union_result->flush()))
{
thd->lex->current_select= lex_select_save;
DBUG_RETURN(1);
}
}
- if (saved_error)
+ if (unlikely(saved_error))
{
thd->lex->current_select= lex_select_save;
goto err;
@@ -1325,7 +1813,7 @@ bool st_select_lex_unit::exec_recursive()
if (!with_element->rec_result->first_rec_table_to_update)
with_element->rec_result->first_rec_table_to_update= rec_table;
if (with_element->level == 1 && rec_table->reginfo.join_tab)
- rec_table->reginfo.join_tab->preread_init_done= true;
+ rec_table->reginfo.join_tab->preread_init_done= true;
}
for (Item_subselect *sq= with_element->sq_with_rec_ref.first;
sq;
@@ -1344,7 +1832,7 @@ err:
bool st_select_lex_unit::cleanup()
{
- int error= 0;
+ bool error= 0;
DBUG_ENTER("st_select_lex_unit::cleanup");
if (cleaned)
@@ -1382,6 +1870,7 @@ bool st_select_lex_unit::cleanup()
DBUG_RETURN(FALSE);
}
}
+ columns_are_renamed= false;
cleaned= 1;
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
@@ -1438,7 +1927,7 @@ bool st_select_lex_unit::cleanup()
void st_select_lex_unit::reinit_exec_mechanism()
{
- prepared= optimized= executed= 0;
+ prepared= optimized= optimized_2= executed= 0;
optimize_started= 0;
if (with_element && with_element->is_recursive)
with_element->reset_recursive_for_exec();
@@ -1497,7 +1986,7 @@ bool st_select_lex_unit::change_result(select_result_interceptor *new_result,
List<Item> *st_select_lex_unit::get_column_types(bool for_cursor)
{
SELECT_LEX *sl= first_select();
- bool is_procedure= MY_TEST(sl->join->procedure);
+ bool is_procedure= !sl->tvc && sl->join->procedure ;
if (is_procedure)
{
@@ -1507,7 +1996,7 @@ List<Item> *st_select_lex_unit::get_column_types(bool for_cursor)
}
- if (is_union())
+ if (is_unit_op())
{
DBUG_ASSERT(prepared);
/* Types are generated during prepare */
@@ -1532,6 +2021,7 @@ bool st_select_lex::cleanup()
cleanup_order(order_list.first);
cleanup_order(group_list.first);
+ cleanup_ftfuncs(this);
if (join)
{
@@ -1589,3 +2079,43 @@ void st_select_lex_unit::set_unique_exclude()
}
}
}
+
+/**
+ @brief
+ Check if the derived table is guaranteed to have distinct rows because of
+ UNION operations used to populate it.
+
+ @detail
+ UNION operation removes duplicate rows from its output. That is, a query like
+
+ select * from t1 UNION select * from t2
+
+ will not produce duplicate rows in its output, even if table t1 (and/or t2)
+ contain duplicate rows. EXCEPT and INTERSECT operations also have this
+ property.
+
+ On the other hand, UNION ALL operation doesn't remove duplicates. (The SQL
+ standard also defines EXCEPT ALL and INTERSECT ALL, but we don't support
+ them).
+
+ st_select_lex_unit computes its value left to right. That is, if there is
+ a st_select_lex_unit object describing
+
+ (select #1) OP1 (select #2) OP2 (select #3)
+
+ then ((select #1) OP1 (select #2)) is computed first, and OP2 is computed
+ second.
+
+ How can one tell if st_select_lex_unit is guaranteed to have distinct
+ output rows? This depends on whether the last operation was duplicate-
+ removing or not:
+ - UNION ALL is not duplicate-removing
+ - all other operations are duplicate-removing
+*/
+
+bool st_select_lex_unit::check_distinct_in_union()
+{
+ if (union_distinct && !union_distinct->next_select())
+ return true;
+ return false;
+}