MDEV-33281 Implement optimizer hints

This commit introduces:
    - the infrastructure for optimizer hints;
    - hints for join buffering: BNL(), NO_BNL(), BKA(), NO_BKA();
    - NO_ICP() hint for disabling index condition pushdown;
    - MRR(), MO_MRR() hint for multi-range reads control;
    - NO_RANGE_OPTIMIZATION() for disabling range optimization;
    - QB_NAME() for assigning names for query blocks.
This commit is contained in:
Oleg Smirnov 2024-07-05 19:10:36 +07:00
parent 6340c23933
commit 877e4a386c
21 changed files with 1554 additions and 63 deletions

View File

@ -160,6 +160,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
../sql/sp_cursor.cc
../sql/opt_hints_parser.cc ../sql/opt_hints_parser.h
../sql/scan_char.h
../sql/opt_hints.cc ../sql/opt_hints.h
${GEN_SOURCES}
${MYSYS_LIBWRAP_SOURCE}
)

View File

@ -139,7 +139,7 @@ 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:
Warning 4199 Unresolved name `t5`@`select#2` `z_idx` for NO_ICP hint
Warning 4205 Unresolved index name `t5`@`select#2` `z_idx` for NO_ICP hint
Note 1003 select /*+ NO_ICP(`t5`@`select#2` `y_idx`) NO_ICP(`t5`@`select#2` `x_idx`) */ `test`.`t4`.`x` AS `x`,`test`.`t5`.`y` AS `y` from `test`.`t4` join `test`.`t4` `t5` where `test`.`t4`.`y` = 8 and `test`.`t5`.`x` between 7 and <cache>(8 + 0)
# ICP should still be used
EXPLAIN EXTENDED SELECT * FROM
@ -405,7 +405,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00
1 SIMPLE t2 ALL f1 NULL NULL NULL 28 25.00 Using where; Using join buffer (flat, BNL join)
Warnings:
Warning 4198 Query block name `qb1` is not found for BKA hint
Warning 4203 Query block name `qb1` is not found for BKA hint
Note 1003 select /*+ QB_NAME(`qb1`) */ `test`.`t2`.`f1` AS `f1`,`test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f3` AS `f3` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f2` between `test`.`t1`.`f1` and `test`.`t1`.`f2` and `test`.`t2`.`f2` + 1 >= `test`.`t1`.`f1` + 1
# Should not crash
PREPARE stmt1 FROM "SELECT /*+ BKA(t2) */ t2.f1, t2.f2, t2.f3 FROM t1,t2
@ -449,9 +449,9 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00
1 SIMPLE t2 ALL f1 NULL NULL NULL 28 25.00 Using where; Using join buffer (flat, BNL join)
Warnings:
Warning 4199 Unresolved name `t3`@`select#1` for BKA hint
Warning 4199 Unresolved name `t3`@`select#1` for NO_RANGE_OPTIMIZATION hint
Warning 4199 Unresolved name `t3`@`select#1` `idx1` for NO_RANGE_OPTIMIZATION hint
Warning 4204 Unresolved table name `t3`@`select#1` for BKA hint
Warning 4204 Unresolved table name `t3`@`select#1` for NO_RANGE_OPTIMIZATION hint
Warning 4205 Unresolved index name `t3`@`select#1` `idx1` for NO_RANGE_OPTIMIZATION hint
Note 1003 select /*+ BKA(`t2`@`select#1`) NO_BNL(`t1`@`select#1`) */ `test`.`t2`.`f1` AS `f1`,`test`.`t2`.`f2` AS `f2`,`test`.`t2`.`f3` AS `f3` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`f1` = `test`.`t1`.`f1` and `test`.`t2`.`f2` between `test`.`t1`.`f1` and `test`.`t1`.`f2` and `test`.`t2`.`f2` + 1 >= `test`.`t1`.`f1` + 1
# Check illegal syntax
EXPLAIN EXTENDED SELECT /*+ BKA(qb1 t3@qb1) */ f2 FROM
@ -488,7 +488,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE tbl12 ALL NULL NULL NULL NULL 10 100.00 Using where
1 SIMPLE tbl13 hash_ALL a #hash#a 5 test.tbl12.a 1000 0.10 Using where; Using join buffer (flat, BNLH join)
Warnings:
Warning 4199 Unresolved name `tbl2`@`select#1` for BKA hint
Warning 4204 Unresolved table name `tbl2`@`select#1` for BKA hint
Note 1003 select `test`.`tbl12`.`a` AS `a`,`test`.`tbl12`.`b` AS `b`,`test`.`tbl13`.`a` AS `a`,`test`.`tbl13`.`b` AS `b`,`test`.`tbl13`.`c` AS `c`,`test`.`tbl13`.`filler` AS `filler` from `test`.`t12` `tbl12` join `test`.`t13` `tbl13` where `test`.`tbl13`.`a` = `test`.`tbl12`.`a` and `test`.`tbl13`.`b` + 1 <= `test`.`tbl13`.`b` + 1
# Check that PS and conventional statements give the same result.
FLUSH STATUS;
@ -930,19 +930,19 @@ SELECT /*+ BKA() BKA() */ 1;
1
1
Warnings:
Warning 4197 Hint BKA( ) is ignored as conflicting/duplicated
Warning 4202 Hint BKA() is ignored as conflicting/duplicated
SELECT /*+ BKA(t1) BKA(t1) */ * FROM t1;
i
Warnings:
Warning 4197 Hint BKA(`t1` ) is ignored as conflicting/duplicated
Warning 4202 Hint BKA(`t1`) is ignored as conflicting/duplicated
SELECT /*+ QB_NAME(q1) BKA(t1@q1) BKA(t1@q1) */ * FROM t1;
i
Warnings:
Warning 4197 Hint BKA(`t1`@`q1` ) is ignored as conflicting/duplicated
Warning 4202 Hint BKA(`t1`@`q1`) is ignored as conflicting/duplicated
SELECT /*+ QB_NAME(q1) NO_ICP(@q1 t1 PRIMARY) NO_ICP(@q1 t1 PRIMARY) */ * FROM t1;
i
Warnings:
Warning 4197 Hint NO_ICP(`t1`@`q1` `PRIMARY` ) is ignored as conflicting/duplicated
Warning 4202 Hint NO_ICP(`t1`@`q1` `PRIMARY`) is ignored as conflicting/duplicated
DROP TABLE t1;
# WL#8016 Parser for optimizer hints
CREATE TABLE t1 (i INT, j INT);
@ -955,12 +955,12 @@ SELECT /*+*/ 1;
1
1
Warnings:
Warning 1064 Optimizer hint syntax error near '*/' at line 1
Warning 1064 Optimizer hint syntax error near '*/ 1' at line 1
SELECT /*+ */ 1;
1
1
Warnings:
Warning 1064 Optimizer hint syntax error near '*/' at line 1
Warning 1064 Optimizer hint syntax error near '*/ 1' at line 1
SELECT /*+ * ** / // /* */ 1;
1
1
@ -990,22 +990,22 @@ SELECT /*+ `@` */ 1;
1
1
Warnings:
Warning 1064 Optimizer hint syntax error near '@` */ 1' at line 1
Warning 1064 Optimizer hint syntax error near '`@` */ 1' at line 1
SELECT /*+ `@foo` */ 1;
1
1
Warnings:
Warning 1064 Optimizer hint syntax error near '@foo` */ 1' at line 1
Warning 1064 Optimizer hint syntax error near '`@foo` */ 1' at line 1
SELECT /*+ `foo@bar` */ 1;
1
1
Warnings:
Warning 1064 Optimizer hint syntax error near 'foo@bar` */ 1' at line 1
Warning 1064 Optimizer hint syntax error near '`foo@bar` */ 1' at line 1
SELECT /*+ `foo @bar` */ 1;
1
1
Warnings:
Warning 1064 Optimizer hint syntax error near 'foo @bar` */ 1' at line 1
Warning 1064 Optimizer hint syntax error near '`foo @bar` */ 1' at line 1
SELECT /*+ BKA( @) */ 1;
1
1
@ -1020,7 +1020,7 @@ SELECT /*+ BKA(t1 @) */ 1;
1
1
Warnings:
Warning 1064 Optimizer hint syntax error near '@) */ 1' at line 1
Warning 1064 Optimizer hint syntax error near ') */ 1' at line 1
# We don't support "*/" inside quoted identifiers (syntax error):
@ -1079,15 +1079,15 @@ Warning 1064 Optimizer hint syntax error near 'b) */ 1 FROM t1 a, t1 b' at lin
SELECT /*+ NO_ICP(i1) */ 1 FROM t1;
1
Warnings:
Warning 4199 Unresolved name `i1`@`select#1` for NO_ICP hint
Warning 4204 Unresolved table name `i1`@`select#1` for NO_ICP hint
SELECT /*+ NO_ICP(i1 i2) */ 1 FROM t1;
1
Warnings:
Warning 4199 Unresolved name `i1`@`select#1` `i2` for NO_ICP hint
Warning 4205 Unresolved index name `i1`@`select#1` `i2` for NO_ICP hint
SELECT /*+ NO_ICP(@qb ident) */ 1 FROM t1;
1
Warnings:
Warning 4198 Query block name `qb` is not found for NO_ICP hint
Warning 4203 Query block name `qb` is not found for NO_ICP hint
# valid hint sequences, no warnings expected:
@ -1142,12 +1142,12 @@ CREATE INDEX 3rd_index ON t1(i, j);
SELECT /*+ NO_ICP(3rd_index) */ 1 FROM t1;
1
Warnings:
Warning 4199 Unresolved name `3rd_index`@`select#1` for NO_ICP hint
Warning 4204 Unresolved table name `3rd_index`@`select#1` for NO_ICP hint
CREATE INDEX $index ON t1(j, i);
SELECT /*+ NO_ICP($index) */ 1 FROM t1;
1
Warnings:
Warning 4199 Unresolved name `$index`@`select#1` for NO_ICP hint
Warning 4204 Unresolved table name `$index`@`select#1` for NO_ICP hint
CREATE TABLE ` quoted name test` (i INT);
SELECT /*+ BKA(` quoted name test`) */ 1 FROM t1;
1
@ -1184,7 +1184,7 @@ SET SQL_MODE = '';
SELECT /*+ BKA(" quoted name test") */ 1 FROM t1;
1
Warnings:
Warning 1064 Optimizer hint syntax error near '" quoted name test") */ 1 FROM t1' at line 1
Warning 4204 Unresolved table name `" quoted name test"`@`select#1` for BKA hint
DROP TABLE ` quoted name test`;
DROP TABLE `test1``test2```;
# Valid hints, no warning:
@ -1228,11 +1228,11 @@ CREATE TABLE tableТ (i INT);
SELECT /*+ BKA(tableТ) */ 1 FROM t1;
1
Warnings:
Warning 4199 Unresolved name `tableТ`@`select#1` for BKA hint
Warning 4204 Unresolved table name `tableТ`@`select#1` for BKA hint
SELECT /*+ BKA(test@tableТ) */ 1 FROM t1;
1
Warnings:
Warning 4198 Query block name `tableТ` is not found for BKA hint
Warning 4203 Query block name `tableТ` is not found for BKA hint
DROP TABLE tableТ;
CREATE TABLE таблица (i INT);
SELECT /*+ BKA(`таблица`) */ 1 FROM t1;
@ -1242,11 +1242,11 @@ Warning 4199 Unresolved name `таблица`@`select#1` for BKA hint
SELECT /*+ BKA(таблица) */ 1 FROM t1;
1
Warnings:
Warning 4199 Unresolved name `таблица`@`select#1` for BKA hint
Warning 4204 Unresolved table name `таблица`@`select#1` for BKA hint
SELECT /*+ BKA(test@таблица) */ 1 FROM t1;
1
Warnings:
Warning 4198 Query block name `таблица` is not found for BKA hint
Warning 4203 Query block name `таблица` is not found for BKA hint
SELECT /*+ NO_ICP(`\D1`) */ 1 FROM t1;
1
Warnings:
@ -1287,12 +1287,12 @@ SELECT /*+ NO_ICP(10) */ 1;
1
1
Warnings:
Warning 1064 Optimizer hint syntax error near '10) */ 1' at line 1
Warning 4204 Unresolved table name `10`@`select#1` for NO_ICP hint
SELECT /*+ NO_ICP( */ 1;
1
1
Warnings:
Warning 1064 Optimizer hint syntax error near '*/' at line 1
Warning 1064 Optimizer hint syntax error near '*/ 1' at line 1
SELECT /*+ NO_ICP) */ 1;
1
1
@ -1302,7 +1302,7 @@ SELECT /*+ NO_ICP(t1 */ 1;
1
1
Warnings:
Warning 1064 Optimizer hint syntax error near '*/' at line 1
Warning 1064 Optimizer hint syntax error near '*/ 1' at line 1
SELECT /*+ NO_ICP(t1 ( */ 1;
1
1
@ -1347,7 +1347,7 @@ NO_ICP(@qb ident)
1
1
Warnings:
Warning 4198 Query block name `qb` is not found for NO_ICP hint
Warning 4203 Query block name `qb` is not found for NO_ICP hint
SELECT /*+
? bad syntax
*/ 1;

View File

@ -194,6 +194,7 @@ SET (SQL_SOURCE
opt_vcol_substitution.h
opt_vcol_substitution.cc
opt_hints_parser.cc opt_hints_parser.h scan_char.h
opt_hints.cc opt_hints.h
${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h
${CMAKE_CURRENT_BINARY_DIR}/lex_token.h
${GEN_SOURCES}

View File

@ -43,6 +43,7 @@
__has_trivial_destructor is supported by some (but not all)
compilers we use.
*/
template<typename Element_type, bool has_trivial_destructor>
class Mem_root_array
{
@ -53,7 +54,6 @@ public:
Mem_root_array(MEM_ROOT *root)
: m_root(root), m_array(NULL), m_size(0), m_capacity(0)
{
DBUG_ASSERT(m_root != NULL);
}
Mem_root_array(MEM_ROOT *root, size_t n, const value_type &val= value_type())
@ -62,6 +62,20 @@ public:
resize(n, val);
}
Mem_root_array(const Mem_root_array& other)
{
do_copy_construct(other);
}
Mem_root_array &operator=(const Mem_root_array& other)
{
if(this != &other)
{
clear();
do_copy_construct(other);
}
}
~Mem_root_array()
{
clear();
@ -231,14 +245,22 @@ public:
const MEM_ROOT *mem_root() const { return m_root; }
private:
MEM_ROOT *const m_root;
Element_type *m_array;
size_t m_size;
size_t m_capacity;
MEM_ROOT *m_root;
Element_type *m_array= nullptr;
size_t m_size= 0;
size_t m_capacity= 0;
// Not (yet) implemented.
Mem_root_array(const Mem_root_array&);
Mem_root_array &operator=(const Mem_root_array&);
void do_copy_construct(const Mem_root_array& other)
{
m_root= other.m_root;
reserve(other.size());
for (size_t ix= 0; ix < other.size(); ++ix)
{
Element_type *p= &m_array[ix];
new (p) Element_type(other[ix]);
}
m_size= other.m_size;
}
};

View File

@ -21,6 +21,7 @@
#include "sql_statistics.h"
#include "rowid_filter.h"
#include "optimizer_defaults.h"
#include "opt_hints.h"
static void get_sweep_read_cost(TABLE *table, ha_rows nrows, bool interrupted,
Cost_estimate *cost);
@ -1148,7 +1149,9 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
buf_manager.reset_buffer_sizes= do_nothing;
buf_manager.redistribute_buffer_space= do_nothing;
if (mode & (HA_MRR_USE_DEFAULT_IMPL | HA_MRR_SORTED))
if (!hint_key_state(thd, table, h_arg->active_index,
MRR_HINT_ENUM, OPTIMIZER_SWITCH_MRR) ||
mode & (HA_MRR_USE_DEFAULT_IMPL | HA_MRR_SORTED))
goto use_default_impl;
/*
@ -1901,10 +1904,16 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
THD *thd= primary_file->get_table()->in_use;
TABLE_SHARE *share= primary_file->get_table_share();
const bool mrr_on= hint_key_state(thd, table, keyno, MRR_HINT_ENUM,
OPTIMIZER_SWITCH_MRR);
const bool force_dsmrr_by_hints=
hint_key_state(thd, table, keyno, MRR_HINT_ENUM, 0) ||
hint_table_state(thd, table, BKA_HINT_ENUM, 0);
bool doing_cpk_scan= check_cpk_scan(thd, share, keyno, *flags);
bool using_cpk= primary_file->is_clustering_key(keyno);
*flags &= ~HA_MRR_IMPLEMENTATION_FLAGS;
if (!optimizer_flag(thd, OPTIMIZER_SWITCH_MRR) ||
if (!(mrr_on || force_dsmrr_by_hints) ||
*flags & HA_MRR_INDEX_ONLY ||
(using_cpk && !doing_cpk_scan) || key_uses_partial_cols(share, keyno))
{
@ -1919,15 +1928,17 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
&dsmrr_cost))
return TRUE;
bool force_dsmrr;
/*
If mrr_cost_based flag is not set, then set cost of DS-MRR to be minimum of
DS-MRR and Default implementations cost. This allows one to force use of
DS-MRR whenever it is applicable without affecting other cost-based
choices.
choices. Note that if MRR or BKA hint is
specified, DS-MRR will be used regardless of cost.
*/
if ((force_dsmrr= !optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_COST_BASED)) &&
dsmrr_cost.total_cost() > cost->total_cost())
const bool force_dsmrr=
(force_dsmrr_by_hints ||
!optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_COST_BASED));
if (force_dsmrr && dsmrr_cost.total_cost() > cost->total_cost())
dsmrr_cost= *cost;
if (force_dsmrr || dsmrr_cost.total_cost() <= cost->total_cost())

726
sql/opt_hints.cc Normal file
View File

@ -0,0 +1,726 @@
/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "my_global.h"
#include "sql_class.h"
#include "sql_lex.h"
#include "sql_select.h"
#include "opt_hints.h"
/**
Information about hints. Sould be
synchronized with opt_hints_enum enum.
Note: Hint name depends on hint state. 'NO_' prefix is added
if appropriate hint state bit(see Opt_hints_map::hints) is not
set. Depending on 'switch_state_arg' argument in 'parse tree
object' constructors(see parse_tree_h../cnm ints.[h,cc]) implementor
can control wishful form of the hint name.
*/
struct st_opt_hint_info opt_hint_info[]=
{
{"BKA", true, true},
{"BNL", true, true},
{"ICP", true, true},
{"MRR", true, true},
{"NO_RANGE_OPTIMIZATION", true, true},
{"QB_NAME", false, false},
{0, 0, 0}
};
/**
Prefix for system generated query block name.
Used in information warning in EXPLAIN oputput.
*/
const LEX_CSTRING sys_qb_prefix= {"select#", 7};
/*
Compare LEX_CSTRING objects.
@param s Pointer to LEX_CSTRING
@param t Pointer to LEX_CSTRING
@return 0 if strings are equal
1 if s is greater
-1 if t is greater
*/
static int cmp_lex_string(const LEX_CSTRING *s,
const LEX_CSTRING *t)
{
return system_charset_info->
coll->strnncollsp(system_charset_info,
(uchar *) s->str, s->length,
(uchar *) t->str, t->length);
}
static void print_warn(THD *thd, uint err_code, opt_hints_enum hint_type,
bool hint_state,
const LEX_CSTRING *qb_name_arg,
const LEX_CSTRING *table_name_arg,
const LEX_CSTRING *key_name_arg/*, PT_hint *hint*/)
{
String str;
/* Append hint name */
if (!hint_state)
str.append(STRING_WITH_LEN("NO_"));
str.append(opt_hint_info[hint_type].hint_name);
/* ER_WARN_UNKNOWN_QB_NAME with two arguments */
if (err_code == ER_WARN_UNKNOWN_QB_NAME)
{
String qb_name_str;
append_identifier(thd, &qb_name_str, qb_name_arg->str, qb_name_arg->length);
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
err_code, ER_THD(thd, err_code),
qb_name_str.c_ptr_safe(), str.c_ptr_safe());
return;
}
/* ER_WARN_CONFLICTING_HINT with one argument */
str.append('(');
/* Append table name */
if (table_name_arg && table_name_arg->length > 0)
append_identifier(thd, &str, table_name_arg->str, table_name_arg->length);
/* Append QB name */
if (qb_name_arg && qb_name_arg->length > 0)
{
str.append(STRING_WITH_LEN("@"));
append_identifier(thd, &str, qb_name_arg->str, qb_name_arg->length);
}
/* Append key name */
if (key_name_arg && key_name_arg->length > 0)
{
str.append(' ');
append_identifier(thd, &str, key_name_arg->str, key_name_arg->length);
}
/* Append additional hint arguments if they exist */
// OLEGS: todo
// if (hint)
// {
// if (qb_name_arg || table_name_arg || key_name_arg)
// str.append(' ');
// hint->append_args(thd, &str);
// }
str.append(')');
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
err_code, ER_THD(thd, err_code), str.c_ptr_safe());
}
/**
Returns a pointer to Opt_hints_global object,
creates Opt_hints object if not exist.
@param pc pointer to Parse_context object
@return pointer to Opt_hints object,
NULL if failed to create the object
*/
static Opt_hints_global *get_global_hints(Parse_context *pc)
{
LEX *lex= pc->thd->lex;
if (!lex->opt_hints_global)
lex->opt_hints_global= new Opt_hints_global(pc->thd->mem_root);
if (lex->opt_hints_global)
lex->opt_hints_global->set_resolved();
return lex->opt_hints_global;
}
static Opt_hints_qb *get_qb_hints(Parse_context *pc)
{
if (pc->select->opt_hints_qb)
return pc->select->opt_hints_qb;
Opt_hints_global *global_hints= get_global_hints(pc);
if (global_hints == NULL)
return NULL;
Opt_hints_qb *qb= new Opt_hints_qb(global_hints, pc->thd->mem_root,
pc->select->select_number);
if (qb)
{
global_hints->register_child(qb);
pc->select->opt_hints_qb= qb;
qb->set_resolved();
}
return qb;
}
/**
Find existing Opt_hints_qb object, print warning
if the query block is not found.
@param pc pointer to Parse_context object
@param table_name query block name
@param hint processed hint // OLEGS: amend this
@return pointer to Opt_hints_table object if found,
NULL otherwise
*/
static Opt_hints_qb *find_qb_hints(Parse_context *pc,
const LEX_CSTRING *qb_name,
opt_hints_enum hint_type,
bool hint_state)
{
if (qb_name->length == 0) // no QB NAME is used
return pc->select->opt_hints_qb;
Opt_hints_qb *qb= static_cast<Opt_hints_qb *>
(pc->thd->lex->opt_hints_global->find_by_name(qb_name));
if (qb == NULL)
{
print_warn(pc->thd, ER_WARN_UNKNOWN_QB_NAME, hint_type, hint_state,
qb_name, NULL, NULL);
}
return qb;
}
/**
Returns pointer to Opt_hints_table object,
create Opt_hints_table object if not exist.
@param pc pointer to Parse_context object
@param table_name pointer to Hint_param_table object
@param qb pointer to Opt_hints_qb object
@return pointer to Opt_hints_table object,
NULL if failed to create the object
*/
static Opt_hints_table *get_table_hints(Parse_context *pc,
const LEX_CSTRING *table_name,
Opt_hints_qb *qb)
{
Opt_hints_table *tab=
static_cast<Opt_hints_table *> (qb->find_by_name(table_name));
if (!tab)
{
tab= new Opt_hints_table(table_name, qb, pc->thd->mem_root);
qb->register_child(tab);
}
return tab;
}
bool Opt_hints::get_switch(opt_hints_enum type_arg) const
{
if (is_specified(type_arg))
return hints_map.switch_on(type_arg);
if (opt_hint_info[type_arg].check_upper_lvl)
return parent->get_switch(type_arg);
return false;
}
Opt_hints* Opt_hints::find_by_name(const LEX_CSTRING *name_arg) const
{
for (uint i= 0; i < child_array.size(); i++)
{
const LEX_CSTRING *name= child_array[i]->get_name();
if (name && !cmp_lex_string(name, name_arg))
return child_array[i];
}
return NULL;
}
void Opt_hints::print(THD *thd, String *str)
{
for (uint i= 0; i < MAX_HINT_ENUM; i++)
{
if (is_specified(static_cast<opt_hints_enum>(i)) && is_resolved())
{
append_hint_type(str, static_cast<opt_hints_enum>(i));
str->append(STRING_WITH_LEN("("));
append_name(thd, str);
// OLEGS:
//if (!opt_hint_info[i].switch_hint)
// get_complex_hints(i)->append_args(thd, str);
str->append(STRING_WITH_LEN(") "));
}
}
for (uint i= 0; i < child_array.size(); i++)
child_array[i]->print(thd, str);
}
void Opt_hints::append_hint_type(String *str, opt_hints_enum type)
{
const char* hint_name= opt_hint_info[type].hint_name;
if(!hints_map.switch_on(type))
str->append(STRING_WITH_LEN("NO_"));
str->append(hint_name);
}
void Opt_hints::print_warn_unresolved(THD *thd)
{
String hint_name_str, hint_type_str;
append_name(thd, &hint_name_str);
for (uint i= 0; i < MAX_HINT_ENUM; i++)
{
if (is_specified(static_cast<opt_hints_enum>(i)))
{
hint_type_str.length(0);
append_hint_type(&hint_type_str, static_cast<opt_hints_enum>(i));
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
get_warn_unresolved_code(),
ER_THD(thd, get_warn_unresolved_code()),
hint_name_str.c_ptr_safe(),
hint_type_str.c_ptr_safe());
}
}
}
void Opt_hints::check_unresolved(THD *thd)
{
if (!is_resolved())
print_warn_unresolved(thd);
if (!is_all_resolved())
{
for (uint i= 0; i < child_array.size(); i++)
child_array[i]->check_unresolved(thd);
}
}
PT_hint *Opt_hints_global::get_complex_hints(uint type)
{
DBUG_ASSERT(0);
return NULL;
}
Opt_hints_qb::Opt_hints_qb(Opt_hints *opt_hints_arg,
MEM_ROOT *mem_root_arg,
uint select_number_arg)
: Opt_hints(NULL, opt_hints_arg, mem_root_arg),
select_number(select_number_arg)
{
sys_name.str= buff;
sys_name.length= my_snprintf(buff, sizeof(buff), "%s%lx",
sys_qb_prefix.str, select_number);
}
Opt_hints_table *Opt_hints_qb::adjust_table_hints(TABLE *table,
const LEX_CSTRING *alias)
{
Opt_hints_table *tab= static_cast<Opt_hints_table *>(find_by_name(alias));
table->pos_in_table_list->opt_hints_qb= this;
if (!tab) // Tables not found
return NULL;
tab->adjust_key_hints(table);
return tab;
}
void Opt_hints_table::adjust_key_hints(TABLE *table)
{
set_resolved();
if (child_array_ptr()->size() == 0) // No key level hints
{
get_parent()->incr_resolved_children();
return;
}
/* Make sure that adjustement is called only once. */
DBUG_ASSERT(keyinfo_array.size() == 0);
keyinfo_array.resize(table->s->keys, NULL);
for (Opt_hints** hint= child_array_ptr()->begin();
hint < child_array_ptr()->end(); ++hint)
{
KEY *key_info= table->key_info;
for (uint j= 0 ; j < table->s->keys ; j++, key_info++)
{
if (!cmp_lex_string((*hint)->get_name(), &key_info->name))
{
(*hint)->set_resolved();
keyinfo_array[j]= static_cast<Opt_hints_key *>(*hint);
incr_resolved_children();
}
}
}
/*
Do not increase number of resolved tables
if there are unresolved key objects. It's
important for check_unresolved() function.
*/
if (is_all_resolved())
get_parent()->incr_resolved_children();
}
/**
Function returns hint value depending on
the specfied hint level. If hint is specified
on current level, current level hint value is
returned, otherwise parent level hint is checked.
@param hint Pointer to the hint object
@param parent_hint Pointer to the parent hint object,
should never be NULL
@param type_arg hint type
@param OUT ret_val hint value depending on
what hint level is used
@return true if hint is specified, false otherwise
*/
static bool get_hint_state(Opt_hints *hint,
Opt_hints *parent_hint,
opt_hints_enum type_arg,
bool *ret_val)
{
DBUG_ASSERT(parent_hint);
if (opt_hint_info[type_arg].switch_hint)
{
if (hint && hint->is_specified(type_arg))
{
*ret_val= hint->get_switch(type_arg);
return true;
}
else if (opt_hint_info[type_arg].check_upper_lvl &&
parent_hint->is_specified(type_arg))
{
*ret_val= parent_hint->get_switch(type_arg);
return true;
}
}
else
{
/* Complex hint, not implemented atm */
DBUG_ASSERT(0);
}
return false;
}
bool hint_key_state(const THD *thd, const TABLE *table,
uint keyno, opt_hints_enum type_arg,
uint optimizer_switch)
{
Opt_hints_table *table_hints= table->pos_in_table_list->opt_hints_table;
/* Parent should always be initialized */
if (table_hints && keyno != MAX_KEY)
{
Opt_hints_key *key_hints= table_hints->keyinfo_array.size() > 0 ?
table_hints->keyinfo_array[keyno] : NULL;
bool ret_val= false;
if (get_hint_state(key_hints, table_hints, type_arg, &ret_val))
return ret_val;
}
return optimizer_flag(thd, optimizer_switch);
}
bool hint_table_state(const THD *thd, const TABLE *table,
opt_hints_enum type_arg,
uint optimizer_switch)
{
TABLE_LIST *table_list= table->pos_in_table_list;
if (table_list->opt_hints_qb)
{
bool ret_val= false;
if (get_hint_state(table_list->opt_hints_table,
table_list->opt_hints_qb,
type_arg, &ret_val))
return ret_val;
}
return optimizer_flag(thd, optimizer_switch);
}
bool hint_table_state_or_fallback(const THD *thd, const TABLE *table,
opt_hints_enum type_arg,
bool fallback_value)
{
TABLE_LIST *table_list= table->pos_in_table_list;
if (table_list->opt_hints_qb)
{
bool ret_val= false;
if (get_hint_state(table_list->opt_hints_table,
table_list->opt_hints_qb,
type_arg, &ret_val))
return ret_val;
}
return fallback_value;
}
bool Optimizer_hint_parser::Table_level_hint::resolve(Parse_context *pc) const
{
const Table_level_hint_type &table_level_hint_type= *this;
opt_hints_enum hint_type;
bool hint_state; // ON or OFF
switch (table_level_hint_type.id())
{
case TokenID::keyword_BNL:
hint_type= BNL_HINT_ENUM;
hint_state= true;
break;
case TokenID::keyword_NO_BNL:
hint_type= BNL_HINT_ENUM;
hint_state= false;
break;
case TokenID::keyword_BKA:
hint_type= BKA_HINT_ENUM;
hint_state= true;
break;
case TokenID::keyword_NO_BKA:
hint_type= BKA_HINT_ENUM;
hint_state= false;
break;
default:
DBUG_ASSERT(0);
return true;
}
if (const At_query_block_name_opt_table_name_list &
at_query_block_name_opt_table_name_list= *this)
{
// this is @ query_block_name opt_table_name_list
const Query_block_name &qb_name= *this;
Opt_hints_qb *qb= find_qb_hints(pc, &qb_name, hint_type, hint_state);
if (qb == NULL)
return false;
if (at_query_block_name_opt_table_name_list.is_empty())
{
// e.g. BKA(@qb1)
if (qb->set_switch(hint_state, hint_type, false))
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
&qb_name, NULL, NULL/*, this*/);
return false;
}
else
{
// e.g. BKA(@qb1 t1, t2, t3)
const Opt_table_name_list &opt_table_name_list= *this;
for (const Table_name &table : opt_table_name_list)
{
Opt_hints_table *tab= get_table_hints(pc, &table, qb);
if (!tab)
return true; // OLEGS: why no warning?
if (tab->set_switch(hint_state, hint_type, true))
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
&qb_name, &table, NULL/*, this*/);
}
}
}
else
{
// this is opt_hint_param_table_list
const Opt_table_name_list &table_name_list= *this;
const Opt_hint_param_table_list &opt_hint_param_table_list= *this;
Opt_hints_qb *qb= find_qb_hints(pc, &null_clex_str, hint_type, hint_state);
if (qb == NULL)
return false;
if (table_name_list.is_empty() && opt_hint_param_table_list.is_empty())
{
// e.g. BKA()
if (qb->set_switch(hint_state, hint_type, false))
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
&null_clex_str, NULL, NULL/*, this*/);
return false;
}
for (const Table_name &table : table_name_list)
{
// e.g. BKA(t1, t2)
Opt_hints_table *tab= get_table_hints(pc, &table, qb);
if (!tab)
return true; // OLEGS: no warning?
if (tab->set_switch(hint_state, hint_type, true))
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
&null_clex_str, &table, NULL/*, this*/);
}
for (const Hint_param_table &table : opt_hint_param_table_list)
{
// e.g. BKA(t1@qb1, t2@qb2)
const Query_block_name &qb_name= table;
const Table_name &table_name= table;
Opt_hints_qb *qb= find_qb_hints(pc, &qb_name, hint_type, hint_state);
if (qb == NULL)
return false;
// OLEGS: todo
Opt_hints_table *tab= get_table_hints(pc, &table_name, qb);
if (!tab)
return true; // OLEGS: why no warning?
if (tab->set_switch(hint_state, hint_type, true))
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
&qb_name, &table_name, NULL/*, this*/);
}
}
return false;
}
bool Optimizer_hint_parser::Index_level_hint::resolve(Parse_context *pc) const
{
const Index_level_hint_type &index_level_hint_type= *this;
opt_hints_enum hint_type;
bool hint_state; // ON or OFF
switch (index_level_hint_type.id())
{
case TokenID::keyword_NO_ICP:
hint_type= ICP_HINT_ENUM;
hint_state= false;
break;
case TokenID::keyword_MRR:
hint_type= MRR_HINT_ENUM;
hint_state= true;
break;
case TokenID::keyword_NO_MRR:
hint_type= MRR_HINT_ENUM;
hint_state= false;
break;
case TokenID::keyword_NO_RANGE_OPTIMIZATION:
hint_type= NO_RANGE_HINT_ENUM;
hint_state= true;
break;
default:
DBUG_ASSERT(0);
return true;
}
const Hint_param_table_ext &table_ext= *this;
const Query_block_name &qb_name= table_ext;
const Table_name &table_name= table_ext;
Opt_hints_qb *qb= find_qb_hints(pc, &qb_name, hint_type, hint_state);
if (qb == NULL)
return false;
Opt_hints_table *tab= get_table_hints(pc, &table_name, qb);
if (!tab)
return true; // OLEGS: why no warning?
if (is_empty()) // Table level hint
{
if (tab->set_switch(hint_state, hint_type, false))
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
&qb_name, &table_name, NULL/*, this*/);
return false;
}
for (const Hint_param_index &index_name : *this)
{
Opt_hints_key *idx= (Opt_hints_key *)tab->find_by_name(&index_name);
if (!idx)
{
idx= new Opt_hints_key(&index_name, tab, pc->thd->mem_root);
tab->register_child(idx);
}
if (idx->set_switch(hint_state, hint_type, true))
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state,
&qb_name, &table_name, &index_name/*, this*/);
}
return false;
}
bool Optimizer_hint_parser::Qb_name_hint::resolve(Parse_context *pc) const
{
Opt_hints_qb *qb= pc->select->opt_hints_qb;
DBUG_ASSERT(qb);
const Query_block_name &qb_name= *this;
if (qb->get_name() || // QB name is already set
qb->get_parent()->find_by_name(&qb_name)) // Name is already used
{
print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, QB_NAME_HINT_ENUM, false,
NULL, NULL, NULL/*, this*/);
return false;
}
qb->set_name(&qb_name);
return false;
}
bool Optimizer_hint_parser::Hint_list::resolve(Parse_context *pc)
{
if (!get_qb_hints(pc))
return true;
List_iterator_fast<Optimizer_hint_parser::Hint> li(*this);
while(Optimizer_hint_parser::Hint *hint= li++)
{
if (const Table_level_hint &table_hint=
static_cast<const Table_level_hint &>(*hint))
{
if (table_hint.resolve(pc))
return true;
}
else if (const Index_level_hint &index_hint=
static_cast<const Index_level_hint &>(*hint))
{
if (index_hint.resolve(pc))
return true;
}
else if (const Qb_name_hint &qb_hint=
static_cast<const Qb_name_hint &>(*hint))
{
if (qb_hint.resolve(pc))
return true; // OLEGS: check this result
}
}
return false;
}

507
sql/opt_hints.h Normal file
View File

@ -0,0 +1,507 @@
/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/*
Parse tree node classes for optimizer hint syntax
*/
#ifndef OPT_HINTS_INCLUDED
#define OPT_HINTS_INCLUDED
#include "my_config.h"
#include "sql_alloc.h"
#include "sql_list.h"
#include "mem_root_array.h"
#include "sql_string.h"
#include "sql_bitmap.h"
#include "sql_show.h"
#include "mysqld_error.h"
struct LEX;
struct TABLE;
/**
Hint types, MAX_HINT_ENUM should be always last.
This enum should be synchronized with opt_hint_info
array(see opt_hints.cc).
*/
enum opt_hints_enum
{
BKA_HINT_ENUM= 0,
BNL_HINT_ENUM,
ICP_HINT_ENUM,
MRR_HINT_ENUM,
NO_RANGE_HINT_ENUM,
QB_NAME_HINT_ENUM,
MAX_HINT_ENUM
};
struct st_opt_hint_info
{
const char* hint_name; // Hint name.
bool check_upper_lvl; // true if upper level hint check is needed (for hints
// which can be specified on more than one level).
bool switch_hint; // true if hint is not complex.
};
/**
Opt_hints_map contains information
about hint state(specified or not, hint value).
*/
class Opt_hints_map : public Sql_alloc
{
Bitmap<64> hints; // hint state
Bitmap<64> hints_specified; // true if hint is specified
public:
Opt_hints_map()
{
hints.clear_all();
hints_specified.clear_all();
}
/**
Check if hint is specified.
@param type_arg hint type
@return true if hint is specified,
false otherwise
*/
my_bool is_specified(opt_hints_enum type_arg) const
{
return hints_specified.is_set(type_arg);
}
/**
Set switch value and set hint into specified state.
@param type_arg hint type
@param switch_state_arg switch value
*/
void set_switch(opt_hints_enum type_arg,
bool switch_state_arg)
{
if (switch_state_arg)
hints.set_bit(type_arg);
else
hints.clear_bit(type_arg);
hints_specified.set_bit(type_arg);
}
/**
Get switch value.
@param type_arg hint type
@return switch value.
*/
bool switch_on(opt_hints_enum type_arg) const
{
return hints.is_set(type_arg);
}
};
class PT_hint;
class PT_hint_max_execution_time;
class Opt_hints_key;
/**
Opt_hints class is used as ancestor for Opt_hints_global,
Opt_hints_qb, Opt_hints_table, Opt_hints_key classes.
Opt_hints_global class is hierarchical structure.
It contains information about global hints and also
conains array of QUERY BLOCK level objects (Opt_hints_qb class).
Each QUERY BLOCK level object contains array of TABLE level hints
(class Opt_hints_table). Each TABLE level hint contains array of
KEY lelev hints (Opt_hints_key class).
Hint information(specified, on|off state) is stored in hints_map object.
*/
class Opt_hints : public Sql_alloc
{
/*
Name of object referred by the hint.
This name is empty for global level,
query block name for query block level,
table name for table level and key name
for key level.
*/
const LEX_CSTRING *name;
/*
Parent object. There is no parent for global level,
for query block level parent is Opt_hints_global object,
for table level parent is Opt_hints_qb object,
for key level parent is Opt_hints_key object.
*/
Opt_hints *parent;
Opt_hints_map hints_map; // Hint map
/* Array of child objects. i.e. array of the lower level objects */
Mem_root_array<Opt_hints*, true> child_array;
/* true if hint is connected to the real object */
bool resolved;
/* Number of resolved children */
uint resolved_children;
public:
Opt_hints(const LEX_CSTRING *name_arg,
Opt_hints *parent_arg,
MEM_ROOT *mem_root_arg)
: name(name_arg), parent(parent_arg), child_array(mem_root_arg),
resolved(false), resolved_children(0)
{ }
bool is_specified(opt_hints_enum type_arg) const
{
return hints_map.is_specified(type_arg);
}
/**
Function sets switch hint state.
@param switch_state_arg switch hint state
@param type_arg hint type
@param check_parent true if hint can be on parent level
@return true if hint is already specified,
false otherwise
*/
bool set_switch(bool switch_state_arg,
opt_hints_enum type_arg,
bool check_parent)
{
if (is_specified(type_arg) ||
(check_parent && parent->is_specified(type_arg)))
return true;
hints_map.set_switch(type_arg, switch_state_arg);
return false;
}
/**
Function returns switch hint state.
@param type_arg hint type
@return hint value if hint is specified,
false otherwise
*/
bool get_switch(opt_hints_enum type_arg) const;
virtual const LEX_CSTRING *get_name() const { return name; }
void set_name(const LEX_CSTRING *name_arg) { name= name_arg; }
Opt_hints *get_parent() const { return parent; }
void set_resolved() { resolved= true; }
bool is_resolved() const { return resolved; }
void incr_resolved_children() { resolved_children++; }
Mem_root_array<Opt_hints*, true> *child_array_ptr() { return &child_array; }
bool is_all_resolved() const
{
return child_array.size() == resolved_children;
}
void register_child(Opt_hints* hint_arg)
{
child_array.push_back(hint_arg);
}
// OLEGS: remove it if not used
/**
Returns pointer to complex hint for a given type
@param type hint type
@return pointer to complex hint for a given type.
*/
// virtual PT_hint *get_complex_hints(uint type)
// {
// DBUG_ASSERT(0);
// return NULL; /* error C4716: must return a value*/
// };
/**
Find hint among lower-level hint objects.
@param name_arg hint name
@return hint if found,
NULL otherwise
*/
Opt_hints *find_by_name(const LEX_CSTRING *name_arg) const;
/**
Print all hints except of QB_NAME hint.
@param thd Pointer to THD object
@param str Pointer to String object
*/
void print(THD *thd, String *str);
/**
Check if there are any unresolved hint objects and
print warnings for them.
@param thd Pointer to THD object
*/
void check_unresolved(THD *thd);
virtual void append_name(THD *thd, String *str)= 0;
virtual ~Opt_hints() {}
private:
/**
Append hint type.
@param str Pointer to String object
@param type Hint type
*/
void append_hint_type(String *str, opt_hints_enum type);
/**
Print warning for unresolved hint name.
@param thd Pointer to THD object
*/
void print_warn_unresolved(THD *thd);
protected:
/**
Override this function in descendants so that print_warn_unresolved()
prints the proper warning text for table/index level unresolved hints
*/
virtual uint get_warn_unresolved_code() const { return 0; }
};
/**
Global level hints.
*/
class Opt_hints_global : public Opt_hints
{
public:
PT_hint_max_execution_time *max_exec_time;
Opt_hints_global(MEM_ROOT *mem_root_arg)
: Opt_hints(NULL, NULL, mem_root_arg)
{
max_exec_time= NULL;
}
virtual void append_name(THD *thd, String *str) {}
virtual PT_hint *get_complex_hints(uint type);
};
class Opt_hints_table;
/**
Query block level hints.
*/
class Opt_hints_qb : public Opt_hints
{
uint select_number; // SELECT_LEX number
LEX_CSTRING sys_name; // System QB name
char buff[32]; // Buffer to hold sys name
public:
Opt_hints_qb(Opt_hints *opt_hints_arg,
MEM_ROOT *mem_root_arg,
uint select_number_arg);
const LEX_CSTRING *get_print_name()
{
const LEX_CSTRING *str= Opt_hints::get_name();
return str ? str : &sys_name;
}
/**
Append query block hint.
@param thd pointer to THD object
@param str pointer to String object
*/
void append_qb_hint(THD *thd, String *str)
{
if (get_name())
{
str->append(STRING_WITH_LEN("QB_NAME("));
append_identifier(thd, str, get_name()->str, get_name()->length);
str->append(STRING_WITH_LEN(") "));
}
}
/**
Append query block name.
@param thd pointer to THD object
@param str pointer to String object
*/
virtual void append_name(THD *thd, String *str)
{
str->append(STRING_WITH_LEN("@"));
append_identifier(thd, str, get_print_name()->str, get_print_name()->length);
}
/**
Function finds Opt_hints_table object corresponding to
table alias in the query block and attaches corresponding
key hint objects to appropriate KEY structures.
@param table Pointer to TABLE object
@param alias Table alias
@return pointer Opt_hints_table object if this object is found,
NULL otherwise.
*/
Opt_hints_table *adjust_table_hints(TABLE *table, const LEX_CSTRING *alias);
};
/**
Table level hints.
*/
class Opt_hints_table : public Opt_hints
{
public:
Mem_root_array<Opt_hints_key*, true> keyinfo_array;
Opt_hints_table(const LEX_CSTRING *table_name_arg,
Opt_hints_qb *qb_hints_arg,
MEM_ROOT *mem_root_arg)
: Opt_hints(table_name_arg, qb_hints_arg, mem_root_arg),
keyinfo_array(mem_root_arg)
{ }
/**
Append table name.
@param thd pointer to THD object
@param str pointer to String object
*/
virtual void append_name(THD *thd, String *str)
{
append_identifier(thd, str, get_name()->str, get_name()->length);
get_parent()->append_name(thd, str);
}
/**
Function sets correlation between key hint objects and
appropriate KEY structures.
@param table Pointer to TABLE object
*/
void adjust_key_hints(TABLE *table);
virtual uint get_warn_unresolved_code() const override
{
return ER_UNRESOLVED_TABLE_HINT_NAME;
}
};
/**
Key level hints.
*/
class Opt_hints_key : public Opt_hints
{
public:
Opt_hints_key(const LEX_CSTRING *key_name_arg,
Opt_hints_table *table_hints_arg,
MEM_ROOT *mem_root_arg)
: Opt_hints(key_name_arg, table_hints_arg, mem_root_arg)
{ }
/**
Append key name.
@param thd pointer to THD object
@param str pointer to String object
*/
virtual void append_name(THD *thd, String *str)
{
get_parent()->append_name(thd, str);
str->append(' ');
append_identifier(thd, str, get_name()->str, get_name()->length);
}
virtual uint get_warn_unresolved_code() const override
{
return ER_UNRESOLVED_INDEX_HINT_NAME;
}
};
/**
Returns key hint value if hint is specified, returns
optimizer switch value if hint is not specified.
@param thd Pointer to THD object
@param tab Pointer to TABLE object
@param keyno Key number
@param type_arg Hint type
@param optimizer_switch Optimizer switch flag
@return key hint value if hint is specified,
otherwise optimizer switch value.
*/
bool hint_key_state(const THD *thd, const TABLE *table,
uint keyno, opt_hints_enum type_arg,
uint optimizer_switch);
/**
Returns table hint value if hint is specified, returns
optimizer switch value if hint is not specified.
@param thd Pointer to THD object
@param tab Pointer to TABLE object
@param type_arg Hint type
@param optimizer_switch Optimizer switch flag
@return table hint value if hint is specified,
otherwise optimizer switch value.
*/
bool hint_table_state(const THD *thd, const TABLE *table,
opt_hints_enum type_arg,
uint optimizer_switch);
/**
Returns table hint value if hint is specified, returns
fallback value if hint is not specified.
@param thd Pointer to THD object
@param tab Pointer to TABLE object
@param type_arg Hint type
@param fallback_value Value to be returned if the hint is not set
@return table hint value if hint is specified,
otherwise fallback value.
*/
bool hint_table_state_or_fallback(const THD *thd, const TABLE *table,
opt_hints_enum type_arg,
bool fallback_value);
#endif /* OPT_HINTS_INCLUDED */

View File

@ -22,6 +22,17 @@
#include "mysqld_error.h"
#include "sql_class.h"
extern struct st_opt_hint_info opt_hint_info[];
Parse_context::Parse_context(THD *thd, st_select_lex *select)
: thd(thd),
mem_root(thd->mem_root),
select(select)
{}
// This method is for debug purposes
bool Optimizer_hint_parser::parse_token_list(THD *thd)
{

View File

@ -23,6 +23,19 @@
#include "sql_list.h"
#include "simple_parser.h"
class st_select_lex;
/**
Environment data for the name resolution phase
*/
struct Parse_context {
THD * const thd; ///< Current thread handler
MEM_ROOT *mem_root; ///< Current MEM_ROOT
st_select_lex * select; ///< Current SELECT_LEX object
Parse_context(THD *thd, st_select_lex *select);
};
class Optimizer_hint_tokenizer: public Extended_string_tokenizer
{
@ -513,7 +526,6 @@ private:
using OR2::OR2;
};
// table_level_hint ::= table_level_hint_type ( table_level_hint_body )
class Table_level_hint: public AND4<PARSER,
Table_level_hint_type,
@ -523,6 +535,8 @@ private:
{
public:
using AND4::AND4;
bool resolve(Parse_context *pc) const;
};
@ -545,6 +559,8 @@ private:
{
public:
using AND4::AND4;
bool resolve(Parse_context *pc) const;
};
@ -557,6 +573,8 @@ private:
{
public:
using AND4::AND4;
bool resolve(Parse_context *pc) const;
};
@ -572,6 +590,7 @@ private:
{
public:
using OR3::OR3;
};
@ -592,6 +611,8 @@ public:
{
public:
using LIST::LIST;
bool resolve(Parse_context *pc);
};
/*

View File

@ -18,6 +18,7 @@
#include "sql_select.h"
#include "sql_test.h"
#include "opt_trace.h"
#include "opt_hints.h"
/*
Index Condition Pushdown Module
@ -416,7 +417,8 @@ void push_index_cond(JOIN_TAB *tab, uint keyno)
re-evaluated when WL#6061 is implemented.
*/
if ((tab->table->key_info[keyno].index_flags & HA_DO_INDEX_COND_PUSHDOWN) &&
optimizer_flag(tab->join->thd, OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN) &&
hint_key_state(tab->join->thd, tab->table, keyno, ICP_HINT_ENUM,
OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN) &&
tab->join->thd->lex->sql_command != SQLCOM_UPDATE_MULTI &&
tab->join->thd->lex->sql_command != SQLCOM_DELETE_MULTI &&
tab->type != JT_CONST && tab->type != JT_SYSTEM &&

View File

@ -116,6 +116,7 @@
#include "sql_statistics.h"
#include "uniques.h"
#include "my_json_writer.h"
#include "opt_hints.h"
#ifndef EXTRA_DEBUG
#define test_rb_tree(A,B) {}
@ -2854,6 +2855,13 @@ SQL_SELECT::test_quick_select(THD *thd,
add("cause", "not applicable");
continue;
}
if (hint_key_state(thd, head, idx, NO_RANGE_HINT_ENUM, 0))
{
trace_idx_details.
add("usable", false).
add("cause", "no_range_optimization hint");
continue;
}
if (key_info->algorithm == HA_KEY_ALG_FULLTEXT)
{
trace_idx_details.add("usable", false).add("cause", "fulltext");

View File

@ -12318,5 +12318,7 @@ ER_WARN_CONFLICTING_HINT
eng "Hint %s is ignored as conflicting/duplicated"
ER_WARN_UNKNOWN_QB_NAME
eng "Query block name %s is not found for %s hint"
ER_UNRESOLVED_HINT_NAME
eng "Unresolved name %s for %s hint"
ER_UNRESOLVED_TABLE_HINT_NAME
eng "Unresolved table name %s for %s hint"
ER_UNRESOLVED_INDEX_HINT_NAME
eng "Unresolved index name %s for %s hint"

View File

@ -65,6 +65,7 @@
#include "wsrep_thd.h"
#include "wsrep_trans_observer.h"
#endif /* WITH_WSREP */
#include "opt_hints.h"
bool
No_such_table_error_handler::handle_condition(THD *,
@ -8266,6 +8267,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
0);
SELECT_LEX *select_lex= select_insert ? thd->lex->first_select_lex() :
thd->lex->current_select;
Opt_hints_qb *qb_hints= select_lex->opt_hints_qb;
if (select_lex->first_cond_optimization)
{
leaves.empty();
@ -8317,6 +8319,12 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
my_error(ER_TOO_MANY_TABLES, MYF(0), static_cast<int>(MAX_TABLES));
DBUG_RETURN(1);
}
if (qb_hints && // QB hints initialized
!table_list->opt_hints_table) // Table hints are not adjusted yet
{
table_list->opt_hints_table=
qb_hints->adjust_table_hints(table_list->table, &table_list->alias);
}
}
if (select_insert && !is_insert_tables_num_set)
{
@ -8385,6 +8393,8 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
if (setup_natural_join_row_types(thd, from_clause, context))
DBUG_RETURN(1);
if (qb_hints)
qb_hints->check_unresolved(thd);
DBUG_RETURN(0);
}

View File

@ -38,6 +38,7 @@
#include "sql_partition.h"
#include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part
#include "event_parse_data.h"
#include "opt_hints_parser.h"
#ifdef WITH_WSREP
#include "mysql/service_wsrep.h"
#endif
@ -1338,6 +1339,7 @@ void LEX::start(THD *thd_arg)
table_count_update= 0;
needs_reprepare= false;
opt_hints_global= 0;
memset(&trg_chistics, 0, sizeof(trg_chistics));
DBUG_VOID_RETURN;
@ -3108,6 +3110,8 @@ void st_select_lex::init_query()
versioned_tables= 0;
pushdown_select= 0;
orig_names_of_item_list_elems= 0;
opt_hints_qb= 0;
parsed_optimizer_hints= 0; // OLEGS: remove if not used
}
void st_select_lex::init_select()
@ -3161,6 +3165,8 @@ void st_select_lex::init_select()
nest_flags= 0;
orig_names_of_item_list_elems= 0;
item_list_usage= MARK_COLUMNS_READ;
opt_hints_qb= 0;
parsed_optimizer_hints= 0; // OLEGS: remove if not used
}
/*
@ -4084,9 +4090,9 @@ void Query_tables_list::destroy_query_tables_list()
*/
LEX::LEX()
: explain(NULL), result(0), part_info(NULL), arena_for_set_stmt(0),
mem_root_for_set_stmt(0), json_table(NULL), analyze_stmt(0),
default_used(0),
: explain(NULL), result(0), opt_hints_global(NULL), part_info(NULL),
arena_for_set_stmt(0), mem_root_for_set_stmt(0), json_table(NULL),
analyze_stmt(0), default_used(0),
with_rownum(0), is_lex_started(0), without_validation(0), option_type(OPT_DEFAULT),
context_analysis_only(0), sphead(0), sp_mem_root_ptr(nullptr),
limit_rows_examined_cnt(ULONGLONG_MAX)

View File

@ -168,6 +168,8 @@ class With_clause;
class my_var;
class select_handler;
class Pushdown_select;
class Opt_hints_global;
class Opt_hints_qb;
#define ALLOC_ROOT_SET 1024
@ -1247,6 +1249,8 @@ public:
*/
table_map select_list_tables;
Opt_hints_qb *opt_hints_qb;
/* Set to 1 if any field in field list has ROWNUM() */
bool rownum_in_field_list;
@ -1256,8 +1260,10 @@ public:
index_clause_map current_index_hint_clause;
/* it is for correct printing SELECT options */
thr_lock_type lock_type;
thr_lock_type lock_type;
Optimizer_hint_parser::Hint_list *parsed_optimizer_hints;
/** System Versioning */
int vers_setup_conds(THD *thd, TABLE_LIST *tables);
/* push new Item_field into item_list */
@ -1554,6 +1560,10 @@ public:
bool is_unit_nest() { return (nest_flags & UNIT_NEST_FL); }
void mark_as_unit_nest() { nest_flags= UNIT_NEST_FL; }
bool is_sj_conversion_prohibited(THD *thd);
void set_optimizer_hints(Optimizer_hint_parser::Hint_list *hl)
{
parsed_optimizer_hints= hl;
}
};
typedef class st_select_lex SELECT_LEX;
@ -3188,6 +3198,9 @@ public:
LEX_USER *grant_user;
XID *xid;
THD *thd;
/* Optimizer hints */
Opt_hints_global *opt_hints_global;
/* maintain a list of used plugins for this LEX */
DYNAMIC_ARRAY plugins;
@ -3723,6 +3736,20 @@ public:
DBUG_RETURN(select_lex);
}
void resolve_optimizer_hints()
{
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)
{
Parse_context pc(thd, select_lex);
select_lex->parsed_optimizer_hints->resolve(&pc);
}
}
SELECT_LEX *current_select_or_default()
{
return current_select ? current_select : &builtin_select;

View File

@ -294,7 +294,7 @@ public:
inline list_node* first_node() { return first;}
inline void *head() { return first->info; }
inline void **head_ref() { return first != &end_of_list ? &first->info : 0; }
inline bool is_empty() { return first == &end_of_list ; }
inline bool is_empty() const { return first == &end_of_list ; }
inline list_node *last_ref() { return &end_of_list; }
template <typename T= void>
inline bool add_unique(T *info, bool (*eq)(T *a, T *b))
@ -531,9 +531,9 @@ public:
}
class Iterator;
class Const_Iterator;
using value_type= T;
using iterator= Iterator;
iterator begin() const { return iterator(first); }
iterator end() const { return iterator(); }
@ -579,6 +579,49 @@ public:
private:
list_node *node{&end_of_list};
};
class Const_Iterator
{
public:
using iterator_category= std::forward_iterator_tag;
using value_type= const T;
using difference_type= std::ptrdiff_t;
using pointer= const T *;
using reference= const T &;
Const_Iterator(const list_node *p= &end_of_list) : node{p} {}
Const_Iterator &operator++()
{
DBUG_ASSERT(node != &end_of_list);
node= node->next;
return *this;
}
Const_Iterator operator++(int)
{
Const_Iterator tmp(*this);
operator++();
return tmp;
}
const T &operator*() { return *static_cast<const T *>(node->info); }
const T *operator->() { return static_cast<const T *>(node->info); }
bool operator==(const typename List<T>::const_iterator &rhs)
{
return node == rhs.node;
}
bool operator!=(const typename List<T>::const_iterator &rhs)
{
return node != rhs.node;
}
private:
const list_node* node{&end_of_list};
};
};
@ -735,7 +778,7 @@ class base_ilist
public:
inline void empty() { first= &last; last.prev= &first; }
base_ilist() { empty(); }
inline bool is_empty() { return first == &last; }
inline bool is_empty() const { return first == &last; }
// Returns true if p is the last "real" object in the list,
// i.e. p->next points to the sentinel.
inline bool is_last(ilink *p) { return p->next == NULL || p->next == &last; }

View File

@ -68,6 +68,7 @@
#include "create_tmp_table.h"
#include "optimizer_defaults.h"
#include "derived_handler.h"
#include "opt_hints.h"
/*
A key part number that means we're using a fulltext scan.
@ -15677,12 +15678,14 @@ uint check_join_cache_usage(JOIN_TAB *tab,
!(join->allowed_join_cache_types & JOIN_CACHE_INCREMENTAL_BIT);
bool no_hashed_cache=
!(join->allowed_join_cache_types & JOIN_CACHE_HASHED_BIT);
bool no_bka_cache=
!(join->allowed_join_cache_types & JOIN_CACHE_BKA_BIT);
bool no_bnl_cache= !hint_table_state_or_fallback(join->thd,
tab->tab_list->table, BNL_HINT_ENUM, true);
bool no_bka_cache= !hint_table_state_or_fallback(join->thd,
tab->tab_list->table, BKA_HINT_ENUM,
join->allowed_join_cache_types & JOIN_CACHE_BKA_BIT);
join->return_tab= 0;
if (tab->no_forced_join_cache)
if (tab->no_forced_join_cache || (no_bnl_cache && no_bka_cache))
goto no_join_cache;
/*
@ -15806,6 +15809,8 @@ uint check_join_cache_usage(JOIN_TAB *tab,
case JT_NEXT:
case JT_ALL:
case JT_RANGE:
if (no_bnl_cache)
goto no_join_cache;
if (cache_level == 1)
prev_cache= 0;
if ((tab->cache= new (root) JOIN_CACHE_BNL(join, tab, prev_cache)))
@ -32100,6 +32105,36 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
return;
}
char buff[NAME_LEN];
String hint_str(buff, sizeof(buff), system_charset_info);
hint_str.length(0);
if (thd->lex->opt_hints_global)
{
char tmp_buff[NAME_LEN];
String hints_tmp(tmp_buff, sizeof(tmp_buff), system_charset_info);
hints_tmp.length(0);
if (select_number == 1)
{
if (opt_hints_qb)
opt_hints_qb->append_qb_hint(thd, &hints_tmp);
thd->lex->opt_hints_global->print(thd, &hints_tmp);
}
else if (opt_hints_qb)
opt_hints_qb->append_qb_hint(thd, &hints_tmp);
if (hints_tmp.length() > 0)
{
hint_str.append(STRING_WITH_LEN("/*+ "));
hint_str.append(hints_tmp);
hint_str.append(STRING_WITH_LEN("*/ "));
}
}
if (hint_str.length() > 0 && (sel_type == SELECT_CMD ||
sel_type == INSERT_CMD ||
sel_type == REPLACE_CMD))
str->append(hint_str);
/* First add options */
if (options & SELECT_STRAIGHT_JOIN)
str->append(STRING_WITH_LEN("straight_join "));
@ -32153,7 +32188,11 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type)
query_type);
}
if (sel_type == UPDATE_CMD || sel_type == DELETE_CMD)
{
str->append(get_explainable_cmd_name(sel_type));
if (hint_str.length() > 0)
str->append(hint_str);
}
if (sel_type == DELETE_CMD)
{
str->append(STRING_WITH_LEN(" from "));

View File

@ -1015,7 +1015,10 @@ public:
{
return Binary_string::append(s);
}
bool append(const char *s)
{
return append(s, (uint) strlen(s));
}
inline bool append(char chr)
{
return Binary_string::append_char(chr);

View File

@ -8981,6 +8981,7 @@ query_specification_start:
if (!(sel= lex->alloc_select(TRUE)) || lex->push_select(sel))
MYSQL_YYABORT;
sel->init_select();
sel->set_optimizer_hints($2);
sel->braces= FALSE;
}
select_options
@ -9153,6 +9154,7 @@ query_expression_body:
query_simple
{
Lex->push_select($1);
Lex->resolve_optimizer_hints();
if (!($$= Lex->create_unit($1)))
MYSQL_YYABORT;
}
@ -13593,11 +13595,13 @@ insert:
}
insert_start insert_lock_option opt_ignore opt_into insert_table
{
Lex->first_select_lex()->set_optimizer_hints($2);
Select->set_lock_for_tables($5, true, false);
}
insert_field_spec opt_insert_update opt_returning
stmt_end
insert_stmt_end
{
Lex->resolve_optimizer_hints();
Lex->mark_first_table_as_inserting();
thd->get_stmt_da()->reset_current_row_for_warning(0);
}
@ -13613,6 +13617,7 @@ replace:
}
insert_start replace_lock_option opt_into insert_table
{
Lex->first_select_lex()->set_optimizer_hints($2);
Select->set_lock_for_tables($5, true, false);
}
insert_field_spec opt_returning
@ -13633,6 +13638,14 @@ insert_start: {
;
stmt_end: {
Lex->resolve_optimizer_hints();
Lex->pop_select(); //main select
if (Lex->check_main_unit_semantics())
MYSQL_YYABORT;
}
;
insert_stmt_end: {
Lex->pop_select(); //main select
if (Lex->check_main_unit_semantics())
MYSQL_YYABORT;
@ -13894,6 +13907,7 @@ update:
if (Lex->main_select_push())
MYSQL_YYABORT;
lex->init_select();
Lex->first_select_lex()->set_optimizer_hints($2);
lex->sql_command= SQLCOM_UPDATE;
lex->duplicates= DUP_ERROR;
}
@ -13985,11 +13999,13 @@ delete:
mysql_init_delete(lex);
lex->ignore= 0;
lex->first_select_lex()->order_list.empty();
lex->first_select_lex()->set_optimizer_hints($2);
}
delete_part2
{
if (Lex->check_cte_dependencies_and_resolve_references())
MYSQL_YYABORT;
Lex->resolve_optimizer_hints();
}
;

View File

@ -84,6 +84,8 @@ class Table_function_json_table;
class Open_table_context;
class MYSQL_LOG;
struct rpl_group_info;
class Opt_hints_qb;
class Opt_hints_table;
/*
Used to identify NESTED_JOIN structures within a join (applicable only to
@ -2992,6 +2994,11 @@ struct TABLE_LIST
List<String> *partition_names;
#endif /* WITH_PARTITION_STORAGE_ENGINE */
/** Table level optimizer hints for this table. */
Opt_hints_table *opt_hints_table;
/* Hints for query block of this table. */
Opt_hints_qb *opt_hints_qb;
void calc_md5(char *buffer);
int view_check_option(THD *thd, bool ignore_failure);
bool create_field_translation(THD *thd);

View File

@ -20050,6 +20050,33 @@ static void test_bug17512527()
}
#endif
/**
Parser for optimizer hints
*/
static void test_optimizer_hints()
{
MYSQL_RES *result;
int rc;
myheader("test_optimizer_hints");
rc= mysql_query(mysql, "SELECT /*+ ");
DIE_UNLESS(rc);
rc= mysql_query(mysql, "SELECT /*+ ICP(`test");
DIE_UNLESS(rc);
rc= mysql_query(mysql, "SELECT /*+ ICP(`test*/ 1");
myquery(rc);
result= mysql_store_result(mysql);
mytest(result);
(void) my_process_result_set(result);
mysql_free_result(result);
rc= mysql_query(mysql, "SELECT /*+ ICP(`test*/`*/ 1");
DIE_UNLESS(rc);
}
/*
Check compressed protocol
@ -23297,6 +23324,7 @@ static struct my_tests_st my_tests[]= {
#ifndef _WIN32
{ "test_bug17512527", test_bug17512527},
#endif
{ "test_optimizer_hints", test_optimizer_hints},
{ "test_compressed_protocol", test_compressed_protocol },
{ "test_big_packet", test_big_packet },
{ "test_prepare_analyze", test_prepare_analyze },