diff --git a/src/graph/context/ast/QueryAstContext.h b/src/graph/context/ast/QueryAstContext.h index 6cc3e4e975e..9e994d745a7 100644 --- a/src/graph/context/ast/QueryAstContext.h +++ b/src/graph/context/ast/QueryAstContext.h @@ -128,6 +128,16 @@ struct SubgraphContext final : public AstContext { bool getEdgeProp{false}; }; +struct FetchVerticesContext final : public AstContext { + Starts from; + bool distinct{false}; + YieldColumns* yieldExpr{nullptr}; + ExpressionProps exprProps; + + // store the result of the previous sentence + std::string inputVarName; +}; + } // namespace graph } // namespace nebula #endif // GRAPH_CONTEXT_AST_QUERYASTCONTEXT_H_ diff --git a/src/graph/planner/CMakeLists.txt b/src/graph/planner/CMakeLists.txt index 2262a2e2049..9f5791f2f3f 100644 --- a/src/graph/planner/CMakeLists.txt +++ b/src/graph/planner/CMakeLists.txt @@ -40,4 +40,5 @@ nebula_add_library( ngql/GoPlanner.cpp ngql/SubgraphPlanner.cpp ngql/LookupPlanner.cpp + ngql/FetchVerticesPlanner.cpp ) diff --git a/src/graph/planner/PlannersRegister.cpp b/src/graph/planner/PlannersRegister.cpp index c67f5ea9d8c..0166d711c8f 100644 --- a/src/graph/planner/PlannersRegister.cpp +++ b/src/graph/planner/PlannersRegister.cpp @@ -13,6 +13,7 @@ #include "graph/planner/match/PropIndexSeek.h" #include "graph/planner/match/StartVidFinder.h" #include "graph/planner/match/VertexIdSeek.h" +#include "graph/planner/ngql/FetchVerticesPlanner.h" #include "graph/planner/ngql/GoPlanner.h" #include "graph/planner/ngql/LookupPlanner.h" #include "graph/planner/ngql/PathPlanner.h" @@ -47,6 +48,10 @@ void PlannersRegister::registSequential() { auto& planners = Planner::plannersMap()[Sentence::Kind::kGetSubgraph]; planners.emplace_back(&SubgraphPlanner::match, &SubgraphPlanner::make); } + { + auto& planners = Planner::plannersMap()[Sentence::Kind::kFetchVertices]; + planners.emplace_back(&FetchVerticesPlanner::match, &FetchVerticesPlanner::make); + } } void PlannersRegister::registMatch() { diff --git a/src/graph/planner/ngql/FetchVerticesPlanner.cpp b/src/graph/planner/ngql/FetchVerticesPlanner.cpp new file mode 100644 index 00000000000..2782ab622e6 --- /dev/null +++ b/src/graph/planner/ngql/FetchVerticesPlanner.cpp @@ -0,0 +1,69 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#include "graph/planner/ngql/FetchVerticesPlanner.h" + +#include "graph/planner/plan/Query.h" +#include "graph/util/PlannerUtil.h" + +namespace nebula { +namespace graph { + +std::unique_ptr FetchVerticesPlanner::buildVertexProps( + const ExpressionProps::TagIDPropsMap& propsMap) { + if (propsMap.empty()) { + return nullptr; + } + auto vertexProps = std::make_unique(propsMap.size()); + auto fun = [](auto& tag) { + VertexProp vp; + vp.set_tag(tag.first); + std::vector props(tag.second.begin(), tag.second.end()); + vp.set_props(std::move(props)); + return vp; + }; + std::transform(propsMap.begin(), propsMap.end(), vertexProps->begin(), fun); + return vertexProps; +} + +StatusOr FetchVerticesPlanner::transform(AstContext* astCtx) { + fetchCtx_ = static_cast(astCtx); + auto qctx = fetchCtx_->qctx; + auto space = fetchCtx_->space; + auto& starts = fetchCtx_->from; + + std::string vidsVar; + if (!starts.vids.empty() && starts.originalSrc == nullptr) { + PlannerUtil::buildConstantInput(qctx, starts, vidsVar); + } else { + starts.src = starts.originalSrc; + if (starts.fromType == kVariable) { + vidsVar = starts.userDefinedVarName; + } else { + vidsVar = fetchCtx_->inputVarName; + } + } + + SubPlan subPlan; + auto* getVertices = GetVertices::make(qctx, + nullptr, + space.id, + starts.src, + buildVertexProps(fetchCtx_->exprProps.tagProps()), + {}, + fetchCtx_->distinct); + getVertices->setInputVar(vidsVar); + + subPlan.root = Project::make(qctx, getVertices, fetchCtx_->yieldExpr); + if (fetchCtx_->distinct) { + subPlan.root = Dedup::make(qctx, subPlan.root); + } + subPlan.tail = getVertices; + return subPlan; +} + +} // namespace graph +} // namespace nebula diff --git a/src/graph/planner/ngql/FetchVerticesPlanner.h b/src/graph/planner/ngql/FetchVerticesPlanner.h new file mode 100644 index 00000000000..7bbd9e34b91 --- /dev/null +++ b/src/graph/planner/ngql/FetchVerticesPlanner.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#ifndef GRAPH_PLANNER_NGQL_FETCH_VERTICES_PLANNER_H_ +#define GRAPH_PLANNER_NGQL_FETCH_VERTICES_PLANNER_H_ + +#include "common/base/Base.h" +#include "graph/context/ast/QueryAstContext.h" +#include "graph/planner/Planner.h" +#include "graph/planner/plan/PlanNode.h" + +namespace nebula { +namespace graph { +class FetchVerticesPlanner final : public Planner { + public: + using VertexProp = nebula::storage::cpp2::VertexProp; + using VertexProps = std::vector; + + static std::unique_ptr make() { + return std::unique_ptr(new FetchVerticesPlanner()); + } + + static bool match(AstContext* astCtx) { + return astCtx->sentence->kind() == Sentence::Kind::kFetchVertices; + } + + StatusOr transform(AstContext* astCtx) override; + + private: + std::unique_ptr buildVertexProps(const ExpressionProps::TagIDPropsMap& propsMap); + + private: + FetchVerticesPlanner() = default; + + FetchVerticesContext* fetchCtx_{nullptr}; +}; +} // namespace graph +} // namespace nebula + +#endif // GRAPH_PLANNER_NGQL_FETCH_VERTICES_PLANNER_H diff --git a/src/graph/planner/ngql/GoPlanner.cpp b/src/graph/planner/ngql/GoPlanner.cpp index 926f8f10f8d..c12278417a1 100644 --- a/src/graph/planner/ngql/GoPlanner.cpp +++ b/src/graph/planner/ngql/GoPlanner.cpp @@ -6,12 +6,9 @@ #include "graph/planner/ngql/GoPlanner.h" -#include "graph/planner/plan/Algo.h" #include "graph/planner/plan/Logic.h" #include "graph/util/ExpressionUtils.h" -#include "graph/util/QueryUtil.h" -#include "graph/util/SchemaUtil.h" -#include "graph/validator/Validator.h" +#include "graph/util/PlannerUtil.h" namespace nebula { namespace graph { @@ -395,7 +392,7 @@ SubPlan GoPlanner::nStepsPlan(SubPlan& startVidPlan) { gn->setEdgeProps(buildEdgeProps(true)); gn->setInputVar(goCtx_->vidsVar); - auto* getDst = QueryUtil::extractDstFromGN(qctx, gn, goCtx_->vidsVar); + auto* getDst = PlannerUtil::extractDstFromGN(qctx, gn, goCtx_->vidsVar); PlanNode* loopBody = getDst; PlanNode* loopDep = nullptr; @@ -429,7 +426,7 @@ SubPlan GoPlanner::mToNStepsPlan(SubPlan& startVidPlan) { gn->setEdgeProps(buildEdgeProps(false)); gn->setInputVar(goCtx_->vidsVar); - auto* getDst = QueryUtil::extractDstFromGN(qctx, gn, goCtx_->vidsVar); + auto* getDst = PlannerUtil::extractDstFromGN(qctx, gn, goCtx_->vidsVar); auto* loopBody = getDst; auto* loopDep = startVidPlan.root; @@ -487,7 +484,7 @@ StatusOr GoPlanner::transform(AstContext* astCtx) { goCtx_->joinInput = goCtx_->from.fromType != FromType::kInstantExpr; goCtx_->joinDst = !goCtx_->exprProps.dstTagProps().empty(); - SubPlan startPlan = QueryUtil::buildStart(qctx, goCtx_->from, goCtx_->vidsVar); + SubPlan startPlan = PlannerUtil::buildStart(qctx, goCtx_->from, goCtx_->vidsVar); auto& steps = goCtx_->steps; if (steps.isMToN()) { diff --git a/src/graph/planner/ngql/GoPlanner.h b/src/graph/planner/ngql/GoPlanner.h index 4a7343bd6fb..19f7e5dbf93 100644 --- a/src/graph/planner/ngql/GoPlanner.h +++ b/src/graph/planner/ngql/GoPlanner.h @@ -12,7 +12,6 @@ #include "graph/planner/Planner.h" #include "graph/planner/plan/PlanNode.h" #include "graph/planner/plan/Query.h" -#include "graph/util/ExpressionUtils.h" namespace nebula { namespace graph { diff --git a/src/graph/planner/ngql/PathPlanner.cpp b/src/graph/planner/ngql/PathPlanner.cpp index 4509e20e4b8..3e2dab8bca2 100644 --- a/src/graph/planner/ngql/PathPlanner.cpp +++ b/src/graph/planner/ngql/PathPlanner.cpp @@ -8,7 +8,7 @@ #include "graph/planner/plan/Algo.h" #include "graph/planner/plan/Logic.h" #include "graph/util/ExpressionUtils.h" -#include "graph/util/QueryUtil.h" +#include "graph/util/PlannerUtil.h" #include "graph/util/SchemaUtil.h" #include "graph/validator/Validator.h" @@ -62,15 +62,15 @@ void PathPlanner::doBuildEdgeProps(std::unique_ptr>& edgeP void PathPlanner::buildStart(Starts& starts, std::string& vidsVar, bool reverse) { auto qctx = pathCtx_->qctx; if (!starts.vids.empty() && starts.originalSrc == nullptr) { - QueryUtil::buildConstantInput(qctx, starts, vidsVar); + PlannerUtil::buildConstantInput(qctx, starts, vidsVar); } else { if (reverse) { - auto subPlan = QueryUtil::buildRuntimeInput(qctx, starts); + auto subPlan = PlannerUtil::buildRuntimeInput(qctx, starts); pathCtx_->runtimeToProject = subPlan.tail; pathCtx_->runtimeToDedup = subPlan.root; vidsVar = pathCtx_->runtimeToDedup->outputVar(); } else { - auto subPlan = QueryUtil::buildRuntimeInput(qctx, starts); + auto subPlan = PlannerUtil::buildRuntimeInput(qctx, starts); pathCtx_->runtimeFromProject = subPlan.tail; pathCtx_->runtimeFromDedup = subPlan.root; vidsVar = pathCtx_->runtimeFromDedup->outputVar(); diff --git a/src/graph/planner/ngql/SubgraphPlanner.cpp b/src/graph/planner/ngql/SubgraphPlanner.cpp index 1621ff1a573..71220a2a2a3 100644 --- a/src/graph/planner/ngql/SubgraphPlanner.cpp +++ b/src/graph/planner/ngql/SubgraphPlanner.cpp @@ -8,7 +8,7 @@ #include "graph/planner/plan/Algo.h" #include "graph/planner/plan/Logic.h" #include "graph/util/ExpressionUtils.h" -#include "graph/util/QueryUtil.h" +#include "graph/util/PlannerUtil.h" #include "graph/util/SchemaUtil.h" #include "graph/validator/Validator.h" @@ -122,7 +122,7 @@ StatusOr SubgraphPlanner::transform(AstContext* astCtx) { auto qctx = subgraphCtx_->qctx; std::string vidsVar; - SubPlan startPlan = QueryUtil::buildStart(qctx, subgraphCtx_->from, vidsVar); + SubPlan startPlan = PlannerUtil::buildStart(qctx, subgraphCtx_->from, vidsVar); if (subgraphCtx_->steps.steps() == 0) { return zeroStep(startPlan, vidsVar); } diff --git a/src/graph/util/CMakeLists.txt b/src/graph/util/CMakeLists.txt index a9feb25f5fa..da7bdf95a2e 100644 --- a/src/graph/util/CMakeLists.txt +++ b/src/graph/util/CMakeLists.txt @@ -13,7 +13,8 @@ nebula_add_library( ZoneUtil.cpp ToJson.cpp ParserUtil.cpp - QueryUtil.cpp + PlannerUtil.cpp + ValidateUtil.cpp ) nebula_add_library( diff --git a/src/graph/util/QueryUtil.cpp b/src/graph/util/PlannerUtil.cpp similarity index 81% rename from src/graph/util/QueryUtil.cpp rename to src/graph/util/PlannerUtil.cpp index 7c542aaa19a..f6613f55a06 100644 --- a/src/graph/util/QueryUtil.cpp +++ b/src/graph/util/PlannerUtil.cpp @@ -4,7 +4,7 @@ * attached with Common Clause Condition 1.0, found in the LICENSES directory. */ -#include "graph/util/QueryUtil.h" +#include "graph/util/PlannerUtil.h" #include "common/base/Base.h" #include "common/expression/ColumnExpression.h" @@ -17,7 +17,7 @@ namespace nebula { namespace graph { // static -void QueryUtil::buildConstantInput(QueryContext* qctx, Starts& starts, std::string& vidsVar) { +void PlannerUtil::buildConstantInput(QueryContext* qctx, Starts& starts, std::string& vidsVar) { vidsVar = qctx->vctx()->anonVarGen()->getVar(); DataSet ds; ds.colNames.emplace_back(kVid); @@ -33,7 +33,7 @@ void QueryUtil::buildConstantInput(QueryContext* qctx, Starts& starts, std::stri } // static -SubPlan QueryUtil::buildRuntimeInput(QueryContext* qctx, Starts& starts) { +SubPlan PlannerUtil::buildRuntimeInput(QueryContext* qctx, Starts& starts) { auto pool = qctx->objPool(); auto* columns = pool->add(new YieldColumns()); auto* column = new YieldColumn(starts.originalSrc->clone(), kVid); @@ -54,7 +54,7 @@ SubPlan QueryUtil::buildRuntimeInput(QueryContext* qctx, Starts& starts) { } // static -SubPlan QueryUtil::buildStart(QueryContext* qctx, Starts& starts, std::string& vidsVar) { +SubPlan PlannerUtil::buildStart(QueryContext* qctx, Starts& starts, std::string& vidsVar) { SubPlan subPlan; if (!starts.vids.empty() && starts.originalSrc == nullptr) { buildConstantInput(qctx, starts, vidsVar); @@ -65,7 +65,9 @@ SubPlan QueryUtil::buildStart(QueryContext* qctx, Starts& starts, std::string& v return subPlan; } -PlanNode* QueryUtil::extractDstFromGN(QueryContext* qctx, PlanNode* gn, const std::string& output) { +PlanNode* PlannerUtil::extractDstFromGN(QueryContext* qctx, + PlanNode* gn, + const std::string& output) { auto pool = qctx->objPool(); auto* columns = pool->add(new YieldColumns()); auto* column = new YieldColumn(EdgePropertyExpression::make(pool, "*", kDst), kVid); @@ -77,5 +79,6 @@ PlanNode* QueryUtil::extractDstFromGN(QueryContext* qctx, PlanNode* gn, const st dedup->setOutputVar(output); return dedup; } + } // namespace graph } // namespace nebula diff --git a/src/graph/util/QueryUtil.h b/src/graph/util/PlannerUtil.h similarity index 82% rename from src/graph/util/QueryUtil.h rename to src/graph/util/PlannerUtil.h index 3ff1ea0e951..7eb0accbec6 100644 --- a/src/graph/util/QueryUtil.h +++ b/src/graph/util/PlannerUtil.h @@ -4,8 +4,8 @@ * attached with Common Clause Condition 1.0, found in the LICENSES directory. */ -#ifndef GRAPH_UTIL_QUERYUTIL_H_ -#define GRAPH_UTIL_QUERYUTIL_H_ +#ifndef GRAPH_UTIL_PLANNER_UTIL_H_ +#define GRAPH_UTIL_PLANNER_UTIL_H_ #include "common/base/Base.h" namespace nebula { @@ -14,9 +14,9 @@ class QueryContext; struct Starts; struct SubPlan; class PlanNode; -class QueryUtil final { +class PlannerUtil final { public: - QueryUtil() = delete; + PlannerUtil() = delete; static void buildConstantInput(QueryContext* qctx, Starts& starts, std::string& vidsVar); @@ -29,4 +29,4 @@ class QueryUtil final { } // namespace graph } // namespace nebula -#endif // GRAPH_UTIL_ZONEUTIL_H_ +#endif // GRAPH_UTIL_PLANNER_UTIL_H_ diff --git a/src/graph/util/ValidateUtil.cpp b/src/graph/util/ValidateUtil.cpp new file mode 100644 index 00000000000..953c7b75ab2 --- /dev/null +++ b/src/graph/util/ValidateUtil.cpp @@ -0,0 +1,93 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#include "graph/util/ValidateUtil.h" + +#include "common/base/Base.h" +#include "common/expression/ColumnExpression.h" +#include "graph/context/QueryContext.h" +#include "graph/context/ast/QueryAstContext.h" +#include "graph/planner/Planner.h" +#include "graph/planner/plan/Query.h" +#include "graph/util/ExpressionUtils.h" + +namespace nebula { +namespace graph { + +Status ValidateUtil::validateStep(const StepClause* clause, StepClause& step) { + if (clause == nullptr) { + return Status::SemanticError("Step clause nullptr."); + } + step = *clause; + if (clause->isMToN()) { + if (step.mSteps() == 0) { + step.setMSteps(1); + } + if (step.nSteps() < step.mSteps()) { + return Status::SemanticError("`%s', upper bound steps should be greater than lower bound.", + step.toString().c_str()); + } + } + return Status::OK(); +} + +Status ValidateUtil::validateOver(QueryContext* qctx, const OverClause* clause, Over& over) { + if (clause == nullptr) { + return Status::SemanticError("Over clause nullptr."); + } + auto space = qctx->vctx()->whichSpace(); + + over.direction = clause->direction(); + auto* schemaMng = qctx->schemaMng(); + if (clause->isOverAll()) { + auto edgeStatus = schemaMng->getAllEdge(space.id); + NG_RETURN_IF_ERROR(edgeStatus); + auto edges = std::move(edgeStatus).value(); + if (edges.empty()) { + return Status::SemanticError("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::SemanticError( + "`%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::SemanticError( + "%s not found in space [%s].", edgeName.c_str(), space.name.c_str()); + } + over.edgeTypes.emplace_back(edgeType.value()); + } + } + return Status::OK(); +} + +Status ValidateUtil::invalidLabelIdentifiers(const Expression* expr) { + auto labelExprs = ExpressionUtils::collectAll(expr, {Expression::Kind::kLabel}); + if (!labelExprs.empty()) { + std::stringstream ss; + ss << "Invalid label identifiers: "; + for (auto* label : labelExprs) { + ss << label->toString() << ","; + } + auto errMsg = ss.str(); + errMsg.pop_back(); + return Status::SemanticError(std::move(errMsg)); + } + return Status::OK(); +} + +} // namespace graph +} // namespace nebula diff --git a/src/graph/util/ValidateUtil.h b/src/graph/util/ValidateUtil.h new file mode 100644 index 00000000000..ea532e166ad --- /dev/null +++ b/src/graph/util/ValidateUtil.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#ifndef GRAPH_UTIL_VALIDATE_UTIL_H_ +#define GRAPH_UTIL_VALIDATE_UTIL_H_ +#include "common/base/Base.h" +#include "common/base/StatusOr.h" +#include "common/expression/Expression.h" +#include "parser/Clauses.h" + +namespace nebula { +namespace graph { +class QueryContext; +class PlanNode; +struct Over; + +class ValidateUtil final { + public: + ValidateUtil() = delete; + + static Status validateStep(const StepClause* clause, StepClause& step); + + static Status validateOver(QueryContext* qctx, const OverClause* clause, Over& over); + + static Status invalidLabelIdentifiers(const Expression* expr); +}; + +} // namespace graph +} // namespace nebula +#endif // GRAPH_UTIL_VALIDATE_UTIL_H_ diff --git a/src/graph/validator/CMakeLists.txt b/src/graph/validator/CMakeLists.txt index 9138284b442..0c84e7c290a 100644 --- a/src/graph/validator/CMakeLists.txt +++ b/src/graph/validator/CMakeLists.txt @@ -24,7 +24,6 @@ nebula_add_library( LimitValidator.cpp OrderByValidator.cpp YieldValidator.cpp - TraversalValidator.cpp ExplainValidator.cpp GroupByValidator.cpp FindPathValidator.cpp diff --git a/src/graph/validator/FetchEdgesValidator.cpp b/src/graph/validator/FetchEdgesValidator.cpp index 60c5636389f..8158906b940 100644 --- a/src/graph/validator/FetchEdgesValidator.cpp +++ b/src/graph/validator/FetchEdgesValidator.cpp @@ -9,6 +9,7 @@ #include "graph/planner/plan/Query.h" #include "graph/util/ExpressionUtils.h" #include "graph/util/SchemaUtil.h" +#include "graph/util/ValidateUtil.h" namespace nebula { namespace graph { @@ -184,7 +185,7 @@ Status FetchEdgesValidator::preparePropertiesWithYield(const YieldClause *yield) dedup_ = newYield_->isDistinct(); for (auto col : newYield_->columns()) { col->setExpr(ExpressionUtils::rewriteLabelAttr2EdgeProp(col->expr())); - NG_RETURN_IF_ERROR(invalidLabelIdentifiers(col->expr())); + NG_RETURN_IF_ERROR(ValidateUtil::invalidLabelIdentifiers(col->expr())); const auto *invalidExpr = findInvalidYieldExpression(col->expr()); if (invalidExpr != nullptr) { return Status::SemanticError("Invalid newYield_ expression `%s'.", diff --git a/src/graph/validator/FetchVerticesValidator.cpp b/src/graph/validator/FetchVerticesValidator.cpp index 10f870ea231..114c983a281 100644 --- a/src/graph/validator/FetchVerticesValidator.cpp +++ b/src/graph/validator/FetchVerticesValidator.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2020 vesoft inc. All rights reserved. +/* Copyright (c) 2021 vesoft inc. All rights reserved. * * This source code is licensed under Apache 2.0 License, * attached with Common Clause Condition 1.0, found in the LICENSES directory. @@ -7,7 +7,7 @@ #include "graph/planner/plan/Query.h" #include "graph/util/ExpressionUtils.h" -#include "graph/util/SchemaUtil.h" +#include "graph/util/ValidateUtil.h" #include "graph/visitor/DeducePropsVisitor.h" namespace nebula { @@ -16,238 +16,136 @@ namespace graph { static constexpr char VertexID[] = "VertexID"; Status FetchVerticesValidator::validateImpl() { - props_ = std::make_unique>(); - exprs_ = std::make_unique>(); - NG_RETURN_IF_ERROR(check()); - NG_RETURN_IF_ERROR(prepareVertices()); - NG_RETURN_IF_ERROR(prepareProperties()); - return Status::OK(); -} + auto *fSentence = static_cast(sentence_); + fetchCtx_ = getContext(); + fetchCtx_->inputVarName = inputVarName_; -Status FetchVerticesValidator::toPlan() { - // Start [-> some input] -> GetVertices [-> Project] [-> Dedup] [-> next - // stage] -> End - std::string vidsVar = (srcRef_ == nullptr ? buildConstantInput() : buildRuntimeInput()); - auto *getVerticesNode = GetVertices::make(qctx_, - nullptr, - space_.id, - src_, - std::move(props_), - std::move(exprs_), - dedup_, - std::move(orderBy_), - limit_, - filter_); - getVerticesNode->setInputVar(vidsVar); - getVerticesNode->setColNames(gvColNames_); - // pipe will set the input variable - PlanNode *current = getVerticesNode; - - if (withYield_) { - current = Project::make(qctx_, current, newYieldColumns_); - - // Project select properties then dedup - if (dedup_) { - current = Dedup::make(qctx_, current); - - // the framework will add data collect to collect the result - // if the result is required - } - } else { - auto *pool = qctx_->objPool(); - auto *columns = pool->add(new YieldColumns()); - columns->addColumn(new YieldColumn(VertexExpression::make(pool), "vertices_")); - current = Project::make(qctx_, current, columns); - } - root_ = current; - tail_ = getVerticesNode; + NG_RETURN_IF_ERROR(validateTag(fSentence->tags())); + NG_RETURN_IF_ERROR(validateStarts(fSentence->vertices(), fetchCtx_->from)); + NG_RETURN_IF_ERROR(validateYield(fSentence->yieldClause())); return Status::OK(); } -Status FetchVerticesValidator::check() { - auto *sentence = static_cast(sentence_); - - if (!sentence->isAllTagProps()) { - onStar_ = false; - auto tags = sentence->tags()->labels(); - for (const auto &tag : tags) { - auto tagStatus = qctx_->schemaMng()->toTagID(space_.id, *tag); - NG_RETURN_IF_ERROR(tagStatus); - auto tagId = tagStatus.value(); +Expression *FetchVerticesValidator::rewriteIDVertex2Vid(const Expression *expr) { + auto *pool = qctx_->objPool(); + auto matcher = [](const Expression *e) -> bool { + std::string lowerStr = e->toString(); + folly::toLowerAscii(lowerStr); + return e->kind() == Expression::Kind::kFunctionCall && lowerStr == "id(vertex)"; + }; + auto rewriter = [pool](const Expression *e) -> Expression * { + UNUSED(e); + return InputPropertyExpression::make(pool, nebula::kVid); + }; + + return RewriteVisitor::transform(expr, std::move(matcher), std::move(rewriter)); +} - tags_.emplace(*tag, tagId); - auto schema = qctx_->schemaMng()->getTagSchema(space_.id, tagId); - if (schema == nullptr) { - LOG(ERROR) << "No schema found for " << *tag; - return Status::SemanticError("No schema found for `%s'", tag->c_str()); - } - tagsSchema_.emplace(tagId, schema); - } - } else { - onStar_ = true; - const auto allTagsResult = qctx_->schemaMng()->getAllLatestVerTagSchema(space_.id); - NG_RETURN_IF_ERROR(allTagsResult); - const auto allTags = std::move(allTagsResult).value(); - for (const auto &tag : allTags) { +Status FetchVerticesValidator::validateTag(const NameLabelList *nameLabels) { + if (nameLabels == nullptr) { + // all tag + const auto &tagStatus = qctx_->schemaMng()->getAllLatestVerTagSchema(space_.id); + NG_RETURN_IF_ERROR(tagStatus); + for (const auto &tag : tagStatus.value()) { tagsSchema_.emplace(tag.first, tag.second); } - for (const auto &tagSchema : tagsSchema_) { - auto tagNameResult = qctx_->schemaMng()->toTagName(space_.id, tagSchema.first); - NG_RETURN_IF_ERROR(tagNameResult); - tags_.emplace(std::move(tagNameResult).value(), tagSchema.first); + } else { + auto labels = nameLabels->labels(); + auto *schemaMng = qctx_->schemaMng(); + for (const auto &label : labels) { + auto tagStatus = schemaMng->toTagID(space_.id, *label); + NG_RETURN_IF_ERROR(tagStatus); + auto tagID = tagStatus.value(); + auto tagSchema = schemaMng->getTagSchema(space_.id, tagID); + if (tagSchema == nullptr) { + return Status::SemanticError("no schema found for `%s'", label->c_str()); + } + tagsSchema_.emplace(tagID, tagSchema); } } return Status::OK(); } -Status FetchVerticesValidator::prepareVertices() { - auto *sentence = static_cast(sentence_); - // from ref, eval when execute - if (sentence->vertices()->isRef()) { - srcRef_ = sentence->vertices()->ref(); - auto result = checkRef(srcRef_, vidType_); - NG_RETURN_IF_ERROR(result); - inputVar_ = std::move(result).value(); - return Status::OK(); +Status FetchVerticesValidator::validateYield(YieldClause *yield) { + auto pool = qctx_->objPool(); + bool noYield = false; + if (yield == nullptr) { + // TODO: compatible with previous version, this will be deprecated in version 3.0. + auto *yieldColumns = new YieldColumns(); + auto *vertex = new YieldColumn(VertexExpression::make(pool), "vertices_"); + yieldColumns->addColumn(vertex); + yield = pool->add(new YieldClause(yieldColumns)); + noYield = true; + } + fetchCtx_->distinct = yield->isDistinct(); + auto size = yield->columns().size(); + outputs_.reserve(size + 1); + + auto *newCols = pool->add(new YieldColumns()); + if (!noYield) { + outputs_.emplace_back(VertexID, vidType_); + auto *vidCol = new YieldColumn(InputPropertyExpression::make(pool, nebula::kVid), VertexID); + newCols->addColumn(vidCol); } - // from constant, eval now - // TODO(shylock) add eval() method for expression - QueryExpressionContext dummy(nullptr); - auto vids = sentence->vertices()->vidList(); - srcVids_.rows.reserve(vids.size()); - for (const auto vid : vids) { - DCHECK(ExpressionUtils::isConstExpr(vid)); - auto v = vid->eval(dummy); - if (v.type() != vidType_) { - std::stringstream ss; - ss << "`" << vid->toString() << "', the vid should be type of " << vidType_ << ", but was`" - << v.type() << "'"; - return Status::SemanticError(ss.str()); + auto &exprProps = fetchCtx_->exprProps; + for (const auto &col : yield->columns()) { + if (col->expr()->kind() == Expression::Kind::kVertex) { + extractVertexProp(exprProps); + break; } - srcVids_.emplace_back(nebula::Row({std::move(v)})); } - return Status::OK(); -} -// TODO(shylock) select _vid property instead of return always. -Status FetchVerticesValidator::prepareProperties() { - auto *sentence = static_cast(sentence_); - auto *yield = sentence->yieldClause(); - if (yield == nullptr) { - return preparePropertiesWithoutYield(); - } else { - return preparePropertiesWithYield(yield); - } -} - -Status FetchVerticesValidator::preparePropertiesWithYield(const YieldClause *yield) { - withYield_ = true; - // outputs - auto yieldSize = yield->columns().size(); - outputs_.reserve(yieldSize + 1); - gvColNames_.emplace_back(nebula::kVid); - outputs_.emplace_back(VertexID, vidType_); // kVid - - dedup_ = yield->isDistinct(); - ExpressionProps exprProps; - DeducePropsVisitor deducePropsVisitor(qctx_, space_.id, &exprProps, &userDefinedVarNameList_); - - auto *pool = qctx_->objPool(); for (auto col : yield->columns()) { - col->setExpr(ExpressionUtils::rewriteLabelAttr2TagProp(col->expr())); - NG_RETURN_IF_ERROR(invalidLabelIdentifiers(col->expr())); - col->expr()->accept(&deducePropsVisitor); - if (!deducePropsVisitor.ok()) { - return std::move(deducePropsVisitor).status(); - } - if (exprProps.hasInputVarProperty()) { - return Status::SemanticError("Unsupported input/variable property expression in yield."); + if (ExpressionUtils::hasAny(col->expr(), + {Expression::Kind::kEdge, Expression::Kind::kPathBuild})) { + return Status::SemanticError("illegal yield clauses `%s'", col->toString().c_str()); } - if (!exprProps.edgeProps().empty()) { - return Status::SemanticError("Unsupported edge property expression in yield."); - } - if (exprProps.hasSrcDstTagProperty()) { - return Status::SemanticError("Unsupported src/dst property expression in yield."); + col->setExpr(ExpressionUtils::rewriteLabelAttr2TagProp(col->expr())); + NG_RETURN_IF_ERROR(ValidateUtil::invalidLabelIdentifiers(col->expr())); + auto colExpr = col->expr(); + auto typeStatus = deduceExprType(colExpr); + NG_RETURN_IF_ERROR(typeStatus); + outputs_.emplace_back(col->name(), typeStatus.value()); + if (colExpr->kind() == Expression::Kind::kFunctionCall) { + col->setAlias(col->name()); + col->setExpr(rewriteIDVertex2Vid(colExpr)); } + newCols->addColumn(col->clone().release()); - auto typeResult = deduceExprType(col->expr()); - NG_RETURN_IF_ERROR(typeResult); - outputs_.emplace_back(col->name(), typeResult.value()); - // TODO(shylock) think about the push-down expr + NG_RETURN_IF_ERROR(deduceProps(colExpr, exprProps)); } if (exprProps.tagProps().empty()) { - return Status::SemanticError("Unsupported empty tag property expression in yield."); + for (const auto &tagSchema : tagsSchema_) { + exprProps.insertTagProp(tagSchema.first, nebula::kTag); + } } + fetchCtx_->yieldExpr = newCols; - for (const auto &tag : exprProps.tagNameIds()) { - if (tags_.find(tag.first) == tags_.end()) { - return Status::SemanticError("Mismatched tag: %s", tag.first.c_str()); - } + if (exprProps.hasInputVarProperty()) { + return Status::SemanticError("unsupported input/variable property expression in yield."); } - for (const auto &tagNameId : exprProps.tagNameIds()) { - storage::cpp2::VertexProp vProp; - std::vector propNames; - propNames.reserve(exprProps.tagProps().at(tagNameId.second).size()); - vProp.set_tag(tagNameId.second); - for (const auto &prop : exprProps.tagProps().at(tagNameId.second)) { - propNames.emplace_back(prop.toString()); - gvColNames_.emplace_back(tagNameId.first + "." + prop.toString()); - } - vProp.set_props(std::move(propNames)); - props_->emplace_back(std::move(vProp)); + if (exprProps.hasSrcDstTagProperty()) { + return Status::SemanticError("unsupported src/dst property expression in yield."); } - // insert the reserved properties expression be compatible with 1.0 - // TODO(shylock) select kVid from storage - newYieldColumns_ = qctx_->objPool()->add(new YieldColumns()); - // note eval vid by input expression - newYieldColumns_->addColumn( - new YieldColumn(InputPropertyExpression::make(pool, nebula::kVid), VertexID)); - for (auto col : yield->columns()) { - newYieldColumns_->addColumn(col->clone().release()); + for (const auto &tag : exprProps.tagNameIds()) { + if (tagsSchema_.find(tag.second) == tagsSchema_.end()) { + return Status::SemanticError("mismatched tag `%s'", tag.first.c_str()); + } } return Status::OK(); } -Status FetchVerticesValidator::preparePropertiesWithoutYield() { - props_->clear(); - outputs_.emplace_back("vertices_", Value::Type::VERTEX); - gvColNames_.emplace_back(nebula::kVid); +void FetchVerticesValidator::extractVertexProp(ExpressionProps &exprProps) { for (const auto &tagSchema : tagsSchema_) { - storage::cpp2::VertexProp vProp; - vProp.set_tag(tagSchema.first); - std::vector propNames; - propNames.reserve(tagSchema.second->getNumFields() + 1); - auto tagNameResult = qctx_->schemaMng()->toTagName(space_.id, tagSchema.first); - NG_RETURN_IF_ERROR(tagNameResult); - auto tagName = std::move(tagNameResult).value(); + auto tagID = tagSchema.first; + exprProps.insertTagProp(tagID, nebula::kTag); for (std::size_t i = 0; i < tagSchema.second->getNumFields(); ++i) { const auto propName = tagSchema.second->getFieldName(i); - propNames.emplace_back(propName); - gvColNames_.emplace_back(tagName + "." + propName); + exprProps.insertTagProp(tagID, propName); } - gvColNames_.emplace_back(tagName + "._tag"); - propNames.emplace_back(nebula::kTag); // "_tag" - vProp.set_props(std::move(propNames)); - props_->emplace_back(std::move(vProp)); } - return Status::OK(); -} - -// TODO(shylock) optimize dedup input when distinct given -std::string FetchVerticesValidator::buildConstantInput() { - auto input = vctx_->anonVarGen()->getVar(); - qctx_->ectx()->setResult(input, ResultBuilder().value(Value(std::move(srcVids_))).build()); - - auto *pool = qctx_->objPool(); - src_ = VariablePropertyExpression::make(pool, input, kVid); - return input; -} - -std::string FetchVerticesValidator::buildRuntimeInput() { - src_ = DCHECK_NOTNULL(srcRef_); - return inputVar_; } } // namespace graph diff --git a/src/graph/validator/FetchVerticesValidator.h b/src/graph/validator/FetchVerticesValidator.h index b915a3cc0de..608aa39ffd7 100644 --- a/src/graph/validator/FetchVerticesValidator.h +++ b/src/graph/validator/FetchVerticesValidator.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2020 vesoft inc. All rights reserved. +/* Copyright (c) 2021 vesoft inc. All rights reserved. * * This source code is licensed under Apache 2.0 License, * attached with Common Clause Condition 1.0, found in the LICENSES directory. @@ -7,8 +7,8 @@ #ifndef _VALIDATOR_FETCH_VERTICES_VALIDATOR_H_ #define _VALIDATOR_FETCH_VERTICES_VALIDATOR_H_ +#include "graph/context/ast/QueryAstContext.h" #include "graph/validator/Validator.h" -#include "interface/gen-cpp2/storage_types.h" #include "parser/TraverseSentences.h" namespace nebula { @@ -16,52 +16,26 @@ namespace graph { class FetchVerticesValidator final : public Validator { public: - using VertexProp = nebula::storage::cpp2::VertexProp; - using Expr = nebula::storage::cpp2::Expr; FetchVerticesValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) {} private: Status validateImpl() override; - Status toPlan() override; + Status validateTag(const NameLabelList* nameLables); - Status check(); + Status validateYield(YieldClause* yield); - Status prepareVertices(); + AstContext* getAstContext() override { return fetchCtx_.get(); } - Status preparePropertiesWithYield(const YieldClause* yield); - Status preparePropertiesWithoutYield(); - Status prepareProperties(); + void extractVertexProp(ExpressionProps& exprProps); - // TODO(shylock) merge the code - std::string buildConstantInput(); - std::string buildRuntimeInput(); + Expression* rewriteIDVertex2Vid(const Expression* expr); private: - // src from constant - DataSet srcVids_{{kVid}}; - // src from runtime - Expression* srcRef_{nullptr}; - Expression* src_{nullptr}; - bool onStar_{false}; - std::unordered_map tags_; std::map> tagsSchema_; - std::unique_ptr> props_; - std::unique_ptr> exprs_; - bool dedup_{false}; - std::vector orderBy_{}; - int64_t limit_{std::numeric_limits::max()}; - Expression* filter_{nullptr}; - // valid when yield expression not require storage - // So expression like these will be evaluate in Project Executor - bool withYield_{false}; - // outputs - std::vector gvColNames_; - // new yield to inject reserved properties for compatible with 1.0 - YieldColumns* newYieldColumns_{nullptr}; - // input - std::string inputVar_; // empty when pipe or no input in fact + + std::unique_ptr fetchCtx_; }; } // namespace graph diff --git a/src/graph/validator/FindPathValidator.cpp b/src/graph/validator/FindPathValidator.cpp index c7d4c5384d1..2f4975149f9 100644 --- a/src/graph/validator/FindPathValidator.cpp +++ b/src/graph/validator/FindPathValidator.cpp @@ -9,6 +9,7 @@ #include "common/expression/VariableExpression.h" #include "graph/planner/plan/Algo.h" #include "graph/planner/plan/Logic.h" +#include "graph/util/ValidateUtil.h" namespace nebula { namespace graph { @@ -22,9 +23,9 @@ Status FindPathValidator::validateImpl() { NG_RETURN_IF_ERROR(validateStarts(fpSentence->from(), pathCtx_->from)); NG_RETURN_IF_ERROR(validateStarts(fpSentence->to(), pathCtx_->to)); - NG_RETURN_IF_ERROR(validateOver(fpSentence->over(), pathCtx_->over)); + NG_RETURN_IF_ERROR(ValidateUtil::validateOver(qctx_, fpSentence->over(), pathCtx_->over)); NG_RETURN_IF_ERROR(validateWhere(fpSentence->where())); - NG_RETURN_IF_ERROR(validateStep(fpSentence->step(), pathCtx_->steps)); + NG_RETURN_IF_ERROR(ValidateUtil::validateStep(fpSentence->step(), pathCtx_->steps)); outputs_.emplace_back("path", Value::Type::PATH); return Status::OK(); diff --git a/src/graph/validator/FindPathValidator.h b/src/graph/validator/FindPathValidator.h index 2cc367dc197..0f2d03a23ec 100644 --- a/src/graph/validator/FindPathValidator.h +++ b/src/graph/validator/FindPathValidator.h @@ -9,15 +9,14 @@ #include "common/base/Base.h" #include "graph/context/ast/QueryAstContext.h" -#include "graph/validator/TraversalValidator.h" +#include "graph/validator/Validator.h" namespace nebula { namespace graph { -class FindPathValidator final : public TraversalValidator { +class FindPathValidator final : public Validator { public: - FindPathValidator(Sentence* sentence, QueryContext* context) - : TraversalValidator(sentence, context) {} + FindPathValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) {} private: Status validateImpl() override; diff --git a/src/graph/validator/GetSubgraphValidator.cpp b/src/graph/validator/GetSubgraphValidator.cpp index 2f4fc5f68d3..fd7cf6fb184 100644 --- a/src/graph/validator/GetSubgraphValidator.cpp +++ b/src/graph/validator/GetSubgraphValidator.cpp @@ -14,6 +14,7 @@ #include "graph/context/QueryExpressionContext.h" #include "graph/planner/plan/Logic.h" #include "graph/planner/plan/Query.h" +#include "graph/util/ValidateUtil.h" #include "parser/TraverseSentences.h" namespace nebula { @@ -24,7 +25,7 @@ Status GetSubgraphValidator::validateImpl() { subgraphCtx_ = getContext(); subgraphCtx_->withProp = gsSentence->withProp(); - NG_RETURN_IF_ERROR(validateStep(gsSentence->step(), subgraphCtx_->steps)); + NG_RETURN_IF_ERROR(ValidateUtil::validateStep(gsSentence->step(), subgraphCtx_->steps)); NG_RETURN_IF_ERROR(validateStarts(gsSentence->from(), subgraphCtx_->from)); NG_RETURN_IF_ERROR(validateInBound(gsSentence->in())); NG_RETURN_IF_ERROR(validateOutBound(gsSentence->out())); diff --git a/src/graph/validator/GetSubgraphValidator.h b/src/graph/validator/GetSubgraphValidator.h index 1bcfce43e03..e51bfefc4f1 100644 --- a/src/graph/validator/GetSubgraphValidator.h +++ b/src/graph/validator/GetSubgraphValidator.h @@ -8,15 +8,14 @@ #define GRAPH_VALIDATOR_GETSUBGRAPHVALIDATOR_H_ #include "graph/context/ast/QueryAstContext.h" -#include "graph/validator/TraversalValidator.h" +#include "graph/validator/Validator.h" #include "parser/Clauses.h" namespace nebula { namespace graph { -class GetSubgraphValidator final : public TraversalValidator { +class GetSubgraphValidator final : public Validator { public: - GetSubgraphValidator(Sentence* sentence, QueryContext* context) - : TraversalValidator(sentence, context) {} + GetSubgraphValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) {} private: Status validateImpl() override; diff --git a/src/graph/validator/GoValidator.cpp b/src/graph/validator/GoValidator.cpp index a6f6c5f715a..ed43b87d03c 100644 --- a/src/graph/validator/GoValidator.cpp +++ b/src/graph/validator/GoValidator.cpp @@ -10,6 +10,7 @@ #include "common/expression/VariableExpression.h" #include "graph/planner/plan/Logic.h" #include "graph/util/ExpressionUtils.h" +#include "graph/util/ValidateUtil.h" #include "graph/visitor/ExtractPropExprVisitor.h" #include "parser/TraverseSentences.h" @@ -20,9 +21,9 @@ Status GoValidator::validateImpl() { goCtx_ = getContext(); goCtx_->inputVarName = inputVarName_; - NG_RETURN_IF_ERROR(validateStep(goSentence->stepClause(), goCtx_->steps)); + NG_RETURN_IF_ERROR(ValidateUtil::validateStep(goSentence->stepClause(), goCtx_->steps)); NG_RETURN_IF_ERROR(validateStarts(goSentence->fromClause(), goCtx_->from)); - NG_RETURN_IF_ERROR(validateOver(goSentence->overClause(), goCtx_->over)); + NG_RETURN_IF_ERROR(ValidateUtil::validateOver(qctx_, goSentence->overClause(), goCtx_->over)); NG_RETURN_IF_ERROR(validateWhere(goSentence->whereClause())); NG_RETURN_IF_ERROR(validateYield(goSentence->yieldClause())); NG_RETURN_IF_ERROR(validateTruncate(goSentence->truncateClause())); @@ -116,9 +117,6 @@ Status GoValidator::validateTruncate(TruncateClause* truncate) { } Status GoValidator::validateYield(YieldClause* yield) { - if (yield == nullptr) { - return Status::SemanticError("Yield clause nullptr."); - } goCtx_->distinct = yield->isDistinct(); const auto& over = goCtx_->over; auto* pool = qctx_->objPool(); @@ -140,7 +138,7 @@ Status GoValidator::validateYield(YieldClause* yield) { for (auto col : cols) { col->setExpr(ExpressionUtils::rewriteLabelAttr2EdgeProp(col->expr())); - NG_RETURN_IF_ERROR(invalidLabelIdentifiers(col->expr())); + NG_RETURN_IF_ERROR(ValidateUtil::invalidLabelIdentifiers(col->expr())); auto* colExpr = col->expr(); if (graph::ExpressionUtils::findAny(colExpr, {Expression::Kind::kAggregate})) { diff --git a/src/graph/validator/GoValidator.h b/src/graph/validator/GoValidator.h index 140bf92530c..e2f97866b17 100644 --- a/src/graph/validator/GoValidator.h +++ b/src/graph/validator/GoValidator.h @@ -9,16 +9,16 @@ #include "graph/context/ast/QueryAstContext.h" #include "graph/planner/plan/Query.h" -#include "graph/validator/TraversalValidator.h" +#include "graph/validator/Validator.h" namespace nebula { namespace graph { -class GoValidator final : public TraversalValidator { +class GoValidator final : public Validator { public: using VertexProp = nebula::storage::cpp2::VertexProp; using EdgeProp = nebula::storage::cpp2::EdgeProp; - GoValidator(Sentence* sentence, QueryContext* context) : TraversalValidator(sentence, context) {} + GoValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) {} private: Status validateImpl() override; diff --git a/src/graph/validator/MatchValidator.cpp b/src/graph/validator/MatchValidator.cpp index 4d8823e6a33..d5e8a3a92d6 100644 --- a/src/graph/validator/MatchValidator.cpp +++ b/src/graph/validator/MatchValidator.cpp @@ -13,7 +13,7 @@ namespace nebula { namespace graph { MatchValidator::MatchValidator(Sentence *sentence, QueryContext *context) - : TraversalValidator(sentence, context) { + : Validator(sentence, context) { matchCtx_ = getContext(); } @@ -353,6 +353,12 @@ Status MatchValidator::validateReturn(MatchReturn *ret, } if (ret->returnItems()->columns()) { for (auto *column : ret->returnItems()->columns()->columns()) { + if (ExpressionUtils::hasAny(column->expr(), + {Expression::Kind::kVertex, Expression::Kind::kEdge})) { + return Status::SemanticError( + "keywords: vertex and edge are not supported in return clause `%s'", + column->toString().c_str()); + } columns->addColumn(column->clone().release()); } } diff --git a/src/graph/validator/MatchValidator.h b/src/graph/validator/MatchValidator.h index 997744b2107..762589a2dc8 100644 --- a/src/graph/validator/MatchValidator.h +++ b/src/graph/validator/MatchValidator.h @@ -11,14 +11,14 @@ #include "graph/context/ast/CypherAstContext.h" #include "graph/planner/plan/Query.h" #include "graph/util/AnonVarGenerator.h" -#include "graph/validator/TraversalValidator.h" +#include "graph/validator/Validator.h" namespace nebula { class MatchStepRange; class ObjectPool; namespace graph { -class MatchValidator final : public TraversalValidator { +class MatchValidator final : public Validator { public: MatchValidator(Sentence *sentence, QueryContext *context); diff --git a/src/graph/validator/TraversalValidator.cpp b/src/graph/validator/TraversalValidator.cpp deleted file mode 100644 index 01cb47f0bb3..00000000000 --- a/src/graph/validator/TraversalValidator.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* Copyright (c) 2020 vesoft inc. All rights reserved. - * - * This source code is licensed under Apache 2.0 License, - * attached with Common Clause Condition 1.0, found in the LICENSES directory. - */ - -#include "graph/validator/TraversalValidator.h" - -#include - -#include "common/expression/VariableExpression.h" -#include "graph/util/SchemaUtil.h" - -namespace nebula { -namespace graph { - -Status TraversalValidator::validateStarts(const VerticesClause* clause, Starts& starts) { - if (clause == nullptr) { - return Status::SemanticError("From clause nullptr."); - } - if (clause->isRef()) { - auto* src = clause->ref(); - if (src->kind() != Expression::Kind::kInputProperty && - src->kind() != Expression::Kind::kVarProperty) { - return Status::SemanticError( - "`%s', Only input and variable expression is acceptable" - " when starts are evaluated at runtime.", - src->toString().c_str()); - } - starts.fromType = src->kind() == Expression::Kind::kInputProperty ? kPipe : kVariable; - auto type = deduceExprType(src); - if (!type.ok()) { - return type.status(); - } - auto vidType = space_.spaceDesc.vid_type_ref().value().get_type(); - if (type.value() != SchemaUtil::propTypeToValueType(vidType)) { - std::stringstream ss; - ss << "`" << src->toString() << "', the srcs should be type of " - << apache::thrift::util::enumNameSafe(vidType) << ", but was`" << type.value() << "'"; - return Status::SemanticError(ss.str()); - } - starts.originalSrc = src; - auto* propExpr = static_cast(src); - if (starts.fromType == kVariable) { - starts.userDefinedVarName = propExpr->sym(); - userDefinedVarNameList_.emplace(starts.userDefinedVarName); - } - starts.runtimeVidName = propExpr->prop(); - } else { - auto vidList = clause->vidList(); - QueryExpressionContext ctx; - for (auto* expr : vidList) { - if (!evaluableExpr(expr)) { - return Status::SemanticError("`%s' is not an evaluable expression.", - expr->toString().c_str()); - } - auto vid = expr->eval(ctx(nullptr)); - auto vidType = space_.spaceDesc.vid_type_ref().value().get_type(); - if (!SchemaUtil::isValidVid(vid, vidType)) { - std::stringstream ss; - ss << "Vid should be a " << apache::thrift::util::enumNameSafe(vidType); - return Status::SemanticError(ss.str()); - } - starts.vids.emplace_back(std::move(vid)); - } - } - return Status::OK(); -} - -Status TraversalValidator::validateOver(const OverClause* clause, Over& over) { - if (clause == nullptr) { - return Status::SemanticError("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::SemanticError("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::SemanticError( - "`%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::SemanticError( - "%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, StepClause& step) { - if (clause == nullptr) { - return Status::SemanticError("Step clause nullptr."); - } - step = *clause; - if (clause->isMToN()) { - if (step.mSteps() == 0) { - step.setMSteps(1); - } - if (step.nSteps() < step.mSteps()) { - return Status::SemanticError("`%s', upper bound steps should be greater than lower bound.", - step.toString().c_str()); - } - } - return Status::OK(); -} - -} // namespace graph -} // namespace nebula diff --git a/src/graph/validator/TraversalValidator.h b/src/graph/validator/TraversalValidator.h deleted file mode 100644 index d6a622c1493..00000000000 --- a/src/graph/validator/TraversalValidator.h +++ /dev/null @@ -1,34 +0,0 @@ -/* Copyright (c) 2020 vesoft inc. All rights reserved. - * - * This source code is licensed under Apache 2.0 License, - * attached with Common Clause Condition 1.0, found in the LICENSES directory. - */ - -#ifndef GRAPH_VALIDATOR_TRAVERSALVALIDATOR_H_ -#define GRAPH_VALIDATOR_TRAVERSALVALIDATOR_H_ - -#include "common/base/Base.h" -#include "graph/context/ast/QueryAstContext.h" -#include "graph/planner/plan/Query.h" -#include "graph/util/ExpressionUtils.h" -#include "graph/validator/Validator.h" - -namespace nebula { -namespace graph { - -// some utils for the validator to traverse the graph -class TraversalValidator : public Validator { - protected: - TraversalValidator(Sentence* sentence, QueryContext* qctx) : Validator(sentence, qctx) {} - - Status validateStarts(const VerticesClause* clause, Starts& starts); - - Status validateOver(const OverClause* clause, Over& over); - - Status validateStep(const StepClause* clause, StepClause& step); -}; - -} // namespace graph -} // namespace nebula - -#endif diff --git a/src/graph/validator/Validator.cpp b/src/graph/validator/Validator.cpp index dd5f330a2e3..135e161a6f7 100644 --- a/src/graph/validator/Validator.cpp +++ b/src/graph/validator/Validator.cpp @@ -6,6 +6,8 @@ #include "graph/validator/Validator.h" +#include + #include "common/function/FunctionManager.h" #include "graph/planner/plan/Query.h" #include "graph/util/ExpressionUtils.h" @@ -449,20 +451,58 @@ Status Validator::checkDuplicateColName() { return Status::OK(); } -Status Validator::invalidLabelIdentifiers(const Expression* expr) const { - auto labelExprs = ExpressionUtils::collectAll(expr, {Expression::Kind::kLabel}); - if (!labelExprs.empty()) { - std::stringstream ss; - ss << "Invalid label identifiers: "; - for (auto* label : labelExprs) { - ss << label->toString() << ","; +Status Validator::validateStarts(const VerticesClause* clause, Starts& starts) { + if (clause == nullptr) { + return Status::SemanticError("From clause nullptr."); + } + if (clause->isRef()) { + auto* src = clause->ref(); + if (src->kind() != Expression::Kind::kInputProperty && + src->kind() != Expression::Kind::kVarProperty) { + return Status::SemanticError( + "`%s', Only input and variable expression is acceptable" + " when starts are evaluated at runtime.", + src->toString().c_str()); + } + starts.fromType = src->kind() == Expression::Kind::kInputProperty ? kPipe : kVariable; + auto type = deduceExprType(src); + if (!type.ok()) { + return type.status(); + } + auto vidType = space_.spaceDesc.vid_type_ref().value().get_type(); + if (type.value() != SchemaUtil::propTypeToValueType(vidType)) { + std::stringstream ss; + ss << "`" << src->toString() << "', the srcs should be type of " + << apache::thrift::util::enumNameSafe(vidType) << ", but was`" << type.value() << "'"; + return Status::SemanticError(ss.str()); + } + starts.originalSrc = src; + auto* propExpr = static_cast(src); + if (starts.fromType == kVariable) { + starts.userDefinedVarName = propExpr->sym(); + userDefinedVarNameList_.emplace(starts.userDefinedVarName); + } + starts.runtimeVidName = propExpr->prop(); + } else { + auto vidList = clause->vidList(); + QueryExpressionContext ctx; + for (auto* expr : vidList) { + if (!evaluableExpr(expr)) { + return Status::SemanticError("`%s' is not an evaluable expression.", + expr->toString().c_str()); + } + auto vid = expr->eval(ctx(nullptr)); + auto vidType = space_.spaceDesc.vid_type_ref().value().get_type(); + if (!SchemaUtil::isValidVid(vid, vidType)) { + std::stringstream ss; + ss << "Vid should be a " << apache::thrift::util::enumNameSafe(vidType); + return Status::SemanticError(ss.str()); + } + starts.vids.emplace_back(std::move(vid)); } - auto errMsg = ss.str(); - errMsg.pop_back(); - return Status::SemanticError(std::move(errMsg)); } - return Status::OK(); } + } // namespace graph } // namespace nebula diff --git a/src/graph/validator/Validator.h b/src/graph/validator/Validator.h index 69e8bcbf8d1..3b204af51ab 100644 --- a/src/graph/validator/Validator.h +++ b/src/graph/validator/Validator.h @@ -19,7 +19,7 @@ namespace nebula { namespace graph { - +struct Starts; class Validator { public: virtual ~Validator() = default; @@ -127,14 +127,14 @@ class Validator { return Status::OK(); } + // Check the output for duplicate column names + Status checkDuplicateColName(); + // Check the variable or input property reference // return the input variable StatusOr checkRef(const Expression* ref, const Value::Type type); - // Check the output for duplicate column names - Status checkDuplicateColName(); - - Status invalidLabelIdentifiers(const Expression* expr) const; + Status validateStarts(const VerticesClause* clause, Starts& starts); template std::unique_ptr getContext() const { diff --git a/src/graph/validator/YieldValidator.cpp b/src/graph/validator/YieldValidator.cpp index fa61b759658..e36c57f9271 100644 --- a/src/graph/validator/YieldValidator.cpp +++ b/src/graph/validator/YieldValidator.cpp @@ -10,6 +10,7 @@ #include "graph/context/QueryContext.h" #include "graph/planner/plan/Query.h" #include "graph/util/ExpressionUtils.h" +#include "graph/util/ValidateUtil.h" #include "parser/Clauses.h" #include "parser/TraverseSentences.h" @@ -111,7 +112,7 @@ Status YieldValidator::validateYieldAndBuildOutputs(const YieldClause *clause) { columns_ = pool->add(new YieldColumns); for (auto column : columns) { auto expr = DCHECK_NOTNULL(column->expr()); - NG_RETURN_IF_ERROR(invalidLabelIdentifiers(expr)); + NG_RETURN_IF_ERROR(ValidateUtil::invalidLabelIdentifiers(expr)); if (expr->kind() == Expression::Kind::kInputProperty) { auto ipe = static_cast(expr); diff --git a/src/graph/validator/test/FetchVerticesTest.cpp b/src/graph/validator/test/FetchVerticesTest.cpp index d574882b93c..2d83805a4cb 100644 --- a/src/graph/validator/test/FetchVerticesTest.cpp +++ b/src/graph/validator/test/FetchVerticesTest.cpp @@ -23,7 +23,7 @@ class FetchVerticesValidatorTest : public ValidatorTestBase { }; TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { - auto src = VariablePropertyExpression::make(pool_.get(), "_VARNAME_", "VertexID"); + auto src = ColumnExpression::make(pool_.get(), 0); { auto qctx = getQCtx("FETCH PROP ON person \"1\""); auto *pool = qctx->objPool(); @@ -42,7 +42,6 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { auto yieldColumns = std::make_unique(); yieldColumns->addColumn(new YieldColumn(VertexExpression::make(pool), "vertices_")); auto *project = Project::make(qctx, gv, yieldColumns.get()); - project->setColNames({"vertices_"}); auto result = Eq(qctx->plan()->root(), project); ASSERT_TRUE(result.ok()) << result; } @@ -73,7 +72,6 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { auto yieldColumns = std::make_unique(); yieldColumns->addColumn(new YieldColumn(VertexExpression::make(pool), "vertices_")); auto *project = Project::make(qctx, gv, yieldColumns.get()); - project->setColNames({"vertices_"}); auto result = Eq(qctx->plan()->root(), project); ASSERT_TRUE(result.ok()) << result; } @@ -407,7 +405,6 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { auto yieldColumns = std::make_unique(); yieldColumns->addColumn(new YieldColumn(VertexExpression::make(pool), "vertices_")); auto *project = Project::make(qctx, gv, yieldColumns.get()); - project->setColNames({"vertices_"}); auto result = Eq(qctx->plan()->root(), project); ASSERT_TRUE(result.ok()) << result; } @@ -422,7 +419,6 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { auto yieldColumns = std::make_unique(); yieldColumns->addColumn(new YieldColumn(VertexExpression::make(pool), "vertices_")); auto *project = Project::make(qctx, gv, yieldColumns.get()); - project->setColNames({"vertices_"}); auto result = Eq(qctx->plan()->root(), project); ASSERT_TRUE(result.ok()) << result; } @@ -443,7 +439,6 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { new YieldColumn(InputPropertyExpression::make(pool, nebula::kVid), "VertexID")); yieldColumns->addColumn(new YieldColumn(TagPropertyExpression::make(pool, "person", "name"))); auto *project = Project::make(qctx, gv, yieldColumns.get()); - project->setColNames(colNames); auto result = Eq(qctx->plan()->root(), project); ASSERT_TRUE(result.ok()) << result; @@ -465,7 +460,6 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { yieldColumns->addColumn(new YieldColumn(TagPropertyExpression::make(pool, "person", "name"))); yieldColumns->addColumn(new YieldColumn(TagPropertyExpression::make(pool, "person", "age"))); auto *project = Project::make(qctx, gv, yieldColumns.get()); - project->setColNames(colNames); auto result = Eq(qctx->plan()->root(), project); ASSERT_TRUE(result.ok()) << result; @@ -488,7 +482,6 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { yieldColumns->addColumn(new YieldColumn(TagPropertyExpression::make(pool, "person", "name"))); yieldColumns->addColumn(new YieldColumn(TagPropertyExpression::make(pool, "person", "age"))); auto *project = Project::make(qctx, gv, yieldColumns.get()); - project->setColNames({"VertexID", "(1+1)", "person.name", "person.age"}); auto result = Eq(qctx->plan()->root(), project); ASSERT_TRUE(result.ok()) << result; diff --git a/src/graph/validator/test/MatchValidatorTest.cpp b/src/graph/validator/test/MatchValidatorTest.cpp index 03947c59366..43bbb9faa6c 100644 --- a/src/graph/validator/test/MatchValidatorTest.cpp +++ b/src/graph/validator/test/MatchValidatorTest.cpp @@ -611,6 +611,34 @@ TEST_F(MatchValidatorTest, validateAlias) { EXPECT_EQ(std::string(result.message()), "SemanticError: Path `p' does not have the type attribute"); } + { + std::string query = "MATCH (v:person) return id(vertex)"; + auto result = checkResult(query); + EXPECT_EQ( + std::string(result.message()), + "SemanticError: keywords: vertex and edge are not supported in return clause `id(VERTEX)'"); + } + { + std::string query = "MATCH (v:person) return vertex as a"; + auto result = checkResult(query); + EXPECT_EQ(std::string(result.message()), + "SemanticError: keywords: vertex and edge are not supported in return clause `VERTEX " + "AS a'"); + } + { + std::string query = "MATCH (v:person)-[e]-(v2) return src(edge)"; + auto result = checkResult(query); + EXPECT_EQ( + std::string(result.message()), + "SemanticError: keywords: vertex and edge are not supported in return clause `src(EDGE)'"); + } + { + std::string query = "MATCH (v:person)-[e]-(v2) return edge as b"; + auto result = checkResult(query); + EXPECT_EQ( + std::string(result.message()), + "SemanticError: keywords: vertex and edge are not supported in return clause `EDGE AS b'"); + } } } // namespace graph diff --git a/src/graph/validator/test/QueryValidatorTest.cpp b/src/graph/validator/test/QueryValidatorTest.cpp index 025e3475f5a..ac931a86420 100644 --- a/src/graph/validator/test/QueryValidatorTest.cpp +++ b/src/graph/validator/test/QueryValidatorTest.cpp @@ -950,6 +950,12 @@ TEST_F(QueryValidatorTest, GoInvalid) { auto result = checkResult(query); EXPECT_EQ(std::string(result.message()), "SemanticError: Duplicate Column Name : `id'"); } + { + std::string query = "GO FROM id(vertex) OVER * "; + auto result = checkResult(query); + EXPECT_EQ(std::string(result.message()), + "SemanticError: `id(VERTEX)' is not an evaluable expression."); + } } TEST_F(QueryValidatorTest, Limit) { diff --git a/src/parser/TraverseSentences.h b/src/parser/TraverseSentences.h index b5ac8df07d4..5dcfd2d5a38 100644 --- a/src/parser/TraverseSentences.h +++ b/src/parser/TraverseSentences.h @@ -243,9 +243,7 @@ class FetchVerticesSentence final : public Sentence { yieldClause_.reset(clause); } - bool isAllTagProps() { return tags_->empty(); } - - const NameLabelList* tags() const { return tags_.get(); } + const NameLabelList* tags() const { return tags_->empty() ? nullptr : tags_.get(); } const VerticesClause* vertices() const { return vertices_.get(); } diff --git a/src/parser/parser.yy b/src/parser/parser.yy index 54888f2fec5..93dd0e7b232 100644 --- a/src/parser/parser.yy +++ b/src/parser/parser.yy @@ -1048,7 +1048,17 @@ opt_argument_list ; argument_list - : expression { + : KW_VERTEX { + $$ = ArgumentList::make(qctx->objPool()); + Expression* arg = VertexExpression::make(qctx->objPool()); + $$->addArgument(arg); + } + | KW_EDGE { + $$ = ArgumentList::make(qctx->objPool()); + Expression *arg = EdgeExpression::make(qctx->objPool()); + $$->addArgument(arg); + } + | expression { $$ = ArgumentList::make(qctx->objPool()); Expression* arg = nullptr; arg = $1; diff --git a/tests/tck/features/fetch/FetchEmpty.feature b/tests/tck/features/fetch/FetchEmpty.feature index 43a5821d054..3fd183e831b 100644 --- a/tests/tck/features/fetch/FetchEmpty.feature +++ b/tests/tck/features/fetch/FetchEmpty.feature @@ -44,9 +44,8 @@ Feature: Fetch prop on empty tag/edge | ("1":zero_prop_tag_0) | When executing query: """ - GO FROM "1" OVER zero_prop_edge - YIELD zero_prop_edge._dst as id - | FETCH PROP ON zero_prop_tag_0 $-.id + GO FROM "1" OVER zero_prop_edge YIELD zero_prop_edge._dst as id | + FETCH PROP ON zero_prop_tag_0 $-.id """ Then the result should be, in any order, with relax comparison: | vertices_ | @@ -75,9 +74,8 @@ Feature: Fetch prop on empty tag/edge | edges_ | When executing query: """ - GO FROM "1" OVER zero_prop_edge - YIELD zero_prop_edge._src as src, zero_prop_edge._dst as dst - | FETCH PROP ON zero_prop_edge $-.src->$-.dst + GO FROM "1" OVER zero_prop_edge YIELD zero_prop_edge._src as src, zero_prop_edge._dst as dst | + FETCH PROP ON zero_prop_edge $-.src->$-.dst """ Then the result should be, in any order: | edges_ | diff --git a/tests/tck/features/fetch/FetchVertices.intVid.feature b/tests/tck/features/fetch/FetchVertices.intVid.feature index 30e092fcf6d..2ea116fe320 100644 --- a/tests/tck/features/fetch/FetchVertices.intVid.feature +++ b/tests/tck/features/fetch/FetchVertices.intVid.feature @@ -104,7 +104,8 @@ Feature: Fetch Int Vid Vertices Scenario: Fetch from pipe When executing query: """ - GO FROM hash('Boris Diaw') over like YIELD like._dst as id | FETCH PROP ON player $-.id YIELD player.name, player.age + GO FROM hash('Boris Diaw') over like YIELD like._dst as id | + FETCH PROP ON player $-.id YIELD player.name, player.age """ Then the result should be, in any order, and the columns 0 should be hashed: | VertexID | player.name | player.age | @@ -113,22 +114,24 @@ Feature: Fetch Int Vid Vertices # empty input When executing query: """ - GO FROM hash('NON EXIST VERTEX ID') over like YIELD like._dst as id | FETCH PROP ON player $-.id yield player.name + GO FROM hash('NON EXIST VERTEX ID') over like YIELD like._dst as id | + FETCH PROP ON player $-.id yield player.name """ Then the result should be, in any order: | VertexID | player.name | When executing query: """ - GO FROM hash('NON EXIST VERTEX ID') over serve YIELD serve._dst as id, serve.start_year as start - | YIELD $-.id as id WHERE $-.start > 20000 | FETCH PROP ON player $-.id yield player.name + GO FROM hash('NON EXIST VERTEX ID') over serve YIELD serve._dst as id, serve.start_year as start | + YIELD $-.id as id WHERE $-.start > 20000 | + FETCH PROP ON player $-.id yield player.name """ Then the result should be, in any order: | VertexID | player.name | # Fetch prop on multi tags of vertices from pipe When executing query: """ - GO FROM hash("Boris Diaw") over like YIELD like._dst as id - | FETCH PROP ON player, team, bachelor $-.id YIELD player.name, player.age, team.name, bachelor.name, bachelor.speciality + GO FROM hash("Boris Diaw") over like YIELD like._dst as id | + FETCH PROP ON player, team, bachelor $-.id YIELD player.name, player.age, team.name, bachelor.name, bachelor.speciality """ Then the result should be, in any order, and the columns 0 should be hashed: | VertexID | player.name | player.age | team.name | bachelor.name | bachelor.speciality | @@ -136,8 +139,8 @@ Feature: Fetch Int Vid Vertices | "Tony Parker" | "Tony Parker" | 36 | EMPTY | EMPTY | EMPTY | When executing query: """ - GO FROM hash('Boris Diaw') over like YIELD like._dst as id - | FETCH PROP ON player, bachelor $-.id YIELD player.name, player.age, bachelor.name, bachelor.speciality + GO FROM hash('Boris Diaw') over like YIELD like._dst as id | + FETCH PROP ON player, bachelor $-.id YIELD player.name, player.age, bachelor.name, bachelor.speciality """ Then the result should be, in any order, and the columns 0 should be hashed: | VertexID | player.name | player.age | bachelor.name | bachelor.speciality | @@ -266,8 +269,8 @@ Feature: Fetch Int Vid Vertices Scenario: Fetch vertices and then GO When executing query: """ - FETCH PROP ON player hash('Tony Parker') YIELD player.name as Name - | GO FROM $-.VertexID OVER like + FETCH PROP ON player hash('Tony Parker') YIELD player.name as Name | + GO FROM $-.VertexID OVER like """ Then the result should be, in any order, and the columns 0 should be hashed: | like._dst | @@ -276,18 +279,33 @@ Feature: Fetch Int Vid Vertices | "Tim Duncan" | Scenario: Typical errors + When executing query: + """ + FETCH PROP ON player hash('Boris Diaw') YIELD vertex + """ + Then a SyntaxError should be raised at runtime: please add alias when using vertex. near `vertex' + When executing query: + """ + FETCH PROP ON player hash('Boris Diaw') YIELD edge as a + """ + Then a SemanticError should be raised at runtime: illegal yield clauses `EDGE AS a' + When executing query: + """ + FETCH PROP ON player hash('Boris Diaw') YIELD src(edge) + """ + Then a SemanticError should be raised at runtime: illegal yield clauses `src(EDGE)' # not support get src property When executing query: """ FETCH PROP ON player hash('Boris Diaw') YIELD $^.player.name, player.age """ - Then a SemanticError should be raised at runtime: Unsupported src/dst property expression in yield. + Then a SemanticError should be raised at runtime: unsupported src/dst property expression in yield. # not support get dst property When executing query: """ FETCH PROP ON player hash('Boris Diaw') YIELD $$.player.name, player.age """ - Then a SemanticError should be raised at runtime: Unsupported src/dst property expression in yield. + Then a SemanticError should be raised at runtime: unsupported src/dst property expression in yield. # yields not existing tag When executing query: """ @@ -331,3 +349,56 @@ Feature: Fetch Int Vid Vertices GO FROM hash('NON EXIST VERTEX ID') OVER serve | FETCH PROP ON team $- """ Then a SyntaxError should be raised at runtime: + + Scenario: format yield + When executing query: + """ + FETCH PROP ON * hash('Boris Diaw') YIELD id(vertex) + """ + Then the result should be, in any order, and the columns 0, 1 should be hashed: + | VertexID | id(VERTEX) | + | "Boris Diaw" | "Boris Diaw" | + When executing query: + """ + FETCH PROP ON * hash('Boris Diaw') YIELD id(vertex), player.age + """ + Then the result should be, in any order, and the columns 0, 1 should be hashed: + | VertexID | id(VERTEX) | player.age | + | "Boris Diaw" | "Boris Diaw" | 36 | + When executing query: + """ + FETCH PROP ON * hash('Boris Diaw') YIELD id(vertex), player.age, vertex as node + """ + Then the result should be, in any order, and the columns 0, 1 should be hashed: + | VertexID | id(VERTEX) | player.age | node | + | "Boris Diaw" | "Boris Diaw" | 36 | ("Boris Diaw":player{name:"Boris Diaw", age:36}) | + When executing query: + """ + FETCH PROP ON * hash('Boris Diaw') YIELD vertex as node + """ + Then the result should be, in any order, and the columns 0 should be hashed: + | VertexID | node | + | "Boris Diaw" | ("Boris Diaw":player{name:"Boris Diaw", age:36}) | + When executing query: + """ + FETCH PROP ON * hash("Tim Duncan") YIELD player.name, player.age, team.name, bachelor.name, bachelor.speciality, vertex as node + """ + Then the result should be, in any order, and the columns 0 should be hashed: + | VertexID | player.name | player.age | team.name | bachelor.name | bachelor.speciality | node | + | "Tim Duncan" | "Tim Duncan" | 42 | EMPTY | "Tim Duncan" | "psychology" | ("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | + When executing query: + """ + GO FROM hash("Tim Duncan") OVER like YIELD like._dst as id | + FETCH PROP ON * $-.id YIELD VERTEX as node + """ + Then the result should be, in any order, and the columns 0 should be hashed: + | VertexID | node | + | "Manu Ginobili" | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | + | "Tony Parker" | ("Tony Parker" :player{age: 36, name: "Tony Parker"}) | + When executing query: + """ + FETCH PROP ON * hash('NON EXIST VERTEX ID'), hash('Boris Diaw') yield player.name, id(vertex) + """ + Then the result should be, in any order, and the columns 0, 2 should be hashed: + | VertexID | player.name | id(VERTEX) | + | "Boris Diaw" | "Boris Diaw" | "Boris Diaw" | diff --git a/tests/tck/features/fetch/FetchVertices.strVid.feature b/tests/tck/features/fetch/FetchVertices.strVid.feature index 4dee9e65a1d..8758c20e853 100644 --- a/tests/tck/features/fetch/FetchVertices.strVid.feature +++ b/tests/tck/features/fetch/FetchVertices.strVid.feature @@ -213,8 +213,8 @@ Feature: Fetch String Vertices | "Tim Duncan" | "Tim Duncan" | 42 | When executing query: """ - GO FROM 'Boris Diaw' over like YIELD like._dst as id - | FETCH PROP ON player, bachelor $-.id YIELD player.name, player.age, bachelor.name, bachelor.speciality + GO FROM 'Boris Diaw' over like YIELD like._dst as id | + FETCH PROP ON player, bachelor $-.id YIELD player.name, player.age, bachelor.name, bachelor.speciality """ Then the result should be, in any order: | VertexID | player.name | player.age | bachelor.name | bachelor.speciality | @@ -223,8 +223,8 @@ Feature: Fetch String Vertices # Fetch prop on multi tags of vertices from pipe When executing query: """ - GO FROM "Boris Diaw" over like YIELD like._dst as id - | FETCH PROP ON player, team, bachelor $-.id YIELD player.name, player.age, team.name, bachelor.name, bachelor.speciality + GO FROM "Boris Diaw" over like YIELD like._dst as id | + FETCH PROP ON player, team, bachelor $-.id YIELD player.name, player.age, team.name, bachelor.name, bachelor.speciality """ Then the result should be, in any order: | VertexID | player.name | player.age | team.name | bachelor.name | bachelor.speciality | @@ -232,8 +232,8 @@ Feature: Fetch String Vertices | "Tony Parker" | "Tony Parker" | 36 | EMPTY | EMPTY | EMPTY | When executing query: """ - GO FROM 'Boris Diaw' over like YIELD like._dst as id - | FETCH PROP ON player, bachelor $-.id YIELD player.name, player.age, bachelor.name, bachelor.speciality + GO FROM 'Boris Diaw' over like YIELD like._dst as id | + FETCH PROP ON player, bachelor $-.id YIELD player.name, player.age, bachelor.name, bachelor.speciality """ Then the result should be, in any order: | VertexID | player.name | player.age | bachelor.name | bachelor.speciality | @@ -248,8 +248,9 @@ Feature: Fetch String Vertices | VertexID | player.name | When executing query: """ - GO FROM 'NON EXIST VERTEX ID' over serve YIELD serve._dst as id, serve.start_year as start - | YIELD $-.id as id WHERE $-.start > 20000 | FETCH PROP ON player $-.id yield player.name + GO FROM 'NON EXIST VERTEX ID' over serve YIELD serve._dst as id, serve.start_year as start | + YIELD $-.id as id WHERE $-.start > 20000 | + FETCH PROP ON player $-.id yield player.name """ Then the result should be, in any order: | VertexID | player.name | @@ -263,8 +264,8 @@ Feature: Fetch String Vertices | "Tim Duncan" | "Tim Duncan" | 42 | "Tim Duncan" | "psychology" | When executing query: """ - GO FROM "Boris Diaw" over like YIELD like._dst as id - | FETCH PROP ON * $-.id YIELD player.name, player.age, team.name, bachelor.name, bachelor.speciality + GO FROM "Boris Diaw" over like YIELD like._dst as id | + FETCH PROP ON * $-.id YIELD player.name, player.age, team.name, bachelor.name, bachelor.speciality """ Then the result should be, in any order: | VertexID | player.name | player.age | team.name | bachelor.name | bachelor.speciality | @@ -355,8 +356,8 @@ Feature: Fetch String Vertices | "Tracy McGrady" | When executing query: """ - FETCH PROP ON player 'Tony Parker' YIELD player.name as Name - | GO FROM $-.Name OVER like + FETCH PROP ON player 'Tony Parker' YIELD player.name as Name | + GO FROM $-.Name OVER like """ Then the result should be, in any order: | like._dst | @@ -365,8 +366,8 @@ Feature: Fetch String Vertices | "Tim Duncan" | When executing query: """ - FETCH PROP ON player 'Tony Parker' YIELD player.name as Name - | GO FROM $-.VertexID OVER like + FETCH PROP ON player 'Tony Parker' YIELD player.name as Name | + GO FROM $-.VertexID OVER like """ Then the result should be, in any order: | like._dst | @@ -375,6 +376,21 @@ Feature: Fetch String Vertices | "Tim Duncan" | Scenario: Typical errors + When executing query: + """ + FETCH PROP ON player 'Boris Diaw' YIELD vertex + """ + Then a SyntaxError should be raised at runtime: please add alias when using vertex. near `vertex' + When executing query: + """ + FETCH PROP ON player 'Boris Diaw' YIELD edge as a + """ + Then a SemanticError should be raised at runtime: illegal yield clauses `EDGE AS a' + When executing query: + """ + FETCH PROP ON player 'Boris Diaw' YIELD src(edge) + """ + Then a SemanticError should be raised at runtime: illegal yield clauses `src(EDGE)' # Fetch Vertices not support get src property When executing query: """ @@ -444,3 +460,56 @@ Feature: Fetch String Vertices GO FROM 'NON EXIST VERTEX ID' OVER serve | FETCH PROP ON team $- """ Then a SyntaxError should be raised at runtime: + + Scenario: format yield + When executing query: + """ + FETCH PROP ON * 'Boris Diaw' YIELD id(vertex) + """ + Then the result should be, in any order: + | VertexID | id(VERTEX) | + | "Boris Diaw" | "Boris Diaw" | + When executing query: + """ + FETCH PROP ON * 'Boris Diaw' YIELD id(vertex), player.age + """ + Then the result should be, in any order: + | VertexID | id(VERTEX) | player.age | + | "Boris Diaw" | "Boris Diaw" | 36 | + When executing query: + """ + FETCH PROP ON * 'Boris Diaw' YIELD id(vertex), player.age, vertex as node + """ + Then the result should be, in any order: + | VertexID | id(VERTEX) | player.age | node | + | "Boris Diaw" | "Boris Diaw" | 36 | ("Boris Diaw":player{name:"Boris Diaw", age:36}) | + When executing query: + """ + FETCH PROP ON * 'Boris Diaw' YIELD vertex as node + """ + Then the result should be, in any order: + | VertexID | node | + | "Boris Diaw" | ("Boris Diaw":player{name:"Boris Diaw", age:36}) | + When executing query: + """ + FETCH PROP ON * "Tim Duncan" YIELD player.name, player.age, team.name, bachelor.name, bachelor.speciality, vertex as node + """ + Then the result should be, in any order: + | VertexID | player.name | player.age | team.name | bachelor.name | bachelor.speciality | node | + | "Tim Duncan" | "Tim Duncan" | 42 | EMPTY | "Tim Duncan" | "psychology" | ("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | + When executing query: + """ + GO FROM "Tim Duncan" OVER like YIELD like._dst as id | + FETCH PROP ON * $-.id YIELD vertex as node + """ + Then the result should be, in any order: + | VertexID | node | + | "Manu Ginobili" | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | + | "Tony Parker" | ("Tony Parker" :player{age: 36, name: "Tony Parker"}) | + When executing query: + """ + FETCH PROP ON * 'NON EXIST VERTEX ID', 'Boris Diaw' yield player.name, id(vertex) + """ + Then the result should be, in any order: + | VertexID | player.name | id(VERTEX) | + | "Boris Diaw" | "Boris Diaw" | "Boris Diaw" | diff --git a/tests/tck/features/go/Orderby.feature b/tests/tck/features/go/Orderby.feature index dee1730e2b8..989407486a6 100644 --- a/tests/tck/features/go/Orderby.feature +++ b/tests/tck/features/go/Orderby.feature @@ -38,9 +38,9 @@ Feature: Orderby Sentence | name | start | team | When executing query: """ - GO FROM "Marco Belinelli" OVER serve YIELD $^.player.name as name, serve.start_year as start, $$.team.name as team - | YIELD $-.name as name WHERE $-.start > 20000 - | ORDER BY $-.name + GO FROM "Marco Belinelli" OVER serve YIELD $^.player.name as name, serve.start_year as start, $$.team.name as team | + YIELD $-.name as name WHERE $-.start > 20000 | + ORDER BY $-.name """ Then the result should be, in order, with relax comparison: | name |