diff options
Diffstat (limited to 'sql/sql_union.cc')
-rw-r--r-- | sql/sql_union.cc | 966 |
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; +} |