MDEV-33281 Optimizer hints cleanup:
- add a comment that opt_hints_global->check_unresolved() is never called - improve comments - rename everything with "resolved_children" to "fully_resolved_children" - Opt_hints_table::adjust_key_hints() now returns value - less "reach-back-to-parent" logic - rename Hint "adjustment" and "resolution" (yes, both terms were used) to "fixing". "Resolution" is already used for parse-tree objects
This commit is contained in:
parent
2c8f6058c1
commit
d2918e10fc
@ -21,8 +21,7 @@
|
|||||||
#include "opt_hints.h"
|
#include "opt_hints.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Information about hints. Sould be
|
Information about hints. Must be in sync with opt_hints_enum.
|
||||||
synchronized with opt_hints_enum enum.
|
|
||||||
|
|
||||||
Note: Hint name depends on hint state. 'NO_' prefix is added
|
Note: Hint name depends on hint state. 'NO_' prefix is added
|
||||||
if appropriate hint state bit(see Opt_hints_map::hints) is not
|
if appropriate hint state bit(see Opt_hints_map::hints) is not
|
||||||
@ -176,7 +175,12 @@ static Opt_hints_qb *get_qb_hints(Parse_context *pc)
|
|||||||
{
|
{
|
||||||
global_hints->register_child(qb);
|
global_hints->register_child(qb);
|
||||||
pc->select->opt_hints_qb= qb;
|
pc->select->opt_hints_qb= qb;
|
||||||
qb->set_resolved();
|
/*
|
||||||
|
Mark the query block as resolved as we know which SELECT_LEX it is
|
||||||
|
attached to.
|
||||||
|
Note that children (indexes, tables) are probably not resolved, yet.
|
||||||
|
*/
|
||||||
|
qb->set_fixed();
|
||||||
}
|
}
|
||||||
return qb;
|
return qb;
|
||||||
}
|
}
|
||||||
@ -272,7 +276,7 @@ void Opt_hints::print(THD *thd, String *str)
|
|||||||
{
|
{
|
||||||
for (uint i= 0; i < MAX_HINT_ENUM; i++)
|
for (uint i= 0; i < MAX_HINT_ENUM; i++)
|
||||||
{
|
{
|
||||||
if (is_specified(static_cast<opt_hints_enum>(i)) && is_resolved())
|
if (is_specified(static_cast<opt_hints_enum>(i)) && is_fixed())
|
||||||
{
|
{
|
||||||
append_hint_type(str, static_cast<opt_hints_enum>(i));
|
append_hint_type(str, static_cast<opt_hints_enum>(i));
|
||||||
str->append(STRING_WITH_LEN("("));
|
str->append(STRING_WITH_LEN("("));
|
||||||
@ -308,7 +312,7 @@ void Opt_hints::append_hint_type(String *str, opt_hints_enum type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Opt_hints::print_warn_unresolved(THD *thd)
|
void Opt_hints::print_unfixed_warnings(THD *thd)
|
||||||
{
|
{
|
||||||
String hint_name_str, hint_type_str;
|
String hint_name_str, hint_type_str;
|
||||||
append_name(thd, &hint_name_str);
|
append_name(thd, &hint_name_str);
|
||||||
@ -320,24 +324,29 @@ void Opt_hints::print_warn_unresolved(THD *thd)
|
|||||||
hint_type_str.length(0);
|
hint_type_str.length(0);
|
||||||
append_hint_type(&hint_type_str, static_cast<opt_hints_enum>(i));
|
append_hint_type(&hint_type_str, static_cast<opt_hints_enum>(i));
|
||||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||||
get_warn_unresolved_code(),
|
get_unfixed_warning_code(),
|
||||||
ER_THD(thd, get_warn_unresolved_code()),
|
ER_THD(thd, get_unfixed_warning_code()),
|
||||||
hint_name_str.c_ptr_safe(),
|
hint_name_str.c_ptr_safe(),
|
||||||
hint_type_str.c_ptr_safe());
|
hint_type_str.c_ptr_safe());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@brief
|
||||||
|
Recursively walk the descendant hints and emit warnings for any
|
||||||
|
unresolved hints
|
||||||
|
*/
|
||||||
|
|
||||||
void Opt_hints::check_unresolved(THD *thd)
|
void Opt_hints::check_unfixed(THD *thd)
|
||||||
{
|
{
|
||||||
if (!is_resolved())
|
if (!is_fixed())
|
||||||
print_warn_unresolved(thd);
|
print_unfixed_warnings(thd);
|
||||||
|
|
||||||
if (!is_all_resolved())
|
if (!are_children_fully_fixed())
|
||||||
{
|
{
|
||||||
for (uint i= 0; i < child_array.size(); i++)
|
for (uint i= 0; i < child_array.size(); i++)
|
||||||
child_array[i]->check_unresolved(thd);
|
child_array[i]->check_unfixed(thd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,7 +363,7 @@ Opt_hints_qb::Opt_hints_qb(Opt_hints *opt_hints_arg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Opt_hints_table *Opt_hints_qb::adjust_hints_for_table(TABLE *table,
|
Opt_hints_table *Opt_hints_qb::fix_hints_for_table(TABLE *table,
|
||||||
const Lex_ident_table &alias)
|
const Lex_ident_table &alias)
|
||||||
{
|
{
|
||||||
Opt_hints_table *tab= static_cast<Opt_hints_table *>(find_by_name(alias));
|
Opt_hints_table *tab= static_cast<Opt_hints_table *>(find_by_name(alias));
|
||||||
@ -364,7 +373,9 @@ Opt_hints_table *Opt_hints_qb::adjust_hints_for_table(TABLE *table,
|
|||||||
if (!tab) // Tables not found
|
if (!tab) // Tables not found
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
tab->adjust_key_hints(table);
|
if (!tab->fix_hint(table))
|
||||||
|
incr_fully_fixed_children();
|
||||||
|
|
||||||
return tab;
|
return tab;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,16 +422,18 @@ uint Opt_hints_qb::sj_enabled_strategies(uint opt_switches) const
|
|||||||
/*
|
/*
|
||||||
@brief
|
@brief
|
||||||
For each index IDX, put its hints into keyinfo_array[IDX]
|
For each index IDX, put its hints into keyinfo_array[IDX]
|
||||||
|
|
||||||
*/
|
*/
|
||||||
void Opt_hints_table::adjust_key_hints(TABLE *table)
|
|
||||||
|
bool Opt_hints_table::fix_hint(TABLE *table)
|
||||||
{
|
{
|
||||||
set_resolved();
|
/*
|
||||||
|
Ok, there's a table we attach to. Mark this hint as fixed and proceed to
|
||||||
|
fixing the child objects.
|
||||||
|
*/
|
||||||
|
set_fixed();
|
||||||
|
|
||||||
if (child_array_ptr()->size() == 0) // No key level hints
|
if (child_array_ptr()->size() == 0) // No key level hints
|
||||||
{
|
return false; // Ok, fully fixed
|
||||||
get_parent()->incr_resolved_children();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure that adjustment is called only once. */
|
/* Make sure that adjustment is called only once. */
|
||||||
DBUG_ASSERT(keyinfo_array.size() == 0);
|
DBUG_ASSERT(keyinfo_array.size() == 0);
|
||||||
@ -434,20 +447,18 @@ void Opt_hints_table::adjust_key_hints(TABLE *table)
|
|||||||
{
|
{
|
||||||
if (key_info->name.streq((*hint)->get_name()))
|
if (key_info->name.streq((*hint)->get_name()))
|
||||||
{
|
{
|
||||||
(*hint)->set_resolved();
|
(*hint)->set_fixed();
|
||||||
keyinfo_array[j]= static_cast<Opt_hints_key *>(*hint);
|
keyinfo_array[j]= static_cast<Opt_hints_key *>(*hint);
|
||||||
incr_resolved_children();
|
incr_fully_fixed_children();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (are_children_fully_fixed())
|
||||||
Do not increase number of resolved tables
|
return false;
|
||||||
if there are unresolved key objects. It's
|
|
||||||
important for check_unresolved() function.
|
return true; // Some children are not fully fixed
|
||||||
*/
|
|
||||||
if (is_all_resolved())
|
|
||||||
get_parent()->incr_resolved_children();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1105,7 +1116,12 @@ ulonglong Parser::Max_execution_time_hint::get_milliseconds() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Opt_hints_global::resolve(THD *thd)
|
/*
|
||||||
|
@brief
|
||||||
|
Fix global-level hints (and only them)
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool Opt_hints_global::fix_hint(THD *thd)
|
||||||
{
|
{
|
||||||
if (thd->lex->is_ps_or_view_context_analysis())
|
if (thd->lex->is_ps_or_view_context_analysis())
|
||||||
return false;
|
return false;
|
||||||
@ -1113,7 +1129,7 @@ bool Opt_hints_global::resolve(THD *thd)
|
|||||||
if (!max_exec_time_hint)
|
if (!max_exec_time_hint)
|
||||||
{
|
{
|
||||||
/* No possible errors */
|
/* No possible errors */
|
||||||
set_resolved();
|
set_fixed();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1136,7 +1152,7 @@ bool Opt_hints_global::resolve(THD *thd)
|
|||||||
thd->reset_query_timer();
|
thd->reset_query_timer();
|
||||||
thd->set_query_timer_force(max_exec_time_hint->get_milliseconds() * 1000);
|
thd->set_query_timer_force(max_exec_time_hint->get_milliseconds() * 1000);
|
||||||
}
|
}
|
||||||
set_resolved();
|
set_fixed();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,31 +32,39 @@
|
|||||||
This process
|
This process
|
||||||
- Creates interpreted hint structures: Opt_hints_global, Opt_hints_qb,
|
- Creates interpreted hint structures: Opt_hints_global, Opt_hints_qb,
|
||||||
Opt_hints_table, Opt_hints_key.
|
Opt_hints_table, Opt_hints_key.
|
||||||
- Interprets QB_NAME hints and assigns Query Block names.
|
- Interprets QB_NAME hints and assigns Opt_hints_qb objects their names.
|
||||||
- Table-level hints are put into their Query Block's Opt_hints_qb object.
|
- Table-level hints are put into their Query Block's Opt_hints_qb object.
|
||||||
- Index-level hints are put into their table's Opt_hints_table object.
|
- Index-level hints are put into their table's Opt_hints_table object.
|
||||||
|
|
||||||
== Hint "adjustment" ==
|
Currently, this process is done at the parser stage. (This is one of the
|
||||||
|
causes why hints do not work across VIEW bounds, here or in MySQL).
|
||||||
|
|
||||||
During Name Resolution, setup_tables() calls adjust_hints_for_table() for
|
== Hint "fixing" ==
|
||||||
each table and sets TABLE_LIST::opt_hints_table to point to its
|
|
||||||
Opt_hints_table.
|
During Name Resolution, hints are attached the real objects they control:
|
||||||
|
- table-level hints find their tables,
|
||||||
|
- index-level hints find their indexes.
|
||||||
|
|
||||||
|
This is done in setup_tables() which calls fix_hints_for_table() for
|
||||||
|
each table, as a result TABLE_LIST::opt_hints_table points to the table's
|
||||||
|
hints.
|
||||||
|
|
||||||
== Hint hierarchy ==
|
== Hint hierarchy ==
|
||||||
|
|
||||||
Hints have this hierarchy, parent to child:
|
Hints have this hierarchy, less specific to more specific:
|
||||||
|
|
||||||
Opt_hints_global
|
Opt_hints_global
|
||||||
Opt_hints_qb
|
Opt_hints_qb
|
||||||
Opt_hints_table
|
Opt_hints_table
|
||||||
Opt_hints_key
|
Opt_hints_key
|
||||||
|
|
||||||
For some hints, one needs to check the hint's base object and its parent. For
|
Some hints can be specified at a specific level (e.g. per-index) or at a
|
||||||
example, MRR can be disabled on a per-index or a per-table basis.
|
more general level (e.g. per-table). When checking the hint, we need
|
||||||
|
to check for per-index commands and then maybe per-table command.
|
||||||
|
|
||||||
== How the optimizer checks hints ==
|
== API for checking hints ==
|
||||||
|
|
||||||
The optimizer checks what hints specify using these calls:
|
The optimizer checks hints' instructions using these calls:
|
||||||
hint_table_state()
|
hint_table_state()
|
||||||
hint_table_state_or_fallback()
|
hint_table_state_or_fallback()
|
||||||
hint_key_state()
|
hint_key_state()
|
||||||
@ -212,9 +220,13 @@ private:
|
|||||||
Mem_root_array<Opt_hints*, true> child_array;
|
Mem_root_array<Opt_hints*, true> child_array;
|
||||||
|
|
||||||
/* true if hint is connected to the real object */
|
/* true if hint is connected to the real object */
|
||||||
bool resolved;
|
bool fixed;
|
||||||
/* Number of resolved children */
|
|
||||||
uint resolved_children;
|
/*
|
||||||
|
Number of child hints that are fully fixed, that is, fixed and
|
||||||
|
have all their children also fully fixed.
|
||||||
|
*/
|
||||||
|
uint n_fully_fixed_children;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -222,7 +234,7 @@ public:
|
|||||||
Opt_hints *parent_arg,
|
Opt_hints *parent_arg,
|
||||||
MEM_ROOT *mem_root_arg)
|
MEM_ROOT *mem_root_arg)
|
||||||
: name(name_arg), parent(parent_arg), child_array(mem_root_arg),
|
: name(name_arg), parent(parent_arg), child_array(mem_root_arg),
|
||||||
resolved(false), resolved_children(0)
|
fixed(false), n_fully_fixed_children(0)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
bool is_specified(opt_hints_enum type_arg) const
|
bool is_specified(opt_hints_enum type_arg) const
|
||||||
@ -274,14 +286,14 @@ public:
|
|||||||
}
|
}
|
||||||
void set_name(const Lex_ident_sys &name_arg) { name= name_arg; }
|
void set_name(const Lex_ident_sys &name_arg) { name= name_arg; }
|
||||||
Opt_hints *get_parent() const { return parent; }
|
Opt_hints *get_parent() const { return parent; }
|
||||||
void set_resolved() { resolved= true; }
|
void set_fixed() { fixed= true; }
|
||||||
bool is_resolved() const { return resolved; }
|
bool is_fixed() const { return fixed; }
|
||||||
void incr_resolved_children() { resolved_children++; }
|
void incr_fully_fixed_children() { n_fully_fixed_children++; }
|
||||||
Mem_root_array<Opt_hints*, true> *child_array_ptr() { return &child_array; }
|
Mem_root_array<Opt_hints*, true> *child_array_ptr() { return &child_array; }
|
||||||
|
|
||||||
bool is_all_resolved() const
|
bool are_children_fully_fixed() const
|
||||||
{
|
{
|
||||||
return child_array.size() == resolved_children;
|
return child_array.size() == n_fully_fixed_children;
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_child(Opt_hints* hint_arg)
|
void register_child(Opt_hints* hint_arg)
|
||||||
@ -306,12 +318,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
void print(THD *thd, String *str);
|
void print(THD *thd, String *str);
|
||||||
/**
|
/**
|
||||||
Check if there are any unresolved hint objects and
|
Check if there are any unfixed hint objects and
|
||||||
print warnings for them.
|
print warnings for them.
|
||||||
|
|
||||||
@param thd Pointer to THD object
|
@param thd Pointer to THD object
|
||||||
*/
|
*/
|
||||||
void check_unresolved(THD *thd);
|
void check_unfixed(THD *thd);
|
||||||
virtual void append_name(THD *thd, String *str)= 0;
|
virtual void append_name(THD *thd, String *str)= 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -335,18 +347,18 @@ private:
|
|||||||
*/
|
*/
|
||||||
void append_hint_type(String *str, opt_hints_enum type);
|
void append_hint_type(String *str, opt_hints_enum type);
|
||||||
/**
|
/**
|
||||||
Print warning for unresolved hint name.
|
Print warnings abount unfixed hints in this hint collection
|
||||||
|
|
||||||
@param thd Pointer to THD object
|
@param thd Pointer to THD object
|
||||||
*/
|
*/
|
||||||
void print_warn_unresolved(THD *thd);
|
void print_unfixed_warnings(THD *thd);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
Override this function in descendants so that print_warn_unresolved()
|
Override this function in descendants so that print_unfixed_warnings()
|
||||||
prints the proper warning text for table/index level unresolved hints
|
prints the proper warning text for table/index level unfixed hints
|
||||||
*/
|
*/
|
||||||
virtual uint get_warn_unresolved_code() const
|
virtual uint get_unfixed_warning_code() const
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(0);
|
DBUG_ASSERT(0);
|
||||||
return 0;
|
return 0;
|
||||||
@ -384,7 +396,7 @@ public:
|
|||||||
max_exec_time_hint, _1, _2);
|
max_exec_time_hint, _1, _2);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool resolve(THD *thd);
|
bool fix_hint(THD *thd);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -466,7 +478,7 @@ public:
|
|||||||
@return pointer Opt_hints_table object if this object is found,
|
@return pointer Opt_hints_table object if this object is found,
|
||||||
NULL otherwise.
|
NULL otherwise.
|
||||||
*/
|
*/
|
||||||
Opt_hints_table *adjust_hints_for_table(TABLE *table,
|
Opt_hints_table *fix_hints_for_table(TABLE *table,
|
||||||
const Lex_ident_table &alias);
|
const Lex_ident_table &alias);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -548,9 +560,9 @@ public:
|
|||||||
|
|
||||||
@param table Pointer to TABLE object
|
@param table Pointer to TABLE object
|
||||||
*/
|
*/
|
||||||
void adjust_key_hints(TABLE *table);
|
bool fix_hint(TABLE *table);
|
||||||
|
|
||||||
virtual uint get_warn_unresolved_code() const override
|
virtual uint get_unfixed_warning_code() const override
|
||||||
{
|
{
|
||||||
return ER_UNRESOLVED_TABLE_HINT_NAME;
|
return ER_UNRESOLVED_TABLE_HINT_NAME;
|
||||||
}
|
}
|
||||||
@ -584,7 +596,7 @@ public:
|
|||||||
append_identifier(thd, str, &name);
|
append_identifier(thd, str, &name);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual uint get_warn_unresolved_code() const override
|
virtual uint get_unfixed_warning_code() const override
|
||||||
{
|
{
|
||||||
return ER_UNRESOLVED_INDEX_HINT_NAME;
|
return ER_UNRESOLVED_INDEX_HINT_NAME;
|
||||||
}
|
}
|
||||||
|
@ -8325,7 +8325,8 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
|
|||||||
!table_list->opt_hints_table) // Table hints are not adjusted yet
|
!table_list->opt_hints_table) // Table hints are not adjusted yet
|
||||||
{
|
{
|
||||||
table_list->opt_hints_table=
|
table_list->opt_hints_table=
|
||||||
qb_hints->adjust_table_hints(table_list->table, table_list->alias);
|
qb_hints->fix_hints_for_table(table_list->table,
|
||||||
|
table_list->alias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (select_insert && !is_insert_tables_num_set)
|
if (select_insert && !is_insert_tables_num_set)
|
||||||
@ -8398,9 +8399,15 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
|
|||||||
if (resolve_opt_hints)
|
if (resolve_opt_hints)
|
||||||
{
|
{
|
||||||
if (thd->lex->opt_hints_global && select_lex->select_number == 1)
|
if (thd->lex->opt_hints_global && select_lex->select_number == 1)
|
||||||
thd->lex->opt_hints_global->resolve(thd);
|
{
|
||||||
|
thd->lex->opt_hints_global->fix_hint(thd);
|
||||||
|
/*
|
||||||
|
There's no need to call opt_hints_global->check_unresolved(),
|
||||||
|
this is done for each query block individually
|
||||||
|
*/
|
||||||
|
}
|
||||||
if (qb_hints)
|
if (qb_hints)
|
||||||
qb_hints->check_unresolved(thd);
|
qb_hints->check_unfixed(thd);
|
||||||
}
|
}
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user