summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <timour@askmonty.org>2012-03-12 00:45:18 +0200
committerunknown <timour@askmonty.org>2012-03-12 00:45:18 +0200
commitecbd868f58db42e52a2dd8f46c6ee39dc4425cc5 (patch)
tree56fd7bd0b23d301d39db1b98d969e1fd5942a665 /sql
parent96a21ab324d02b53cbc7ee8d4585d66eda6dd429 (diff)
parent8aebd44e0ea9c4ae6f573f1ece27b276452122b8 (diff)
downloadmariadb-git-ecbd868f58db42e52a2dd8f46c6ee39dc4425cc5.tar.gz
Merged the implementation of MDEV-28 LIMIT ROWS EXAMINED into MariaDB 5.5.
Diffstat (limited to 'sql')
-rw-r--r--sql/filesort.cc7
-rw-r--r--sql/lex.h1
-rw-r--r--sql/share/errmsg-utf8.txt2
-rw-r--r--sql/signal_handler.cc4
-rw-r--r--sql/sql_class.cc29
-rw-r--r--sql/sql_class.h44
-rw-r--r--sql/sql_insert.cc2
-rw-r--r--sql/sql_lex.cc5
-rw-r--r--sql/sql_lex.h16
-rw-r--r--sql/sql_parse.cc1
-rw-r--r--sql/sql_select.cc94
-rw-r--r--sql/sql_union.cc25
-rw-r--r--sql/sql_view.cc11
-rw-r--r--sql/sql_yacc.yy21
14 files changed, 215 insertions, 47 deletions
diff --git a/sql/filesort.cc b/sql/filesort.cc
index f9fe0d731cd..b531c4d8026 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -329,13 +329,14 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
if (error)
{
int kill_errno= thd->killed_errno();
- DBUG_ASSERT(thd->is_error() || kill_errno);
+ DBUG_ASSERT(thd->is_error() || kill_errno || thd->killed == ABORT_QUERY);
my_printf_error(ER_FILSORT_ABORT,
"%s: %s",
MYF(0),
ER_THD(thd, ER_FILSORT_ABORT),
- kill_errno ? ER(kill_errno) : thd->stmt_da->message());
-
+ kill_errno ? ER(kill_errno) :
+ thd->killed == ABORT_QUERY ? "" : thd->stmt_da->message());
+
if (global_system_variables.log_warnings > 1)
{
sql_print_warning("%s, host: %s, user: %s, thread: %lu, query: %-.4096s",
diff --git a/sql/lex.h b/sql/lex.h
index bb8f5825879..9f4369630a0 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -212,6 +212,7 @@ static SYMBOL symbols[] = {
{ "EVENT", SYM(EVENT_SYM)},
{ "EVENTS", SYM(EVENTS_SYM)},
{ "EVERY", SYM(EVERY_SYM)},
+ { "EXAMINED", SYM(EXAMINED_SYM)},
{ "EXECUTE", SYM(EXECUTE_SYM)},
{ "EXISTS", SYM(EXISTS)},
{ "EXIT", SYM(EXIT_SYM)},
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 90cee00fde4..73357b6c4a2 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -6561,3 +6561,5 @@ ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SKIP_REPLICATION
eng "Cannot modify @@session.skip_replication inside a transaction"
ER_STORED_FUNCTION_PREVENTS_SWITCH_SKIP_REPLICATION
eng "Cannot modify @@session.skip_replication inside a stored function or trigger"
+ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT
+ eng "Query execution was interrupted. The query examined at least %llu rows, which exceeds LIMIT ROWS EXAMINED (%llu). The query result may be incomplete."
diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc
index dd50854d818..61b67c9d229 100644
--- a/sql/signal_handler.cc
+++ b/sql/signal_handler.cc
@@ -196,6 +196,10 @@ extern "C" sig_handler handle_fatal_signal(int sig)
case KILL_SERVER_HARD:
kreason= "KILL_SERVER";
break;
+ case ABORT_QUERY:
+ case ABORT_QUERY_HARD:
+ kreason= "ABORT_QUERY";
+ break;
}
my_safe_printf_stderr("%s", "\n"
"Trying to get some variables.\n"
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index f970f6a4f26..d7d0c8d3f68 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -737,6 +737,7 @@ THD::THD()
first_successful_insert_id_in_cur_stmt(0),
stmt_depends_on_first_successful_insert_id_in_prev_stmt(FALSE),
examined_row_count(0),
+ accessed_rows_and_keys(0),
warning_info(&main_warning_info),
stmt_da(&main_da),
global_disable_checkpoint(0),
@@ -1644,26 +1645,31 @@ void THD::disconnect()
int killed_errno(killed_state killed)
{
+ DBUG_ENTER("killed_errno");
+ DBUG_PRINT("enter", ("killed: %d", killed));
+
switch (killed) {
case NOT_KILLED:
case KILL_HARD_BIT:
- return 0; // Probably wrong usage
+ DBUG_RETURN(0); // Probably wrong usage
case KILL_BAD_DATA:
case KILL_BAD_DATA_HARD:
- return 0; // Not a real error
+ case ABORT_QUERY_HARD:
+ case ABORT_QUERY:
+ DBUG_RETURN(0); // Not a real error
case KILL_CONNECTION:
case KILL_CONNECTION_HARD:
case KILL_SYSTEM_THREAD:
case KILL_SYSTEM_THREAD_HARD:
- return ER_CONNECTION_KILLED;
+ DBUG_RETURN(ER_CONNECTION_KILLED);
case KILL_QUERY:
case KILL_QUERY_HARD:
- return ER_QUERY_INTERRUPTED;
+ DBUG_RETURN(ER_QUERY_INTERRUPTED);
case KILL_SERVER:
case KILL_SERVER_HARD:
- return ER_SERVER_SHUTDOWN;
+ DBUG_RETURN(ER_SERVER_SHUTDOWN);
}
- return 0; // Keep compiler happy
+ DBUG_RETURN(0); // Keep compiler happy
}
@@ -2248,6 +2254,8 @@ int select_send::send_data(List<Item> &items)
unit->offset_limit_cnt--;
DBUG_RETURN(FALSE);
}
+ if (thd->killed == ABORT_QUERY)
+ DBUG_RETURN(FALSE);
/*
We may be passing the control from mysqld to the client: release the
@@ -2541,6 +2549,8 @@ int select_export::send_data(List<Item> &items)
unit->offset_limit_cnt--;
DBUG_RETURN(0);
}
+ if (thd->killed == ABORT_QUERY)
+ DBUG_RETURN(0);
row_count++;
Item *item;
uint used_length=0,items_left=items.elements;
@@ -2796,6 +2806,9 @@ int select_dump::send_data(List<Item> &items)
unit->offset_limit_cnt--;
DBUG_RETURN(0);
}
+ if (thd->killed == ABORT_QUERY)
+ DBUG_RETURN(0);
+
if (row_count++ > 1)
{
my_message(ER_TOO_MANY_ROWS, ER(ER_TOO_MANY_ROWS), MYF(0));
@@ -2842,6 +2855,8 @@ int select_singlerow_subselect::send_data(List<Item> &items)
unit->offset_limit_cnt--;
DBUG_RETURN(0);
}
+ if (thd->killed == ABORT_QUERY)
+ DBUG_RETURN(0);
List_iterator_fast<Item> li(items);
Item *val_item;
for (uint i= 0; (val_item= li++); i++)
@@ -2985,6 +3000,8 @@ int select_exists_subselect::send_data(List<Item> &items)
unit->offset_limit_cnt--;
DBUG_RETURN(0);
}
+ if (thd->killed == ABORT_QUERY)
+ DBUG_RETURN(0);
it->value= 1;
it->assigned(1);
DBUG_RETURN(0);
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 5c35bc37623..c6c46975076 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -396,7 +396,10 @@ typedef enum enum_diag_condition_item_name
*/
extern const LEX_STRING Diag_condition_item_names[];
-/* Note: these states are actually bit coded with HARD */
+/**
+ These states are bit coded with HARD. For each state there must be a pair
+ <state_even_num>, and <state_odd_num>_HARD.
+*/
enum killed_state
{
NOT_KILLED= 0,
@@ -406,15 +409,23 @@ enum killed_state
KILL_QUERY= 4,
KILL_QUERY_HARD= 5,
/*
+ ABORT_QUERY signals to the query processor to stop execution ASAP without
+ issuing an error. Instead a warning is issued, and when possible a partial
+ query result is returned to the client.
+ */
+ ABORT_QUERY= 6,
+ ABORT_QUERY_HARD= 7,
+ /*
All of the following killed states will kill the connection
- KILL_CONNECTION must be the first of these!
- */
- KILL_CONNECTION= 6,
- KILL_CONNECTION_HARD= 7,
- KILL_SYSTEM_THREAD= 8,
- KILL_SYSTEM_THREAD_HARD= 9,
- KILL_SERVER= 10,
- KILL_SERVER_HARD= 11
+ KILL_CONNECTION must be the first of these and it must start with
+ an even number (becasue of HARD bit)!
+ */
+ KILL_CONNECTION= 8,
+ KILL_CONNECTION_HARD= 9,
+ KILL_SYSTEM_THREAD= 10,
+ KILL_SYSTEM_THREAD_HARD= 11,
+ KILL_SERVER= 12,
+ KILL_SERVER_HARD= 13
};
extern int killed_errno(killed_state killed);
@@ -2091,6 +2102,20 @@ public:
filesort() before reading it for e.g. update.
*/
ha_rows examined_row_count;
+ /**
+ The number of rows and/or keys examined by the query, both read,
+ changed or written.
+ */
+ ulonglong accessed_rows_and_keys;
+ /**
+ Check if the number of rows accessed by a statement exceeded
+ LIMIT ROWS EXAMINED. If so, signal the query engine to stop execution.
+ */
+ void check_limit_rows_examined()
+ {
+ if (++accessed_rows_and_keys > lex->limit_rows_examined_cnt)
+ killed= ABORT_QUERY;
+ }
USER_CONN *user_connect;
CHARSET_INFO *db_charset;
@@ -4102,6 +4127,7 @@ inline bool add_group_to_list(THD *thd, Item *item, bool asc)
inline void handler::increment_statistics(ulong SSV::*offset) const
{
status_var_increment(table->in_use->status_var.*offset);
+ table->in_use->check_limit_rows_examined();
}
inline void handler::decrement_statistics(ulong SSV::*offset) const
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 2209a62d7ed..54f94ce78c1 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -3476,6 +3476,8 @@ int select_insert::send_data(List<Item> &values)
unit->offset_limit_cnt--;
DBUG_RETURN(0);
}
+ if (thd->killed == ABORT_QUERY)
+ DBUG_RETURN(0);
thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields
store_values(values);
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 56e05c3dcad..b2dfae5ded4 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -526,6 +526,8 @@ void lex_start(THD *thd)
lex->is_lex_started= TRUE;
lex->used_tables= 0;
lex->reset_slave_info.all= false;
+ lex->limit_rows_examined= 0;
+ lex->limit_rows_examined_cnt= ULONGLONG_MAX;
DBUG_VOID_RETURN;
}
@@ -2523,7 +2525,8 @@ void Query_tables_list::destroy_query_tables_list()
*/
LEX::LEX()
- :result(0), option_type(OPT_DEFAULT), is_lex_started(0)
+ :result(0), option_type(OPT_DEFAULT), is_lex_started(0),
+ limit_rows_examined_cnt(ULONGLONG_MAX)
{
my_init_dynamic_array2(&plugins, sizeof(plugin_ref),
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index d4e94c8d2d8..718c9c7e6a2 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -2568,6 +2568,22 @@ struct LEX: public Query_tables_list
into the select_lex.
*/
table_map used_tables;
+ /**
+ Maximum number of rows and/or keys examined by the query, both read,
+ changed or written. This is the argument of LIMIT ROWS EXAMINED.
+ The limit is represented by two variables - the Item is needed because
+ in case of parameters we have to delay its evaluation until execution.
+ Once evaluated, its value is stored in examined_rows_limit_cnt.
+ */
+ Item *limit_rows_examined;
+ ulonglong limit_rows_examined_cnt;
+ inline void set_limit_rows_examined()
+ {
+ if (limit_rows_examined)
+ limit_rows_examined_cnt= limit_rows_examined->val_uint();
+ else
+ limit_rows_examined_cnt= ULONGLONG_MAX;
+ }
LEX();
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index d1b2eade165..e0b2acd199d 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -5460,6 +5460,7 @@ void THD::reset_for_next_command(bool calculate_userstat)
thd->warning_info->reset_for_next_command();
thd->rand_used= 0;
thd->sent_row_count= thd->examined_row_count= 0;
+ thd->accessed_rows_and_keys= 0;
/* Copy data for user stats */
if ((thd->userstat_running= calculate_userstat))
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 7cacf0fd811..2e63a16d4e6 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -314,6 +314,21 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
res|= thd->is_error();
if (unlikely(res))
result->abort_result_set();
+ if (thd->killed == ABORT_QUERY)
+ {
+ /*
+ If LIMIT ROWS EXAMINED interrupted query execution, issue a warning,
+ continue with normal processing and produce an incomplete query 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->killed= NOT_KILLED;
+ }
+ /* Disable LIMIT ROWS EXAMINED after query execution. */
+ thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;
MYSQL_SELECT_DONE((int) res, (ulong) thd->limit_found_rows);
DBUG_RETURN(res);
@@ -1725,6 +1740,19 @@ int JOIN::init_execution()
DBUG_ASSERT(!(select_options & SELECT_DESCRIBE));
initialized= true;
+ /*
+ Enable LIMIT ROWS EXAMINED during query execution if:
+ (1) This JOIN is the outermost query (not a subquery or derived table)
+ This ensures that the limit is enabled when actual execution begins, and
+ not if a subquery is evaluated during optimization of the outer query.
+ (2) This JOIN is not the result of a UNION. In this case do not apply the
+ limit in order to produce the partial query result stored in the
+ UNION temp table.
+ */
+ if (!select_lex->outer_select() && // (1)
+ select_lex != select_lex->master_unit()->fake_select_lex) // (2)
+ thd->lex->set_limit_rows_examined();
+
/* Create a tmp table if distinct or if the sort is too complicated */
if (need_tmp)
{
@@ -15377,12 +15405,13 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
error= NESTED_LOOP_NO_MORE_ROWS;
else
error= sub_select(join,join_tab,0);
- if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS)
+ if ((error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS) &&
+ join->thd->killed != ABORT_QUERY)
error= sub_select(join,join_tab,1);
if (error == NESTED_LOOP_QUERY_LIMIT)
error= NESTED_LOOP_OK; /* select_limit used */
}
- if (error == NESTED_LOOP_NO_MORE_ROWS)
+ if (error == NESTED_LOOP_NO_MORE_ROWS || join->thd->killed == ABORT_QUERY)
error= NESTED_LOOP_OK;
if (table)
@@ -16953,11 +16982,6 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
TABLE *table=join->tmp_table;
DBUG_ENTER("end_write");
- if (join->thd->killed) // Aborted by user
- {
- join->thd->send_kill_message();
- DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
- }
if (!end_of_records)
{
copy_fields(&join->tmp_table_param);
@@ -16986,11 +17010,15 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
join->do_send_rows=0;
join->unit->select_limit_cnt = HA_POS_ERROR;
- DBUG_RETURN(NESTED_LOOP_OK);
}
}
}
end:
+ if (join->thd->killed)
+ {
+ join->thd->send_kill_message();
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
+ }
DBUG_RETURN(NESTED_LOOP_OK);
}
@@ -17008,11 +17036,6 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
- if (join->thd->killed) // Aborted by user
- {
- join->thd->send_kill_message();
- DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
- }
join->found_records++;
copy_fields(&join->tmp_table_param); // Groups are copied twice.
@@ -17038,7 +17061,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
- DBUG_RETURN(NESTED_LOOP_OK);
+ goto end;
}
/*
@@ -17073,6 +17096,12 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
join->join_tab[join->top_join_tab_count-1].next_select=end_unique_update;
}
join->send_records++;
+end:
+ if (join->thd->killed)
+ {
+ join->thd->send_kill_message();
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
+ }
DBUG_RETURN(NESTED_LOOP_OK);
}
@@ -17089,11 +17118,6 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
- if (join->thd->killed) // Aborted by user
- {
- join->thd->send_kill_message();
- DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
- }
init_tmptable_sum_functions(join->sum_funcs);
copy_fields(&join->tmp_table_param); // Groups are copied twice.
@@ -17123,6 +17147,11 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
}
+ if (join->thd->killed)
+ {
+ join->thd->send_kill_message();
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
+ }
DBUG_RETURN(NESTED_LOOP_OK);
}
@@ -17136,11 +17165,6 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
int idx= -1;
DBUG_ENTER("end_write_group");
- if (join->thd->killed)
- { // Aborted by user
- join->thd->send_kill_message();
- DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
- }
if (!join->first_record || end_of_records ||
(idx=test_if_group_changed(join->group_fields)) >= 0)
{
@@ -17174,13 +17198,13 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR);
}
if (end_of_records)
- DBUG_RETURN(NESTED_LOOP_OK);
+ goto end;
}
}
else
{
if (end_of_records)
- DBUG_RETURN(NESTED_LOOP_OK);
+ goto end;
join->first_record=1;
(void) test_if_group_changed(join->group_fields);
}
@@ -17193,13 +17217,19 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR);
if (join->procedure)
join->procedure->add();
- DBUG_RETURN(NESTED_LOOP_OK);
+ goto end;
}
}
if (update_sum_func(join->sum_funcs))
DBUG_RETURN(NESTED_LOOP_ERROR);
if (join->procedure)
join->procedure->add();
+end:
+ if (join->thd->killed)
+ {
+ join->thd->send_kill_message();
+ DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
+ }
DBUG_RETURN(NESTED_LOOP_OK);
}
@@ -18586,6 +18616,7 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
ulong reclength,offset;
uint field_count;
THD *thd= join->thd;
+
DBUG_ENTER("remove_duplicates");
entry->reginfo.lock_type=TL_WRITE;
@@ -18611,6 +18642,13 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
offset(entry->record[0]) : 0);
reclength=entry->s->reclength-offset;
+ /*
+ Disable LIMIT ROWS EXAMINED in order to avoid interrupting prematurely
+ duplicate removal, and produce a possibly incomplete query result.
+ */
+ thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;
+ if (thd->killed == ABORT_QUERY)
+ thd->killed= NOT_KILLED;
free_io_cache(entry); // Safety
entry->file->info(HA_STATUS_VARIABLE);
if (entry->s->db_type() == heap_hton ||
@@ -18624,6 +18662,8 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
error=remove_dup_with_compare(join->thd, entry, first_field, offset,
having);
+ if (join->select_lex != join->select_lex->master_unit()->fake_select_lex)
+ thd->lex->set_limit_rows_examined();
free_blobs(first_field);
DBUG_RETURN(error);
}
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 2118bc8c30d..021267b53bd 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -59,6 +59,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);
@@ -701,6 +703,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->killed= NOT_KILLED;
+ break;
+ }
}
}
@@ -709,6 +725,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
{
@@ -729,7 +750,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;
@@ -801,6 +822,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);
}
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index a307ebecca1..0db69af094f 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -451,6 +451,17 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
goto err;
}
+ if (lex->limit_rows_examined)
+ {
+ /*
+ LIMIT ROWS EXAMINED is not supported inside views to avoid complicated
+ side-effects and semantics of the clause.
+ */
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "LIMIT ROWS EXAMINED inside views");
+ res= TRUE;
+ goto err;
+ }
+
sp_cache_invalidate();
if (!lex->definer)
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 54e2f3012ff..5f9066afd39 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -977,6 +977,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token EVENTS_SYM
%token EVENT_SYM
%token EVERY_SYM /* SQL-2003-N */
+%token EXAMINED_SYM
%token EXECUTE_SYM /* SQL-2003-R */
%token EXISTS /* SQL-2003-R */
%token EXIT_SYM
@@ -10304,6 +10305,7 @@ opt_limit_clause_init:
SELECT_LEX *sel= lex->current_select;
sel->offset_limit= 0;
sel->select_limit= 0;
+ lex->limit_rows_examined= 0;
}
| limit_clause {}
;
@@ -10318,6 +10320,14 @@ limit_clause:
{
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
}
+ | LIMIT limit_options ROWS_SYM EXAMINED_SYM limit_rows_option
+ {
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
+ }
+ | LIMIT ROWS_SYM EXAMINED_SYM limit_rows_option
+ {
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
+ }
;
limit_options:
@@ -10403,6 +10413,13 @@ limit_option:
}
;
+limit_rows_option:
+ limit_option
+ {
+ LEX *lex=Lex;
+ lex->limit_rows_examined= $1;
+ }
+
delete_limit_clause:
/* empty */
{
@@ -10416,6 +10433,8 @@ delete_limit_clause:
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
sel->explicit_limit= 1;
}
+ | LIMIT ROWS_SYM EXAMINED_SYM { my_parse_error(ER(ER_SYNTAX_ERROR)); MYSQL_YYABORT; }
+ | LIMIT limit_option ROWS_SYM EXAMINED_SYM { my_parse_error(ER(ER_SYNTAX_ERROR)); MYSQL_YYABORT; }
;
int_num:
@@ -12892,6 +12911,7 @@ keyword:
| DEALLOCATE_SYM {}
| DO_SYM {}
| END {}
+ | EXAMINED_SYM {}
| EXECUTE_SYM {}
| FLUSH_SYM {}
| HANDLER_SYM {}
@@ -13793,6 +13813,7 @@ handler:
MYSQL_YYABORT;
lex->current_select->select_limit= one;
lex->current_select->offset_limit= 0;
+ lex->limit_rows_examined= 0;
if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0))
MYSQL_YYABORT;
}