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.cc94
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);
}