Skip to content

MDEV-36092 New-style hint: [NO_]SPLIT_MATERIALIZED #4019

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 12.0-mdev-36106-no-derived-condition-pushdown-hint
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
781 changes: 781 additions & 0 deletions mysql-test/main/opt_hints_split_materialized.result

Large diffs are not rendered by default.

525 changes: 525 additions & 0 deletions mysql-test/main/opt_hints_split_materialized.test

Large diffs are not rendered by default.

49 changes: 49 additions & 0 deletions sql/opt_hints.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct st_opt_hint_info opt_hint_info[]=
{{STRING_WITH_LEN("JOIN_FIXED_ORDER")}, false, true, false},
{{STRING_WITH_LEN("DERIVED_CONDITION_PUSHDOWN")}, false, false, false},
{{STRING_WITH_LEN("MERGE")}, false, false, false},
{{STRING_WITH_LEN("SPLIT_MATERIALIZED")}, false, false, false},
{null_clex_str, 0, 0, 0}
};

Expand Down Expand Up @@ -608,6 +609,54 @@ static bool get_hint_state(Opt_hints *hint,
}


/*
In addition to indicating the state of a hint, also indicates
if the hint is present or not. Serves to disambiguate cases
that the other version of hint_table_state cannot, such as
when a hint is forcing a behavior in the optimizer that it
would not normally do and the corresponding optimizer switch
is enabled.

@param thd Current thread connection state
@param table_list Table having the hint
@param type_arg The hint kind in question

@return appropriate value from hint_state enumeration
indicating hint enabled/disabled (if present) or
if the hint was not present.
*/

hint_state hint_table_state(const THD *thd,
const TABLE_LIST *table_list,
opt_hints_enum type_arg)
{
if (!table_list->opt_hints_qb)
return hint_state::NOT_PRESENT;

DBUG_ASSERT(!opt_hint_info[type_arg].has_arguments);

Opt_hints *hint= table_list->opt_hints_table;
Opt_hints *parent_hint= table_list->opt_hints_qb;

if (hint && hint->is_specified(type_arg))
{
const bool hint_value= hint->get_switch(type_arg);
return hint_value ? hint_state::ENABLED :
hint_state::DISABLED;
}

if (opt_hint_info[type_arg].check_upper_lvl &&
parent_hint->is_specified(type_arg))
{
const bool hint_value= parent_hint->get_switch(type_arg);
return hint_value ? hint_state::ENABLED :
hint_state::DISABLED;
}

return hint_state::NOT_PRESENT;
}


/*
@brief
Check whether a given optimization is enabled for table.keyno.
Expand Down
16 changes: 16 additions & 0 deletions sql/opt_hints.h
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,14 @@ class Opt_hints_key : public Opt_hints
};


enum class hint_state
{
NOT_PRESENT, // Hint is not specified
ENABLED, // Hint is specified as enabled
DISABLED // Hint is specified as disabled
};


/**
Returns key hint value if hint is specified, returns
optimizer switch value if hint is not specified.
Expand Down Expand Up @@ -706,6 +714,14 @@ bool hint_table_state(const THD *thd, const TABLE_LIST *table_list,
bool hint_table_state(const THD *thd, const TABLE *table,
opt_hints_enum type_arg, bool fallback_value);


/*
Similar to above but returns hint_state enum
*/
hint_state hint_table_state(const THD *thd,
const TABLE_LIST *table_list,
opt_hints_enum type_arg);

#ifndef DBUG_OFF
const char *dbug_print_hints(Opt_hints_qb *hint);
#endif
Expand Down
36 changes: 24 additions & 12 deletions sql/opt_hints_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,15 @@ Optimizer_hint_tokenizer::find_keyword(const LEX_CSTRING &str)
case 18:
if ("MAX_EXECUTION_TIME"_Lex_ident_column.streq(str))
return TokenID::keyword_MAX_EXECUTION_TIME;
if ("SPLIT_MATERIALIZED"_Lex_ident_column.streq(str))
return TokenID::keyword_SPLIT_MATERIALIZED;
break;

case 21:
if ("NO_RANGE_OPTIMIZATION"_Lex_ident_column.streq(str))
return TokenID::keyword_NO_RANGE_OPTIMIZATION;
if ("NO_SPLIT_MATERIALIZED"_Lex_ident_column.streq(str))
return TokenID::keyword_NO_SPLIT_MATERIALIZED;
break;

case 26:
Expand Down Expand Up @@ -342,21 +346,29 @@ bool Parser::Table_level_hint::resolve(Parse_context *pc) const
hint_state= false;
break;
case TokenID::keyword_DERIVED_CONDITION_PUSHDOWN:
hint_type= DERIVED_CONDITION_PUSHDOWN_HINT_ENUM;
hint_state= true;
break;
hint_type= DERIVED_CONDITION_PUSHDOWN_HINT_ENUM;
hint_state= true;
break;
case TokenID::keyword_NO_DERIVED_CONDITION_PUSHDOWN:
hint_type= DERIVED_CONDITION_PUSHDOWN_HINT_ENUM;
hint_state= false;
break;
hint_type= DERIVED_CONDITION_PUSHDOWN_HINT_ENUM;
hint_state= false;
break;
case TokenID::keyword_MERGE:
hint_type= MERGE_HINT_ENUM;
hint_state= true;
break;
hint_type= MERGE_HINT_ENUM;
hint_state= true;
break;
case TokenID::keyword_NO_MERGE:
hint_type= MERGE_HINT_ENUM;
hint_state= false;
break;
hint_type= MERGE_HINT_ENUM;
hint_state= false;
break;
case TokenID::keyword_SPLIT_MATERIALIZED:
hint_type= SPLIT_MATERIALIZED_HINT_ENUM;
hint_state= true;
break;
case TokenID::keyword_NO_SPLIT_MATERIALIZED:
hint_type= SPLIT_MATERIALIZED_HINT_ENUM;
hint_state= false;
break;
default:
DBUG_ASSERT(0);
return true;
Expand Down
9 changes: 7 additions & 2 deletions sql/opt_hints_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ enum opt_hints_enum
JOIN_FIXED_ORDER_HINT_ENUM,
DERIVED_CONDITION_PUSHDOWN_HINT_ENUM,
MERGE_HINT_ENUM,
SPLIT_MATERIALIZED_HINT_ENUM,
MAX_HINT_ENUM // This one must be the last in the list
};

Expand Down Expand Up @@ -118,7 +119,9 @@ class Optimizer_hint_tokenizer: public Extended_string_tokenizer
keyword_DERIVED_CONDITION_PUSHDOWN,
keyword_NO_DERIVED_CONDITION_PUSHDOWN,
keyword_MERGE,
keyword_NO_MERGE
keyword_NO_MERGE,
keyword_SPLIT_MATERIALIZED,
keyword_NO_SPLIT_MATERIALIZED
};

class Token: public Lex_cstring
Expand Down Expand Up @@ -373,7 +376,9 @@ class Optimizer_hint_parser: public Optimizer_hint_tokenizer,
id == TokenID::keyword_DERIVED_CONDITION_PUSHDOWN ||
id == TokenID::keyword_NO_DERIVED_CONDITION_PUSHDOWN ||
id == TokenID::keyword_MERGE ||
id == TokenID::keyword_NO_MERGE;
id == TokenID::keyword_NO_MERGE ||
id == TokenID::keyword_SPLIT_MATERIALIZED ||
id == TokenID::keyword_NO_SPLIT_MATERIALIZED;
}
};
class Table_level_hint_type: public TokenChoice<Parser,
Expand Down
72 changes: 60 additions & 12 deletions sql/opt_split.cc
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@

#include "mariadb.h"
#include "sql_select.h"
#include "opt_hints.h"
#include "opt_trace.h"
#include "optimizer_defaults.h"

Expand Down Expand Up @@ -273,7 +274,7 @@ class SplM_opt_info : public Sql_alloc
public:
/* The join for the select specifying T */
JOIN *join;
/* The map of tables from 'join' whose columns can be used for partitioning */
/* The map of tables from 'join' whose columns can be used for partitioning */
table_map tables_usable_for_splitting;
/* Info about the fields of the joined tables usable for splitting */
SplM_field_info *spl_fields;
Expand All @@ -292,15 +293,17 @@ class SplM_opt_info : public Sql_alloc
/* Cardinality of T when nothing is pushed */
double unsplit_card;
double last_refills;
/* True when SPLIT_MATERIALIZED hint present and forces this split. */
bool hint_forced_split{false};

SplM_plan_info *find_plan(TABLE *table, uint key, uint parts);
};


void TABLE::set_spl_opt_info(SplM_opt_info *spl_info)
{
if (spl_info)
spl_info->join->spl_opt_info= spl_info;
DBUG_ASSERT(spl_info);
spl_info->join->spl_opt_info= spl_info;
spl_opt_info= spl_info;
}

Expand All @@ -320,6 +323,40 @@ double TABLE::get_materialization_cost()
}


/**
Returns true if split materialization is permitted for the
derived table passed in argument 'derived'.

@param thd The connection state for the current thread.
@param derived The candidate derived table for split materialization.
@param hint [OUT] One of the values from the hint_state enumeration
found in opt_hints.h
@return true if split materialization allowed, either by hint
or by optimizer option. The hint takes precedence.
*/

static bool is_split_materialized_allowed(THD *thd,
const TABLE_LIST *derived,
hint_state *hint)
{
if (!derived)
{
*hint= hint_state::DISABLED; // there is no derived table to hint on
return false;
}

*hint= hint_table_state(thd,
derived,
SPLIT_MATERIALIZED_HINT_ENUM);

const bool opt_flag= optimizer_flag(thd, OPTIMIZER_SWITCH_SPLIT_MATERIALIZED);
const bool allow_split= (*hint == hint_state::ENABLED ||
(*hint == hint_state::NOT_PRESENT && opt_flag));

return allow_split;
}


/* This structure is auxiliary and used only in the function that follows it */
struct SplM_field_ext_info: public SplM_field_info
{
Expand All @@ -330,7 +367,8 @@ struct SplM_field_ext_info: public SplM_field_info

/**
@brief
Check whether this join is one for potentially splittable materialized table
Check whether this join is one for potentially splittable materialized
table

@details
The function checks whether this join is for select that specifies
Expand All @@ -339,7 +377,8 @@ struct SplM_field_ext_info: public SplM_field_info
of the TABLE structure for T.

The function returns a positive answer if the following holds:
1. the optimizer switch 'split_materialized' is set 'on'
1. The is_split_materialized_allowed() function indicates that split
materialization is permitted for the derived table.
2. the select owning this join specifies a materialized derived/view/cte T
3. this is the only select in the specification of T
4. condition pushdown is not prohibited into T
Expand All @@ -355,7 +394,7 @@ struct SplM_field_ext_info: public SplM_field_info
9. There are defined some keys usable for ref access of fields from C
with available statistics.
10. The select doesn't use WITH ROLLUP (This limitation can probably be
lifted)
lifted)

@retval
true if the answer is positive
Expand All @@ -367,8 +406,10 @@ bool JOIN::check_for_splittable_materialized()
ORDER *partition_list= 0;
st_select_lex_unit *unit= select_lex->master_unit();
TABLE_LIST *derived= unit->derived;
if (!(optimizer_flag(thd, OPTIMIZER_SWITCH_SPLIT_MATERIALIZED)) || // !(1)
!(derived && derived->is_materialized_derived()) || // !(2)

hint_state hint;
if (!is_split_materialized_allowed(thd, derived, &hint) || // !(1)
!derived->is_materialized_derived() || // !(2)
(unit->first_select()->next_select()) || // !(3)
(derived->prohibit_cond_pushdown) || // !(4)
(derived->is_recursive_with_table()) || // !(5)
Expand Down Expand Up @@ -519,6 +560,7 @@ bool JOIN::check_for_splittable_materialized()
spl_opt_info->tables_usable_for_splitting= 0;
spl_opt_info->spl_field_cnt= spl_field_cnt;
spl_opt_info->spl_fields= spl_field;
spl_opt_info->hint_forced_split= (hint == hint_state::ENABLED);

{
Json_writer_array trace_range(thd, "split_candidates");
Expand Down Expand Up @@ -1130,8 +1172,8 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(uint idx,
spl_plan->cost= (join->best_positions[join->table_count-1].read_time +
oper_cost);

chosen= (refills * spl_plan->cost + COST_EPS <
spl_opt_info->unsplit_cost);
chosen= ((refills * spl_plan->cost + COST_EPS <
spl_opt_info->unsplit_cost) || spl_opt_info->hint_forced_split);

if (unlikely(thd->trace_started()))
{
Expand All @@ -1149,6 +1191,9 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(uint idx,
add("refills", refills).
add("total_splitting_cost", refills * spl_plan->cost).
add("chosen", chosen);

if (spl_opt_info->hint_forced_split)
find_trace.add("forced_by_hint", true);
}
memcpy((char *) spl_plan->best_positions,
(char *) join->best_positions,
Expand All @@ -1160,8 +1205,11 @@ SplM_plan_info * JOIN_TAB::choose_best_splitting(uint idx,
else
{
trace_obj.add("cached_plan_found", 1);
chosen= (refills * spl_plan->cost + COST_EPS <
spl_opt_info->unsplit_cost);
if (spl_opt_info->hint_forced_split)
trace_obj.add("forced_by_hint", true);

chosen= ((refills * spl_plan->cost + COST_EPS <
spl_opt_info->unsplit_cost) || spl_opt_info->hint_forced_split);
already_printed= 0;
}
}
Expand Down
5 changes: 5 additions & 0 deletions sql/table.h
Original file line number Diff line number Diff line change
Expand Up @@ -2971,6 +2971,11 @@ struct TABLE_LIST
/* I_S: Flags to open_table (e.g. OPEN_TABLE_ONLY or OPEN_VIEW_ONLY) */
uint i_s_requested_object;

/*
The [NO_]SPLIT_MATERIALIZED hint will not override this because
this is in place for correctness (see Item_func_set_user_var::fix_fields
for the rationale).
*/
bool prohibit_cond_pushdown;

/*
Expand Down