Commit 8aebd44e authored by unknown's avatar unknown

Implementation of MDEV-28 LIMIT ROWS EXAMINED

https://mariadb.atlassian.net/browse/MDEV-28
  
This task implements a new clause LIMIT ROWS EXAMINED <num>
as an extention to the ANSI LIMIT clause. This extension
allows to limit the number of rows and/or keys a query
would access (read and/or write) during query execution.
parent f92cfdb8
This diff is collapsed.
...@@ -14,7 +14,7 @@ include/stop_slave.inc ...@@ -14,7 +14,7 @@ include/stop_slave.inc
# Suspend the INSERT statement in current transaction on SQL thread. # Suspend the INSERT statement in current transaction on SQL thread.
# It guarantees that SQL thread is applying the transaction when # It guarantees that SQL thread is applying the transaction when
# STOP SLAVE command launchs. # STOP SLAVE command launchs.
SET GLOBAL debug= 'd,after_mysql_insert'; SET GLOBAL debug= '+d,after_mysql_insert,*';
include/start_slave.inc include/start_slave.inc
# CREATE TEMPORARY TABLE with InnoDB engine # CREATE TEMPORARY TABLE with InnoDB engine
...@@ -131,7 +131,6 @@ include/diff_tables.inc [master:t1, slave:t1] ...@@ -131,7 +131,6 @@ include/diff_tables.inc [master:t1, slave:t1]
START SLAVE SQL_THREAD; START SLAVE SQL_THREAD;
include/wait_for_slave_sql_to_start.inc include/wait_for_slave_sql_to_start.inc
# Test end # Test end
SET GLOBAL debug= '$debug_save';
include/restart_slave.inc include/restart_slave.inc
[connection master] [connection master]
DROP TABLE t1, t2; DROP TABLE t1, t2;
...@@ -151,7 +150,7 @@ CREATE TABLE t1 (c1 INT KEY, c2 INT) ENGINE=InnoDB; ...@@ -151,7 +150,7 @@ CREATE TABLE t1 (c1 INT KEY, c2 INT) ENGINE=InnoDB;
CREATE TABLE t2 (c1 INT) ENGINE=MyISAM; CREATE TABLE t2 (c1 INT) ENGINE=MyISAM;
INSERT INTO t1 VALUES(1, 1); INSERT INTO t1 VALUES(1, 1);
[connection master] [connection master]
SET GLOBAL debug= 'd,dump_thread_wait_before_send_xid'; SET GLOBAL debug= '+d,dump_thread_wait_before_send_xid,*';
[connection slave] [connection slave]
include/restart_slave.inc include/restart_slave.inc
BEGIN; BEGIN;
...@@ -176,5 +175,4 @@ include/wait_for_slave_to_stop.inc ...@@ -176,5 +175,4 @@ include/wait_for_slave_to_stop.inc
include/start_slave.inc include/start_slave.inc
[connection master] [connection master]
DROP TABLE t1, t2; DROP TABLE t1, t2;
SET GLOBAL debug= $debug_save;
include/rpl_end.inc include/rpl_end.inc
...@@ -23,7 +23,7 @@ source include/stop_slave.inc; ...@@ -23,7 +23,7 @@ source include/stop_slave.inc;
--echo # It guarantees that SQL thread is applying the transaction when --echo # It guarantees that SQL thread is applying the transaction when
--echo # STOP SLAVE command launchs. --echo # STOP SLAVE command launchs.
let $debug_save= `SELECT @@GLOBAL.debug`; let $debug_save= `SELECT @@GLOBAL.debug`;
SET GLOBAL debug= 'd,after_mysql_insert'; SET GLOBAL debug= '+d,after_mysql_insert,*';
source include/start_slave.inc; source include/start_slave.inc;
--echo --echo
...@@ -53,7 +53,9 @@ let $tmp_table_stm= CREATE TEMPORARY TABLE tt1(c1 INT) ENGINE = MyISAM ...@@ -53,7 +53,9 @@ let $tmp_table_stm= CREATE TEMPORARY TABLE tt1(c1 INT) ENGINE = MyISAM
source extra/rpl_tests/rpl_stop_slave.test; source extra/rpl_tests/rpl_stop_slave.test;
--echo # Test end --echo # Test end
SET GLOBAL debug= '$debug_save'; --disable_query_log
eval SET GLOBAL debug= '$debug_save';
--enable_query_log
source include/restart_slave_sql.inc; source include/restart_slave_sql.inc;
--source include/rpl_connection_master.inc --source include/rpl_connection_master.inc
...@@ -81,7 +83,7 @@ sync_slave_with_master; ...@@ -81,7 +83,7 @@ sync_slave_with_master;
--source include/rpl_connection_master.inc --source include/rpl_connection_master.inc
let $debug_save= `SELECT @@GLOBAL.debug`; let $debug_save= `SELECT @@GLOBAL.debug`;
SET GLOBAL debug= 'd,dump_thread_wait_before_send_xid'; SET GLOBAL debug= '+d,dump_thread_wait_before_send_xid,*';
--source include/rpl_connection_slave.inc --source include/rpl_connection_slave.inc
source include/restart_slave_sql.inc; source include/restart_slave_sql.inc;
...@@ -121,5 +123,7 @@ source include/start_slave.inc; ...@@ -121,5 +123,7 @@ source include/start_slave.inc;
--source include/rpl_connection_master.inc --source include/rpl_connection_master.inc
DROP TABLE t1, t2; DROP TABLE t1, t2;
SET GLOBAL debug= $debug_save; --disable_query_log
eval SET GLOBAL debug= '$debug_save';
--enable_query_log
--source include/rpl_end.inc --source include/rpl_end.inc
This diff is collapsed.
...@@ -204,6 +204,7 @@ static SYMBOL symbols[] = { ...@@ -204,6 +204,7 @@ static SYMBOL symbols[] = {
{ "EVENT", SYM(EVENT_SYM)}, { "EVENT", SYM(EVENT_SYM)},
{ "EVENTS", SYM(EVENTS_SYM)}, { "EVENTS", SYM(EVENTS_SYM)},
{ "EVERY", SYM(EVERY_SYM)}, { "EVERY", SYM(EVERY_SYM)},
{ "EXAMINED", SYM(EXAMINED_SYM)},
{ "EXECUTE", SYM(EXECUTE_SYM)}, { "EXECUTE", SYM(EXECUTE_SYM)},
{ "EXISTS", SYM(EXISTS)}, { "EXISTS", SYM(EXISTS)},
{ "EXIT", SYM(EXIT_SYM)}, { "EXIT", SYM(EXIT_SYM)},
......
...@@ -6296,4 +6296,5 @@ ER_INTERNAL_ERROR ...@@ -6296,4 +6296,5 @@ ER_INTERNAL_ERROR
eng "Internal error: '%-.192s'" eng "Internal error: '%-.192s'"
ER_SPATIAL_MUST_HAVE_GEOM_COL 42000 ER_SPATIAL_MUST_HAVE_GEOM_COL 42000
eng "A SPATIAL index may only contain a geometrical type column" eng "A SPATIAL index may only contain a geometrical type column"
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."
...@@ -191,6 +191,10 @@ extern "C" sig_handler handle_fatal_signal(int sig) ...@@ -191,6 +191,10 @@ extern "C" sig_handler handle_fatal_signal(int sig)
case KILL_SERVER_HARD: case KILL_SERVER_HARD:
kreason= "KILL_SERVER"; kreason= "KILL_SERVER";
break; break;
case ABORT_QUERY:
case ABORT_QUERY_HARD:
kreason= "ABORT_QUERY";
break;
} }
my_safe_printf_stderr("%s", "\n" my_safe_printf_stderr("%s", "\n"
"Trying to get some variables.\n" "Trying to get some variables.\n"
......
...@@ -675,6 +675,7 @@ THD::THD() ...@@ -675,6 +675,7 @@ THD::THD()
first_successful_insert_id_in_cur_stmt(0), first_successful_insert_id_in_cur_stmt(0),
stmt_depends_on_first_successful_insert_id_in_prev_stmt(FALSE), stmt_depends_on_first_successful_insert_id_in_prev_stmt(FALSE),
examined_row_count(0), examined_row_count(0),
accessed_rows_and_keys(0),
global_read_lock(0), global_read_lock(0),
global_disable_checkpoint(0), global_disable_checkpoint(0),
is_fatal_error(0), is_fatal_error(0),
...@@ -1373,26 +1374,31 @@ void THD::awake(killed_state state_to_set) ...@@ -1373,26 +1374,31 @@ void THD::awake(killed_state state_to_set)
int killed_errno(killed_state killed) int killed_errno(killed_state killed)
{ {
DBUG_ENTER("killed_errno");
DBUG_PRINT("enter", ("killed: %d", killed));
switch (killed) { switch (killed) {
case NOT_KILLED: case NOT_KILLED:
case KILL_HARD_BIT: case KILL_HARD_BIT:
return 0; // Probably wrong usage DBUG_RETURN(0); // Probably wrong usage
case KILL_BAD_DATA: case KILL_BAD_DATA:
case KILL_BAD_DATA_HARD: 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:
case KILL_CONNECTION_HARD: case KILL_CONNECTION_HARD:
case KILL_SYSTEM_THREAD: case KILL_SYSTEM_THREAD:
case KILL_SYSTEM_THREAD_HARD: case KILL_SYSTEM_THREAD_HARD:
return ER_CONNECTION_KILLED; DBUG_RETURN(ER_CONNECTION_KILLED);
case KILL_QUERY: case KILL_QUERY:
case KILL_QUERY_HARD: case KILL_QUERY_HARD:
return ER_QUERY_INTERRUPTED; DBUG_RETURN(ER_QUERY_INTERRUPTED);
case KILL_SERVER: case KILL_SERVER:
case KILL_SERVER_HARD: case KILL_SERVER_HARD:
return ER_SERVER_SHUTDOWN; DBUG_RETURN(ER_SERVER_SHUTDOWN);
} }
return 0; // Keep compiler happy DBUG_RETURN(0); // Keep compiler happy
} }
...@@ -1975,6 +1981,8 @@ int select_send::send_data(List<Item> &items) ...@@ -1975,6 +1981,8 @@ int select_send::send_data(List<Item> &items)
unit->offset_limit_cnt--; unit->offset_limit_cnt--;
return 0; return 0;
} }
if (thd->killed == ABORT_QUERY)
return 0;
/* /*
We may be passing the control from mysqld to the client: release the We may be passing the control from mysqld to the client: release the
...@@ -2293,6 +2301,8 @@ int select_export::send_data(List<Item> &items) ...@@ -2293,6 +2301,8 @@ int select_export::send_data(List<Item> &items)
unit->offset_limit_cnt--; unit->offset_limit_cnt--;
DBUG_RETURN(0); DBUG_RETURN(0);
} }
if (thd->killed == ABORT_QUERY)
DBUG_RETURN(0);
row_count++; row_count++;
Item *item; Item *item;
uint used_length=0,items_left=items.elements; uint used_length=0,items_left=items.elements;
...@@ -2548,6 +2558,9 @@ int select_dump::send_data(List<Item> &items) ...@@ -2548,6 +2558,9 @@ int select_dump::send_data(List<Item> &items)
unit->offset_limit_cnt--; unit->offset_limit_cnt--;
DBUG_RETURN(0); DBUG_RETURN(0);
} }
if (thd->killed == ABORT_QUERY)
DBUG_RETURN(0);
if (row_count++ > 1) if (row_count++ > 1)
{ {
my_message(ER_TOO_MANY_ROWS, ER(ER_TOO_MANY_ROWS), MYF(0)); my_message(ER_TOO_MANY_ROWS, ER(ER_TOO_MANY_ROWS), MYF(0));
...@@ -2594,6 +2607,8 @@ int select_singlerow_subselect::send_data(List<Item> &items) ...@@ -2594,6 +2607,8 @@ int select_singlerow_subselect::send_data(List<Item> &items)
unit->offset_limit_cnt--; unit->offset_limit_cnt--;
DBUG_RETURN(0); DBUG_RETURN(0);
} }
if (thd->killed == ABORT_QUERY)
DBUG_RETURN(0);
List_iterator_fast<Item> li(items); List_iterator_fast<Item> li(items);
Item *val_item; Item *val_item;
for (uint i= 0; (val_item= li++); i++) for (uint i= 0; (val_item= li++); i++)
...@@ -2737,6 +2752,8 @@ int select_exists_subselect::send_data(List<Item> &items) ...@@ -2737,6 +2752,8 @@ int select_exists_subselect::send_data(List<Item> &items)
unit->offset_limit_cnt--; unit->offset_limit_cnt--;
DBUG_RETURN(0); DBUG_RETURN(0);
} }
if (thd->killed == ABORT_QUERY)
DBUG_RETURN(0);
it->value= 1; it->value= 1;
it->assigned(1); it->assigned(1);
DBUG_RETURN(0); DBUG_RETURN(0);
......
...@@ -365,7 +365,10 @@ public: ...@@ -365,7 +365,10 @@ public:
}; };
/* 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 enum killed_state
{ {
NOT_KILLED= 0, NOT_KILLED= 0,
...@@ -374,16 +377,24 @@ enum killed_state ...@@ -374,16 +377,24 @@ enum killed_state
KILL_BAD_DATA_HARD= 3, KILL_BAD_DATA_HARD= 3,
KILL_QUERY= 4, KILL_QUERY= 4,
KILL_QUERY_HARD= 5, 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 All of the following killed states will kill the connection
KILL_CONNECTION must be the first of these! KILL_CONNECTION must be the first of these and it must start with
*/ an even number (becasue of HARD bit)!
KILL_CONNECTION= 6, */
KILL_CONNECTION_HARD= 7, KILL_CONNECTION= 8,
KILL_SYSTEM_THREAD= 8, KILL_CONNECTION_HARD= 9,
KILL_SYSTEM_THREAD_HARD= 9, KILL_SYSTEM_THREAD= 10,
KILL_SERVER= 10, KILL_SYSTEM_THREAD_HARD= 11,
KILL_SERVER_HARD= 11 KILL_SERVER= 12,
KILL_SERVER_HARD= 13
}; };
extern int killed_errno(killed_state killed); extern int killed_errno(killed_state killed);
...@@ -1951,6 +1962,20 @@ public: ...@@ -1951,6 +1962,20 @@ public:
filesort() before reading it for e.g. update. filesort() before reading it for e.g. update.
*/ */
ha_rows examined_row_count; 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; USER_CONN *user_connect;
CHARSET_INFO *db_charset; CHARSET_INFO *db_charset;
...@@ -3643,6 +3668,7 @@ void mark_transaction_to_rollback(THD *thd, bool all); ...@@ -3643,6 +3668,7 @@ void mark_transaction_to_rollback(THD *thd, bool all);
inline void handler::increment_statistics(ulong SSV::*offset) const inline void handler::increment_statistics(ulong SSV::*offset) const
{ {
status_var_increment(table->in_use->status_var.*offset); 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 inline void handler::decrement_statistics(ulong SSV::*offset) const
......
...@@ -3280,6 +3280,8 @@ int select_insert::send_data(List<Item> &values) ...@@ -3280,6 +3280,8 @@ int select_insert::send_data(List<Item> &values)
unit->offset_limit_cnt--; unit->offset_limit_cnt--;
DBUG_RETURN(0); DBUG_RETURN(0);
} }
if (thd->killed == ABORT_QUERY)
DBUG_RETURN(0);
thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields
store_values(values); store_values(values);
......
...@@ -363,6 +363,8 @@ void lex_start(THD *thd) ...@@ -363,6 +363,8 @@ void lex_start(THD *thd)
lex->is_lex_started= TRUE; lex->is_lex_started= TRUE;
lex->used_tables= 0; lex->used_tables= 0;
lex->limit_rows_examined= 0;
lex->limit_rows_examined_cnt= ULONGLONG_MAX;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
......
...@@ -1947,6 +1947,22 @@ typedef struct st_lex : public Query_tables_list ...@@ -1947,6 +1947,22 @@ typedef struct st_lex : public Query_tables_list
into the select_lex. into the select_lex.
*/ */
table_map used_tables; 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;
}
st_lex(); st_lex();
......
...@@ -5876,6 +5876,7 @@ void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat) ...@@ -5876,6 +5876,7 @@ void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat)
thd->total_warn_count=0; // Warnings for this query thd->total_warn_count=0; // Warnings for this query
thd->rand_used= 0; thd->rand_used= 0;
thd->sent_row_count= thd->examined_row_count= 0; thd->sent_row_count= thd->examined_row_count= 0;
thd->accessed_rows_and_keys= 0;
/* Copy data for user stats */ /* Copy data for user stats */
if ((thd->userstat_running= calculate_userstat)) if ((thd->userstat_running= calculate_userstat))
......
...@@ -289,6 +289,21 @@ bool handle_select(THD *thd, LEX *lex, select_result *result, ...@@ -289,6 +289,21 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
res|= thd->is_error(); res|= thd->is_error();
if (unlikely(res)) if (unlikely(res))
result->abort(); result->abort();
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;
DBUG_RETURN(res); DBUG_RETURN(res);
} }
...@@ -1682,6 +1697,19 @@ int JOIN::init_execution() ...@@ -1682,6 +1697,19 @@ int JOIN::init_execution()
DBUG_ASSERT(!(select_options & SELECT_DESCRIBE)); DBUG_ASSERT(!(select_options & SELECT_DESCRIBE));
initialized= true; 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 */ /* Create a tmp table if distinct or if the sort is too complicated */
if (need_tmp) if (need_tmp)
{ {
...@@ -15091,14 +15119,14 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) ...@@ -15091,14 +15119,14 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
error= NESTED_LOOP_NO_MORE_ROWS; error= NESTED_LOOP_NO_MORE_ROWS;
else else
error= sub_select(join,join_tab,0); 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); error= sub_select(join,join_tab,1);
if (error == NESTED_LOOP_QUERY_LIMIT) if (error == NESTED_LOOP_QUERY_LIMIT)
error= NESTED_LOOP_OK; /* select_limit used */ 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; error= NESTED_LOOP_OK;
if (table == NULL) // If sending data to client if (table == NULL) // If sending data to client
{ {
/* /*
...@@ -16652,11 +16680,6 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), ...@@ -16652,11 +16680,6 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
TABLE *table=join->tmp_table; TABLE *table=join->tmp_table;
DBUG_ENTER("end_write"); 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) if (!end_of_records)
{ {
copy_fields(&join->tmp_table_param); copy_fields(&join->tmp_table_param);
...@@ -16701,11 +16724,15 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), ...@@ -16701,11 +16724,15 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT); DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
join->do_send_rows=0; join->do_send_rows=0;
join->unit->select_limit_cnt = HA_POS_ERROR; join->unit->select_limit_cnt = HA_POS_ERROR;
DBUG_RETURN(NESTED_LOOP_OK);
} }
} }
} }
end: end:
if (join->thd->killed)
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
}
DBUG_RETURN(NESTED_LOOP_OK); DBUG_RETURN(NESTED_LOOP_OK);
} }
...@@ -16723,11 +16750,6 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), ...@@ -16723,11 +16750,6 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (end_of_records) if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK); 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++; join->found_records++;
copy_fields(&join->tmp_table_param); // Groups are copied twice. copy_fields(&join->tmp_table_param); // Groups are copied twice.
...@@ -16753,7 +16775,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), ...@@ -16753,7 +16775,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
table->file->print_error(error,MYF(0)); /* purecov: inspected */ table->file->print_error(error,MYF(0)); /* purecov: inspected */
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
} }
DBUG_RETURN(NESTED_LOOP_OK); goto end;
} }
/* /*
...@@ -16788,6 +16810,12 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), ...@@ -16788,6 +16810,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->join_tab[join->top_join_tab_count-1].next_select=end_unique_update;
} }
join->send_records++; 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); DBUG_RETURN(NESTED_LOOP_OK);
} }
...@@ -16804,11 +16832,6 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), ...@@ -16804,11 +16832,6 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (end_of_records) if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK); 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); init_tmptable_sum_functions(join->sum_funcs);
copy_fields(&join->tmp_table_param); // Groups are copied twice. copy_fields(&join->tmp_table_param); // Groups are copied twice.
...@@ -16838,6 +16861,11 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), ...@@ -16838,6 +16861,11 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ 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); DBUG_RETURN(NESTED_LOOP_OK);
} }
...@@ -16851,11 +16879,6 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), ...@@ -16851,11 +16879,6 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
int idx= -1; int idx= -1;
DBUG_ENTER("end_write_group"); 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 || if (!join->first_record || end_of_records ||
(idx=test_if_group_changed(join->group_fields)) >= 0) (idx=test_if_group_changed(join->group_fields)) >= 0)
{ {
...@@ -16889,13 +16912,13 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), ...@@ -16889,13 +16912,13 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR); DBUG_RETURN(NESTED_LOOP_ERROR);
} }
if (end_of_records) if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK); goto end;
} }
} }
else else
{ {
if (end_of_records) if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK); goto end;
join->first_record=1; join->first_record=1;
VOID(test_if_group_changed(join->group_fields)); VOID(test_if_group_changed(join->group_fields));
} }
...@@ -16908,13 +16931,19 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), ...@@ -16908,13 +16931,19 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR); DBUG_RETURN(NESTED_LOOP_ERROR);
if (join->procedure) if (join->procedure)
join->procedure->add(); join->procedure->add();
DBUG_RETURN(NESTED_LOOP_OK); goto end;
} }
} }
if (update_sum_func(join->sum_funcs)) if (update_sum_func(join->sum_funcs))
DBUG_RETURN(NESTED_LOOP_ERROR); DBUG_RETURN(NESTED_LOOP_ERROR);
if (join->procedure) if (join->procedure)
join->procedure->add(); 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); DBUG_RETURN(NESTED_LOOP_OK);
} }
...@@ -18561,6 +18590,7 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having) ...@@ -18561,6 +18590,7 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
ulong reclength,offset; ulong reclength,offset;
uint field_count; uint field_count;
THD *thd= join->thd; THD *thd= join->thd;
DBUG_ENTER("remove_duplicates"); DBUG_ENTER("remove_duplicates");
entry->reginfo.lock_type=TL_WRITE; entry->reginfo.lock_type=TL_WRITE;
...@@ -18586,6 +18616,13 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having) ...@@ -18586,6 +18616,13 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
offset(entry->record[0]) : 0); offset(entry->record[0]) : 0);
reclength=entry->s->reclength-offset; 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 free_io_cache(entry); // Safety
entry->file->info(HA_STATUS_VARIABLE); entry->file->info(HA_STATUS_VARIABLE);
if (entry->s->db_type() == heap_hton || if (entry->s->db_type() == heap_hton ||
...@@ -18599,6 +18636,8 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having) ...@@ -18599,6 +18636,8 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
error=remove_dup_with_compare(join->thd, entry, first_field, offset, error=remove_dup_with_compare(join->thd, entry, first_field, offset,
having); having);
if (join->select_lex != join->select_lex->master_unit()->fake_select_lex)
thd->lex->set_limit_rows_examined();
free_blobs(first_field); free_blobs(first_field);
DBUG_RETURN(error); DBUG_RETURN(error);
} }
......
...@@ -57,6 +57,8 @@ int select_union::send_data(List<Item> &values) ...@@ -57,6 +57,8 @@ int select_union::send_data(List<Item> &values)
unit->offset_limit_cnt--; unit->offset_limit_cnt--;
return 0; return 0;
} }
if (thd->killed == ABORT_QUERY)
return 0;
if (table->no_rows_with_nulls) if (table->no_rows_with_nulls)
table->null_catch_flags= CHECK_ROW_FOR_NULLS_TO_REJECT; table->null_catch_flags= CHECK_ROW_FOR_NULLS_TO_REJECT;
fill_record(thd, table->field, values, TRUE, FALSE); fill_record(thd, table->field, values, TRUE, FALSE);
...@@ -698,6 +700,20 @@ bool st_select_lex_unit::exec() ...@@ -698,6 +700,20 @@ bool st_select_lex_unit::exec()
add_rows+= (ulonglong) (thd->limit_found_rows - (ulonglong) add_rows+= (ulonglong) (thd->limit_found_rows - (ulonglong)
((table->file->stats.records - records_at_start))); ((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;
}
} }
} }
...@@ -706,6 +722,11 @@ bool st_select_lex_unit::exec() ...@@ -706,6 +722,11 @@ bool st_select_lex_unit::exec()
{ {
List<Item_func_match> empty_list; List<Item_func_match> empty_list;
empty_list.empty(); 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 if (!thd->is_fatal_error) // Check if EOM
{ {
...@@ -726,7 +747,7 @@ bool st_select_lex_unit::exec() ...@@ -726,7 +747,7 @@ bool st_select_lex_unit::exec()
fake_select_lex->options, result))) fake_select_lex->options, result)))
{ {
fake_select_lex->table_list.empty(); fake_select_lex->table_list.empty();
DBUG_RETURN(TRUE); goto err;
} }
fake_select_lex->join->no_const_tables= TRUE; fake_select_lex->join->no_const_tables= TRUE;
...@@ -798,6 +819,8 @@ bool st_select_lex_unit::exec() ...@@ -798,6 +819,8 @@ bool st_select_lex_unit::exec()
} }
} }
thd->lex->current_select= lex_select_save; thd->lex->current_select= lex_select_save;
err:
thd->lex->set_limit_rows_examined();
DBUG_RETURN(saved_error); DBUG_RETURN(saved_error);
} }
......
...@@ -435,6 +435,17 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, ...@@ -435,6 +435,17 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
goto err; 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(); sp_cache_invalidate();
if (!lex->definer) if (!lex->definer)
......
...@@ -865,6 +865,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ...@@ -865,6 +865,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token EVENTS_SYM %token EVENTS_SYM
%token EVENT_SYM %token EVENT_SYM
%token EVERY_SYM /* SQL-2003-N */ %token EVERY_SYM /* SQL-2003-N */
%token EXAMINED_SYM
%token EXECUTE_SYM /* SQL-2003-R */ %token EXECUTE_SYM /* SQL-2003-R */
%token EXISTS /* SQL-2003-R */ %token EXISTS /* SQL-2003-R */
%token EXIT_SYM %token EXIT_SYM
...@@ -9620,6 +9621,7 @@ opt_limit_clause_init: ...@@ -9620,6 +9621,7 @@ opt_limit_clause_init:
SELECT_LEX *sel= lex->current_select; SELECT_LEX *sel= lex->current_select;
sel->offset_limit= 0; sel->offset_limit= 0;
sel->select_limit= 0; sel->select_limit= 0;
lex->limit_rows_examined= 0;
} }
| limit_clause {} | limit_clause {}
; ;
...@@ -9631,6 +9633,8 @@ opt_limit_clause: ...@@ -9631,6 +9633,8 @@ opt_limit_clause:
limit_clause: limit_clause:
LIMIT limit_options {} LIMIT limit_options {}
| LIMIT limit_options ROWS_SYM EXAMINED_SYM limit_rows_option {}
| LIMIT ROWS_SYM EXAMINED_SYM limit_rows_option {}
; ;
limit_options: limit_options:
...@@ -9682,6 +9686,13 @@ limit_option: ...@@ -9682,6 +9686,13 @@ limit_option:
} }
; ;
limit_rows_option:
limit_option
{
LEX *lex=Lex;
lex->limit_rows_examined= $1;
}
delete_limit_clause: delete_limit_clause:
/* empty */ /* empty */
{ {
...@@ -9694,6 +9705,8 @@ delete_limit_clause: ...@@ -9694,6 +9705,8 @@ delete_limit_clause:
sel->select_limit= $2; sel->select_limit= $2;
sel->explicit_limit= 1; 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: int_num:
...@@ -12131,6 +12144,7 @@ keyword: ...@@ -12131,6 +12144,7 @@ keyword:
| DO_SYM {} | DO_SYM {}
| END {} | END {}
| EXECUTE_SYM {} | EXECUTE_SYM {}
| EXAMINED_SYM {}
| FLUSH_SYM {} | FLUSH_SYM {}
| HANDLER_SYM {} | HANDLER_SYM {}
| HELP_SYM {} | HELP_SYM {}
...@@ -13011,6 +13025,7 @@ handler: ...@@ -13011,6 +13025,7 @@ handler:
MYSQL_YYABORT; MYSQL_YYABORT;
lex->current_select->select_limit= one; lex->current_select->select_limit= one;
lex->current_select->offset_limit= 0; lex->current_select->offset_limit= 0;
lex->limit_rows_examined= 0;
if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0)) if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment