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:
parent
6340c23933
commit
877e4a386c
@ -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}
|
||||
)
|
||||
|
@ -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;
|
||||
|
@ -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}
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
726
sql/opt_hints.cc
Normal 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
507
sql/opt_hints.h
Normal 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 */
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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 &&
|
||||
|
@ -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");
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
@ -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 "));
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 },
|
||||
|
Loading…
x
Reference in New Issue
Block a user