Browse Source
MDEV-33281 Implement optimizer hints
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.pull/3878/head
21 changed files with 1555 additions and 64 deletions
-
1libmysqld/CMakeLists.txt
-
62mysql-test/main/opt_hints.result
-
1sql/CMakeLists.txt
-
40sql/mem_root_array.h
-
23sql/multi_range_read.cc
-
726sql/opt_hints.cc
-
507sql/opt_hints.h
-
11sql/opt_hints_parser.cc
-
23sql/opt_hints_parser.h
-
4sql/opt_index_cond_pushdown.cc
-
8sql/opt_range.cc
-
6sql/share/errmsg-utf8.txt
-
10sql/sql_base.cc
-
12sql/sql_lex.cc
-
31sql/sql_lex.h
-
49sql/sql_list.h
-
47sql/sql_select.cc
-
5sql/sql_string.h
-
18sql/sql_yacc.yy
-
7sql/table.h
-
28tests/mysql_client_test.c
@ -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; |
|||
} |
@ -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 */ |
Write
Preview
Loading…
Cancel
Save
Reference in new issue