Skip to content

Commit 71cadbf

Browse files
committed
MDEV-36486 Optimizer hints are resolved against the INSERT part of INSERT..SELECT
When processing queries like INSERT INTO t1 (..) SELECT .. FROM t1, t2 ..., there is a single query block (i.e., a single SELECT_LEX) for both INSERT and SELECT parts. During hints resolution, when hints are attached to particular TABLE_LIST's, the search is performed by table name across the whole query block. So, if a table mentioned in an optimizer hint is present in the INSERT part, the hint is attached to the that table. This is obviously wrong as optimizer hints are supposed to only affect the SELECT part of and INSERT..SELECT clause. This commit disables possible attaching hints to tables in the INSERT part and fixes some other bugs related to INSERT..SELECT statements processing
1 parent 127f28a commit 71cadbf

File tree

12 files changed

+152
-39
lines changed

12 files changed

+152
-39
lines changed

mysql-test/main/opt_hints.result

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1982,8 +1982,63 @@ SELECT
19821982
Warnings:
19831983
Warning 1064 Optimizer hint syntax error near '? bad syntax */ 1' at line 2
19841984
DROP TABLE t1;
1985+
1986+
# MDEV-36486 Optimizer hints are resolved against the INSERT part of INSERT..SELECT
1987+
1988+
CREATE TABLE t1 (a INT, KEY(a));
1989+
INSERT INTO t1 VALUES (1),(2),(3);
1990+
CREATE TABLE t2 (a INT, KEY(a));
1991+
INSERT INTO t2 VALUES (1),(2),(3);
1992+
# See that the range optimization is employed when there are no hints:
1993+
EXPLAIN EXTENDED
1994+
INSERT INTO t1 (a) SELECT a FROM t1 WHERE a>1 AND a<=3;
1995+
id select_type table type possible_keys key key_len ref rows filtered Extra
1996+
1 SIMPLE t1 range a a 5 NULL 2 100.00 Using where; Using index; Using temporary
1997+
Warnings:
1998+
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
1999+
# No range optimization any more:
2000+
EXPLAIN EXTENDED
2001+
INSERT INTO t1 (a) SELECT /*+ no_range_optimization (t1 a)*/ a FROM t1 WHERE a>1 AND a<=3;
2002+
id select_type table type possible_keys key key_len ref rows filtered Extra
2003+
1 SIMPLE t1 index a a 5 NULL 3 100.00 Using where; Using index; Using temporary
2004+
Warnings:
2005+
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
2006+
# Alternatively, a hint may be placed next to INSERT keyword:
2007+
EXPLAIN EXTENDED
2008+
INSERT /*+ no_range_optimization (t1)*/ INTO t1 (a) SELECT a FROM t1 WHERE a>1 AND a<=3;
2009+
id select_type table type possible_keys key key_len ref rows filtered Extra
2010+
1 SIMPLE t1 index a a 5 NULL 3 100.00 Using where; Using index; Using temporary
2011+
Warnings:
2012+
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
2013+
# But if hints are present at both INSERT and SELECT parts,
2014+
# those at the INSERT part are ignored:
2015+
EXPLAIN EXTENDED
2016+
INSERT /*+ no_range_optimization (t1)*/ INTO t1 (a) SELECT /*+ mrr(t1)*/ a
2017+
FROM t1 WHERE a>1 AND a<=3;
2018+
id select_type table type possible_keys key key_len ref rows filtered Extra
2019+
1 SIMPLE t1 range a a 5 NULL 2 100.00 Using where; Using index; Using temporary
2020+
Warnings:
2021+
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
2022+
# Table `t2` cannot be resolved since it is not present in the SELECT part
2023+
# (a warning expected):
2024+
EXPLAIN EXTENDED
2025+
INSERT INTO t2 (a) SELECT /*+ no_range_optimization (t2)*/ a FROM t1 WHERE a>1 AND a<=3;
2026+
id select_type table type possible_keys key key_len ref rows filtered Extra
2027+
1 SIMPLE t1 range a a 5 NULL 2 100.00 Using where; Using index
2028+
Warnings:
2029+
Warning 4212 Unresolved table name `t2`@`select#2` for NO_RANGE_OPTIMIZATION hint
2030+
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
2031+
# Alternative placement of the hint:
2032+
EXPLAIN EXTENDED
2033+
INSERT /*+ no_range_optimization (t2 ix1)*/ INTO t2 (a) SELECT a FROM t1 WHERE a>1 AND a<=3;
2034+
id select_type table type possible_keys key key_len ref rows filtered Extra
2035+
1 SIMPLE t1 range a a 5 NULL 2 100.00 Using where; Using index
2036+
Warnings:
2037+
Warning 4213 Unresolved index name `t2`@`select#1` `ix1` for NO_RANGE_OPTIMIZATION hint
2038+
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
2039+
DROP TABLE t1, t2;
19852040
set optimizer_switch = DEFAULT;
19862041
set join_cache_level = DEFAULT;
19872042
#
1988-
# End of 11.7 tests
2043+
# End of 12.0 tests
19892044
#

mysql-test/main/opt_hints.test

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1019,8 +1019,48 @@ SELECT
10191019
/*+ ? bad syntax */ 1;
10201020

10211021
DROP TABLE t1;
1022+
1023+
--echo
1024+
--echo # MDEV-36486 Optimizer hints are resolved against the INSERT part of INSERT..SELECT
1025+
--echo
1026+
1027+
CREATE TABLE t1 (a INT, KEY(a));
1028+
INSERT INTO t1 VALUES (1),(2),(3);
1029+
1030+
CREATE TABLE t2 (a INT, KEY(a));
1031+
INSERT INTO t2 VALUES (1),(2),(3);
1032+
1033+
--echo # See that the range optimization is employed when there are no hints:
1034+
EXPLAIN EXTENDED
1035+
INSERT INTO t1 (a) SELECT a FROM t1 WHERE a>1 AND a<=3;
1036+
1037+
--echo # No range optimization any more:
1038+
EXPLAIN EXTENDED
1039+
INSERT INTO t1 (a) SELECT /*+ no_range_optimization (t1 a)*/ a FROM t1 WHERE a>1 AND a<=3;
1040+
1041+
--echo # Alternatively, a hint may be placed next to INSERT keyword:
1042+
EXPLAIN EXTENDED
1043+
INSERT /*+ no_range_optimization (t1)*/ INTO t1 (a) SELECT a FROM t1 WHERE a>1 AND a<=3;
1044+
1045+
--echo # But if hints are present at both INSERT and SELECT parts,
1046+
--echo # those at the INSERT part are ignored:
1047+
EXPLAIN EXTENDED
1048+
INSERT /*+ no_range_optimization (t1)*/ INTO t1 (a) SELECT /*+ mrr(t1)*/ a
1049+
FROM t1 WHERE a>1 AND a<=3;
1050+
1051+
--echo # Table `t2` cannot be resolved since it is not present in the SELECT part
1052+
--echo # (a warning expected):
1053+
EXPLAIN EXTENDED
1054+
INSERT INTO t2 (a) SELECT /*+ no_range_optimization (t2)*/ a FROM t1 WHERE a>1 AND a<=3;
1055+
1056+
--echo # Alternative placement of the hint:
1057+
EXPLAIN EXTENDED
1058+
INSERT /*+ no_range_optimization (t2 ix1)*/ INTO t2 (a) SELECT a FROM t1 WHERE a>1 AND a<=3;
1059+
1060+
DROP TABLE t1, t2;
1061+
10221062
set optimizer_switch = DEFAULT;
10231063
set join_cache_level = DEFAULT;
10241064
--echo #
1025-
--echo # End of 11.7 tests
1065+
--echo # End of 12.0 tests
10261066
--echo #

sql/opt_hints_parser.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ bool Parser::Index_level_hint::resolve(Parse_context *pc) const
475475
if (!idx)
476476
{
477477
idx= new (pc->thd->mem_root)
478-
Opt_hints_key(index_name_sys, tab, pc->thd->mem_root);
478+
Opt_hints_key(index_name_sys, tab, pc->thd->mem_root);
479479
tab->register_child(idx);
480480
}
481481

sql/sql_base.cc

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8222,7 +8222,6 @@ void make_leaves_list(THD *thd, List<TABLE_LIST> &list, TABLE_LIST *tables,
82228222
refresh It is only refresh for subquery
82238223
select_insert It is SELECT ... INSERT command
82248224
full_table_list a parameter to pass to the make_leaves_list function
8225-
resolve_opt_hints Whether optimizer hints must be resolved here
82268225
82278226
NOTE
82288227
Check also that the 'used keys' and 'ignored keys' exists and set up the
@@ -8242,7 +8241,7 @@ void make_leaves_list(THD *thd, List<TABLE_LIST> &list, TABLE_LIST *tables,
82428241
bool setup_tables(THD *thd, Name_resolution_context *context,
82438242
List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
82448243
List<TABLE_LIST> &leaves, bool select_insert,
8245-
bool full_table_list, bool resolve_opt_hints)
8244+
bool full_table_list)
82468245
{
82478246
uint tablenr= 0;
82488247
List_iterator<TABLE_LIST> ti(leaves);
@@ -8278,18 +8277,23 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
82788277
leaves.push_back(table_list, thd->mem_root);
82798278
}
82808279

8281-
bool is_insert_tables_num_set= false;
8280+
/*
8281+
This variable is only used for INSERT..SELECT's:
8282+
true: processing the INSERT part of an INSERT..SELECT
8283+
false: processing the SELECT part of it
8284+
*/
8285+
bool is_insert_part= true;
82828286
while ((table_list= ti++))
82838287
{
82848288
TABLE *table= table_list->table;
82858289
if (table && !table->pos_in_table_list)
82868290
table->pos_in_table_list= table_list;
8287-
if (select_insert && !is_insert_tables_num_set &&
8291+
if (select_insert && is_insert_part &&
82888292
table_list->top_table() == first_select_table)
82898293
{
82908294
/* new counting for SELECT of INSERT ... SELECT command */
82918295
thd->lex->first_select_lex()->insert_tables= tablenr;
8292-
is_insert_tables_num_set= true;
8296+
is_insert_part= false;
82938297
tablenr= 0;
82948298
}
82958299
if(table_list->jtbm_subselect)
@@ -8314,15 +8318,22 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
83148318
DBUG_RETURN(1);
83158319
}
83168320

8317-
if (qb_hints && // QB hints initialized
8318-
!table_list->opt_hints_table) // Table hints are not adjusted yet
8321+
/*
8322+
Conditions to meet for optimizer hints resolution:
8323+
(1) QB hints initialized
8324+
(2) Table hints are not adjusted yet
8325+
(3) Table is not in the INSERT part of INSERT..SELECT
8326+
*/
8327+
if (qb_hints && // (1)
8328+
!table_list->opt_hints_table && // (2)
8329+
!(select_insert && is_insert_part)) // (3)
83198330
{
83208331
table_list->opt_hints_table=
83218332
qb_hints->fix_hints_for_table(table_list->table,
83228333
table_list->alias);
83238334
}
83248335
}
8325-
if (select_insert && !is_insert_tables_num_set)
8336+
if (select_insert && is_insert_part)
83268337
{
83278338
/*
83288339
This happens for statements like `INSERT INTO t1 SELECT 1`,
@@ -8389,19 +8400,6 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
83898400
if (setup_natural_join_row_types(thd, from_clause, context))
83908401
DBUG_RETURN(1);
83918402

8392-
if (resolve_opt_hints)
8393-
{
8394-
if (thd->lex->opt_hints_global && select_lex->select_number == 1)
8395-
{
8396-
thd->lex->opt_hints_global->fix_hint(thd);
8397-
/*
8398-
There's no need to call opt_hints_global->check_unresolved(),
8399-
this is done for each query block individually
8400-
*/
8401-
}
8402-
if (qb_hints)
8403-
qb_hints->check_unfixed(thd);
8404-
}
84058403
DBUG_RETURN(0);
84068404
}
84078405

@@ -8421,7 +8419,6 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
84218419
select_insert It is SELECT ... INSERT command
84228420
want_access what access is needed
84238421
full_table_list a parameter to pass to the make_leaves_list function
8424-
resolve_opt_hints Whether optimizer hints must be resolved here
84258422
84268423
NOTE
84278424
a wrapper for check_tables that will also check the resulting
@@ -8438,13 +8435,12 @@ bool setup_tables_and_check_access(THD *thd, Name_resolution_context *context,
84388435
bool select_insert,
84398436
privilege_t want_access_first,
84408437
privilege_t want_access,
8441-
bool full_table_list,
8442-
bool resolve_opt_hints)
8438+
bool full_table_list)
84438439
{
84448440
DBUG_ENTER("setup_tables_and_check_access");
84458441

84468442
if (setup_tables(thd, context, from_clause, tables,
8447-
leaves, select_insert, full_table_list, resolve_opt_hints))
8443+
leaves, select_insert, full_table_list))
84488444
DBUG_RETURN(TRUE);
84498445

84508446
List_iterator<TABLE_LIST> ti(leaves);

sql/sql_base.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ Item ** find_item_in_list(Item *item, List<Item> &items, uint *counter,
228228
bool setup_tables(THD *thd, Name_resolution_context *context,
229229
List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
230230
List<TABLE_LIST> &leaves, bool select_insert,
231-
bool full_table_list, bool resolve_opt_hints);
231+
bool full_table_list);
232232
bool setup_tables_and_check_access(THD *thd,
233233
Name_resolution_context *context,
234234
List<TABLE_LIST> *from_clause,
@@ -237,8 +237,7 @@ bool setup_tables_and_check_access(THD *thd,
237237
bool select_insert,
238238
privilege_t want_access_first,
239239
privilege_t want_access,
240-
bool full_table_list,
241-
bool resolve_opt_hints);
240+
bool full_table_list);
242241
bool wait_while_table_is_used(THD *thd, TABLE *table,
243242
enum ha_extra_function function);
244243

sql/sql_delete.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,11 +1865,11 @@ bool Sql_cmd_delete::prepare_inner(THD *thd)
18651865
if (setup_tables_and_check_access(thd, &select_lex->context,
18661866
&select_lex->top_join_list,
18671867
table_list, select_lex->leaf_tables,
1868-
false, DELETE_ACL, SELECT_ACL, true, false))
1868+
false, DELETE_ACL, SELECT_ACL, true))
18691869
DBUG_RETURN(TRUE);
18701870

18711871
if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list,
1872-
table_list, select_lex->leaf_tables, false, false, true))
1872+
table_list, select_lex->leaf_tables, false, false))
18731873
DBUG_RETURN(TRUE);
18741874

18751875
if (!multitable)

sql/sql_help.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ static bool init_items_for_help_command(THD *thd,
801801

802802
if (setup_tables(thd, &first_select_lex->context,
803803
&first_select_lex->top_join_list,
804-
&tables[0], leaves, false, false, true))
804+
&tables[0], leaves, false, false))
805805
return true;
806806

807807
memcpy((char*) used_fields, (char*) init_used_fields,

sql/sql_insert.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1629,7 +1629,7 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list,
16291629
table_list,
16301630
thd->lex->first_select_lex()->leaf_tables,
16311631
select_insert, INSERT_ACL, SELECT_ACL,
1632-
true, true))
1632+
true))
16331633
DBUG_RETURN(TRUE);
16341634

16351635
if (insert_into_view && !fields.elements)

sql/sql_lex.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10959,6 +10959,18 @@ bool LEX::parsed_insert_select(SELECT_LEX *first_select)
1095910959
SELECT_LEX *blt __attribute__((unused))= pop_select();
1096010960
DBUG_ASSERT(blt == &builtin_select);
1096110961
push_select(first_select);
10962+
10963+
// INSERT..SELECT allows placing hints next to either INSERT or SELECT, i.e.:
10964+
// `INSERT /* hint(t1) */ INTO t2 SELECT a FROM t1` or
10965+
// `INSERT INTO t2 SELECT /* hint(t1) */ a FROM t1`
10966+
// but not at both places at the same time.
10967+
// `first_select` represents the SELECT part here while `builtin_select` -
10968+
// the INSERT part. Future processing will proceed with `first_select`,
10969+
// so transfer the hints from `builtin_select` to `first_select` in case
10970+
// they were not already set. If hints are present for both INSERT and SELECT
10971+
// parts, SELECT part hints are preserved while INSERT part hints are discarded
10972+
if (!first_select->opt_hints_qb && blt->opt_hints_qb)
10973+
first_select->opt_hints_qb= blt->opt_hints_qb;
1096210974
return false;
1096310975
}
1096410976

sql/sql_load.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ int mysql_load(THD *thd, const sql_exchange *ex, TABLE_LIST *table_list,
419419
thd->lex->first_select_lex()->leaf_tables,
420420
FALSE,
421421
INSERT_ACL | UPDATE_ACL,
422-
INSERT_ACL | UPDATE_ACL, false, true))
422+
INSERT_ACL | UPDATE_ACL, false))
423423
DBUG_RETURN(-1);
424424
if (!table_list->table || // do not support join view
425425
!table_list->single_table_updatable() || // and derived tables

sql/sql_select.cc

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1468,9 +1468,20 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
14681468
if (!(select_options & OPTION_SETUP_TABLES_DONE) &&
14691469
setup_tables_and_check_access(thd, &select_lex->context, join_list,
14701470
tables_list, select_lex->leaf_tables,
1471-
false, SELECT_ACL, SELECT_ACL, false, true))
1471+
false, SELECT_ACL, SELECT_ACL, false))
14721472
DBUG_RETURN(-1);
14731473

1474+
if (thd->lex->opt_hints_global && select_lex->select_number == 1)
1475+
{
1476+
thd->lex->opt_hints_global->fix_hint(thd);
1477+
/*
1478+
There's no need to call opt_hints_global->check_unresolved(),
1479+
this is done for each query block individually
1480+
*/
1481+
}
1482+
if (select_lex->opt_hints_qb)
1483+
select_lex->opt_hints_qb->check_unfixed(thd);
1484+
14741485
/* System Versioning: handle FOR SYSTEM_TIME clause. */
14751486
if (select_lex->vers_setup_conds(thd, tables_list) < 0)
14761487
DBUG_RETURN(-1);

sql/sql_update.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,7 +1666,7 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd)
16661666

16671667
if (setup_tables_and_check_access(thd, &select_lex->context,
16681668
&select_lex->top_join_list, table_list, select_lex->leaf_tables,
1669-
false, UPDATE_ACL, SELECT_ACL, true, false))
1669+
false, UPDATE_ACL, SELECT_ACL, true))
16701670
DBUG_RETURN(1);
16711671

16721672
if (table_list->has_period() &&
@@ -3105,7 +3105,7 @@ bool Sql_cmd_update::prepare_inner(THD *thd)
31053105
DBUG_RETURN(TRUE);
31063106

31073107
if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list,
3108-
table_list, select_lex->leaf_tables, false, false, true))
3108+
table_list, select_lex->leaf_tables, false, false))
31093109
DBUG_RETURN(TRUE);
31103110

31113111
if (select_lex->vers_setup_conds(thd, table_list))

0 commit comments

Comments
 (0)