From 1e87fb21586b275db9591d677be3bc265c13a855 Mon Sep 17 00:00:00 2001 From: lipanpan03 <41904587+lipanpan03@users.noreply.github.com> Date: Tue, 16 Jul 2024 17:59:40 +0800 Subject: [PATCH] Implementation feature list:Union, Remove, ListComprehension, Profile & Tests includes TestUnion, TestRemove, TestListComprehension, TestProfile (#577) * merge cypher_lpp * merge cypher_lpp * rm explicit * fix log_info * fix pattern graph dump plan * fix comment * fix comment * fix comment * fix comment * fix comment * fix comment * fix comment * fix comment * fix comment * fix comment * fix comment * fix comment * fix comment * fix comment * fix comment * fix comment * fix comment * update ci.yml * fix list comprehension * fix list comprehension * fix list comprehension * fix list comprehension * fix list comprehension * fix list comprehension * fix list comprehension --- .../include/geax-front-end/ast/AstDumper.h | 21 ++- .../include/geax-front-end/ast/AstNode.h | 2 + .../geax-front-end/ast/AstNodeVisitor.h | 15 ++ .../geax-front-end/ast/clause/ClauseNodeFwd.h | 1 + .../geax-front-end/ast/clause/RemoveItem.h | 22 +++ .../ast/clause/RemoveSingleProperty.h | 35 +++++ .../geax-front-end/ast/expr/ExprNodeFwd.h | 1 + .../ast/expr/ListComprehension.h | 61 ++++++++ .../geax-front-end/ast/stmt/RemoveStatement.h | 7 + .../geax-front-end/ast/utils/AstUtil.h | 3 + .../geax-front-end/isogql/GQLAstVisitor.h | 4 + .../geax-front-end/isogql/GQLAstVisitor.cpp | 54 ++++++++ src/BuildCypherLib.cmake | 1 + src/cypher/arithmetic/ast_expr_evaluator.cpp | 21 +++ src/cypher/arithmetic/ast_expr_evaluator.h | 1 + .../execution_plan/execution_plan_maker.cpp | 87 +++++++++++- .../execution_plan/execution_plan_maker.h | 3 + src/cypher/execution_plan/ops/op.h | 3 +- .../execution_plan/ops/op_gql_remove.cpp | 19 +++ src/cypher/execution_plan/ops/op_gql_remove.h | 82 +++++++++++ src/cypher/execution_plan/ops/op_project.h | 10 +- src/cypher/execution_plan/ops/ops.h | 1 + .../execution_plan/pattern_graph_maker.cpp | 61 ++++++-- .../execution_plan/pattern_graph_maker.h | 3 + src/cypher/parser/cypher_base_visitor_v2.cpp | 130 ++++++++++++++---- src/cypher/parser/cypher_base_visitor_v2.h | 14 +- src/cypher/utils/ast_node_visitor_impl.h | 8 ++ src/cypher/utils/geax_expr_util.h | 22 ++- .../create_yago/cypher/create_yago.result | 2 +- .../create_yago/cypher/create_yago.test | 2 +- .../cypher/list_comprehension.result | 12 ++ .../cypher/list_comprehension.test | 8 +- .../unit_test/profile/cypher/profile.test | 2 +- .../undefined_var/cypher/undefined_var.result | 2 +- test/test_cypher_v2.cpp | 65 +++++---- 35 files changed, 703 insertions(+), 82 deletions(-) create mode 100644 deps/geax-front-end/include/geax-front-end/ast/clause/RemoveItem.h create mode 100644 deps/geax-front-end/include/geax-front-end/ast/clause/RemoveSingleProperty.h create mode 100644 deps/geax-front-end/include/geax-front-end/ast/expr/ListComprehension.h create mode 100644 src/cypher/execution_plan/ops/op_gql_remove.cpp create mode 100644 src/cypher/execution_plan/ops/op_gql_remove.h diff --git a/deps/geax-front-end/include/geax-front-end/ast/AstDumper.h b/deps/geax-front-end/include/geax-front-end/ast/AstDumper.h index 4f2f54ba6e..da3e929c23 100644 --- a/deps/geax-front-end/include/geax-front-end/ast/AstDumper.h +++ b/deps/geax-front-end/include/geax-front-end/ast/AstDumper.h @@ -489,6 +489,15 @@ class AstDumper : public AstNodeVisitor { VISIT_PARAM_AND_CHECK_WITH_MSG(value); return GEAXErrorCode::GEAX_SUCCEED; } + std::any visit(RemoveSingleProperty* node) override { + INDET_GUARD(); + VARIABLE_GUARD_WITH_TYPE_NAME(RemoveSingleProperty); + auto& v = node->v(); + auto& property = node->property(); + VISIT_PARAM_AND_CHECK_WITH_MSG(v); + VISIT_PARAM_AND_CHECK_WITH_MSG(property); + return GEAXErrorCode::GEAX_SUCCEED; + } std::any visit(SetLabel* node) override { INDET_GUARD(); VARIABLE_GUARD_WITH_TYPE_NAME(GetField); @@ -1345,9 +1354,13 @@ class AstDumper : public AstNodeVisitor { VISIT_PARAM_AND_CHECK_WITH_MSG(items); return GEAXErrorCode::GEAX_SUCCEED; } - std::any visit(RemoveStatement*) override { + std::any visit(RemoveStatement* node) override { INDET_GUARD(); VARIABLE_GUARD_WITH_TYPE_NAME(RemoveStatement); + auto& items = node->items(); + for (auto &item : items) { + VISIT_PARAM_AND_CHECK_WITH_MSG(item); + } return GEAXErrorCode::GEAX_SUCCEED; } std::any visit(MergeStatement* node) override { @@ -1496,6 +1509,12 @@ class AstDumper : public AstNodeVisitor { return GEAXErrorCode::GEAX_SUCCEED; } std::any visit(DummyNode* node) override { return reportError(node); } + std::any visit(ListComprehension* node) override { + VISIT_PARAM_AND_CHECK_WITH_MSG(node->getVariable()); + VISIT_PARAM_AND_CHECK_WITH_MSG(node->getInExpression()); + VISIT_PARAM_AND_CHECK_WITH_MSG(node->getOpExpression()); + return GEAXErrorCode::GEAX_SUCCEED; + } protected: std::any reportError() override { return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; } diff --git a/deps/geax-front-end/include/geax-front-end/ast/AstNode.h b/deps/geax-front-end/include/geax-front-end/ast/AstNode.h index 220262bc20..c06f719761 100644 --- a/deps/geax-front-end/include/geax-front-end/ast/AstNode.h +++ b/deps/geax-front-end/include/geax-front-end/ast/AstNode.h @@ -92,6 +92,7 @@ using StrArray = std::array; TYPE(ReplaceStatement, kReplaceStatement, "ReplaceStatement") \ TYPE(SetStatement, kSetStatement, "SetStatement") \ TYPE(RemoveStatement, kRemoveStatement, "RemoveStatement") \ + TYPE(RemoveSingleProperty, kRemoveSingleProperty, "RemoveSingleProperty") \ TYPE(MergeStatement, kMergeStatement, "MergeStatement") \ TYPE(OtherWise, kOtherWise, "OtherWise") \ TYPE(Union, kUnion, "Union") \ @@ -188,6 +189,7 @@ using StrArray = std::array; TYPE(MkRecord, kMkRecord, "MkRecord") \ TYPE(MkSet, kMkSet, "MkSet") \ TYPE(MkTuple, kMkTuple, "MkTuple") \ + TYPE(ListComprehension, kListComprehension, "ListComprehension") \ TYPE(UnwindStatement, kUnwindStatement, "UnwindStatement") \ TYPE(InQueryProcedureCall, kInQueryProcedureCall, "InQueryProcedureCall") \ TYPE(DummyNode, kNotDefined, "NotDefined") diff --git a/deps/geax-front-end/include/geax-front-end/ast/AstNodeVisitor.h b/deps/geax-front-end/include/geax-front-end/ast/AstNodeVisitor.h index 2634a3ac57..00f069e04e 100644 --- a/deps/geax-front-end/include/geax-front-end/ast/AstNodeVisitor.h +++ b/deps/geax-front-end/include/geax-front-end/ast/AstNodeVisitor.h @@ -66,6 +66,7 @@ class ResetSchema; class ResetTimeZone; class ResetGraph; class ResetParam; +class RemoveSingleProperty; class BEqual; class BNotEqual; @@ -181,6 +182,7 @@ class KillStatement; class ManagerStatement; class UnwindStatement; class InQueryProcedureCall; +class ListComprehension; class DummyNode; @@ -233,6 +235,7 @@ class AstNodeVisitor { virtual std::any visit(ResetTimeZone* node) = 0; virtual std::any visit(ResetGraph* node) = 0; virtual std::any visit(ResetParam* node) = 0; + virtual std::any visit(RemoveSingleProperty* node) = 0; //--------------------------------------------------------------------------------- // exprs @@ -285,6 +288,8 @@ class AstNodeVisitor { virtual std::any visit(MkSet* node) = 0; virtual std::any visit(MkTuple* node) = 0; + virtual std::any visit(ListComprehension* node) = 0; + virtual std::any visit(VBool* node) = 0; virtual std::any visit(VInt* node) = 0; virtual std::any visit(VDouble* node) = 0; @@ -482,6 +487,9 @@ class AstExprNodeVisitorImpl : public AstNodeVisitor { virtual std::any visit(ResetParam*) override { return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; } + virtual std::any visit(RemoveSingleProperty* node) override { + return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; + } //--------------------------------------------------------------------------------- // exprs @@ -546,6 +554,7 @@ class AstExprNodeVisitorImpl : public AstNodeVisitor { virtual std::any visit(VNone* node) override = 0; virtual std::any visit(Ref* node) override = 0; virtual std::any visit(Param* node) override = 0; + virtual std::any visit(ListComprehension* node) override = 0; // predicates virtual std::any visit(IsNull*) override { @@ -820,6 +829,9 @@ class AstLabelTreeNodeVisitorImpl : public AstNodeVisitor { virtual std::any visit(SetSingleProperty*) override { return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; } + virtual std::any visit(RemoveSingleProperty*) override { + return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; + } virtual std::any visit(SetSchemaClause*) override { return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; } @@ -1054,6 +1066,9 @@ class AstLabelTreeNodeVisitorImpl : public AstNodeVisitor { virtual std::any visit(Exists*) override { return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; } + virtual std::any visit(ListComprehension*) override { + return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; + } //--------------------------------------------------------------------------------- // stmt diff --git a/deps/geax-front-end/include/geax-front-end/ast/clause/ClauseNodeFwd.h b/deps/geax-front-end/include/geax-front-end/ast/clause/ClauseNodeFwd.h index d471442536..dc9127050c 100644 --- a/deps/geax-front-end/include/geax-front-end/ast/clause/ClauseNodeFwd.h +++ b/deps/geax-front-end/include/geax-front-end/ast/clause/ClauseNodeFwd.h @@ -59,5 +59,6 @@ #include "geax-front-end/ast/clause/UpdateProperties.h" #include "geax-front-end/ast/clause/WhereClause.h" #include "geax-front-end/ast/clause/YieldField.h" +#include "geax-front-end/ast/clause/RemoveSingleProperty.h" #endif // GEAXFRONTEND_AST_CLAUSE_CLAUSENODEFWD_H_ diff --git a/deps/geax-front-end/include/geax-front-end/ast/clause/RemoveItem.h b/deps/geax-front-end/include/geax-front-end/ast/clause/RemoveItem.h new file mode 100644 index 0000000000..a4872a7888 --- /dev/null +++ b/deps/geax-front-end/include/geax-front-end/ast/clause/RemoveItem.h @@ -0,0 +1,22 @@ +// +// Created by lipanpan on 2024/6/18. +// + +#ifndef GEAXFRONTEND_AST_CLAUSE_REMOVEITEM_H_ +#define GEAXFRONTEND_AST_CLAUSE_REMOVEITEM_H_ + +#include "geax-front-end/ast/AstNode.h" + +namespace geax { +namespace frontend { + +class RemoveItem : public AstNode { + public: + explicit RemoveItem(AstNodeType type) : AstNode(type) {} + ~RemoveItem() = default; +}; + +} // namespace frontend +} // namespace geax + +#endif // GEAXFRONTEND_AST_CLAUSE_REMOVEITEM_H_ diff --git a/deps/geax-front-end/include/geax-front-end/ast/clause/RemoveSingleProperty.h b/deps/geax-front-end/include/geax-front-end/ast/clause/RemoveSingleProperty.h new file mode 100644 index 0000000000..4408b5a29e --- /dev/null +++ b/deps/geax-front-end/include/geax-front-end/ast/clause/RemoveSingleProperty.h @@ -0,0 +1,35 @@ +// +// Created by lipanpan on 2024/6/18. +// + +#ifndef GEAXFRONTEND_AST_CLAUSE_REMOVESINGLEPROPERTY_H_ +#define GEAXFRONTEND_AST_CLAUSE_REMOVESINGLEPROPERTY_H_ + +#include "geax-front-end/ast/clause/RemoveItem.h" +#include "geax-front-end/ast/expr/Expr.h" + +namespace geax { +namespace frontend { + +class RemoveSingleProperty : public RemoveItem { + public: + RemoveSingleProperty() : RemoveItem(AstNodeType::kRemoveSingleProperty) {} + ~RemoveSingleProperty() = default; + + void setV(std::string&& v) { v_ = std::move(v); } + const std::string& v() const { return v_; } + + void setProperty(std::string&& property) { property_ = property; } + const std::string& property() const { return property_; } + + std::any accept(AstNodeVisitor& visitor) override { return visitor.visit(this); } + + private: + std::string v_; + std::string property_; +}; + +} // namespace frontend +} // namespace geax + +#endif // GEAXFRONTEND_AST_CLAUSE_REMOVESINGLEPROPERTY_H_ diff --git a/deps/geax-front-end/include/geax-front-end/ast/expr/ExprNodeFwd.h b/deps/geax-front-end/include/geax-front-end/ast/expr/ExprNodeFwd.h index efedac946c..aabc54c0ec 100644 --- a/deps/geax-front-end/include/geax-front-end/ast/expr/ExprNodeFwd.h +++ b/deps/geax-front-end/include/geax-front-end/ast/expr/ExprNodeFwd.h @@ -66,6 +66,7 @@ #include "geax-front-end/ast/expr/MkRecord.h" #include "geax-front-end/ast/expr/MkSet.h" #include "geax-front-end/ast/expr/MkTuple.h" +#include "geax-front-end/ast/expr/ListComprehension.h" #include "geax-front-end/ast/expr/MultiCount.h" #include "geax-front-end/ast/expr/Neg.h" #include "geax-front-end/ast/expr/Not.h" diff --git a/deps/geax-front-end/include/geax-front-end/ast/expr/ListComprehension.h b/deps/geax-front-end/include/geax-front-end/ast/expr/ListComprehension.h new file mode 100644 index 0000000000..dbb8380365 --- /dev/null +++ b/deps/geax-front-end/include/geax-front-end/ast/expr/ListComprehension.h @@ -0,0 +1,61 @@ +/** +* Copyright 2023 AntGroup CO., Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +* Author: +* lili +*/ + +#ifndef GEAXFRONTEND_AST_EXPR_LIST_COMPREHENSION_ +#define GEAXFRONTEND_AST_EXPR_LIST_COMPREHENSION_ + +#include "geax-front-end/ast/expr/Expr.h" +#include + +namespace geax { +namespace frontend { + +class ListComprehension : public Expr { +public: + ListComprehension() : Expr(AstNodeType::kListComprehension) {} + ~ListComprehension() = default; + void setVariable(Expr* expr) {variable_ = expr; } + void setInExpression(Expr* expr) {in_expression_ = expr; } + void setOpExpression(Expr* expr) {op_expression_ = expr; } + Expr* getVariable() {return variable_; } + Expr* getInExpression() {return in_expression_; } + Expr* getOpExpression() {return op_expression_; } + + std::any accept(AstNodeVisitor& visitor) override { return visitor.visit(this); } + +private: + bool equals(const Expr& other) const override; + + // now (variable, in_expression, op_expression), scalable + Expr* variable_; + Expr* in_expression_; + Expr* op_expression_; +}; // class ListComprehension + +inline bool ListComprehension::equals(const Expr& other) const { + const auto& expr = dynamic_cast(other); + return variable_ != nullptr && expr.variable_ != nullptr && + variable_ == expr.variable_ && in_expression_ != nullptr && + expr.in_expression_ != nullptr && in_expression_ == expr.in_expression_ && + op_expression_ != nullptr && expr.op_expression_ != nullptr && + op_expression_ == expr.op_expression_; +} + +} // namespace frontend +} // namespace geax + +#endif // GEAXFRONTEND_AST_EXPR_LIST_COMPREHENSION_ diff --git a/deps/geax-front-end/include/geax-front-end/ast/stmt/RemoveStatement.h b/deps/geax-front-end/include/geax-front-end/ast/stmt/RemoveStatement.h index 84eb58a8c8..b702215091 100644 --- a/deps/geax-front-end/include/geax-front-end/ast/stmt/RemoveStatement.h +++ b/deps/geax-front-end/include/geax-front-end/ast/stmt/RemoveStatement.h @@ -18,6 +18,7 @@ #ifndef GEAXFRONTEND_AST_STMT_REMOVESTATEMENT_H_ #define GEAXFRONTEND_AST_STMT_REMOVESTATEMENT_H_ +#include "geax-front-end/ast/clause/RemoveItem.h" #include "geax-front-end/ast/stmt/PrimitiveDataModifyStatement.h" namespace geax { @@ -27,8 +28,14 @@ class RemoveStatement : public PrimitiveDataModifyStatement { public: RemoveStatement() : PrimitiveDataModifyStatement(AstNodeType::kRemoveStatement) {} ~RemoveStatement() = default; + void appendItem(RemoveItem* item) { items_.emplace_back(item); } + void setItems(std::vector&& items) { items_ = std::move(items); } + const std::vector& items() const { return items_; } std::any accept(AstNodeVisitor& visitor) override { return visitor.visit(this); } + + private: + std::vector items_; }; // class RemoveStatement } // namespace frontend diff --git a/deps/geax-front-end/include/geax-front-end/ast/utils/AstUtil.h b/deps/geax-front-end/include/geax-front-end/ast/utils/AstUtil.h index 4745d7fbcf..cbf1bd716a 100644 --- a/deps/geax-front-end/include/geax-front-end/ast/utils/AstUtil.h +++ b/deps/geax-front-end/include/geax-front-end/ast/utils/AstUtil.h @@ -155,6 +155,9 @@ class AstExprToString : public AstExprNodeVisitorImpl { std::any visit(geax::frontend::MkList* node) override { return geax::frontend::GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; } + std::any visit(geax::frontend::ListComprehension* node) override { + return geax::frontend::GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; + } std::any visit(geax::frontend::MkMap* node) override { str_ += "{"; for (auto& pair : node->elems()) { diff --git a/deps/geax-front-end/include/geax-front-end/isogql/GQLAstVisitor.h b/deps/geax-front-end/include/geax-front-end/isogql/GQLAstVisitor.h index 74ad12e5b8..9be7093462 100644 --- a/deps/geax-front-end/include/geax-front-end/isogql/GQLAstVisitor.h +++ b/deps/geax-front-end/include/geax-front-end/isogql/GQLAstVisitor.h @@ -406,6 +406,10 @@ class GQLAstVisitor : public parser::GqlParserBaseVisitor { std::any visitTerminal(antlr4::tree::TerminalNode *node) override; std::any visitChildren(antlr4::tree::ParseTree *node) override; + // remove function + std::any visitRemoveStatement(parser::GqlParser::RemoveStatementContext *ctx) override; + std::any visitRemovePropertyItem(parser::GqlParser::RemovePropertyItemContext *ctx) override; + // helper functions GEAXErrorCode visitBinaryExpr(antlr4::ParserRuleContext *lhs, antlr4::ParserRuleContext *rhs, BinaryOp *expr); diff --git a/deps/geax-front-end/src/geax-front-end/isogql/GQLAstVisitor.cpp b/deps/geax-front-end/src/geax-front-end/isogql/GQLAstVisitor.cpp index 3fe17c47c1..9bb6565ea2 100644 --- a/deps/geax-front-end/src/geax-front-end/isogql/GQLAstVisitor.cpp +++ b/deps/geax-front-end/src/geax-front-end/isogql/GQLAstVisitor.cpp @@ -939,6 +939,11 @@ std::any GQLAstVisitor::visitAmbientLinearDataModifyingStatementBody( dataModifyStmt->appendModifyStatement(merge); break; } + case AstNodeType::kRemoveStatement: { + RemoveStatement* remove = castAs(childRes_, AstNodeType::kRemoveStatement); + dataModifyStmt->appendModifyStatement(remove); + break; + } default: ret = GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; LOG(WARNING) << "Child type is not supported. " << KV("type", childRes_->type()) @@ -5332,6 +5337,55 @@ std::string GQLAstVisitor::escapeText(const std::string& text) const { } return buff; } +std::any GQLAstVisitor::visitRemoveStatement(GqlParser::RemoveStatementContext* ctx) { + auto ret = GEAXErrorCode::GEAX_SUCCEED; + RemoveStatement* removeStatement = ALLOC_GEAOBJECT(RemoveStatement); + DEFER(removeStatement, ret); + + GqlParser::RemoveItemListContext* removeItemListCtx = nullptr; + if (GEAX_IS_NULL(removeItemListCtx = ctx->removeItemList())) { + ret = GEAXErrorCode::GEAX_COMMON_NULLPTR; + LOG(WARNING) << "RemoveItemListCtx is not set: " << K(ret); + } else { + std::vector removeItemCtxVec = removeItemListCtx->removeItem(); + for (size_t i = 0; GEAX_OK(ret) && i < removeItemCtxVec.size(); ++i) { + if (GEAX_IS_NULL(removeItemCtxVec[i])) { + ret = GEAXErrorCode::GEAX_COMMON_NULLPTR; + LOG(WARNING) << "SetItem is not set in SetStatement: " << K(ret); + } else if (GEAX_RET_FAIL(VISIT_RULE_WITH_FA(removeItemCtxVec[i], removeStatement))) { + LOG(WARNING) << "Failed to visit setItem: " << K(ret); + } + } + } + + return {}; +} +std::any GQLAstVisitor::visitRemovePropertyItem(GqlParser::RemovePropertyItemContext* ctx) { + auto ret = GEAXErrorCode::GEAX_SUCCEED; + DEFER_RET(ret); + + GqlParser::BindingVariableContext* refCtx = nullptr; + GqlParser::PropertyNameContext* nameCtx = nullptr; + RemoveStatement* removeStatement = nullptr; + if (GEAX_IS_NULL(removeStatement = castAs(faRes_, AstNodeType::kRemoveStatement))) { + ret = GEAXErrorCode::GEAX_COMMON_NULLPTR; + LOG(WARNING) << "Failed to cast to node kRemoveStatement: " << K(ret); + } else if (GEAX_IS_NULL(ctx->bindingVariableReference()) || + GEAX_IS_NULL(refCtx = ctx->bindingVariableReference()->bindingVariable())) { + ret = GEAXErrorCode::GEAX_COMMON_NULLPTR; + LOG(WARNING) << "BindingVariable is not set: " << K(ret); + } else if (GEAX_IS_NULL(nameCtx = ctx->propertyName())) { + ret = GEAXErrorCode::GEAX_COMMON_NULLPTR; + LOG(WARNING) << "PropertyName is not set: " << K(ret); + } else { + auto* removeSingleProperty = ALLOC_GEAOBJECT(RemoveSingleProperty); + removeSingleProperty->setV(refCtx->getText()); + removeSingleProperty->setProperty(trimAccentGrave(nameCtx->getText())); + removeStatement->appendItem(removeSingleProperty); + } + + return {}; +} template T* GQLAstVisitor::castAs(AstNode* node, AstNodeType type) { diff --git a/src/BuildCypherLib.cmake b/src/BuildCypherLib.cmake index 08edf416fc..34154a6dc6 100644 --- a/src/BuildCypherLib.cmake +++ b/src/BuildCypherLib.cmake @@ -54,6 +54,7 @@ set(LGRAPH_CYPHER_SRC # find cypher/ -name "*.cpp" | sort cypher/execution_plan/ops/op_gql_merge.cpp cypher/execution_plan/ops/op_node_by_id_seek.cpp cypher/execution_plan/ops/op_traversal.cpp + cypher/execution_plan/ops/op_gql_remove.cpp cypher/execution_plan/scheduler.cpp cypher/filter/filter.cpp cypher/filter/iterator.cpp diff --git a/src/cypher/arithmetic/ast_expr_evaluator.cpp b/src/cypher/arithmetic/ast_expr_evaluator.cpp index 51b2a06a60..1e1d11e21e 100644 --- a/src/cypher/arithmetic/ast_expr_evaluator.cpp +++ b/src/cypher/arithmetic/ast_expr_evaluator.cpp @@ -539,4 +539,25 @@ std::any cypher::AstExprEvaluator::visit(geax::frontend::Exists* node) { std::any cypher::AstExprEvaluator::reportError() { return error_msg_; } +std::any AstExprEvaluator::visit(geax::frontend::ListComprehension* node) { + geax::frontend::Ref *ref = nullptr; + geax::frontend::Expr *in_expr = nullptr, *op_expr = nullptr; + checkedCast(node->getVariable(), ref); + checkedCast(node->getInExpression(), in_expr); + checkedCast(node->getOpExpression(), op_expr); + Entry in_e; + checkedAnyCast(in_expr->accept(*this), in_e); + CYPHER_THROW_ASSERT(in_e.IsArray()); + auto data_array = in_e.constant.array; + std::vector<::lgraph::FieldData> ret_data; + auto it = sym_tab_->symbols.find(ref->name()); + for (auto &data : *data_array) { + const_cast(record_)->values[it->second.id] = Entry(cypher::FieldData(data)); + Entry one_result; + checkedAnyCast(op_expr->accept(*this), one_result); + ret_data.push_back(one_result.constant.scalar); + } + return Entry(cypher::FieldData(ret_data)); +} + } // namespace cypher diff --git a/src/cypher/arithmetic/ast_expr_evaluator.h b/src/cypher/arithmetic/ast_expr_evaluator.h index 582c45958f..f09e12be91 100644 --- a/src/cypher/arithmetic/ast_expr_evaluator.h +++ b/src/cypher/arithmetic/ast_expr_evaluator.h @@ -150,6 +150,7 @@ class AstExprEvaluator : public geax::frontend::AstExprNodeVisitorImpl { std::any visit(geax::frontend::LabelOr* node) override; std::any visit(geax::frontend::IsLabeled* node) override; std::any visit(geax::frontend::IsNull* node) override; + std::any visit(geax::frontend::ListComprehension* node) override; std::any visit(geax::frontend::Exists* node) override; std::any reportError() override; diff --git a/src/cypher/execution_plan/execution_plan_maker.cpp b/src/cypher/execution_plan/execution_plan_maker.cpp index e86953e717..3742cafc93 100644 --- a/src/cypher/execution_plan/execution_plan_maker.cpp +++ b/src/cypher/execution_plan/execution_plan_maker.cpp @@ -117,6 +117,23 @@ static OpBase* _Connect(OpBase* lhs, OpBase* rhs, PatternGraph* pattern_graph) { return rhs; } +static bool CheckReturnElements(const std::vector &last_ret, + const std::vector &now_ret) { + // some certain single query (e.g. MATCH(n) RETURN *) without no return items + // cannot be unioned. + if (last_ret.empty() || now_ret.empty()) { + return false; + } + // if two queries return different size of items, cannot be unioned + if (last_ret.size() != now_ret.size()) { + return false; + } + for (int j = 0; j < (int)last_ret.size(); j++) { + if (last_ret[j] != now_ret[j]) return false; + } + return true; +} + geax::frontend::GEAXErrorCode ExecutionPlanMaker::Build(geax::frontend::AstNode* astNode, OpBase*& root) { cur_types_.clear(); @@ -125,9 +142,12 @@ geax::frontend::GEAXErrorCode ExecutionPlanMaker::Build(geax::frontend::AstNode* return ret; } _DumpPlanBeforeConnect(0, false); + LOG_DEBUG() << "Dump plan finished!"; root = pattern_graph_root_[0]; for (size_t i = 1; i < pattern_graph_root_.size(); i++) { - root = _Connect(root, pattern_graph_root_[i], &(pattern_graphs_[i])); + if (should_connect_[i]) { + root = _Connect(root, pattern_graph_root_[i], &(pattern_graphs_[i])); + } } if (op_filter_ != nullptr) { NOT_SUPPORT(); @@ -137,8 +157,15 @@ geax::frontend::GEAXErrorCode ExecutionPlanMaker::Build(geax::frontend::AstNode* std::string ExecutionPlanMaker::_DumpPlanBeforeConnect(int indent, bool statistics) const { std::string s = "Execution Plan Before Connect: \n"; - for (auto seg : pattern_graph_root_) OpBase::DumpStream(seg, 0, false, s); - LOG_DEBUG() << s; + if (lgraph_log::LoggerManager::GetInstance().GetLevel() + == lgraph_log::severity_level::DEBUG) { + for (size_t i = 0; i < pattern_graph_root_.size(); i++) { + if (should_connect_[i]) { + OpBase::DumpStream(pattern_graph_root_[i], 0, false, s); + } + } + LOG_DEBUG() << s; + } return s; } @@ -769,10 +796,12 @@ std::any ExecutionPlanMaker::visit(geax::frontend::SessionSet* node) { NOT_SUPPO std::any ExecutionPlanMaker::visit(geax::frontend::SessionReset* node) { NOT_SUPPORT(); } std::any ExecutionPlanMaker::visit(geax::frontend::ProcedureBody* node) { - pattern_graph_size_ = node->statements().size(); + pattern_graph_size_ = pattern_graphs_.size(); pattern_graph_root_.resize(pattern_graph_size_, nullptr); + should_connect_.resize(pattern_graph_size_, true); + cur_pattern_graph_ = -1; for (size_t i = 0; i < node->statements().size(); i++) { - cur_pattern_graph_ = i; + cur_pattern_graph_ += 1; // Build Argument auto& sym_tab = pattern_graphs_[cur_pattern_graph_].symbol_table; for (auto& symbol : sym_tab.symbols) { @@ -829,6 +858,19 @@ std::any ExecutionPlanMaker::visit(geax::frontend::JoinRightPart* node) { NOT_SU std::any ExecutionPlanMaker::visit(geax::frontend::CompositeQueryStatement* node) { auto head = node->head(); ACCEPT_AND_CHECK_WITH_ERROR_MSG(head); + if (!node->body().empty()) { + auto op_union = new Union(); + op_union->AddChild(pattern_graph_root_[cur_pattern_graph_]); + auto op_produce = new ProduceResults(); + op_produce->AddChild(op_union); + pattern_graph_root_[cur_pattern_graph_] = op_produce; + for (auto statement : node->body()) { + cur_pattern_graph_ += 1; + should_connect_[cur_pattern_graph_] = false; + ACCEPT_AND_CHECK_WITH_ERROR_MSG(std::get<1>(statement)); + op_union->AddChild(pattern_graph_root_[cur_pattern_graph_]->children[0]); + } + } return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } @@ -931,6 +973,21 @@ std::any ExecutionPlanMaker::visit(geax::frontend::PrimitiveResultStatement* nod } std::vector> arith_items; auto& pattern_graph = pattern_graphs_[cur_pattern_graph_]; + if (!should_connect_[cur_pattern_graph_]) { + std::vector last_ret, now_ret; + for (auto &col : result_info_.header.colums) { + last_ret.push_back(col.alias); + } + for (auto& item : items) { + now_ret.push_back(std::get<0>(item)); + } + std::sort(last_ret.begin(), last_ret.end()); + std::sort(now_ret.begin(), now_ret.end()); + if (!CheckReturnElements(last_ret, now_ret)) { + throw lgraph::CypherException( + "All sub queries in an UNION must have the same column names."); + } + } result_info_.header.colums.clear(); for (auto& item : items) { ArithExprNode ae(std::get<1>(item), pattern_graph.symbol_table); @@ -1103,7 +1160,20 @@ std::any ExecutionPlanMaker::visit(geax::frontend::DeleteStatement* node) { return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } -std::any ExecutionPlanMaker::visit(geax::frontend::RemoveStatement* node) { NOT_SUPPORT(); } +std::any ExecutionPlanMaker::visit(geax::frontend::RemoveStatement* node) { + auto& pattern_graph = pattern_graphs_[cur_pattern_graph_]; + for (auto &item : node->items()) { + geax::frontend::RemoveSingleProperty *remove; + checkedCast(item, remove); + if (pattern_graphs_[cur_pattern_graph_].symbol_table.symbols.find(remove->v()) == + pattern_graphs_[cur_pattern_graph_].symbol_table.symbols.end()) { + THROW_CODE(InputError, "Variable `{}` not defined", remove->v()); + } + } + auto op = new OpGqlRemove(node->items(), &pattern_graph); + _UpdateStreamRoot(op, pattern_graph_root_[cur_pattern_graph_]); + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} std::any ExecutionPlanMaker::visit(geax::frontend::MergeStatement* node) { auto& pattern_graph = pattern_graphs_[cur_pattern_graph_]; @@ -1158,4 +1228,9 @@ std::any ExecutionPlanMaker::visit(geax::frontend::DummyNode* node) { NOT_SUPPOR std::any ExecutionPlanMaker::reportError() { return error_msg_; } +std::any ExecutionPlanMaker::visit(geax::frontend::RemoveSingleProperty* node) { + return std::any(); +} +std::any ExecutionPlanMaker::visit(geax::frontend::ListComprehension* node) { return std::any(); } + } // namespace cypher diff --git a/src/cypher/execution_plan/execution_plan_maker.h b/src/cypher/execution_plan/execution_plan_maker.h index 9a729b0096..9041f18fcf 100644 --- a/src/cypher/execution_plan/execution_plan_maker.h +++ b/src/cypher/execution_plan/execution_plan_maker.h @@ -39,6 +39,7 @@ class ExecutionPlanMaker : public geax::frontend::AstNodeVisitor { std::string error_msg_; std::vector pattern_graph_root_; + std::vector should_connect_; std::vector& pattern_graphs_; size_t cur_pattern_graph_; size_t pattern_graph_size_; @@ -103,6 +104,7 @@ class ExecutionPlanMaker : public geax::frontend::AstNodeVisitor { std::any visit(geax::frontend::ResetTimeZone* node) override; std::any visit(geax::frontend::ResetGraph* node) override; std::any visit(geax::frontend::ResetParam* node) override; + std::any visit(geax::frontend::RemoveSingleProperty* node) override; //--------------------------------------------------------------------------------- // exprs @@ -167,6 +169,7 @@ class ExecutionPlanMaker : public geax::frontend::AstNodeVisitor { std::any visit(geax::frontend::VNone* node) override; std::any visit(geax::frontend::Ref* node) override; std::any visit(geax::frontend::Param* node) override; + std::any visit(geax::frontend::ListComprehension* node) override; // predicates std::any visit(geax::frontend::IsNull* node) override; diff --git a/src/cypher/execution_plan/ops/op.h b/src/cypher/execution_plan/ops/op.h index e67c4ff55e..7df71f3924 100644 --- a/src/cypher/execution_plan/ops/op.h +++ b/src/cypher/execution_plan/ops/op.h @@ -76,7 +76,8 @@ enum OpType { GQL_DELETE, GQL_UPDATE, GQL_INQUERY_CALL, - GQL_MERGE + GQL_MERGE, + GQL_REMOVE }; struct OpStats { diff --git a/src/cypher/execution_plan/ops/op_gql_remove.cpp b/src/cypher/execution_plan/ops/op_gql_remove.cpp new file mode 100644 index 0000000000..102d419434 --- /dev/null +++ b/src/cypher/execution_plan/ops/op_gql_remove.cpp @@ -0,0 +1,19 @@ +/** +* Copyright 2022 AntGroup CO., Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +*/ + +// +// Created by lpp on 24-6-19. +// + +#include "cypher/execution_plan/ops/op_gql_remove.h" diff --git a/src/cypher/execution_plan/ops/op_gql_remove.h b/src/cypher/execution_plan/ops/op_gql_remove.h new file mode 100644 index 0000000000..06c6e5872a --- /dev/null +++ b/src/cypher/execution_plan/ops/op_gql_remove.h @@ -0,0 +1,82 @@ +/** +* Copyright 2022 AntGroup CO., Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +*/ + +#pragma once + +#include "parser/clause.h" +#include "cypher/execution_plan/ops/op.h" +#include "cypher/utils/geax_util.h" +#include "geax-front-end/ast/clause/RemoveItem.h" +#include "geax-front-end/ast/clause/RemoveSingleProperty.h" + +namespace cypher { + +class OpGqlRemove : public OpBase { + const std::vector& remove_items_; + + public: + OpGqlRemove(const std::vector& remove_items, + PatternGraph *pattern_graph) + : OpBase(OpType::GQL_REMOVE, "GqlRemove") + , remove_items_(remove_items) { + state = StreamUnInitialized; + } + + OpResult Initialize(RTContext *ctx) override { + CYPHER_THROW_ASSERT(children.size() == 1); + auto res = children[0]->Initialize(ctx); + record = children[0]->record; + state = StreamUnInitialized; + return res; + } + + OpResult RealConsume(RTContext *ctx) override { + if (children[0]->Consume(ctx) != OP_OK) return OP_DEPLETED; + for (auto &item : remove_items_) { + if (typeid(*item) != typeid(geax::frontend::RemoveSingleProperty)) + CYPHER_TODO(); + geax::frontend::RemoveSingleProperty* props = nullptr; + checkedCast(item, props); + auto &var = props->v(); + auto &key = props->property(); + auto it = record->symbol_table->symbols.find(var); + if (it == record->symbol_table->symbols.end()) CYPHER_TODO(); + auto &entry = record->values[it->second.id]; + if (entry.type != Entry::NODE) CYPHER_TODO(); + ctx->txn_->GetTxn()->SetVertexProperty( + entry.node->PullVid(), std::vector{key}, + std::vector{lgraph::FieldData()}); + } + return OP_OK; + } + + OpResult ResetImpl(bool complete) override { + state = StreamUnInitialized; + return OP_OK; + } + + std::string ToString() const override { + std::string str(name); + str.append(" ["); + for (auto &m : modifies) str.append(m).append(","); + if (!modifies.empty()) str.pop_back(); + str.append("]"); + return str; + } + + CYPHER_DEFINE_VISITABLE() + + CYPHER_DEFINE_CONST_VISITABLE() +}; +} // namespace cypher diff --git a/src/cypher/execution_plan/ops/op_project.h b/src/cypher/execution_plan/ops/op_project.h index 8f3eee8694..1ca2768ef1 100644 --- a/src/cypher/execution_plan/ops/op_project.h +++ b/src/cypher/execution_plan/ops/op_project.h @@ -101,7 +101,15 @@ class Project : public OpBase { // on the second call. if (single_response_) return OP_DEPLETED; single_response_ = true; - r = std::make_shared(); // fake empty record. + // QUERY: RETURN [x IN range(0,10) | x^3] AS result + // List comprehension is divided into three parts: + // x (reference), range(0,10) (range), and x^3 (expression). + // When calculating x^3, we need to get the value of x. + // The symbol of x is stored in the symbol table, + // and its value is stored in the Project op. + // Therefore, we need to change r from an empty record to a record + // of the same size as the symbol table. + r = std::make_shared(sym_tab_.symbols.size()); } if (res != OP_OK) return res; int re_idx = 0; diff --git a/src/cypher/execution_plan/ops/ops.h b/src/cypher/execution_plan/ops/ops.h index 0783461db4..8f6e869f58 100644 --- a/src/cypher/execution_plan/ops/ops.h +++ b/src/cypher/execution_plan/ops/ops.h @@ -25,6 +25,7 @@ #include "cypher/execution_plan/ops/op_expand_all.h" #include "cypher/execution_plan/ops/op_create.h" #include "cypher/execution_plan/ops/op_gql_create.h" +#include "cypher/execution_plan/ops/op_gql_remove.h" #include "cypher/execution_plan/ops/op_standalone_call.h" #include "cypher/execution_plan/ops/op_gql_standalone_call.h" #include "cypher/execution_plan/ops/op_var_len_expand.h" diff --git a/src/cypher/execution_plan/pattern_graph_maker.cpp b/src/cypher/execution_plan/pattern_graph_maker.cpp index 595a70aa10..5db1aea942 100644 --- a/src/cypher/execution_plan/pattern_graph_maker.cpp +++ b/src/cypher/execution_plan/pattern_graph_maker.cpp @@ -763,7 +763,7 @@ std::any PatternGraphMaker::visit(geax::frontend::VNone* node) { std::any PatternGraphMaker::visit(geax::frontend::Ref* node) { auto& symbols = pattern_graphs_[cur_pattern_graph_].symbol_table.symbols; if (symbols.find(node->name()) == symbols.end()) { - throw lgraph::CypherException("Unknown variable: " + node->name()); + THROW_CODE(CypherException, "Unknown variable: " + node->name()); } return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } @@ -845,8 +845,30 @@ std::any PatternGraphMaker::visit(geax::frontend::SessionSet* node) { NOT_SUPPOR std::any PatternGraphMaker::visit(geax::frontend::SessionReset* node) { NOT_SUPPORT(); } std::any PatternGraphMaker::visit(geax::frontend::ProcedureBody* node) { - pattern_graphs_.resize(node->statements().size()); - symbols_idx_.resize(node->statements().size(), 0); + size_t pattern_graphs_size = 0; + for (auto i : node->statements()) { + auto statement = i->statement(); + pattern_graphs_size += 1; + if (statement->type() == geax::frontend::AstNodeType::kQueryStatement) { + geax::frontend::QueryStatement *queryStatement = + (geax::frontend::QueryStatement*)statement; + // not support join query, but support union query + // in CompositeQueryStatement + int union_size = queryStatement->joinQuery()->head()->body().size(); + pattern_graphs_size += union_size; + if (union_size) { + for (int j = 0; j <= union_size; ++j) { + pattern_graph_in_union_.push_back(true); + } + } else { + pattern_graph_in_union_.push_back(false); + } + } else { + pattern_graph_in_union_.push_back(false); + } + } + pattern_graphs_.resize(pattern_graphs_size); + symbols_idx_.resize(pattern_graphs_size, 0); for (auto stmt : node->statements()) { cur_pattern_graph_ += 1; ACCEPT_AND_CHECK_WITH_ERROR_MSG(stmt); @@ -922,6 +944,10 @@ std::any PatternGraphMaker::visit(geax::frontend::JoinRightPart* node) { NOT_SUP std::any PatternGraphMaker::visit(geax::frontend::CompositeQueryStatement* node) { auto head = node->head(); ACCEPT_AND_CHECK_WITH_ERROR_MSG(head); + for (auto statement : node->body()) { + cur_pattern_graph_ += 1; + ACCEPT_AND_CHECK_WITH_ERROR_MSG(std::get<1>(statement)); + } return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } @@ -1009,12 +1035,14 @@ std::any PatternGraphMaker::visit(geax::frontend::PrimitiveResultStatement* node alias, SymbolNode(symbols_idx_[cur_pattern_graph_]++, symbol_type, SymbolNode::LOCAL)); } - if (cur_pattern_graph_ < pattern_graphs_.size() - 1 && - pattern_graphs_[cur_pattern_graph_ + 1].symbol_table.symbols.find(alias) == - pattern_graphs_[cur_pattern_graph_ + 1].symbol_table.symbols.end()) { - pattern_graphs_[cur_pattern_graph_ + 1].symbol_table.symbols.emplace( - alias, SymbolNode(symbols_idx_[cur_pattern_graph_ + 1]++, symbol_type, - SymbolNode::ARGUMENT)); + if (!pattern_graph_in_union_[cur_pattern_graph_]) { + if (cur_pattern_graph_ < pattern_graphs_.size() - 1 && + pattern_graphs_[cur_pattern_graph_ + 1].symbol_table.symbols.find(alias) == + pattern_graphs_[cur_pattern_graph_ + 1].symbol_table.symbols.end()) { + pattern_graphs_[cur_pattern_graph_ + 1].symbol_table.symbols.emplace( + alias, SymbolNode(symbols_idx_[cur_pattern_graph_ + 1]++, symbol_type, + SymbolNode::ARGUMENT)); + } } } return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; @@ -1068,7 +1096,9 @@ std::any PatternGraphMaker::visit(geax::frontend::DeleteStatement* node) { return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } -std::any PatternGraphMaker::visit(geax::frontend::RemoveStatement* node) { NOT_SUPPORT(); } +std::any PatternGraphMaker::visit(geax::frontend::RemoveStatement* node) { + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} std::any PatternGraphMaker::visit(geax::frontend::MergeStatement* node) { ClauseGuard cg(node->type(), cur_types_); @@ -1151,4 +1181,15 @@ void PatternGraphMaker::AddRelationship(Relationship* rel) { pattern_graph.AddRelationship(rel); } +std::any PatternGraphMaker::visit(geax::frontend::RemoveSingleProperty* node) { NOT_SUPPORT(); } +std::any PatternGraphMaker::visit(geax::frontend::ListComprehension* node) { + geax::frontend::Ref *ref = nullptr; + checkedCast(node->getVariable(), ref); + AddSymbol(ref->name(), cypher::SymbolNode::CONSTANT, cypher::SymbolNode::LOCAL); + ACCEPT_AND_CHECK_WITH_ERROR_MSG(node->getVariable()); + ACCEPT_AND_CHECK_WITH_ERROR_MSG(node->getInExpression()); + ACCEPT_AND_CHECK_WITH_ERROR_MSG(node->getOpExpression()); + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} + } // namespace cypher diff --git a/src/cypher/execution_plan/pattern_graph_maker.h b/src/cypher/execution_plan/pattern_graph_maker.h index bdd1c19f12..f5544cd586 100644 --- a/src/cypher/execution_plan/pattern_graph_maker.h +++ b/src/cypher/execution_plan/pattern_graph_maker.h @@ -66,6 +66,7 @@ class PatternGraphMaker : public geax::frontend::AstNodeVisitor { std::any visit(geax::frontend::ResetTimeZone* node) override; std::any visit(geax::frontend::ResetGraph* node) override; std::any visit(geax::frontend::ResetParam* node) override; + std::any visit(geax::frontend::RemoveSingleProperty* node) override; //--------------------------------------------------------------------------------- // exprs @@ -130,6 +131,7 @@ class PatternGraphMaker : public geax::frontend::AstNodeVisitor { std::any visit(geax::frontend::VNone* node) override; std::any visit(geax::frontend::Ref* node) override; std::any visit(geax::frontend::Param* node) override; + std::any visit(geax::frontend::ListComprehension* node) override; // predicates std::any visit(geax::frontend::IsNull* node) override; @@ -206,6 +208,7 @@ class PatternGraphMaker : public geax::frontend::AstNodeVisitor { void AddNode(Node* node); void AddRelationship(Relationship* rel); std::vector& pattern_graphs_; + std::vector pattern_graph_in_union_; std::vector symbols_idx_; size_t cur_pattern_graph_; std::unordered_set cur_types_; diff --git a/src/cypher/parser/cypher_base_visitor_v2.cpp b/src/cypher/parser/cypher_base_visitor_v2.cpp index 7939b7e16d..4a984832f5 100644 --- a/src/cypher/parser/cypher_base_visitor_v2.cpp +++ b/src/cypher/parser/cypher_base_visitor_v2.cpp @@ -134,15 +134,12 @@ std::string CypherBaseVisitorV2::GenAnonymousAlias(bool is_node) { std::any CypherBaseVisitorV2::visitOC_Statement(LcypherParser::OC_StatementContext *ctx) { geax::frontend::NormalTransaction *node = nullptr; checkedCast(node_, node); - if (ctx->EXPLAIN()) { - NOT_SUPPORT_AND_THROW(); - } else if (ctx->PROFILE()) { - NOT_SUPPORT_AND_THROW(); - } else { - auto body = ALLOC_GEAOBJECT(geax::frontend::ProcedureBody); - node->setProcedureBody(body); - SWITCH_CONTEXT_VISIT_CHILDREN(ctx, body); - } + cmd_type_ = ctx->EXPLAIN() ? parser::CmdType::EXPLAIN + : ctx->PROFILE() ? parser::CmdType::PROFILE + : parser::CmdType::QUERY; + auto body = ALLOC_GEAOBJECT(geax::frontend::ProcedureBody); + node->setProcedureBody(body); + SWITCH_CONTEXT_VISIT_CHILDREN(ctx, body); return 0; } @@ -157,15 +154,20 @@ std::any CypherBaseVisitorV2::visitOC_Query(LcypherParser::OC_QueryContext *ctx) } std::any CypherBaseVisitorV2::visitOC_RegularQuery(LcypherParser::OC_RegularQueryContext *ctx) { - visit(ctx->oC_SingleQuery()); - for (auto token : ctx->oC_Union()) { - visit(token); + geax::frontend::ProcedureBody *body = nullptr; + checkedCast(node_, body); + SWITCH_CONTEXT_VISIT(ctx->oC_SingleQuery(), body); + if (!ctx->oC_Union().empty()) { + VisitGuard guard(VisitType::kUnionClause, visit_types_); + for (auto token : ctx->oC_Union()) { + SWITCH_CONTEXT_VISIT(token, body); + } } return 0; } std::any CypherBaseVisitorV2::visitOC_Union(LcypherParser::OC_UnionContext *ctx) { - NOT_SUPPORT_AND_THROW(); + return visitChildren(ctx); } std::any CypherBaseVisitorV2::visitOC_SingleQuery(LcypherParser::OC_SingleQueryContext *ctx) { @@ -181,18 +183,25 @@ std::any CypherBaseVisitorV2::visitOC_SinglePartQuery( if (ctx->oC_ReadingClause().size() > 2) NOT_SUPPORT_AND_THROW(); geax::frontend::ProcedureBody *body = nullptr; checkedCast(node_, body); - auto node = ALLOC_GEAOBJECT(geax::frontend::StatementWithYield); - body->appendStatement(node); + geax::frontend::StatementWithYield* node; if (ctx->oC_UpdatingClause().empty()) { VisitGuard guard(VisitType::kReadingClause, visit_types_); - auto stmt = ALLOC_GEAOBJECT(geax::frontend::QueryStatement); - node->setStatement(stmt); - auto join = ALLOC_GEAOBJECT(geax::frontend::JoinQueryExpression); - stmt->setJoinQuery(join); - auto co = ALLOC_GEAOBJECT(geax::frontend::CompositeQueryStatement); - join->setHead(co); auto l = ALLOC_GEAOBJECT(geax::frontend::AmbientLinearQueryStatement); - co->setHead(l); + if (!VisitGuard::InClause(VisitType::kUnionClause, visit_types_)) { + node = ALLOC_GEAOBJECT(geax::frontend::StatementWithYield); + body->appendStatement(node); + auto stmt = ALLOC_GEAOBJECT(geax::frontend::QueryStatement); + node->setStatement(stmt); + auto join = ALLOC_GEAOBJECT(geax::frontend::JoinQueryExpression); + stmt->setJoinQuery(join); + auto co = ALLOC_GEAOBJECT(geax::frontend::CompositeQueryStatement); + join->setHead(co); + co->setHead(l); + } else { + node = body->statements().back(); + auto stmt = (geax::frontend::QueryStatement*)node->statement(); + stmt->joinQuery()->head()->appendBody(ALLOC_GEAOBJECT(geax::frontend::Union), l); + } SWITCH_CONTEXT_VISIT_CHILDREN(ctx, l); if (VisitGuard::InClause(VisitType::kSinglePartQuery, visit_types_) && filter_in_with_clause_) { @@ -200,6 +209,8 @@ std::any CypherBaseVisitorV2::visitOC_SinglePartQuery( } } else { VisitGuard guard(VisitType::kUpdatingClause, visit_types_); + node = ALLOC_GEAOBJECT(geax::frontend::StatementWithYield); + body->appendStatement(node); auto stmt = ALLOC_GEAOBJECT(geax::frontend::LinearDataModifyingStatement); node->setStatement(stmt); SWITCH_CONTEXT_VISIT_CHILDREN(ctx, stmt); @@ -456,12 +467,39 @@ std::any CypherBaseVisitorV2::visitOC_Delete(LcypherParser::OC_DeleteContext *ct } std::any CypherBaseVisitorV2::visitOC_Remove(LcypherParser::OC_RemoveContext *ctx) { - NOT_SUPPORT_AND_THROW(); + VisitGuard::InClause(VisitType::kUpdatingClause, visit_types_); + geax::frontend::LinearDataModifyingStatement *node = nullptr; + checkedCast(node_, node); + for (auto &item : ctx->oC_RemoveItem()) { + auto stmt = ALLOC_GEAOBJECT(geax::frontend::RemoveStatement); + node->appendModifyStatement(stmt); + SWITCH_CONTEXT_VISIT(item, stmt); + } return 0; } std::any CypherBaseVisitorV2::visitOC_RemoveItem(LcypherParser::OC_RemoveItemContext *ctx) { - NOT_SUPPORT_AND_THROW(); + geax::frontend::RemoveStatement *node = nullptr; + checkedCast(node_, node); + if (ctx->oC_PropertyExpression()) { + geax::frontend::Expr *name_expr = nullptr, *property_expr = nullptr; + auto pe_ctx = ctx->oC_PropertyExpression(); + checkedAnyCast(visit(ctx->oC_PropertyExpression()->oC_Atom()), name_expr); + if (pe_ctx->oC_PropertyLookup().empty()) + CYPHER_TODO(); + checkedAnyCast(visit(pe_ctx->oC_PropertyLookup(0)), property_expr); + geax::frontend::Ref *vstr = nullptr; + geax::frontend::VString *pstr = nullptr; + checkedCast(name_expr, vstr); + checkedCast(property_expr, pstr); + std::string variable = vstr->name(), property = pstr->val(); + auto remove = ALLOC_GEAOBJECT(geax::frontend::RemoveSingleProperty); + node->appendItem(remove); + remove->setV(std::move(variable)); + remove->setProperty(std::move(property)); + } else { + CYPHER_TODO(); + } return 0; } @@ -1404,7 +1442,13 @@ std::any CypherBaseVisitorV2::visitOC_Atom(LcypherParser::OC_AtomContext *ctx) { checkedCast(name_expr, vstr); std::string name = vstr->val(); auto expr = ALLOC_GEAOBJECT(geax::frontend::Ref); - expr->setName(std::move(name)); + if (list_comprehension_depth == 0 || list_comprehension_anonymous_symbols_.find(name) == + list_comprehension_anonymous_symbols_.end()) { + expr->setName(std::move(name)); + } else { + auto list_comprehension_name = list_comprehension_anonymous_symbols_[name].top(); + expr->setName(std::move(list_comprehension_name)); + } return (geax::frontend::Expr *)expr; } else if (ctx->oC_Literal()) { return visit(ctx->oC_Literal()); @@ -1536,12 +1580,26 @@ std::any CypherBaseVisitorV2::visitOC_RelationshipsPattern( std::any CypherBaseVisitorV2::visitOC_FilterExpression( LcypherParser::OC_FilterExpressionContext *ctx) { - NOT_SUPPORT_AND_THROW(); - return 0; + return visitChildren(ctx); } std::any CypherBaseVisitorV2::visitOC_IdInColl(LcypherParser::OC_IdInCollContext *ctx) { - NOT_SUPPORT_AND_THROW(); + geax::frontend::ListComprehension *listComprehension = nullptr; + checkedCast(node_, listComprehension); + auto var = ctx->oC_Variable()->getText(); + if (list_comprehension_anonymous_symbols_.find(var) == + list_comprehension_anonymous_symbols_.end()) { + list_comprehension_anonymous_symbols_[var] = std::stack(); + } + auto anonymous_var = GenerateListComprehension(var); + list_comprehension_anonymous_symbols_[var].push(anonymous_var); + var = anonymous_var; + auto variable_ref = ALLOC_GEAOBJECT(geax::frontend::Ref); + variable_ref->setName(std::move(var)); + listComprehension->setVariable(variable_ref); + geax::frontend::Expr *in_expr; + checkedAnyCast(visit(ctx->oC_Expression()), in_expr); + listComprehension->setInExpression(in_expr); return 0; } @@ -1684,8 +1742,20 @@ std::any CypherBaseVisitorV2::visitOC_Namespace(LcypherParser::OC_NamespaceConte std::any CypherBaseVisitorV2::visitOC_ListComprehension( LcypherParser::OC_ListComprehensionContext *ctx) { - NOT_SUPPORT_AND_THROW(); - return 0; + list_comprehension_depth++; + auto listComprehension = ALLOC_GEAOBJECT(geax::frontend::ListComprehension); + SWITCH_CONTEXT_VISIT(ctx->oC_FilterExpression(), listComprehension); + geax::frontend::Expr *op_expr; + if (ctx->oC_Expression()) { + checkedAnyCast(visit(ctx->oC_Expression()), op_expr); + } else { + CYPHER_TODO(); + } + listComprehension->setOpExpression(op_expr); + list_comprehension_depth--; + list_comprehension_anonymous_symbols_[ctx->oC_FilterExpression()-> + oC_IdInColl()->oC_Variable()->getText()].pop(); + return (geax::frontend::Expr*)listComprehension; } std::any CypherBaseVisitorV2::visitOC_PatternComprehension( diff --git a/src/cypher/parser/cypher_base_visitor_v2.h b/src/cypher/parser/cypher_base_visitor_v2.h index 58416b1ad5..fcee525ff9 100644 --- a/src/cypher/parser/cypher_base_visitor_v2.h +++ b/src/cypher/parser/cypher_base_visitor_v2.h @@ -21,6 +21,7 @@ #include "cypher/cypher_exception.h" #include "geax-front-end/common/ObjectAllocator.h" #include "geax-front-end/ast/Ast.h" +#include "cypher/parser/data_typedef.h" #if __APPLE__ #ifdef TRUE @@ -48,7 +49,8 @@ enum class VisitType { kWithClause, kStandaloneCall, kInQueryCall, - kSinglePartQuery + kSinglePartQuery, + kUnionClause }; class VisitGuard { @@ -81,10 +83,20 @@ class CypherBaseVisitorV2 : public LcypherVisitor { static const std::unordered_map S_BAGG_LIST; geax::frontend::PathChain* path_chain_; geax::frontend::FilterStatement* filter_in_with_clause_; + parser::CmdType cmd_type_; + std::unordered_map> list_comprehension_anonymous_symbols_; + std::unordered_map list_comprehension_anonymous_idx_; + int32_t list_comprehension_depth {0}; + + std::string GenerateListComprehension(const std::string &x) { + return std::string("LIST_COMPREHENSION@") + x + "@" + + std::to_string(list_comprehension_anonymous_idx_[x]++); + } public: CypherBaseVisitorV2() = delete; + parser::CmdType CommandType() const { return cmd_type_; } CypherBaseVisitorV2(geax::common::ObjectArenaAllocator& objAlloc, antlr4::tree::ParseTree *tree); diff --git a/src/cypher/utils/ast_node_visitor_impl.h b/src/cypher/utils/ast_node_visitor_impl.h index c12b153301..d183d529d2 100644 --- a/src/cypher/utils/ast_node_visitor_impl.h +++ b/src/cypher/utils/ast_node_visitor_impl.h @@ -760,6 +760,10 @@ class AstNodeVisitorImpl : public geax::frontend::AstNodeVisitor { return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } + std::any visit(geax::frontend::RemoveSingleProperty* node) override { + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; + } + std::any visit(geax::frontend::SetSchemaClause* node) override { ACCEPT_AND_CHECK_WITH_ERROR_MSG(node->initExpr()); return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; @@ -938,6 +942,10 @@ class AstNodeVisitorImpl : public geax::frontend::AstNodeVisitor { return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } + std::any visit(geax::frontend::ListComprehension* node) override { + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; + } + protected: std::string error_msg_; }; diff --git a/src/cypher/utils/geax_expr_util.h b/src/cypher/utils/geax_expr_util.h index a6ec5dc14b..14be4c2a7a 100644 --- a/src/cypher/utils/geax_expr_util.h +++ b/src/cypher/utils/geax_expr_util.h @@ -125,17 +125,33 @@ class AstExprToString : public geax::frontend::AstExprNodeVisitorImpl { const auto& exprs = node->elems(); for (size_t idx = 0; idx < exprs.size(); ++idx) { std::string temp; + checkedAnyCast(exprs[idx]->accept(*this), temp); + res += temp; if (idx != exprs.size() - 1) { - checkedAnyCast(exprs[idx]->accept(*this), temp); - res += temp; res += ", "; + } else { + res += "}"; } + } + return res; + } + std::any visit(geax::frontend::ListComprehension* node) override { + std::string res("{"); + const auto & exprs = std::vector{ node->getVariable(), + node->getInExpression(), node->getOpExpression()}; + for (size_t idx = 0; idx < exprs.size(); ++idx) { + std::string temp; checkedAnyCast(exprs[idx]->accept(*this), temp); res += temp; - res += "}"; + if (idx != exprs.size() - 1) { + res += ", "; + } else { + res += "}"; + } } return res; } + std::any visit(geax::frontend::MkMap* node) override { NOT_SUPPORT_AND_THROW(); } std::any visit(geax::frontend::MkRecord* node) override { NOT_SUPPORT_AND_THROW(); } std::any visit(geax::frontend::MkSet* node) override { NOT_SUPPORT_AND_THROW(); } diff --git a/test/resource/unit_test/create_yago/cypher/create_yago.result b/test/resource/unit_test/create_yago/cypher/create_yago.result index a056eb8c9a..df3e08da6d 100644 --- a/test/resource/unit_test/create_yago/cypher/create_yago.result +++ b/test/resource/unit_test/create_yago/cypher/create_yago.result @@ -16,5 +16,5 @@ CALL db.createEdgeLabel('WROTE_MUSIC_FOR', '[]'); [] CALL db.createEdgeLabel('ACTED_IN', '[]', 'charactername', 'STRING', false); [] - CREATE (rachel:Person:Actor {name: 'Rachel Kempson', birthyear: 1910}) CREATE (michael:Person:Actor {name: 'Michael Redgrave', birthyear: 1908}) CREATE (vanessa:Person:Actor {name: 'Vanessa Redgrave', birthyear: 1937}) CREATE (corin:Person:Actor {name: 'Corin Redgrave', birthyear: 1939}) CREATE (liam:Person:Actor {name: 'Liam Neeson', birthyear: 1952}) CREATE (natasha:Person:Actor {name: 'Natasha Richardson', birthyear: 1963}) CREATE (richard:Person:Actor {name: 'Richard Harris', birthyear: 1930}) CREATE (dennis:Person:Actor {name: 'Dennis Quaid', birthyear: 1954}) CREATE (lindsay:Person:Actor {name: 'Lindsay Lohan', birthyear: 1986}) CREATE (jemma:Person:Actor {name: 'Jemma Redgrave', birthyear: 1965}) CREATE (roy:Person:Actor {name: 'Roy Redgrave', birthyear: 1873}) CREATE (john:Person {name: 'John Williams', birthyear: 1932}) CREATE (christopher:Person {name: 'Christopher Nolan', birthyear: 1970}) CREATE (newyork:City {name: 'New York'}) CREATE (london:City {name: 'London'}) CREATE (houston:City {name: 'Houston'}) CREATE (mrchips:Film {title: 'Goodbye, Mr. Chips'}) CREATE (batmanbegins:Film {title: 'Batman Begins'}) CREATE (harrypotter:Film {title: 'Harry Potter and the Sorcerer\'s Stone'}) CREATE (parent:Film {title: 'The Parent Trap'}) CREATE (camelot:Film {title: 'Camelot'}) CREATE (rachel)-[:HAS_CHILD]->(vanessa), (rachel)-[:HAS_CHILD]->(corin), (michael)-[:HAS_CHILD]->(vanessa), (michael)-[:HAS_CHILD]->(corin), (corin)-[:HAS_CHILD]->(jemma), (vanessa)-[:HAS_CHILD]->(natasha), (roy)-[:HAS_CHILD]->(michael), (rachel)-[:MARRIED]->(michael), (michael)-[:MARRIED]->(rachel), (natasha)-[:MARRIED]->(liam), (liam)-[:MARRIED]->(natasha), (vanessa)-[:BORN_IN]->(london), (natasha)-[:BORN_IN]->(london), (christopher)-[:BORN_IN]->(london), (dennis)-[:BORN_IN]->(houston), (lindsay)-[:BORN_IN]->(newyork), (john)-[:BORN_IN]->(newyork), (christopher)-[:DIRECTED]->(batmanbegins), (john)-[:WROTE_MUSIC_FOR]->(harrypotter), (john)-[:WROTE_MUSIC_FOR]->(mrchips), (michael)-[:ACTED_IN {charactername: 'The Headmaster'}]->(mrchips), (vanessa)-[:ACTED_IN {charactername: 'Guenevere'}]->(camelot), (richard)-[:ACTED_IN {charactername: 'King Arthur'}]->(camelot), (richard)-[:ACTED_IN {charactername: 'Albus Dumbledore'}]->(harrypotter), (natasha)-[:ACTED_IN {charactername: 'Liz James'}]->(parent), (dennis)-[:ACTED_IN {charactername: 'Nick Parker'}]->(parent),(lindsay)-[:ACTED_IN {charactername: 'Halle/Annie'}]->(parent), (liam)-[:ACTED_IN {charactername: 'Henri Ducard'}]->(batmanbegins) ; +CREATE (rachel:Person:Actor {name: 'Rachel Kempson', birthyear: 1910}) CREATE (michael:Person:Actor {name: 'Michael Redgrave', birthyear: 1908}) CREATE (vanessa:Person:Actor {name: 'Vanessa Redgrave', birthyear: 1937}) CREATE (corin:Person:Actor {name: 'Corin Redgrave', birthyear: 1939}) CREATE (liam:Person:Actor {name: 'Liam Neeson', birthyear: 1952}) CREATE (natasha:Person:Actor {name: 'Natasha Richardson', birthyear: 1963}) CREATE (richard:Person:Actor {name: 'Richard Harris', birthyear: 1930}) CREATE (dennis:Person:Actor {name: 'Dennis Quaid', birthyear: 1954}) CREATE (lindsay:Person:Actor {name: 'Lindsay Lohan', birthyear: 1986}) CREATE (jemma:Person:Actor {name: 'Jemma Redgrave', birthyear: 1965}) CREATE (roy:Person:Actor {name: 'Roy Redgrave', birthyear: 1873}) CREATE (john:Person {name: 'John Williams', birthyear: 1932}) CREATE (christopher:Person {name: 'Christopher Nolan', birthyear: 1970}) CREATE (newyork:City {name: 'New York'}) CREATE (london:City {name: 'London'}) CREATE (houston:City {name: 'Houston'}) CREATE (mrchips:Film {title: 'Goodbye, Mr. Chips'}) CREATE (batmanbegins:Film {title: 'Batman Begins'}) CREATE (harrypotter:Film {title: 'Harry Potter and the Sorcerer\'s Stone'}) CREATE (parent:Film {title: 'The Parent Trap'}) CREATE (camelot:Film {title: 'Camelot'}) CREATE (rachel)-[:HAS_CHILD]->(vanessa), (rachel)-[:HAS_CHILD]->(corin), (michael)-[:HAS_CHILD]->(vanessa), (michael)-[:HAS_CHILD]->(corin), (corin)-[:HAS_CHILD]->(jemma), (vanessa)-[:HAS_CHILD]->(natasha), (roy)-[:HAS_CHILD]->(michael), (rachel)-[:MARRIED]->(michael), (michael)-[:MARRIED]->(rachel), (natasha)-[:MARRIED]->(liam), (liam)-[:MARRIED]->(natasha), (vanessa)-[:BORN_IN]->(london), (natasha)-[:BORN_IN]->(london), (christopher)-[:BORN_IN]->(london), (dennis)-[:BORN_IN]->(houston), (lindsay)-[:BORN_IN]->(newyork), (john)-[:BORN_IN]->(newyork), (christopher)-[:DIRECTED]->(batmanbegins), (john)-[:WROTE_MUSIC_FOR]->(harrypotter), (john)-[:WROTE_MUSIC_FOR]->(mrchips), (michael)-[:ACTED_IN {charactername: 'The Headmaster'}]->(mrchips), (vanessa)-[:ACTED_IN {charactername: 'Guenevere'}]->(camelot), (richard)-[:ACTED_IN {charactername: 'King Arthur'}]->(camelot), (richard)-[:ACTED_IN {charactername: 'Albus Dumbledore'}]->(harrypotter), (natasha)-[:ACTED_IN {charactername: 'Liz James'}]->(parent), (dennis)-[:ACTED_IN {charactername: 'Nick Parker'}]->(parent),(lindsay)-[:ACTED_IN {charactername: 'Halle/Annie'}]->(parent), (liam)-[:ACTED_IN {charactername: 'Henri Ducard'}]->(batmanbegins) ; [{"":"created 21 vertices, created 28 edges."}] diff --git a/test/resource/unit_test/create_yago/cypher/create_yago.test b/test/resource/unit_test/create_yago/cypher/create_yago.test index 698f2569a1..5ed6028a59 100644 --- a/test/resource/unit_test/create_yago/cypher/create_yago.test +++ b/test/resource/unit_test/create_yago/cypher/create_yago.test @@ -7,4 +7,4 @@ CALL db.createEdgeLabel('BORN_IN', '[]'); CALL db.createEdgeLabel('DIRECTED', '[]'); CALL db.createEdgeLabel('WROTE_MUSIC_FOR', '[]'); CALL db.createEdgeLabel('ACTED_IN', '[]', 'charactername', 'STRING', false); - CREATE (rachel:Person:Actor {name: 'Rachel Kempson', birthyear: 1910}) CREATE (michael:Person:Actor {name: 'Michael Redgrave', birthyear: 1908}) CREATE (vanessa:Person:Actor {name: 'Vanessa Redgrave', birthyear: 1937}) CREATE (corin:Person:Actor {name: 'Corin Redgrave', birthyear: 1939}) CREATE (liam:Person:Actor {name: 'Liam Neeson', birthyear: 1952}) CREATE (natasha:Person:Actor {name: 'Natasha Richardson', birthyear: 1963}) CREATE (richard:Person:Actor {name: 'Richard Harris', birthyear: 1930}) CREATE (dennis:Person:Actor {name: 'Dennis Quaid', birthyear: 1954}) CREATE (lindsay:Person:Actor {name: 'Lindsay Lohan', birthyear: 1986}) CREATE (jemma:Person:Actor {name: 'Jemma Redgrave', birthyear: 1965}) CREATE (roy:Person:Actor {name: 'Roy Redgrave', birthyear: 1873}) CREATE (john:Person {name: 'John Williams', birthyear: 1932}) CREATE (christopher:Person {name: 'Christopher Nolan', birthyear: 1970}) CREATE (newyork:City {name: 'New York'}) CREATE (london:City {name: 'London'}) CREATE (houston:City {name: 'Houston'}) CREATE (mrchips:Film {title: 'Goodbye, Mr. Chips'}) CREATE (batmanbegins:Film {title: 'Batman Begins'}) CREATE (harrypotter:Film {title: 'Harry Potter and the Sorcerer\'s Stone'}) CREATE (parent:Film {title: 'The Parent Trap'}) CREATE (camelot:Film {title: 'Camelot'}) CREATE (rachel)-[:HAS_CHILD]->(vanessa), (rachel)-[:HAS_CHILD]->(corin), (michael)-[:HAS_CHILD]->(vanessa), (michael)-[:HAS_CHILD]->(corin), (corin)-[:HAS_CHILD]->(jemma), (vanessa)-[:HAS_CHILD]->(natasha), (roy)-[:HAS_CHILD]->(michael), (rachel)-[:MARRIED]->(michael), (michael)-[:MARRIED]->(rachel), (natasha)-[:MARRIED]->(liam), (liam)-[:MARRIED]->(natasha), (vanessa)-[:BORN_IN]->(london), (natasha)-[:BORN_IN]->(london), (christopher)-[:BORN_IN]->(london), (dennis)-[:BORN_IN]->(houston), (lindsay)-[:BORN_IN]->(newyork), (john)-[:BORN_IN]->(newyork), (christopher)-[:DIRECTED]->(batmanbegins), (john)-[:WROTE_MUSIC_FOR]->(harrypotter), (john)-[:WROTE_MUSIC_FOR]->(mrchips), (michael)-[:ACTED_IN {charactername: 'The Headmaster'}]->(mrchips), (vanessa)-[:ACTED_IN {charactername: 'Guenevere'}]->(camelot), (richard)-[:ACTED_IN {charactername: 'King Arthur'}]->(camelot), (richard)-[:ACTED_IN {charactername: 'Albus Dumbledore'}]->(harrypotter), (natasha)-[:ACTED_IN {charactername: 'Liz James'}]->(parent), (dennis)-[:ACTED_IN {charactername: 'Nick Parker'}]->(parent),(lindsay)-[:ACTED_IN {charactername: 'Halle/Annie'}]->(parent), (liam)-[:ACTED_IN {charactername: 'Henri Ducard'}]->(batmanbegins) ; \ No newline at end of file +CREATE (rachel:Person:Actor {name: 'Rachel Kempson', birthyear: 1910}) CREATE (michael:Person:Actor {name: 'Michael Redgrave', birthyear: 1908}) CREATE (vanessa:Person:Actor {name: 'Vanessa Redgrave', birthyear: 1937}) CREATE (corin:Person:Actor {name: 'Corin Redgrave', birthyear: 1939}) CREATE (liam:Person:Actor {name: 'Liam Neeson', birthyear: 1952}) CREATE (natasha:Person:Actor {name: 'Natasha Richardson', birthyear: 1963}) CREATE (richard:Person:Actor {name: 'Richard Harris', birthyear: 1930}) CREATE (dennis:Person:Actor {name: 'Dennis Quaid', birthyear: 1954}) CREATE (lindsay:Person:Actor {name: 'Lindsay Lohan', birthyear: 1986}) CREATE (jemma:Person:Actor {name: 'Jemma Redgrave', birthyear: 1965}) CREATE (roy:Person:Actor {name: 'Roy Redgrave', birthyear: 1873}) CREATE (john:Person {name: 'John Williams', birthyear: 1932}) CREATE (christopher:Person {name: 'Christopher Nolan', birthyear: 1970}) CREATE (newyork:City {name: 'New York'}) CREATE (london:City {name: 'London'}) CREATE (houston:City {name: 'Houston'}) CREATE (mrchips:Film {title: 'Goodbye, Mr. Chips'}) CREATE (batmanbegins:Film {title: 'Batman Begins'}) CREATE (harrypotter:Film {title: 'Harry Potter and the Sorcerer\'s Stone'}) CREATE (parent:Film {title: 'The Parent Trap'}) CREATE (camelot:Film {title: 'Camelot'}) CREATE (rachel)-[:HAS_CHILD]->(vanessa), (rachel)-[:HAS_CHILD]->(corin), (michael)-[:HAS_CHILD]->(vanessa), (michael)-[:HAS_CHILD]->(corin), (corin)-[:HAS_CHILD]->(jemma), (vanessa)-[:HAS_CHILD]->(natasha), (roy)-[:HAS_CHILD]->(michael), (rachel)-[:MARRIED]->(michael), (michael)-[:MARRIED]->(rachel), (natasha)-[:MARRIED]->(liam), (liam)-[:MARRIED]->(natasha), (vanessa)-[:BORN_IN]->(london), (natasha)-[:BORN_IN]->(london), (christopher)-[:BORN_IN]->(london), (dennis)-[:BORN_IN]->(houston), (lindsay)-[:BORN_IN]->(newyork), (john)-[:BORN_IN]->(newyork), (christopher)-[:DIRECTED]->(batmanbegins), (john)-[:WROTE_MUSIC_FOR]->(harrypotter), (john)-[:WROTE_MUSIC_FOR]->(mrchips), (michael)-[:ACTED_IN {charactername: 'The Headmaster'}]->(mrchips), (vanessa)-[:ACTED_IN {charactername: 'Guenevere'}]->(camelot), (richard)-[:ACTED_IN {charactername: 'King Arthur'}]->(camelot), (richard)-[:ACTED_IN {charactername: 'Albus Dumbledore'}]->(harrypotter), (natasha)-[:ACTED_IN {charactername: 'Liz James'}]->(parent), (dennis)-[:ACTED_IN {charactername: 'Nick Parker'}]->(parent),(lindsay)-[:ACTED_IN {charactername: 'Halle/Annie'}]->(parent), (liam)-[:ACTED_IN {charactername: 'Henri Ducard'}]->(batmanbegins) ; \ No newline at end of file diff --git a/test/resource/unit_test/list_comprehension/cypher/list_comprehension.result b/test/resource/unit_test/list_comprehension/cypher/list_comprehension.result index e19e655a1e..0124fe6ac1 100644 --- a/test/resource/unit_test/list_comprehension/cypher/list_comprehension.result +++ b/test/resource/unit_test/list_comprehension/cypher/list_comprehension.result @@ -6,3 +6,15 @@ WITH [2,4,6] AS y RETURN [x IN y | x] AS result; [{"result":[2,4,6]}] WITH [2,4,6] AS y RETURN [x IN range(0, size(y)) | x] AS result; [{"result":[0,1,2,3]}] +RETURN [x in [x in [x in [1,2] |x+1] |x+1] | x+1+head([x in [1,2,3] | x^2])]; +[{"[x in [x in [x in [1,2] |x+1] |x+1] | x+1+head([x in [1,2,3] | x^2])]":[5.0,6.0]}] +RETURN [x in [1] | x + head([x in [2,3] | x+1]) + x ]; +[{"[x in [1] | x + head([x in [2,3] | x+1]) + x ]":[5]}] +RETURN [ _ in range(1,10) | x]; +[CypherException] Unknown variable: x +RETURN [x in [x in [x in [2,3,4] |x+1] |x+1] | x+1+head([x in [1,2,3] | x^2])+x]; +[{"[x in [x in [x in [2,3,4] |x+1] |x+1] | x+1+head([x in [1,2,3] | x^2])+x]":[10.0,12.0,14.0]}] +RETURN [x in [1] | x + head([x]) + x]; +[{"[x in [1] | x + head([x]) + x]":[3]}] +RETURN [x in [1] | x + head([x in [2] | x]) + x]; +[{"[x in [1] | x + head([x in [2] | x]) + x]":[4]}] diff --git a/test/resource/unit_test/list_comprehension/cypher/list_comprehension.test b/test/resource/unit_test/list_comprehension/cypher/list_comprehension.test index c68d7a244b..14ebddeebd 100644 --- a/test/resource/unit_test/list_comprehension/cypher/list_comprehension.test +++ b/test/resource/unit_test/list_comprehension/cypher/list_comprehension.test @@ -1,4 +1,10 @@ RETURN [x IN range(0,10) | x] AS result; RETURN [x IN range(0,10) | x^3] AS result; WITH [2,4,6] AS y RETURN [x IN y | x] AS result; -WITH [2,4,6] AS y RETURN [x IN range(0, size(y)) | x] AS result; \ No newline at end of file +WITH [2,4,6] AS y RETURN [x IN range(0, size(y)) | x] AS result; +RETURN [x in [x in [x in [1,2] |x+1] |x+1] | x+1+head([x in [1,2,3] | x^2])]; +RETURN [x in [1] | x + head([x in [2,3] | x+1]) + x ]; +RETURN [ _ in range(1,10) | x]; +RETURN [x in [x in [x in [2,3,4] |x+1] |x+1] | x+1+head([x in [1,2,3] | x^2])+x]; +RETURN [x in [1] | x + head([x]) + x]; +RETURN [x in [1] | x + head([x in [2] | x]) + x]; diff --git a/test/resource/unit_test/profile/cypher/profile.test b/test/resource/unit_test/profile/cypher/profile.test index f5d36a835b..9a0f429363 100644 --- a/test/resource/unit_test/profile/cypher/profile.test +++ b/test/resource/unit_test/profile/cypher/profile.test @@ -1,2 +1,2 @@ MATCH (n:Person) WHERE n.birthyear > 1960 RETURN n LIMIT 5; -#PROFILE MATCH (n:Person) WHERE n.birthyear > 1960 RETURN n LIMIT 5; +PROFILE MATCH (n:Person) WHERE n.birthyear > 1960 RETURN n LIMIT 5; diff --git a/test/resource/unit_test/undefined_var/cypher/undefined_var.result b/test/resource/unit_test/undefined_var/cypher/undefined_var.result index 34819408b8..34f979c298 100644 --- a/test/resource/unit_test/undefined_var/cypher/undefined_var.result +++ b/test/resource/unit_test/undefined_var/cypher/undefined_var.result @@ -13,7 +13,7 @@ MATCH (n:Person) WHERE n.name = k RETURN n; MATCH (v1:Film)<-[:ACTED_IN|DIRECTED]-(v2:Person) return v1.title,count(distinct v2) as cnt order by number; [InputError] Unknown order by field WITH [1,2,3] AS k RETURN [x in k | x + y] AS result; -[InputError] Variable `y` not defined +[CypherException] Unknown variable: y REMOVE a.name; [InputError] Variable `a` not defined SET a :MyLabel; diff --git a/test/test_cypher_v2.cpp b/test/test_cypher_v2.cpp index 8608e501b5..50148f909e 100644 --- a/test/test_cypher_v2.cpp +++ b/test/test_cypher_v2.cpp @@ -200,6 +200,23 @@ class TestCypherV2 : public TuGraphTest { result = execution_plan_v2.ErrorMsg(); return false; } else { +// if (visitor.CommandType() != parser::CmdType::QUERY) { +// ctx_->result_info_ = std::make_unique(); +// ctx_->result_ = std::make_unique(); +// std::string header, data; +// if (visitor.CommandType() == parser::CmdType::EXPLAIN) { +// header = "@plan"; +// data = execution_plan_v2.DumpPlan(0, false); +// } else { +// header = "@profile"; +// data = execution_plan_v2.DumpGraph(); +// } +// ctx_->result_->ResetHeader({{header, lgraph_api::LGraphType::STRING}}); +// auto r = ctx_->result_->MutableRecord(); +// r->Insert(header, lgraph::FieldData(data)); +// result = ctx_->result_->Dump(false); +// return true; +// } try { execution_plan_v2.Execute(ctx_.get()); } catch (std::exception& e) { @@ -373,12 +390,12 @@ TEST_F(TestCypherV2, TestMultiMatch) { // test_files(dir); // } -// TEST_F(TestCypherV2, TestUnion) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/union/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestUnion) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/union/cypher"; + test_files(dir); +} TEST_F(TestCypherV2, TestFunction) { set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); @@ -429,19 +446,19 @@ TEST_F(TestCypherV2, TestWith) { test_files(dir); } -// TEST_F(TestCypherV2, TestListComprehension) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/list_comprehension/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestListComprehension) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/list_comprehension/cypher"; + test_files(dir); +} -// TEST_F(TestCypherV2, TestProfile) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/profile/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestProfile) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/profile/cypher"; + test_files(dir); +} TEST_F(TestCypherV2, TestUnwind) { set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); @@ -478,12 +495,12 @@ TEST_F(TestCypherV2, TestDelete) { test_files(dir); } -// TEST_F(TestCypherV2, TestRemove) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/remove/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestRemove) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/remove/cypher"; + test_files(dir); +} TEST_F(TestCypherV2, TestOrderby) { set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO);