diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 4b884fba8e20e7db8c7da14af295bff761a9e37d..40a0dddb46365174e2759f2f5421e0b7a98e8486 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -25,17 +25,7 @@ jobs: - name: Cpplint run: | ln -snf $PWD/.linters/cpp/hooks/pre-commit.sh $PWD/.linters/cpp/pre-commit.sh - .linters/cpp/pre-commit.sh ${{ steps.diff.outputs.all }} - - name: Pylint - if: false - uses: docker://github/super-linter:v3 - env: - VALIDATE_ALL_CODEBASE: false - DEFAULT_BRANCH: master - DEFAULT_WORKSPACE: ${{ github.workspace }}/tests - PYTHON_PYLINT_CONFIG_FILE: ${{ github.workspace }}/tests/.pylintrc - VALIDATE_PYTHON_FLAKE8: false - GITHUB_TOKEN: ${{ github.token }} + .linters/cpp/pre-commit.sh $(git --no-pager diff --diff-filter=d --name-only HEAD^ HEAD) build: name: build diff --git a/src/scheduler/Scheduler.cpp b/src/scheduler/Scheduler.cpp index 5b207907a9f37bfd7e9f5eccbec2b2ff8d3173ee..8f60314c7b08741c6fd4845f1500f2ffc5468d20 100644 --- a/src/scheduler/Scheduler.cpp +++ b/src/scheduler/Scheduler.cpp @@ -19,6 +19,9 @@ namespace graph { Scheduler::Task::Task(const Executor *e) : planId(DCHECK_NOTNULL(e)->node()->id()) {} +Scheduler::MultiOutputsData::MultiOutputsData(int32_t outputs) + : promise(std::make_unique<folly::SharedPromise<Status>>()), numOutputs(outputs) {} + Scheduler::Scheduler(QueryContext *qctx) : qctx_(DCHECK_NOTNULL(qctx)) {} folly::Future<Status> Scheduler::schedule() { diff --git a/src/scheduler/Scheduler.h b/src/scheduler/Scheduler.h index ca7825ff8c3d6aa2eabba72e5b9e5bf6be9950b5..0a88ed1c752b525686bad4390eaf9371e955142c 100644 --- a/src/scheduler/Scheduler.h +++ b/src/scheduler/Scheduler.h @@ -28,7 +28,7 @@ class LoopExecutor; class Scheduler final : private cpp::NonCopyable, private cpp::NonMovable { public: - // For check whether a task is a Scheduler::Task by std::is_base_of<>::value in thread pool + // check whether a task is a Scheduler::Task by std::is_base_of<>::value in thread pool struct Task { int64_t planId; explicit Task(const Executor *e); @@ -41,7 +41,7 @@ public: private: // Enable thread pool check the query plan id of each callback registered in future. The functor - // is only the proxy of the invocable function `fn`. + // is only the proxy of the invocable function `fn'. template <typename F> struct ExecTask : Task { using Extract = folly::futures::detail::Extract<F>; @@ -68,17 +68,15 @@ private: folly::Future<Status> iterate(LoopExecutor *loop); folly::Future<Status> execute(Executor *executor); - QueryContext *qctx_{nullptr}; - struct MultiOutputsData { folly::SpinLock lock; std::unique_ptr<folly::SharedPromise<Status>> promise; int32_t numOutputs; - explicit MultiOutputsData(int32_t outputs) - : promise(std::make_unique<folly::SharedPromise<Status>>()), numOutputs(outputs) {} + explicit MultiOutputsData(int32_t outputs); }; + QueryContext *qctx_{nullptr}; std::unordered_map<std::string, MultiOutputsData> multiOutputPromiseMap_; }; diff --git a/src/service/QueryInstance.cpp b/src/service/QueryInstance.cpp index c0d19c0dcc59cf65dc5cc975a7d30e2909b379b7..08fbc7aff4dc240c3542d270eed961a1e66d2fe7 100644 --- a/src/service/QueryInstance.cpp +++ b/src/service/QueryInstance.cpp @@ -13,6 +13,7 @@ #include "planner/ExecutionPlan.h" #include "planner/PlanNode.h" #include "scheduler/Scheduler.h" +#include "validator/Validator.h" namespace nebula { namespace graph { @@ -46,10 +47,9 @@ Status QueryInstance::validateAndOptimize() { VLOG(1) << "Parsing query: " << rctx->query(); auto result = GQLParser().parse(rctx->query()); NG_RETURN_IF_ERROR(result); - sentences_ = std::move(result).value(); + sentence_ = std::move(result).value(); - validator_ = std::make_unique<ASTValidator>(sentences_.get(), qctx()); - NG_RETURN_IF_ERROR(validator_->validate()); + NG_RETURN_IF_ERROR(Validator::validate(sentence_.get(), qctx())); // TODO: optional optimize for plan. @@ -57,11 +57,11 @@ Status QueryInstance::validateAndOptimize() { } bool QueryInstance::explainOrContinue() { - if (sentences_->kind() != Sentence::Kind::kExplain) { + if (sentence_->kind() != Sentence::Kind::kExplain) { return true; } qctx_->fillPlanDescription(); - return static_cast<const ExplainSentence *>(sentences_.get())->isProfile(); + return static_cast<const ExplainSentence *>(sentence_.get())->isProfile(); } void QueryInstance::onFinish() { diff --git a/src/service/QueryInstance.h b/src/service/QueryInstance.h index 615c5950317d6a714b0e18d4ef1defc442c54511..8c813fddf5db7240a619390deca34cd83f051b7c 100644 --- a/src/service/QueryInstance.h +++ b/src/service/QueryInstance.h @@ -13,7 +13,6 @@ #include "context/QueryContext.h" #include "parser/GQLParser.h" #include "scheduler/Scheduler.h" -#include "validator/ASTValidator.h" /** * QueryInstance coordinates the execution process, @@ -57,9 +56,8 @@ private: // return true if continue to execute bool explainOrContinue(); - std::unique_ptr<Sentence> sentences_; + std::unique_ptr<Sentence> sentence_; std::unique_ptr<QueryContext> qctx_; - std::unique_ptr<ASTValidator> validator_; std::unique_ptr<Scheduler> scheduler_; }; diff --git a/src/validator/ASTValidator.cpp b/src/validator/ASTValidator.cpp deleted file mode 100644 index 0b343aaed019927c702b7c925825bf9142bd413a..0000000000000000000000000000000000000000 --- a/src/validator/ASTValidator.cpp +++ /dev/null @@ -1,36 +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 "validator/ASTValidator.h" - -#include "parser/Sentence.h" -#include "planner/ExecutionPlan.h" -#include "validator/Validator.h" - -namespace nebula { -namespace graph { - -Status ASTValidator::validate() { - // Check if space chosen from session. if chosen, add it to context. - auto session = qctx_->rctx()->session(); - if (session->space() > -1) { - qctx_->vctx()->switchToSpace(session->spaceName(), session->space()); - } - - auto validator = Validator::makeValidator(sentences_, qctx_); - NG_RETURN_IF_ERROR(validator->validate()); - - auto root = validator->root(); - if (!root) { - return Status::Error("Get null plan from sequantial validator."); - } - - qctx_->plan()->setRoot(root); - return Status::OK(); -} - -} // namespace graph -} // namespace nebula diff --git a/src/validator/ASTValidator.h b/src/validator/ASTValidator.h deleted file mode 100644 index dadbdf7190895ca914ee96ea4867cf9384ab4b2a..0000000000000000000000000000000000000000 --- a/src/validator/ASTValidator.h +++ /dev/null @@ -1,35 +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 VALIDATOR_ASTVALIDATOR_H_ -#define VALIDATOR_ASTVALIDATOR_H_ - -#include "common/base/Base.h" -#include "parser/Sentence.h" -#include "context/QueryContext.h" - -namespace nebula { - -class Sentence; - -namespace graph { - -class ExecutionPlan; - -class ASTValidator final { -public: - ASTValidator(Sentence* sentences, QueryContext* qctx) - : sentences_(DCHECK_NOTNULL(sentences)), qctx_(DCHECK_NOTNULL(qctx)) {} - - Status validate(); - -private: - Sentence* sentences_{nullptr}; - QueryContext* qctx_{nullptr}; -}; -} // namespace graph -} // namespace nebula -#endif diff --git a/src/validator/CMakeLists.txt b/src/validator/CMakeLists.txt index ce27a56e2bcff00bf5b66df3f403d9c1cb51da08..cf8c317ded842302c83ed23527386b1f1505c56f 100644 --- a/src/validator/CMakeLists.txt +++ b/src/validator/CMakeLists.txt @@ -7,7 +7,6 @@ nebula_add_library( validator_obj OBJECT Validator.cpp AssignmentValidator.cpp - ASTValidator.cpp GoValidator.cpp PipeValidator.cpp SequentialValidator.cpp diff --git a/src/validator/Validator.cpp b/src/validator/Validator.cpp index f5346911bffd36d9274a6aa183c394fac3ab3dfe..203fcae53ea4f54c594f3d3397675d004ab8f42e 100644 --- a/src/validator/Validator.cpp +++ b/src/validator/Validator.cpp @@ -187,6 +187,28 @@ std::unique_ptr<Validator> Validator::makeValidator(Sentence* sentence, QueryCon return std::make_unique<ReportError>(sentence, context); } +// static +Status Validator::validate(Sentence* sentence, QueryContext* qctx) { + DCHECK(sentence != nullptr); + DCHECK(qctx != nullptr); + + // Check if space chosen from session. if chosen, add it to context. + auto session = qctx->rctx()->session(); + if (session->space() > -1) { + qctx->vctx()->switchToSpace(session->spaceName(), session->space()); + } + + auto validator = makeValidator(sentence, qctx); + NG_RETURN_IF_ERROR(validator->validate()); + + auto root = validator->root(); + if (!root) { + return Status::Error("Get null plan from sequential validator"); + } + qctx->plan()->setRoot(root); + return Status::OK(); +} + Status Validator::appendPlan(PlanNode* node, PlanNode* appended) { switch (DCHECK_NOTNULL(node)->kind()) { case PlanNode::Kind::kShowHosts: diff --git a/src/validator/Validator.h b/src/validator/Validator.h index a0e65a3d00c3840272f8fbf9294583653f1a546d..fc780aa14dcfe4eaa43a481f0dd09afecf3e5fb6 100644 --- a/src/validator/Validator.h +++ b/src/validator/Validator.h @@ -78,10 +78,12 @@ public: static std::unique_ptr<Validator> makeValidator(Sentence* sentence, QueryContext* context); - MUST_USE_RESULT Status appendPlan(PlanNode* tail); + static Status validate(Sentence* sentence, QueryContext* qctx); Status validate(); + MUST_USE_RESULT Status appendPlan(PlanNode* tail); + void setInputVarName(std::string name) { inputVarName_ = std::move(name); } diff --git a/src/validator/test/ACLValidatorTest.cpp b/src/validator/test/ACLValidatorTest.cpp index 338e4d147500502d826903918c108148ec1c2d9c..f737cad07adb2e09da5ab629c31d2ee6251f2fef 100644 --- a/src/validator/test/ACLValidatorTest.cpp +++ b/src/validator/test/ACLValidatorTest.cpp @@ -22,8 +22,7 @@ TEST_F(ACLValidatorTest, Simple) { constexpr char space[] = "test_space"; // create user { - ASSERT_TRUE(toPlan(folly::stringPrintf("CREATE USER %s", user))); - const ExecutionPlan *plan = qCtx_->plan(); + const ExecutionPlan *plan = toPlan(folly::stringPrintf("CREATE USER %s", user)); std::vector<PlanNode::Kind> expectedTop { PlanNode::Kind::kCreateUser, @@ -39,8 +38,8 @@ TEST_F(ACLValidatorTest, Simple) { ASSERT_EQ(*createUser->password(), ""); } { // if not exists - ASSERT_TRUE(toPlan(folly::stringPrintf("CREATE USER IF NOT EXISTS %s", user))); - const ExecutionPlan *plan = qCtx_->plan(); + const ExecutionPlan *plan = + toPlan(folly::stringPrintf("CREATE USER IF NOT EXISTS %s", user)); std::vector<PlanNode::Kind> expectedTop { PlanNode::Kind::kCreateUser, @@ -56,10 +55,8 @@ TEST_F(ACLValidatorTest, Simple) { ASSERT_EQ(*createUser->password(), ""); } { // with password - ASSERT_TRUE(toPlan(folly::stringPrintf("CREATE USER %s WITH PASSWORD \"%s\"", - user, - password))); - const ExecutionPlan *plan = qCtx_->plan(); + const ExecutionPlan *plan = + toPlan(folly::stringPrintf("CREATE USER %s WITH PASSWORD \"%s\"", user, password)); std::vector<PlanNode::Kind> expectedTop { PlanNode::Kind::kCreateUser, @@ -77,9 +74,7 @@ TEST_F(ACLValidatorTest, Simple) { // drop user { - ASSERT_TRUE(toPlan(folly::stringPrintf("DROP USER %s", - user))); - const ExecutionPlan *plan = qCtx_->plan(); + const ExecutionPlan *plan = toPlan(folly::stringPrintf("DROP USER %s", user)); std::vector<PlanNode::Kind> expectedTop { PlanNode::Kind::kDropUser, @@ -94,9 +89,7 @@ TEST_F(ACLValidatorTest, Simple) { ASSERT_EQ(*dropUser->username(), user); } { // if exits - ASSERT_TRUE(toPlan(folly::stringPrintf("DROP USER IF EXISTS %s", - user))); - const ExecutionPlan *plan = qCtx_->plan(); + const ExecutionPlan *plan = toPlan(folly::stringPrintf("DROP USER IF EXISTS %s", user)); std::vector<PlanNode::Kind> expectedTop { PlanNode::Kind::kDropUser, @@ -113,9 +106,8 @@ TEST_F(ACLValidatorTest, Simple) { // update user { - ASSERT_TRUE(toPlan(folly::stringPrintf("ALTER USER %s WITH PASSWORD \"%s\"", - user, password))); - const ExecutionPlan *plan = qCtx_->plan(); + const ExecutionPlan *plan = + toPlan(folly::stringPrintf("ALTER USER %s WITH PASSWORD \"%s\"", user, password)); std::vector<PlanNode::Kind> expectedTop { PlanNode::Kind::kUpdateUser, @@ -132,8 +124,7 @@ TEST_F(ACLValidatorTest, Simple) { // show users { - ASSERT_TRUE(toPlan("SHOW USERS")); - const ExecutionPlan *plan = qCtx_->plan(); + const ExecutionPlan *plan = toPlan("SHOW USERS"); std::vector<PlanNode::Kind> expectedTop { PlanNode::Kind::kListUsers, @@ -144,9 +135,8 @@ TEST_F(ACLValidatorTest, Simple) { // change password { - ASSERT_TRUE(toPlan(folly::stringPrintf("CHANGE PASSWORD %s FROM \"%s\" TO \"%s\"", - user, password, newPassword))); - const ExecutionPlan *plan = qCtx_->plan(); + const ExecutionPlan *plan = toPlan(folly::stringPrintf( + "CHANGE PASSWORD %s FROM \"%s\" TO \"%s\"", user, password, newPassword)); std::vector<PlanNode::Kind> expectedTop { PlanNode::Kind::kChangePassword, @@ -164,9 +154,8 @@ TEST_F(ACLValidatorTest, Simple) { // grant role { - ASSERT_TRUE(toPlan(folly::stringPrintf("GRANT ROLE %s ON %s TO %s", - roleTypeName, space, user))); - const ExecutionPlan *plan = qCtx_->plan(); + const ExecutionPlan *plan = + toPlan(folly::stringPrintf("GRANT ROLE %s ON %s TO %s", roleTypeName, space, user)); std::vector<PlanNode::Kind> expectedTop { PlanNode::Kind::kGrantRole, @@ -184,9 +173,8 @@ TEST_F(ACLValidatorTest, Simple) { // revoke role { - ASSERT_TRUE(toPlan(folly::stringPrintf("REVOKE ROLE %s ON %s FROM %s", - roleTypeName, space, user))); - const ExecutionPlan *plan = qCtx_->plan(); + const ExecutionPlan *plan = + toPlan(folly::stringPrintf("REVOKE ROLE %s ON %s FROM %s", roleTypeName, space, user)); std::vector<PlanNode::Kind> expectedTop { PlanNode::Kind::kRevokeRole, @@ -204,9 +192,7 @@ TEST_F(ACLValidatorTest, Simple) { // show roles in space { - ASSERT_TRUE(toPlan(folly::stringPrintf("SHOW ROLES IN %s", - space))); - const ExecutionPlan *plan = qCtx_->plan(); + const ExecutionPlan *plan = toPlan(folly::stringPrintf("SHOW ROLES IN %s", space)); std::vector<PlanNode::Kind> expectedTop { PlanNode::Kind::kListRoles, diff --git a/src/validator/test/FetchEdgesTest.cpp b/src/validator/test/FetchEdgesTest.cpp index da4285e119f3b7bcbf5dae38420993b6ecfb2683..362882b4804e3a11e32a260e600e192c40987d88 100644 --- a/src/validator/test/FetchEdgesTest.cpp +++ b/src/validator/test/FetchEdgesTest.cpp @@ -14,20 +14,20 @@ namespace graph { class FetchEdgesValidatorTest : public ValidatorTestBase {}; TEST_F(FetchEdgesValidatorTest, FetchEdgesProp) { - auto src = std::make_unique<VariablePropertyExpression>( - new std::string(qCtx_->vctx()->anonVarGen()->getVar()), new std::string(kSrc)); - auto type = std::make_unique<VariablePropertyExpression>( - new std::string(qCtx_->vctx()->anonVarGen()->getVar()), new std::string(kType)); - auto rank = std::make_unique<VariablePropertyExpression>( - new std::string(qCtx_->vctx()->anonVarGen()->getVar()), new std::string(kRank)); - auto dst = std::make_unique<VariablePropertyExpression>( - new std::string(qCtx_->vctx()->anonVarGen()->getVar()), new std::string(kDst)); + auto src = std::make_unique<VariablePropertyExpression>(new std::string("_VAR1_"), + new std::string(kSrc)); + auto type = std::make_unique<VariablePropertyExpression>(new std::string("_VAR2_"), + new std::string(kType)); + auto rank = std::make_unique<VariablePropertyExpression>(new std::string("_VAR3_"), + new std::string(kRank)); + auto dst = std::make_unique<VariablePropertyExpression>(new std::string("_VAR4_"), + new std::string(kDst)); { - ASSERT_TRUE(toPlan("FETCH PROP ON like \"1\"->\"2\"")); + auto plan = toPlan("FETCH PROP ON like \"1\"->\"2\""); - auto *start = StartNode::make(expectedQueryCtx_->plan()); + ExecutionPlan expectedPlan(pool_.get()); + auto *start = StartNode::make(&expectedPlan); - auto *plan = qCtx_->plan(); auto edgeTypeResult = schemaMng_->toEdgeType(1, "like"); ASSERT_TRUE(edgeTypeResult.ok()); auto edgeType = edgeTypeResult.value(); @@ -36,7 +36,7 @@ TEST_F(FetchEdgesValidatorTest, FetchEdgesProp) { prop.set_props({kSrc, kDst, kRank, "start", "end", "likeness"}); std::vector<storage::cpp2::EdgeProp> props; props.emplace_back(std::move(prop)); - auto *ge = GetEdges::make(expectedQueryCtx_->plan(), + auto *ge = GetEdges::make(&expectedPlan, start, 1, src.get(), @@ -51,17 +51,17 @@ TEST_F(FetchEdgesValidatorTest, FetchEdgesProp) { "like.start", "like.end", "like.likeness"}); - expectedQueryCtx_->plan()->setRoot(ge); + expectedPlan.setRoot(ge); auto result = Eq(plan->root(), ge); ASSERT_TRUE(result.ok()) << result; } // With YIELD { - ASSERT_TRUE(toPlan("FETCH PROP ON like \"1\"->\"2\" YIELD like.start, like.end")); + auto plan = toPlan("FETCH PROP ON like \"1\"->\"2\" YIELD like.start, like.end"); - auto *start = StartNode::make(expectedQueryCtx_->plan()); + ExecutionPlan expectedPlan(pool_.get()); + auto *start = StartNode::make(&expectedPlan); - auto *plan = qCtx_->plan(); auto edgeTypeResult = schemaMng_->toEdgeType(1, "like"); ASSERT_TRUE(edgeTypeResult.ok()); auto edgeType = edgeTypeResult.value(); @@ -79,7 +79,7 @@ TEST_F(FetchEdgesValidatorTest, FetchEdgesProp) { EdgePropertyExpression(new std::string("like"), new std::string("end")).encode()); exprs.emplace_back(std::move(expr1)); exprs.emplace_back(std::move(expr2)); - auto *ge = GetEdges::make(expectedQueryCtx_->plan(), + auto *ge = GetEdges::make(&expectedPlan, start, 1, src.get(), @@ -103,23 +103,22 @@ TEST_F(FetchEdgesValidatorTest, FetchEdgesProp) { new EdgePropertyExpression(new std::string("like"), new std::string("start")))); yieldColumns->addColumn(new YieldColumn( new EdgePropertyExpression(new std::string("like"), new std::string("end")))); - auto *project = Project::make(expectedQueryCtx_->plan(), ge, yieldColumns.get()); + auto *project = Project::make(&expectedPlan, ge, yieldColumns.get()); project->setColNames({std::string("like.") + kSrc, std::string("like.") + kDst, std::string("like.") + kRank, "like.start", "like.end"}); - expectedQueryCtx_->plan()->setRoot(project); + expectedPlan.setRoot(project); auto result = Eq(plan->root(), project); ASSERT_TRUE(result.ok()) << result; } // With YIELD const expression { - ASSERT_TRUE(toPlan("FETCH PROP ON like \"1\"->\"2\" YIELD like.start, 1 + 1, like.end")); + auto plan = toPlan("FETCH PROP ON like \"1\"->\"2\" YIELD like.start, 1 + 1, like.end"); - auto *start = StartNode::make(expectedQueryCtx_->plan()); - - auto *plan = qCtx_->plan(); + ExecutionPlan expectedPlan(pool_.get()); + auto *start = StartNode::make(&expectedPlan); // GetEdges auto edgeTypeResult = schemaMng_->toEdgeType(1, "like"); @@ -139,7 +138,7 @@ TEST_F(FetchEdgesValidatorTest, FetchEdgesProp) { EdgePropertyExpression(new std::string("like"), new std::string("end")).encode()); exprs.emplace_back(std::move(expr1)); exprs.emplace_back(std::move(expr2)); - auto *ge = GetEdges::make(expectedQueryCtx_->plan(), + auto *ge = GetEdges::make(&expectedPlan, start, 1, src.get(), @@ -166,24 +165,24 @@ TEST_F(FetchEdgesValidatorTest, FetchEdgesProp) { Expression::Kind::kAdd, new ConstantExpression(1), new ConstantExpression(1)))); yieldColumns->addColumn(new YieldColumn( new EdgePropertyExpression(new std::string("like"), new std::string("end")))); - auto *project = Project::make(expectedQueryCtx_->plan(), ge, yieldColumns.get()); + auto *project = Project::make(&expectedPlan, ge, yieldColumns.get()); project->setColNames({std::string("like.") + kSrc, std::string("like.") + kDst, std::string("like.") + kRank, "like.start", "(1+1)", "like.end"}); - expectedQueryCtx_->plan()->setRoot(project); + expectedPlan.setRoot(project); auto result = Eq(plan->root(), project); ASSERT_TRUE(result.ok()) << result; } // With YIELD combine properties { - ASSERT_TRUE(toPlan("FETCH PROP ON like \"1\"->\"2\" YIELD like.start > like.end")); + auto plan = toPlan("FETCH PROP ON like \"1\"->\"2\" YIELD like.start > like.end"); - auto *start = StartNode::make(expectedQueryCtx_->plan()); + ExecutionPlan expectedPlan(pool_.get()); + auto *start = StartNode::make(&expectedPlan); - auto *plan = qCtx_->plan(); auto edgeTypeResult = schemaMng_->toEdgeType(1, "like"); ASSERT_TRUE(edgeTypeResult.ok()); auto edgeType = edgeTypeResult.value(); @@ -202,7 +201,7 @@ TEST_F(FetchEdgesValidatorTest, FetchEdgesProp) { new EdgePropertyExpression(new std::string("like"), new std::string("end"))) .encode()); exprs.emplace_back(std::move(expr1)); - auto *ge = GetEdges::make(expectedQueryCtx_->plan(), + auto *ge = GetEdges::make(&expectedPlan, start, 1, src.get(), @@ -225,23 +224,23 @@ TEST_F(FetchEdgesValidatorTest, FetchEdgesProp) { Expression::Kind::kRelGT, new EdgePropertyExpression(new std::string("like"), new std::string("start")), new EdgePropertyExpression(new std::string("like"), new std::string("end"))))); - auto *project = Project::make(expectedQueryCtx_->plan(), ge, yieldColumns.get()); + auto *project = Project::make(&expectedPlan, ge, yieldColumns.get()); project->setColNames({std::string("like.") + kSrc, std::string("like.") + kDst, std::string("like.") + kRank, "(like.start>like.end)"}); - expectedQueryCtx_->plan()->setRoot(project); + expectedPlan.setRoot(project); auto result = Eq(plan->root(), project); ASSERT_TRUE(result.ok()) << result; } // With YIELD distinct { - ASSERT_TRUE(toPlan("FETCH PROP ON like \"1\"->\"2\" YIELD distinct like.start, like.end")); + auto plan = toPlan("FETCH PROP ON like \"1\"->\"2\" YIELD distinct like.start, like.end"); - auto *start = StartNode::make(expectedQueryCtx_->plan()); + ExecutionPlan expectedPlan(pool_.get()); + auto *start = StartNode::make(&expectedPlan); - auto *plan = qCtx_->plan(); auto edgeTypeResult = schemaMng_->toEdgeType(1, "like"); ASSERT_TRUE(edgeTypeResult.ok()); auto edgeType = edgeTypeResult.value(); @@ -259,7 +258,7 @@ TEST_F(FetchEdgesValidatorTest, FetchEdgesProp) { EdgePropertyExpression(new std::string("like"), new std::string("end")).encode()); exprs.emplace_back(std::move(expr1)); exprs.emplace_back(std::move(expr2)); - auto *ge = GetEdges::make(expectedQueryCtx_->plan(), + auto *ge = GetEdges::make(&expectedPlan, start, 1, src.get(), @@ -285,24 +284,22 @@ TEST_F(FetchEdgesValidatorTest, FetchEdgesProp) { new EdgePropertyExpression(new std::string("like"), new std::string("start")))); yieldColumns->addColumn(new YieldColumn( new EdgePropertyExpression(new std::string("like"), new std::string("end")))); - auto *project = Project::make(expectedQueryCtx_->plan(), ge, yieldColumns.get()); + auto *project = Project::make(&expectedPlan, ge, yieldColumns.get()); project->setColNames({std::string("like.") + kSrc, std::string("like.") + kDst, std::string("like.") + kRank, "like.start", "like.end"}); // dedup - auto *dedup = Dedup::make(expectedQueryCtx_->plan(), project); + auto *dedup = Dedup::make(&expectedPlan, project); dedup->setColNames(colNames); // data collect - auto *dataCollect = DataCollect::make(expectedQueryCtx_->plan(), - dedup, - DataCollect::CollectKind::kRowBasedMove, - {dedup->varName()}); + auto *dataCollect = DataCollect::make( + &expectedPlan, dedup, DataCollect::CollectKind::kRowBasedMove, {dedup->varName()}); dataCollect->setColNames(colNames); - expectedQueryCtx_->plan()->setRoot(dataCollect); + expectedPlan.setRoot(dataCollect); auto result = Eq(plan->root(), dataCollect); ASSERT_TRUE(result.ok()) << result; } @@ -372,132 +369,49 @@ TEST_F(FetchEdgesValidatorTest, FetchEdgesInputOutput) { TEST_F(FetchEdgesValidatorTest, FetchEdgesPropFailed) { // mismatched tag - { - auto result = GQLParser().parse("FETCH PROP ON edge1 \"1\"->\"2\" YIELD edge2.prop2"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } + ASSERT_FALSE(validate("FETCH PROP ON edge1 \"1\"->\"2\" YIELD edge2.prop2")); // notexist edge - { - auto result = GQLParser().parse("FETCH PROP ON not_exist_edge \"1\"->\"2\" " - "YIELD not_exist_edge.prop1"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } + ASSERT_FALSE(validate("FETCH PROP ON not_exist_edge \"1\"->\"2\" YIELD not_exist_edge.prop1")); // notexist edge property - { - auto result = GQLParser().parse("FETCH PROP ON like \"1\"->\"2\" " - "YIELD like.not_exist_prop"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } + ASSERT_FALSE(validate("FETCH PROP ON like \"1\"->\"2\" YIELD like.not_exist_prop")); // invalid yield expression - { - auto result = GQLParser().parse("$a = FETCH PROP ON like \"1\"->\"2\" " - "YIELD like._src AS src;" - "FETCH PROP ON like \"1\"->\"2\" YIELD $a.src + 1"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } - { - auto result = GQLParser().parse("FETCH PROP ON like \"1\"->\"2\" " - "YIELD like._src AS src | " - "FETCH PROP ON like \"1\"->\"2\" YIELD $-.src + 1"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } - { - auto result = GQLParser().parse("FETCH PROP ON like \"1\"->\"2\" " - "YIELD $^.like.start + 1"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } - { - auto result = GQLParser().parse("FETCH PROP ON like \"1\"->\"2\" " - "YIELD $$.like.start + 1"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } + ASSERT_FALSE(validate("$a = FETCH PROP ON like \"1\"->\"2\" " + "YIELD like._src AS src;" + "FETCH PROP ON like \"1\"->\"2\" YIELD $a.src + 1")); + + ASSERT_FALSE(validate("FETCH PROP ON like \"1\"->\"2\" " + "YIELD like._src AS src | " + "FETCH PROP ON like \"1\"->\"2\" YIELD $-.src + 1")); + + ASSERT_FALSE(validate("FETCH PROP ON like \"1\"->\"2\" YIELD $^.like.start + 1")); + ASSERT_FALSE(validate("FETCH PROP ON like \"1\"->\"2\" YIELD $$.like.start + 1")); } TEST_F(FetchEdgesValidatorTest, FetchEdgesInputFailed) { // mismatched variable - { - auto result = - GQLParser().parse("$a = FETCH PROP ON like \"1\"->\"2\" " - "YIELD like._src AS src, like._dst AS dst, like._rank AS rank;" - "FETCH PROP ON like $b.src->$b.dst@$b.rank"); - ASSERT_TRUE(result.ok()) << result.status(); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } + ASSERT_FALSE(validate("$a = FETCH PROP ON like \"1\"->\"2\" " + "YIELD like._src AS src, like._dst AS dst, like._rank AS rank;" + "FETCH PROP ON like $b.src->$b.dst@$b.rank")); // mismatched variable property - { - auto result = - GQLParser().parse("$a = FETCH PROP ON like \"1\"->\"2\" " - "YIELD like._src AS src, like._dst AS dst, like._rank AS rank;" - "FETCH PROP ON like $b.src->$b.dst@$b.not_exist_property"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } + ASSERT_FALSE(validate("$a = FETCH PROP ON like \"1\"->\"2\" " + "YIELD like._src AS src, like._dst AS dst, like._rank AS rank;" + "FETCH PROP ON like $b.src->$b.dst@$b.not_exist_property")); // mismatched input property - { - auto result = - GQLParser().parse("FETCH PROP ON like \"1\"->\"2\" " - "YIELD like._src AS src, like._dst AS dst, like._rank AS rank | " - "FETCH PROP ON like $-.src->$-.dst@$-.not_exist_property"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } + ASSERT_FALSE(validate("FETCH PROP ON like \"1\"->\"2\" " + "YIELD like._src AS src, like._dst AS dst, like._rank AS rank | " + "FETCH PROP ON like $-.src->$-.dst@$-.not_exist_property")); // refer to different variables - { - auto result = - GQLParser().parse("$a = FETCH PROP ON like \"1\"->\"2\" " - "YIELD like._src AS src, like._dst AS dst, like._rank AS rank;" - "$b = FETCH PROP ON like \"1\"->\"2\" " - "YIELD like._src AS src, like._dst AS dst, like._rank AS rank;" - "FETCH PROP ON like $a.src->$b.dst@$b.rank"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } + ASSERT_FALSE(validate("$a = FETCH PROP ON like \"1\"->\"2\" " + "YIELD like._src AS src, like._dst AS dst, like._rank AS rank;" + "$b = FETCH PROP ON like \"1\"->\"2\" " + "YIELD like._src AS src, like._dst AS dst, like._rank AS rank;" + "FETCH PROP ON like $a.src->$b.dst@$b.rank")); } } // namespace graph diff --git a/src/validator/test/FetchVerticesTest.cpp b/src/validator/test/FetchVerticesTest.cpp index 1cfd96a09aef28b49c1b2d0033737a3cfd34aa9c..bfabf2fc69fe5a7a7cd91abed9814ed0de63824c 100644 --- a/src/validator/test/FetchVerticesTest.cpp +++ b/src/validator/test/FetchVerticesTest.cpp @@ -4,7 +4,9 @@ * attached with Common Clause Condition 1.0, found in the LICENSES directory. */ +#include "planner/Logic.h" #include "planner/Query.h" +#include "util/ObjectPool.h" #include "validator/FetchVerticesValidator.h" #include "validator/test/ValidatorTestBase.h" @@ -15,35 +17,36 @@ class FetchVerticesValidatorTest : public ValidatorTestBase {}; TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { auto src = std::make_unique<VariablePropertyExpression>( - new std::string(qCtx_->vctx()->anonVarGen()->getVar()), new std::string(kVid)); + new std::string("_VARNAME_"), new std::string(kVid)); { - ASSERT_TRUE(toPlan("FETCH PROP ON person \"1\"")); + auto plan = toPlan("FETCH PROP ON person \"1\""); - auto *start = StartNode::make(expectedQueryCtx_->plan()); + ExecutionPlan expectedPlan(pool_.get()); + auto *start = StartNode::make(&expectedPlan); - auto *plan = qCtx_->plan(); auto tagIdResult = schemaMng_->toTagID(1, "person"); ASSERT_TRUE(tagIdResult.ok()); auto tagId = tagIdResult.value(); storage::cpp2::VertexProp prop; prop.set_tag(tagId); - auto *gv = GetVertices::make(expectedQueryCtx_->plan(), + auto *gv = GetVertices::make(&expectedPlan, start, 1, src.get(), std::vector<storage::cpp2::VertexProp>{std::move(prop)}, {}); gv->setColNames({kVid, "person.name", "person.age"}); + expectedPlan.setRoot(gv); auto result = Eq(plan->root(), gv); ASSERT_TRUE(result.ok()) << result; } // With YIELD { - ASSERT_TRUE(toPlan("FETCH PROP ON person \"1\" YIELD person.name, person.age")); + auto plan = toPlan("FETCH PROP ON person \"1\" YIELD person.name, person.age"); - auto *start = StartNode::make(expectedQueryCtx_->plan()); + ExecutionPlan expectedPlan(pool_.get()); + auto *start = StartNode::make(&expectedPlan); - auto *plan = qCtx_->plan(); auto tagIdResult = schemaMng_->toTagID(1, "person"); ASSERT_TRUE(tagIdResult.ok()); auto tagId = tagIdResult.value(); @@ -57,7 +60,7 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { expr2.set_expr( TagPropertyExpression(new std::string("person"), new std::string("age")).encode()); auto *gv = - GetVertices::make(expectedQueryCtx_->plan(), + GetVertices::make(&expectedPlan, start, 1, src.get(), @@ -73,19 +76,19 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { new TagPropertyExpression(new std::string("person"), new std::string("name")))); yieldColumns->addColumn(new YieldColumn( new TagPropertyExpression(new std::string("person"), new std::string("age")))); - auto *project = Project::make(expectedQueryCtx_->plan(), gv, yieldColumns.get()); + auto *project = Project::make(&expectedPlan, gv, yieldColumns.get()); project->setColNames({kVid, "person.name", "person.age"}); + expectedPlan.setRoot(project); auto result = Eq(plan->root(), project); ASSERT_TRUE(result.ok()) << result; } // With YIELD const expression { - ASSERT_TRUE(toPlan("FETCH PROP ON person \"1\" YIELD person.name, 1 > 1, person.age")); + auto plan = toPlan("FETCH PROP ON person \"1\" YIELD person.name, 1 > 1, person.age"); - auto *start = StartNode::make(expectedQueryCtx_->plan()); - - auto *plan = qCtx_->plan(); + ExecutionPlan expectedPlan(pool_.get()); + auto *start = StartNode::make(&expectedPlan); // get vertices auto tagIdResult = schemaMng_->toTagID(1, "person"); @@ -101,7 +104,7 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { expr2.set_expr( TagPropertyExpression(new std::string("person"), new std::string("age")).encode()); auto *gv = - GetVertices::make(expectedQueryCtx_->plan(), + GetVertices::make(&expectedPlan, start, 1, src.get(), @@ -119,18 +122,20 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { Expression::Kind::kRelGT, new ConstantExpression(1), new ConstantExpression(1)))); yieldColumns->addColumn(new YieldColumn( new TagPropertyExpression(new std::string("person"), new std::string("age")))); - auto *project = Project::make(expectedQueryCtx_->plan(), gv, yieldColumns.get()); + auto *project = Project::make(&expectedPlan, gv, yieldColumns.get()); project->setColNames({kVid, "person.name", "(1>1)", "person.age"}); + expectedPlan.setRoot(project); + auto result = Eq(plan->root(), project); ASSERT_TRUE(result.ok()) << result; } // With YIELD combine properties { - ASSERT_TRUE(toPlan("FETCH PROP ON person \"1\" YIELD person.name + person.age")); - auto *plan = qCtx_->plan(); + auto plan = toPlan("FETCH PROP ON person \"1\" YIELD person.name + person.age"); - auto *start = StartNode::make(expectedQueryCtx_->plan()); + ExecutionPlan expectedPlan(pool_.get()); + auto *start = StartNode::make(&expectedPlan); auto tagIdResult = schemaMng_->toTagID(1, "person"); ASSERT_TRUE(tagIdResult.ok()); @@ -146,7 +151,7 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { new TagPropertyExpression(new std::string("person"), new std::string("age"))) .encode()); - auto *gv = GetVertices::make(expectedQueryCtx_->plan(), + auto *gv = GetVertices::make(&expectedPlan, start, 1, src.get(), @@ -162,19 +167,21 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { Expression::Kind::kAdd, new TagPropertyExpression(new std::string("person"), new std::string("name")), new TagPropertyExpression(new std::string("person"), new std::string("age"))))); - auto *project = Project::make(expectedQueryCtx_->plan(), gv, yieldColumns.get()); + auto *project = Project::make(&expectedPlan, gv, yieldColumns.get()); project->setColNames({kVid, "(person.name+person.age)"}); + expectedPlan.setRoot(project); + auto result = Eq(plan->root(), project); ASSERT_TRUE(result.ok()) << result; } // With YIELD distinct { - ASSERT_TRUE(toPlan("FETCH PROP ON person \"1\" YIELD distinct person.name, person.age")); + auto plan = toPlan("FETCH PROP ON person \"1\" YIELD distinct person.name, person.age"); - auto *start = StartNode::make(expectedQueryCtx_->plan()); + ExecutionPlan expectedPlan(pool_.get()); + auto *start = StartNode::make(&expectedPlan); - auto *plan = qCtx_->plan(); auto tagIdResult = schemaMng_->toTagID(1, "person"); ASSERT_TRUE(tagIdResult.ok()); auto tagId = tagIdResult.value(); @@ -188,7 +195,7 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { expr2.set_expr( TagPropertyExpression(new std::string("person"), new std::string("age")).encode()); auto *gv = - GetVertices::make(expectedQueryCtx_->plan(), + GetVertices::make(&expectedPlan, start, 1, src.get(), @@ -206,34 +213,34 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesProp) { new TagPropertyExpression(new std::string("person"), new std::string("name")))); yieldColumns->addColumn(new YieldColumn( new TagPropertyExpression(new std::string("person"), new std::string("age")))); - auto *project = Project::make(expectedQueryCtx_->plan(), gv, yieldColumns.get()); + auto *project = Project::make(&expectedPlan, gv, yieldColumns.get()); project->setColNames(colNames); // dedup - auto *dedup = Dedup::make(expectedQueryCtx_->plan(), project); + auto *dedup = Dedup::make(&expectedPlan, project); dedup->setColNames(colNames); // data collect - auto *dataCollect = DataCollect::make(expectedQueryCtx_->plan(), - dedup, - DataCollect::CollectKind::kRowBasedMove, - {dedup->varName()}); + auto *dataCollect = DataCollect::make( + &expectedPlan, dedup, DataCollect::CollectKind::kRowBasedMove, {dedup->varName()}); dataCollect->setColNames(colNames); + expectedPlan.setRoot(dataCollect); + auto result = Eq(plan->root(), dataCollect); ASSERT_TRUE(result.ok()) << result; } // ON * { - ASSERT_TRUE(toPlan("FETCH PROP ON * \"1\"")); - - auto *start = StartNode::make(expectedQueryCtx_->plan()); + auto plan = toPlan("FETCH PROP ON * \"1\""); - auto *plan = qCtx_->plan(); + ExecutionPlan expectedPlan(pool_.get()); + auto *start = StartNode::make(&expectedPlan); auto *gv = GetVertices::make( - expectedQueryCtx_->plan(), start, 1, src.get(), {}, {}); + &expectedPlan, start, 1, src.get(), {}, {}); gv->setColNames({kVid, "person.name", "person.age"}); + expectedPlan.setRoot(gv); auto result = Eq(plan->root(), gv); ASSERT_TRUE(result.ok()) << result; } @@ -296,140 +303,40 @@ TEST_F(FetchVerticesValidatorTest, FetchVerticesInputOutput) { TEST_F(FetchVerticesValidatorTest, FetchVerticesPropFailed) { // mismatched tag - { - auto result = GQLParser().parse("FETCH PROP ON tag1 \"1\" YIELD tag2.prop2"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } + ASSERT_FALSE(validate("FETCH PROP ON tag1 \"1\" YIELD tag2.prop2")); // not exist tag - { - auto result = - GQLParser().parse("FETCH PROP ON not_exist_tag \"1\" YIELD not_exist_tag.prop1"); - ASSERT_TRUE(result.ok()) << result.status(); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } + ASSERT_FALSE(validate("FETCH PROP ON not_exist_tag \"1\" YIELD not_exist_tag.prop1")); // not exist property - { - auto result = - GQLParser().parse("FETCH PROP ON person \"1\" YIELD person.not_exist_property"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } + ASSERT_FALSE(validate("FETCH PROP ON person \"1\" YIELD person.not_exist_property")); // invalid yield expression - { - auto result = GQLParser().parse("$a = FETCH PROP ON person \"1\" YIELD person.name AS name;" - " FETCH PROP ON person \"1\" YIELD $a.name + 1"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } - // invalid yield expression - { - auto result = GQLParser().parse("FETCH PROP ON person \"1\" YIELD $^.person.name"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } - { - auto result = GQLParser().parse("FETCH PROP ON person \"1\" YIELD $$.person.name"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } - { - auto result = GQLParser().parse("FETCH PROP ON person \"1\" YIELD person.name AS name | " - " FETCH PROP ON person \"1\" YIELD $-.name + 1"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } - { - auto result = GQLParser().parse("FETCH PROP ON person \"1\" YIELD person._src + 1"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } - { - auto result = GQLParser().parse("FETCH PROP ON person \"1\" YIELD person._type"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } - { - auto result = GQLParser().parse("FETCH PROP ON person \"1\" YIELD person._rank + 1"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } - { - auto result = GQLParser().parse("FETCH PROP ON person \"1\" YIELD person._dst + 1"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } + ASSERT_FALSE(validate("$a = FETCH PROP ON person \"1\" YIELD person.name AS name;" + " FETCH PROP ON person \"1\" YIELD $a.name + 1")); + + ASSERT_FALSE(validate("FETCH PROP ON person \"1\" YIELD $^.person.name")); + ASSERT_FALSE(validate("FETCH PROP ON person \"1\" YIELD $$.person.name")); + ASSERT_FALSE(validate("FETCH PROP ON person \"1\" YIELD person.name AS name | " + " FETCH PROP ON person \"1\" YIELD $-.name + 1")); + ASSERT_FALSE(validate("FETCH PROP ON person \"1\" YIELD person._src + 1")); + ASSERT_FALSE(validate("FETCH PROP ON person \"1\" YIELD person._type")); + ASSERT_FALSE(validate("FETCH PROP ON person \"1\" YIELD person._rank + 1")); + ASSERT_FALSE(validate("FETCH PROP ON person \"1\" YIELD person._dst + 1")); } TEST_F(FetchVerticesValidatorTest, FetchVerticesInputFailed) { // mismatched varirable - { - auto result = GQLParser().parse("$a = FETCH PROP ON person \"1\" YIELD person.name AS name;" - "FETCH PROP ON person $b.name"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } + ASSERT_FALSE(validate("$a = FETCH PROP ON person \"1\" YIELD person.name AS name;" + "FETCH PROP ON person $b.name")); // mismatched varirable property - { - auto result = GQLParser().parse("$a = FETCH PROP ON person \"1\" YIELD person.name AS name;" - "FETCH PROP ON person $a.not_exist_property"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } + ASSERT_FALSE(validate("$a = FETCH PROP ON person \"1\" YIELD person.name AS name;" + "FETCH PROP ON person $a.not_exist_property")); // mismatched input property - { - auto result = GQLParser().parse("FETCH PROP ON person \"1\" YIELD person.name AS name | " - "FETCH PROP ON person $-.not_exist_property"); - ASSERT_TRUE(result.ok()); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - auto validateResult = validator.validate(); - ASSERT_FALSE(validateResult.ok()); - } + ASSERT_FALSE(validate("FETCH PROP ON person \"1\" YIELD person.name AS name | " + "FETCH PROP ON person $-.not_exist_property")); } } // namespace graph diff --git a/src/validator/test/ValidatorTestBase.cpp b/src/validator/test/ValidatorTestBase.cpp index 8290bf4a57dfb13281599b01579147d9d4f0ddc4..9275d1a33f723a1c8b272132b0db46937ce665f3 100644 --- a/src/validator/test/ValidatorTestBase.cpp +++ b/src/validator/test/ValidatorTestBase.cpp @@ -4,6 +4,8 @@ * attached with Common Clause Condition 1.0, found in the LICENSES directory. */ +#include "validator/test/ValidatorTestBase.h" + #include "common/base/Base.h" #include "context/QueryContext.h" #include "context/ValidateContext.h" @@ -14,14 +16,116 @@ #include "planner/Mutate.h" #include "planner/PlanNode.h" #include "planner/Query.h" -#include "validator/ASTValidator.h" -#include "validator/test/ValidatorTestBase.h" namespace nebula { namespace graph { +// static +void ValidatorTestBase::bfsTraverse(const PlanNode *root, std::vector<PlanNode::Kind> &result) { + std::queue<const PlanNode *> queue; + std::unordered_set<int64_t> visited; + queue.emplace(root); + + while (!queue.empty()) { + auto node = queue.front(); + VLOG(1) << "node kind: " << node->kind(); + queue.pop(); + if (visited.find(node->id()) != visited.end()) { + continue; + } + visited.emplace(node->id()); + result.emplace_back(node->kind()); + + switch (node->kind()) { + case PlanNode::Kind::kUnknown: + ASSERT_TRUE(false) << "Unknown Plan Node."; + case PlanNode::Kind::kStart: { + break; + } + case PlanNode::Kind::kCreateUser: + case PlanNode::Kind::kDropUser: + case PlanNode::Kind::kUpdateUser: + case PlanNode::Kind::kGrantRole: + case PlanNode::Kind::kRevokeRole: + case PlanNode::Kind::kChangePassword: + case PlanNode::Kind::kListUserRoles: + case PlanNode::Kind::kListUsers: + case PlanNode::Kind::kListRoles: + case PlanNode::Kind::kShowHosts: + case PlanNode::Kind::kGetNeighbors: + case PlanNode::Kind::kGetVertices: + case PlanNode::Kind::kGetEdges: + case PlanNode::Kind::kIndexScan: + case PlanNode::Kind::kFilter: + case PlanNode::Kind::kProject: + case PlanNode::Kind::kSort: + case PlanNode::Kind::kLimit: + case PlanNode::Kind::kAggregate: + case PlanNode::Kind::kSwitchSpace: + case PlanNode::Kind::kMultiOutputs: + case PlanNode::Kind::kDedup: + case PlanNode::Kind::kDataCollect: + case PlanNode::Kind::kCreateSpace: + case PlanNode::Kind::kCreateTag: + case PlanNode::Kind::kCreateEdge: + case PlanNode::Kind::kDescSpace: + case PlanNode::Kind::kDescTag: + case PlanNode::Kind::kDescEdge: + case PlanNode::Kind::kInsertVertices: + case PlanNode::Kind::kInsertEdges: + case PlanNode::Kind::kShowCreateSpace: + case PlanNode::Kind::kShowCreateTag: + case PlanNode::Kind::kShowCreateEdge: + case PlanNode::Kind::kDropSpace: + case PlanNode::Kind::kDropTag: + case PlanNode::Kind::kDropEdge: + case PlanNode::Kind::kShowSpaces: + case PlanNode::Kind::kShowTags: + case PlanNode::Kind::kShowEdges: + case PlanNode::Kind::kCreateSnapshot: + case PlanNode::Kind::kDropSnapshot: + case PlanNode::Kind::kShowSnapshots: + case PlanNode::Kind::kDataJoin: + case PlanNode::Kind::kDeleteVertices: + case PlanNode::Kind::kDeleteEdges: + case PlanNode::Kind::kUpdateVertex: + case PlanNode::Kind::kUpdateEdge: { + auto *current = static_cast<const SingleDependencyNode *>(node); + queue.emplace(current->dep()); + break; + } + case PlanNode::Kind::kUnion: + case PlanNode::Kind::kIntersect: + case PlanNode::Kind::kMinus: { + auto *current = static_cast<const BiInputNode *>(node); + queue.emplace(current->left()); + queue.emplace(current->right()); + break; + } + case PlanNode::Kind::kSelect: { + auto *current = static_cast<const Select *>(node); + queue.emplace(current->dep()); + queue.emplace(current->then()); + if (current->otherwise() != nullptr) { + queue.emplace(current->otherwise()); + } + break; + } + case PlanNode::Kind::kLoop: { + auto *current = static_cast<const Loop *>(node); + queue.emplace(current->dep()); + queue.emplace(current->body()); + break; + } + default: + LOG(FATAL) << "Unknown PlanNode: " << static_cast<int64_t>(node->kind()); + } + } +} + // Compare the node itself's field in this function -/*static*/ Status ValidatorTestBase::EqSelf(const PlanNode *l, const PlanNode *r) { +// static +Status ValidatorTestBase::EqSelf(const PlanNode *l, const PlanNode *r) { if (l != nullptr && r != nullptr) { // continue } else if (l == nullptr && r == nullptr) { @@ -213,4 +317,3 @@ int main(int argc, char** argv) { return RUN_ALL_TESTS(); } - diff --git a/src/validator/test/ValidatorTestBase.h b/src/validator/test/ValidatorTestBase.h index 737324d5f3141440c06071d8d002784de0d31565..592a4b161ac2ea1ea1159ca7c3421980ce1c17cf 100644 --- a/src/validator/test/ValidatorTestBase.h +++ b/src/validator/test/ValidatorTestBase.h @@ -19,7 +19,8 @@ #include "planner/Logic.h" #include "planner/PlanNode.h" #include "planner/Query.h" -#include "validator/ASTValidator.h" +#include "util/ObjectPool.h" +#include "validator/Validator.h" #include "validator/test/MockSchemaManager.h" namespace nebula { @@ -31,43 +32,31 @@ protected: session_ = Session::create(0); session_->setSpace("test_space", 1); schemaMng_ = CHECK_NOTNULL(MockSchemaManager::makeUnique()); - qCtx_ = buildContext(); - expectedQueryCtx_ = buildContext(); + pool_ = std::make_unique<ObjectPool>(); } - void TearDown() override { + ExecutionPlan* toPlan(const std::string& query) { + auto planStatus = validate(query); + EXPECT_TRUE(planStatus); + return std::move(planStatus).value(); } - // some utils - inline ::testing::AssertionResult toPlan(const std::string &query) { + StatusOr<ExecutionPlan*> validate(const std::string& query) { + VLOG(1) << "query: " << query; auto result = GQLParser().parse(query); if (!result.ok()) { - return ::testing::AssertionFailure() << result.status().toString(); - } - sentences_ = std::move(result).value(); - qCtx_ = buildContext(); - ASTValidator validator(sentences_.get(), qCtx_.get()); - auto validateResult = validator.validate(); - if (!validateResult.ok()) { - return ::testing::AssertionFailure() << validateResult.toString(); + return std::move(result).status(); } - return ::testing::AssertionSuccess(); - } - - StatusOr<ExecutionPlan*> validate(const std::string& query) { - auto result = GQLParser().parse(query); - if (!result.ok()) return std::move(result).status(); - auto sentences = std::move(result).value(); - ASTValidator validator(sentences.get(), qCtx_.get()); - NG_RETURN_IF_ERROR(validator.validate()); - return qCtx_->plan(); + auto sentences = pool_->add(std::move(result).value().release()); + auto qctx = buildContext(); + NG_RETURN_IF_ERROR(Validator::validate(sentences, qctx)); + return qctx->plan(); } - - std::unique_ptr<QueryContext> buildContext() { + QueryContext* buildContext() { auto rctx = std::make_unique<RequestContext<cpp2::ExecutionResponse>>(); rctx->setSession(session_); - auto qctx = std::make_unique<QueryContext>(); + auto qctx = pool_->add(new QueryContext()); qctx->setRctx(std::move(rctx)); qctx->setSchemaManager(schemaMng_.get()); qctx->setCharsetInfo(CharsetInfo::instance()); @@ -81,26 +70,17 @@ protected: ::testing::AssertionResult checkResult(const std::string& query, const std::vector<PlanNode::Kind>& expected = {}, const std::vector<std::string> &rootColumns = {}) { - VLOG(1) << "query: " << query; - auto result = GQLParser().parse(query); - if (!result.ok()) { - return ::testing::AssertionFailure() << result.status(); + auto planStatus = validate(query); + if (!planStatus) { + return ::testing::AssertionFailure() << std::move(planStatus).status().toString(); } - - auto sentences = std::move(result).value(); - auto context = buildContext(); - ASTValidator validator(sentences.get(), context.get()); - auto validateResult = validator.validate(); - if (!validateResult.ok()) { - return ::testing::AssertionFailure() << validateResult; + auto plan = std::move(planStatus).value(); + if (plan == nullptr) { + return ::testing::AssertionFailure() << "plan is nullptr"; } if (expected.empty()) { return ::testing::AssertionSuccess(); } - auto plan = context->plan(); - if (plan == nullptr) { - return ::testing::AssertionFailure() << "plan is nullptr"; - } auto assertResult = verifyPlan(plan->root(), expected); if (!assertResult) { return assertResult; @@ -132,115 +112,13 @@ protected: << "\n\tResult: " << result << "\n\tExpected: " << expected; } - static void bfsTraverse(const PlanNode* root, std::vector<PlanNode::Kind>& result) { - std::queue<const PlanNode*> queue; - std::unordered_set<int64_t> visited; - queue.emplace(root); - - while (!queue.empty()) { - auto node = queue.front(); - VLOG(1) << "node kind: " << node->kind(); - queue.pop(); - if (visited.find(node->id()) != visited.end()) { - continue; - } - visited.emplace(node->id()); - result.emplace_back(node->kind()); - - switch (node->kind()) { - case PlanNode::Kind::kUnknown: - ASSERT_TRUE(false) << "Unknown Plan Node."; - case PlanNode::Kind::kStart: { - break; - } - case PlanNode::Kind::kCreateUser: - case PlanNode::Kind::kDropUser: - case PlanNode::Kind::kUpdateUser: - case PlanNode::Kind::kGrantRole: - case PlanNode::Kind::kRevokeRole: - case PlanNode::Kind::kChangePassword: - case PlanNode::Kind::kListUserRoles: - case PlanNode::Kind::kListUsers: - case PlanNode::Kind::kListRoles: - case PlanNode::Kind::kShowHosts: - case PlanNode::Kind::kGetNeighbors: - case PlanNode::Kind::kGetVertices: - case PlanNode::Kind::kGetEdges: - case PlanNode::Kind::kIndexScan: - case PlanNode::Kind::kFilter: - case PlanNode::Kind::kProject: - case PlanNode::Kind::kSort: - case PlanNode::Kind::kLimit: - case PlanNode::Kind::kAggregate: - case PlanNode::Kind::kSwitchSpace: - case PlanNode::Kind::kMultiOutputs: - case PlanNode::Kind::kDedup: - case PlanNode::Kind::kDataCollect: - case PlanNode::Kind::kCreateSpace: - case PlanNode::Kind::kCreateTag: - case PlanNode::Kind::kCreateEdge: - case PlanNode::Kind::kDescSpace: - case PlanNode::Kind::kDescTag: - case PlanNode::Kind::kDescEdge: - case PlanNode::Kind::kInsertVertices: - case PlanNode::Kind::kInsertEdges: - case PlanNode::Kind::kShowCreateSpace: - case PlanNode::Kind::kShowCreateTag: - case PlanNode::Kind::kShowCreateEdge: - case PlanNode::Kind::kDropSpace: - case PlanNode::Kind::kDropTag: - case PlanNode::Kind::kDropEdge: - case PlanNode::Kind::kShowSpaces: - case PlanNode::Kind::kShowTags: - case PlanNode::Kind::kShowEdges: - case PlanNode::Kind::kCreateSnapshot: - case PlanNode::Kind::kDropSnapshot: - case PlanNode::Kind::kShowSnapshots: - case PlanNode::Kind::kDataJoin: - case PlanNode::Kind::kDeleteVertices: - case PlanNode::Kind::kDeleteEdges: - case PlanNode::Kind::kUpdateVertex: - case PlanNode::Kind::kUpdateEdge: { - auto* current = static_cast<const SingleDependencyNode*>(node); - queue.emplace(current->dep()); - break; - } - case PlanNode::Kind::kUnion: - case PlanNode::Kind::kIntersect: - case PlanNode::Kind::kMinus: { - auto* current = static_cast<const BiInputNode*>(node); - queue.emplace(current->left()); - queue.emplace(current->right()); - break; - } - case PlanNode::Kind::kSelect: { - auto* current = static_cast<const Select*>(node); - queue.emplace(current->dep()); - queue.emplace(current->then()); - if (current->otherwise() != nullptr) { - queue.emplace(current->otherwise()); - } - break; - } - case PlanNode::Kind::kLoop: { - auto* current = static_cast<const Loop*>(node); - queue.emplace(current->dep()); - queue.emplace(current->body()); - break; - } - default: - LOG(FATAL) << "Unknown PlanNode: " << static_cast<int64_t>(node->kind()); - } - } - } + static void bfsTraverse(const PlanNode* root, std::vector<PlanNode::Kind>& result); protected: std::shared_ptr<Session> session_; std::unique_ptr<MockSchemaManager> schemaMng_; - std::unique_ptr<QueryContext> qCtx_; std::unique_ptr<Sentence> sentences_; - // used to hold the expected query plan - std::unique_ptr<QueryContext> expectedQueryCtx_; + std::unique_ptr<ObjectPool> pool_; }; std::ostream& operator<<(std::ostream& os, const std::vector<PlanNode::Kind>& plan);