From 5c211cdbc54a0a072a0a27960e01647b44eaa0f1 Mon Sep 17 00:00:00 2001 From: William Chen <13495049+CPWstatic@users.noreply.github.com> Date: Tue, 6 Jul 2021 14:33:52 +0800 Subject: [PATCH 01/12] Rename promise to notifier and future to receiver. --- .../AsyncMsgNotifyBasedScheduler.cpp | 214 +++++++++--------- src/scheduler/AsyncMsgNotifyBasedScheduler.h | 28 +-- 2 files changed, 121 insertions(+), 121 deletions(-) diff --git a/src/scheduler/AsyncMsgNotifyBasedScheduler.cpp b/src/scheduler/AsyncMsgNotifyBasedScheduler.cpp index e658079d9..46514317b 100644 --- a/src/scheduler/AsyncMsgNotifyBasedScheduler.cpp +++ b/src/scheduler/AsyncMsgNotifyBasedScheduler.cpp @@ -26,16 +26,16 @@ folly::Future AsyncMsgNotifyBasedScheduler::schedule() { } folly::Future AsyncMsgNotifyBasedScheduler::doSchedule(Executor* root) const { - std::unordered_map>> promiseMap; - std::unordered_map>> futureMap; + std::unordered_map> notifierMap; + std::unordered_map> receiverMap; std::queue queue; std::queue queue2; std::unordered_set visited; auto* runner = qctx_->rctx()->runner(); - folly::Promise promiseForRoot; - auto resultFuture = promiseForRoot.getFuture(); - promiseMap[root->id()].emplace_back(std::move(promiseForRoot)); + Notifier notifierOfRoot; + auto resultReceiver = notifierOfRoot.getFuture(); + notifierMap[root->id()].emplace_back(std::move(notifierOfRoot)); queue.push(root); visited.emplace(root); while (!queue.empty()) { @@ -43,149 +43,147 @@ folly::Future AsyncMsgNotifyBasedScheduler::doSchedule(Executor* root) c queue.pop(); queue2.push(exe); - std::vector> futures; + std::vector receivers; for (auto* dep : exe->depends()) { auto notVisited = visited.emplace(dep).second; if (notVisited) { queue.push(dep); } - folly::Promise p; - futures.emplace_back(p.getFuture()); - auto& promises = promiseMap[dep->id()]; - promises.emplace_back(std::move(p)); + Notifier p; + receivers.emplace_back(p.getFuture()); + auto& notifiers = notifierMap[dep->id()]; + notifiers.emplace_back(std::move(p)); } - futureMap.emplace(exe->id(), std::move(futures)); + receiverMap.emplace(exe->id(), std::move(receivers)); } while (!queue2.empty()) { auto* exe = queue2.front(); queue2.pop(); - auto currentFuturesFound = futureMap.find(exe->id()); - DCHECK(currentFuturesFound != futureMap.end()); - auto currentExeFutures = std::move(currentFuturesFound->second); + auto currentReceiversFound = receiverMap.find(exe->id()); + DCHECK(currentReceiversFound != receiverMap.end()); + auto currentExeReceivers = std::move(currentReceiversFound->second); - auto currentPromisesFound = promiseMap.find(exe->id()); - DCHECK(currentPromisesFound != promiseMap.end()); - auto currentExePromises = std::move(currentPromisesFound->second); + auto currentNotifiersFound = notifierMap.find(exe->id()); + DCHECK(currentNotifiersFound != notifierMap.end()); + auto currentExeNotifiers = std::move(currentNotifiersFound->second); - scheduleExecutor(std::move(currentExeFutures), exe, runner, std::move(currentExePromises)); + scheduleExecutor( + std::move(currentExeReceivers), exe, runner, std::move(currentExeNotifiers)); } - return resultFuture; + return resultReceiver; } void AsyncMsgNotifyBasedScheduler::scheduleExecutor( - std::vector>&& futures, + std::vector&& receivers, Executor* exe, folly::Executor* runner, - std::vector>&& promises) const { + std::vector&& notifiers) const { switch (exe->node()->kind()) { case PlanNode::Kind::kSelect: { auto select = static_cast(exe); - runSelect(std::move(futures), select, runner, std::move(promises)); + runSelect(std::move(receivers), select, runner, std::move(notifiers)); break; } case PlanNode::Kind::kLoop: { auto loop = static_cast(exe); - runLoop(std::move(futures), loop, runner, std::move(promises)); + runLoop(std::move(receivers), loop, runner, std::move(notifiers)); break; } default: { if (exe->depends().empty()) { - runLeafExecutor(exe, runner, std::move(promises)); + runLeafExecutor(exe, runner, std::move(notifiers)); } else { - runExecutor(std::move(futures), exe, runner, std::move(promises)); + runExecutor(std::move(receivers), exe, runner, std::move(notifiers)); } break; } } } -void AsyncMsgNotifyBasedScheduler::runSelect(std::vector>&& futures, +void AsyncMsgNotifyBasedScheduler::runSelect(std::vector&& receivers, SelectExecutor* select, folly::Executor* runner, - std::vector>&& promises) const { - folly::collect(futures).via(runner).thenTry( - [select, pros = std::move(promises), this](auto&& t) mutable { + std::vector&& notifiers) const { + folly::collect(receivers).via(runner).thenTry( + [select, notifiers = std::move(notifiers), this](auto&& t) mutable { if (t.hasException()) { - return notifyError(pros, Status::Error(t.exception().what())); + return notifyError(notifiers, Status::Error(t.exception().what())); } auto status = std::move(t).value(); auto s = checkStatus(std::move(status)); if (!s.ok()) { - return notifyError(pros, s); + return notifyError(notifiers, s); } std::move(execute(select)) - .thenTry( - [select, pros = std::move(pros), this](auto&& selectTry) mutable { - if (selectTry.hasException()) { - return notifyError(pros, Status::Error( - selectTry.exception().what())); - } - auto selectStatus = std::move(selectTry).value(); - if (!selectStatus.ok()) { - return notifyError(pros, selectStatus); - } - auto val = qctx_->ectx()->getValue(select->node()->outputVar()); - if (!val.isBool()) { - std::stringstream ss; - ss << "Loop produces a bad condition result: " << val - << " type: " << val.type(); - return notifyError(pros, Status::Error(ss.str())); - } + .thenTry([select, notifiers = std::move(notifiers), this]( + auto&& selectTry) mutable { + if (selectTry.hasException()) { + return notifyError(notifiers, Status::Error(selectTry.exception().what())); + } + auto selectStatus = std::move(selectTry).value(); + if (!selectStatus.ok()) { + return notifyError(notifiers, selectStatus); + } + auto val = qctx_->ectx()->getValue(select->node()->outputVar()); + if (!val.isBool()) { + std::stringstream ss; + ss << "Loop produces a bad condition result: " << val + << " type: " << val.type(); + return notifyError(notifiers, Status::Error(ss.str())); + } - auto selectFuture = folly::makeFuture(Status::OK()); - if (val.getBool()) { - selectFuture = doSchedule(select->thenBody()); - } else { - selectFuture = doSchedule(select->elseBody()); - } - std::move(selectFuture) - .thenTry([pros = std::move(pros), this](auto&& bodyTry) mutable { - if (bodyTry.hasException()) { - return notifyError(pros, - Status::Error( - bodyTry.exception().what())); - } - auto bodyStatus = std::move(bodyTry).value(); - if (!bodyStatus.ok()) { - return notifyError(pros, bodyStatus); - } else { - return notifyOK(pros); - } - }); - }); + auto selectFuture = folly::makeFuture(Status::OK()); + if (val.getBool()) { + selectFuture = doSchedule(select->thenBody()); + } else { + selectFuture = doSchedule(select->elseBody()); + } + std::move(selectFuture) + .thenTry([notifiers = std::move(notifiers), this](auto&& bodyTry) mutable { + if (bodyTry.hasException()) { + return notifyError(notifiers, + Status::Error(bodyTry.exception().what())); + } + auto bodyStatus = std::move(bodyTry).value(); + if (!bodyStatus.ok()) { + return notifyError(notifiers, bodyStatus); + } else { + return notifyOK(notifiers); + } + }); + }); }); } -void AsyncMsgNotifyBasedScheduler::runExecutor( - std::vector>&& futures, - Executor* exe, - folly::Executor* runner, - std::vector>&& promises) const { - folly::collect(futures).via(runner).thenTry( - [exe, pros = std::move(promises), this](auto&& t) mutable { +void AsyncMsgNotifyBasedScheduler::runExecutor(std::vector&& receivers, + Executor* exe, + folly::Executor* runner, + std::vector&& notifiers) const { + folly::collect(receivers).via(runner).thenTry( + [exe, notifiers = std::move(notifiers), this](auto&& t) mutable { if (t.hasException()) { - return notifyError(pros, Status::Error(t.exception().what())); + return notifyError(notifiers, Status::Error(t.exception().what())); } auto status = std::move(t).value(); auto depStatus = checkStatus(std::move(status)); if (!depStatus.ok()) { - return notifyError(pros, depStatus); + return notifyError(notifiers, depStatus); } // Execute in current thread. std::move(execute(exe)).thenTry( - [pros = std::move(pros), this](auto&& exeTry) mutable { + [notifiers = std::move(notifiers), this](auto&& exeTry) mutable { if (exeTry.hasException()) { - return notifyError(pros, Status::Error(exeTry.exception().what())); + return notifyError(notifiers, Status::Error(exeTry.exception().what())); } auto exeStatus = std::move(exeTry).value(); if (!exeStatus.ok()) { - return notifyError(pros, exeStatus); + return notifyError(notifiers, exeStatus); } - return notifyOK(pros); + return notifyOK(notifiers); }); }); } @@ -193,60 +191,60 @@ void AsyncMsgNotifyBasedScheduler::runExecutor( void AsyncMsgNotifyBasedScheduler::runLeafExecutor( Executor* exe, folly::Executor* runner, - std::vector>&& promises) const { + std::vector&& notifiers) const { std::move(execute(exe)) .via(runner) - .thenTry([pros = std::move(promises), this](auto&& t) mutable { + .thenTry([notifiers = std::move(notifiers), this](auto&& t) mutable { if (t.hasException()) { - return notifyError(pros, Status::Error(t.exception().what())); + return notifyError(notifiers, Status::Error(t.exception().what())); } auto s = std::move(t).value(); if (!s.ok()) { - return notifyError(pros, s); + return notifyError(notifiers, s); } - return notifyOK(pros); + return notifyOK(notifiers); }); } -void AsyncMsgNotifyBasedScheduler::runLoop(std::vector>&& futures, +void AsyncMsgNotifyBasedScheduler::runLoop(std::vector&& receivers, LoopExecutor* loop, folly::Executor* runner, - std::vector>&& promises) const { - folly::collect(futures).via(runner).thenTry( - [loop, runner, pros = std::move(promises), this](auto&& t) mutable { + std::vector&& notifiers) const { + folly::collect(receivers).via(runner).thenTry( + [loop, runner, notifiers = std::move(notifiers), this](auto&& t) mutable { if (t.hasException()) { - return notifyError(pros, Status::Error(t.exception().what())); + return notifyError(notifiers, Status::Error(t.exception().what())); } auto status = std::move(t).value(); auto s = checkStatus(std::move(status)); if (!s.ok()) { - return notifyError(pros, s); + return notifyError(notifiers, s); } std::move(execute(loop)).thenTry( - [loop, runner, pros = std::move(pros), this](auto&& loopTry) mutable { + [loop, runner, notifiers = std::move(notifiers), this](auto&& loopTry) mutable { if (loopTry.hasException()) { - return notifyError(pros, Status::Error(loopTry.exception().what())); + return notifyError(notifiers, Status::Error(loopTry.exception().what())); } auto loopStatus = std::move(loopTry).value(); if (!loopStatus.ok()) { - return notifyError(pros, loopStatus); + return notifyError(notifiers, loopStatus); } auto val = qctx_->ectx()->getValue(loop->node()->outputVar()); if (!val.isBool()) { std::stringstream ss; ss << "Loop produces a bad condition result: " << val << " type: " << val.type(); - return notifyError(pros, Status::Error(ss.str())); + return notifyError(notifiers, Status::Error(ss.str())); } if (val.getBool()) { auto loopBody = loop->loopBody(); - auto scheduleFuture = doSchedule(loopBody); - std::vector> fs; - fs.emplace_back(std::move(scheduleFuture)); - runLoop(std::move(fs), loop, runner, std::move(pros)); + auto scheduleReceiver = doSchedule(loopBody); + std::vector rs; + rs.emplace_back(std::move(scheduleReceiver)); + runLoop(std::move(rs), loop, runner, std::move(notifiers)); } else { - return notifyOK(pros); + return notifyOK(notifiers); } }); }); @@ -261,16 +259,16 @@ Status AsyncMsgNotifyBasedScheduler::checkStatus(std::vector&& status) c return Status::OK(); } -void AsyncMsgNotifyBasedScheduler::notifyOK(std::vector>& promises) const { - for (auto& p : promises) { - p.setValue(Status::OK()); +void AsyncMsgNotifyBasedScheduler::notifyOK(std::vector& notifiers) const { + for (auto& n : notifiers) { + n.setValue(Status::OK()); } } -void AsyncMsgNotifyBasedScheduler::notifyError(std::vector>& promises, +void AsyncMsgNotifyBasedScheduler::notifyError(std::vector& notifiers, Status status) const { - for (auto& p : promises) { - p.setValue(status); + for (auto& n : notifiers) { + n.setValue(status); } } diff --git a/src/scheduler/AsyncMsgNotifyBasedScheduler.h b/src/scheduler/AsyncMsgNotifyBasedScheduler.h index af2c0c6b6..b05ec697b 100644 --- a/src/scheduler/AsyncMsgNotifyBasedScheduler.h +++ b/src/scheduler/AsyncMsgNotifyBasedScheduler.h @@ -29,44 +29,46 @@ class AsyncMsgNotifyBasedScheduler final : public Scheduler { folly::Future schedule() override; private: + using Notifier = folly::Promise; + using Receiver = folly::Future; folly::Future doSchedule(Executor* root) const; /** - * futures: current executor will be triggered when all the futures are notified. + * receivers: current executor will be triggered when all the futures are notified. * exe: current executor * runner: a thread-pool - * promises: the promises will be set a value which triggers the other executors + * notifiers: the promises will be set a value which triggers the other executors * if current executor is done working. */ - void scheduleExecutor(std::vector>&& futures, + void scheduleExecutor(std::vector&& receivers, Executor* exe, folly::Executor* runner, - std::vector>&& promises) const; + std::vector&& notifiers) const; - void runSelect(std::vector>&& futures, + void runSelect(std::vector&& receivers, SelectExecutor* select, folly::Executor* runner, - std::vector>&& promises) const; + std::vector&& notifiers) const; - void runExecutor(std::vector>&& futures, + void runExecutor(std::vector&& receivers, Executor* exe, folly::Executor* runner, - std::vector>&& promises) const; + std::vector&& notifiers) const; void runLeafExecutor(Executor* exe, folly::Executor* runner, - std::vector>&& promises) const; + std::vector&& notifiers) const; - void runLoop(std::vector>&& futures, + void runLoop(std::vector&& receivers, LoopExecutor* loop, folly::Executor* runner, - std::vector>&& promises) const; + std::vector&& notifiers) const; Status checkStatus(std::vector&& status) const; - void notifyOK(std::vector>& promises) const; + void notifyOK(std::vector& notifiers) const; - void notifyError(std::vector>& promises, Status status) const; + void notifyError(std::vector& notifiers, Status status) const; folly::Future execute(Executor *executor) const; From 7ce9b3e132b29cedc11014ea90e067721b0feaa2 Mon Sep 17 00:00:00 2001 From: William Chen <13495049+CPWstatic@users.noreply.github.com> Date: Tue, 13 Jul 2021 17:07:30 +0800 Subject: [PATCH 02/12] Using binary input join, --- src/executor/query/JoinExecutor.cpp | 8 +- src/executor/query/LeftJoinExecutor.cpp | 2 +- .../rule/PushFilterDownLeftJoinRule.cpp | 9 ++- src/planner/match/InnerJoinStrategy.cpp | 5 +- src/planner/ngql/GoPlanner.cpp | 66 ++++++++++------ src/planner/ngql/GoPlanner.h | 2 +- src/planner/plan/Query.cpp | 35 ++++---- src/planner/plan/Query.h | 79 +++++++------------ 8 files changed, 106 insertions(+), 100 deletions(-) diff --git a/src/executor/query/JoinExecutor.cpp b/src/executor/query/JoinExecutor.cpp index 0c7e0fe7b..e94b10a19 100644 --- a/src/executor/query/JoinExecutor.cpp +++ b/src/executor/query/JoinExecutor.cpp @@ -15,18 +15,18 @@ namespace graph { Status JoinExecutor::checkInputDataSets() { auto* join = asNode(node()); - lhsIter_ = ectx_->getVersionedResult(join->leftVar().first, join->leftVar().second).iter(); + lhsIter_ = ectx_->getVersionedResult(join->leftInputVar(), join->leftVarVersion()).iter(); DCHECK(!!lhsIter_); - VLOG(1) << "lhs: " << join->leftVar().first << " " << lhsIter_->size(); + VLOG(1) << "lhs: " << join->leftInputVar() << " " << lhsIter_->size(); if (lhsIter_->isGetNeighborsIter() || lhsIter_->isDefaultIter()) { std::stringstream ss; ss << "Join executor does not support " << lhsIter_->kind(); return Status::Error(ss.str()); } rhsIter_ = - ectx_->getVersionedResult(join->rightVar().first, join->rightVar().second).iter(); + ectx_->getVersionedResult(join->rightInputVar(), join->rightVarVersion()).iter(); DCHECK(!!rhsIter_); - VLOG(1) << "rhs: " << join->rightVar().first << " " << rhsIter_->size(); + VLOG(1) << "rhs: " << join->rightInputVar() << " " << rhsIter_->size(); if (rhsIter_->isGetNeighborsIter() || rhsIter_->isDefaultIter()) { std::stringstream ss; ss << "Join executor does not support " << rhsIter_->kind(); diff --git a/src/executor/query/LeftJoinExecutor.cpp b/src/executor/query/LeftJoinExecutor.cpp index 5889bc230..46e5e1d0a 100644 --- a/src/executor/query/LeftJoinExecutor.cpp +++ b/src/executor/query/LeftJoinExecutor.cpp @@ -25,7 +25,7 @@ Status LeftJoinExecutor::close() { folly::Future LeftJoinExecutor::join() { auto* join = asNode(node()); - auto& rhsResult = ectx_->getVersionedResult(join->rightVar().first, join->rightVar().second); + auto& rhsResult = ectx_->getVersionedResult(join->rightInputVar(), join->rightVarVersion()); rightColSize_ = rhsResult.valuePtr()->getDataSet().colNames.size(); auto& hashKeys = join->hashKeys(); diff --git a/src/optimizer/rule/PushFilterDownLeftJoinRule.cpp b/src/optimizer/rule/PushFilterDownLeftJoinRule.cpp index 40d7073fa..5af27c179 100644 --- a/src/optimizer/rule/PushFilterDownLeftJoinRule.cpp +++ b/src/optimizer/rule/PushFilterDownLeftJoinRule.cpp @@ -32,6 +32,7 @@ const Pattern& PushFilterDownLeftJoinRule::pattern() const { } StatusOr PushFilterDownLeftJoinRule::transform( + // TODO: Need a refactor after the join refactored OptContext* octx, const MatchedResult& matched) const { auto* filterGroupNode = matched.node; @@ -45,9 +46,9 @@ StatusOr PushFilterDownLeftJoinRule::transform( auto* oldLeftJoinNode = static_cast(leftJoinNode); const auto* condition = static_cast(oldFilterNode)->condition(); DCHECK(condition); - const std::pair& leftVar = oldLeftJoinNode->leftVar(); + const std::string& leftVar = oldLeftJoinNode->leftInputVar(); auto symTable = octx->qctx()->symTable(); - std::vector leftVarColNames = symTable->getVar(leftVar.first)->colNames; + std::vector leftVarColNames = symTable->getVar(leftVar)->colNames; // split the `condition` based on whether the varPropExpr comes from the left child auto picker = [&leftVarColNames](const Expression* e) -> bool { @@ -82,8 +83,8 @@ StatusOr PushFilterDownLeftJoinRule::transform( auto* newLeftFilterNode = graph::Filter::make( octx->qctx(), const_cast(oldLeftJoinNode->dep()), - graph::ExpressionUtils::rewriteInnerVar(filterPicked, leftVar.first)); - newLeftFilterNode->setInputVar(leftVar.first); + graph::ExpressionUtils::rewriteInnerVar(filterPicked, leftVar)); + newLeftFilterNode->setInputVar(leftVar); newLeftFilterNode->setColNames(leftVarColNames); auto newFilterGroup = OptGroup::create(octx); auto newFilterGroupNode = newFilterGroup->makeGroupNode(newLeftFilterNode); diff --git a/src/planner/match/InnerJoinStrategy.cpp b/src/planner/match/InnerJoinStrategy.cpp index 5bab56fd0..565a69283 100644 --- a/src/planner/match/InnerJoinStrategy.cpp +++ b/src/planner/match/InnerJoinStrategy.cpp @@ -37,9 +37,8 @@ PlanNode* InnerJoinStrategy::joinDataSet(const PlanNode* left, const PlanNode* r } auto join = InnerJoin::make(qctx_, - const_cast(right), - {left->outputVar(), 0}, - {right->outputVar(), 0}, + {const_cast(left), ExecutionContext::kLatestVersion}, + {const_cast(right), ExecutionContext::kLatestVersion}, {buildExpr}, {probeExpr}); std::vector colNames = left->colNames(); diff --git a/src/planner/ngql/GoPlanner.cpp b/src/planner/ngql/GoPlanner.cpp index 19a00b2ac..db6031ed2 100644 --- a/src/planner/ngql/GoPlanner.cpp +++ b/src/planner/ngql/GoPlanner.cpp @@ -187,9 +187,8 @@ PlanNode* GoPlanner::trackStartVid(PlanNode* left, PlanNode* right) { VariablePropertyExpression::make(pool, right->outputVar(), goCtx_->srcVidColName); auto* join = InnerJoin::make(qctx, - right, - {left->outputVar(), ExecutionContext::kLatestVersion}, - {right->outputVar(), ExecutionContext::kLatestVersion}, + {left, ExecutionContext::kLatestVersion}, + {right, ExecutionContext::kLatestVersion}, {hashKey}, {probeKey}); std::vector colNames = left->colNames(); @@ -213,9 +212,19 @@ PlanNode* GoPlanner::trackStartVid(PlanNode* left, PlanNode* right) { } /* + * connecting the edges and dst vertices * output ColNames {srcProps, edgeProps, kVid, "JOIN_DST_VID", "DST_VID", dstProps} + * Join + * | + * ---------- + * | | + * Left Project + * | + * GetVertices + * | + * Dep */ -PlanNode* GoPlanner::buildJoinDstPlan(PlanNode* dep) { +PlanNode* GoPlanner::buildJoinDstPlan(PlanNode* dep, PlanNode* left) { auto qctx = goCtx_->qctx; auto* pool = qctx->objPool(); @@ -241,9 +250,8 @@ PlanNode* GoPlanner::buildJoinDstPlan(PlanNode* dep) { auto* hashKey = dstExpr->clone(); auto* probeKey = ColumnExpression::make(pool, LAST_COL_INDEX); auto* join = LeftJoin::make(qctx, - project, - {dep->outputVar(), ExecutionContext::kLatestVersion}, - {project->outputVar(), ExecutionContext::kLatestVersion}, + {left, ExecutionContext::kLatestVersion}, + {project, ExecutionContext::kLatestVersion}, {hashKey}, {probeKey}); @@ -257,26 +265,28 @@ PlanNode* GoPlanner::buildJoinDstPlan(PlanNode* dep) { return join; } -PlanNode* GoPlanner::buildJoinInputPlan(PlanNode* dep) { +PlanNode* GoPlanner::buildJoinInputPlan(PlanNode* left) { auto qctx = goCtx_->qctx; const auto& from = goCtx_->from; const auto& steps = goCtx_->steps; auto* pool = qctx->objPool(); const auto& vidName = (!steps.isMToN() && steps.steps() == 1) ? kVid : from.runtimeVidName; - auto* hashKey = VariablePropertyExpression::make(pool, dep->outputVar(), vidName); + auto* hashKey = VariablePropertyExpression::make(pool, left->outputVar(), vidName); auto* probeKey = from.originalSrc; std::string probeName = from.fromType == kPipe ? goCtx_->inputVarName : from.userDefinedVarName; + auto* varPtr = qctx->symTable()->getVar(probeName); + DCHECK(varPtr != nullptr); + auto pass = PassThroughNode::make(qctx, nullptr); + pass->setOutputVar(probeName); + pass->setColNames(varPtr->colNames); auto* join = InnerJoin::make(qctx, - dep, - {dep->outputVar(), ExecutionContext::kLatestVersion}, - {probeName, ExecutionContext::kLatestVersion}, + {left, ExecutionContext::kLatestVersion}, + {pass, ExecutionContext::kLatestVersion}, {hashKey}, {probeKey}); - std::vector colNames = dep->colNames(); - auto* varPtr = qctx->symTable()->getVar(probeName); - DCHECK(varPtr != nullptr); + std::vector colNames = left->colNames(); colNames.insert(colNames.end(), varPtr->colNames.begin(), varPtr->colNames.end()); join->setColNames(std::move(colNames)); @@ -300,9 +310,8 @@ PlanNode* GoPlanner::lastStepJoinInput(PlanNode* left, PlanNode* right) { : ExecutionContext::kLatestVersion; auto* join = InnerJoin::make(qctx, - right, - {left->outputVar(), leftVersion}, - {right->outputVar(), ExecutionContext::kLatestVersion}, + {left, leftVersion}, + {right, ExecutionContext::kLatestVersion}, {hashKey}, {probeKey}); @@ -319,7 +328,7 @@ PlanNode* GoPlanner::buildLastStepJoinPlan(PlanNode* gn, PlanNode* join) { } auto* dep = extractSrcEdgePropsFromGN(gn, gn->outputVar()); - dep = goCtx_->joinDst ? buildJoinDstPlan(dep) : dep; + dep = goCtx_->joinDst ? buildJoinDstPlan(dep, dep) : dep; dep = goCtx_->joinInput ? lastStepJoinInput(join, dep) : dep; dep = goCtx_->joinInput ? buildJoinInputPlan(dep) : dep; @@ -349,13 +358,16 @@ PlanNode* GoPlanner::lastStep(PlanNode* dep, PlanNode* join) { return root; } +/** + * Connecting the dst vertices or input if needed. + */ PlanNode* GoPlanner::buildOneStepJoinPlan(PlanNode* gn) { if (!goCtx_->joinInput && !goCtx_->joinDst) { return gn; } auto* dep = extractSrcEdgePropsFromGN(gn, gn->outputVar()); - dep = goCtx_->joinDst ? buildJoinDstPlan(dep) : dep; + dep = goCtx_->joinDst ? buildJoinDstPlan(dep, dep) : dep; dep = goCtx_->joinInput ? buildJoinInputPlan(dep) : dep; return dep; @@ -402,7 +414,11 @@ SubPlan GoPlanner::nStepsPlan(SubPlan& startVidPlan) { if (goCtx_->joinInput) { auto* joinLeft = extractVidFromRuntimeInput(startVidPlan.root); auto* joinRight = extractSrcDstFromGN(getDst, gn->outputVar()); - loopBody = trackStartVid(joinLeft, joinRight); + + auto pass = PassThroughNode::make(qctx, nullptr); + pass->setOutputVar(joinLeft->outputVar()); + pass->setColNames(joinLeft->colNames()); + loopBody = trackStartVid(pass, joinRight); loopDep = joinLeft; } @@ -437,14 +453,18 @@ SubPlan GoPlanner::mToNStepsPlan(SubPlan& startVidPlan) { if (joinInput) { auto* joinLeft = extractVidFromRuntimeInput(startVidPlan.root); auto* joinRight = extractSrcDstFromGN(getDst, gn->outputVar()); - trackVid = trackStartVid(joinLeft, joinRight); + + auto pass = PassThroughNode::make(qctx, nullptr); + pass->setOutputVar(joinLeft->outputVar()); + pass->setColNames(joinLeft->colNames()); + trackVid = trackStartVid(pass, joinRight); loopBody = trackVid; loopDep = joinLeft; } if (joinInput || joinDst) { loopBody = extractSrcEdgePropsFromGN(loopBody, gn->outputVar()); - loopBody = joinDst ? buildJoinDstPlan(loopBody) : loopBody; + loopBody = joinDst ? buildJoinDstPlan(loopBody, loopBody) : loopBody; loopBody = joinInput ? lastStepJoinInput(trackVid, loopBody) : loopBody; loopBody = joinInput ? buildJoinInputPlan(loopBody) : loopBody; } diff --git a/src/planner/ngql/GoPlanner.h b/src/planner/ngql/GoPlanner.h index 0810c851c..81dc2f8ec 100644 --- a/src/planner/ngql/GoPlanner.h +++ b/src/planner/ngql/GoPlanner.h @@ -57,7 +57,7 @@ class GoPlanner final : public Planner { PlanNode* trackStartVid(PlanNode* left, PlanNode* right); - PlanNode* buildJoinDstPlan(PlanNode* dep); + PlanNode* buildJoinDstPlan(PlanNode* dep, PlanNode* left); PlanNode* buildJoinInputPlan(PlanNode* dep); diff --git a/src/planner/plan/Query.cpp b/src/planner/plan/Query.cpp index 00e59bdf9..817125ece 100644 --- a/src/planner/plan/Query.cpp +++ b/src/planner/plan/Query.cpp @@ -502,10 +502,10 @@ void DataCollect::cloneMembers(const DataCollect &l) { std::unique_ptr Join::explain() const { - auto desc = SingleDependencyNode::explain(); + auto desc = BinaryInputNode::explain(); folly::dynamic inputVar = folly::dynamic::object(); - inputVar.insert("leftVar", util::toJson(leftVar_)); - inputVar.insert("rightVar", util::toJson(rightVar_)); + inputVar.insert("leftVar", util::toJson(std::make_pair(leftInputVar(), leftVarVersion()))); + inputVar.insert("rightVar", util::toJson(std::make_pair(rightInputVar(), rightVarVersion()))); addDescription("inputVar", folly::toJson(inputVar), desc.get()); addDescription("hashKeys", folly::toJson(util::toJson(hashKeys_)), desc.get()); addDescription("probeKeys", folly::toJson(util::toJson(probeKeys_)), desc.get()); @@ -513,10 +513,9 @@ std::unique_ptr Join::explain() const { } void Join::cloneMembers(const Join& j) { - SingleDependencyNode::cloneMembers(j); + BinaryInputNode::cloneMembers(j); - leftVar_ = j.leftVar(); - rightVar_ = j.rightVar(); + // TODO: std::vector hKeys; for (auto* item : j.hashKeys()) { @@ -534,19 +533,15 @@ void Join::cloneMembers(const Join& j) { Join::Join(QueryContext* qctx, Kind kind, - PlanNode* input, - std::pair leftVar, - std::pair rightVar, + std::pair left, + std::pair right, std::vector hashKeys, std::vector probeKeys) - : SingleDependencyNode(qctx, kind, input), - leftVar_(std::move(leftVar)), - rightVar_(std::move(rightVar)), + : BinaryInputNode(qctx, kind, left.first, right.first), hashKeys_(std::move(hashKeys)), probeKeys_(std::move(probeKeys)) { - inputVars_.clear(); - readVariable(leftVar_.first); - readVariable(rightVar_.first); + inputVarVersion_.emplace_back(left.second); + inputVarVersion_.emplace_back(right.second); } @@ -557,12 +552,17 @@ std::unique_ptr LeftJoin::explain() const { } PlanNode* LeftJoin::clone() const { + // TODO + /* auto* newLeftJoin = LeftJoin::make(qctx_, nullptr, leftVar_, rightVar_); newLeftJoin->cloneMembers(*this); return newLeftJoin; + */ + return nullptr; } void LeftJoin::cloneMembers(const LeftJoin &l) { + // TODO Join::cloneMembers(l); } @@ -574,12 +574,17 @@ std::unique_ptr InnerJoin::explain() const { } PlanNode* InnerJoin::clone() const { + // TODO + /* auto* newInnerJoin = InnerJoin::make(qctx_, nullptr, leftVar_, rightVar_); newInnerJoin->cloneMembers(*this); return newInnerJoin; + */ + return nullptr; } void InnerJoin::cloneMembers(const InnerJoin &l) { + // TODO Join::cloneMembers(l); } diff --git a/src/planner/plan/Query.h b/src/planner/plan/Query.h index e7fab16ac..ccc30c72d 100644 --- a/src/planner/plan/Query.h +++ b/src/planner/plan/Query.h @@ -1006,26 +1006,8 @@ class DataCollect final : public VariableDependencyNode { bool distinct_{false}; }; -class Join : public SingleDependencyNode { +class Join : public BinaryInputNode { public: - const std::pair& leftVar() const { - return leftVar_; - } - - const std::pair& rightVar() const { - return rightVar_; - } - - void setLeftVar(std::pair lvar) { - setInputVar(lvar.first, 0); - leftVar_ = lvar; - } - - void setRightVar(std::pair rvar) { - setInputVar(rvar.first, 1); - rightVar_ = rvar; - } - const std::vector& hashKeys() const { return hashKeys_; } @@ -1042,23 +1024,30 @@ class Join : public SingleDependencyNode { probeKeys_ = newProbeKeys; } + int64_t leftVarVersion() const { + DCHECK_EQ(inputVarVersion_.size(), 2); + return inputVarVersion_[0]; + } + + int64_t rightVarVersion() const { + DCHECK_EQ(inputVarVersion_.size(), 2); + return inputVarVersion_[1]; + } + std::unique_ptr explain() const override; protected: Join(QueryContext* qctx, Kind kind, - PlanNode* input, - std::pair leftVar, - std::pair rightVar, + std::pair left, + std::pair right, std::vector hashKeys, std::vector probeKeys); void cloneMembers(const Join&); protected: - // var name, var version - std::pair leftVar_; - std::pair rightVar_; + std::vector inputVarVersion_; std::vector hashKeys_; std::vector probeKeys_; }; @@ -1069,15 +1058,13 @@ class Join : public SingleDependencyNode { class LeftJoin final : public Join { public: static LeftJoin* make(QueryContext* qctx, - PlanNode* input, - std::pair leftVar, - std::pair rightVar, + std::pair left, + std::pair right, std::vector hashKeys = {}, std::vector probeKeys = {}) { return qctx->objPool()->add(new LeftJoin(qctx, - input, - std::move(leftVar), - std::move(rightVar), + std::move(left), + std::move(right), std::move(hashKeys), std::move(probeKeys))); } @@ -1087,16 +1074,14 @@ class LeftJoin final : public Join { private: LeftJoin(QueryContext* qctx, - PlanNode* input, - std::pair leftVar, - std::pair rightVar, + std::pair left, + std::pair right, std::vector hashKeys, std::vector probeKeys) : Join(qctx, Kind::kLeftJoin, - input, - std::move(leftVar), - std::move(rightVar), + std::move(left), + std::move(right), std::move(hashKeys), std::move(probeKeys)) {} @@ -1109,15 +1094,13 @@ class LeftJoin final : public Join { class InnerJoin final : public Join { public: static InnerJoin* make(QueryContext* qctx, - PlanNode* input, - std::pair leftVar, - std::pair rightVar, + std::pair left, + std::pair right, std::vector hashKeys = {}, std::vector probeKeys = {}) { return qctx->objPool()->add(new InnerJoin(qctx, - input, - std::move(leftVar), - std::move(rightVar), + std::move(left), + std::move(right), std::move(hashKeys), std::move(probeKeys))); } @@ -1127,16 +1110,14 @@ class InnerJoin final : public Join { private: InnerJoin(QueryContext* qctx, - PlanNode* input, - std::pair leftVar, - std::pair rightVar, + std::pair left, + std::pair right, std::vector hashKeys, std::vector probeKeys) : Join(qctx, Kind::kInnerJoin, - input, - std::move(leftVar), - std::move(rightVar), + std::move(left), + std::move(right), std::move(hashKeys), std::move(probeKeys)) {} From 06d269670208c821bcc0db41c227964a0b962102 Mon Sep 17 00:00:00 2001 From: William Chen <13495049+CPWstatic@users.noreply.github.com> Date: Tue, 13 Jul 2021 20:21:12 +0800 Subject: [PATCH 03/12] Fix go. --- src/planner/ngql/GoPlanner.cpp | 15 +++++++++++---- tests/Makefile | 4 ++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/planner/ngql/GoPlanner.cpp b/src/planner/ngql/GoPlanner.cpp index db6031ed2..12d7995aa 100644 --- a/src/planner/ngql/GoPlanner.cpp +++ b/src/planner/ngql/GoPlanner.cpp @@ -278,7 +278,7 @@ PlanNode* GoPlanner::buildJoinInputPlan(PlanNode* left) { auto* varPtr = qctx->symTable()->getVar(probeName); DCHECK(varPtr != nullptr); - auto pass = PassThroughNode::make(qctx, nullptr); + auto pass = PassThroughNode::make(qctx, StartNode::make(qctx)); pass->setOutputVar(probeName); pass->setColNames(varPtr->colNames); auto* join = InnerJoin::make(qctx, @@ -329,7 +329,14 @@ PlanNode* GoPlanner::buildLastStepJoinPlan(PlanNode* gn, PlanNode* join) { auto* dep = extractSrcEdgePropsFromGN(gn, gn->outputVar()); dep = goCtx_->joinDst ? buildJoinDstPlan(dep, dep) : dep; - dep = goCtx_->joinInput ? lastStepJoinInput(join, dep) : dep; + + PlanNode* left; + if (goCtx_->joinInput && join != nullptr) { + left = PassThroughNode::make(goCtx_->qctx, StartNode::make(goCtx_->qctx)); + left->setOutputVar(join->outputVar()); + left->setColNames(join->colNames()); + } + dep = goCtx_->joinInput ? lastStepJoinInput(left, dep) : dep; dep = goCtx_->joinInput ? buildJoinInputPlan(dep) : dep; return dep; @@ -415,7 +422,7 @@ SubPlan GoPlanner::nStepsPlan(SubPlan& startVidPlan) { auto* joinLeft = extractVidFromRuntimeInput(startVidPlan.root); auto* joinRight = extractSrcDstFromGN(getDst, gn->outputVar()); - auto pass = PassThroughNode::make(qctx, nullptr); + auto pass = PassThroughNode::make(qctx, StartNode::make(qctx)); pass->setOutputVar(joinLeft->outputVar()); pass->setColNames(joinLeft->colNames()); loopBody = trackStartVid(pass, joinRight); @@ -454,7 +461,7 @@ SubPlan GoPlanner::mToNStepsPlan(SubPlan& startVidPlan) { auto* joinLeft = extractVidFromRuntimeInput(startVidPlan.root); auto* joinRight = extractSrcDstFromGN(getDst, gn->outputVar()); - auto pass = PassThroughNode::make(qctx, nullptr); + auto pass = PassThroughNode::make(qctx, StartNode::make(qctx)); pass->setOutputVar(joinLeft->outputVar()); pass->setColNames(joinLeft->colNames()); trackVid = trackStartVid(pass, joinRight); diff --git a/tests/Makefile b/tests/Makefile index 10fc66d04..779bbe6fe 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -67,8 +67,8 @@ test: sess jobs python3 -m pytest -n$(J) --dist=loadfile -m "not skip" -k "not tck" $(TEST_DIR) slow-query: currdir - python3 -m pytest -n$(J) -m "not skip" tck/steps/test_kill_slow_query_via_same_service.py && \ - python3 -m pytest -n$(J) -m "not skip" tck/steps/test_kill_slow_query_via_different_service.py + python3 -m pytest -n 2 -m "not skip" tck/steps/test_kill_slow_query_via_same_service.py && \ + python3 -m pytest -n 2 -m "not skip" tck/steps/test_kill_slow_query_via_different_service.py tck: slow-query python3 -m pytest -n$(J) -m "not skip" tck/steps/test_tck.py From bd7094adcdd2ecef7fea6f5ac05ea2f8e82e449a Mon Sep 17 00:00:00 2001 From: William Chen <13495049+CPWstatic@users.noreply.github.com> Date: Wed, 14 Jul 2021 10:17:07 +0800 Subject: [PATCH 04/12] Fix clone --- src/planner/plan/Query.cpp | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/src/planner/plan/Query.cpp b/src/planner/plan/Query.cpp index 817125ece..85f87ef29 100644 --- a/src/planner/plan/Query.cpp +++ b/src/planner/plan/Query.cpp @@ -515,8 +515,6 @@ std::unique_ptr Join::explain() const { void Join::cloneMembers(const Join& j) { BinaryInputNode::cloneMembers(j); - // TODO: - std::vector hKeys; for (auto* item : j.hashKeys()) { hKeys.emplace_back(item->clone()); @@ -530,7 +528,6 @@ void Join::cloneMembers(const Join& j) { probeKeys_ = std::move(pKeys); } - Join::Join(QueryContext* qctx, Kind kind, std::pair left, @@ -544,7 +541,6 @@ Join::Join(QueryContext* qctx, inputVarVersion_.emplace_back(right.second); } - std::unique_ptr LeftJoin::explain() const { auto desc = Join::explain(); addDescription("kind", "LeftJoin", desc.get()); @@ -552,21 +548,20 @@ std::unique_ptr LeftJoin::explain() const { } PlanNode* LeftJoin::clone() const { - // TODO - /* - auto* newLeftJoin = LeftJoin::make(qctx_, nullptr, leftVar_, rightVar_); + auto* newLeftJoin = + LeftJoin::make(qctx_, + std::make_pair(const_cast(dep(0)), leftVarVersion()), + std::make_pair(const_cast(dep(1)), rightVarVersion()), + {}, + {}); newLeftJoin->cloneMembers(*this); return newLeftJoin; - */ - return nullptr; } -void LeftJoin::cloneMembers(const LeftJoin &l) { - // TODO +void LeftJoin::cloneMembers(const LeftJoin& l) { Join::cloneMembers(l); } - std::unique_ptr InnerJoin::explain() const { auto desc = Join::explain(); addDescription("kind", "InnerJoin", desc.get()); @@ -574,21 +569,20 @@ std::unique_ptr InnerJoin::explain() const { } PlanNode* InnerJoin::clone() const { - // TODO - /* - auto* newInnerJoin = InnerJoin::make(qctx_, nullptr, leftVar_, rightVar_); + auto* newInnerJoin = + InnerJoin::make(qctx_, + std::make_pair(const_cast(dep(0)), leftVarVersion()), + std::make_pair(const_cast(dep(1)), rightVarVersion()), + {}, + {}); newInnerJoin->cloneMembers(*this); return newInnerJoin; - */ - return nullptr; } -void InnerJoin::cloneMembers(const InnerJoin &l) { - // TODO +void InnerJoin::cloneMembers(const InnerJoin& l) { Join::cloneMembers(l); } - std::unique_ptr Assign::explain() const { auto desc = SingleDependencyNode::explain(); for (size_t i = 0; i < items_.size(); ++i) { From 1045324d272aaa5f0b4f488f37186a63d3c1d4ac Mon Sep 17 00:00:00 2001 From: William Chen <13495049+CPWstatic@users.noreply.github.com> Date: Wed, 14 Jul 2021 17:05:11 +0800 Subject: [PATCH 05/12] Fix push filter down join. --- src/optimizer/OptGroup.cpp | 3 ++- src/optimizer/rule/PushFilterDownLeftJoinRule.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/optimizer/OptGroup.cpp b/src/optimizer/OptGroup.cpp index b3f2cae55..e1a7f72fc 100644 --- a/src/optimizer/OptGroup.cpp +++ b/src/optimizer/OptGroup.cpp @@ -203,7 +203,8 @@ const PlanNode *OptGroupNode::getPlan() const { auto loop = static_cast(node_); loop->setBody(const_cast(bodies_[0]->getPlan())); } - DCHECK_EQ(node_->numDeps(), dependencies_.size()); + DCHECK_EQ(node_->numDeps(), dependencies_.size()) + << " node kind: " << node_->kind() << " node output: " << node_->outputVar(); for (size_t i = 0; i < node_->numDeps(); ++i) { node_->setDep(i, dependencies_[i]->getPlan()); } diff --git a/src/optimizer/rule/PushFilterDownLeftJoinRule.cpp b/src/optimizer/rule/PushFilterDownLeftJoinRule.cpp index 5af27c179..a8bf6aa93 100644 --- a/src/optimizer/rule/PushFilterDownLeftJoinRule.cpp +++ b/src/optimizer/rule/PushFilterDownLeftJoinRule.cpp @@ -49,6 +49,7 @@ StatusOr PushFilterDownLeftJoinRule::transform( const std::string& leftVar = oldLeftJoinNode->leftInputVar(); auto symTable = octx->qctx()->symTable(); std::vector leftVarColNames = symTable->getVar(leftVar)->colNames; + auto rightDepGroupNode = leftJoinGroupNode->dependencies()[1]; // split the `condition` based on whether the varPropExpr comes from the left child auto picker = [&leftVarColNames](const Expression* e) -> bool { @@ -88,9 +89,8 @@ StatusOr PushFilterDownLeftJoinRule::transform( newLeftFilterNode->setColNames(leftVarColNames); auto newFilterGroup = OptGroup::create(octx); auto newFilterGroupNode = newFilterGroup->makeGroupNode(newLeftFilterNode); - for (auto dep : leftJoinGroupNode->dependencies()) { - newFilterGroupNode->dependsOn(dep); - } + DCHECK_EQ(leftJoinGroupNode->dependencies().size(), 2); + newFilterGroupNode->dependsOn(leftJoinGroupNode->dependencies()[0]); auto newLeftFilterOutputVar = newLeftFilterNode->outputVar(); // produce new LeftJoin node @@ -116,14 +116,14 @@ StatusOr PushFilterDownLeftJoinRule::transform( auto newLeftJoinGroup = OptGroup::create(octx); auto newLeftJoinGroupNode = newLeftJoinGroup->makeGroupNode(newLeftJoinNode); newAboveFilterGroupNode->setDeps({newLeftJoinGroup}); - newLeftJoinGroupNode->setDeps({newFilterGroup}); + newLeftJoinGroupNode->setDeps({newFilterGroup, rightDepGroupNode}); result.newGroupNodes.emplace_back(newAboveFilterGroupNode); } else { newLeftJoinNode->setOutputVar(oldFilterNode->outputVar()); newLeftJoinNode->setColNames(oldLeftJoinNode->colNames()); auto newLeftJoinGroupNode = OptGroupNode::create(octx, newLeftJoinNode, filterGroupNode->group()); - newLeftJoinGroupNode->setDeps({newFilterGroup}); + newLeftJoinGroupNode->setDeps({newFilterGroup, rightDepGroupNode}); result.newGroupNodes.emplace_back(newLeftJoinGroupNode); } return result; From 57b78d2c58e1ac7d09ab0c4d0ce8c48d600219c6 Mon Sep 17 00:00:00 2001 From: William Chen <13495049+CPWstatic@users.noreply.github.com> Date: Wed, 14 Jul 2021 18:19:49 +0800 Subject: [PATCH 06/12] Use start. --- src/planner/ngql/GoPlanner.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/planner/ngql/GoPlanner.cpp b/src/planner/ngql/GoPlanner.cpp index 12d7995aa..9ec571c27 100644 --- a/src/planner/ngql/GoPlanner.cpp +++ b/src/planner/ngql/GoPlanner.cpp @@ -278,12 +278,12 @@ PlanNode* GoPlanner::buildJoinInputPlan(PlanNode* left) { auto* varPtr = qctx->symTable()->getVar(probeName); DCHECK(varPtr != nullptr); - auto pass = PassThroughNode::make(qctx, StartNode::make(qctx)); - pass->setOutputVar(probeName); - pass->setColNames(varPtr->colNames); + auto right = StartNode::make(qctx); + right->setOutputVar(probeName); + right->setColNames(varPtr->colNames); auto* join = InnerJoin::make(qctx, {left, ExecutionContext::kLatestVersion}, - {pass, ExecutionContext::kLatestVersion}, + {right, ExecutionContext::kLatestVersion}, {hashKey}, {probeKey}); std::vector colNames = left->colNames(); @@ -332,7 +332,7 @@ PlanNode* GoPlanner::buildLastStepJoinPlan(PlanNode* gn, PlanNode* join) { PlanNode* left; if (goCtx_->joinInput && join != nullptr) { - left = PassThroughNode::make(goCtx_->qctx, StartNode::make(goCtx_->qctx)); + left = StartNode::make(goCtx_->qctx); left->setOutputVar(join->outputVar()); left->setColNames(join->colNames()); } @@ -422,10 +422,10 @@ SubPlan GoPlanner::nStepsPlan(SubPlan& startVidPlan) { auto* joinLeft = extractVidFromRuntimeInput(startVidPlan.root); auto* joinRight = extractSrcDstFromGN(getDst, gn->outputVar()); - auto pass = PassThroughNode::make(qctx, StartNode::make(qctx)); - pass->setOutputVar(joinLeft->outputVar()); - pass->setColNames(joinLeft->colNames()); - loopBody = trackStartVid(pass, joinRight); + auto left = StartNode::make(qctx); + left->setOutputVar(joinLeft->outputVar()); + left->setColNames(joinLeft->colNames()); + loopBody = trackStartVid(left, joinRight); loopDep = joinLeft; } @@ -461,10 +461,10 @@ SubPlan GoPlanner::mToNStepsPlan(SubPlan& startVidPlan) { auto* joinLeft = extractVidFromRuntimeInput(startVidPlan.root); auto* joinRight = extractSrcDstFromGN(getDst, gn->outputVar()); - auto pass = PassThroughNode::make(qctx, StartNode::make(qctx)); - pass->setOutputVar(joinLeft->outputVar()); - pass->setColNames(joinLeft->colNames()); - trackVid = trackStartVid(pass, joinRight); + auto left = StartNode::make(qctx); + left->setOutputVar(joinLeft->outputVar()); + left->setColNames(joinLeft->colNames()); + trackVid = trackStartVid(left, joinRight); loopBody = trackVid; loopDep = joinLeft; } From 35b2098efa04d10ec517d8732c4849bef437e014 Mon Sep 17 00:00:00 2001 From: William Chen <13495049+CPWstatic@users.noreply.github.com> Date: Thu, 15 Jul 2021 11:03:07 +0800 Subject: [PATCH 07/12] Fix executor test. --- src/executor/test/JoinTest.cpp | 87 ++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 14 deletions(-) diff --git a/src/executor/test/JoinTest.cpp b/src/executor/test/JoinTest.cpp index d1a30d1c3..6d1c5eff8 100644 --- a/src/executor/test/JoinTest.cpp +++ b/src/executor/test/JoinTest.cpp @@ -8,6 +8,7 @@ #include "context/QueryContext.h" #include "planner/plan/Query.h" +#include "planner/plan/Logic.h" #include "executor/query/InnerJoinExecutor.h" #include "executor/query/LeftJoinExecutor.h" #include "executor/test/QueryTestBase.h" @@ -94,9 +95,14 @@ void JoinTest::testInnerJoin(std::string left, std::string right, auto probe = VariablePropertyExpression::make(pool_, right, "_vid"); std::vector probeKeys = {probe}; - auto* join = - InnerJoin::make(qctx_.get(), nullptr, {left, 0}, {right, 0}, std::move(hashKeys), - std::move(probeKeys)); + auto* leftNode = StartNode::make(qctx_.get()); + leftNode->setOutputVar(left); + + auto* rightNode = StartNode::make(qctx_.get()); + rightNode->setOutputVar(right); + + auto* join = InnerJoin::make( + qctx_.get(), {leftNode, 0}, {rightNode, 0}, std::move(hashKeys), std::move(probeKeys)); join->setColNames(std::vector{ "src", "dst", kVid, "tag_prop", "edge_prop", kDst}); @@ -132,9 +138,14 @@ void JoinTest::testLeftJoin(std::string left, std::string right, auto probe = VariablePropertyExpression::make(pool_, right, "dst"); std::vector probeKeys = {probe}; - auto* join = - LeftJoin::make(qctx_.get(), nullptr, {left, 0}, {right, 0}, std::move(hashKeys), - std::move(probeKeys)); + auto* leftNode = StartNode::make(qctx_.get()); + leftNode->setOutputVar(left); + + auto* rightNode = StartNode::make(qctx_.get()); + rightNode->setOutputVar(right); + + auto* join = LeftJoin::make( + qctx_.get(), {leftNode, 0}, {rightNode, 0}, std::move(hashKeys), std::move(probeKeys)); join->setColNames(std::vector{ kVid, "tag_prop", "edge_prop", kDst, "src", "dst"}); @@ -201,8 +212,14 @@ TEST_F(JoinTest, InnerJoinTwice) { auto probe = VariablePropertyExpression::make(pool_, right, "_vid"); std::vector probeKeys = {probe}; + auto* leftNode = StartNode::make(qctx_.get()); + leftNode->setOutputVar(left); + + auto* rightNode = StartNode::make(qctx_.get()); + rightNode->setOutputVar(right); + auto* join = - InnerJoin::make(qctx_.get(), nullptr, {left, 0}, {right, 0}, std::move(hashKeys), + InnerJoin::make(qctx_.get(), {leftNode, 0}, {rightNode, 0}, std::move(hashKeys), std::move(probeKeys)); join->setColNames( std::vector{"src", "dst", kVid, "tag_prop", "edge_prop", kDst}); @@ -221,8 +238,14 @@ TEST_F(JoinTest, InnerJoinTwice) { auto probe = VariablePropertyExpression::make(pool_, right, "col1"); std::vector probeKeys = {probe}; + auto* leftNode = StartNode::make(qctx_.get()); + leftNode->setOutputVar(left); + + auto* rightNode = StartNode::make(qctx_.get()); + rightNode->setOutputVar(right); + auto* join = - InnerJoin::make(qctx_.get(), nullptr, {left, 0}, {right, 0}, std::move(hashKeys), + InnerJoin::make(qctx_.get(), {leftNode, 0}, {rightNode, 0}, std::move(hashKeys), std::move(probeKeys)); join->setColNames(std::vector{ "src", "dst", kVid, "tag_prop", "edge_prop", kDst, "col1"}); @@ -323,8 +346,14 @@ TEST_F(JoinTest, LeftJoinTwice) { auto probe = VariablePropertyExpression::make(pool_, right, "dst"); std::vector probeKeys = {probe}; + auto* leftNode = StartNode::make(qctx_.get()); + leftNode->setOutputVar(left); + + auto* rightNode = StartNode::make(qctx_.get()); + rightNode->setOutputVar(right); + auto* join = LeftJoin::make( - qctx_.get(), nullptr, {left, 0}, {right, 0}, std::move(hashKeys), std::move(probeKeys)); + qctx_.get(), {leftNode, 0}, {rightNode, 0}, std::move(hashKeys), std::move(probeKeys)); join->setColNames( std::vector{kVid, "tag_prop", "edge_prop", kDst, "src", "dst"}); @@ -341,8 +370,14 @@ TEST_F(JoinTest, LeftJoinTwice) { auto probe = VariablePropertyExpression::make(pool_, right, "col1"); std::vector probeKeys = {probe}; + auto* leftNode = StartNode::make(qctx_.get()); + leftNode->setOutputVar(left); + + auto* rightNode = StartNode::make(qctx_.get()); + rightNode->setOutputVar(right); + auto* join = LeftJoin::make( - qctx_.get(), nullptr, {left, 0}, {right, 0}, std::move(hashKeys), std::move(probeKeys)); + qctx_.get(), {leftNode, 0}, {rightNode, 0}, std::move(hashKeys), std::move(probeKeys)); join->setColNames( std::vector{kVid, "tag_prop", "edge_prop", kDst, "src", "dst", "col1"}); @@ -426,8 +461,14 @@ TEST_F(JoinTest, LeftJoinAndInnerjoin) { auto probe = VariablePropertyExpression::make(pool_, right, "dst"); std::vector probeKeys = {probe}; + auto* leftNode = StartNode::make(qctx_.get()); + leftNode->setOutputVar(left); + + auto* rightNode = StartNode::make(qctx_.get()); + rightNode->setOutputVar(right); + auto* join = LeftJoin::make( - qctx_.get(), nullptr, {left, 0}, {right, 0}, std::move(hashKeys), std::move(probeKeys)); + qctx_.get(), {leftNode, 0}, {rightNode, 0}, std::move(hashKeys), std::move(probeKeys)); join->setColNames( std::vector{kVid, "tag_prop", "edge_prop", kDst, "src", "dst"}); @@ -444,8 +485,14 @@ TEST_F(JoinTest, LeftJoinAndInnerjoin) { auto probe = VariablePropertyExpression::make(pool_, right, "col1"); std::vector probeKeys = {probe}; + auto* leftNode = StartNode::make(qctx_.get()); + leftNode->setOutputVar(left); + + auto* rightNode = StartNode::make(qctx_.get()); + rightNode->setOutputVar(right); + auto* join = InnerJoin::make( - qctx_.get(), nullptr, {left, 0}, {right, 0}, std::move(hashKeys), std::move(probeKeys)); + qctx_.get(), {leftNode, 0}, {rightNode, 0}, std::move(hashKeys), std::move(probeKeys)); join->setColNames( std::vector{kVid, "tag_prop", "edge_prop", kDst, "src", "dst", "col1"}); @@ -495,8 +542,14 @@ TEST_F(JoinTest, InnerJoinAndLeftjoin) { auto probe = VariablePropertyExpression::make(pool_, right, "dst"); std::vector probeKeys = {probe}; + auto* leftNode = StartNode::make(qctx_.get()); + leftNode->setOutputVar(left); + + auto* rightNode = StartNode::make(qctx_.get()); + rightNode->setOutputVar(right); + auto* join = InnerJoin::make( - qctx_.get(), nullptr, {left, 0}, {right, 0}, std::move(hashKeys), std::move(probeKeys)); + qctx_.get(), {leftNode, 0}, {rightNode, 0}, std::move(hashKeys), std::move(probeKeys)); join->setColNames( std::vector{kVid, "tag_prop", "edge_prop", kDst, "src", "dst"}); @@ -513,8 +566,14 @@ TEST_F(JoinTest, InnerJoinAndLeftjoin) { auto probe = VariablePropertyExpression::make(pool_, right, "col1"); std::vector probeKeys = {probe}; + auto* leftNode = StartNode::make(qctx_.get()); + leftNode->setOutputVar(left); + + auto* rightNode = StartNode::make(qctx_.get()); + rightNode->setOutputVar(right); + auto* join = LeftJoin::make( - qctx_.get(), nullptr, {left, 0}, {right, 0}, std::move(hashKeys), std::move(probeKeys)); + qctx_.get(), {leftNode, 0}, {rightNode, 0}, std::move(hashKeys), std::move(probeKeys)); join->setColNames( std::vector{kVid, "tag_prop", "edge_prop", kDst, "src", "dst", "col1"}); From 8b8e0678c12db53eef513ee964bcafee0dc36c4f Mon Sep 17 00:00:00 2001 From: William Chen <13495049+CPWstatic@users.noreply.github.com> Date: Thu, 15 Jul 2021 15:00:22 +0800 Subject: [PATCH 08/12] Fix validator test --- src/validator/test/GroupByValidatorTest.cpp | 14 +- src/validator/test/MatchValidatorTest.cpp | 100 +++++------ src/validator/test/QueryValidatorTest.cpp | 177 ++++++++++++-------- src/validator/test/SymbolsTest.cpp | 78 +++++---- 4 files changed, 212 insertions(+), 157 deletions(-) diff --git a/src/validator/test/GroupByValidatorTest.cpp b/src/validator/test/GroupByValidatorTest.cpp index a0dfac6dc..1a8ca9399 100644 --- a/src/validator/test/GroupByValidatorTest.cpp +++ b/src/validator/test/GroupByValidatorTest.cpp @@ -75,9 +75,9 @@ TEST_F(GroupByValidatorTest, TestGroupBy) { PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart }; EXPECT_TRUE(checkResult(query, expected)); @@ -103,9 +103,9 @@ TEST_F(GroupByValidatorTest, TestGroupBy) { PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart }; EXPECT_TRUE(checkResult(query, expected)); @@ -126,9 +126,9 @@ TEST_F(GroupByValidatorTest, TestGroupBy) { PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart }; EXPECT_TRUE(checkResult(query, expected)); @@ -150,9 +150,9 @@ TEST_F(GroupByValidatorTest, TestGroupBy) { PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart }; EXPECT_TRUE(checkResult(query, expected)); @@ -174,9 +174,9 @@ TEST_F(GroupByValidatorTest, TestGroupBy) { PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart }; EXPECT_TRUE(checkResult(query, expected)); @@ -209,9 +209,9 @@ TEST_F(GroupByValidatorTest, VariableTest) { PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart }; EXPECT_TRUE(checkResult(query, expected)); @@ -233,9 +233,9 @@ TEST_F(GroupByValidatorTest, VariableTest) { PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart }; EXPECT_TRUE(checkResult(query, expected)); diff --git a/src/validator/test/MatchValidatorTest.cpp b/src/validator/test/MatchValidatorTest.cpp index 8cdf030bd..6dcb178b2 100644 --- a/src/validator/test/MatchValidatorTest.cpp +++ b/src/validator/test/MatchValidatorTest.cpp @@ -53,16 +53,16 @@ TEST_F(MatchValidatorTest, SeekByTagIndex) { PlanNode::Kind::kFilter, PlanNode::Kind::kProject, PlanNode::Kind::kInnerJoin, - PlanNode::Kind::kProject, PlanNode::Kind::kFilter, - PlanNode::Kind::kGetVertices, - PlanNode::Kind::kDedup, PlanNode::Kind::kProject, - PlanNode::Kind::kFilter, PlanNode::Kind::kProject, PlanNode::Kind::kFilter, + PlanNode::Kind::kFilter, + PlanNode::Kind::kGetVertices, PlanNode::Kind::kGetNeighbors, PlanNode::Kind::kDedup, + PlanNode::Kind::kDedup, + PlanNode::Kind::kProject, PlanNode::Kind::kProject, PlanNode::Kind::kIndexScan, PlanNode::Kind::kStart}; @@ -83,14 +83,14 @@ TEST_F(MatchValidatorTest, SeekByEdgeIndex) { PlanNode::Kind::kFilter, PlanNode::Kind::kProject, PlanNode::Kind::kInnerJoin, - PlanNode::Kind::kProject, - PlanNode::Kind::kGetVertices, - PlanNode::Kind::kDedup, - PlanNode::Kind::kProject, PlanNode::Kind::kFilter, PlanNode::Kind::kProject, + PlanNode::Kind::kProject, + PlanNode::Kind::kGetVertices, PlanNode::Kind::kGetNeighbors, PlanNode::Kind::kDedup, + PlanNode::Kind::kDedup, + PlanNode::Kind::kProject, PlanNode::Kind::kProject, PlanNode::Kind::kIndexScan, PlanNode::Kind::kStart}; @@ -207,14 +207,14 @@ TEST_F(MatchValidatorTest, groupby) { PlanNode::Kind::kFilter, PlanNode::Kind::kProject, PlanNode::Kind::kInnerJoin, - PlanNode::Kind::kProject, - PlanNode::Kind::kGetVertices, - PlanNode::Kind::kDedup, - PlanNode::Kind::kProject, PlanNode::Kind::kFilter, PlanNode::Kind::kProject, + PlanNode::Kind::kProject, + PlanNode::Kind::kGetVertices, PlanNode::Kind::kFilter, + PlanNode::Kind::kDedup, PlanNode::Kind::kGetNeighbors, + PlanNode::Kind::kProject, PlanNode::Kind::kDedup, PlanNode::Kind::kProject, PlanNode::Kind::kIndexScan, @@ -235,14 +235,14 @@ TEST_F(MatchValidatorTest, groupby) { PlanNode::Kind::kFilter, PlanNode::Kind::kProject, PlanNode::Kind::kInnerJoin, - PlanNode::Kind::kProject, - PlanNode::Kind::kGetVertices, - PlanNode::Kind::kDedup, - PlanNode::Kind::kProject, PlanNode::Kind::kFilter, PlanNode::Kind::kProject, + PlanNode::Kind::kProject, + PlanNode::Kind::kGetVertices, PlanNode::Kind::kFilter, + PlanNode::Kind::kDedup, PlanNode::Kind::kGetNeighbors, + PlanNode::Kind::kProject, PlanNode::Kind::kDedup, PlanNode::Kind::kProject, PlanNode::Kind::kIndexScan, @@ -264,14 +264,14 @@ TEST_F(MatchValidatorTest, groupby) { PlanNode::Kind::kFilter, PlanNode::Kind::kProject, PlanNode::Kind::kInnerJoin, - PlanNode::Kind::kProject, - PlanNode::Kind::kGetVertices, - PlanNode::Kind::kDedup, - PlanNode::Kind::kProject, PlanNode::Kind::kFilter, PlanNode::Kind::kProject, + PlanNode::Kind::kProject, + PlanNode::Kind::kGetVertices, PlanNode::Kind::kFilter, + PlanNode::Kind::kDedup, PlanNode::Kind::kGetNeighbors, + PlanNode::Kind::kProject, PlanNode::Kind::kDedup, PlanNode::Kind::kProject, PlanNode::Kind::kIndexScan, @@ -296,14 +296,14 @@ TEST_F(MatchValidatorTest, groupby) { PlanNode::Kind::kFilter, PlanNode::Kind::kProject, PlanNode::Kind::kInnerJoin, - PlanNode::Kind::kProject, - PlanNode::Kind::kGetVertices, - PlanNode::Kind::kDedup, - PlanNode::Kind::kProject, PlanNode::Kind::kFilter, PlanNode::Kind::kProject, + PlanNode::Kind::kProject, + PlanNode::Kind::kGetVertices, PlanNode::Kind::kFilter, + PlanNode::Kind::kDedup, PlanNode::Kind::kGetNeighbors, + PlanNode::Kind::kProject, PlanNode::Kind::kDedup, PlanNode::Kind::kProject, PlanNode::Kind::kIndexScan, @@ -327,14 +327,14 @@ TEST_F(MatchValidatorTest, groupby) { PlanNode::Kind::kFilter, PlanNode::Kind::kProject, PlanNode::Kind::kInnerJoin, - PlanNode::Kind::kProject, - PlanNode::Kind::kGetVertices, - PlanNode::Kind::kDedup, - PlanNode::Kind::kProject, PlanNode::Kind::kFilter, PlanNode::Kind::kProject, + PlanNode::Kind::kProject, + PlanNode::Kind::kGetVertices, PlanNode::Kind::kFilter, + PlanNode::Kind::kDedup, PlanNode::Kind::kGetNeighbors, + PlanNode::Kind::kProject, PlanNode::Kind::kDedup, PlanNode::Kind::kProject, PlanNode::Kind::kIndexScan, @@ -360,14 +360,14 @@ TEST_F(MatchValidatorTest, groupby) { PlanNode::Kind::kFilter, PlanNode::Kind::kProject, PlanNode::Kind::kInnerJoin, - PlanNode::Kind::kProject, - PlanNode::Kind::kGetVertices, - PlanNode::Kind::kDedup, - PlanNode::Kind::kProject, PlanNode::Kind::kFilter, PlanNode::Kind::kProject, + PlanNode::Kind::kProject, + PlanNode::Kind::kGetVertices, PlanNode::Kind::kFilter, + PlanNode::Kind::kDedup, PlanNode::Kind::kGetNeighbors, + PlanNode::Kind::kProject, PlanNode::Kind::kDedup, PlanNode::Kind::kProject, PlanNode::Kind::kIndexScan, @@ -396,14 +396,14 @@ TEST_F(MatchValidatorTest, groupby) { PlanNode::Kind::kFilter, PlanNode::Kind::kProject, PlanNode::Kind::kInnerJoin, - PlanNode::Kind::kProject, - PlanNode::Kind::kGetVertices, - PlanNode::Kind::kDedup, - PlanNode::Kind::kProject, PlanNode::Kind::kFilter, PlanNode::Kind::kProject, + PlanNode::Kind::kProject, + PlanNode::Kind::kGetVertices, PlanNode::Kind::kFilter, + PlanNode::Kind::kDedup, PlanNode::Kind::kGetNeighbors, + PlanNode::Kind::kProject, PlanNode::Kind::kDedup, PlanNode::Kind::kProject, PlanNode::Kind::kIndexScan, @@ -431,14 +431,14 @@ TEST_F(MatchValidatorTest, groupby) { PlanNode::Kind::kFilter, PlanNode::Kind::kProject, PlanNode::Kind::kInnerJoin, - PlanNode::Kind::kProject, - PlanNode::Kind::kGetVertices, - PlanNode::Kind::kDedup, - PlanNode::Kind::kProject, PlanNode::Kind::kFilter, PlanNode::Kind::kProject, + PlanNode::Kind::kProject, + PlanNode::Kind::kGetVertices, PlanNode::Kind::kFilter, + PlanNode::Kind::kDedup, PlanNode::Kind::kGetNeighbors, + PlanNode::Kind::kProject, PlanNode::Kind::kDedup, PlanNode::Kind::kProject, PlanNode::Kind::kIndexScan, @@ -467,21 +467,21 @@ TEST_F(MatchValidatorTest, groupby) { PlanNode::Kind::kFilter, PlanNode::Kind::kProject, PlanNode::Kind::kInnerJoin, + PlanNode::Kind::kInnerJoin, PlanNode::Kind::kProject, + PlanNode::Kind::kFilter, + PlanNode::Kind::kFilter, PlanNode::Kind::kGetVertices, - PlanNode::Kind::kDedup, PlanNode::Kind::kProject, - PlanNode::Kind::kInnerJoin, - PlanNode::Kind::kFilter, PlanNode::Kind::kProject, - PlanNode::Kind::kGetNeighbors, PlanNode::Kind::kDedup, - PlanNode::Kind::kProject, PlanNode::Kind::kFilter, + PlanNode::Kind::kGetNeighbors, PlanNode::Kind::kProject, - PlanNode::Kind::kFilter, PlanNode::Kind::kGetNeighbors, PlanNode::Kind::kDedup, + PlanNode::Kind::kDedup, + PlanNode::Kind::kProject, PlanNode::Kind::kProject, PlanNode::Kind::kIndexScan, PlanNode::Kind::kStart}; @@ -500,14 +500,14 @@ TEST_F(MatchValidatorTest, with) { PlanNode::Kind::kFilter, PlanNode::Kind::kProject, PlanNode::Kind::kInnerJoin, - PlanNode::Kind::kProject, - PlanNode::Kind::kGetVertices, - PlanNode::Kind::kDedup, - PlanNode::Kind::kProject, PlanNode::Kind::kFilter, PlanNode::Kind::kProject, + PlanNode::Kind::kProject, + PlanNode::Kind::kGetVertices, PlanNode::Kind::kFilter, + PlanNode::Kind::kDedup, PlanNode::Kind::kGetNeighbors, + PlanNode::Kind::kProject, PlanNode::Kind::kDedup, PlanNode::Kind::kProject, PlanNode::Kind::kIndexScan, diff --git a/src/validator/test/QueryValidatorTest.cpp b/src/validator/test/QueryValidatorTest.cpp index 8b2154830..ad2bbd0d8 100644 --- a/src/validator/test/QueryValidatorTest.cpp +++ b/src/validator/test/QueryValidatorTest.cpp @@ -62,6 +62,7 @@ TEST_F(QueryValidatorTest, GoZeroStep) { PK::kProject, PK::kInnerJoin, PK::kProject, + PK::kStart, PK::kGetNeighbors, PK::kDedup, PK::kProject, @@ -157,6 +158,8 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kProject, PK::kInnerJoin, PK::kInnerJoin, + PK::kStart, + PK::kStart, PK::kProject, PK::kGetNeighbors, PK::kLoop, @@ -167,6 +170,7 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kDedup, PK::kInnerJoin, PK::kProject, + PK::kStart, PK::kDedup, PK::kProject, PK::kProject, @@ -186,6 +190,7 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kProject, PK::kInnerJoin, PK::kProject, + PK::kStart, PK::kGetNeighbors, PK::kDedup, PK::kProject, @@ -206,6 +211,7 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kProject, PK::kInnerJoin, PK::kProject, + PK::kStart, PK::kGetNeighbors, PK::kDedup, PK::kProject, @@ -221,6 +227,7 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kProject, PK::kInnerJoin, PK::kProject, + PK::kStart, PK::kGetNeighbors, PK::kDedup, PK::kProject, @@ -239,6 +246,7 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kFilter, PK::kInnerJoin, PK::kProject, + PK::kStart, PK::kGetNeighbors, PK::kDedup, PK::kProject, @@ -259,6 +267,7 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kFilter, PK::kInnerJoin, PK::kProject, + PK::kStart, PK::kGetNeighbors, PK::kDedup, PK::kProject, @@ -275,6 +284,8 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kProject, PK::kInnerJoin, PK::kInnerJoin, + PK::kStart, + PK::kStart, PK::kProject, PK::kGetNeighbors, PK::kLoop, @@ -285,6 +296,7 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kDedup, PK::kInnerJoin, PK::kProject, + PK::kStart, PK::kDedup, PK::kProject, PK::kProject, @@ -306,6 +318,8 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kFilter, PK::kInnerJoin, PK::kInnerJoin, + PK::kStart, + PK::kStart, PK::kProject, PK::kGetNeighbors, PK::kLoop, @@ -316,6 +330,7 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kDedup, PK::kInnerJoin, PK::kProject, + PK::kStart, PK::kDedup, PK::kProject, PK::kProject, @@ -339,6 +354,8 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kFilter, PK::kInnerJoin, PK::kInnerJoin, + PK::kStart, + PK::kStart, PK::kProject, PK::kGetNeighbors, PK::kLoop, @@ -349,6 +366,7 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kDedup, PK::kInnerJoin, PK::kProject, + PK::kStart, PK::kDedup, PK::kProject, PK::kProject, @@ -370,18 +388,19 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kProject, PK::kInnerJoin, PK::kLeftJoin, + PK::kStart, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kDedup, PK::kProject, PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart, }; EXPECT_TRUE(checkResult(query, expected)); @@ -397,18 +416,19 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kProject, PK::kInnerJoin, PK::kLeftJoin, + PK::kStart, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kDedup, PK::kProject, PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart, }; EXPECT_TRUE(checkResult(query, expected)); @@ -422,10 +442,11 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kProject, PK::kInnerJoin, PK::kLeftJoin, + PK::kStart, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kDedup, PK::kProject, PK::kProject, @@ -443,11 +464,13 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kProject, PK::kInnerJoin, PK::kInnerJoin, + PK::kStart, + PK::kStart, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kLoop, PK::kDedup, PK::kDedup, @@ -456,6 +479,7 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kDedup, PK::kInnerJoin, PK::kProject, + PK::kStart, PK::kDedup, PK::kProject, PK::kProject, @@ -463,11 +487,11 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kDedup, PK::kProject, PK::kProject, + PK::kProject, + PK::kGetNeighbors, PK::kGetVertices, PK::kGetNeighbors, - PK::kProject, PK::kStart, - PK::kGetNeighbors, PK::kStart, }; EXPECT_TRUE(checkResult(query, expected)); @@ -483,11 +507,13 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kProject, PK::kInnerJoin, PK::kInnerJoin, + PK::kStart, + PK::kStart, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kLoop, PK::kDedup, PK::kDedup, @@ -496,6 +522,7 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kDedup, PK::kInnerJoin, PK::kProject, + PK::kStart, PK::kDedup, PK::kProject, PK::kProject, @@ -503,11 +530,11 @@ TEST_F(QueryValidatorTest, GoWithPipe) { PK::kDedup, PK::kProject, PK::kProject, + PK::kProject, + PK::kGetNeighbors, PK::kGetVertices, PK::kGetNeighbors, - PK::kProject, PK::kStart, - PK::kGetNeighbors, PK::kStart, }; EXPECT_TRUE(checkResult(query, expected)); @@ -536,10 +563,11 @@ TEST_F(QueryValidatorTest, GoWithVariable) { PK::kProject, PK::kInnerJoin, PK::kLeftJoin, + PK::kStart, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kDedup, PK::kProject, PK::kProject, @@ -558,9 +586,9 @@ TEST_F(QueryValidatorTest, GoReversely) { PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart, }; EXPECT_TRUE(checkResult(query, expected)); @@ -572,9 +600,9 @@ TEST_F(QueryValidatorTest, GoReversely) { PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kLoop, PK::kStart, PK::kDedup, @@ -603,9 +631,9 @@ TEST_F(QueryValidatorTest, GoBidirectly) { PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart, }; EXPECT_TRUE(checkResult(query, expected)); @@ -666,9 +694,9 @@ TEST_F(QueryValidatorTest, GoOneStep) { PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart, }; EXPECT_TRUE(checkResult(query, expected)); @@ -681,9 +709,9 @@ TEST_F(QueryValidatorTest, GoOneStep) { PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart, }; EXPECT_TRUE(checkResult(query, expected)); @@ -698,9 +726,9 @@ TEST_F(QueryValidatorTest, GoOneStep) { PK::kFilter, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart, }; EXPECT_TRUE(checkResult(query, expected)); @@ -717,9 +745,9 @@ TEST_F(QueryValidatorTest, GoOneStep) { PK::kFilter, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart, }; EXPECT_TRUE(checkResult(query, expected)); @@ -773,6 +801,7 @@ TEST_F(QueryValidatorTest, GoOneStep) { PK::kProject, PK::kInnerJoin, PK::kProject, + PK::kStart, PK::kGetNeighbors, PK::kDedup, PK::kProject, @@ -819,9 +848,9 @@ TEST_F(QueryValidatorTest, GoOverAll) { PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kGetNeighbors, + PK::kGetVertices, PK::kStart, }; EXPECT_TRUE(checkResult(query, expected)); @@ -837,12 +866,14 @@ TEST_F(QueryValidatorTest, OutputToAPipe) { PK::kProject, PK::kInnerJoin, PK::kProject, + PK::kStart, PK::kGetNeighbors, PK::kDedup, PK::kProject, PK::kProject, PK::kInnerJoin, PK::kProject, + PK::kStart, PK::kGetNeighbors, PK::kDedup, PK::kProject, @@ -899,9 +930,9 @@ TEST_F(QueryValidatorTest, GoMToN) { PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kDedup, + PK::kGetVertices, PK::kProject, PK::kGetNeighbors, PK::kStart, @@ -966,9 +997,9 @@ TEST_F(QueryValidatorTest, GoMToN) { PK::kProject, PK::kLeftJoin, PK::kProject, - PK::kGetVertices, PK::kProject, PK::kDedup, + PK::kGetVertices, PK::kProject, PK::kGetNeighbors, PK::kStart, @@ -988,14 +1019,16 @@ TEST_F(QueryValidatorTest, GoMToN) { PK::kInnerJoin, PK::kDedup, PK::kInnerJoin, + PK::kStart, PK::kProject, + PK::kDedup, PK::kProject, PK::kProject, - PK::kDedup, - PK::kGetNeighbors, PK::kProject, - PK::kStart, + PK::kGetNeighbors, PK::kInnerJoin, + PK::kStart, + PK::kStart, PK::kDedup, PK::kProject, PK::kDedup, @@ -1248,14 +1281,14 @@ TEST_F(QueryValidatorTest, TestMatch) { PK::kFilter, PK::kProject, PK::kInnerJoin, - PK::kProject, - PK::kGetVertices, - PK::kDedup, - PK::kProject, PK::kFilter, PK::kProject, + PK::kProject, + PK::kGetVertices, PK::kFilter, + PK::kDedup, PK::kGetNeighbors, + PK::kProject, PK::kDedup, PK::kProject, PK::kIndexScan, @@ -1273,21 +1306,21 @@ TEST_F(QueryValidatorTest, TestMatch) { PK::kFilter, PK::kProject, PK::kInnerJoin, + PK::kInnerJoin, PK::kProject, + PK::kFilter, + PK::kFilter, PK::kGetVertices, - PK::kDedup, PK::kProject, - PK::kInnerJoin, - PK::kFilter, PK::kProject, - PK::kGetNeighbors, PK::kDedup, - PK::kProject, PK::kFilter, + PK::kGetNeighbors, PK::kProject, - PK::kFilter, PK::kGetNeighbors, PK::kDedup, + PK::kDedup, + PK::kProject, PK::kProject, PK::kIndexScan, PK::kStart, @@ -1304,14 +1337,14 @@ TEST_F(QueryValidatorTest, TestMatch) { PK::kFilter, PK::kProject, PK::kInnerJoin, - PK::kProject, - PK::kGetVertices, - PK::kDedup, - PK::kProject, PK::kFilter, PK::kProject, + PK::kProject, + PK::kGetVertices, PK::kGetNeighbors, PK::kDedup, + PK::kDedup, + PK::kProject, PK::kProject, PK::kPassThrough, PK::kStart, @@ -1322,51 +1355,53 @@ TEST_F(QueryValidatorTest, TestMatch) { std::string query = "MATCH (v1)-[e:serve*2..3{start_year: 2000}]-(v2) " "WHERE id(v1) == \"LeBron James\"" "RETURN v1, v2"; - std::vector expected = {PK::kProject, - PK::kFilter, - PK::kFilter, - PK::kProject, - PK::kInnerJoin, - PK::kProject, - PK::kGetVertices, - PK::kDedup, - PK::kProject, - PK::kFilter, - PK::kUnionAllVersionVar, - PK::kLoop, - PK::kProject, - PK::kFilter, - PK::kFilter, - PK::kProject, - PK::kGetNeighbors, - PK::kInnerJoin, - PK::kDedup, - PK::kProject, - PK::kProject, - PK::kFilter, - PK::kPassThrough, - PK::kGetNeighbors, - PK::kStart, - PK::kDedup, - PK::kProject, - PK::kStart}; - EXPECT_TRUE(checkResult(query, expected)); - } - { - std::string query = "MATCH p = (n)-[]-(m:person{name:\"LeBron James\"}) RETURN p"; std::vector expected = { PK::kProject, PK::kFilter, + PK::kFilter, PK::kProject, PK::kInnerJoin, + PK::kFilter, PK::kProject, + PK::kUnionAllVersionVar, PK::kGetVertices, + PK::kLoop, PK::kDedup, PK::kProject, PK::kFilter, PK::kProject, PK::kFilter, + PK::kProject, PK::kGetNeighbors, + PK::kInnerJoin, + PK::kDedup, + PK::kStart, + PK::kProject, + PK::kProject, + PK::kFilter, + PK::kPassThrough, + PK::kGetNeighbors, + PK::kStart, + PK::kDedup, + PK::kProject, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "MATCH p = (n)-[]-(m:person{name:\"LeBron James\"}) RETURN p"; + std::vector expected = { + PK::kProject, + PK::kFilter, + PK::kProject, + PK::kInnerJoin, + PK::kFilter, + PK::kProject, + PK::kProject, + PK::kGetVertices, + PK::kFilter, + PK::kDedup, + PK::kGetNeighbors, + PK::kProject, PK::kDedup, PK::kProject, PK::kIndexScan, diff --git a/src/validator/test/SymbolsTest.cpp b/src/validator/test/SymbolsTest.cpp index 493bd16ab..549e57f25 100644 --- a/src/validator/test/SymbolsTest.cpp +++ b/src/validator/test/SymbolsTest.cpp @@ -50,7 +50,16 @@ TEST_F(SymbolsTest, Variables) { auto qctx = std::move(status).value(); EXPECT_NE(qctx, nullptr); auto* symTable = qctx->symTable(); - + { + auto varName = "__Start_22"; + auto* variable = symTable->getVar(varName); + EXPECT_NE(variable, nullptr); + EXPECT_EQ(variable->name, varName); + EXPECT_EQ(variable->type, Value::Type::DATASET); + EXPECT_TRUE(variable->colNames.empty()); + EXPECT_TRUE(checkNodes(variable->readBy, {})); + EXPECT_TRUE(checkNodes(variable->writtenBy, {})); + } { auto varName = "__Start_0"; auto* variable = symTable->getVar(varName); @@ -78,8 +87,8 @@ TEST_F(SymbolsTest, Variables) { EXPECT_EQ(variable->name, varName); EXPECT_EQ(variable->type, Value::Type::DATASET); EXPECT_EQ(variable->colNames, std::vector({"id"})); - EXPECT_TRUE(checkNodes(variable->readBy, {3, 9, 20})); - EXPECT_TRUE(checkNodes(variable->writtenBy, {2})); + EXPECT_TRUE(checkNodes(variable->readBy, {3, 9, 23})); + EXPECT_TRUE(checkNodes(variable->writtenBy, {2, 22})); } { auto varName = "__Project_3"; @@ -98,7 +107,7 @@ TEST_F(SymbolsTest, Variables) { EXPECT_EQ(variable->name, varName); EXPECT_EQ(variable->type, Value::Type::DATASET); EXPECT_EQ(variable->colNames, std::vector({"_vid"})); - EXPECT_TRUE(checkNodes(variable->readBy, {17, 6})); + EXPECT_TRUE(checkNodes(variable->readBy, {18, 6})); EXPECT_TRUE(checkNodes(variable->writtenBy, {8, 4})); } { @@ -118,8 +127,8 @@ TEST_F(SymbolsTest, Variables) { EXPECT_EQ(variable->name, varName); EXPECT_EQ(variable->type, Value::Type::DATASET); EXPECT_EQ(variable->colNames, std::vector({"id", "__COL_1"})); - EXPECT_TRUE(checkNodes(variable->readBy, {13, 16, 19})); - EXPECT_TRUE(checkNodes(variable->writtenBy, {10, 15})); + EXPECT_TRUE(checkNodes(variable->readBy, {14, 17, 21})); + EXPECT_TRUE(checkNodes(variable->writtenBy, {10, 13, 16, 20})); } { auto varName = "__Start_5"; @@ -168,91 +177,102 @@ TEST_F(SymbolsTest, Variables) { EXPECT_EQ(variable->name, varName); EXPECT_EQ(variable->type, Value::Type::DATASET); EXPECT_EQ(variable->colNames, std::vector({"__COL_2", "TRACK_DST_VID"})); - EXPECT_TRUE(checkNodes(variable->readBy, {13})); + EXPECT_TRUE(checkNodes(variable->readBy, {14})); EXPECT_TRUE(checkNodes(variable->writtenBy, {12})); } { - auto varName = "__InnerJoin_13"; + auto varName = "__InnerJoin_14"; auto* variable = symTable->getVar(varName); EXPECT_NE(variable, nullptr); EXPECT_EQ(variable->name, varName); EXPECT_EQ(variable->type, Value::Type::DATASET); EXPECT_EQ(variable->colNames, std::vector({"id", "__COL_1", "__COL_2", "TRACK_DST_VID"})); - EXPECT_TRUE(checkNodes(variable->readBy, {14})); - EXPECT_TRUE(checkNodes(variable->writtenBy, {13})); + EXPECT_TRUE(checkNodes(variable->readBy, {15})); + EXPECT_TRUE(checkNodes(variable->writtenBy, {14})); } { - auto varName = "__Project_14"; + auto varName = "__Project_15"; auto* variable = symTable->getVar(varName); EXPECT_NE(variable, nullptr); EXPECT_EQ(variable->name, varName); EXPECT_EQ(variable->type, Value::Type::DATASET); EXPECT_EQ(variable->colNames, std::vector({"id", "__COL_1"})); - EXPECT_TRUE(checkNodes(variable->readBy, {15})); - EXPECT_TRUE(checkNodes(variable->writtenBy, {14})); + EXPECT_TRUE(checkNodes(variable->readBy, {16})); + EXPECT_TRUE(checkNodes(variable->writtenBy, {15})); } { - auto varName = "__Loop_16"; + auto varName = "__Loop_17"; auto* variable = symTable->getVar(varName); EXPECT_NE(variable, nullptr); EXPECT_EQ(variable->name, varName); EXPECT_EQ(variable->type, Value::Type::DATASET); EXPECT_EQ(variable->colNames, std::vector({})); EXPECT_TRUE(checkNodes(variable->readBy, {})); - EXPECT_TRUE(checkNodes(variable->writtenBy, {16})); + EXPECT_TRUE(checkNodes(variable->writtenBy, {17})); } { - auto varName = "__GetNeighbors_17"; + auto varName = "__GetNeighbors_18"; auto* variable = symTable->getVar(varName); EXPECT_NE(variable, nullptr); EXPECT_EQ(variable->name, varName); EXPECT_EQ(variable->type, Value::Type::DATASET); EXPECT_TRUE(variable->colNames.empty()); - EXPECT_TRUE(checkNodes(variable->readBy, {18})); - EXPECT_TRUE(checkNodes(variable->writtenBy, {17})); + EXPECT_TRUE(checkNodes(variable->readBy, {19})); + EXPECT_TRUE(checkNodes(variable->writtenBy, {18})); } { - auto varName = "__Project_18"; + auto varName = "__Project_19"; auto* variable = symTable->getVar(varName); EXPECT_NE(variable, nullptr); EXPECT_EQ(variable->name, varName); EXPECT_EQ(variable->type, Value::Type::DATASET); EXPECT_EQ(variable->colNames, std::vector({"__COL_0", "_vid"})); - EXPECT_TRUE(checkNodes(variable->readBy, {19})); - EXPECT_TRUE(checkNodes(variable->writtenBy, {18})); + EXPECT_TRUE(checkNodes(variable->readBy, {21})); + EXPECT_TRUE(checkNodes(variable->writtenBy, {19})); + } + { + auto varName = "__Start_20"; + auto* variable = symTable->getVar(varName); + EXPECT_NE(variable, nullptr); + EXPECT_EQ(variable->name, varName); + EXPECT_EQ(variable->type, Value::Type::DATASET); + EXPECT_TRUE(variable->colNames.empty()); + EXPECT_TRUE(checkNodes(variable->readBy, {})); + EXPECT_TRUE(checkNodes(variable->writtenBy, {})); } { - auto varName = "__InnerJoin_19"; + auto varName = "__InnerJoin_21"; auto* variable = symTable->getVar(varName); EXPECT_NE(variable, nullptr); EXPECT_EQ(variable->name, varName); EXPECT_EQ(variable->type, Value::Type::DATASET); EXPECT_EQ(variable->colNames, std::vector({"id", "__COL_1", "__COL_0", "_vid"})); - EXPECT_TRUE(checkNodes(variable->readBy, {20})); - EXPECT_TRUE(checkNodes(variable->writtenBy, {19})); + EXPECT_TRUE(checkNodes(variable->readBy, {23})); + EXPECT_TRUE(checkNodes(variable->writtenBy, {21})); } + { - auto varName = "__InnerJoin_20"; + auto varName = "__InnerJoin_23"; auto* variable = symTable->getVar(varName); EXPECT_NE(variable, nullptr); EXPECT_EQ(variable->name, varName); EXPECT_EQ(variable->type, Value::Type::DATASET); EXPECT_EQ(variable->colNames, std::vector({"id", "__COL_1", "__COL_0", "_vid", "id"})); - EXPECT_TRUE(checkNodes(variable->readBy, {21})); - EXPECT_TRUE(checkNodes(variable->writtenBy, {20})); + EXPECT_TRUE(checkNodes(variable->readBy, {24})); + EXPECT_TRUE(checkNodes(variable->writtenBy, {23})); } { - auto varName = "__Project_21"; + auto varName = "__Project_24"; auto* variable = symTable->getVar(varName); EXPECT_NE(variable, nullptr); EXPECT_EQ(variable->name, varName); EXPECT_EQ(variable->type, Value::Type::DATASET); EXPECT_EQ(variable->colNames, std::vector({"like._dst"})); EXPECT_TRUE(checkNodes(variable->readBy, {})); - EXPECT_TRUE(checkNodes(variable->writtenBy, {21})); + EXPECT_TRUE(checkNodes(variable->writtenBy, {24})); } } } From 2ad5c0366328961d314f7fb99e0e40725d679adb Mon Sep 17 00:00:00 2001 From: William Chen <13495049+CPWstatic@users.noreply.github.com> Date: Mon, 19 Jul 2021 10:18:20 +0800 Subject: [PATCH 09/12] Fix test. --- src/optimizer/OptRule.cpp | 48 +++++++++++++++ src/optimizer/OptRule.h | 4 ++ .../optimizer/CombineFilterRule.feature | 14 ++--- .../features/optimizer/IndexScanRule.feature | 32 +++++----- .../MergeGetNbrsDedupProjectRule.feature | 42 +++++++------- .../PushFilterDownLeftJoinRule.feature | 58 ++++++++++--------- 6 files changed, 127 insertions(+), 71 deletions(-) diff --git a/src/optimizer/OptRule.cpp b/src/optimizer/OptRule.cpp index 2bc112880..0a404d59e 100644 --- a/src/optimizer/OptRule.cpp +++ b/src/optimizer/OptRule.cpp @@ -32,6 +32,28 @@ const PlanNode *MatchedResult::planNode(const std::vector &pos) const { return DCHECK_NOTNULL(result->node)->node(); } +std::string MatchedResult::toString() const { + std::stringstream ss; + std::queue queue; + std::unordered_set visited; + queue.emplace(this); + + while (!queue.empty()) { + auto* n = queue.front(); + queue.pop(); + visited.emplace(n); + ss << n->node->node()->outputVar() << ","; + + for (auto& dep : n->dependencies) { + queue.emplace(&dep); + } + } + + auto ret = ss.str(); + ret.pop_back(); + return ret; +} + Pattern Pattern::create(graph::PlanNode::Kind kind, std::initializer_list patterns) { Pattern pattern; pattern.kind_ = kind; @@ -77,7 +99,30 @@ StatusOr Pattern::match(const OptGroup *group) const { return Status::Error(); } +std::string Pattern::toString() const { + std::stringstream ss; + std::queue queue; + std::unordered_set visited; + queue.emplace(this); + while (!queue.empty()) { + auto& p = queue.front(); + queue.pop(); + visited.emplace(p); + + ss << p->kind_ << ","; + for (auto& pattern : p->dependencies_) { + queue.emplace(&pattern); + } + } + + auto ret = ss.str(); + ret.pop_back(); + return ss.str(); +} + StatusOr OptRule::match(OptContext *ctx, const OptGroupNode *groupNode) const { + VLOG(1) << "Apply rule: " + this->toString() + << " to node: " << groupNode->node()->outputVar(); const auto &pattern = this->pattern(); auto status = pattern.match(groupNode); NG_RETURN_IF_ERROR(status); @@ -85,6 +130,9 @@ StatusOr OptRule::match(OptContext *ctx, const OptGroupNode *grou if (!this->match(ctx, matched)) { return Status::Error(); } + VLOG(1) << "Hit rule: " + this->toString() + << " pattern: " << pattern.toString() + << " subtree of plan: " << matched.toString(); return matched; } diff --git a/src/optimizer/OptRule.h b/src/optimizer/OptRule.h index ffb920ef4..d8f135bc3 100644 --- a/src/optimizer/OptRule.h +++ b/src/optimizer/OptRule.h @@ -41,6 +41,8 @@ struct MatchedResult { // {0, 1, 0} | this->dependencies[1].dependencies[0] // {0, 1, 0, 1} | this->dependencies[1].dependencies[0].dependencies[1] const graph::PlanNode *planNode(const std::vector &pos = {}) const; + + std::string toString() const; }; class Pattern final { @@ -49,6 +51,8 @@ class Pattern final { StatusOr match(const OptGroupNode *groupNode) const; + std::string toString() const; + private: Pattern() = default; StatusOr match(const OptGroup *group) const; diff --git a/tests/tck/features/optimizer/CombineFilterRule.feature b/tests/tck/features/optimizer/CombineFilterRule.feature index b07d5ee51..5997801a5 100644 --- a/tests/tck/features/optimizer/CombineFilterRule.feature +++ b/tests/tck/features/optimizer/CombineFilterRule.feature @@ -21,15 +21,15 @@ Feature: combine filters | ("Jason Kidd" :player{age: 45, name: "Jason Kidd"}) | ("Steve Nash" :player{age: 45, name: "Steve Nash"}) | And the execution plan should be: | id | name | dependencies | operator info | - | 16 | Project | 18 | | - | 18 | Filter | 13 | {"condition": "(($v.age>40) AND ($n.age>42) AND !(hasSameEdgeInPath($-.__COL_0)))"} | + | 16 | Project | 19 | | + | 19 | Filter | 13 | {"condition": "(($v.age>40) AND ($n.age>42) AND !(hasSameEdgeInPath($-.__COL_0)))"} | | 13 | Project | 12 | | - | 12 | InnerJoin | 11 | | - | 11 | Project | 20 | | - | 20 | GetVertices | 7 | | + | 12 | InnerJoin | 7, 11 | | | 7 | Filter | 6 | | | 6 | Project | 5 | | - | 5 | Filter | 22 | | - | 22 | GetNeighbors | 17 | | + | 5 | Filter | 23 | | + | 23 | GetNeighbors | 17 | | | 17 | IndexScan | 0 | | | 0 | Start | | | + | 11 | Project | 21 | | + | 21 | GetVertices | 7 | | diff --git a/tests/tck/features/optimizer/IndexScanRule.feature b/tests/tck/features/optimizer/IndexScanRule.feature index 214a2eaba..8138e1ff0 100644 --- a/tests/tck/features/optimizer/IndexScanRule.feature +++ b/tests/tck/features/optimizer/IndexScanRule.feature @@ -83,18 +83,18 @@ Feature: Match index selection | ("Aron Baynes" :player{age: 32, name: "Aron Baynes"}) | ("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | And the execution plan should be: | id | name | dependencies | operator info | - | 16 | Project | 19 | | - | 19 | Filter | 13 | { "condition": "((($v.name<=\"Aron Baynes\") OR ($n.age>45)) AND !(hasSameEdgeInPath($-.__COL_0)))"} | + | 16 | Project | 18 | | + | 18 | Filter | 13 | { "condition": "((($v.name<=\"Aron Baynes\") OR ($n.age>45)) AND !(hasSameEdgeInPath($-.__COL_0)))"} | | 13 | Project | 12 | | - | 12 | InnerJoin | 11 | | - | 11 | Project | 21 | | - | 21 | GetVertices | 7 | | + | 12 | InnerJoin | 7, 11 | | | 7 | Filter | 6 | | | 6 | Project | 5 | | - | 5 | Filter | 23 | | - | 23 | GetNeighbors | 17 | | - | 17 | IndexScan | 0 | | + | 5 | Filter | 22 | | + | 22 | GetNeighbors | 1 | | + | 1 | IndexScan | 0 | | | 0 | Start | | | + | 11 | Project | 20 | | + | 20 | GetVertices | 7 | | # This is actually the optimization for another optRule, # but it is necessary to ensure that the current optimization does not destroy this scenario # and it can be considered in the subsequent refactoring @@ -114,15 +114,15 @@ Feature: Match index selection | 81 | And the execution plan should be: | id | name | dependencies | operator info | - | 16 | Aggregate | 18 | | - | 18 | Filter | 13 | | + | 16 | Aggregate | 17 | | + | 17 | Filter | 13 | | | 13 | Project | 12 | | - | 12 | InnerJoin | 11 | | - | 11 | Project | 20 | | - | 20 | GetVertices | 7 | | + | 12 | InnerJoin | 7, 11 | | | 7 | Filter | 6 | | | 6 | Project | 5 | | - | 5 | Filter | 22 | | - | 22 | GetNeighbors | 17 | | - | 17 | IndexScan | 0 | | + | 5 | Filter | 21 | | + | 21 | GetNeighbors | 1 | | + | 1 | IndexScan | 0 | | | 0 | Start | | | + | 11 | Project | 19 | | + | 19 | GetVertices | 7 | | diff --git a/tests/tck/features/optimizer/MergeGetNbrsDedupProjectRule.feature b/tests/tck/features/optimizer/MergeGetNbrsDedupProjectRule.feature index a141a10c2..b4d3bc029 100644 --- a/tests/tck/features/optimizer/MergeGetNbrsDedupProjectRule.feature +++ b/tests/tck/features/optimizer/MergeGetNbrsDedupProjectRule.feature @@ -19,24 +19,24 @@ Feature: merge get neighbors, dedup and project rule | "Tony Parker" | | "Tim Duncan" | And the execution plan should be: - | id | name | dependencies | operator info | - | 0 | Project | 1 | | - | 1 | Filter | 2 | | - | 2 | Project | 3 | | - | 3 | InnerJoin | 4 | | - | 4 | Project | 5 | | - | 5 | GetVertices | 6 | {"dedup": "true"} | - | 6 | Filter | 7 | | - | 7 | UnionAllVersionVar | 8 | | - | 8 | Loop | 15 | {"loopBody": "9"} | - | 9 | Filter | 10 | | - | 10 | Project | 11 | | - | 11 | InnerJoin | 12 | {"inputVar": {"rightVar":{"__Project_11":"0"}}} | - | 12 | Project | 13 | | - | 13 | GetNeighbors | 14 | {"dedup": "true"} | - | 14 | Start | | | - | 15 | Project | 16 | | - | 16 | Filter | 17 | | - | 17 | GetVertices | 18 | {"dedup": "true"} | - | 18 | IndexScan | 19 | {"indexCtx": {"columnHints":{"scanType":"PREFIX"}}} | - | 19 | Start | | | + | id | name | dependencies | operator info | + | 25 | Project | 24 | | + | 24 | Filter | 23 | | + | 23 | Project | 22 | | + | 22 | InnerJoin | 17,21 | | + | 17 | Filter | 16 | | + | 16 | UnionAllVersionVar | 15 | | + | 15 | Loop | 6 | {"loopBody": "14"} | + | 14 | Filter | 13 | | + | 13 | Project | 12 | | + | 12 | InnerJoin | 7,11 | | + | 7 | Start | | | + | 11 | Project | 32 | | + | 32 | GetNeighbors | 7 | {"dedup": "true"} | + | 6 | Project | 5 | | + | 5 | Filter | 29 | | + | 29 | GetVertices | 26 | {"dedup": "true"} | + | 26 | IndexScan | 0 | | + | 0 | Start | | | + | 21 | Project | 30 | | + | 30 | GetVertices | 17 | {"dedup": "true"} | diff --git a/tests/tck/features/optimizer/PushFilterDownLeftJoinRule.feature b/tests/tck/features/optimizer/PushFilterDownLeftJoinRule.feature index 23e3208eb..356278699 100644 --- a/tests/tck/features/optimizer/PushFilterDownLeftJoinRule.feature +++ b/tests/tck/features/optimizer/PushFilterDownLeftJoinRule.feature @@ -23,25 +23,28 @@ Feature: Push Filter down LeftJoin rule | "Tim Duncan" | And the execution plan should be: | id | name | dependencies | operator info | - | 24 | Project | 23 | | - | 23 | Filter | 22 | | - | 22 | InnerJoin | 21 | | - | 21 | LeftJoin | 20 | | - | 20 | Project | 19 | | - | 19 | GetVertices | 18 | | - | 18 | Project | 31 | | - | 31 | GetNeighbors | 14 | | - | 14 | Project | 13 | | - | 13 | Filter | 12 | | - | 12 | InnerJoin | 11 | | - | 11 | LeftJoin | 10 | | + | 26 | Project | 25 | | + | 25 | Filter | 24 | | + | 24 | InnerJoin | 22,23 | | + | 22 | LeftJoin | 19,21 | | + | 19 | Project | 32 | | + | 32 | GetNeighbors | 15 | | + | 15 | Project | 14 | | + | 14 | Filter | 13 | | + | 13 | InnerJoin | 11,12 | | + | 11 | LeftJoin | 8,10 | | + | 8 | Project | 31 | | + | 31 | GetNeighbors | 4 | | + | 4 | Project | 3 | | + | 3 | Project | 27 | | + | 27 | TagIndexPrefixScan | 0 | | + | 0 | Start | | | | 10 | Project | 9 | | | 9 | GetVertices | 8 | | - | 8 | Project | 30 | | - | 30 | GetNeighbors | 27 | | - | 27 | Project | 25 | | - | 25 | TagIndexPrefixScan | 0 | | - | 0 | Start | | | + | 12 | Start | | | + | 21 | Project | 20 | | + | 20 | GetVertices | 19 | | + | 23 | Start | | | When profiling query: """ GO FROM "Tony Parker" OVER like @@ -54,13 +57,14 @@ Feature: Push Filter down LeftJoin rule | "Manu Ginobili" | 95 | 41 | | "Tim Duncan" | 95 | 42 | And the execution plan should be: - | id | name | dependencies | operator info | - | 7 | Project | 6 | | - | 6 | Filter | 5 | {"condition" : "($__COL_0>=32)"} | - | 5 | LeftJoin | 8 | | - | 8 | Filter | 4 | {"condition" : "($__Project_2.__COL_1>85)"} | - | 4 | Project | 3 | | - | 3 | GetVertices | 2 | | - | 2 | Project | 1 | | - | 1 | GetNeighbors | 0 | | - | 0 | Start | | | + | id | name | dependencies | operator info | + | 7 | Project | 10 | | + | 10 | Filter | 9 | | + | 9 | LeftJoin | 12,4 | | + | 12 | Project | 11 | | + | 11 | Filter | 1 | | + | 1 | GetNeighbors | 0 | | + | 0 | Start | | | + | 4 | Project | 3 | | + | 3 | GetVertices | 2 | | + | 2 | Project | 1 | | From aee727d6b777f86ce4ab53b9ddeed73c9635de4c Mon Sep 17 00:00:00 2001 From: William Chen <13495049+CPWstatic@users.noreply.github.com> Date: Wed, 21 Jul 2021 17:48:14 +0800 Subject: [PATCH 10/12] Fix compile. --- src/planner/ngql/GoPlanner.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/planner/ngql/GoPlanner.cpp b/src/planner/ngql/GoPlanner.cpp index 9ec571c27..2276f606f 100644 --- a/src/planner/ngql/GoPlanner.cpp +++ b/src/planner/ngql/GoPlanner.cpp @@ -220,7 +220,7 @@ PlanNode* GoPlanner::trackStartVid(PlanNode* left, PlanNode* right) { * | | * Left Project * | - * GetVertices + * GetVertices * | * Dep */ @@ -330,7 +330,7 @@ PlanNode* GoPlanner::buildLastStepJoinPlan(PlanNode* gn, PlanNode* join) { auto* dep = extractSrcEdgePropsFromGN(gn, gn->outputVar()); dep = goCtx_->joinDst ? buildJoinDstPlan(dep, dep) : dep; - PlanNode* left; + PlanNode* left = nullptr; if (goCtx_->joinInput && join != nullptr) { left = StartNode::make(goCtx_->qctx); left->setOutputVar(join->outputVar()); From e58614fbebe8b43f3a7653a71253b05a59814d6d Mon Sep 17 00:00:00 2001 From: William Chen <13495049+CPWstatic@users.noreply.github.com> Date: Fri, 23 Jul 2021 10:06:39 +0800 Subject: [PATCH 11/12] Add more msg. --- tests/common/plan_differ.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/common/plan_differ.py b/tests/common/plan_differ.py index 1d722a40b..b9b80eeb0 100644 --- a/tests/common/plan_differ.py +++ b/tests/common/plan_differ.py @@ -41,7 +41,7 @@ def _diff_plan_node(self, plan_desc, line_num, rows, column_names) -> bool: name_col_idx = column_names.index(self.NAME) if not self._is_same_node(name, expect_node[name_col_idx]): - self._err_msg = f"{name} is not expected {expect_node[name_col_idx]}" + self._err_msg = f"{plan_node_desc} is not expected {expect_node}" return False if self._is_same_node(name, "Loop"): @@ -139,7 +139,7 @@ def _is_subdict(small, big): return dict(big, **small) == big return _is_subdict(extracted_expected_dict, extracted_resp_dict) - + # resp: pair(key, jsonStr) def _convert_jsonStr_to_dict(self, resp, key_list): resp_json_str = '' @@ -194,7 +194,7 @@ def _validate_expect(self, rows, column_names): if self.OP_INFO not in column_names: self._err_msg = "Plan node operator info column is missing in expectde plan" return False - + id_idx_dict = {} # Check node id existence for i in range(len(rows)): From c46e2669d8687660b073734f1d919de86b190b4c Mon Sep 17 00:00:00 2001 From: William Chen <13495049+CPWstatic@users.noreply.github.com> Date: Fri, 30 Jul 2021 14:06:23 +0800 Subject: [PATCH 12/12] Fix filter push down join opt. --- src/planner/ngql/GoPlanner.cpp | 18 ++++--- src/planner/ngql/GoPlanner.h | 2 +- src/planner/plan/Logic.h | 4 +- .../AsyncMsgNotifyBasedScheduler.cpp | 50 +++++++++++++++---- .../PushFilterDownLeftJoinRule.feature | 8 +-- 5 files changed, 60 insertions(+), 22 deletions(-) diff --git a/src/planner/ngql/GoPlanner.cpp b/src/planner/ngql/GoPlanner.cpp index 2276f606f..1b3a63eef 100644 --- a/src/planner/ngql/GoPlanner.cpp +++ b/src/planner/ngql/GoPlanner.cpp @@ -224,14 +224,17 @@ PlanNode* GoPlanner::trackStartVid(PlanNode* left, PlanNode* right) { * | * Dep */ -PlanNode* GoPlanner::buildJoinDstPlan(PlanNode* dep, PlanNode* left) { +PlanNode* GoPlanner::buildJoinDstPlan(PlanNode* left) { auto qctx = goCtx_->qctx; auto* pool = qctx->objPool(); + auto start = StartNode::make(qctx); + start->setOutputVar(left->outputVar()); + start->setInputVar(left->outputVar()); // dst is the last column, columnName is "JOIN_DST_VID" auto* dstExpr = ColumnExpression::make(pool, LAST_COL_INDEX); auto* getVertex = GetVertices::make(qctx, - dep, + start, goCtx_->space.id, dstExpr, buildVertexProps(goCtx_->exprProps.dstTagProps()), @@ -258,7 +261,7 @@ PlanNode* GoPlanner::buildJoinDstPlan(PlanNode* dep, PlanNode* left) { VLOG(1) << join->outputVar() << " hasKey: " << hashKey->toString() << " probeKey: " << probeKey->toString(); - std::vector colNames = dep->colNames(); + std::vector colNames = left->colNames(); colNames.insert(colNames.end(), project->colNames().begin(), project->colNames().end()); join->setColNames(std::move(colNames)); @@ -280,6 +283,7 @@ PlanNode* GoPlanner::buildJoinInputPlan(PlanNode* left) { auto right = StartNode::make(qctx); right->setOutputVar(probeName); + right->setInputVar(probeName); right->setColNames(varPtr->colNames); auto* join = InnerJoin::make(qctx, {left, ExecutionContext::kLatestVersion}, @@ -328,12 +332,13 @@ PlanNode* GoPlanner::buildLastStepJoinPlan(PlanNode* gn, PlanNode* join) { } auto* dep = extractSrcEdgePropsFromGN(gn, gn->outputVar()); - dep = goCtx_->joinDst ? buildJoinDstPlan(dep, dep) : dep; + dep = goCtx_->joinDst ? buildJoinDstPlan(dep) : dep; PlanNode* left = nullptr; if (goCtx_->joinInput && join != nullptr) { left = StartNode::make(goCtx_->qctx); left->setOutputVar(join->outputVar()); + left->setInputVar(join->outputVar()); left->setColNames(join->colNames()); } dep = goCtx_->joinInput ? lastStepJoinInput(left, dep) : dep; @@ -374,7 +379,7 @@ PlanNode* GoPlanner::buildOneStepJoinPlan(PlanNode* gn) { } auto* dep = extractSrcEdgePropsFromGN(gn, gn->outputVar()); - dep = goCtx_->joinDst ? buildJoinDstPlan(dep, dep) : dep; + dep = goCtx_->joinDst ? buildJoinDstPlan(dep) : dep; dep = goCtx_->joinInput ? buildJoinInputPlan(dep) : dep; return dep; @@ -463,6 +468,7 @@ SubPlan GoPlanner::mToNStepsPlan(SubPlan& startVidPlan) { auto left = StartNode::make(qctx); left->setOutputVar(joinLeft->outputVar()); + left->setInputVar(joinLeft->outputVar()); left->setColNames(joinLeft->colNames()); trackVid = trackStartVid(left, joinRight); loopBody = trackVid; @@ -471,7 +477,7 @@ SubPlan GoPlanner::mToNStepsPlan(SubPlan& startVidPlan) { if (joinInput || joinDst) { loopBody = extractSrcEdgePropsFromGN(loopBody, gn->outputVar()); - loopBody = joinDst ? buildJoinDstPlan(loopBody, loopBody) : loopBody; + loopBody = joinDst ? buildJoinDstPlan(loopBody) : loopBody; loopBody = joinInput ? lastStepJoinInput(trackVid, loopBody) : loopBody; loopBody = joinInput ? buildJoinInputPlan(loopBody) : loopBody; } diff --git a/src/planner/ngql/GoPlanner.h b/src/planner/ngql/GoPlanner.h index 81dc2f8ec..47aaaba02 100644 --- a/src/planner/ngql/GoPlanner.h +++ b/src/planner/ngql/GoPlanner.h @@ -57,7 +57,7 @@ class GoPlanner final : public Planner { PlanNode* trackStartVid(PlanNode* left, PlanNode* right); - PlanNode* buildJoinDstPlan(PlanNode* dep, PlanNode* left); + PlanNode* buildJoinDstPlan(PlanNode* left); PlanNode* buildJoinInputPlan(PlanNode* dep); diff --git a/src/planner/plan/Logic.h b/src/planner/plan/Logic.h index 5dfb5e4d7..9b6860676 100644 --- a/src/planner/plan/Logic.h +++ b/src/planner/plan/Logic.h @@ -22,7 +22,9 @@ class StartNode final : public PlanNode { private: explicit StartNode(QueryContext* qctx) - : PlanNode(qctx, Kind::kStart) {} + : PlanNode(qctx, Kind::kStart) { + inputVars_.emplace_back(nullptr); + } void cloneMembers(const StartNode&); }; diff --git a/src/scheduler/AsyncMsgNotifyBasedScheduler.cpp b/src/scheduler/AsyncMsgNotifyBasedScheduler.cpp index 46514317b..fa71abb3b 100644 --- a/src/scheduler/AsyncMsgNotifyBasedScheduler.cpp +++ b/src/scheduler/AsyncMsgNotifyBasedScheduler.cpp @@ -44,17 +44,44 @@ folly::Future AsyncMsgNotifyBasedScheduler::doSchedule(Executor* root) c queue2.push(exe); std::vector receivers; - for (auto* dep : exe->depends()) { - auto notVisited = visited.emplace(dep).second; - if (notVisited) { - queue.push(dep); + if (exe->node()->kind() == PlanNode::Kind::kStart) { + // if the leaf node bypass a var, we should check the implicit dependencies. + auto nodeOutputVar = exe->node()->outputVar(); + const auto& writtenBy = qctx_->symTable()->getVar(nodeOutputVar)->writtenBy; + auto refCount = qctx_->symTable()->getVar(nodeOutputVar)->userCount.load(); + VLOG(1) << "var: " << nodeOutputVar + << "refCount: " << refCount + << "writtenBy: " << writtenBy.size() + << " if Exist this node: " + << (writtenBy.find(const_cast(exe->node())) != writtenBy.end()); + if (writtenBy.size() == 2 && + writtenBy.find(const_cast(exe->node())) != writtenBy.end()) { + for (auto& node : writtenBy) { + if (exe->node() == node) { + continue; + } + VLOG(1) << "register notifier to: " << node->id(); + Notifier p; + receivers.emplace_back(p.getFuture()); + auto& notifiers = notifierMap[node->id()]; + notifiers.emplace_back(std::move(p)); + } + } + } else { + for (auto* dep : exe->depends()) { + auto notVisited = visited.emplace(dep).second; + if (notVisited) { + queue.push(dep); + } + Notifier p; + receivers.emplace_back(p.getFuture()); + auto& notifiers = notifierMap[dep->id()]; + notifiers.emplace_back(std::move(p)); } - Notifier p; - receivers.emplace_back(p.getFuture()); - auto& notifiers = notifierMap[dep->id()]; - notifiers.emplace_back(std::move(p)); } - receiverMap.emplace(exe->id(), std::move(receivers)); + auto& receiversHist = receiverMap[exe->id()]; + receiversHist.insert(receiversHist.end(), std::make_move_iterator(receivers.begin()), + std::make_move_iterator(receivers.end())); } while (!queue2.empty()) { @@ -93,7 +120,10 @@ void AsyncMsgNotifyBasedScheduler::scheduleExecutor( break; } default: { - if (exe->depends().empty()) { + VLOG(1) << "node: " << exe->node()->kind() + << "exe: " << exe->node()->outputVar() + << " receivers: " << receivers.size(); + if (exe->depends().empty() && receivers.empty()) { runLeafExecutor(exe, runner, std::move(notifiers)); } else { runExecutor(std::move(receivers), exe, runner, std::move(notifiers)); diff --git a/tests/tck/features/optimizer/PushFilterDownLeftJoinRule.feature b/tests/tck/features/optimizer/PushFilterDownLeftJoinRule.feature index 356278699..9426c522a 100644 --- a/tests/tck/features/optimizer/PushFilterDownLeftJoinRule.feature +++ b/tests/tck/features/optimizer/PushFilterDownLeftJoinRule.feature @@ -2,6 +2,7 @@ # # This source code is licensed under Apache 2.0 License, # attached with Common Clause Condition 1.0, found in the LICENSES directory. +@push_down_join Feature: Push Filter down LeftJoin rule Background: @@ -61,10 +62,9 @@ Feature: Push Filter down LeftJoin rule | 7 | Project | 10 | | | 10 | Filter | 9 | | | 9 | LeftJoin | 12,4 | | - | 12 | Project | 11 | | - | 11 | Filter | 1 | | - | 1 | GetNeighbors | 0 | | + | 12 | Project | 13 | | + | 13 | GetNeighbors | 0 | | | 0 | Start | | | | 4 | Project | 3 | | | 3 | GetVertices | 2 | | - | 2 | Project | 1 | | + | 2 | Project | 13 | |