diff --git a/sql/join_optimizer/explain_access_path.cc b/sql/join_optimizer/explain_access_path.cc index 1bb7eb5cf921..d5731e8737ae 100644 --- a/sql/join_optimizer/explain_access_path.cc +++ b/sql/join_optimizer/explain_access_path.cc @@ -22,43 +22,74 @@ #include "sql/join_optimizer/explain_access_path.h" -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include +#include #include #include +#include "lex_string.h" +#include "mem_root_deque.h" #include "my_base.h" +#include "my_dbug.h" +#include "my_sqlcommand.h" #include "mysql/strings/dtoa.h" #include "sha2.h" #include "sql-common/json_dom.h" -#include "sql/filesort.h" +#include "sql/current_thd.h" +#include "sql/field.h" +#include "sql/handler.h" +#include "sql/item.h" #include "sql/item_cmpfunc.h" +#include "sql/item_subselect.h" #include "sql/item_sum.h" -#include "sql/iterators/basic_row_iterators.h" -#include "sql/iterators/bka_iterator.h" -#include "sql/iterators/composite_iterators.h" -#include "sql/iterators/hash_join_iterator.h" -#include "sql/iterators/ref_row_iterators.h" -#include "sql/iterators/sorting_iterator.h" -#include "sql/iterators/timing_iterator.h" +#include "sql/iterators/row_iterator.h" #include "sql/join_optimizer/access_path.h" +#include "sql/join_optimizer/bit_utils.h" #include "sql/join_optimizer/cost_model.h" +#include "sql/join_optimizer/materialize_path_parameters.h" #include "sql/join_optimizer/print_utils.h" #include "sql/join_optimizer/relational_expression.h" +#include "sql/join_type.h" +#include "sql/key.h" +#include "sql/key_spec.h" +#include "sql/mem_root_array.h" +#include "sql/olap.h" #include "sql/opt_explain.h" +#include "sql/opt_explain_format.h" #include "sql/opt_explain_traditional.h" -#include "sql/query_result.h" #include "sql/range_optimizer/group_index_skip_scan_plan.h" #include "sql/range_optimizer/index_skip_scan_plan.h" #include "sql/range_optimizer/internal.h" #include "sql/range_optimizer/range_optimizer.h" +#include "sql/sql_array.h" +#include "sql/sql_class.h" +#include "sql/sql_cmd.h" +#include "sql/sql_const.h" +#include "sql/sql_executor.h" +#include "sql/sql_lex.h" +#include "sql/sql_list.h" +#include "sql/sql_opt_exec_shared.h" #include "sql/sql_optimizer.h" #include "sql/table.h" +#include "sql/temp_table_param.h" +#include "sql/window.h" +#include "sql_string.h" #include "template_utils.h" using std::string; +using std::unique_ptr; using std::vector; /// This structure encapsulates the information needed to create a Json object @@ -77,19 +108,19 @@ struct ExplainChild { // If it's convenient to assign json fields for this child while creating this // structure, then a json object can be allocated and set here. - Json_object *obj = nullptr; + unique_ptr obj{nullptr}; }; /// Convenience function to add a json field. -template -static bool AddMemberToObject(Json_object *obj, const char *alias, +template +static bool AddMemberToObject(const JsonObjectPtr &obj, const char *alias, Args &&... ctor_args) { return obj->add_alias( alias, create_dom_ptr(std::forward(ctor_args)...)); } template -static bool AddElementToArray(const std::unique_ptr &array, +static bool AddElementToArray(const unique_ptr &array, Args &&... ctor_args) { return array->append_alias( create_dom_ptr(std::forward(ctor_args)...)); @@ -97,16 +128,16 @@ static bool AddElementToArray(const std::unique_ptr &array, static bool PrintRanges(const QUICK_RANGE *const *ranges, unsigned num_ranges, const KEY_PART_INFO *key_part, bool single_part_only, - const std::unique_ptr &range_array, + const unique_ptr &range_array, string *ranges_out); -static std::unique_ptr ExplainAccessPath( +static unique_ptr ExplainAccessPath( const AccessPath *path, const AccessPath *materialized_path, JOIN *join, - bool is_root_of_join, Json_object *input_obj = nullptr); -static std::unique_ptr ExplainNoAccessPath( + bool is_root_of_join, unique_ptr root_obj); +static unique_ptr ExplainNoAccessPath( const THD::Query_plan *query_plan); -static std::unique_ptr AssignParentPath( +static unique_ptr AssignParentPath( AccessPath *parent_path, const AccessPath *materialized_path, - std::unique_ptr obj, JOIN *join); + unique_ptr obj, JOIN *join); inline static double GetJSONDouble(const Json_object *obj, const char *key) { return down_cast(obj->get(key))->value(); } @@ -135,12 +166,14 @@ static bool IsCoveringIndexScan(const KEY &key, const TABLE &table) { NULL in case of failure, we need to return something non-NULL to indicate success. */ -static bool SetIndexInfoInObject( - string *str, const char *json_index_access_type, const char *prefix, - const TABLE &table, const KEY &key, const char *index_access_type, - const string lookup_condition, const string *ranges_text, - std::unique_ptr range_arr, bool reverse, Item *pushed_idx_cond, - Json_object *obj) { +static bool SetIndexInfoInObject(string *str, + const char *json_index_access_type, + const char *prefix, const TABLE &table, + const KEY &key, const char *index_access_type, + const string lookup_condition, + const string *ranges_text, + unique_ptr range_arr, bool reverse, + Item *pushed_idx_cond, Json_object *obj) { string idx_cond_str = pushed_idx_cond ? ItemToString(pushed_idx_cond) : ""; string covering_index = string(IsCoveringIndexScan(key, table) ? "Covering index " : "Index "); @@ -270,7 +303,7 @@ static bool AddSubqueryPaths(const Item *item_arg, const char *source_text, "Select #%d (subquery in %s; run only once)", query_block->select_number, source_text); } - Json_object *child_obj = new (std::nothrow) Json_object(); + unique_ptr child_obj{new (std::nothrow) Json_object()}; if (child_obj == nullptr) return true; // Populate the subquery-specific json fields. error |= AddMemberToObject(child_obj, "subquery", true); @@ -281,7 +314,8 @@ static bool AddSubqueryPaths(const Item *item_arg, const char *source_text, if (query_block->is_cacheable()) error |= AddMemberToObject(child_obj, "cacheable", true); - children->push_back({path, description, query_block->join, child_obj}); + children->push_back( + {path, description, query_block->join, std::move(child_obj)}); } return error; @@ -313,8 +347,8 @@ static bool GetAccessPathsFromSelectList(JOIN *join, return false; } -static std::unique_ptr ExplainMaterializeAccessPath( - const AccessPath *path, JOIN *join, std::unique_ptr ret_obj, +static unique_ptr ExplainMaterializeAccessPath( + const AccessPath *path, JOIN *join, unique_ptr ret_obj, vector *children, bool explain_analyze) { Json_object *obj = ret_obj.get(); bool error = false; @@ -411,8 +445,7 @@ static std::unique_ptr ExplainMaterializeAccessPath( } // else: do not print deduplication for intersect, except if (param->invalidators != nullptr) { - std::unique_ptr cache_invalidators(new (std::nothrow) - Json_array()); + unique_ptr cache_invalidators(new (std::nothrow) Json_array()); if (cache_invalidators == nullptr) return nullptr; bool first = true; str += " (invalidate on row from "; @@ -529,13 +562,14 @@ static std::unique_ptr ExplainMaterializeAccessPath( @param join the JOIN to which 'table_path' belongs. @returns the JSON object describing table_path. */ -static std::unique_ptr AssignParentPath( +static unique_ptr AssignParentPath( AccessPath *table_path, const AccessPath *materialized_path, - std::unique_ptr materialized_obj, JOIN *join) { + unique_ptr materialized_obj, JOIN *join) { // We don't want to include the SELECT subquery list in the parent path; // Let them get printed in the actual root node. So is_root_of_join=false. - std::unique_ptr table_obj = ExplainAccessPath( - table_path, materialized_path, join, /*is_root_of_join=*/false); + unique_ptr table_obj = + ExplainAccessPath(table_path, materialized_path, join, + /*is_root_of_join=*/false, /*root_obj=*/nullptr); if (table_obj == nullptr) return nullptr; /* Get the bottommost object from the new object tree. */ @@ -548,7 +582,7 @@ static std::unique_ptr AssignParentPath( } /* Place the input object as a child of the bottom-most object */ - std::unique_ptr children(new (std::nothrow) Json_array()); + unique_ptr children(new (std::nothrow) Json_array()); if (children == nullptr || children->append_alias(std::move(materialized_obj))) return nullptr; @@ -568,7 +602,7 @@ static bool ExplainIndexSkipScanAccessPath(Json_object *obj, // Print out any equality ranges. bool first = true; - std::unique_ptr range_arr(new (std::nothrow) Json_array()); + unique_ptr range_arr(new (std::nothrow) Json_array()); if (range_arr == nullptr) return true; for (unsigned key_part_idx = 0; key_part_idx < param->eq_prefix_key_parts; ++key_part_idx) { @@ -637,7 +671,7 @@ static bool ExplainGroupIndexSkipScanAccessPath(Json_object *obj, GroupIndexSkipScanParameters *param = path->group_index_skip_scan().param; string ranges; bool error = false; - std::unique_ptr range_arr(new (std::nothrow) Json_array()); + unique_ptr range_arr(new (std::nothrow) Json_array()); if (range_arr == nullptr) return true; // Print out prefix ranges, if any. @@ -700,7 +734,7 @@ static bool AddChildrenFromPushedCondition(const TABLE &table, */ static bool PrintRanges(const QUICK_RANGE *const *ranges, unsigned num_ranges, const KEY_PART_INFO *key_part, bool single_part_only, - const std::unique_ptr &range_array, + const unique_ptr &range_array, string *ranges_out) { string range, shortened_range; for (unsigned range_idx = 0; range_idx < num_ranges; ++range_idx) { @@ -730,25 +764,25 @@ static bool PrintRanges(const QUICK_RANGE *const *ranges, unsigned num_ranges, return false; } -static bool AddChildrenToObject(Json_object *obj, - const vector &children, +static bool AddChildrenToObject(Json_object *obj, vector children, JOIN *parent_join, bool parent_is_root_of_join, string alias) { if (children.empty()) return false; - std::unique_ptr children_json(new (std::nothrow) Json_array()); + unique_ptr children_json(new (std::nothrow) Json_array()); if (children_json == nullptr) return true; - for (const ExplainChild &child : children) { + for (ExplainChild &child : children) { JOIN *subjoin = child.join != nullptr ? child.join : parent_join; bool child_is_root_of_join = subjoin != parent_join || parent_is_root_of_join; - std::unique_ptr child_obj = ExplainAccessPath( - child.path, nullptr, subjoin, child_is_root_of_join, child.obj); + unique_ptr child_obj = + ExplainAccessPath(child.path, nullptr, subjoin, child_is_root_of_join, + std::move(child.obj)); if (child_obj == nullptr) return true; if (!child.description.empty()) { - if (AddMemberToObject(child_obj.get(), "heading", + if (AddMemberToObject(child_obj, "heading", child.description)) return true; } @@ -758,16 +792,17 @@ static bool AddChildrenToObject(Json_object *obj, return obj->add_alias(alias, std::move(children_json)); } -static std::unique_ptr ExplainQueryPlan( +static unique_ptr ExplainQueryPlan( const AccessPath *path, THD::Query_plan const *query_plan, JOIN *join, bool is_root_of_join) { string dml_desc; string access_type; - std::unique_ptr obj = nullptr; + unique_ptr obj = nullptr; /* Create a Json object for the SELECT path */ if (path != nullptr) { - obj = ExplainAccessPath(path, nullptr, join, is_root_of_join); + obj = ExplainAccessPath(path, /*materialized_path=*/nullptr, join, + is_root_of_join, /*root_obj=*/nullptr); } else { obj = ExplainNoAccessPath(query_plan); } @@ -797,12 +832,12 @@ static std::unique_ptr ExplainQueryPlan( /* If there is a DML node, add it on top of the SELECT plan */ if (!dml_desc.empty()) { - std::unique_ptr dml_obj(new (std::nothrow) Json_object()); + unique_ptr dml_obj(new (std::nothrow) Json_object()); if (dml_obj == nullptr) return nullptr; - if (AddMemberToObject(dml_obj.get(), "operation", dml_desc)) + if (AddMemberToObject(dml_obj, "operation", dml_desc)) return nullptr; - std::unique_ptr children(new (std::nothrow) Json_array()); + unique_ptr children(new (std::nothrow) Json_array()); if (children == nullptr || children->append_alias(std::move(obj))) return nullptr; @@ -815,8 +850,7 @@ static std::unique_ptr ExplainQueryPlan( } if (!access_type.empty() && - AddMemberToObject(dml_obj.get(), "access_type", - access_type)) { + AddMemberToObject(dml_obj, "access_type", access_type)) { return nullptr; } obj = std::move(dml_obj); @@ -971,8 +1005,8 @@ static bool AddPathCosts(const AccessPath *path, returned JSON object represents (i.e. the next paths to be explained). @returns either ret_obj or a new JSON object with ret_obj as a descendant. */ -static std::unique_ptr SetObjectMembers( - std::unique_ptr ret_obj, const AccessPath *path, +static unique_ptr SetObjectMembers( + unique_ptr ret_obj, const AccessPath *path, const AccessPath *materialized_path, JOIN *join, vector *children) { bool error = false; @@ -1109,7 +1143,7 @@ static std::unique_ptr SetObjectMembers( const TABLE &table = *param.used_key_part[0].field->table; const KEY &key_info = table.key_info[param.index]; - std::unique_ptr range_arr(new (std::nothrow) Json_array()); + unique_ptr range_arr(new (std::nothrow) Json_array()); if (range_arr == nullptr) return nullptr; string ranges; error |= PrintRanges(param.ranges, param.num_ranges, key_info.key_part, @@ -1266,8 +1300,7 @@ static std::unique_ptr SetObjectMembers( string json_join_type; description = HashJoinTypeToString(type, &json_join_type); - std::unique_ptr hash_condition(new (std::nothrow) - Json_array()); + unique_ptr hash_condition(new (std::nothrow) Json_array()); if (hash_condition == nullptr) return nullptr; vector equijoin_conditions; @@ -1305,8 +1338,7 @@ static std::unique_ptr SetObjectMembers( equijoin_conditions, predicate->expr->join_conditions); if (extra_join_conditions == nullptr) return nullptr; - std::unique_ptr extra_condition(new (std::nothrow) - Json_array()); + unique_ptr extra_condition(new (std::nothrow) Json_array()); if (extra_condition == nullptr) return nullptr; bool first = true; for (Item *cond : *extra_join_conditions) { @@ -1365,7 +1397,7 @@ static std::unique_ptr SetObjectMembers( description += ": "; } - std::unique_ptr sort_fields(new (std::nothrow) Json_array()); + unique_ptr sort_fields(new (std::nothrow) Json_array()); if (sort_fields == nullptr) return nullptr; for (ORDER *order = path->sort().order; order != nullptr; order = order->next) { @@ -1422,7 +1454,7 @@ static std::unique_ptr SetObjectMembers( description = "Aggregate: "; } - std::unique_ptr funcs(new (std::nothrow) Json_array()); + unique_ptr funcs(new (std::nothrow) Json_array()); if (funcs == nullptr) return nullptr; bool first = true; for (Item_sum **item = join->sum_funcs; *item != nullptr; ++item) { @@ -1532,7 +1564,7 @@ static std::unique_ptr SetObjectMembers( description = "Window aggregate: "; } - std::unique_ptr funcs(new (std::nothrow) Json_array()); + unique_ptr funcs(new (std::nothrow) Json_array()); if (funcs == nullptr) return nullptr; bool first = true; for (const Item_sum &func : window->functions()) { @@ -1560,7 +1592,7 @@ static std::unique_ptr SetObjectMembers( } case AccessPath::WEEDOUT: { SJ_TMP_TABLE *sj = path->weedout().weedout_table; - std::unique_ptr tables(new (std::nothrow) Json_array()); + unique_ptr tables(new (std::nothrow) Json_array()); if (tables == nullptr) return nullptr; description = "Remove duplicate "; @@ -1588,7 +1620,7 @@ static std::unique_ptr SetObjectMembers( } case AccessPath::REMOVE_DUPLICATES: { description = "Remove duplicates from input grouped on "; - std::unique_ptr group_items(new (std::nothrow) Json_array()); + unique_ptr group_items(new (std::nothrow) Json_array()); if (group_items == nullptr) return nullptr; for (int i = 0; i < path->remove_duplicates().group_items_size; ++i) { string group_item = @@ -1730,37 +1762,37 @@ static std::unique_ptr SetObjectMembers( @param join the JOIN to which 'path' belongs. @param is_root_of_join 'true' if 'path' is the root path of a Query_expression that is not a union. - @param input_obj The JSON object describing 'path', or nullptr if a new + @param root_obj The JSON object describing 'path', or nullptr if a new object should be allocated.. @returns the root of the tree of JSON objects generated from 'path'. (In most cases a single object.) */ -static std::unique_ptr ExplainAccessPath( +static unique_ptr ExplainAccessPath( const AccessPath *path, const AccessPath *materialized_path, JOIN *join, - bool is_root_of_join, Json_object *input_obj) { + bool is_root_of_join, unique_ptr root_obj) { bool error = false; vector children; - Json_object *obj; - std::unique_ptr ret_obj(input_obj); - if (ret_obj == nullptr) { - ret_obj = create_dom_ptr(); + if (root_obj == nullptr) { + root_obj = create_dom_ptr(); + if (root_obj == nullptr) return nullptr; } + // Keep a handle to the original object. - obj = ret_obj.get(); + Json_object *original_object = root_obj.get(); // This should not happen, but some unit tests have shown to cause null child // paths to be present in the AccessPath tree. if (path == nullptr) { - if (AddMemberToObject(obj, "operation", + if (AddMemberToObject(root_obj, "operation", "")) return nullptr; - return ret_obj; + return root_obj; } - if ((ret_obj = SetObjectMembers(std::move(ret_obj), path, materialized_path, - join, &children)) == nullptr) - return nullptr; + root_obj = SetObjectMembers(std::move(root_obj), path, materialized_path, + join, &children); + if (root_obj == nullptr) return nullptr; // If we are crossing into a different query block, but there's a streaming // or materialization node in the way, don't count it as the root; we want @@ -1774,9 +1806,6 @@ static std::unique_ptr ExplainAccessPath( is_root_of_join = false; } - if (AddChildrenToObject(obj, children, join, delayed_root_of_join, "inputs")) - return nullptr; - // If we know that the join will return zero rows, we don't bother // optimizing any subqueries in the SELECT list, but end optimization // early (see Query_block::optimize()). If so, don't attempt to print @@ -1790,7 +1819,7 @@ static std::unique_ptr ExplainAccessPath( // as 'sel_child'. const auto in_children = [&children](const ExplainChild &sel_child) { return std::any_of(children.cbegin(), children.cend(), - [sel_child](const ExplainChild &child) { + [&sel_child](const ExplainChild &child) { return sel_child.path == child.path; }); }; @@ -1803,30 +1832,34 @@ static std::unique_ptr ExplainAccessPath( in_children), children_from_select.end()); - if (AddChildrenToObject(obj, children_from_select, join, - /*is_root_of_join*/ true, - "inputs_from_select_list")) + if (AddChildrenToObject( + original_object, std::move(children_from_select), join, + /*is_root_of_join*/ true, "inputs_from_select_list")) return nullptr; } + if (AddChildrenToObject(original_object, std::move(children), join, + delayed_root_of_join, "inputs")) { + return nullptr; + } + if (error == 0) - return ret_obj; + return root_obj; else return nullptr; } -std::unique_ptr ExplainNoAccessPath( - const THD::Query_plan *query_plan) { +unique_ptr ExplainNoAccessPath(const THD::Query_plan *query_plan) { bool error = false; - std::unique_ptr ret_obj = create_dom_ptr(); + unique_ptr ret_obj = create_dom_ptr(); LEX *lex = query_plan->get_lex(); switch (lex->m_sql_cmd->sql_command_code()) { case SQLCOM_INSERT: case SQLCOM_REPLACE: - error |= AddMemberToObject(ret_obj.get(), "operation", + error |= AddMemberToObject(ret_obj, "operation", "Rows fetched before execution"); - error |= AddMemberToObject(ret_obj.get(), "access_type", + error |= AddMemberToObject(ret_obj, "access_type", "rows_fetched_before_execution"); break; case SQLCOM_UPDATE: @@ -1854,7 +1887,7 @@ std::string PrintQueryPlan(THD *ethd, const THD *query_thd, join = unit->first_query_block()->join; /* Create a Json object for the plan */ - std::unique_ptr obj = + unique_ptr obj = ExplainQueryPlan(path, &query_thd->query_plan, join, is_root_of_join); if (obj == nullptr) return ""; @@ -1864,8 +1897,7 @@ std::string PrintQueryPlan(THD *ethd, const THD *query_thd, StringBuffer<1024> str; print_query_for_explain(query_thd, unit, &str); if (!str.is_empty()) { - if (AddMemberToObject(obj.get(), "query", str.ptr(), - str.length())) + if (AddMemberToObject(obj, "query", str.ptr(), str.length())) return ""; } } @@ -1891,8 +1923,9 @@ std::string PrintQueryPlan(int level, AccessPath *path, JOIN *join, } /* Create a Json object for the plan */ - std::unique_ptr json = - ExplainAccessPath(path, nullptr, join, is_root_of_join); + unique_ptr json = + ExplainAccessPath(path, /*materialized_path=*/nullptr, join, + is_root_of_join, /*root_obj=*/nullptr); if (json == nullptr) return ""; /* Output in tree format.*/ @@ -1931,8 +1964,9 @@ string GetForceSubplanToken(AccessPath *path, JOIN *join) { vector tokens_for_force_subplan; /* Create a Json object for the plan */ - std::unique_ptr json = - ExplainAccessPath(path, nullptr, join, /*is_root_of_join=*/true); + unique_ptr json = + ExplainAccessPath(path, /*materialized_path=*/nullptr, join, + /*is_root_of_join=*/true, /*root_obj=*/nullptr); if (json == nullptr) return ""; format.ExplainPrintTreeNode(json.get(), 0, &explain, diff --git a/sql/join_optimizer/explain_access_path.h b/sql/join_optimizer/explain_access_path.h index fd71ac2f36a9..15c594da8dd8 100644 --- a/sql/join_optimizer/explain_access_path.h +++ b/sql/join_optimizer/explain_access_path.h @@ -24,7 +24,6 @@ #define SQL_JOIN_OPTIMIZER_EXPLAIN_ACCESS_PATH_H #include -#include struct AccessPath; class Query_expression;