MDEV-34860 Implement MAX_EXECUTION_TIME hint
It places a limit N (a timeout value in milliseconds) on how long a statement is permitted to execute before the server terminates it. Syntax: SELECT /*+ MAX_EXECUTION_TIME(milliseconds) */ ... Only top-level SELECT statements support the hint.
This commit is contained in:
parent
1e2774d829
commit
67319f3e8d
113
mysql-test/main/opt_hint_timeout.result
Normal file
113
mysql-test/main/opt_hint_timeout.result
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#
|
||||||
|
# MAX_EXECUTION_TIME hint testing
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (a INT, b VARCHAR(300));
|
||||||
|
INSERT INTO t1 VALUES (1, 'string');
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
# Correct hint usage
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(10) */* FROM t1 a, t1 b;
|
||||||
|
ERROR 70100: Query execution was interrupted (max_statement_time exceeded)
|
||||||
|
EXPLAIN EXTENDED SELECT /*+ MAX_EXECUTION_TIME(000149) */* FROM t1;
|
||||||
|
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||||
|
1 SIMPLE t1 ALL NULL NULL NULL NULL 512 100.00
|
||||||
|
Warnings:
|
||||||
|
Note 1003 select /*+ MAX_EXECUTION_TIME(000149) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1`
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(20) */ *, SLEEP(1) FROM t1 UNION SELECT 1, 2, 3;
|
||||||
|
ERROR 70100: Query execution was interrupted (max_statement_time exceeded)
|
||||||
|
(SELECT /*+ MAX_EXECUTION_TIME(30) */ *, SLEEP(1) FROM t1) UNION (SELECT 1, 2, 3);
|
||||||
|
ERROR 70100: Query execution was interrupted (max_statement_time exceeded)
|
||||||
|
((SELECT /*+ MAX_EXECUTION_TIME(50) */ *, SLEEP(1) FROM t1));
|
||||||
|
ERROR 70100: Query execution was interrupted (max_statement_time exceeded)
|
||||||
|
# Check that prepared statements process the hint correctly
|
||||||
|
PREPARE s FROM 'SELECT /*+ MAX_EXECUTION_TIME(20) */ seq, SLEEP(1) FROM seq_1_to_10';
|
||||||
|
EXECUTE s;
|
||||||
|
ERROR 70100: Query execution was interrupted (max_statement_time exceeded)
|
||||||
|
EXECUTE s;
|
||||||
|
ERROR 70100: Query execution was interrupted (max_statement_time exceeded)
|
||||||
|
# Hint duplication
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(10) MAX_EXECUTION_TIME(100) */ count(*) FROM t1;
|
||||||
|
count(*)
|
||||||
|
512
|
||||||
|
Warnings:
|
||||||
|
Warning 4202 Hint MAX_EXECUTION_TIME(100) is ignored as conflicting/duplicated
|
||||||
|
# Wrong values
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(0) */ count(*) FROM t1;
|
||||||
|
count(*)
|
||||||
|
512
|
||||||
|
Warnings:
|
||||||
|
Warning 1912 Incorrect value '0' for option 'MAX_EXECUTION_TIME'
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(-1) */ count(*) FROM t1;
|
||||||
|
count(*)
|
||||||
|
512
|
||||||
|
Warnings:
|
||||||
|
Warning 1064 Optimizer hint syntax error near '-1) */ count(*) FROM t1' at line 1
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(4294967296) */ count(*) FROM t1;
|
||||||
|
count(*)
|
||||||
|
512
|
||||||
|
Warnings:
|
||||||
|
Warning 1912 Incorrect value '4294967296' for option 'MAX_EXECUTION_TIME'
|
||||||
|
# Conflicting max_statement_time and hint (must issue a warning)
|
||||||
|
SET STATEMENT max_statement_time=1 FOR
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(500) */ count(*) FROM t1 a;
|
||||||
|
count(*)
|
||||||
|
512
|
||||||
|
Warnings:
|
||||||
|
Warning 4202 Hint MAX_EXECUTION_TIME(500) is ignored as conflicting/duplicated
|
||||||
|
|
||||||
|
# only SELECT statements supports the MAX_EXECUTION_TIME hint (warning):
|
||||||
|
|
||||||
|
CREATE TABLE t2 (i INT);
|
||||||
|
INSERT /*+ MAX_EXECUTION_TIME(10) */ INTO t2 SELECT 1;
|
||||||
|
Warnings:
|
||||||
|
Warning 4172 'MAX_EXECUTION_TIME(10)' is not allowed in this context
|
||||||
|
REPLACE /*+ MAX_EXECUTION_TIME(15) */ INTO t2 SELECT 1;
|
||||||
|
Warnings:
|
||||||
|
Warning 4172 'MAX_EXECUTION_TIME(15)' is not allowed in this context
|
||||||
|
UPDATE /*+ MAX_EXECUTION_TIME(23) */ t2 SET i = 1;
|
||||||
|
Warnings:
|
||||||
|
Warning 4172 'MAX_EXECUTION_TIME(23)' is not allowed in this context
|
||||||
|
DELETE /*+ MAX_EXECUTION_TIME(5000) */ FROM t2 WHERE i = 1;
|
||||||
|
Warnings:
|
||||||
|
Warning 4172 'MAX_EXECUTION_TIME(5000)' is not allowed in this context
|
||||||
|
# Not supported inside stored procedures/functions
|
||||||
|
CREATE PROCEDURE p1() BEGIN SELECT /*+ MAX_EXECUTION_TIME(10) */ count(*) FROM t1 a, t1 b
|
||||||
|
INTO @a; END|
|
||||||
|
CALL p1();
|
||||||
|
Warnings:
|
||||||
|
Warning 4172 'MAX_EXECUTION_TIME(10)' is not allowed in this context
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
# Hint in a subquery is not allowed (warning):
|
||||||
|
SELECT 1 FROM (SELECT /*+ MAX_EXECUTION_TIME(10) */ 1) a;
|
||||||
|
1
|
||||||
|
1
|
||||||
|
Warnings:
|
||||||
|
Warning 4172 'MAX_EXECUTION_TIME(10)' is not allowed in this context
|
||||||
|
# Hint is allowed only for the first select of UNION (warning):
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(20) */ count(*) FROM t1
|
||||||
|
UNION
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(30) */ count(*) FROM t1;
|
||||||
|
count(*)
|
||||||
|
512
|
||||||
|
Warnings:
|
||||||
|
Warning 4202 Hint MAX_EXECUTION_TIME(30) is ignored as conflicting/duplicated
|
||||||
|
SELECT count(*) FROM t1
|
||||||
|
UNION
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(30) */ count(*) FROM t1;
|
||||||
|
count(*)
|
||||||
|
512
|
||||||
|
Warnings:
|
||||||
|
Warning 4172 'MAX_EXECUTION_TIME(30)' is not allowed in this context
|
||||||
|
# Check that hint actually works:
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(20) */ count(*), SLEEP(1) FROM t1
|
||||||
|
UNION
|
||||||
|
SELECT count(*), SLEEP(1) FROM t1;
|
||||||
|
ERROR 70100: Query execution was interrupted (max_statement_time exceeded)
|
||||||
|
DROP TABLE t1, t2;
|
95
mysql-test/main/opt_hint_timeout.test
Normal file
95
mysql-test/main/opt_hint_timeout.test
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
--source include/have_sequence.inc
|
||||||
|
--echo #
|
||||||
|
--echo # MAX_EXECUTION_TIME hint testing
|
||||||
|
--echo #
|
||||||
|
--enable_prepare_warnings
|
||||||
|
|
||||||
|
CREATE TABLE t1 (a INT, b VARCHAR(300));
|
||||||
|
INSERT INTO t1 VALUES (1, 'string');
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
INSERT INTO t1 SELECT * FROM t1;
|
||||||
|
|
||||||
|
-- disable_query_log
|
||||||
|
-- disable_result_log
|
||||||
|
analyze table t1;
|
||||||
|
-- enable_result_log
|
||||||
|
-- enable_query_log
|
||||||
|
|
||||||
|
--echo # Correct hint usage
|
||||||
|
--error ER_STATEMENT_TIMEOUT
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(10) */* FROM t1 a, t1 b;
|
||||||
|
|
||||||
|
EXPLAIN EXTENDED SELECT /*+ MAX_EXECUTION_TIME(000149) */* FROM t1;
|
||||||
|
|
||||||
|
--error ER_STATEMENT_TIMEOUT
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(20) */ *, SLEEP(1) FROM t1 UNION SELECT 1, 2, 3;
|
||||||
|
--error ER_STATEMENT_TIMEOUT
|
||||||
|
(SELECT /*+ MAX_EXECUTION_TIME(30) */ *, SLEEP(1) FROM t1) UNION (SELECT 1, 2, 3);
|
||||||
|
--error ER_STATEMENT_TIMEOUT
|
||||||
|
((SELECT /*+ MAX_EXECUTION_TIME(50) */ *, SLEEP(1) FROM t1));
|
||||||
|
|
||||||
|
--echo # Check that prepared statements process the hint correctly
|
||||||
|
PREPARE s FROM 'SELECT /*+ MAX_EXECUTION_TIME(20) */ seq, SLEEP(1) FROM seq_1_to_10';
|
||||||
|
--error ER_STATEMENT_TIMEOUT
|
||||||
|
EXECUTE s;
|
||||||
|
--error ER_STATEMENT_TIMEOUT
|
||||||
|
EXECUTE s;
|
||||||
|
|
||||||
|
--echo # Hint duplication
|
||||||
|
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(10) MAX_EXECUTION_TIME(100) */ count(*) FROM t1;
|
||||||
|
|
||||||
|
--echo # Wrong values
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(0) */ count(*) FROM t1;
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(-1) */ count(*) FROM t1;
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(4294967296) */ count(*) FROM t1;
|
||||||
|
|
||||||
|
--echo # Conflicting max_statement_time and hint (must issue a warning)
|
||||||
|
SET STATEMENT max_statement_time=1 FOR
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(500) */ count(*) FROM t1 a;
|
||||||
|
|
||||||
|
--echo
|
||||||
|
--echo # only SELECT statements supports the MAX_EXECUTION_TIME hint (warning):
|
||||||
|
--echo
|
||||||
|
CREATE TABLE t2 (i INT);
|
||||||
|
INSERT /*+ MAX_EXECUTION_TIME(10) */ INTO t2 SELECT 1;
|
||||||
|
REPLACE /*+ MAX_EXECUTION_TIME(15) */ INTO t2 SELECT 1;
|
||||||
|
UPDATE /*+ MAX_EXECUTION_TIME(23) */ t2 SET i = 1;
|
||||||
|
DELETE /*+ MAX_EXECUTION_TIME(5000) */ FROM t2 WHERE i = 1;
|
||||||
|
|
||||||
|
--echo # Not supported inside stored procedures/functions
|
||||||
|
DELIMITER |;
|
||||||
|
CREATE PROCEDURE p1() BEGIN SELECT /*+ MAX_EXECUTION_TIME(10) */ count(*) FROM t1 a, t1 b
|
||||||
|
INTO @a; END|
|
||||||
|
DELIMITER ;|
|
||||||
|
|
||||||
|
CALL p1();
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
--echo # Hint in a subquery is not allowed (warning):
|
||||||
|
SELECT 1 FROM (SELECT /*+ MAX_EXECUTION_TIME(10) */ 1) a;
|
||||||
|
|
||||||
|
--echo # Hint is allowed only for the first select of UNION (warning):
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(20) */ count(*) FROM t1
|
||||||
|
UNION
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(30) */ count(*) FROM t1;
|
||||||
|
|
||||||
|
SELECT count(*) FROM t1
|
||||||
|
UNION
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(30) */ count(*) FROM t1;
|
||||||
|
|
||||||
|
--echo # Check that hint actually works:
|
||||||
|
--error ER_STATEMENT_TIMEOUT
|
||||||
|
SELECT /*+ MAX_EXECUTION_TIME(20) */ count(*), SLEEP(1) FROM t1
|
||||||
|
UNION
|
||||||
|
SELECT count(*), SLEEP(1) FROM t1;
|
||||||
|
|
||||||
|
DROP TABLE t1, t2;
|
||||||
|
|
@ -1799,6 +1799,14 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
|
|||||||
Warnings:
|
Warnings:
|
||||||
Note 1003 select /*+ QB_NAME(`a
|
Note 1003 select /*+ QB_NAME(`a
|
||||||
b`) */ 1 AS `1`
|
b`) */ 1 AS `1`
|
||||||
|
# Identifiers starting with digits must be supported:
|
||||||
|
CREATE OR REPLACE TABLE 0a (8a INT, KEY 6a(8a));
|
||||||
|
EXPLAIN EXTENDED SELECT /*+ NO_MRR(0a 6a) BKA(0a)*/ 8a FROM 0a;
|
||||||
|
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||||
|
1 SIMPLE 0a system NULL NULL NULL NULL 0 0.00 Const row not found
|
||||||
|
Warnings:
|
||||||
|
Note 1003 select /*+ BKA(`0a`@`select#1`) NO_MRR(`0a`@`select#1` `6a`) */ NULL AS `8a` from `test`.`0a`
|
||||||
|
DROP TABLE 0a;
|
||||||
# hint syntax error: empty quoted identifier
|
# hint syntax error: empty quoted identifier
|
||||||
EXPLAIN EXTENDED SELECT /*+ QB_NAME(``) */ 1;
|
EXPLAIN EXTENDED SELECT /*+ QB_NAME(``) */ 1;
|
||||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||||
@ -1876,7 +1884,7 @@ SELECT /*+ NO_ICP(10) */ 1;
|
|||||||
1
|
1
|
||||||
1
|
1
|
||||||
Warnings:
|
Warnings:
|
||||||
Warning 4204 Unresolved table name `10`@`select#1` for NO_ICP hint
|
Warning 1064 Optimizer hint syntax error near '10) */ 1' at line 1
|
||||||
SELECT /*+ NO_ICP( */ 1;
|
SELECT /*+ NO_ICP( */ 1;
|
||||||
1
|
1
|
||||||
1
|
1
|
||||||
|
@ -914,6 +914,11 @@ EXPLAIN EXTENDED SELECT /*+ QB_NAME(`*b`) */ 1;
|
|||||||
EXPLAIN EXTENDED SELECT /*+ QB_NAME(`a
|
EXPLAIN EXTENDED SELECT /*+ QB_NAME(`a
|
||||||
b`) */ 1;
|
b`) */ 1;
|
||||||
|
|
||||||
|
--echo # Identifiers starting with digits must be supported:
|
||||||
|
CREATE OR REPLACE TABLE 0a (8a INT, KEY 6a(8a));
|
||||||
|
EXPLAIN EXTENDED SELECT /*+ NO_MRR(0a 6a) BKA(0a)*/ 8a FROM 0a;
|
||||||
|
DROP TABLE 0a;
|
||||||
|
|
||||||
--echo # hint syntax error: empty quoted identifier
|
--echo # hint syntax error: empty quoted identifier
|
||||||
EXPLAIN EXTENDED SELECT /*+ QB_NAME(``) */ 1;
|
EXPLAIN EXTENDED SELECT /*+ QB_NAME(``) */ 1;
|
||||||
|
|
||||||
|
@ -4857,7 +4857,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi)
|
|||||||
set/reset the slave thread's timer; a Rows_log_event update needs to set
|
set/reset the slave thread's timer; a Rows_log_event update needs to set
|
||||||
the timer itself
|
the timer itself
|
||||||
*/
|
*/
|
||||||
thd->set_query_timer();
|
thd->set_query_timer_if_needed();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If there are no tables open, this must be the first row event seen
|
If there are no tables open, this must be the first row event seen
|
||||||
|
188
sql/opt_hints.cc
188
sql/opt_hints.cc
@ -19,7 +19,6 @@
|
|||||||
#include "sql_lex.h"
|
#include "sql_lex.h"
|
||||||
#include "sql_select.h"
|
#include "sql_select.h"
|
||||||
#include "opt_hints.h"
|
#include "opt_hints.h"
|
||||||
#include "opt_hints_parser.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Information about hints. Sould be
|
Information about hints. Sould be
|
||||||
@ -34,12 +33,13 @@
|
|||||||
|
|
||||||
struct st_opt_hint_info opt_hint_info[]=
|
struct st_opt_hint_info opt_hint_info[]=
|
||||||
{
|
{
|
||||||
{{STRING_WITH_LEN("BKA")}, true, true},
|
{{STRING_WITH_LEN("BKA")}, true, false},
|
||||||
{{STRING_WITH_LEN("BNL")}, true, true},
|
{{STRING_WITH_LEN("BNL")}, true, false},
|
||||||
{{STRING_WITH_LEN("ICP")}, true, true},
|
{{STRING_WITH_LEN("ICP")}, true, false},
|
||||||
{{STRING_WITH_LEN("MRR")}, true, true},
|
{{STRING_WITH_LEN("MRR")}, true, false},
|
||||||
{{STRING_WITH_LEN("NO_RANGE_OPTIMIZATION")}, true, true},
|
{{STRING_WITH_LEN("NO_RANGE_OPTIMIZATION")}, true, false},
|
||||||
{{STRING_WITH_LEN("QB_NAME")}, false, false},
|
{{STRING_WITH_LEN("QB_NAME")}, false, false},
|
||||||
|
{{STRING_WITH_LEN("MAX_EXECUTION_TIME")}, false, true},
|
||||||
{null_clex_str, 0, 0}
|
{null_clex_str, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -52,12 +52,14 @@ const LEX_CSTRING sys_qb_prefix= {"select#", 7};
|
|||||||
|
|
||||||
static const Lex_ident_sys null_ident_sys;
|
static const Lex_ident_sys null_ident_sys;
|
||||||
|
|
||||||
|
template<typename Hint_type>
|
||||||
static
|
static
|
||||||
void print_warn(THD *thd, uint err_code, opt_hints_enum hint_type,
|
void print_warn(THD *thd, uint err_code, opt_hints_enum hint_type,
|
||||||
bool hint_state,
|
bool hint_state,
|
||||||
const Lex_ident_sys *qb_name_arg,
|
const Lex_ident_sys *qb_name_arg,
|
||||||
const Lex_ident_sys *table_name_arg,
|
const Lex_ident_sys *table_name_arg,
|
||||||
const Lex_ident_sys *key_name_arg)
|
const Lex_ident_sys *key_name_arg,
|
||||||
|
Hint_type *hint)
|
||||||
{
|
{
|
||||||
String str;
|
String str;
|
||||||
|
|
||||||
@ -77,6 +79,18 @@ void print_warn(THD *thd, uint err_code, opt_hints_enum hint_type,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ER_BAD_OPTION_VALUE with two arguments. hint argument is required here */
|
||||||
|
if (err_code == ER_BAD_OPTION_VALUE)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(hint);
|
||||||
|
String args;
|
||||||
|
hint->append_args(thd, &args);
|
||||||
|
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||||
|
err_code, ER_THD(thd, err_code),
|
||||||
|
args.c_ptr_safe(), str.c_ptr_safe());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* ER_WARN_CONFLICTING_HINT with one argument */
|
/* ER_WARN_CONFLICTING_HINT with one argument */
|
||||||
str.append('(');
|
str.append('(');
|
||||||
|
|
||||||
@ -105,6 +119,15 @@ void print_warn(THD *thd, uint err_code, opt_hints_enum hint_type,
|
|||||||
append_identifier(thd, &str, key_name_arg->str, key_name_arg->length);
|
append_identifier(thd, &str, key_name_arg->str, key_name_arg->length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Append additional hint arguments if they exist */
|
||||||
|
if (hint)
|
||||||
|
{
|
||||||
|
if (qb_name_arg || table_name_arg || key_name_arg)
|
||||||
|
str.append(' ');
|
||||||
|
|
||||||
|
hint->append_args(thd, &str);
|
||||||
|
}
|
||||||
|
|
||||||
str.append(')');
|
str.append(')');
|
||||||
|
|
||||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||||
@ -131,8 +154,6 @@ static Opt_hints_global *get_global_hints(Parse_context *pc)
|
|||||||
lex->opt_hints_global= new (pc->thd->mem_root)
|
lex->opt_hints_global= new (pc->thd->mem_root)
|
||||||
Opt_hints_global(pc->thd->mem_root);
|
Opt_hints_global(pc->thd->mem_root);
|
||||||
}
|
}
|
||||||
if (lex->opt_hints_global)
|
|
||||||
lex->opt_hints_global->set_resolved();
|
|
||||||
return lex->opt_hints_global;
|
return lex->opt_hints_global;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +205,7 @@ static Opt_hints_qb *find_qb_hints(Parse_context *pc,
|
|||||||
if (qb == NULL)
|
if (qb == NULL)
|
||||||
{
|
{
|
||||||
print_warn(pc->thd, ER_WARN_UNKNOWN_QB_NAME, hint_type, hint_state,
|
print_warn(pc->thd, ER_WARN_UNKNOWN_QB_NAME, hint_type, hint_state,
|
||||||
&qb_name, NULL, NULL);
|
&qb_name, NULL, NULL, (Parser::Hint*) NULL);
|
||||||
}
|
}
|
||||||
return qb;
|
return qb;
|
||||||
}
|
}
|
||||||
@ -252,7 +273,21 @@ void Opt_hints::print(THD *thd, String *str)
|
|||||||
{
|
{
|
||||||
append_hint_type(str, static_cast<opt_hints_enum>(i));
|
append_hint_type(str, static_cast<opt_hints_enum>(i));
|
||||||
str->append(STRING_WITH_LEN("("));
|
str->append(STRING_WITH_LEN("("));
|
||||||
|
uint32 len_before_name= str->length();
|
||||||
append_name(thd, str);
|
append_name(thd, str);
|
||||||
|
uint32 len_after_name= str->length();
|
||||||
|
if (len_after_name > len_before_name)
|
||||||
|
str->append(' ');
|
||||||
|
if (opt_hint_info[i].has_arguments)
|
||||||
|
{
|
||||||
|
std::function<void(THD*, String*)> args_printer= get_args_printer();
|
||||||
|
args_printer(thd, str);
|
||||||
|
}
|
||||||
|
if (str->length() == len_after_name + 1)
|
||||||
|
{
|
||||||
|
// No additional arguments were printed, trim the space added before
|
||||||
|
str->length(len_after_name);
|
||||||
|
}
|
||||||
str->append(STRING_WITH_LEN(") "));
|
str->append(STRING_WITH_LEN(") "));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -396,7 +431,7 @@ static bool get_hint_state(Opt_hints *hint,
|
|||||||
{
|
{
|
||||||
DBUG_ASSERT(parent_hint);
|
DBUG_ASSERT(parent_hint);
|
||||||
|
|
||||||
if (opt_hint_info[type_arg].switch_hint)
|
if (!opt_hint_info[type_arg].has_arguments)
|
||||||
{
|
{
|
||||||
if (hint && hint->is_specified(type_arg))
|
if (hint && hint->is_specified(type_arg))
|
||||||
{
|
{
|
||||||
@ -412,7 +447,7 @@ static bool get_hint_state(Opt_hints *hint,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Complex hint, not implemented atm */
|
/* Complex hint with arguments, not implemented atm */
|
||||||
DBUG_ASSERT(0);
|
DBUG_ASSERT(0);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -524,8 +559,10 @@ bool Optimizer_hint_parser::Table_level_hint::resolve(Parse_context *pc) const
|
|||||||
{
|
{
|
||||||
// e.g. BKA(@qb1)
|
// e.g. BKA(@qb1)
|
||||||
if (qb->set_switch(hint_state, hint_type, false))
|
if (qb->set_switch(hint_state, hint_type, false))
|
||||||
|
{
|
||||||
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
|
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
|
||||||
&qb_name_sys, NULL, NULL);
|
&qb_name_sys, NULL, NULL, (Parser::Hint*) NULL);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -539,8 +576,10 @@ bool Optimizer_hint_parser::Table_level_hint::resolve(Parse_context *pc) const
|
|||||||
if (!tab)
|
if (!tab)
|
||||||
return true;
|
return true;
|
||||||
if (tab->set_switch(hint_state, hint_type, true))
|
if (tab->set_switch(hint_state, hint_type, true))
|
||||||
|
{
|
||||||
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
|
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
|
||||||
&qb_name_sys, &table_name_sys, NULL);
|
&qb_name_sys, &table_name_sys, NULL, (Parser::Hint*) NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -556,8 +595,10 @@ bool Optimizer_hint_parser::Table_level_hint::resolve(Parse_context *pc) const
|
|||||||
{
|
{
|
||||||
// e.g. BKA()
|
// e.g. BKA()
|
||||||
if (qb->set_switch(hint_state, hint_type, false))
|
if (qb->set_switch(hint_state, hint_type, false))
|
||||||
|
{
|
||||||
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
|
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
|
||||||
&null_ident_sys, NULL, NULL);
|
&null_ident_sys, NULL, NULL, (Parser::Hint*) NULL);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (const Table_name &table : table_name_list)
|
for (const Table_name &table : table_name_list)
|
||||||
@ -568,8 +609,11 @@ bool Optimizer_hint_parser::Table_level_hint::resolve(Parse_context *pc) const
|
|||||||
if (!tab)
|
if (!tab)
|
||||||
return true;
|
return true;
|
||||||
if (tab->set_switch(hint_state, hint_type, true))
|
if (tab->set_switch(hint_state, hint_type, true))
|
||||||
|
{
|
||||||
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
|
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
|
||||||
&null_ident_sys, &table_name_sys, NULL);
|
&null_ident_sys, &table_name_sys,
|
||||||
|
NULL, (Parser::Hint*) NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const Hint_param_table &table : opt_hint_param_table_list)
|
for (const Hint_param_table &table : opt_hint_param_table_list)
|
||||||
@ -586,15 +630,17 @@ bool Optimizer_hint_parser::Table_level_hint::resolve(Parse_context *pc) const
|
|||||||
if (!tab)
|
if (!tab)
|
||||||
return true;
|
return true;
|
||||||
if (tab->set_switch(hint_state, hint_type, true))
|
if (tab->set_switch(hint_state, hint_type, true))
|
||||||
|
{
|
||||||
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
|
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
|
||||||
&qb_name_sys, &table_name_sys, NULL);
|
&qb_name_sys, &table_name_sys, NULL, (Parser::Hint*) NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Optimizer_hint_parser::Index_level_hint::resolve(Parse_context *pc) const
|
bool Parser::Index_level_hint::resolve(Parse_context *pc) const
|
||||||
{
|
{
|
||||||
const Index_level_hint_type &index_level_hint_type= *this;
|
const Index_level_hint_type &index_level_hint_type= *this;
|
||||||
opt_hints_enum hint_type;
|
opt_hints_enum hint_type;
|
||||||
@ -639,8 +685,10 @@ bool Optimizer_hint_parser::Index_level_hint::resolve(Parse_context *pc) const
|
|||||||
if (is_empty()) // Table level hint
|
if (is_empty()) // Table level hint
|
||||||
{
|
{
|
||||||
if (tab->set_switch(hint_state, hint_type, false))
|
if (tab->set_switch(hint_state, hint_type, false))
|
||||||
|
{
|
||||||
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
|
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
|
||||||
&qb_name_sys, &table_name_sys, NULL);
|
&qb_name_sys, &table_name_sys, NULL, (Parser::Hint*) NULL);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,15 +704,18 @@ bool Optimizer_hint_parser::Index_level_hint::resolve(Parse_context *pc) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (idx->set_switch(hint_state, hint_type, true))
|
if (idx->set_switch(hint_state, hint_type, true))
|
||||||
|
{
|
||||||
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
|
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
|
||||||
&qb_name_sys, &table_name_sys, &index_name_sys);
|
&qb_name_sys, &table_name_sys, &index_name_sys,
|
||||||
|
(Parser::Hint*) NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Optimizer_hint_parser::Qb_name_hint::resolve(Parse_context *pc) const
|
bool Parser::Qb_name_hint::resolve(Parse_context *pc) const
|
||||||
{
|
{
|
||||||
Opt_hints_qb *qb= pc->select->opt_hints_qb;
|
Opt_hints_qb *qb= pc->select->opt_hints_qb;
|
||||||
|
|
||||||
@ -676,7 +727,7 @@ bool Optimizer_hint_parser::Qb_name_hint::resolve(Parse_context *pc) const
|
|||||||
qb->get_parent()->find_by_name(qb_name_sys)) // Name is already used
|
qb->get_parent()->find_by_name(qb_name_sys)) // Name is already used
|
||||||
{
|
{
|
||||||
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, QB_NAME_HINT_ENUM, true,
|
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, QB_NAME_HINT_ENUM, true,
|
||||||
&qb_name_sys, NULL, NULL);
|
&qb_name_sys, NULL, NULL, (Parser::Hint*) NULL);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -684,6 +735,94 @@ bool Optimizer_hint_parser::Qb_name_hint::resolve(Parse_context *pc) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is the first step of MAX_EXECUTION_TIME() hint resolution. It is invoked
|
||||||
|
during the parsing phase, but at this stage some essential information is
|
||||||
|
not yet available, preventing a full validation of the hint.
|
||||||
|
Particularly, the type of SQL command, mark of a stored procedure execution
|
||||||
|
or whether SELECT_LEX is not top-level (i.e., a subquery) are not yet set.
|
||||||
|
However, some basic checks like the numeric argument validation or hint
|
||||||
|
duplication check can still be performed.
|
||||||
|
The second step of hint validation is performed during the JOIN preparation
|
||||||
|
phase, within Opt_hints_global::resolve(). By this point, all necessary
|
||||||
|
information is up-to-date, allowing the hint to be fully resolved
|
||||||
|
*/
|
||||||
|
bool Parser::Max_execution_time_hint::resolve(Parse_context *pc) const
|
||||||
|
{
|
||||||
|
const Unsigned_Number& hint_arg= *this;
|
||||||
|
const ULonglong_null time_ms= hint_arg.get_ulonglong();
|
||||||
|
|
||||||
|
if (time_ms.is_null() || time_ms.value() == 0 || time_ms.value() > INT_MAX32)
|
||||||
|
{
|
||||||
|
print_warn(pc->thd, ER_BAD_OPTION_VALUE, MAX_EXEC_TIME_HINT_ENUM,
|
||||||
|
true, NULL, NULL, NULL, this);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Opt_hints_global *global_hint= get_global_hints(pc);
|
||||||
|
if (global_hint->is_specified(MAX_EXEC_TIME_HINT_ENUM))
|
||||||
|
{
|
||||||
|
// Hint duplication: /*+ MAX_EXECUTION_TIME ... MAX_EXECUTION_TIME */
|
||||||
|
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, MAX_EXEC_TIME_HINT_ENUM, true,
|
||||||
|
NULL, NULL, NULL, this);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
global_hint->set_switch(true, MAX_EXEC_TIME_HINT_ENUM, false);
|
||||||
|
global_hint->max_exec_time_hint= this;
|
||||||
|
global_hint->max_exec_time_select_lex= pc->select;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Parser::Max_execution_time_hint::append_args(THD *thd, String *str) const
|
||||||
|
{
|
||||||
|
const Unsigned_Number& hint_arg= *this;
|
||||||
|
str->append(ErrConvString(hint_arg.str, hint_arg.length,
|
||||||
|
&my_charset_latin1).lex_cstring());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ulong Parser::Max_execution_time_hint::get_milliseconds() const
|
||||||
|
{
|
||||||
|
const Unsigned_Number& hint_arg= *this;
|
||||||
|
return hint_arg.get_ulonglong().value();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Opt_hints_global::resolve(THD *thd)
|
||||||
|
{
|
||||||
|
if (!max_exec_time_hint || thd->lex->is_ps_or_view_context_analysis())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
2nd step of MAX_EXECUTION_TIME() hint validation. Some checks were already
|
||||||
|
performed during the parsing stage (Max_execution_time_hint::resolve()),
|
||||||
|
but the following checks can only be performed during the JOIN preparation
|
||||||
|
because thd->lex variables are not available during parsing
|
||||||
|
*/
|
||||||
|
if (thd->lex->sql_command != SQLCOM_SELECT || // not a SELECT statement
|
||||||
|
thd->lex->sphead || thd->in_sub_stmt != 0 || // or a SP/trigger/event
|
||||||
|
max_exec_time_select_lex->master_unit() != &thd->lex->unit || // or a subquery
|
||||||
|
max_exec_time_select_lex->select_number != 1) // not a top-level select
|
||||||
|
{
|
||||||
|
print_warn(thd, ER_NOT_ALLOWED_IN_THIS_CONTEXT, MAX_EXEC_TIME_HINT_ENUM,
|
||||||
|
true, NULL, NULL, NULL, max_exec_time_hint);
|
||||||
|
}
|
||||||
|
else if (thd->variables.max_statement_time != 0 ||
|
||||||
|
thd->query_timer.expired == 0)
|
||||||
|
{
|
||||||
|
print_warn(thd, ER_WARN_CONFLICTING_HINT, MAX_EXEC_TIME_HINT_ENUM, true,
|
||||||
|
NULL, NULL, NULL, max_exec_time_hint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
thd->set_query_timer_force(max_exec_time_hint->get_milliseconds() * 1000);
|
||||||
|
}
|
||||||
|
set_resolved();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Optimizer_hint_parser::Hint_list::resolve(Parse_context *pc) const
|
bool Optimizer_hint_parser::Hint_list::resolve(Parse_context *pc) const
|
||||||
{
|
{
|
||||||
@ -717,6 +856,11 @@ bool Optimizer_hint_parser::Hint_list::resolve(Parse_context *pc) const
|
|||||||
if (qb_hint.resolve(pc))
|
if (qb_hint.resolve(pc))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (const Max_execution_time_hint &max_hint= hint)
|
||||||
|
{
|
||||||
|
if (max_hint.resolve(pc))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,7 @@
|
|||||||
#ifndef OPT_HINTS_INCLUDED
|
#ifndef OPT_HINTS_INCLUDED
|
||||||
#define OPT_HINTS_INCLUDED
|
#define OPT_HINTS_INCLUDED
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include "my_config.h"
|
#include "my_config.h"
|
||||||
#include "sql_alloc.h"
|
#include "sql_alloc.h"
|
||||||
#include "sql_list.h"
|
#include "sql_list.h"
|
||||||
@ -73,7 +74,7 @@
|
|||||||
#include "sql_bitmap.h"
|
#include "sql_bitmap.h"
|
||||||
#include "sql_show.h"
|
#include "sql_show.h"
|
||||||
#include "mysqld_error.h"
|
#include "mysqld_error.h"
|
||||||
|
#include "opt_hints_parser.h"
|
||||||
|
|
||||||
struct LEX;
|
struct LEX;
|
||||||
struct TABLE;
|
struct TABLE;
|
||||||
@ -91,6 +92,7 @@ enum opt_hints_enum
|
|||||||
MRR_HINT_ENUM,
|
MRR_HINT_ENUM,
|
||||||
NO_RANGE_HINT_ENUM,
|
NO_RANGE_HINT_ENUM,
|
||||||
QB_NAME_HINT_ENUM,
|
QB_NAME_HINT_ENUM,
|
||||||
|
MAX_EXEC_TIME_HINT_ENUM,
|
||||||
MAX_HINT_ENUM
|
MAX_HINT_ENUM
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -100,9 +102,10 @@ struct st_opt_hint_info
|
|||||||
LEX_CSTRING hint_name; // Hint name.
|
LEX_CSTRING hint_name; // Hint name.
|
||||||
bool check_upper_lvl; // true if upper level hint check is needed (for hints
|
bool check_upper_lvl; // true if upper level hint check is needed (for hints
|
||||||
// which can be specified on more than one level).
|
// which can be specified on more than one level).
|
||||||
bool switch_hint; // true if hint is not complex.
|
bool has_arguments; // true if hint has additional arguments.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef Optimizer_hint_parser Parser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Opt_hints_map contains information
|
Opt_hints_map contains information
|
||||||
@ -304,6 +307,16 @@ public:
|
|||||||
void check_unresolved(THD *thd);
|
void check_unresolved(THD *thd);
|
||||||
virtual void append_name(THD *thd, String *str)= 0;
|
virtual void append_name(THD *thd, String *str)= 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get the function appending additional hint arguments to the printed string,
|
||||||
|
if the arguments exist. For example, SEMIJOIN and SUBQUERY hints may have
|
||||||
|
a list of strategies as additional arguments
|
||||||
|
*/
|
||||||
|
virtual std::function<void(THD*, String*)> get_args_printer() const
|
||||||
|
{
|
||||||
|
return [](THD*, String*) {};
|
||||||
|
}
|
||||||
|
|
||||||
virtual ~Opt_hints() {}
|
virtual ~Opt_hints() {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -336,13 +349,31 @@ protected:
|
|||||||
|
|
||||||
class Opt_hints_global : public Opt_hints
|
class Opt_hints_global : public Opt_hints
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
const Parser::Max_execution_time_hint *max_exec_time_hint= nullptr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
If MAX_EXECUTION_TIME() hint was provided, this pointer is set to
|
||||||
|
the SELECT_LEX which the hint is attached to.
|
||||||
|
NULL if MAX_EXECUTION_TIME() hint is missing.
|
||||||
|
*/
|
||||||
|
st_select_lex *max_exec_time_select_lex= nullptr;
|
||||||
|
|
||||||
Opt_hints_global(MEM_ROOT *mem_root_arg)
|
Opt_hints_global(MEM_ROOT *mem_root_arg)
|
||||||
: Opt_hints(Lex_ident_sys(), NULL, mem_root_arg)
|
: Opt_hints(Lex_ident_sys(), NULL, mem_root_arg)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual void append_name(THD *thd, String *str) override {}
|
virtual void append_name(THD *thd, String *str) override {}
|
||||||
|
|
||||||
|
virtual std::function<void(THD*, String*)> get_args_printer() const override
|
||||||
|
{
|
||||||
|
using std::placeholders::_1;
|
||||||
|
using std::placeholders::_2;
|
||||||
|
return std::bind(&Parser::Max_execution_time_hint::append_args,
|
||||||
|
max_exec_time_hint, _1, _2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool resolve(THD *thd);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,11 +57,30 @@ Optimizer_hint_tokenizer::find_keyword(const LEX_CSTRING &str)
|
|||||||
return TokenID::keyword_QB_NAME;
|
return TokenID::keyword_QB_NAME;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 18:
|
||||||
|
if ("MAX_EXECUTION_TIME"_Lex_ident_column.streq(str))
|
||||||
|
return TokenID::keyword_MAX_EXECUTION_TIME;
|
||||||
|
break;
|
||||||
|
|
||||||
case 21:
|
case 21:
|
||||||
if ("NO_RANGE_OPTIMIZATION"_Lex_ident_column.streq(str))
|
if ("NO_RANGE_OPTIMIZATION"_Lex_ident_column.streq(str))
|
||||||
return TokenID::keyword_NO_RANGE_OPTIMIZATION;
|
return TokenID::keyword_NO_RANGE_OPTIMIZATION;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (str.length > 0 && (str.str[0] >= '0' && str.str[0] <= '9'))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
If all characters are digits, qualify the token as a number,
|
||||||
|
otherwise as an identifier
|
||||||
|
*/
|
||||||
|
for(size_t i = 1; i < str.length; i++)
|
||||||
|
{
|
||||||
|
if (str.str[i] < '0' || str.str[i] > '9')
|
||||||
|
return TokenID::tIDENT;
|
||||||
|
}
|
||||||
|
return TokenID::tUNSIGNED_NUMBER;
|
||||||
|
}
|
||||||
return TokenID::tIDENT;
|
return TokenID::tIDENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "simple_tokenizer.h"
|
#include "simple_tokenizer.h"
|
||||||
#include "sql_list.h"
|
#include "sql_list.h"
|
||||||
#include "sql_string.h"
|
#include "sql_string.h"
|
||||||
|
#include "sql_type_int.h"
|
||||||
#include "simple_parser.h"
|
#include "simple_parser.h"
|
||||||
|
|
||||||
class st_select_lex;
|
class st_select_lex;
|
||||||
@ -72,9 +73,11 @@ public:
|
|||||||
keyword_NO_RANGE_OPTIMIZATION,
|
keyword_NO_RANGE_OPTIMIZATION,
|
||||||
keyword_MRR,
|
keyword_MRR,
|
||||||
keyword_QB_NAME,
|
keyword_QB_NAME,
|
||||||
|
keyword_MAX_EXECUTION_TIME,
|
||||||
|
|
||||||
// Other token types
|
// Other token types
|
||||||
tIDENT
|
tIDENT,
|
||||||
|
tUNSIGNED_NUMBER
|
||||||
};
|
};
|
||||||
|
|
||||||
class Token: public Lex_cstring
|
class Token: public Lex_cstring
|
||||||
@ -240,6 +243,13 @@ private:
|
|||||||
using TOKEN::TOKEN;
|
using TOKEN::TOKEN;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Keyword_MAX_EXECUTION_TIME:
|
||||||
|
public TOKEN<Parser, TokenID::keyword_MAX_EXECUTION_TIME>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using TOKEN::TOKEN;
|
||||||
|
};
|
||||||
|
|
||||||
class Identifier: public TOKEN<Parser, TokenID::tIDENT>
|
class Identifier: public TOKEN<Parser, TokenID::tIDENT>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -258,6 +268,28 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Unsigned_Number: public TOKEN<Parser, TokenID::tUNSIGNED_NUMBER>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using TOKEN::TOKEN;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Converts token string to a non-negative number ( >=0 ).
|
||||||
|
Returns the converted number if the conversion succeeds.
|
||||||
|
Returns non-NULL ULonglong_null value on successful string conversion and
|
||||||
|
NULL ULonglong_null if the conversion failed or the number is negative
|
||||||
|
*/
|
||||||
|
ULonglong_null get_ulonglong() const
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
char *end= const_cast<char *>(str + length);
|
||||||
|
longlong n= my_strtoll10(str, &end, &error);
|
||||||
|
if (error != 0 || end != str + length || n < 0)
|
||||||
|
return ULonglong_null(0, true);
|
||||||
|
return ULonglong_null(n, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class LParen: public TOKEN<Parser, TokenID::tLPAREN>
|
class LParen: public TOKEN<Parser, TokenID::tLPAREN>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -554,21 +586,40 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
// max_execution_time_hint ::= MAX_EXECUTION_TIME ( milliseconds )
|
||||||
|
class Max_execution_time_hint: public AND4<Parser,
|
||||||
|
Keyword_MAX_EXECUTION_TIME,
|
||||||
|
LParen,
|
||||||
|
Unsigned_Number,
|
||||||
|
RParen>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using AND4::AND4;
|
||||||
|
|
||||||
|
bool resolve(Parse_context *pc) const;
|
||||||
|
void append_args(THD *thd, String *str) const;
|
||||||
|
ulong get_milliseconds() const;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
hint ::= index_level_hint
|
hint ::= index_level_hint
|
||||||
| table_level_hint
|
| table_level_hint
|
||||||
| qb_name_hint
|
| qb_name_hint
|
||||||
|
| statement_level_hint
|
||||||
*/
|
*/
|
||||||
class Hint: public OR3<Parser,
|
class Hint: public OR4<Parser,
|
||||||
Index_level_hint,
|
Index_level_hint,
|
||||||
Table_level_hint,
|
Table_level_hint,
|
||||||
Qb_name_hint>
|
Qb_name_hint,
|
||||||
|
Max_execution_time_hint>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using OR3::OR3;
|
using OR4::OR4;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
// hint_list ::= hint [ hint... ]
|
// hint_list ::= hint [ hint... ]
|
||||||
class Hint_list_container: public List<Hint>
|
class Hint_list_container: public List<Hint>
|
||||||
{
|
{
|
||||||
|
@ -380,7 +380,7 @@ protected:
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A rule consisting of a choice of thee rules:
|
A rule consisting of a choice of three rules:
|
||||||
rule ::= rule1 | rule2 | rule3
|
rule ::= rule1 | rule2 | rule3
|
||||||
|
|
||||||
For the case when the three branches have incompatible storage
|
For the case when the three branches have incompatible storage
|
||||||
@ -478,7 +478,50 @@ protected:
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A list with at least MIN_COUNT elements (typlically 0 or 1),
|
A rule consisting of a choice of four rules:
|
||||||
|
rule ::= rule1 | rule2 | rule3 | rule4
|
||||||
|
|
||||||
|
For the case when the three branches have incompatible storage
|
||||||
|
*/
|
||||||
|
template<class PARSER, class A, class B, class C, class D>
|
||||||
|
class OR4: public A, public B, public C, public D
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OR4()
|
||||||
|
{ }
|
||||||
|
OR4(OR4 &&rhs)
|
||||||
|
:A(std::move(static_cast<A&&>(rhs))),
|
||||||
|
B(std::move(static_cast<B&&>(rhs))),
|
||||||
|
C(std::move(static_cast<C&&>(rhs))),
|
||||||
|
D(std::move(static_cast<D&&>(rhs)))
|
||||||
|
{ }
|
||||||
|
OR4 & operator=(OR4 &&rhs)
|
||||||
|
{
|
||||||
|
A::operator=(std::move(static_cast<A&&>(rhs)));
|
||||||
|
B::operator=(std::move(static_cast<B&&>(rhs)));
|
||||||
|
C::operator=(std::move(static_cast<C&&>(rhs)));
|
||||||
|
D::operator=(std::move(static_cast<D&&>(rhs)));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
OR4(PARSER *p)
|
||||||
|
:A(p),
|
||||||
|
B(A::operator bool() ? B() : B(p)),
|
||||||
|
C(A::operator bool() || B::operator bool() ? C() : C(p)),
|
||||||
|
D(A::operator bool() || B::operator bool() || C::operator bool() ?
|
||||||
|
D() : D(p))
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(!operator bool() || !p->is_error());
|
||||||
|
}
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return A::operator bool() || B::operator bool() || C::operator bool() ||
|
||||||
|
D::operator bool();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
A list with at least MIN_COUNT elements (typically 0 or 1),
|
||||||
with or without a token separator between elements:
|
with or without a token separator between elements:
|
||||||
|
|
||||||
list ::= element [ {, element }... ] // with a separator
|
list ::= element [ {, element }... ] // with a separator
|
||||||
|
@ -8229,6 +8229,7 @@ void make_leaves_list(THD *thd, List<TABLE_LIST> &list, TABLE_LIST *tables,
|
|||||||
refresh It is only refresh for subquery
|
refresh It is only refresh for subquery
|
||||||
select_insert It is SELECT ... INSERT command
|
select_insert It is SELECT ... INSERT command
|
||||||
full_table_list a parameter to pass to the make_leaves_list function
|
full_table_list a parameter to pass to the make_leaves_list function
|
||||||
|
resolve_opt_hints Whether optimizer hints must be resolved here
|
||||||
|
|
||||||
NOTE
|
NOTE
|
||||||
Check also that the 'used keys' and 'ignored keys' exists and set up the
|
Check also that the 'used keys' and 'ignored keys' exists and set up the
|
||||||
@ -8248,7 +8249,7 @@ void make_leaves_list(THD *thd, List<TABLE_LIST> &list, TABLE_LIST *tables,
|
|||||||
bool setup_tables(THD *thd, Name_resolution_context *context,
|
bool setup_tables(THD *thd, Name_resolution_context *context,
|
||||||
List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
|
List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
|
||||||
List<TABLE_LIST> &leaves, bool select_insert,
|
List<TABLE_LIST> &leaves, bool select_insert,
|
||||||
bool full_table_list)
|
bool full_table_list, bool resolve_opt_hints)
|
||||||
{
|
{
|
||||||
uint tablenr= 0;
|
uint tablenr= 0;
|
||||||
List_iterator<TABLE_LIST> ti(leaves);
|
List_iterator<TABLE_LIST> ti(leaves);
|
||||||
@ -8393,8 +8394,13 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
|
|||||||
if (setup_natural_join_row_types(thd, from_clause, context))
|
if (setup_natural_join_row_types(thd, from_clause, context))
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
|
|
||||||
if (qb_hints)
|
if (resolve_opt_hints)
|
||||||
qb_hints->check_unresolved(thd);
|
{
|
||||||
|
if (thd->lex->opt_hints_global && select_lex->select_number == 1)
|
||||||
|
thd->lex->opt_hints_global->resolve(thd);
|
||||||
|
if (qb_hints)
|
||||||
|
qb_hints->check_unresolved(thd);
|
||||||
|
}
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8414,6 +8420,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
|
|||||||
select_insert It is SELECT ... INSERT command
|
select_insert It is SELECT ... INSERT command
|
||||||
want_access what access is needed
|
want_access what access is needed
|
||||||
full_table_list a parameter to pass to the make_leaves_list function
|
full_table_list a parameter to pass to the make_leaves_list function
|
||||||
|
resolve_opt_hints Whether optimizer hints must be resolved here
|
||||||
|
|
||||||
NOTE
|
NOTE
|
||||||
a wrapper for check_tables that will also check the resulting
|
a wrapper for check_tables that will also check the resulting
|
||||||
@ -8430,12 +8437,13 @@ bool setup_tables_and_check_access(THD *thd, Name_resolution_context *context,
|
|||||||
bool select_insert,
|
bool select_insert,
|
||||||
privilege_t want_access_first,
|
privilege_t want_access_first,
|
||||||
privilege_t want_access,
|
privilege_t want_access,
|
||||||
bool full_table_list)
|
bool full_table_list,
|
||||||
|
bool resolve_opt_hints)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("setup_tables_and_check_access");
|
DBUG_ENTER("setup_tables_and_check_access");
|
||||||
|
|
||||||
if (setup_tables(thd, context, from_clause, tables,
|
if (setup_tables(thd, context, from_clause, tables,
|
||||||
leaves, select_insert, full_table_list))
|
leaves, select_insert, full_table_list, resolve_opt_hints))
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
List_iterator<TABLE_LIST> ti(leaves);
|
List_iterator<TABLE_LIST> ti(leaves);
|
||||||
|
@ -228,7 +228,7 @@ Item ** find_item_in_list(Item *item, List<Item> &items, uint *counter,
|
|||||||
bool setup_tables(THD *thd, Name_resolution_context *context,
|
bool setup_tables(THD *thd, Name_resolution_context *context,
|
||||||
List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
|
List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
|
||||||
List<TABLE_LIST> &leaves, bool select_insert,
|
List<TABLE_LIST> &leaves, bool select_insert,
|
||||||
bool full_table_list);
|
bool full_table_list, bool resolve_opt_hints);
|
||||||
bool setup_tables_and_check_access(THD *thd,
|
bool setup_tables_and_check_access(THD *thd,
|
||||||
Name_resolution_context *context,
|
Name_resolution_context *context,
|
||||||
List<TABLE_LIST> *from_clause,
|
List<TABLE_LIST> *from_clause,
|
||||||
@ -237,7 +237,8 @@ bool setup_tables_and_check_access(THD *thd,
|
|||||||
bool select_insert,
|
bool select_insert,
|
||||||
privilege_t want_access_first,
|
privilege_t want_access_first,
|
||||||
privilege_t want_access,
|
privilege_t want_access,
|
||||||
bool full_table_list);
|
bool full_table_list,
|
||||||
|
bool resolve_opt_hints);
|
||||||
bool wait_while_table_is_used(THD *thd, TABLE *table,
|
bool wait_while_table_is_used(THD *thd, TABLE *table,
|
||||||
enum ha_extra_function function);
|
enum ha_extra_function function);
|
||||||
|
|
||||||
|
@ -6040,7 +6040,7 @@ public:
|
|||||||
ulonglong num_of_strings_sorted_on_truncated_length;
|
ulonglong num_of_strings_sorted_on_truncated_length;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void set_query_timer()
|
void set_query_timer_if_needed()
|
||||||
{
|
{
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
/*
|
/*
|
||||||
@ -6059,9 +6059,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
if (!timeout_val || spcont || in_sub_stmt || query_timer.expired == 0)
|
if (!timeout_val || spcont || in_sub_stmt || query_timer.expired == 0)
|
||||||
return;
|
return;
|
||||||
thr_timer_settime(&query_timer, timeout_val);
|
set_query_timer_force(timeout_val);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
void set_query_timer_force(ulonglong timeout_val)
|
||||||
|
{
|
||||||
|
thr_timer_settime(&query_timer, timeout_val);
|
||||||
|
}
|
||||||
void reset_query_timer()
|
void reset_query_timer()
|
||||||
{
|
{
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
|
@ -1880,11 +1880,11 @@ bool Sql_cmd_delete::prepare_inner(THD *thd)
|
|||||||
if (setup_tables_and_check_access(thd, &select_lex->context,
|
if (setup_tables_and_check_access(thd, &select_lex->context,
|
||||||
&select_lex->top_join_list,
|
&select_lex->top_join_list,
|
||||||
table_list, select_lex->leaf_tables,
|
table_list, select_lex->leaf_tables,
|
||||||
false, DELETE_ACL, SELECT_ACL, true))
|
false, DELETE_ACL, SELECT_ACL, true, false))
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list,
|
if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list,
|
||||||
table_list, select_lex->leaf_tables, false, false))
|
table_list, select_lex->leaf_tables, false, false, true))
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
if (!multitable)
|
if (!multitable)
|
||||||
|
@ -801,7 +801,7 @@ static bool init_items_for_help_command(THD *thd,
|
|||||||
|
|
||||||
if (setup_tables(thd, &first_select_lex->context,
|
if (setup_tables(thd, &first_select_lex->context,
|
||||||
&first_select_lex->top_join_list,
|
&first_select_lex->top_join_list,
|
||||||
&tables[0], leaves, false, false))
|
&tables[0], leaves, false, false, true))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
memcpy((char*) used_fields, (char*) init_used_fields,
|
memcpy((char*) used_fields, (char*) init_used_fields,
|
||||||
|
@ -1647,7 +1647,7 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list,
|
|||||||
table_list,
|
table_list,
|
||||||
thd->lex->first_select_lex()->leaf_tables,
|
thd->lex->first_select_lex()->leaf_tables,
|
||||||
select_insert, INSERT_ACL, SELECT_ACL,
|
select_insert, INSERT_ACL, SELECT_ACL,
|
||||||
TRUE))
|
true, true))
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
if (insert_into_view && !fields.elements)
|
if (insert_into_view && !fields.elements)
|
||||||
|
@ -419,7 +419,7 @@ int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list,
|
|||||||
thd->lex->first_select_lex()->leaf_tables,
|
thd->lex->first_select_lex()->leaf_tables,
|
||||||
FALSE,
|
FALSE,
|
||||||
INSERT_ACL | UPDATE_ACL,
|
INSERT_ACL | UPDATE_ACL,
|
||||||
INSERT_ACL | UPDATE_ACL, FALSE))
|
INSERT_ACL | UPDATE_ACL, false, true))
|
||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(-1);
|
||||||
if (!table_list->table || // do not support join view
|
if (!table_list->table || // do not support join view
|
||||||
!table_list->single_table_updatable() || // and derived tables
|
!table_list->single_table_updatable() || // and derived tables
|
||||||
|
@ -3822,7 +3822,7 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt)
|
|||||||
thd->query_plan_flags|= QPLAN_ADMIN;
|
thd->query_plan_flags|= QPLAN_ADMIN;
|
||||||
|
|
||||||
/* Start timeouts */
|
/* Start timeouts */
|
||||||
thd->set_query_timer();
|
thd->set_query_timer_if_needed();
|
||||||
|
|
||||||
#ifdef WITH_WSREP
|
#ifdef WITH_WSREP
|
||||||
/* Check wsrep_mode rules before command execution. */
|
/* Check wsrep_mode rules before command execution. */
|
||||||
|
@ -1468,7 +1468,7 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
|
|||||||
if (!(select_options & OPTION_SETUP_TABLES_DONE) &&
|
if (!(select_options & OPTION_SETUP_TABLES_DONE) &&
|
||||||
setup_tables_and_check_access(thd, &select_lex->context, join_list,
|
setup_tables_and_check_access(thd, &select_lex->context, join_list,
|
||||||
tables_list, select_lex->leaf_tables,
|
tables_list, select_lex->leaf_tables,
|
||||||
FALSE, SELECT_ACL, SELECT_ACL, FALSE))
|
false, SELECT_ACL, SELECT_ACL, false, true))
|
||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(-1);
|
||||||
|
|
||||||
/* System Versioning: handle FOR SYSTEM_TIME clause. */
|
/* System Versioning: handle FOR SYSTEM_TIME clause. */
|
||||||
|
@ -1672,7 +1672,7 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd)
|
|||||||
|
|
||||||
if (setup_tables_and_check_access(thd, &select_lex->context,
|
if (setup_tables_and_check_access(thd, &select_lex->context,
|
||||||
&select_lex->top_join_list, table_list, select_lex->leaf_tables,
|
&select_lex->top_join_list, table_list, select_lex->leaf_tables,
|
||||||
FALSE, UPDATE_ACL, SELECT_ACL, TRUE))
|
false, UPDATE_ACL, SELECT_ACL, true, false))
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
|
|
||||||
if (table_list->has_period() &&
|
if (table_list->has_period() &&
|
||||||
@ -3118,7 +3118,7 @@ bool Sql_cmd_update::prepare_inner(THD *thd)
|
|||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list,
|
if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list,
|
||||||
table_list, select_lex->leaf_tables, false, false))
|
table_list, select_lex->leaf_tables, false, false, true))
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
if (select_lex->vers_setup_conds(thd, table_list))
|
if (select_lex->vers_setup_conds(thd, table_list))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user