diff --git a/src/parser/parser.yy b/src/parser/parser.yy index 1391b617c3f..237d53ce19b 100644 --- a/src/parser/parser.yy +++ b/src/parser/parser.yy @@ -1133,9 +1133,9 @@ find_path_sentence ; find_path_upto_clause - : %empty { $$ = new StepClause(5, true); } + : %empty { $$ = new StepClause(5); } | KW_UPTO legal_integer KW_STEPS { - $$ = new StepClause($2, true); + $$ = new StepClause($2); } ; diff --git a/src/validator/CMakeLists.txt b/src/validator/CMakeLists.txt index ee242c6a2b8..a96428a2807 100644 --- a/src/validator/CMakeLists.txt +++ b/src/validator/CMakeLists.txt @@ -28,6 +28,7 @@ nebula_add_library( TraversalValidator.cpp ExplainValidator.cpp GroupByValidator.cpp + FindPathValidator.cpp ) nebula_add_subdirectory(test) diff --git a/src/validator/FindPathValidator.cpp b/src/validator/FindPathValidator.cpp new file mode 100644 index 00000000000..dd41123b9d9 --- /dev/null +++ b/src/validator/FindPathValidator.cpp @@ -0,0 +1,31 @@ +/* Copyright (c) 2020 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#include "validator/FindPathValidator.h" +#include "planner/Logic.h" + +namespace nebula { +namespace graph { +Status FindPathValidator::validateImpl() { + auto fpSentence = static_cast(sentence_); + isShortest_ = fpSentence->isShortest(); + + NG_RETURN_IF_ERROR(validateStarts(fpSentence->from(), from_)); + NG_RETURN_IF_ERROR(validateStarts(fpSentence->to(), to_)); + NG_RETURN_IF_ERROR(validateOver(fpSentence->over(), over_)); + NG_RETURN_IF_ERROR(validateStep(fpSentence->step(), steps_)); + return Status::OK(); +} + +Status FindPathValidator::toPlan() { + // TODO: Implement the path plan. + auto* passThrough = PassThroughNode::make(qctx_, nullptr); + tail_ = passThrough; + root_ = tail_; + return Status::OK(); +} +} // namespace graph +} // namespace nebula diff --git a/src/validator/FindPathValidator.h b/src/validator/FindPathValidator.h new file mode 100644 index 00000000000..e9769284a78 --- /dev/null +++ b/src/validator/FindPathValidator.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2020 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#ifndef VALIDATOR_FINDPATHVALIDATOR_H_ +#define VALIDATOR_FINDPATHVALIDATOR_H_ + +#include "common/base/Base.h" +#include "validator/TraversalValidator.h" + +namespace nebula { +namespace graph { + + +class FindPathValidator final : public TraversalValidator { +public: + FindPathValidator(Sentence* sentence, QueryContext* context) + : TraversalValidator(sentence, context) {} + +private: + Status validateImpl() override; + + Status toPlan() override; + +private: + bool isShortest_{false}; + Starts from_; + Starts to_; + Over over_; + Steps steps_; +}; +} // namespace graph +} // namespace nebula +#endif diff --git a/src/validator/GetSubgraphValidator.cpp b/src/validator/GetSubgraphValidator.cpp index aec5f26a7c1..190f26f94e5 100644 --- a/src/validator/GetSubgraphValidator.cpp +++ b/src/validator/GetSubgraphValidator.cpp @@ -22,12 +22,12 @@ Status GetSubgraphValidator::validateImpl() { Status status; auto* gsSentence = static_cast(sentence_); do { - status = validateStep(gsSentence->step()); + status = validateStep(gsSentence->step(), steps_); if (!status.ok()) { break; } - status = validateFrom(gsSentence->from()); + status = validateStarts(gsSentence->from(), from_); if (!status.ok()) { return status; } @@ -136,7 +136,7 @@ Status GetSubgraphValidator::toPlan() { std::string startVidsVar; PlanNode* projectStartVid = nullptr; - if (!starts_.empty() && srcRef_ == nullptr) { + if (!from_.vids.empty() && from_.srcRef == nullptr) { startVidsVar = buildConstantInput(); } else { projectStartVid = buildRuntimeInput(); @@ -162,7 +162,7 @@ Status GetSubgraphValidator::toPlan() { // ++counter{0} <= steps // TODO(shylock) add condition when gn get empty result - auto* condition = buildNStepLoopCondition(steps_); + auto* condition = buildNStepLoopCondition(steps_.steps); // The input of loop will set by father validator. auto* loop = Loop::make(qctx_, nullptr, projectVids, condition); diff --git a/src/validator/GoValidator.cpp b/src/validator/GoValidator.cpp index 28932fd5d33..1fd4d720ee9 100644 --- a/src/validator/GoValidator.cpp +++ b/src/validator/GoValidator.cpp @@ -18,17 +18,17 @@ namespace nebula { namespace graph { Status GoValidator::validateImpl() { auto* goSentence = static_cast(sentence_); - NG_RETURN_IF_ERROR(validateStep(goSentence->stepClause())); - NG_RETURN_IF_ERROR(validateFrom(goSentence->fromClause())); - NG_RETURN_IF_ERROR(validateOver(goSentence->overClause())); + NG_RETURN_IF_ERROR(validateStep(goSentence->stepClause(), steps_)); + NG_RETURN_IF_ERROR(validateStarts(goSentence->fromClause(), from_)); + NG_RETURN_IF_ERROR(validateOver(goSentence->overClause(), over_)); NG_RETURN_IF_ERROR(validateWhere(goSentence->whereClause())); NG_RETURN_IF_ERROR(validateYield(goSentence->yieldClause())); - if (!exprProps_.inputProps().empty() && fromType_ != kPipe) { + if (!exprProps_.inputProps().empty() && from_.fromType != kPipe) { return Status::Error("$- must be referred in FROM before used in WHERE or YIELD"); } - if (!exprProps_.varProps().empty() && fromType_ != kVariable) { + if (!exprProps_.varProps().empty() && from_.fromType != kVariable) { return Status::Error("A variable must be referred in FROM before used in WHERE or YIELD"); } @@ -42,47 +42,6 @@ Status GoValidator::validateImpl() { return Status::OK(); } -Status GoValidator::validateOver(const OverClause* over) { - if (over == nullptr) { - return Status::Error("Over clause nullptr."); - } - - direction_ = over->direction(); - auto* schemaMng = qctx_->schemaMng(); - if (over->isOverAll()) { - auto allEdgeStatus = schemaMng->getAllEdge(space_.id); - NG_RETURN_IF_ERROR(allEdgeStatus); - auto edges = std::move(allEdgeStatus).value(); - if (edges.empty()) { - return Status::Error("No edge type found in space %s", - space_.name.c_str()); - } - for (auto edge : edges) { - auto edgeType = schemaMng->toEdgeType(space_.id, edge); - if (!edgeType.ok()) { - return Status::Error("%s not found in space [%s].", - edge.c_str(), space_.name.c_str()); - } - VLOG(1) << "et: " << edgeType.value(); - edgeTypes_.emplace_back(edgeType.value()); - } - allEdges_ = std::move(edges); - isOverAll_ = true; - } else { - auto edges = over->edges(); - for (auto* edge : edges) { - auto edgeName = *edge->edge(); - auto edgeType = schemaMng->toEdgeType(space_.id, edgeName); - if (!edgeType.ok()) { - return Status::Error("%s not found in space [%s].", - edgeName.c_str(), space_.name.c_str()); - } - edgeTypes_.emplace_back(edgeType.value()); - } - } - return Status::OK(); -} - Status GoValidator::validateWhere(WhereClause* where) { if (where == nullptr) { return Status::OK(); @@ -126,11 +85,11 @@ Status GoValidator::validateYield(YieldClause* yield) { distinct_ = yield->isDistinct(); auto cols = yield->columns(); - if (cols.empty() && isOverAll_) { - DCHECK(!allEdges_.empty()); + if (cols.empty() && over_.isOverAll) { + DCHECK(!over_.allEdges.empty()); auto* newCols = new YieldColumns(); qctx_->objPool()->add(newCols); - for (auto& e : allEdges_) { + for (auto& e : over_.allEdges) { auto* col = new YieldColumn(new EdgeDstIdExpression(new std::string(e))); newCols->addColumn(col); auto colName = deduceColName(col); @@ -166,8 +125,8 @@ Status GoValidator::validateYield(YieldClause* yield) { NG_RETURN_IF_ERROR(deduceProps(col->expr(), exprProps_)); } for (auto& e : exprProps_.edgeProps()) { - auto found = std::find(edgeTypes_.begin(), edgeTypes_.end(), e.first); - if (found == edgeTypes_.end()) { + auto found = std::find(over_.edgeTypes.begin(), over_.edgeTypes.end(), e.first); + if (found == over_.edgeTypes.end()) { return Status::Error("Edges should be declared first in over clause."); } } @@ -178,14 +137,14 @@ Status GoValidator::validateYield(YieldClause* yield) { } Status GoValidator::toPlan() { - if (mToN_ == nullptr) { - if (steps_ == 0) { + if (steps_.mToN == nullptr) { + if (steps_.steps == 0) { auto* passThrough = PassThroughNode::make(qctx_, nullptr); passThrough->setColNames(std::move(colNames_)); tail_ = passThrough; root_ = tail_; return Status::OK(); - } else if (steps_ == 1) { + } else if (steps_.steps == 1) { return buildOneStepPlan(); } else { return buildNStepsPlan(); @@ -209,7 +168,7 @@ Status GoValidator::oneStep(PlanNode* dependencyForGn, PlanNode* projectSrcEdgeProps = nullptr; if (!exprProps_.inputProps().empty() || !exprProps_.varProps().empty() || - !exprProps_.dstTagProps().empty() || fromType_ != FromType::kInstantExpr) { + !exprProps_.dstTagProps().empty() || from_.fromType != FromType::kInstantExpr) { projectSrcEdgeProps = buildProjectSrcEdgePropsForGN(gn->varName(), gn); } @@ -223,7 +182,7 @@ Status GoValidator::oneStep(PlanNode* dependencyForGn, } PlanNode* joinInput = nullptr; - if (fromType_ != FromType::kInstantExpr) { + if (from_.fromType != FromType::kInstantExpr) { joinInput = buildJoinPipeOrVariableInput( projectFromJoin, joinDstProps == nullptr ? projectSrcEdgeProps : joinDstProps); } @@ -251,7 +210,6 @@ Status GoValidator::oneStep(PlanNode* dependencyForGn, } else { root_ = projectResult; } - VLOG(1) << root_->kind(); tail_ = gn; return Status::OK(); } @@ -261,7 +219,7 @@ Status GoValidator::buildNStepsPlan() { std::string startVidsVar; PlanNode* dedupStartVid = nullptr; - if (!starts_.empty() && srcRef_ == nullptr) { + if (!from_.vids.empty() && from_.srcRef == nullptr) { startVidsVar = buildConstantInput(); } else { dedupStartVid = buildRuntimeInput(); @@ -269,7 +227,7 @@ Status GoValidator::buildNStepsPlan() { } PlanNode* projectLeftVarForJoin = nullptr; - if (fromType_ != FromType::kInstantExpr) { + if (from_.fromType != FromType::kInstantExpr) { projectLeftVarForJoin = buildLeftVarForTraceJoin(dedupStartVid); } @@ -283,7 +241,7 @@ Status GoValidator::buildNStepsPlan() { // Trace to the start vid if starts from a runtime start vid. PlanNode* projectFromJoin = nullptr; - if (fromType_ != FromType::kInstantExpr && + if (from_.fromType != FromType::kInstantExpr && projectLeftVarForJoin != nullptr && dedupDstVids != nullptr) { projectFromJoin = traceToStartVid(projectLeftVarForJoin, dedupDstVids); } @@ -293,7 +251,7 @@ Status GoValidator::buildNStepsPlan() { projectLeftVarForJoin == nullptr ? dedupStartVid : projectLeftVarForJoin, // dep projectFromJoin == nullptr ? dedupDstVids : projectFromJoin, // body - buildNStepLoopCondition(steps_ - 1)); + buildNStepLoopCondition(steps_.steps - 1)); auto status = oneStep(loop, dedupDstVids->varName(), projectFromJoin); if (!status.ok()) { @@ -316,7 +274,7 @@ Status GoValidator::buildMToNPlan() { std::string startVidsVar; PlanNode* dedupStartVid = nullptr; - if (!starts_.empty() && srcRef_ == nullptr) { + if (!from_.vids.empty() && from_.srcRef == nullptr) { startVidsVar = buildConstantInput(); } else { dedupStartVid = buildRuntimeInput(); @@ -408,7 +366,7 @@ Status GoValidator::buildMToNPlan() { projectLeftVarForJoin == nullptr ? dedupStartVid : projectLeftVarForJoin, // dep dedupNode == nullptr ? projectResult : dedupNode, // body - buildNStepLoopCondition(mToN_->nSteps)); + buildNStepLoopCondition(steps_.mToN->nSteps)); if (projectStartVid_ != nullptr) { tail_ = projectStartVid_; @@ -424,7 +382,7 @@ Status GoValidator::buildMToNPlan() { } auto* dataCollect = DataCollect::make(qctx_, loop, DataCollect::CollectKind::kMToN, collectVars); - dataCollect->setMToN(mToN_); + dataCollect->setMToN(steps_.mToN); dataCollect->setDistinct(distinct_); dataCollect->setColNames(projectResult->colNames()); root_ = dataCollect; @@ -435,14 +393,13 @@ PlanNode* GoValidator::buildProjectSrcEdgePropsForGN(std::string gnVar, PlanNode DCHECK(dependency != nullptr); // Get _vid for join if $-/$var were declared. - if (fromType_ != FromType::kInstantExpr) { + if (from_.fromType != FromType::kInstantExpr) { auto* srcVidCol = new YieldColumn( new VariablePropertyExpression(new std::string(gnVar), new std::string(kVid)), new std::string(kVid)); srcAndEdgePropCols_->addColumn(srcVidCol); } - VLOG(1) << "build dst cols"; // Get all _dst to a single column. if (!exprProps_.dstTagProps().empty()) { joinDstVidColName_ = vctx_->anonColGen()->getCol(); @@ -512,7 +469,7 @@ PlanNode* GoValidator::buildJoinPipeOrVariableInput(PlanNode* projectFromJoin, PlanNode* dependencyForJoinInput) { auto* pool = qctx_->objPool(); - if (steps_ > 1 || mToN_ != nullptr) { + if (steps_.steps > 1 || steps_.mToN != nullptr) { DCHECK(projectFromJoin != nullptr); auto* joinHashKey = pool->add(new VariablePropertyExpression( new std::string(dependencyForJoinInput->varName()), new std::string(kVid))); @@ -523,8 +480,8 @@ PlanNode* GoValidator::buildJoinPipeOrVariableInput(PlanNode* projectFromJoin, dependencyForJoinInput, {dependencyForJoinInput->varName(), ExecutionContext::kLatestVersion}, {projectFromJoin->varName(), - mToN_ != nullptr ? ExecutionContext::kPreviousOneVersion - : ExecutionContext::kLatestVersion}, + steps_.mToN != nullptr ? ExecutionContext::kPreviousOneVersion + : ExecutionContext::kLatestVersion}, {joinHashKey}, {probeKey}); std::vector colNames = dependencyForJoinInput->colNames(); @@ -537,16 +494,18 @@ PlanNode* GoValidator::buildJoinPipeOrVariableInput(PlanNode* projectFromJoin, } DCHECK(dependencyForJoinInput != nullptr); - auto* joinHashKey = pool->add(new VariablePropertyExpression( - new std::string(dependencyForJoinInput->varName()), - new std::string((steps_ > 1 || mToN_ != nullptr) ? firstBeginningSrcVidColName_ : kVid))); + auto* joinHashKey = pool->add( + new VariablePropertyExpression(new std::string(dependencyForJoinInput->varName()), + new std::string((steps_.steps > 1 || steps_.mToN != nullptr) + ? from_.firstBeginningSrcVidColName + : kVid))); auto* joinInput = DataJoin::make(qctx_, dependencyForJoinInput, {dependencyForJoinInput->varName(), ExecutionContext::kLatestVersion}, - {fromType_ == kPipe ? inputVarName_ : userDefinedVarName_, + {from_.fromType == kPipe ? inputVarName_ : from_.userDefinedVarName, ExecutionContext::kLatestVersion}, - {joinHashKey}, {srcRef_}); + {joinHashKey}, {from_.srcRef}); std::vector colNames = dependencyForJoinInput->colNames(); for (auto& col : outputs_) { colNames.emplace_back(col.first); @@ -584,10 +543,9 @@ PlanNode* GoValidator::traceToStartVid(PlanNode* projectLeftVarForJoin, VLOG(1) << join->varName(); auto* columns = pool->add(new YieldColumns()); - auto* column = - new YieldColumn(new InputPropertyExpression( - new std::string(firstBeginningSrcVidColName_)), - new std::string(firstBeginningSrcVidColName_)); + auto* column = new YieldColumn( + new InputPropertyExpression(new std::string(from_.firstBeginningSrcVidColName)), + new std::string(from_.firstBeginningSrcVidColName)); columns->addColumn(column); column = new YieldColumn(new InputPropertyExpression(new std::string(kVid)), @@ -610,17 +568,17 @@ PlanNode* GoValidator::buildLeftVarForTraceJoin(PlanNode* dedupStartVid) { dstVidColName_ = vctx_->anonColGen()->getCol(); auto* columns = pool->add(new YieldColumns()); auto* column = - new YieldColumn(Expression::decode(srcRef_->encode()).release(), - new std::string(firstBeginningSrcVidColName_)); + new YieldColumn(Expression::decode(from_.srcRef->encode()).release(), + new std::string(from_.firstBeginningSrcVidColName)); columns->addColumn(column); column = - new YieldColumn(Expression::decode(srcRef_->encode()).release(), + new YieldColumn(Expression::decode(from_.srcRef->encode()).release(), new std::string(dstVidColName_)); columns->addColumn(column); // dedupStartVid could be nullptr, that means no input for this project. auto* projectLeftVarForJoin = Project::make(qctx_, dedupStartVid, columns); projectLeftVarForJoin->setInputVar( - fromType_ == kPipe ? inputVarName_ : userDefinedVarName_); + from_.fromType == kPipe ? inputVarName_ : from_.userDefinedVarName); projectLeftVarForJoin->setColNames(deduceColNames(columns)); auto* dedup = Dedup::make(qctx_, projectLeftVarForJoin); @@ -632,7 +590,7 @@ PlanNode* GoValidator::buildLeftVarForTraceJoin(PlanNode* dedupStartVid) { Status GoValidator::buildOneStepPlan() { std::string startVidsVar; PlanNode* dedupStartVid = nullptr; - if (!starts_.empty() && srcRef_ == nullptr) { + if (!from_.vids.empty() && from_.srcRef == nullptr) { startVidsVar = buildConstantInput(); } else { dedupStartVid = buildRuntimeInput(); @@ -686,10 +644,10 @@ std::vector GoValidator::buildDstVertexProps() { GetNeighbors::EdgeProps GoValidator::buildEdgeProps() { GetNeighbors::EdgeProps edgeProps; if (!exprProps_.edgeProps().empty()) { - if (direction_ == storage::cpp2::EdgeDirection::IN_EDGE) { + if (over_.direction == storage::cpp2::EdgeDirection::IN_EDGE) { edgeProps = std::make_unique>(); buildEdgeProps(edgeProps, true); - } else if (direction_ == storage::cpp2::EdgeDirection::BOTH) { + } else if (over_.direction == storage::cpp2::EdgeDirection::BOTH) { edgeProps = std::make_unique>(); buildEdgeProps(edgeProps, false); buildEdgeProps(edgeProps, true); @@ -705,8 +663,8 @@ GetNeighbors::EdgeProps GoValidator::buildEdgeProps() { } void GoValidator::buildEdgeProps(GetNeighbors::EdgeProps& edgeProps, bool isInEdge) { - edgeProps->reserve(edgeTypes_.size()); - for (auto& e : edgeTypes_) { + edgeProps->reserve(over_.edgeTypes.size()); + for (auto& e : over_.edgeTypes) { storage::cpp2::EdgeProp ep; if (isInEdge) { ep.set_type(-e); @@ -732,45 +690,41 @@ void GoValidator::buildEdgeProps(GetNeighbors::EdgeProps& edgeProps, bool isInEd GetNeighbors::EdgeProps GoValidator::buildEdgeDst() { GetNeighbors::EdgeProps edgeProps; if (!exprProps_.edgeProps().empty() || !exprProps_.dstTagProps().empty()) { - if (direction_ == storage::cpp2::EdgeDirection::IN_EDGE) { + if (over_.direction == storage::cpp2::EdgeDirection::IN_EDGE) { edgeProps = std::make_unique>( - edgeTypes_.size()); - std::transform(edgeTypes_.begin(), edgeTypes_.end(), edgeProps->begin(), + over_.edgeTypes.size()); + std::transform(over_.edgeTypes.begin(), over_.edgeTypes.end(), edgeProps->begin(), [](auto& type) { storage::cpp2::EdgeProp ep; ep.type = -type; - VLOG(1) << "edge type: " << ep.type; ep.props = {kDst}; return ep; }); - } else if (direction_ == storage::cpp2::EdgeDirection::BOTH) { + } else if (over_.direction == storage::cpp2::EdgeDirection::BOTH) { edgeProps = std::make_unique>( - edgeTypes_.size() * 2); - std::transform(edgeTypes_.begin(), edgeTypes_.end(), edgeProps->begin(), + over_.edgeTypes.size() * 2); + std::transform(over_.edgeTypes.begin(), over_.edgeTypes.end(), edgeProps->begin(), [](auto& type) { storage::cpp2::EdgeProp ep; ep.type = type; - VLOG(1) << "edge type: " << ep.type; ep.props = {kDst}; return ep; }); - std::transform(edgeTypes_.begin(), edgeTypes_.end(), - edgeProps->begin() + edgeTypes_.size(), + std::transform(over_.edgeTypes.begin(), over_.edgeTypes.end(), + edgeProps->begin() + over_.edgeTypes.size(), [](auto& type) { storage::cpp2::EdgeProp ep; ep.type = -type; - VLOG(1) << "edge type: " << ep.type; ep.props = {kDst}; return ep; }); } else { edgeProps = std::make_unique>( - edgeTypes_.size()); - std::transform(edgeTypes_.begin(), edgeTypes_.end(), edgeProps->begin(), + over_.edgeTypes.size()); + std::transform(over_.edgeTypes.begin(), over_.edgeTypes.end(), edgeProps->begin(), [](auto& type) { storage::cpp2::EdgeProp ep; ep.type = type; - VLOG(1) << "edge type: " << ep.type; ep.props = {kDst}; return ep; }); @@ -996,7 +950,7 @@ std::unique_ptr GoValidator::rewriteToInputProp(Expression* expr) { Status GoValidator::buildColumns() { if (exprProps_.dstTagProps().empty() && exprProps_.inputProps().empty() && - exprProps_.varProps().empty() && fromType_ == FromType::kInstantExpr) { + exprProps_.varProps().empty() && from_.fromType == FromType::kInstantExpr) { return Status::OK(); } diff --git a/src/validator/GoValidator.h b/src/validator/GoValidator.h index 00f818a6617..5bc9eb57a45 100644 --- a/src/validator/GoValidator.h +++ b/src/validator/GoValidator.h @@ -23,8 +23,6 @@ class GoValidator final : public TraversalValidator { Status toPlan() override; - Status validateOver(const OverClause* over); - Status validateWhere(WhereClause* where); Status validateYield(YieldClause* yield); @@ -68,9 +66,7 @@ class GoValidator final : public TraversalValidator { PlanNode* buildJoinDstProps(PlanNode* projectSrcDstProps); private: - bool isOverAll_{false}; - std::vector edgeTypes_; - storage::cpp2::EdgeDirection direction_; + Over over_; Expression* filter_{nullptr}; std::vector colNames_; YieldColumns* yields_{nullptr}; @@ -88,7 +84,6 @@ class GoValidator final : public TraversalValidator { std::string dstVidColName_; // Used for get dst props std::string joinDstVidColName_; - std::vector allEdges_; }; } // namespace graph } // namespace nebula diff --git a/src/validator/TraversalValidator.cpp b/src/validator/TraversalValidator.cpp index f2597e592e2..1453f76b4ed 100644 --- a/src/validator/TraversalValidator.cpp +++ b/src/validator/TraversalValidator.cpp @@ -10,51 +10,19 @@ namespace nebula { namespace graph { -Status TraversalValidator::validateStep(const StepClause* step) { - if (step == nullptr) { - return Status::Error("Step clause nullptr."); - } - if (step->isMToN()) { - auto* mToN = qctx_->objPool()->makeAndAdd(); - mToN->mSteps = step->mToN()->mSteps; - mToN->nSteps = step->mToN()->nSteps; - - if (mToN->mSteps == 0 && mToN->nSteps == 0) { - steps_ = 0; - return Status::OK(); - } - if (mToN->mSteps == 0) { - mToN->mSteps = 1; - } - if (mToN->nSteps < mToN->mSteps) { - return Status::Error("`%s', upper bound steps should be greater than lower bound.", - step->toString().c_str()); - } - if (mToN->mSteps == mToN->nSteps) { - steps_ = mToN->mSteps; - return Status::OK(); - } - mToN_ = mToN; - } else { - auto steps = step->steps(); - steps_ = steps; - } - return Status::OK(); -} - -Status TraversalValidator::validateFrom(const FromClause* from) { - if (from == nullptr) { +Status TraversalValidator::validateStarts(const VerticesClause* clause, Starts& starts) { + if (clause == nullptr) { return Status::Error("From clause nullptr."); } - if (from->isRef()) { - auto* src = from->ref(); + if (clause->isRef()) { + auto* src = clause->ref(); if (src->kind() != Expression::Kind::kInputProperty && src->kind() != Expression::Kind::kVarProperty) { return Status::Error( "`%s', Only input and variable expression is acceptable" " when starts are evaluated at runtime.", src->toString().c_str()); } else { - fromType_ = src->kind() == Expression::Kind::kInputProperty ? kPipe : kVariable; + starts.fromType = src->kind() == Expression::Kind::kInputProperty ? kPipe : kVariable; auto type = deduceExprType(src); if (!type.ok()) { return type.status(); @@ -65,15 +33,15 @@ Status TraversalValidator::validateFrom(const FromClause* from) { << "but was`" << type.value() << "'"; return Status::Error(ss.str()); } - srcRef_ = src; + starts.srcRef = src; auto* propExpr = static_cast(src); - if (fromType_ == kVariable) { - userDefinedVarName_ = *(propExpr->sym()); + if (starts.fromType == kVariable) { + starts.userDefinedVarName = *(propExpr->sym()); } - firstBeginningSrcVidColName_ = *(propExpr->prop()); + starts.firstBeginningSrcVidColName = *(propExpr->prop()); } } else { - auto vidList = from->vidList(); + auto vidList = clause->vidList(); QueryExpressionContext ctx; for (auto* expr : vidList) { if (!evaluableExpr(expr)) { @@ -84,12 +52,85 @@ Status TraversalValidator::validateFrom(const FromClause* from) { if (!vid.isStr()) { return Status::Error("Vid should be a string."); } - starts_.emplace_back(std::move(vid)); + starts.vids.emplace_back(std::move(vid)); } } return Status::OK(); } +Status TraversalValidator::validateOver(const OverClause* clause, Over& over) { + if (clause == nullptr) { + return Status::Error("Over clause nullptr."); + } + + over.direction = clause->direction(); + auto* schemaMng = qctx_->schemaMng(); + if (clause->isOverAll()) { + auto allEdgeStatus = schemaMng->getAllEdge(space_.id); + NG_RETURN_IF_ERROR(allEdgeStatus); + auto edges = std::move(allEdgeStatus).value(); + if (edges.empty()) { + return Status::Error("No edge type found in space %s", + space_.name.c_str()); + } + for (auto edge : edges) { + auto edgeType = schemaMng->toEdgeType(space_.id, edge); + if (!edgeType.ok()) { + return Status::Error("%s not found in space [%s].", + edge.c_str(), space_.name.c_str()); + } + over.edgeTypes.emplace_back(edgeType.value()); + } + over.allEdges = std::move(edges); + over.isOverAll = true; + } else { + auto edges = clause->edges(); + for (auto* edge : edges) { + auto edgeName = *edge->edge(); + auto edgeType = schemaMng->toEdgeType(space_.id, edgeName); + if (!edgeType.ok()) { + return Status::Error("%s not found in space [%s].", + edgeName.c_str(), space_.name.c_str()); + } + over.edgeTypes.emplace_back(edgeType.value()); + } + } + return Status::OK(); +} + +Status TraversalValidator::validateStep(const StepClause* clause, Steps& step) { + if (clause == nullptr) { + return Status::Error("Step clause nullptr."); + } + if (clause->isMToN()) { + auto* mToN = qctx_->objPool()->makeAndAdd(); + mToN->mSteps = clause->mToN()->mSteps; + mToN->nSteps = clause->mToN()->nSteps; + + if (mToN->mSteps == 0 && mToN->nSteps == 0) { + step.steps = 0; + return Status::OK(); + } + if (mToN->mSteps == 0) { + mToN->mSteps = 1; + } + if (mToN->nSteps < mToN->mSteps) { + return Status::Error("`%s', upper bound steps should be greater than lower bound.", + clause->toString().c_str()); + } + if (mToN->mSteps == mToN->nSteps) { + steps_.steps = mToN->mSteps; + return Status::OK(); + } + step.mToN = mToN; + } else { + auto steps = clause->steps(); + step.steps = steps; + } + return Status::OK(); +} + + PlanNode* TraversalValidator::projectDstVidsFromGN(PlanNode* gn, const std::string& outputVar) { Project* project = nullptr; @@ -123,7 +164,7 @@ std::string TraversalValidator::buildConstantInput() { auto input = vctx_->anonVarGen()->getVar(); DataSet ds; ds.colNames.emplace_back(kVid); - for (auto& vid : starts_) { + for (auto& vid : from_.vids) { Row row; row.values.emplace_back(vid); ds.rows.emplace_back(std::move(row)); @@ -140,13 +181,13 @@ std::string TraversalValidator::buildConstantInput() { PlanNode* TraversalValidator::buildRuntimeInput() { auto pool = qctx_->objPool(); auto* columns = pool->add(new YieldColumns()); - auto encode = srcRef_->encode(); + auto encode = from_.srcRef->encode(); auto decode = Expression::decode(encode); auto* column = new YieldColumn(decode.release(), new std::string(kVid)); columns->addColumn(column); auto* project = Project::make(qctx_, nullptr, columns); - if (fromType_ == kVariable) { - project->setInputVar(userDefinedVarName_); + if (from_.fromType == kVariable) { + project->setInputVar(from_.userDefinedVarName); } project->setColNames({ kVid }); VLOG(1) << project->varName() << " input: " << project->inputVar(); diff --git a/src/validator/TraversalValidator.h b/src/validator/TraversalValidator.h index b1f2aac9c2f..3e6702ec19a 100644 --- a/src/validator/TraversalValidator.h +++ b/src/validator/TraversalValidator.h @@ -16,33 +16,55 @@ namespace graph { // some utils for the validator to traverse the graph class TraversalValidator : public Validator { +protected: + enum FromType { + kInstantExpr, + kVariable, + kPipe, + }; + + struct Starts { + FromType fromType{kInstantExpr}; + Expression* srcRef{nullptr}; + std::string userDefinedVarName; + std::string firstBeginningSrcVidColName; + std::vector vids; + }; + + struct Over { + bool isOverAll{false}; + std::vector edgeTypes; + storage::cpp2::EdgeDirection direction; + std::vector allEdges; + }; + + struct Steps { + StepClause::MToN* mToN{nullptr}; + uint32_t steps{1}; + }; + protected: TraversalValidator(Sentence* sentence, QueryContext* qctx) : Validator(sentence, qctx) {} - Status validateStep(const StepClause* step); - Status validateFrom(const FromClause* from); + Status validateStarts(const VerticesClause* clause, Starts& starts); + + Status validateOver(const OverClause* clause, Over& over); + + Status validateStep(const StepClause* clause, Steps& step); PlanNode* projectDstVidsFromGN(PlanNode* gn, const std::string& outputVar); + std::string buildConstantInput(); + PlanNode* buildRuntimeInput(); - Expression* buildNStepLoopCondition(uint32_t steps) const; - enum FromType { - kInstantExpr, - kVariable, - kPipe, - }; + Expression* buildNStepLoopCondition(uint32_t steps) const; protected: - StepClause::MToN* mToN_{nullptr}; - uint32_t steps_{1}; + Starts from_; + Steps steps_; std::string srcVidColName_; - FromType fromType_{kInstantExpr}; - Expression* srcRef_{nullptr}; Expression* src_{nullptr}; - std::vector starts_; - std::string firstBeginningSrcVidColName_; - std::string userDefinedVarName_; ExpressionProps exprProps_; PlanNode* projectStartVid_{nullptr}; }; diff --git a/src/validator/Validator.cpp b/src/validator/Validator.cpp index e67e75d6e21..7f54b7cee8e 100644 --- a/src/validator/Validator.cpp +++ b/src/validator/Validator.cpp @@ -19,6 +19,7 @@ #include "validator/ExplainValidator.h" #include "validator/FetchEdgesValidator.h" #include "validator/FetchVerticesValidator.h" +#include "validator/FindPathValidator.h" #include "validator/GetSubgraphValidator.h" #include "validator/GoValidator.h" #include "validator/GroupByValidator.h" @@ -161,6 +162,8 @@ std::unique_ptr Validator::makeValidator(Sentence* sentence, QueryCon return std::make_unique(sentence, context); case Sentence::Kind::kShowConfigs: return std::make_unique(sentence, context); + case Sentence::Kind::kFindPath: + return std::make_unique(sentence, context); case Sentence::Kind::kMatch: case Sentence::Kind::kUnknown: case Sentence::Kind::kCreateTagIndex: @@ -180,7 +183,6 @@ std::unique_ptr Validator::makeValidator(Sentence* sentence, QueryCon case Sentence::Kind::kLookup: case Sentence::Kind::kDownload: case Sentence::Kind::kIngest: - case Sentence::Kind::kFindPath: case Sentence::Kind::kReturn: { // nothing DLOG(FATAL) << "Unimplemented sentence " << kind; diff --git a/src/validator/test/QueryValidatorTest.cpp b/src/validator/test/QueryValidatorTest.cpp index 52fced1edf0..bdd6051b119 100644 --- a/src/validator/test/QueryValidatorTest.cpp +++ b/src/validator/test/QueryValidatorTest.cpp @@ -1122,102 +1122,103 @@ TEST_F(QueryValidatorTest, OrderByAndLimt) { } TEST_F(QueryValidatorTest, TestSetValidator) { - // UNION ALL - { - std::string query = - "GO FROM \"1\" OVER like YIELD like.start AS start UNION ALL GO FROM \"2\" " - "OVER like YIELD like.start AS start"; - std::vector expected = { - PK::kDataCollect, - PK::kUnion, - PK::kProject, - PK::kProject, - PK::kGetNeighbors, - PK::kGetNeighbors, - PK::kPassThrough, - PK::kStart, - }; - EXPECT_TRUE(checkResult(query, expected)); - } - // UNION DISTINCT twice - { - std::string query = "GO FROM \"1\" OVER like YIELD like.start AS start UNION GO FROM \"2\" " - "OVER like YIELD like.start AS start UNION GO FROM \"3\" OVER like YIELD " - "like.start AS start"; - std::vector expected = { - PK::kDataCollect, - PK::kDedup, - PK::kUnion, - PK::kDedup, - PK::kProject, - PK::kUnion, - PK::kGetNeighbors, - PK::kProject, - PK::kProject, - PK::kPassThrough, - PK::kGetNeighbors, - PK::kGetNeighbors, - PK::kStart, - PK::kPassThrough, - }; - EXPECT_TRUE(checkResult(query, expected)); - } - // UNION DISTINCT - { - std::string query = - "GO FROM \"1\" OVER like YIELD like.start AS start UNION DISTINCT GO FROM \"2\" " - "OVER like YIELD like.start AS start"; - std::vector expected = { - PK::kDataCollect, - PK::kDedup, - PK::kUnion, - PK::kProject, - PK::kProject, - PK::kGetNeighbors, - PK::kGetNeighbors, - PK::kPassThrough, - PK::kStart, - }; - EXPECT_TRUE(checkResult(query, expected)); - } - // INVALID UNION ALL - { - std::string query = "GO FROM \"1\" OVER like YIELD like.start AS start, $^.person.name AS " - "name UNION GO FROM \"2\" OVER like YIELD like.start AS start"; - EXPECT_FALSE(checkResult(query)); - } - // INTERSECT - { - std::string query = "GO FROM \"1\" OVER like YIELD like.start AS start INTERSECT GO FROM " - "\"2\" OVER like YIELD like.start AS start"; - std::vector expected = { - PK::kDataCollect, - PK::kIntersect, - PK::kProject, - PK::kProject, - PK::kGetNeighbors, - PK::kGetNeighbors, - PK::kPassThrough, - PK::kStart, - }; - EXPECT_TRUE(checkResult(query, expected)); - } - // MINUS - { - std::string query = "GO FROM \"1\" OVER like YIELD like.start AS start MINUS GO FROM " - "\"2\" OVER like YIELD like.start AS start"; - std::vector expected = { - PK::kDataCollect, - PK::kMinus, - PK::kProject, - PK::kProject, - PK::kGetNeighbors, - PK::kGetNeighbors, - PK::kPassThrough, - PK::kStart, - }; - EXPECT_TRUE(checkResult(query, expected)); - } + // UNION ALL + { + std::string query = + "GO FROM \"1\" OVER like YIELD like.start AS start UNION ALL GO FROM \"2\" " + "OVER like YIELD like.start AS start"; + std::vector expected = { + PK::kDataCollect, + PK::kUnion, + PK::kProject, + PK::kProject, + PK::kGetNeighbors, + PK::kGetNeighbors, + PK::kPassThrough, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + // UNION DISTINCT twice + { + std::string query = + "GO FROM \"1\" OVER like YIELD like.start AS start UNION GO FROM \"2\" " + "OVER like YIELD like.start AS start UNION GO FROM \"3\" OVER like YIELD " + "like.start AS start"; + std::vector expected = { + PK::kDataCollect, + PK::kDedup, + PK::kUnion, + PK::kDedup, + PK::kProject, + PK::kUnion, + PK::kGetNeighbors, + PK::kProject, + PK::kProject, + PK::kPassThrough, + PK::kGetNeighbors, + PK::kGetNeighbors, + PK::kStart, + PK::kPassThrough, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + // UNION DISTINCT + { + std::string query = + "GO FROM \"1\" OVER like YIELD like.start AS start UNION DISTINCT GO FROM \"2\" " + "OVER like YIELD like.start AS start"; + std::vector expected = { + PK::kDataCollect, + PK::kDedup, + PK::kUnion, + PK::kProject, + PK::kProject, + PK::kGetNeighbors, + PK::kGetNeighbors, + PK::kPassThrough, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + // INVALID UNION ALL + { + std::string query = "GO FROM \"1\" OVER like YIELD like.start AS start, $^.person.name AS " + "name UNION GO FROM \"2\" OVER like YIELD like.start AS start"; + EXPECT_FALSE(checkResult(query)); + } + // INTERSECT + { + std::string query = "GO FROM \"1\" OVER like YIELD like.start AS start INTERSECT GO FROM " + "\"2\" OVER like YIELD like.start AS start"; + std::vector expected = { + PK::kDataCollect, + PK::kIntersect, + PK::kProject, + PK::kProject, + PK::kGetNeighbors, + PK::kGetNeighbors, + PK::kPassThrough, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + // MINUS + { + std::string query = "GO FROM \"1\" OVER like YIELD like.start AS start MINUS GO FROM " + "\"2\" OVER like YIELD like.start AS start"; + std::vector expected = { + PK::kDataCollect, + PK::kMinus, + PK::kProject, + PK::kProject, + PK::kGetNeighbors, + PK::kGetNeighbors, + PK::kPassThrough, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } } TEST_F(QueryValidatorTest, TestMaxAllowedStatements) { @@ -1237,5 +1238,74 @@ TEST_F(QueryValidatorTest, TestMaxAllowedStatements) { "SemanticError: The maximum number of statements allowed has been exceeded"); } +TEST_F(QueryValidatorTest, FindPath) { + // TODO: Implement the plan. + // shortest + { + std::string query = "FIND SHORTEST PATH FROM \"1\" TO \"2\" OVER like UPTO 5 STEPS"; + std::vector expected = { + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "FIND SHORTEST PATH FROM \"1\" TO \"2\",\"3\" OVER like UPTO 5 STEPS"; + std::vector expected = { + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = + "FIND SHORTEST PATH FROM \"1\",\"2\" TO \"3\",\"4\" OVER like UPTO 5 STEPS"; + std::vector expected = {}; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "FIND SHORTEST PATH FROM \"1\" TO \"2\" OVER like, serve UPTO 5 STEPS"; + std::vector expected = { + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = + "YIELD \"1\" AS src, \"2\" AS dst" + " | FIND SHORTEST PATH FROM $-.src TO $-.dst OVER like, serve UPTO 5 STEPS"; + std::vector expected = { + }; + EXPECT_TRUE(checkResult(query, expected)); + } + + // all + { + std::string query = "FIND ALL PATH FROM \"1\" TO \"2\" OVER like UPTO 5 STEPS"; + std::vector expected = { + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "FIND ALL PATH FROM \"1\" TO \"2\",\"3\" OVER like UPTO 5 STEPS"; + std::vector expected = { + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "FIND ALL PATH FROM \"1\",\"2\" TO \"3\",\"4\" OVER like UPTO 5 STEPS"; + std::vector expected = { + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "FIND ALL PATH FROM \"1\" TO \"2\" OVER like, serve UPTO 5 STEPS"; + std::vector expected = { + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "YIELD \"1\" AS src, \"2\" AS dst" + " | FIND ALL PATH FROM $-.src TO $-.dst OVER like, serve UPTO 5 STEPS"; + std::vector expected = { + }; + EXPECT_TRUE(checkResult(query, expected)); + } +} } // namespace graph } // namespace nebula