diff --git a/conf/nebula-graphd.conf.default b/conf/nebula-graphd.conf.default index 4f567ee0a4d953f90f02e3567dbb524ca4d8c197..441f714cfa7d09a2f7f8fb1ae9c5c5f33059812a 100644 --- a/conf/nebula-graphd.conf.default +++ b/conf/nebula-graphd.conf.default @@ -4,7 +4,7 @@ # The file to host the process id --pid_file=pids/nebula-graphd.pid # Whether to enable optimizer ---enable_optimizer=false +--enable_optimizer=true ########## logging ########## # The directory to host logging files, which must already exists @@ -24,7 +24,7 @@ --stderrthreshold=2 ########## query ########## -# Whether to treat partial success as an error. +# Whether to treat partial success as an error. # This flag is only used for Read-only access, and Modify access always treats partial success as an error. --accept_partial_success=false diff --git a/conf/nebula-graphd.conf.production b/conf/nebula-graphd.conf.production index 0a5dddb966a082c1c3ab5bab43be224de6e31a0e..89c3f78a22b082ddd2154203da20ff5ff561715e 100644 --- a/conf/nebula-graphd.conf.production +++ b/conf/nebula-graphd.conf.production @@ -4,7 +4,7 @@ # The file to host the process id --pid_file=pids/nebula-graphd.pid # Whether to enable optimizer ---enable_optimizer=false +--enable_optimizer=true ########## logging ########## # The directory to host logging files, which must already exists @@ -24,7 +24,7 @@ --stderrthreshold=2 ########## query ########## -# Whether to treat partial success as an error. +# Whether to treat partial success as an error. # This flag is only used for Read-only access, and Modify access always treats partial success as an error. --accept_partial_success=false diff --git a/src/context/CMakeLists.txt b/src/context/CMakeLists.txt index 71e52540b24d635649dd0bdd7c89b93ea0a63277..9ec166da37f2521ab4613b0ff20b4a7a09c212a7 100644 --- a/src/context/CMakeLists.txt +++ b/src/context/CMakeLists.txt @@ -10,6 +10,7 @@ nebula_add_library( ExecutionContext.cpp Iterator.cpp Result.cpp + Symbols.cpp ) diff --git a/src/context/Symbols.cpp b/src/context/Symbols.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a6141803ef41832b36fbadd30a07e493fda20a98 --- /dev/null +++ b/src/context/Symbols.cpp @@ -0,0 +1,39 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#include "context/Symbols.h" + +#include <sstream> + +#include "planner/PlanNode.h" +#include "util/Utils.h" + +namespace nebula { +namespace graph { + +std::string Variable::toString() const { + std::stringstream ss; + ss << "name: " << name << ", type: " << type << ", colNames: <" << folly::join(",", colNames) + << ">, readBy: <" << util::join(readBy, [](auto pn) { return pn->toString(); }) + << ">, writtenBy: <" << util::join(writtenBy, [](auto pn) { return pn->toString(); }) << ">"; + return ss.str(); +} + +std::string SymbolTable::toString() const { + std::stringstream ss; + ss << "SymTable: ["; + for (const auto& p : vars_) { + ss << "\n" << p.first << ": "; + if (p.second) { + ss << p.second->toString(); + } + } + ss << "\n]"; + return ss.str(); +} + +} // namespace graph +} // namespace nebula diff --git a/src/context/Symbols.h b/src/context/Symbols.h index 43e78cb797a439c22bc155c3c5db7c50118525d7..4cd6e7f5ae27055af63d69c99cb99aeda810cb3a 100644 --- a/src/context/Symbols.h +++ b/src/context/Symbols.h @@ -37,6 +37,7 @@ using ColsDef = std::vector<ColDef>; struct Variable { explicit Variable(std::string n) : name(std::move(n)) {} + std::string toString() const; std::string name; Value::Type type{Value::Type::DATASET}; @@ -118,6 +119,8 @@ public: } } + std::string toString() const; + private: ObjectPool* objPool_{nullptr}; // var name -> variable diff --git a/src/optimizer/CMakeLists.txt b/src/optimizer/CMakeLists.txt index f175551ec5aedbde06788a0b9d062810d2ee2046..e9aa7cde26b96aab1712a94d94fbeb1c74f28713 100644 --- a/src/optimizer/CMakeLists.txt +++ b/src/optimizer/CMakeLists.txt @@ -10,6 +10,7 @@ nebula_add_library( Optimizer.cpp OptGroup.cpp OptRule.cpp + OptContext.cpp rule/PushFilterDownGetNbrsRule.cpp rule/IndexScanRule.cpp rule/LimitPushDownRule.cpp diff --git a/src/optimizer/OptContext.cpp b/src/optimizer/OptContext.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1670a7df206e7848f2e049a68104af382bfdc22d --- /dev/null +++ b/src/optimizer/OptContext.cpp @@ -0,0 +1,28 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#include "optimizer/OptContext.h" + +#include "common/base/Logging.h" +#include "common/base/ObjectPool.h" + +namespace nebula { +namespace opt { + +OptContext::OptContext(graph::QueryContext *qctx) + : qctx_(DCHECK_NOTNULL(qctx)), objPool_(std::make_unique<ObjectPool>()) {} + +void OptContext::addPlanNodeAndOptGroupNode(int64_t planNodeId, const OptGroupNode *optGroupNode) { + planNodeToOptGroupNodeMap_.emplace(planNodeId, optGroupNode); +} + +const OptGroupNode *OptContext::findOptGroupNodeByPlanNodeId(int64_t planNodeId) const { + auto found = planNodeToOptGroupNodeMap_.find(planNodeId); + return found == planNodeToOptGroupNodeMap_.end() ? nullptr : found->second; +} + +} // namespace opt +} // namespace nebula diff --git a/src/optimizer/OptContext.h b/src/optimizer/OptContext.h new file mode 100644 index 0000000000000000000000000000000000000000..073f1e3ac1c3096e9a936ced8a9f4b234a739be0 --- /dev/null +++ b/src/optimizer/OptContext.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#ifndef OPTIMIZER_OPTCONTEXT_H_ +#define OPTIMIZER_OPTCONTEXT_H_ + +#include <memory> +#include <unordered_map> + +#include "common/cpp/helpers.h" + +namespace nebula { + +class ObjectPool; + +namespace graph { +class QueryContext; +} // namespace graph + +namespace opt { + +class OptGroupNode; + +class OptContext final : private cpp::NonCopyable, private cpp::NonMovable { +public: + explicit OptContext(graph::QueryContext *qctx); + + graph::QueryContext *qctx() const { + return qctx_; + } + + ObjectPool *objPool() const { + return objPool_.get(); + } + + void addPlanNodeAndOptGroupNode(int64_t planNodeId, const OptGroupNode *optGroupNode); + const OptGroupNode *findOptGroupNodeByPlanNodeId(int64_t planNodeId) const; + +private: + graph::QueryContext *qctx_{nullptr}; + std::unique_ptr<ObjectPool> objPool_; + std::unordered_map<int64_t, const OptGroupNode *> planNodeToOptGroupNodeMap_; +}; + +} // namespace opt +} // namespace nebula + +#endif // OPTIMIZER_OPTCONTEXT_H_ diff --git a/src/optimizer/OptGroup.cpp b/src/optimizer/OptGroup.cpp index d8c4f6e7503781f6955babdae68b97b9cfc5a806..92364be5dc3cd1ff904b7c9c1b13b952c59231f6 100644 --- a/src/optimizer/OptGroup.cpp +++ b/src/optimizer/OptGroup.cpp @@ -9,6 +9,7 @@ #include <limits> #include "context/QueryContext.h" +#include "optimizer/OptContext.h" #include "optimizer/OptRule.h" #include "planner/Logic.h" #include "planner/PlanNode.h" @@ -23,12 +24,12 @@ using nebula::graph::SingleDependencyNode; namespace nebula { namespace opt { -OptGroup *OptGroup::create(QueryContext *qctx) { - return qctx->objPool()->add(new OptGroup(qctx)); +OptGroup *OptGroup::create(OptContext *ctx) { + return ctx->objPool()->add(new OptGroup(ctx)); } -OptGroup::OptGroup(QueryContext *qctx) noexcept : qctx_(qctx) { - DCHECK(qctx != nullptr); +OptGroup::OptGroup(OptContext *ctx) noexcept : ctx_(ctx) { + DCHECK(ctx != nullptr); } void OptGroup::addGroupNode(OptGroupNode *groupNode) { @@ -37,8 +38,8 @@ void OptGroup::addGroupNode(OptGroupNode *groupNode) { groupNodes_.emplace_back(groupNode); } -OptGroupNode *OptGroup::makeGroupNode(QueryContext *qctx, PlanNode *node) { - groupNodes_.emplace_back(OptGroupNode::create(qctx, node, this)); +OptGroupNode *OptGroup::makeGroupNode(PlanNode *node) { + groupNodes_.emplace_back(OptGroupNode::create(ctx_, node, this)); return groupNodes_.back(); } @@ -59,13 +60,13 @@ Status OptGroup::explore(const OptRule *rule) { NG_RETURN_IF_ERROR(groupNode->explore(rule)); // Find more equivalents - auto status = rule->match(groupNode); + auto status = rule->match(ctx_, groupNode); if (!status.ok()) { ++iter; continue; } auto matched = std::move(status).value(); - auto resStatus = rule->transform(qctx_, matched); + auto resStatus = rule->transform(ctx_, matched); NG_RETURN_IF_ERROR(resStatus); auto result = std::move(resStatus).value(); if (result.eraseAll) { @@ -130,8 +131,10 @@ const PlanNode *OptGroup::getPlan() const { return minGroupNode->getPlan(); } -OptGroupNode *OptGroupNode::create(QueryContext *qctx, PlanNode *node, const OptGroup *group) { - return qctx->objPool()->add(new OptGroupNode(node, group)); +OptGroupNode *OptGroupNode::create(OptContext *ctx, PlanNode *node, const OptGroup *group) { + auto optGNode = ctx->objPool()->add(new OptGroupNode(node, group)); + ctx->addPlanNodeAndOptGroupNode(node->id(), optGNode); + return optGNode; } OptGroupNode::OptGroupNode(PlanNode *node, const OptGroup *group) noexcept diff --git a/src/optimizer/OptGroup.h b/src/optimizer/OptGroup.h index e7a15089a0f9d30b63e40f3d6f3d49de7e24d0a1..de47cad15c529b338d01d43bbb5ea4c12dfc90de 100644 --- a/src/optimizer/OptGroup.h +++ b/src/optimizer/OptGroup.h @@ -10,22 +10,23 @@ #include <algorithm> #include <list> #include <vector> + #include "common/base/Status.h" namespace nebula { namespace graph { class PlanNode; -class QueryContext; } // namespace graph namespace opt { +class OptContext; class OptGroupNode; class OptRule; class OptGroup final { public: - static OptGroup *create(graph::QueryContext *qctx); + static OptGroup *create(OptContext *ctx); bool isExplored(const OptRule *rule) const { return std::find(exploredRules_.cbegin(), exploredRules_.cend(), rule) != @@ -44,7 +45,7 @@ public: } void addGroupNode(OptGroupNode *groupNode); - OptGroupNode *makeGroupNode(graph::QueryContext *qctx, graph::PlanNode *node); + OptGroupNode *makeGroupNode(graph::PlanNode *node); const std::list<OptGroupNode *> &groupNodes() const { return groupNodes_; } @@ -55,22 +56,20 @@ public: const graph::PlanNode *getPlan() const; private: - explicit OptGroup(graph::QueryContext *qctx) noexcept; + explicit OptGroup(OptContext *ctx) noexcept; static constexpr int16_t kMaxExplorationRound = 128; std::pair<double, const OptGroupNode *> findMinCostGroupNode() const; - graph::QueryContext *qctx_{nullptr}; + OptContext *ctx_{nullptr}; std::list<OptGroupNode *> groupNodes_; std::vector<const OptRule *> exploredRules_; }; class OptGroupNode final { public: - static OptGroupNode *create(graph::QueryContext *qctx, - graph::PlanNode *node, - const OptGroup *group); + static OptGroupNode *create(OptContext *ctx, graph::PlanNode *node, const OptGroup *group); void dependsOn(OptGroup *dep) { dependencies_.emplace_back(dep); diff --git a/src/optimizer/OptRule.cpp b/src/optimizer/OptRule.cpp index fed3f0e67387c399cdd5024179cff5b3d8aef4da..30d9e834fbd9da7a734768da79391b37786b153d 100644 --- a/src/optimizer/OptRule.cpp +++ b/src/optimizer/OptRule.cpp @@ -7,7 +7,10 @@ #include "optimizer/OptRule.h" #include "common/base/Logging.h" +#include "context/Symbols.h" +#include "optimizer/OptContext.h" #include "optimizer/OptGroup.h" +#include "planner/PlanNode.h" namespace nebula { namespace opt { @@ -57,21 +60,58 @@ StatusOr<MatchedResult> Pattern::match(const OptGroup *group) const { return Status::Error(); } -StatusOr<MatchedResult> OptRule::match(const OptGroupNode *groupNode) const { +StatusOr<MatchedResult> OptRule::match(OptContext *ctx, const OptGroupNode *groupNode) const { const auto &pattern = this->pattern(); auto status = pattern.match(groupNode); NG_RETURN_IF_ERROR(status); auto matched = std::move(status).value(); - if (!this->match(matched)) { + if (!this->match(ctx, matched)) { return Status::Error(); } return matched; } -bool OptRule::match(const MatchedResult &matched) const { - UNUSED(matched); - // Return true if subclass doesn't override this interface, - // so optimizer will only check whether pattern is matched +bool OptRule::match(OptContext *ctx, const MatchedResult &matched) const { + return checkDataflowDeps(ctx, matched, matched.node->node()->outputVar(), true); +} + +bool OptRule::checkDataflowDeps(OptContext *ctx, + const MatchedResult &matched, + const std::string &var, + bool isRoot) const { + auto node = matched.node; + auto planNode = node->node(); + const auto &outVarName = planNode->outputVar(); + if (outVarName != var) { + return false; + } + auto symTbl = ctx->qctx()->symTable(); + auto outVar = symTbl->getVar(outVarName); + // Check whether the data flow is same as the control flow in execution plan. + if (!isRoot) { + for (auto pnode : outVar->readBy) { + auto optGNode = ctx->findOptGroupNodeByPlanNodeId(pnode->id()); + if (!optGNode) continue; + const auto &deps = optGNode->dependencies(); + if (deps.empty()) continue; + auto found = std::find(deps.begin(), deps.end(), node->group()); + if (found == deps.end()) { + VLOG(2) << ctx->qctx()->symTable()->toString(); + return false; + } + } + } + + const auto &deps = matched.dependencies; + if (deps.empty()) { + return true; + } + DCHECK_EQ(deps.size(), node->dependencies().size()); + for (size_t i = 0; i < deps.size(); ++i) { + if (!checkDataflowDeps(ctx, deps[i], planNode->inputVar(i), false)) { + return false; + } + } return true; } @@ -81,7 +121,7 @@ RuleSet &RuleSet::DefaultRules() { } RuleSet &RuleSet::QueryRules() { - static RuleSet kQueryRules("QueryRules"); + static RuleSet kQueryRules("QueryRuleSet"); return kQueryRules; } diff --git a/src/optimizer/OptRule.h b/src/optimizer/OptRule.h index 9041afead0b72f1a53e5f52affc71137d6768ae4..a9c01a23950c6914179a3edf688e0bb5b40fb0b6 100644 --- a/src/optimizer/OptRule.h +++ b/src/optimizer/OptRule.h @@ -16,12 +16,14 @@ #include "planner/PlanNode.h" namespace nebula { + namespace graph { class QueryContext; } // namespace graph namespace opt { +class OptContext; class OptGroupNode; class OptGroup; @@ -57,18 +59,25 @@ public: std::vector<OptGroupNode *> newGroupNodes; }; - StatusOr<MatchedResult> match(const OptGroupNode *groupNode) const; + StatusOr<MatchedResult> match(OptContext *ctx, const OptGroupNode *groupNode) const; virtual ~OptRule() = default; virtual const Pattern &pattern() const = 0; - virtual bool match(const MatchedResult &matched) const; - virtual StatusOr<TransformResult> transform(graph::QueryContext *qctx, + virtual bool match(OptContext *ctx, const MatchedResult &matched) const; + virtual StatusOr<TransformResult> transform(OptContext *ctx, const MatchedResult &matched) const = 0; virtual std::string toString() const = 0; protected: OptRule() = default; + + // Return false if the output variable of this matched plan node is not the + // input of other plan node + bool checkDataflowDeps(OptContext *ctx, + const MatchedResult &matched, + const std::string &var, + bool isRoot) const; }; class RuleSet final { diff --git a/src/optimizer/Optimizer.cpp b/src/optimizer/Optimizer.cpp index 0c35446e72009c6adc0fc3e1706bcadcf55011d0..2a38db09acc045c3c06d7b8449cc084b9b1219b8 100644 --- a/src/optimizer/Optimizer.cpp +++ b/src/optimizer/Optimizer.cpp @@ -7,6 +7,7 @@ #include "optimizer/Optimizer.h" #include "context/QueryContext.h" +#include "optimizer/OptContext.h" #include "optimizer/OptGroup.h" #include "optimizer/OptRule.h" #include "planner/ExecutionPlan.h" @@ -27,9 +28,10 @@ Optimizer::Optimizer(std::vector<const RuleSet *> ruleSets) : ruleSets_(std::mov StatusOr<const PlanNode *> Optimizer::findBestPlan(QueryContext *qctx) { DCHECK(qctx != nullptr); + auto optCtx = std::make_unique<OptContext>(qctx); auto root = qctx->plan()->root(); - auto status = prepare(qctx, root); + auto status = prepare(optCtx.get(), root); NG_RETURN_IF_ERROR(status); auto rootGroup = std::move(status).value(); @@ -37,9 +39,9 @@ StatusOr<const PlanNode *> Optimizer::findBestPlan(QueryContext *qctx) { return rootGroup->getPlan(); } -StatusOr<OptGroup *> Optimizer::prepare(QueryContext *qctx, PlanNode *root) { +StatusOr<OptGroup *> Optimizer::prepare(OptContext *ctx, PlanNode *root) { std::unordered_map<int64_t, OptGroup *> visited; - return convertToGroup(qctx, root, &visited); + return convertToGroup(ctx, root, &visited); } Status Optimizer::doExploration(OptGroup *rootGroup) { @@ -51,7 +53,7 @@ Status Optimizer::doExploration(OptGroup *rootGroup) { return Status::OK(); } -OptGroup *Optimizer::convertToGroup(QueryContext *qctx, +OptGroup *Optimizer::convertToGroup(OptContext *ctx, PlanNode *node, std::unordered_map<int64_t, OptGroup *> *visited) { auto iter = visited->find(node->id()); @@ -59,8 +61,8 @@ OptGroup *Optimizer::convertToGroup(QueryContext *qctx, return iter->second; } - auto group = OptGroup::create(qctx); - auto groupNode = group->makeGroupNode(qctx, node); + auto group = OptGroup::create(ctx); + auto groupNode = group->makeGroupNode(node); switch (node->dependencies().size()) { case 0: { @@ -70,29 +72,29 @@ OptGroup *Optimizer::convertToGroup(QueryContext *qctx, case 1: { if (node->kind() == PlanNode::Kind::kSelect) { auto select = static_cast<Select *>(node); - auto then = convertToGroup(qctx, const_cast<PlanNode *>(select->then()), visited); + auto then = convertToGroup(ctx, const_cast<PlanNode *>(select->then()), visited); groupNode->addBody(then); auto otherNode = const_cast<PlanNode *>(select->otherwise()); - auto otherwise = convertToGroup(qctx, otherNode, visited); + auto otherwise = convertToGroup(ctx, otherNode, visited); groupNode->addBody(otherwise); } else if (node->kind() == PlanNode::Kind::kLoop) { auto loop = static_cast<Loop *>(node); - auto body = convertToGroup(qctx, const_cast<PlanNode *>(loop->body()), visited); + auto body = convertToGroup(ctx, const_cast<PlanNode *>(loop->body()), visited); groupNode->addBody(body); } auto dep = static_cast<SingleDependencyNode *>(node)->dep(); DCHECK(dep != nullptr); - auto depGroup = convertToGroup(qctx, const_cast<graph::PlanNode *>(dep), visited); + auto depGroup = convertToGroup(ctx, const_cast<graph::PlanNode *>(dep), visited); groupNode->dependsOn(depGroup); break; } case 2: { auto bNode = static_cast<BiInputNode *>(node); auto leftNode = const_cast<graph::PlanNode *>(bNode->left()); - auto leftGroup = convertToGroup(qctx, leftNode, visited); + auto leftGroup = convertToGroup(ctx, leftNode, visited); groupNode->dependsOn(leftGroup); auto rightNode = const_cast<graph::PlanNode *>(bNode->right()); - auto rightGroup = convertToGroup(qctx, rightNode, visited); + auto rightGroup = convertToGroup(ctx, rightNode, visited); groupNode->dependsOn(rightGroup); break; } diff --git a/src/optimizer/Optimizer.h b/src/optimizer/Optimizer.h index 176a7d86bedf80ba0d8bc75ed411c7f2e01a323f..41294917fd0d99ea4a8df96a15436cd139af3d80 100644 --- a/src/optimizer/Optimizer.h +++ b/src/optimizer/Optimizer.h @@ -19,6 +19,7 @@ class QueryContext; namespace opt { +class OptContext; class OptGroup; class OptGroupNode; class RuleSet; @@ -31,10 +32,10 @@ public: StatusOr<const graph::PlanNode *> findBestPlan(graph::QueryContext *qctx); private: - StatusOr<OptGroup *> prepare(graph::QueryContext *qctx, graph::PlanNode *root); + StatusOr<OptGroup *> prepare(OptContext *ctx, graph::PlanNode *root); Status doExploration(OptGroup *rootGroup); - OptGroup *convertToGroup(graph::QueryContext *qctx, + OptGroup *convertToGroup(OptContext *ctx, graph::PlanNode *node, std::unordered_map<int64_t, OptGroup *> *visited); diff --git a/src/optimizer/rule/IndexScanRule.cpp b/src/optimizer/rule/IndexScanRule.cpp index 07cb3d383d5868d2e6a26a68570590ab0f1cee2d..a776c9c66638189d963a1f77c4aea68e6606f857 100644 --- a/src/optimizer/rule/IndexScanRule.cpp +++ b/src/optimizer/rule/IndexScanRule.cpp @@ -6,11 +6,13 @@ #include "optimizer/rule/IndexScanRule.h" #include "common/expression/LabelAttributeExpression.h" +#include "optimizer/OptContext.h" #include "optimizer/OptGroup.h" #include "planner/PlanNode.h" #include "planner/Query.h" using nebula::graph::IndexScan; +using nebula::graph::OptimizerUtils; namespace nebula { namespace opt { @@ -27,7 +29,7 @@ const Pattern& IndexScanRule::pattern() const { return pattern; } -StatusOr<OptRule::TransformResult> IndexScanRule::transform(graph::QueryContext* qctx, +StatusOr<OptRule::TransformResult> IndexScanRule::transform(OptContext* ctx, const MatchedResult& matched) const { auto groupNode = matched.node; if (isEmptyResultSet(groupNode)) { @@ -35,6 +37,7 @@ StatusOr<OptRule::TransformResult> IndexScanRule::transform(graph::QueryContext* } auto filter = filterExpr(groupNode); + auto qctx = ctx->qctx(); IndexQueryCtx iqctx = std::make_unique<std::vector<IndexQueryContext>>(); if (filter == nullptr) { // Only filter is nullptr when lookup on tagname @@ -48,7 +51,7 @@ StatusOr<OptRule::TransformResult> IndexScanRule::transform(graph::QueryContext* auto newIN = static_cast<const IndexScan*>(groupNode->node())->clone(qctx); newIN->setIndexQueryContext(std::move(iqctx)); - auto newGroupNode = OptGroupNode::create(qctx, newIN, groupNode->group()); + auto newGroupNode = OptGroupNode::create(ctx, newIN, groupNode->group()); if (groupNode->dependencies().size() != 1) { return Status::Error("Plan node dependencies error"); } diff --git a/src/optimizer/rule/IndexScanRule.h b/src/optimizer/rule/IndexScanRule.h index 76f91776acab066508215b907d36d0d5dbf238a6..697dcf1bc545d4ea44660400cd81094f5723c5d0 100644 --- a/src/optimizer/rule/IndexScanRule.h +++ b/src/optimizer/rule/IndexScanRule.h @@ -11,14 +11,8 @@ #include "optimizer/OptimizerUtils.h" namespace nebula { -namespace graph { -class IndexScan; -} // namespace graph namespace opt { -using graph::IndexScan; -using graph::OptimizerUtils; -using graph::PlanNode; using graph::QueryContext; using storage::cpp2::IndexQueryContext; using storage::cpp2::IndexColumnHint; @@ -26,6 +20,8 @@ using BVO = graph::OptimizerUtils::BoundValueOperator; using IndexItem = std::shared_ptr<meta::cpp2::IndexItem>; using IndexQueryCtx = std::unique_ptr<std::vector<IndexQueryContext>>; +class OptContext; + class IndexScanRule final : public OptRule { FRIEND_TEST(IndexScanRuleTest, BoundValueTest); FRIEND_TEST(IndexScanRuleTest, IQCtxTest); @@ -34,7 +30,7 @@ class IndexScanRule final : public OptRule { public: const Pattern& pattern() const override; - StatusOr<TransformResult> transform(graph::QueryContext* qctx, + StatusOr<TransformResult> transform(OptContext* ctx, const MatchedResult& matched) const override; std::string toString() const override; diff --git a/src/optimizer/rule/LimitPushDownRule.cpp b/src/optimizer/rule/LimitPushDownRule.cpp index 2ed688806f09a939c3b5fb5e8d1c6d54786db167..313069fc8534c1e7a6b1f946e9ca0baa9e3e3d41 100644 --- a/src/optimizer/rule/LimitPushDownRule.cpp +++ b/src/optimizer/rule/LimitPushDownRule.cpp @@ -12,6 +12,7 @@ #include "common/expression/FunctionCallExpression.h" #include "common/expression/LogicalExpression.h" #include "common/expression/UnaryExpression.h" +#include "optimizer/OptContext.h" #include "optimizer/OptGroup.h" #include "planner/PlanNode.h" #include "planner/Query.h" @@ -42,7 +43,7 @@ const Pattern &LimitPushDownRule::pattern() const { } StatusOr<OptRule::TransformResult> LimitPushDownRule::transform( - QueryContext *qctx, + OptContext *ctx, const MatchedResult &matched) const { auto limitGroupNode = matched.node; auto projGroupNode = matched.dependencies.front().node; @@ -57,17 +58,18 @@ StatusOr<OptRule::TransformResult> LimitPushDownRule::transform( return TransformResult::noTransform(); } + auto qctx = ctx->qctx(); auto newLimit = limit->clone(qctx); - auto newLimitGroupNode = OptGroupNode::create(qctx, newLimit, limitGroupNode->group()); + auto newLimitGroupNode = OptGroupNode::create(ctx, newLimit, limitGroupNode->group()); auto newProj = proj->clone(qctx); - auto newProjGroup = OptGroup::create(qctx); - auto newProjGroupNode = newProjGroup->makeGroupNode(qctx, newProj); + auto newProjGroup = OptGroup::create(ctx); + auto newProjGroupNode = newProjGroup->makeGroupNode(newProj); auto newGn = gn->clone(qctx); newGn->setLimit(limitRows); - auto newGnGroup = OptGroup::create(qctx); - auto newGnGroupNode = newGnGroup->makeGroupNode(qctx, newGn); + auto newGnGroup = OptGroup::create(ctx); + auto newGnGroupNode = newGnGroup->makeGroupNode(newGn); newLimitGroupNode->dependsOn(newProjGroup); newProjGroupNode->dependsOn(newGnGroup); diff --git a/src/optimizer/rule/LimitPushDownRule.h b/src/optimizer/rule/LimitPushDownRule.h index 90b134b60542709f0091a64f709a5effa36aad44..8414b0b2a43a5238d08d7695e284d3c89a853030 100644 --- a/src/optimizer/rule/LimitPushDownRule.h +++ b/src/optimizer/rule/LimitPushDownRule.h @@ -12,19 +12,13 @@ #include "optimizer/OptRule.h" namespace nebula { -namespace graph { -class Limit; -class Project; -class GetNeighbors; -} // namespace graph - namespace opt { class LimitPushDownRule final : public OptRule { public: const Pattern &pattern() const override; - StatusOr<OptRule::TransformResult> transform(graph::QueryContext *qctx, + StatusOr<OptRule::TransformResult> transform(OptContext *ctx, const MatchedResult &matched) const override; std::string toString() const override; diff --git a/src/optimizer/rule/PushFilterDownGetNbrsRule.cpp b/src/optimizer/rule/PushFilterDownGetNbrsRule.cpp index 8218afe600dfaace63705b66a95802c8902ea17f..3623b365679a8bebfee53dc0303be978de9a1678 100644 --- a/src/optimizer/rule/PushFilterDownGetNbrsRule.cpp +++ b/src/optimizer/rule/PushFilterDownGetNbrsRule.cpp @@ -12,6 +12,7 @@ #include "common/expression/FunctionCallExpression.h" #include "common/expression/LogicalExpression.h" #include "common/expression/UnaryExpression.h" +#include "optimizer/OptContext.h" #include "optimizer/OptGroup.h" #include "planner/PlanNode.h" #include "planner/Query.h" @@ -39,7 +40,7 @@ const Pattern &PushFilterDownGetNbrsRule::pattern() const { } StatusOr<OptRule::TransformResult> PushFilterDownGetNbrsRule::transform( - QueryContext *qctx, + OptContext *ctx, const MatchedResult &matched) const { auto filterGroupNode = matched.node; auto gnGroupNode = matched.dependencies.front().node; @@ -53,6 +54,7 @@ StatusOr<OptRule::TransformResult> PushFilterDownGetNbrsRule::transform( return TransformResult::noTransform(); } + auto qctx = ctx->qctx(); auto pool = qctx->objPool(); auto remainedExpr = std::move(visitor).remainedExpr(); OptGroupNode *newFilterGroupNode = nullptr; @@ -60,7 +62,7 @@ StatusOr<OptRule::TransformResult> PushFilterDownGetNbrsRule::transform( auto newFilter = Filter::make(qctx, nullptr, pool->add(remainedExpr.release())); newFilter->setOutputVar(filter->outputVar()); newFilter->setInputVar(filter->inputVar()); - newFilterGroupNode = OptGroupNode::create(qctx, newFilter, filterGroupNode->group()); + newFilterGroupNode = OptGroupNode::create(ctx, newFilter, filterGroupNode->group()); } auto newGNFilter = condition->encode(); @@ -77,12 +79,12 @@ StatusOr<OptRule::TransformResult> PushFilterDownGetNbrsRule::transform( OptGroupNode *newGnGroupNode = nullptr; if (newFilterGroupNode != nullptr) { // Filter(A&&B)<-GetNeighbors(C) => Filter(A)<-GetNeighbors(B&&C) - auto newGroup = OptGroup::create(qctx); - newGnGroupNode = newGroup->makeGroupNode(qctx, newGN); + auto newGroup = OptGroup::create(ctx); + newGnGroupNode = newGroup->makeGroupNode(newGN); newFilterGroupNode->dependsOn(newGroup); } else { // Filter(A)<-GetNeighbors(C) => GetNeighbors(A&&C) - newGnGroupNode = OptGroupNode::create(qctx, newGN, filterGroupNode->group()); + newGnGroupNode = OptGroupNode::create(ctx, newGN, filterGroupNode->group()); newGN->setOutputVar(filter->outputVar()); } diff --git a/src/optimizer/rule/PushFilterDownGetNbrsRule.h b/src/optimizer/rule/PushFilterDownGetNbrsRule.h index 4cabc0677e21e50d939bd82c125fcf5f80620ac4..08384d4805facce1985473c50f3e5772f5af4567 100644 --- a/src/optimizer/rule/PushFilterDownGetNbrsRule.h +++ b/src/optimizer/rule/PushFilterDownGetNbrsRule.h @@ -12,17 +12,13 @@ #include "optimizer/OptRule.h" namespace nebula { -namespace graph { -class GetNeighbors; -} // namespace graph - namespace opt { class PushFilterDownGetNbrsRule final : public OptRule { public: const Pattern &pattern() const override; - StatusOr<TransformResult> transform(graph::QueryContext *qctx, + StatusOr<TransformResult> transform(OptContext *ctx, const MatchedResult &matched) const override; std::string toString() const override; diff --git a/src/optimizer/rule/TopNRule.cpp b/src/optimizer/rule/TopNRule.cpp index b7886b0bd958e4d28abc46477a1ac39e0f775991..e10318fffd0cff13c306efcaf335d7d32245df79 100644 --- a/src/optimizer/rule/TopNRule.cpp +++ b/src/optimizer/rule/TopNRule.cpp @@ -12,6 +12,7 @@ #include "common/expression/FunctionCallExpression.h" #include "common/expression/LogicalExpression.h" #include "common/expression/UnaryExpression.h" +#include "optimizer/OptContext.h" #include "optimizer/OptGroup.h" #include "planner/PlanNode.h" #include "planner/Query.h" @@ -38,7 +39,7 @@ const Pattern &TopNRule::pattern() const { return pattern; } -StatusOr<OptRule::TransformResult> TopNRule::transform(QueryContext *qctx, +StatusOr<OptRule::TransformResult> TopNRule::transform(OptContext *ctx, const MatchedResult &matched) const { auto limitGroupNode = matched.node; auto sortGroupNode = matched.dependencies.front().node; @@ -51,11 +52,12 @@ StatusOr<OptRule::TransformResult> TopNRule::transform(QueryContext *qctx, return TransformResult::noTransform(); } + auto qctx = ctx->qctx(); auto topn = TopN::make(qctx, nullptr, sort->factors(), limit->offset(), limit->count()); topn->setOutputVar(limit->outputVar()); topn->setInputVar(sort->inputVar()); topn->setColNames(sort->colNames()); - auto topnNode = OptGroupNode::create(qctx, topn, limitGroupNode->group()); + auto topnNode = OptGroupNode::create(ctx, topn, limitGroupNode->group()); for (auto dep : sortGroupNode->dependencies()) { topnNode->dependsOn(dep); } diff --git a/src/optimizer/rule/TopNRule.h b/src/optimizer/rule/TopNRule.h index f6114f23e7f721013a47d8943e4fc89653831aa7..ef34d0ec9c8155f7d59f7f07c1b8ebe3f3edfbb6 100644 --- a/src/optimizer/rule/TopNRule.h +++ b/src/optimizer/rule/TopNRule.h @@ -18,7 +18,7 @@ class TopNRule final : public OptRule { public: const Pattern &pattern() const override; - StatusOr<OptRule::TransformResult> transform(graph::QueryContext *qctx, + StatusOr<OptRule::TransformResult> transform(OptContext *ctx, const MatchedResult &matched) const override; std::string toString() const override; diff --git a/src/optimizer/test/IndexScanRuleTest.cpp b/src/optimizer/test/IndexScanRuleTest.cpp index b0ced7f6d3df2603a52f342eb6bd7ce73c7ac569..b025072a6b5c861d7b088796944b7d589523b892 100644 --- a/src/optimizer/test/IndexScanRuleTest.cpp +++ b/src/optimizer/test/IndexScanRuleTest.cpp @@ -8,6 +8,8 @@ #include "optimizer/OptimizerUtils.h" #include "optimizer/rule/IndexScanRule.h" +using nebula::graph::OptimizerUtils; + namespace nebula { namespace opt { diff --git a/src/planner/PlanNode.cpp b/src/planner/PlanNode.cpp index e2d45176438c5d7deb86cee4517efdf6c3b4dd98..edd18cb7696319506f424d6ad069ac6c8369b4e2 100644 --- a/src/planner/PlanNode.cpp +++ b/src/planner/PlanNode.cpp @@ -9,6 +9,7 @@ #include <memory> #include <vector> +#include <folly/String.h> #include <folly/json.h> #include "common/graph/Response.h" @@ -263,6 +264,10 @@ const char* PlanNode::toString(PlanNode::Kind kind) { LOG(FATAL) << "Impossible kind plan node " << static_cast<int>(kind); } +std::string PlanNode::toString() const { + return folly::stringPrintf("%s_%ld", toString(kind_), id_); +} + // static void PlanNode::addDescription(std::string key, std::string value, PlanNodeDescription* desc) { if (desc->description == nullptr) { @@ -275,6 +280,28 @@ void PlanNode::calcCost() { VLOG(1) << "unimplemented cost calculation."; } +void PlanNode::setOutputVar(const std::string& var) { + DCHECK_EQ(1, outputVars_.size()); + auto* outputVarPtr = qctx_->symTable()->getVar(var); + DCHECK(outputVarPtr != nullptr); + auto oldVar = outputVars_[0]->name; + outputVars_[0] = outputVarPtr; + qctx_->symTable()->updateWrittenBy(oldVar, var, this); +} + +void PlanNode::setInputVar(const std::string& varname, size_t idx) { + std::string oldVar = inputVar(idx); + auto symTable = qctx_->symTable(); + auto varPtr = symTable->getVar(varname); + DCHECK(varPtr != nullptr); + inputVars_[idx] = varPtr; + if (!oldVar.empty()) { + symTable->updateReadBy(oldVar, varname, this); + } else { + symTable->readBy(varname, this); + } +} + std::unique_ptr<PlanNodeDescription> PlanNode::explain() const { auto desc = std::make_unique<PlanNodeDescription>(); desc->id = id_; diff --git a/src/planner/PlanNode.h b/src/planner/PlanNode.h index 47217724c627fc02e058e1ce3f9b18c5ddc5b413..3645c05473a8ef14569034fdc6af7ec8af2b7039 100644 --- a/src/planner/PlanNode.h +++ b/src/planner/PlanNode.h @@ -164,14 +164,7 @@ public: return false; } - void setOutputVar(const std::string &var) { - DCHECK_EQ(1, outputVars_.size()); - auto* outputVarPtr = qctx_->symTable()->getVar(var); - DCHECK(outputVarPtr != nullptr); - auto oldVar = outputVars_[0]->name; - outputVars_[0] = outputVarPtr; - qctx_->symTable()->updateWrittenBy(oldVar, var, this); - } + void setOutputVar(const std::string &var); std::string outputVar(size_t index = 0) const { DCHECK_LT(index, outputVars_.size()); @@ -225,7 +218,19 @@ public: return dependencies_; } + std::string inputVar(size_t idx = 0UL) const { + DCHECK_LT(idx, inputVars_.size()); + return inputVars_[idx] ? inputVars_[idx]->name : ""; + } + + void setInputVar(const std::string& varname, size_t idx = 0UL); + + const std::vector<Variable*>& inputVars() const { + return inputVars_; + } + static const char* toString(Kind kind); + std::string toString() const; double cost() const { return cost_; @@ -280,31 +285,6 @@ public: return true; } - void setInputVar(std::string inputVar) { - DCHECK(!inputVars_.empty()); - auto* inputVarPtr = qctx_->symTable()->getVar(inputVar); - DCHECK(inputVarPtr != nullptr); - std::string oldVar; - if (inputVars_[0] != nullptr) { - oldVar = inputVars_[0]->name; - } - inputVars_[0] = inputVarPtr; - if (!oldVar.empty()) { - qctx_->symTable()->updateReadBy(oldVar, inputVar, this); - } else { - qctx_->symTable()->readBy(inputVar, this); - } - } - - std::string inputVar() const { - DCHECK(!inputVars_.empty()); - if (inputVars_[0] != nullptr) { - return inputVars_[0]->name; - } else { - return ""; - } - } - std::unique_ptr<PlanNodeDescription> explain() const override; protected: @@ -334,36 +314,12 @@ public: setDep(1, right); } - void setLeftVar(std::string leftVar) { - DCHECK_GE(inputVars_.size(), 1); - auto* leftVarPtr = qctx_->symTable()->getVar(leftVar); - DCHECK(leftVarPtr != nullptr); - std::string oldVar; - if (inputVars_[0] != nullptr) { - oldVar = inputVars_[0]->name; - } - inputVars_[0] = leftVarPtr; - if (!oldVar.empty()) { - qctx_->symTable()->updateReadBy(oldVar, leftVar, this); - } else { - qctx_->symTable()->readBy(leftVar, this); - } + void setLeftVar(const std::string& leftVar) { + setInputVar(leftVar, 0); } - void setRightVar(std::string rightVar) { - DCHECK_EQ(inputVars_.size(), 2); - auto* rightVarPtr = qctx_->symTable()->getVar(rightVar); - DCHECK(rightVarPtr != nullptr); - std::string oldVar; - if (inputVars_[1] != nullptr) { - oldVar = inputVars_[1]->name; - } - inputVars_[1] = rightVarPtr; - if (!oldVar.empty()) { - qctx_->symTable()->updateReadBy(oldVar, rightVar, this); - } else { - qctx_->symTable()->readBy(rightVar, this); - } + void setRightVar(const std::string& rightVar) { + setInputVar(rightVar, 1); } const PlanNode* left() const { diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 9050a007b1d08d6d57c13fe7673a26df46b8738a..0cb2e6c6640445b7af4d9460954e98990c46f12b 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -10,8 +10,6 @@ nebula_add_library( SchemaUtil.cpp IndexUtil.cpp ZoneUtil.cpp - - GroupUtil.cpp ToJson.cpp ) diff --git a/src/util/GroupUtil.cpp b/src/util/GroupUtil.cpp deleted file mode 100644 index c3f9ded9f8145f3725a62a45299d3b0d27a363cf..0000000000000000000000000000000000000000 --- a/src/util/GroupUtil.cpp +++ /dev/null @@ -1,14 +0,0 @@ -/* Copyright (c) 2020 vesoft inc. All rights reserved. - * - * This source code is licensed under Apache 2.0 License, - * attached with Common Clause Condition 1.0, found in the LICENSES directory. - */ - -#include "util/GroupUtil.h" - -namespace nebula { -namespace graph { - - -} // namespace graph -} // namespace nebula diff --git a/src/util/GroupUtil.h b/src/util/GroupUtil.h deleted file mode 100644 index 8b815ab7071d2109be6e4d58f57e52f793adf999..0000000000000000000000000000000000000000 --- a/src/util/GroupUtil.h +++ /dev/null @@ -1,24 +0,0 @@ -/* Copyright (c) 2020 vesoft inc. All rights reserved. - * - * This source code is licensed under Apache 2.0 License, - * attached with Common Clause Condition 1.0, found in the LICENSES directory. - */ - -#ifndef UTIL_GROUPUTIL_H_ -#define UTIL_GROUPUTIL_H_ - -#include "common/base/Base.h" -#include "common/base/StatusOr.h" -#include "parser/MaintainSentences.h" - -namespace nebula { -namespace graph { - -class GroupUtil final { -public: - GroupUtil() = delete; -}; - -} // namespace graph -} // namespace nebula -#endif // UTIL_GROUPUTIL_H_ diff --git a/src/util/Utils.h b/src/util/Utils.h new file mode 100644 index 0000000000000000000000000000000000000000..28ec452cbe57c3368ded04520b2769ffaeae4c7e --- /dev/null +++ b/src/util/Utils.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#ifndef UTIL_UTILS_H_ +#define UTIL_UTILS_H_ + +#include <iterator> +#include <string> +#include <vector> + +#include <folly/String.h> + +namespace nebula { +namespace util { + +template <typename Container, typename Fn> +std::string join(const Container& container, Fn fn, const std::string& delimiter = ",") { + std::vector<std::string> strs; + for (auto iter = std::begin(container), end = std::end(container); iter != end; ++iter) { + strs.emplace_back(fn(*iter)); + } + return folly::join(delimiter, strs); +} + +} // namespace util +} // namespace nebula + +#endif // UTIL_UTILS_H_ diff --git a/src/validator/test/ValidatorTestBase.cpp b/src/validator/test/ValidatorTestBase.cpp index d67d2bd1ace71325afffb38d5727d197f62a7b03..8706529db7348d21ab6b4ab758537e3b7b4fc265 100644 --- a/src/validator/test/ValidatorTestBase.cpp +++ b/src/validator/test/ValidatorTestBase.cpp @@ -16,6 +16,7 @@ #include "planner/Mutate.h" #include "planner/PlanNode.h" #include "planner/Query.h" +#include "util/Utils.h" namespace nebula { namespace graph { @@ -248,9 +249,8 @@ Status ValidatorTestBase::EqSelf(const PlanNode *l, const PlanNode *r) { } std::ostream& operator<<(std::ostream& os, const std::vector<PlanNode::Kind>& plan) { - std::vector<const char*> kinds(plan.size()); - std::transform(plan.cbegin(), plan.cend(), kinds.begin(), PlanNode::toString); - os << "[" << folly::join(", ", kinds) << "]"; + auto printPNKind = [](auto k) { return PlanNode::toString(k); }; + os << "[" << util::join(plan, printPNKind, ", ") << "]"; return os; }