diff options
Diffstat (limited to 'sql/sql_union.cc')
-rw-r--r-- | sql/sql_union.cc | 94 |
1 files changed, 67 insertions, 27 deletions
diff --git a/sql/sql_union.cc b/sql/sql_union.cc index f9fecae38c4..6e2e6e06ff7 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -1,4 +1,5 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. +/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. + Copyright (c) 2010, 2014, SkySQL Ab. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -11,8 +12,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* @@ -21,9 +21,13 @@ */ -#include "mysql_priv.h" +#include "sql_priv.h" +#include "unireg.h" +#include "sql_union.h" #include "sql_select.h" #include "sql_cursor.h" +#include "sql_base.h" // fill_record +#include "filesort.h" // filesort_free_buffers bool mysql_union(THD *thd, LEX *lex, select_result *result, SELECT_LEX_UNIT *unit, ulong setup_tables_done_option) @@ -33,8 +37,7 @@ bool mysql_union(THD *thd, LEX *lex, select_result *result, if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | setup_tables_done_option))) res= unit->exec(); - if (res || !thd->cursor || !thd->cursor->is_open()) - res|= unit->cleanup(); + res|= unit->cleanup(); DBUG_RETURN(res); } @@ -57,6 +60,8 @@ int select_union::send_data(List<Item> &values) unit->offset_limit_cnt--; return 0; } + if (thd->killed == ABORT_QUERY) + return 0; if (table->no_rows_with_nulls) table->null_catch_flags= CHECK_ROW_FOR_NULLS_TO_REJECT; fill_record(thd, table->field, values, TRUE, FALSE); @@ -79,13 +84,16 @@ int select_union::send_data(List<Item> &values) */ return -1; } + 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)) + write_err, 1, &is_duplicate)) return 1; + if (is_duplicate) + return -1; } return 0; } @@ -122,6 +130,7 @@ bool select_union::flush() table_alias name of the temporary table 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 DESCRIPTION Create a temporary table that is used to store the result of a UNION, @@ -136,7 +145,8 @@ bool select_union::create_result_table(THD *thd_arg, List<Item> *column_types, bool is_union_distinct, ulonglong options, const char *alias, - bool bit_fields_as_long, bool create_table) + bool bit_fields_as_long, bool create_table, + bool keep_row_order) { DBUG_ASSERT(table == 0); tmp_table_param.init(); @@ -145,8 +155,8 @@ select_union::create_result_table(THD *thd_arg, List<Item> *column_types, if (! (table= create_tmp_table(thd_arg, &tmp_table_param, *column_types, (ORDER*) 0, is_union_distinct, 1, - options, HA_POS_ERROR, (char*) alias, - !create_table))) + options, HA_POS_ERROR, alias, + !create_table, keep_row_order))) return TRUE; table->keys_in_use_for_query.clear_all(); @@ -289,7 +299,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, bool can_skip_order_by; sl->options|= SELECT_NO_UNLOCK; JOIN *join= new JOIN(thd_arg, sl->item_list, - sl->options | thd_arg->options | additional_options, + sl->options | thd_arg->variables.option_bits | additional_options, tmp_result); /* setup_tables_done_option should be set only for very first SELECT, @@ -305,18 +315,6 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, can_skip_order_by= is_union_select && !(sl->braces && sl->explicit_limit); - /* - 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); - } - } - saved_error= join->prepare(&sl->ref_pointer_array, sl->table_list.first, sl->with_wild, @@ -340,6 +338,18 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, 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 */ @@ -430,7 +440,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, } - create_options= (first_sl->options | thd_arg->options | + create_options= (first_sl->options | thd_arg->variables.option_bits | TMP_TABLE_ALL_COLUMNS); /* Force the temporary table to be a MyISAM table if we're going to use @@ -483,7 +493,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, init_prepare_fake_select_lex(thd, TRUE); /* Should be done only once (the only item_list per statement) */ DBUG_ASSERT(fake_select_lex->join == 0); - if (!(fake_select_lex->join= new JOIN(thd, item_list, thd->options, + if (!(fake_select_lex->join= new JOIN(thd, item_list, thd->variables.option_bits, result))) { fake_select_lex->table_list.empty(); @@ -531,6 +541,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, err: thd_arg->lex->current_select= lex_select_save; + (void) cleanup(); DBUG_RETURN(TRUE); } @@ -638,7 +649,10 @@ bool st_select_lex_unit::exec() { ha_rows records_at_start= 0; thd->lex->current_select= sl; - fake_select_lex->uncacheable|= sl->uncacheable; + if (sl != &thd->lex->select_lex) + fake_select_lex->uncacheable|= sl->uncacheable; + else + fake_select_lex->uncacheable= 0; { set_limit(sl); @@ -679,6 +693,11 @@ bool st_select_lex_unit::exec() 0); if (!saved_error) { + /* + Save the current examined row count locally and clear the global + counter, so that we can accumulate the number of evaluated rows for + the current query block. + */ examined_rows+= thd->examined_row_count; thd->examined_row_count= 0; if (union_result->flush()) @@ -712,6 +731,20 @@ bool st_select_lex_unit::exec() add_rows+= (ulonglong) (thd->limit_found_rows - (ulonglong) ((table->file->stats.records - records_at_start))); } + if (thd->killed == ABORT_QUERY) + { + /* + Stop execution of the remaining queries in the UNIONS, and produce + the current result. + */ + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT, + ER(ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT), + thd->accessed_rows_and_keys, + thd->lex->limit_rows_examined->val_uint()); + thd->reset_killed(); + break; + } } } @@ -720,6 +753,11 @@ bool st_select_lex_unit::exec() { List<Item_func_match> empty_list; empty_list.empty(); + /* + Disable LIMIT ROWS EXAMINED in order to produce the possibly incomplete + result of the UNION without interruption due to exceeding the limit. + */ + thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX; if (!thd->is_fatal_error) // Check if EOM { @@ -740,7 +778,7 @@ bool st_select_lex_unit::exec() fake_select_lex->options, result))) { fake_select_lex->table_list.empty(); - DBUG_RETURN(TRUE); + goto err; } fake_select_lex->join->no_const_tables= TRUE; @@ -812,6 +850,8 @@ bool st_select_lex_unit::exec() } } thd->lex->current_select= lex_select_save; +err: + thd->lex->set_limit_rows_examined(); DBUG_RETURN(saved_error); } |