Add hooks for post processing generated plans

Reviewers: mtomic, llugovic

Reviewed By: mtomic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1824
This commit is contained in:
Teon Banek 2019-01-22 15:45:59 +01:00
parent 9d36f775f2
commit abc9f0b0e7
3 changed files with 82 additions and 30 deletions

View File

@ -21,6 +21,48 @@ class SymbolTable;
namespace plan { namespace plan {
class PostProcessor final {
Parameters parameters_;
public:
using ProcessedPlan = std::unique_ptr<LogicalOperator>;
explicit PostProcessor(const Parameters &parameters)
: parameters_(parameters) {}
template <class TPlanningContext>
std::unique_ptr<LogicalOperator> Rewrite(
std::unique_ptr<LogicalOperator> plan, TPlanningContext *context) {
return plan;
}
template <class TVertexCounts>
double EstimatePlanCost(const std::unique_ptr<LogicalOperator> &plan,
TVertexCounts *vertex_counts) {
return ::query::plan::EstimatePlanCost(vertex_counts, parameters_, *plan);
}
template <class TPlanningContext>
std::unique_ptr<LogicalOperator> MergeWithCombinator(
std::unique_ptr<LogicalOperator> curr_op,
std::unique_ptr<LogicalOperator> last_op, const Tree &combinator,
TPlanningContext *context) {
if (const auto *union_ = dynamic_cast<const CypherUnion *>(&combinator)) {
return std::unique_ptr<LogicalOperator>(
impl::GenUnion(*union_, std::move(last_op), std::move(curr_op),
*context->symbol_table));
}
throw utils::NotYetImplemented("query combinator");
}
template <class TPlanningContext>
std::unique_ptr<LogicalOperator> MakeDistinct(
std::unique_ptr<LogicalOperator> last_op, TPlanningContext *context) {
auto output_symbols = last_op->OutputSymbols(*context->symbol_table);
return std::make_unique<Distinct>(std::move(last_op), output_symbols);
}
};
/// @brief Generates the LogicalOperator tree for a single query and returns the /// @brief Generates the LogicalOperator tree for a single query and returns the
/// resulting plan. /// resulting plan.
/// ///
@ -45,63 +87,73 @@ auto MakeLogicalPlanForSingleQuery(
/// Generates the LogicalOperator tree and returns the resulting plan. /// Generates the LogicalOperator tree and returns the resulting plan.
/// ///
/// @tparam TPlanningContext Type of the context used. /// @tparam TPlanningContext Type of the context used.
/// @tparam TPlanPostProcess Type of the plan post processor used.
///
/// @param context PlanningContext used for generating plans. /// @param context PlanningContext used for generating plans.
/// @param parameters Parameters used in query . /// @param post_process performs plan rewrites and cost estimation.
/// @param boolean flag use_variable_planner to choose which planner to use /// @param use_variable_planner boolean flag to choose which planner to use.
/// @return pair consisting of the plan's first logical operator @c ///
/// LogicalOperator and the estimated cost of that plan /// @return pair consisting of the final `TPlanPostProcess::ProcessedPlan` and
template <class TPlanningContext> /// the estimated cost of that plan as a `double`.
auto MakeLogicalPlan(TPlanningContext *context, const Parameters &parameters, template <class TPlanningContext, class TPlanPostProcess>
auto MakeLogicalPlan(TPlanningContext *context, TPlanPostProcess *post_process,
bool use_variable_planner) { bool use_variable_planner) {
auto query_parts = CollectQueryParts(*context->symbol_table, auto query_parts = CollectQueryParts(*context->symbol_table,
*context->ast_storage, context->query); *context->ast_storage, context->query);
auto &vertex_counts = *context->db; auto &vertex_counts = *context->db;
double total_cost = 0; double total_cost = 0;
std::unique_ptr<LogicalOperator> last_op;
using ProcessedPlan = typename TPlanPostProcess::ProcessedPlan;
ProcessedPlan last_plan;
for (const auto &query_part : query_parts.query_parts) { for (const auto &query_part : query_parts.query_parts) {
std::unique_ptr<LogicalOperator> op; std::experimental::optional<ProcessedPlan> curr_plan;
double min_cost = std::numeric_limits<double>::max(); double min_cost = std::numeric_limits<double>::max();
if (use_variable_planner) { if (use_variable_planner) {
auto plans = MakeLogicalPlanForSingleQuery<VariableStartPlanner>( auto plans = MakeLogicalPlanForSingleQuery<VariableStartPlanner>(
query_part.single_query_parts, context); query_part.single_query_parts, context);
for (auto plan : plans) { for (auto plan : plans) {
auto cost = EstimatePlanCost(&vertex_counts, parameters, *plan);
if (!op || cost < min_cost) {
// Plans are generated lazily and the current plan will disappear, so // Plans are generated lazily and the current plan will disappear, so
// it's ok to move it. // it's ok to move it.
op = std::move(plan); auto rewritten_plan = post_process->Rewrite(std::move(plan), context);
double cost =
post_process->EstimatePlanCost(rewritten_plan, &vertex_counts);
if (!curr_plan || cost < min_cost) {
curr_plan.emplace(std::move(rewritten_plan));
min_cost = cost; min_cost = cost;
} }
} }
} else { } else {
op = MakeLogicalPlanForSingleQuery<RuleBasedPlanner>( auto plan = MakeLogicalPlanForSingleQuery<RuleBasedPlanner>(
query_part.single_query_parts, context); query_part.single_query_parts, context);
min_cost = EstimatePlanCost(&vertex_counts, parameters, *op); auto rewritten_plan = post_process->Rewrite(std::move(plan), context);
min_cost = post_process->EstimatePlanCost(rewritten_plan, &vertex_counts);
curr_plan.emplace(std::move(rewritten_plan));
} }
total_cost += min_cost; total_cost += min_cost;
if (auto *union_ = if (query_part.query_combinator) {
dynamic_cast<CypherUnion *>(query_part.query_combinator)) { last_plan = post_process->MergeWithCombinator(
std::shared_ptr<LogicalOperator> curr_op(std::move(op)); std::move(*curr_plan), std::move(last_plan),
std::shared_ptr<LogicalOperator> prev_op(std::move(last_op)); *query_part.query_combinator, context);
last_op = std::unique_ptr<LogicalOperator>(
impl::GenUnion(*union_, prev_op, curr_op, *context->symbol_table));
} else if (query_part.query_combinator) {
throw utils::NotYetImplemented("query combinator");
} else { } else {
last_op = std::move(op); last_plan = std::move(*curr_plan);
} }
} }
if (query_parts.distinct) { if (query_parts.distinct) {
std::shared_ptr<LogicalOperator> prev_op(std::move(last_op)); last_plan = post_process->MakeDistinct(std::move(last_plan), context);
last_op = std::make_unique<Distinct>(
prev_op, prev_op->OutputSymbols(*context->symbol_table));
} }
return std::make_pair(std::move(last_op), total_cost); return std::make_pair(std::move(last_plan), total_cost);
}
template <class TPlanningContext>
auto MakeLogicalPlan(TPlanningContext *context, const Parameters &parameters,
bool use_variable_planner) {
PostProcessor post_processor(parameters);
return MakeLogicalPlan(context, &post_processor, use_variable_planner);
} }
} // namespace plan } // namespace plan

View File

@ -584,7 +584,7 @@ std::unique_ptr<LogicalOperator> GenWith(
} }
std::unique_ptr<LogicalOperator> GenUnion( std::unique_ptr<LogicalOperator> GenUnion(
CypherUnion &cypher_union, std::shared_ptr<LogicalOperator> left_op, const CypherUnion &cypher_union, std::shared_ptr<LogicalOperator> left_op,
std::shared_ptr<LogicalOperator> right_op, SymbolTable &symbol_table) { std::shared_ptr<LogicalOperator> right_op, SymbolTable &symbol_table) {
return std::make_unique<Union>(left_op, right_op, cypher_union.union_symbols_, return std::make_unique<Union>(left_op, right_op, cypher_union.union_symbols_,
left_op->OutputSymbols(symbol_table), left_op->OutputSymbols(symbol_table),

View File

@ -135,7 +135,7 @@ std::unique_ptr<LogicalOperator> GenWith(
std::unordered_set<Symbol> &bound_symbols, AstStorage &storage); std::unordered_set<Symbol> &bound_symbols, AstStorage &storage);
std::unique_ptr<LogicalOperator> GenUnion( std::unique_ptr<LogicalOperator> GenUnion(
CypherUnion &cypher_union, std::shared_ptr<LogicalOperator> left_op, const CypherUnion &cypher_union, std::shared_ptr<LogicalOperator> left_op,
std::shared_ptr<LogicalOperator> right_op, SymbolTable &symbol_table); std::shared_ptr<LogicalOperator> right_op, SymbolTable &symbol_table);
template <class TBoolOperator> template <class TBoolOperator>