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.cc798
1 files changed, 624 insertions, 174 deletions
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 19c9330481f..0149c2848c2 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"
@@ -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,192 @@ 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 (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 (thd->is_error())
- return 1;
+ {
+ 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 ((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 (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 (file->ha_rnd_init_with_error(1))
+ return 1;
+
+ do
+ {
+ error= file->ha_rnd_next(table->record[0]);
+ if (error)
+ {
+ if (error == HA_ERR_END_OF_FILE)
+ {
+ error= 0;
+ break;
+ }
+ if (unlikely(error == HA_ERR_RECORD_DELETED))
+ {
+ error= 0;
+ continue;
+ }
+ break;
+ }
+ if (table->field[0]->val_int() != curr_step)
+ error= file->ha_delete_tmp_row(table->record[0]);
+ } while (!error);
+ file->ha_rnd_end();
+
+ if (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,13 +322,7 @@ 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)))
@@ -140,11 +333,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 +349,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 +361,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 +397,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,
!create_table, keep_row_order)))
return true;
@@ -253,7 +452,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();
@@ -264,7 +463,7 @@ void select_union_recursive::cleanup()
{
if (table)
{
- select_union::cleanup();
+ select_unit::cleanup();
free_tmp_table(thd, table);
}
@@ -440,6 +639,180 @@ 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 (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);
+
+ types.empty();
+ List_iterator_fast<Item> it(first_sl->item_list);
+ Item *item_tmp;
+ for (uint pos= 0; (item_tmp= it++); pos++)
+ {
+ /* 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*/,
+ holders[pos].get_maybe_null()));
+ }
+ if (thd_arg->is_fatal_error)
+ DBUG_RETURN(true); // out of memory
+ DBUG_RETURN(false);
+}
bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
@@ -449,9 +822,12 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
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;
DBUG_ENTER("st_select_lex_unit::prepare");
DBUG_ASSERT(thd == thd_arg);
DBUG_ASSERT(thd == current_thd);
@@ -478,16 +854,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);
@@ -497,13 +883,28 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
thd_arg->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())
@@ -518,7 +919,7 @@ 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_arg->mem_root) select_unit(thd_arg);
else
{
with_element->rec_result=
@@ -535,14 +936,32 @@ 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_arg, sl, tmp_result, this))
+ goto err;
+ }
+ else if (prepare_join(thd_arg, first_sl, tmp_result, additional_options,
+ is_union_select))
+ goto err;
+ 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->prepare(thd_arg, sl, tmp_result, this))
+ goto err;
+ }
+ else if (prepare_join(thd_arg, 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
@@ -550,105 +969,29 @@ 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 (derived->with->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)
- {
- 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)
+ if (first_sl->item_list.elements != sl->item_list.elements)
{
- 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_arg, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),
+ MYF(0));
+ goto err;
}
}
if (is_recursive)
@@ -662,11 +1005,15 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
ulonglong create_options;
create_options= (first_sl->options | thd_arg->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,
+ create_options, &derived->alias,
false,
- instantiate_tmp_table, false))
+ instantiate_tmp_table, false,
+ 0))
goto err;
if (!derived->table)
derived->table= derived->derived_result->table=
@@ -676,7 +1023,11 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
}
}
}
+ // 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
@@ -747,12 +1098,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_arg->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 (error)
+ goto err;
+ }
if (fake_select_lex && !fake_select_lex->first_cond_optimization)
{
save_tablenr= result_table_list.tablenr_exec;
@@ -760,8 +1147,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)
{
@@ -778,6 +1167,9 @@ 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);
@@ -831,7 +1223,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);
}
}
@@ -875,11 +1267,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))
@@ -889,6 +1281,18 @@ 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);
+ }
+ continue;
+ }
thd->lex->current_select= sl;
if (optimized)
@@ -912,7 +1316,7 @@ 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;
@@ -960,6 +1364,17 @@ bool st_select_lex_unit::exec()
if (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))
@@ -968,6 +1383,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)
@@ -994,15 +1411,28 @@ 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)
{
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
@@ -1011,7 +1441,8 @@ bool st_select_lex_unit::exec()
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);
@@ -1240,9 +1671,28 @@ bool st_select_lex_unit::exec_recursive()
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;
- sl->join->exec();
- saved_error= sl->join->error;
+ if (sl->tvc)
+ sl->tvc->exec(sl);
+ else
+ {
+ sl->join->exec();
+ saved_error= sl->join->error;
+ }
if (!saved_error)
{
examined_rows+= thd->get_examined_row_count();
@@ -1278,7 +1728,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;
@@ -1297,7 +1747,7 @@ err:
bool st_select_lex_unit::cleanup()
{
- int error= 0;
+ bool error= 0;
DBUG_ENTER("st_select_lex_unit::cleanup");
if (cleaned)
@@ -1360,7 +1810,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();
@@ -1429,7 +1879,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 */