diff --git a/src/parser/TraverseSentences.h b/src/parser/TraverseSentences.h index 0c717e1d11a968f65673764c47e5b5598e1f31a6..3b85b1fbdf9a1c765a8f0817b363af935f4bb683 100644 --- a/src/parser/TraverseSentences.h +++ b/src/parser/TraverseSentences.h @@ -158,7 +158,7 @@ public: return right_.get(); } - auto op() { + Operator op() const { return op_; } @@ -166,7 +166,7 @@ public: distinct_ = true; } - auto distinct() { + bool distinct() const { return distinct_; } diff --git a/src/parser/parser.yy b/src/parser/parser.yy index d17d67073c01e5bd52b55ddb6183655cb4d7576e..e8b422e23bf0e35e817954770af1bac8d57618cf 100644 --- a/src/parser/parser.yy +++ b/src/parser/parser.yy @@ -794,8 +794,8 @@ group_clause yield_sentence : KW_YIELD yield_columns where_clause { auto *s = new YieldSentence($2); - s->setWhereClause($3); - $$ = s; + s->setWhereClause($3); + $$ = s; } | KW_YIELD KW_DISTINCT yield_columns where_clause { auto *s = new YieldSentence($3, true); diff --git a/src/planner/PlanNode.h b/src/planner/PlanNode.h index 459db15a6ec0cb2d82514d9f0a3ffdb5d6eda9ae..2c5c1f94a0742bd1762aa12fa120cdb422e9dda0 100644 --- a/src/planner/PlanNode.h +++ b/src/planner/PlanNode.h @@ -97,8 +97,8 @@ public: plan_ = plan; } - void setColNames(std::vector<std::string>&& cols) { - colNames_ = std::move(cols); + void setColNames(const std::vector<std::string>& cols) { + colNames_ = cols; } static const char* toString(Kind kind); diff --git a/src/validator/SequentialValidator.cpp b/src/validator/SequentialValidator.cpp index 263518f766f39319b079295790a9cb0fb4d87976..23df2d34c1c15782dc9bb06d7776f7332976591e 100644 --- a/src/validator/SequentialValidator.cpp +++ b/src/validator/SequentialValidator.cpp @@ -46,10 +46,7 @@ Status SequentialValidator::validateImpl() { } } auto validator = makeValidator(sentence, qctx_); - status = validator->validate(); - if (!status.ok()) { - return status; - } + NG_RETURN_IF_ERROR(validator->validate()); validators_.emplace_back(std::move(validator)); } @@ -61,13 +58,10 @@ Status SequentialValidator::toPlan() { root_ = validators_.back()->root(); ifBuildDataCollectForRoot(root_); for (auto iter = validators_.begin(); iter < validators_.end() - 1; ++iter) { - auto status = Validator::appendPlan((iter + 1)->get()->tail(), iter->get()->root()); - if (!status.ok()) { - return status; - } + NG_RETURN_IF_ERROR((iter + 1)->get()->appendPlan(iter->get()->root())); } tail_ = StartNode::make(plan); - Validator::appendPlan(validators_.front()->tail(), tail_); + NG_RETURN_IF_ERROR(validators_.front()->appendPlan(tail_)); return Status::OK(); } diff --git a/src/validator/SetValidator.cpp b/src/validator/SetValidator.cpp index 846f73a8cbef2a68be2aad930a9576cceaa167b0..59690ae62c3e30e7dbc2c8ecbb7a47aad6dfff3e 100644 --- a/src/validator/SetValidator.cpp +++ b/src/validator/SetValidator.cpp @@ -5,27 +5,24 @@ */ #include "validator/SetValidator.h" + #include "planner/Query.h" namespace nebula { namespace graph { + Status SetValidator::validateImpl() { - auto setSentence = static_cast<SetSentence*>(sentence_); + auto setSentence = static_cast<SetSentence *>(sentence_); lValidator_ = makeValidator(setSentence->left(), qctx_); - auto status = lValidator_->validate(); - if (!status.ok()) { - return status; - } + NG_RETURN_IF_ERROR(lValidator_->validate()); rValidator_ = makeValidator(setSentence->right(), qctx_); - status = rValidator_->validate(); - if (!status.ok()) { - return status; - } + NG_RETURN_IF_ERROR(rValidator_->validate()); auto lCols = lValidator_->outputs(); auto rCols = rValidator_->outputs(); - if (lCols.size() != rCols.size()) { - return Status::Error("The used statements have a diffrent number of columns."); + + if (UNLIKELY(lCols != rCols)) { + return Status::Error("Different columns to UNION/INTERSECT/MINUS"); } outputs_ = std::move(lCols); @@ -33,43 +30,48 @@ Status SetValidator::validateImpl() { } Status SetValidator::toPlan() { - auto* plan = qctx_->plan(); - switch (op_) { + auto plan = qctx_->plan(); + auto setSentence = static_cast<const SetSentence *>(sentence_); + auto lRoot = DCHECK_NOTNULL(lValidator_->root()); + auto rRoot = DCHECK_NOTNULL(rValidator_->root()); + auto colNames = lRoot->colNames(); + BiInputNode *bNode = nullptr; + switch (setSentence->op()) { case SetSentence::Operator::UNION: { - auto unionOp = Union::make( - plan, lValidator_->root(), rValidator_->root()); - if (distinct_) { - auto dedup = Dedup::make( - plan, - lValidator_->root()); + bNode = Union::make(plan, lRoot, rRoot); + if (setSentence->distinct()) { + auto dedup = Dedup::make(plan, bNode); + dedup->setInputVar(bNode->varName()); + dedup->setColNames(colNames); root_ = dedup; } else { - root_ = unionOp; + root_ = bNode; } break; } case SetSentence::Operator::INTERSECT: { - root_ = Intersect::make( - plan, - lValidator_->root(), - rValidator_->root()); + bNode = Intersect::make(plan, lRoot, rRoot); + root_ = bNode; break; } case SetSentence::Operator::MINUS: { - root_ = Minus::make( - plan, - lValidator_->root(), - rValidator_->root()); + bNode = Minus::make(plan, lRoot, rRoot); + root_ = bNode; break; } default: - return Status::Error("Unkown operator: %ld", static_cast<int64_t>(op_)); + return Status::Error("Unknown operator: %ld", static_cast<int64_t>(setSentence->op())); } + bNode->setColNames(colNames); + bNode->setLeftVar(lRoot->varName()); + bNode->setRightVar(rRoot->varName()); + tail_ = MultiOutputsNode::make(plan, nullptr); - Validator::appendPlan(lValidator_->tail(), tail_); - Validator::appendPlan(rValidator_->tail(), tail_); + NG_RETURN_IF_ERROR(lValidator_->appendPlan(tail_)); + NG_RETURN_IF_ERROR(rValidator_->appendPlan(tail_)); return Status::OK(); } -} // namespace graph -} // namespace nebula + +} // namespace graph +} // namespace nebula diff --git a/src/validator/SetValidator.h b/src/validator/SetValidator.h index 19cc8c690a09f3d3af27f740a3e34bd6c62ecd3a..3a475a6971e9409a38213ff75524ebe991205b31 100644 --- a/src/validator/SetValidator.h +++ b/src/validator/SetValidator.h @@ -12,6 +12,7 @@ namespace nebula { namespace graph { + class SetValidator final : public Validator { public: SetValidator(Sentence* sentence, QueryContext* context) @@ -37,9 +38,8 @@ private: private: std::unique_ptr<Validator> lValidator_; std::unique_ptr<Validator> rValidator_; - SetSentence::Operator op_; - bool distinct_; }; + } // namespace graph } // namespace nebula #endif // VALIDATOR_SETVALIDATOR_H_ diff --git a/src/validator/Validator.cpp b/src/validator/Validator.cpp index b72bc1121a8dbf5caad1880e5c83c02f272a1d00..25b2ec7f9104c3aed704e19baa0dcb674cce2572 100644 --- a/src/validator/Validator.cpp +++ b/src/validator/Validator.cpp @@ -74,7 +74,7 @@ std::unique_ptr<Validator> Validator::makeValidator(Sentence* sentence, QueryCon } Status Validator::appendPlan(PlanNode* node, PlanNode* appended) { - switch (node->kind()) { + switch (DCHECK_NOTNULL(node)->kind()) { case PlanNode::Kind::kFilter: case PlanNode::Kind::kProject: case PlanNode::Kind::kSort: @@ -82,6 +82,7 @@ Status Validator::appendPlan(PlanNode* node, PlanNode* appended) { case PlanNode::Kind::kAggregate: case PlanNode::Kind::kSelect: case PlanNode::Kind::kLoop: + case PlanNode::Kind::kMultiOutputs: case PlanNode::Kind::kSwitchSpace: case PlanNode::Kind::kCreateSpace: case PlanNode::Kind::kCreateTag: @@ -105,9 +106,11 @@ Status Validator::appendPlan(PlanNode* node, PlanNode* appended) { return Status::OK(); } -Status Validator::validate() { - Status status; +Status Validator::appendPlan(PlanNode* tail) { + return appendPlan(tail_, DCHECK_NOTNULL(tail)); +} +Status Validator::validate() { if (!vctx_) { VLOG(1) << "Validate context was not given."; return Status::Error("Validate context was not given."); @@ -120,23 +123,15 @@ Status Validator::validate() { if (!noSpaceRequired_ && !spaceChosen()) { VLOG(1) << "Space was not chosen."; - status = Status::Error("Space was not chosen."); - return status; + return Status::Error("Space was not chosen."); } if (!noSpaceRequired_) { space_ = vctx_->whichSpace(); } - status = validateImpl(); - if (!status.ok()) { - return status; - } - - status = toPlan(); - if (!status.ok()) { - return status; - } + NG_RETURN_IF_ERROR(validateImpl()); + NG_RETURN_IF_ERROR(toPlan()); return Status::OK(); } @@ -511,4 +506,3 @@ bool Validator::evaluableExpr(const Expression* expr) const { } // namespace graph } // namespace nebula - diff --git a/src/validator/Validator.h b/src/validator/Validator.h index e588785eb87a9eb520cebb9a1a23731fb3f3d6c0..c207693ee1ad318f61627b63f95938b3f6401e3d 100644 --- a/src/validator/Validator.h +++ b/src/validator/Validator.h @@ -28,7 +28,7 @@ public: static std::unique_ptr<Validator> makeValidator(Sentence* sentence, QueryContext* context); - static Status appendPlan(PlanNode* plan, PlanNode* appended); + MUST_USE_RESULT Status appendPlan(PlanNode* tail); Status validate(); @@ -36,19 +36,19 @@ public: inputs_ = std::move(inputs); } - auto root() const { + PlanNode* root() const { return root_; } - auto tail() const { + PlanNode* tail() const { return tail_; } - auto outputs() const { + ColsDef outputs() const { return outputs_; } - auto inputs() const { + ColsDef inputs() const { return inputs_; } @@ -80,6 +80,8 @@ protected: bool evaluableExpr(const Expression* expr) const; + static Status appendPlan(PlanNode* plan, PlanNode* appended); + protected: SpaceDescription space_; Sentence* sentence_{nullptr}; diff --git a/src/validator/test/QueryValidatorTest.cpp b/src/validator/test/QueryValidatorTest.cpp index 1a16ab44277a1a24db23de1c80cf58595e657f6d..c56ef8519f656d8a2177cc27d0f83a21e4b15816 100644 --- a/src/validator/test/QueryValidatorTest.cpp +++ b/src/validator/test/QueryValidatorTest.cpp @@ -288,6 +288,124 @@ TEST_F(QueryValidatorTest, OrderByAndLimt) { } } +TEST_F(QueryValidatorTest, TestSetValidator) { + // UNION ALL + { + std::string query = + "GO FROM \"1\" OVER like YIELD like.start AS start UNION ALL GO FROM \"2\" " + "OVER like YIELD like.start AS start"; + auto status = validate(query); + ASSERT_TRUE(status.ok()) << status.status(); + auto plan = std::move(status).value(); + ASSERT_NE(plan, nullptr); + std::vector<PlanNode::Kind> expected = { + PK::kDataCollect, + PK::kUnion, + PK::kProject, + PK::kProject, + PK::kGetNeighbors, + PK::kGetNeighbors, + PK::kMultiOutputs, + PK::kStart, + }; + ASSERT_TRUE(verifyPlan(plan->root(), expected)); + } + // UNION DISTINCT twice + { + std::string query = "GO FROM \"1\" OVER like YIELD like.start AS start UNION GO FROM \"2\" " + "OVER like YIELD like.start AS start UNION GO FROM \"3\" OVER like YIELD " + "like.start AS start"; + auto status = validate(query); + ASSERT_TRUE(status.ok()) << status.status(); + auto plan = std::move(status).value(); + ASSERT_NE(plan, nullptr); + std::vector<PlanNode::Kind> expected = { + PK::kDataCollect, + PK::kDedup, + PK::kUnion, + PK::kDedup, + PK::kProject, + PK::kUnion, + PK::kGetNeighbors, + PK::kProject, + PK::kProject, + PK::kMultiOutputs, + PK::kGetNeighbors, + PK::kGetNeighbors, + PK::kStart, + PK::kMultiOutputs, + }; + ASSERT_TRUE(verifyPlan(plan->root(), expected)); + } + // UNION DISTINCT + { + std::string query = + "GO FROM \"1\" OVER like YIELD like.start AS start UNION DISTINCT GO FROM \"2\" " + "OVER like YIELD like.start AS start"; + auto status = validate(query); + EXPECT_TRUE(status.ok()) << status.status(); + auto plan = std::move(status).value(); + ASSERT_NE(plan, nullptr); + std::vector<PlanNode::Kind> expected = { + PK::kDataCollect, + PK::kDedup, + PK::kUnion, + PK::kProject, + PK::kProject, + PK::kGetNeighbors, + PK::kGetNeighbors, + PK::kMultiOutputs, + PK::kStart, + }; + ASSERT_TRUE(verifyPlan(plan->root(), expected)); + } + // INVALID UNION ALL + { + std::string query = "GO FROM \"1\" OVER like YIELD like.start AS start, $^.person.name AS " + "name UNION GO FROM \"2\" OVER like YIELD like.start AS start"; + auto status = validate(query); + ASSERT_FALSE(status.ok()) << status.status(); + } + // INTERSECT + { + std::string query = "GO FROM \"1\" OVER like YIELD like.start AS start INTERSECT GO FROM " + "\"2\" OVER like YIELD like.start AS start"; + auto status = validate(query); + EXPECT_TRUE(status.ok()) << status.status(); + auto plan = std::move(status).value(); + ASSERT_NE(plan, nullptr); + std::vector<PlanNode::Kind> expected = { + PK::kDataCollect, + PK::kIntersect, + PK::kProject, + PK::kProject, + PK::kGetNeighbors, + PK::kGetNeighbors, + PK::kMultiOutputs, + PK::kStart, + }; + ASSERT_TRUE(verifyPlan(plan->root(), expected)); + } + // MINUS + { + std::string query = "GO FROM \"1\" OVER like YIELD like.start AS start MINUS GO FROM " + "\"2\" OVER like YIELD like.start AS start"; + auto status = validate(query); + EXPECT_TRUE(status.ok()) << status.status(); + auto plan = std::move(status).value(); + ASSERT_NE(plan, nullptr); + std::vector<PlanNode::Kind> expected = { + PK::kDataCollect, + PK::kMinus, + PK::kProject, + PK::kProject, + PK::kGetNeighbors, + PK::kGetNeighbors, + PK::kMultiOutputs, + PK::kStart, + }; + ASSERT_TRUE(verifyPlan(plan->root(), expected)); + } +} } // namespace graph } // namespace nebula -