Skip to content
Snippets Groups Projects
Unverified Commit cc812d3d authored by cpw's avatar cpw Committed by GitHub
Browse files

Find path validator. (#245)


* Add find path validator.

* Implement validate.

* Remove useless vlog.

* Add test.

* Refactor.

* Address comment.

* Rebase and address comment.

* Rebase.

Co-authored-by: default avatardutor <440396+dutor@users.noreply.github.com>
parent d5a75cdf
No related branches found
No related tags found
No related merge requests found
......@@ -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);
}
;
......
......@@ -28,6 +28,7 @@ nebula_add_library(
TraversalValidator.cpp
ExplainValidator.cpp
GroupByValidator.cpp
FindPathValidator.cpp
)
nebula_add_subdirectory(test)
/* 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<FindPathSentence*>(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
/* 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
......@@ -22,12 +22,12 @@ Status GetSubgraphValidator::validateImpl() {
Status status;
auto* gsSentence = static_cast<GetSubgraphSentence*>(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);
......
......@@ -18,17 +18,17 @@ namespace nebula {
namespace graph {
Status GoValidator::validateImpl() {
auto* goSentence = static_cast<GoSentence*>(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,7 +480,7 @@ PlanNode* GoValidator::buildJoinPipeOrVariableInput(PlanNode* projectFromJoin,
dependencyForJoinInput,
{dependencyForJoinInput->varName(), ExecutionContext::kLatestVersion},
{projectFromJoin->varName(),
mToN_ != nullptr ? ExecutionContext::kPreviousOneVersion
steps_.mToN != nullptr ? ExecutionContext::kPreviousOneVersion
: ExecutionContext::kLatestVersion},
{joinHashKey},
{probeKey});
......@@ -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<std::string> 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<storage::cpp2::VertexProp> 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<std::vector<storage::cpp2::EdgeProp>>();
buildEdgeProps(edgeProps, true);
} else if (direction_ == storage::cpp2::EdgeDirection::BOTH) {
} else if (over_.direction == storage::cpp2::EdgeDirection::BOTH) {
edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>();
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<std::vector<storage::cpp2::EdgeProp>>(
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<std::vector<storage::cpp2::EdgeProp>>(
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<std::vector<storage::cpp2::EdgeProp>>(
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<Expression> 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();
}
......
......@@ -23,8 +23,6 @@ private:
Status toPlan() override;
Status validateOver(const OverClause* over);
Status validateWhere(WhereClause* where);
Status validateYield(YieldClause* yield);
......@@ -68,9 +66,7 @@ private:
PlanNode* buildJoinDstProps(PlanNode* projectSrcDstProps);
private:
bool isOverAll_{false};
std::vector<EdgeType> edgeTypes_;
storage::cpp2::EdgeDirection direction_;
Over over_;
Expression* filter_{nullptr};
std::vector<std::string> colNames_;
YieldColumns* yields_{nullptr};
......@@ -88,7 +84,6 @@ private:
std::string dstVidColName_;
// Used for get dst props
std::string joinDstVidColName_;
std::vector<std::string> allEdges_;
};
} // namespace graph
} // namespace nebula
......
......@@ -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<StepClause::MToN>();
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<PropertyExpression*>(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<StepClause::MToN>();
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();
......
......@@ -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<Value> vids;
};
struct Over {
bool isOverAll{false};
std::vector<EdgeType> edgeTypes;
storage::cpp2::EdgeDirection direction;
std::vector<std::string> 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<Value> starts_;
std::string firstBeginningSrcVidColName_;
std::string userDefinedVarName_;
ExpressionProps exprProps_;
PlanNode* projectStartVid_{nullptr};
};
......
......@@ -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> Validator::makeValidator(Sentence* sentence, QueryCon
return std::make_unique<SetConfigValidator>(sentence, context);
case Sentence::Kind::kShowConfigs:
return std::make_unique<ShowConfigsValidator>(sentence, context);
case Sentence::Kind::kFindPath:
return std::make_unique<FindPathValidator>(sentence, context);
case Sentence::Kind::kMatch:
case Sentence::Kind::kUnknown:
case Sentence::Kind::kCreateTagIndex:
......@@ -180,7 +183,6 @@ std::unique_ptr<Validator> 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;
......
......@@ -1141,7 +1141,8 @@ TEST_F(QueryValidatorTest, TestSetValidator) {
}
// UNION DISTINCT twice
{
std::string query = "GO FROM \"1\" OVER like YIELD like.start AS start UNION GO FROM \"2\" "
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<PlanNode::Kind> expected = {
......@@ -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<PlanNode::Kind> expected = {
};
EXPECT_TRUE(checkResult(query, expected));
}
{
std::string query = "FIND SHORTEST PATH FROM \"1\" TO \"2\",\"3\" OVER like UPTO 5 STEPS";
std::vector<PlanNode::Kind> 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<PlanNode::Kind> expected = {};
EXPECT_TRUE(checkResult(query, expected));
}
{
std::string query = "FIND SHORTEST PATH FROM \"1\" TO \"2\" OVER like, serve UPTO 5 STEPS";
std::vector<PlanNode::Kind> 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<PlanNode::Kind> expected = {
};
EXPECT_TRUE(checkResult(query, expected));
}
// all
{
std::string query = "FIND ALL PATH FROM \"1\" TO \"2\" OVER like UPTO 5 STEPS";
std::vector<PlanNode::Kind> expected = {
};
EXPECT_TRUE(checkResult(query, expected));
}
{
std::string query = "FIND ALL PATH FROM \"1\" TO \"2\",\"3\" OVER like UPTO 5 STEPS";
std::vector<PlanNode::Kind> 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<PlanNode::Kind> expected = {
};
EXPECT_TRUE(checkResult(query, expected));
}
{
std::string query = "FIND ALL PATH FROM \"1\" TO \"2\" OVER like, serve UPTO 5 STEPS";
std::vector<PlanNode::Kind> 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<PlanNode::Kind> expected = {
};
EXPECT_TRUE(checkResult(query, expected));
}
}
} // namespace graph
} // namespace nebula
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment