Browse Source

MDEV-36486 Forbid placing optimizer hints at the INSERT part of INSERT..SELECT

Due to complications while parsing of INSERT..SELECT statements,
optimizer hints placed at the INSERT part are ignored.

At the same time, hints placed at the SELECT part of
INSERT..SELECT statements are fully supported.
pull/3878/head
Oleg Smirnov 5 months ago
parent
commit
b11767846f
  1. 8
      mysql-test/main/opt_hint_timeout.result
  2. 2
      mysql-test/main/opt_hint_timeout.test
  3. 42
      mysql-test/main/opt_hints.result
  4. 30
      mysql-test/main/opt_hints.test
  5. 2
      mysql-test/main/opt_hints_join_cache.test
  6. 8
      mysql-test/main/opt_hints_join_order.result
  7. 2
      mysql-test/main/opt_hints_join_order.test
  8. 2
      sql/share/errmsg-utf8.txt
  9. 46
      sql/sql_lex.cc
  10. 2
      sql/sql_lex.h

8
mysql-test/main/opt_hint_timeout.result

@ -72,10 +72,16 @@ Warning 1912 Incorrect value '4294967296' for option 'MAX_EXECUTION_TIME'
CREATE TABLE t2 (i INT);
INSERT /*+ MAX_EXECUTION_TIME(10) */ INTO t2 SELECT 1;
Warnings:
Warning 4223 Optimizer hints at the INSERT part of a INSERT..SELECT statement are not supported
INSERT INTO t2 SELECT /*+ MAX_EXECUTION_TIME(10) */ 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
Warning 4223 Optimizer hints at the INSERT part of a INSERT..SELECT statement are not supported
REPLACE INTO t2 SELECT /*+ MAX_EXECUTION_TIME(10) */ 1;
Warnings:
Warning 4172 'MAX_EXECUTION_TIME(10)' 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

2
mysql-test/main/opt_hint_timeout.test

@ -78,7 +78,9 @@ SELECT /*+ MAX_EXECUTION_TIME(4294967296) */ count(*) FROM t1;
--echo
CREATE TABLE t2 (i INT);
INSERT /*+ MAX_EXECUTION_TIME(10) */ INTO t2 SELECT 1;
INSERT INTO t2 SELECT /*+ MAX_EXECUTION_TIME(10) */ 1;
REPLACE /*+ MAX_EXECUTION_TIME(15) */ INTO t2 SELECT 1;
REPLACE INTO t2 SELECT /*+ MAX_EXECUTION_TIME(10) */ 1;
UPDATE /*+ MAX_EXECUTION_TIME(23) */ t2 SET i = 1;
DELETE /*+ MAX_EXECUTION_TIME(5000) */ FROM t2 WHERE i = 1;

42
mysql-test/main/opt_hints.result

@ -490,17 +490,17 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
Warnings:
Note 1003 (insert into `test`.`t3`(f1,f2,f3) select /*+ NO_ICP(`t5`@`select#2`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and <cache>(8 + 0))
# Turn off ICP for a particular table
EXPLAIN EXTENDED INSERT /*+ NO_ICP(t5@QB1) */ INTO t3(f1, f2, f3)
(SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5
EXPLAIN EXTENDED INSERT INTO t3(f1, f2, f3)
(SELECT /*+ NO_ICP(t5) */ t4.x, t5.y, 'filler' FROM t4, t4 t5
WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00
1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using where; Using join buffer (flat, BNL join)
Warnings:
Note 1003 (insert into `test`.`t3`(f1,f2,f3) select /*+ QB_NAME(`qb1`) NO_ICP(`t5`@`qb1`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and <cache>(8 + 0))
Note 1003 (insert into `test`.`t3`(f1,f2,f3) select /*+ NO_ICP(`t5`@`select#2`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and <cache>(8 + 0))
# Turn off ICP for a particular table and a key
EXPLAIN EXTENDED INSERT /*+ NO_ICP(t5@QB1 x_idx) */ INTO t3(f1, f2, f3)
(SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5
EXPLAIN EXTENDED INSERT INTO t3(f1, f2, f3)
(SELECT /*+ QB_NAME(qb1) NO_ICP(t5@QB1 x_idx)*/ t4.x, t5.y, 'filler' FROM t4, t4 t5
WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00
@ -525,8 +525,8 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
Warnings:
Note 1003 (replace into `test`.`t3`(f1,f2,f3) select /*+ NO_ICP(`t5`@`select#2`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and <cache>(8 + 0))
# Turn off ICP for a particular table
EXPLAIN EXTENDED REPLACE /*+ NO_ICP(t5@QB1) */ INTO t3(f1, f2, f3)
(SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5
EXPLAIN EXTENDED REPLACE INTO t3(f1, f2, f3)
(SELECT /*+ QB_NAME(qb1) NO_ICP(t5@QB1)*/ t4.x, t5.y, 'filler' FROM t4, t4 t5
WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00
@ -534,14 +534,14 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
Warnings:
Note 1003 (replace into `test`.`t3`(f1,f2,f3) select /*+ QB_NAME(`qb1`) NO_ICP(`t5`@`qb1`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and <cache>(8 + 0))
# Turn off ICP for a particular table and a key
EXPLAIN EXTENDED REPLACE /*+ NO_ICP(t5@QB1 x_idx) */ INTO t3(f1, f2, f3)
(SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5
EXPLAIN EXTENDED REPLACE INTO t3(f1, f2, f3)
(SELECT /*+ NO_ICP(t5 x_idx) */ t4.x, t5.y, 'filler' FROM t4, t4 t5
WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t4 ref y_idx y_idx 5 const 1 100.00
1 SIMPLE t5 range x_idx x_idx 5 NULL 2 100.00 Using where; Using join buffer (flat, BNL join)
Warnings:
Note 1003 (replace into `test`.`t3`(f1,f2,f3) select /*+ QB_NAME(`qb1`) NO_ICP(`t5`@`qb1` `x_idx`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and <cache>(8 + 0))
Note 1003 (replace into `test`.`t3`(f1,f2,f3) select /*+ NO_ICP(`t5`@`select#2` `x_idx`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y`,'filler' AS `filler` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and <cache>(8 + 0))
# Misc tests
# Should issue warning
EXPLAIN EXTENDED SELECT /*+ QB_NAME(qb1) QB_NAME(qb1 ) */ * FROM t2;
@ -1400,21 +1400,23 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 index a a 5 NULL 3 100.00 Using where; Using index; Using temporary
Warnings:
Note 1003 insert into `test`.`t1`(a) select /*+ NO_RANGE_OPTIMIZATION(`t1`@`select#2` `a`) */ sql_buffer_result `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 1 and `test`.`t1`.`a` <= 3
# Alternatively, a hint may be placed next to INSERT keyword:
# Hints at the INSERT part of a INSERT..SELECT are not supported:
EXPLAIN EXTENDED
INSERT /*+ no_range_optimization (t1)*/ INTO t1 (a) SELECT a FROM t1 WHERE a>1 AND a<=3;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 index a a 5 NULL 3 100.00 Using where; Using index; Using temporary
1 SIMPLE t1 range a a 5 NULL 2 100.00 Using where; Using index; Using temporary
Warnings:
Note 1003 insert into `test`.`t1`(a) select /*+ NO_RANGE_OPTIMIZATION(`t1`@`select#1`) */ sql_buffer_result `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 1 and `test`.`t1`.`a` <= 3
# But if hints are present at both INSERT and SELECT parts,
# those at the INSERT part are ignored:
Warning 4223 Optimizer hints at the INSERT part of a INSERT..SELECT statement are not supported
Note 1003 insert into `test`.`t1`(a) select sql_buffer_result `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 1 and `test`.`t1`.`a` <= 3
# If hints are present at both INSERT and SELECT parts,
# those at the INSERT part are ignored
EXPLAIN EXTENDED
INSERT /*+ no_range_optimization (t1)*/ INTO t1 (a) SELECT /*+ mrr(t1)*/ a
FROM t1 WHERE a>1 AND a<=3;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 range a a 5 NULL 2 100.00 Using where; Using index; Using temporary
Warnings:
Warning 4223 Optimizer hints at the INSERT part of a INSERT..SELECT statement are not supported
Note 1003 insert into `test`.`t1`(a) select /*+ MRR(`t1`@`select#2`) */ sql_buffer_result `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 1 and `test`.`t1`.`a` <= 3
# Table `t2` cannot be resolved since it is not present in the SELECT part
# (a warning expected):
@ -1423,15 +1425,7 @@ INSERT INTO t2 (a) SELECT /*+ no_range_optimization (t2)*/ a FROM t1 WHERE a>1 A
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 range a a 5 NULL 2 100.00 Using where; Using index
Warnings:
Warning 4212 Unresolved table name `t2`@`select#2` for NO_RANGE_OPTIMIZATION hint
Note 1003 insert into `test`.`t2`(a) select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 1 and `test`.`t1`.`a` <= 3
# Alternative placement of the hint:
EXPLAIN EXTENDED
INSERT /*+ no_range_optimization (t2 ix1)*/ INTO t2 (a) SELECT a FROM t1 WHERE a>1 AND a<=3;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 range a a 5 NULL 2 100.00 Using where; Using index
Warnings:
Warning 4213 Unresolved index name `t2`@`select#1` `ix1` for NO_RANGE_OPTIMIZATION hint
Warning 4219 Unresolved table name `t2`@`select#2` for NO_RANGE_OPTIMIZATION hint
Note 1003 insert into `test`.`t2`(a) select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 1 and `test`.`t1`.`a` <= 3
DROP TABLE t1, t2;
set optimizer_switch = DEFAULT;

30
mysql-test/main/opt_hints.test

@ -275,13 +275,13 @@ EXPLAIN EXTENDED INSERT INTO t3(f1, f2, f3)
WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0);
--echo # Turn off ICP for a particular table
EXPLAIN EXTENDED INSERT /*+ NO_ICP(t5@QB1) */ INTO t3(f1, f2, f3)
(SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5
EXPLAIN EXTENDED INSERT INTO t3(f1, f2, f3)
(SELECT /*+ NO_ICP(t5) */ t4.x, t5.y, 'filler' FROM t4, t4 t5
WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0);
--echo # Turn off ICP for a particular table and a key
EXPLAIN EXTENDED INSERT /*+ NO_ICP(t5@QB1 x_idx) */ INTO t3(f1, f2, f3)
(SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5
EXPLAIN EXTENDED INSERT INTO t3(f1, f2, f3)
(SELECT /*+ QB_NAME(qb1) NO_ICP(t5@QB1 x_idx)*/ t4.x, t5.y, 'filler' FROM t4, t4 t5
WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0);
--echo # Make sure ICP is expected to be used when there are no hints
@ -294,13 +294,13 @@ EXPLAIN EXTENDED REPLACE INTO t3(f1, f2, f3)
WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0);
--echo # Turn off ICP for a particular table
EXPLAIN EXTENDED REPLACE /*+ NO_ICP(t5@QB1) */ INTO t3(f1, f2, f3)
(SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5
EXPLAIN EXTENDED REPLACE INTO t3(f1, f2, f3)
(SELECT /*+ QB_NAME(qb1) NO_ICP(t5@QB1)*/ t4.x, t5.y, 'filler' FROM t4, t4 t5
WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0);
--echo # Turn off ICP for a particular table and a key
EXPLAIN EXTENDED REPLACE /*+ NO_ICP(t5@QB1 x_idx) */ INTO t3(f1, f2, f3)
(SELECT /*+ QB_NAME(qb1) */ t4.x, t5.y, 'filler' FROM t4, t4 t5
EXPLAIN EXTENDED REPLACE INTO t3(f1, f2, f3)
(SELECT /*+ NO_ICP(t5 x_idx) */ t4.x, t5.y, 'filler' FROM t4, t4 t5
WHERE t4.y = 8 AND t5.x BETWEEN 7 AND t4.y+0);
--echo # Misc tests
@ -723,24 +723,24 @@ INSERT INTO t1 (a) SELECT a FROM t1 WHERE a>1 AND a<=3;
EXPLAIN EXTENDED
INSERT INTO t1 (a) SELECT /*+ no_range_optimization (t1 a)*/ a FROM t1 WHERE a>1 AND a<=3;
--echo # Alternatively, a hint may be placed next to INSERT keyword:
--echo # Hints at the INSERT part of a INSERT..SELECT are not supported:
EXPLAIN EXTENDED
INSERT /*+ no_range_optimization (t1)*/ INTO t1 (a) SELECT a FROM t1 WHERE a>1 AND a<=3;
--echo # But if hints are present at both INSERT and SELECT parts,
--echo # those at the INSERT part are ignored:
--echo # If hints are present at both INSERT and SELECT parts,
--echo # those at the INSERT part are ignored
EXPLAIN EXTENDED
INSERT /*+ no_range_optimization (t1)*/ INTO t1 (a) SELECT /*+ mrr(t1)*/ a
FROM t1 WHERE a>1 AND a<=3;
--echo # Table `t2` cannot be resolved since it is not present in the SELECT part
--echo # (a warning expected):
# Warnings "Unresolved table/index name..." are generated during both prepare
# and execution stages. So disable PS protocol to avoid duplication
--disable_ps_protocol
EXPLAIN EXTENDED
INSERT INTO t2 (a) SELECT /*+ no_range_optimization (t2)*/ a FROM t1 WHERE a>1 AND a<=3;
--echo # Alternative placement of the hint:
EXPLAIN EXTENDED
INSERT /*+ no_range_optimization (t2 ix1)*/ INTO t2 (a) SELECT a FROM t1 WHERE a>1 AND a<=3;
--enable_ps_protocol
DROP TABLE t1, t2;

2
mysql-test/main/opt_hints_join_cache.test

@ -1,3 +1,5 @@
--disable_view_protocol # Since optimizer hints are not supported inside views
set @tmp_jcl= @@join_cache_level;
set @tmp_opt= @@optimizer_switch;

8
mysql-test/main/opt_hints_join_order.result

@ -1336,6 +1336,14 @@ INSERT INTO t2 (id, f2) VALUES
(4,5),(3,3),(1,0),(1,3),(6,1),(2,0),(4,1),(2,7),(2,1),(1,0),(3,0),(5,8),(5,4),
(3,9),(2,0),(7,2),(2,0),(1,8),(6,5),(4,1);
CREATE TABLE tn (fn_1 INT, fn_2 INT);
ANALYZE TABLE t1, t2, tn;
Table Op Msg_type Msg_text
test.t1 analyze status Engine-independent statistics collected
test.t1 analyze status OK
test.t2 analyze status Engine-independent statistics collected
test.t2 analyze status OK
test.tn analyze status Engine-independent statistics collected
test.tn analyze status OK
EXPLAIN EXTENDED
INSERT INTO tn (fn_1, fn_2)
SELECT /*+ JOIN_ORDER(t2,t1) JOIN_FIXED_ORDER() */ f1,f2

2
mysql-test/main/opt_hints_join_order.test

@ -807,6 +807,8 @@ INSERT INTO t2 (id, f2) VALUES
CREATE TABLE tn (fn_1 INT, fn_2 INT);
ANALYZE TABLE t1, t2, tn;
EXPLAIN EXTENDED
INSERT INTO tn (fn_1, fn_2)
SELECT /*+ JOIN_ORDER(t2,t1) JOIN_FIXED_ORDER() */ f1,f2

2
sql/share/errmsg-utf8.txt

@ -12326,3 +12326,5 @@ ER_HINTS_INSIDE_VIEWS_NOT_SUPPORTED
eng "Optimizer hints are not supported inside view definitions"
ER_WARN_MALFORMED_HINT
eng "Hint %s is ignored as malformed"
ER_WARN_HINTS_ON_INSERT_PART_OF_INSERT_SELECT
eng "Optimizer hints at the INSERT part of a INSERT..SELECT statement are not supported"

46
sql/sql_lex.cc

@ -11185,22 +11185,17 @@ bool LEX::parsed_insert_select(SELECT_LEX *first_select)
return true;
// fix "main" select
resolve_optimizer_hints_in_last_select();
if (discard_optimizer_hints_in_last_select())
{
// Hints were specified at the INSERT part of an INSERT..SELECT
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_HINTS_ON_INSERT_PART_OF_INSERT_SELECT,
ER_THD(thd, ER_WARN_HINTS_ON_INSERT_PART_OF_INSERT_SELECT));
}
SELECT_LEX *blt __attribute__((unused))= pop_select();
DBUG_ASSERT(blt == &builtin_select);
push_select(first_select);
// INSERT..SELECT allows placing hints next to either INSERT or SELECT, i.e.:
// `INSERT /* hint(t1) */ INTO t2 SELECT a FROM t1` or
// `INSERT INTO t2 SELECT /* hint(t1) */ a FROM t1`
// but not at both places at the same time.
// `first_select` represents the SELECT part here while `builtin_select` -
// the INSERT part. Future processing will proceed with `first_select`,
// so transfer the hints from `builtin_select` to `first_select` in case
// they were not already set. If hints are present for both INSERT and SELECT
// parts, SELECT part hints are preserved while INSERT part hints are discarded
if (!first_select->opt_hints_qb && blt->opt_hints_qb)
first_select->opt_hints_qb= blt->opt_hints_qb;
return false;
}
@ -13004,3 +12999,30 @@ void LEX::resolve_optimizer_hints_in_last_select()
select_lex->parsed_optimizer_hints->resolve(&pc);
}
}
/*
This method discards previously parsed optimizer hints attached to
the last select_lex without their resolving, which may be required
in some scenarios (for example, ignoring hints at the INSERT part of a
INSERT..SELECT statement).
Also see resolve_optimizer_hints_in_last_select().
Return value:
- false optimizer hints were not found
- true optimizer hints were found and discarded
*/
bool LEX::discard_optimizer_hints_in_last_select()
{
SELECT_LEX *select_lex;
if (likely(select_stack_top))
select_lex= select_stack[select_stack_top - 1];
else
select_lex= nullptr;
if (select_lex && select_lex->parsed_optimizer_hints)
{
select_lex->parsed_optimizer_hints= nullptr;
return true;
}
return false;
}

2
sql/sql_lex.h

@ -3758,6 +3758,8 @@ public:
void resolve_optimizer_hints_in_last_select();
bool discard_optimizer_hints_in_last_select();
SELECT_LEX *current_select_or_default()
{
return current_select ? current_select : &builtin_select;

Loading…
Cancel
Save