diff --git a/src/binder/bind/bind_query.cpp b/src/binder/bind/bind_query.cpp index 12beed581b..9873a8fc54 100644 --- a/src/binder/bind/bind_query.cpp +++ b/src/binder/bind/bind_query.cpp @@ -53,7 +53,6 @@ std::unique_ptr Binder::bindQuery(const RegularQuery& regular auto boundRegularQuery = std::make_unique( regularQuery.getIsUnionAll(), normalizedSingleQueries[0].getStatementResult()->copy()); for (auto& normalizedSingleQuery : normalizedSingleQueries) { - validateReadNotFollowUpdate(normalizedSingleQuery); boundRegularQuery->addSingleQuery(std::move(normalizedSingleQuery)); } validateIsAllUnionOrUnionAll(*boundRegularQuery); diff --git a/src/binder/binder.cpp b/src/binder/binder.cpp index be764c123d..e3ec0be181 100644 --- a/src/binder/binder.cpp +++ b/src/binder/binder.cpp @@ -153,18 +153,6 @@ void Binder::validateOrderByFollowedBySkipOrLimitInWithClause( } } -void Binder::validateReadNotFollowUpdate(const NormalizedSingleQuery& singleQuery) { - bool hasSeenUpdateClause = false; - for (auto i = 0u; i < singleQuery.getNumQueryParts(); ++i) { - auto normalizedQueryPart = singleQuery.getQueryPart(i); - if (hasSeenUpdateClause && normalizedQueryPart->hasReadingClause()) { - throw BinderException( - "Read after update is not supported. Try query with multiple statements."); - } - hasSeenUpdateClause |= normalizedQueryPart->hasUpdatingClause(); - } -} - void Binder::validateTableType(table_id_t tableID, TableType expectedTableType) { auto tableEntry = clientContext->getCatalog()->getTableCatalogEntry(clientContext->getTx(), tableID); diff --git a/src/include/binder/binder.h b/src/include/binder/binder.h index bc45de246b..8cf55fab53 100644 --- a/src/include/binder/binder.h +++ b/src/include/binder/binder.h @@ -273,10 +273,6 @@ class Binder { static void validateOrderByFollowedBySkipOrLimitInWithClause( const BoundProjectionBody& boundProjectionBody); - // We don't support read after write for simplicity. User should instead querying through - // multiple statement. - static void validateReadNotFollowUpdate(const NormalizedSingleQuery& singleQuery); - void validateTableType(common::table_id_t tableID, common::TableType expectedTableType); void validateTableExist(const std::string& tableName); diff --git a/src/include/planner/operator/logical_operator.h b/src/include/planner/operator/logical_operator.h index 6bcbf104e6..4c65a8ea54 100644 --- a/src/include/planner/operator/logical_operator.h +++ b/src/include/planner/operator/logical_operator.h @@ -58,9 +58,9 @@ enum class LogicalOperatorType : uint8_t { IMPORT_DATABASE, }; -class LogicalOperatorUtils { -public: +struct LogicalOperatorUtils { static std::string logicalOperatorTypeToString(LogicalOperatorType type); + static bool isUpdate(LogicalOperatorType type); }; class LogicalOperator; @@ -68,35 +68,33 @@ using logical_op_vector_t = std::vector>; class LogicalOperator { public: - // Leaf operator. explicit LogicalOperator(LogicalOperatorType operatorType) : operatorType{operatorType} {} - // Unary operator. explicit LogicalOperator( LogicalOperatorType operatorType, std::shared_ptr child); - // Binary operator. explicit LogicalOperator(LogicalOperatorType operatorType, std::shared_ptr left, std::shared_ptr right); explicit LogicalOperator(LogicalOperatorType operatorType, const logical_op_vector_t& children); virtual ~LogicalOperator() = default; - inline uint32_t getNumChildren() const { return children.size(); } - - inline std::shared_ptr getChild(uint64_t idx) const { return children[idx]; } - inline std::vector> getChildren() const { return children; } - inline void setChild(uint64_t idx, std::shared_ptr child) { + uint32_t getNumChildren() const { return children.size(); } + std::shared_ptr getChild(uint64_t idx) const { return children[idx]; } + std::vector> getChildren() const { return children; } + void setChild(uint64_t idx, std::shared_ptr child) { children[idx] = std::move(child); } - inline void setChildren(logical_op_vector_t children_) { children = std::move(children_); } - inline LogicalOperatorType getOperatorType() const { return operatorType; } + // Operator type. + LogicalOperatorType getOperatorType() const { return operatorType; } + bool hasUpdateRecursive(); - inline Schema* getSchema() const { return schema.get(); } + // Schema + Schema* getSchema() const { return schema.get(); } virtual void computeFactorizedSchema() = 0; virtual void computeFlatSchema() = 0; + // Printing. virtual std::string getExpressionsForPrinting() const = 0; - // Print the sub-plan rooted at this operator. virtual std::string toString(uint64_t depth = 0) const; @@ -105,8 +103,8 @@ class LogicalOperator { static logical_op_vector_t copy(const logical_op_vector_t& ops); protected: - inline void createEmptySchema() { schema = std::make_unique(); } - inline void copyChildSchema(uint32_t idx) { schema = children[idx]->getSchema()->copy(); } + void createEmptySchema() { schema = std::make_unique(); } + void copyChildSchema(uint32_t idx) { schema = children[idx]->getSchema()->copy(); } protected: LogicalOperatorType operatorType; diff --git a/src/include/planner/operator/logical_plan.h b/src/include/planner/operator/logical_plan.h index 8948e01382..40ae4c15a1 100644 --- a/src/include/planner/operator/logical_plan.h +++ b/src/include/planner/operator/logical_plan.h @@ -1,6 +1,5 @@ #pragma once -#include "logical_explain.h" #include "logical_operator.h" namespace kuzu { @@ -13,28 +12,23 @@ class LogicalPlan { public: LogicalPlan() : estCardinality{1}, cost{0} {} - inline void setLastOperator(std::shared_ptr op) { - lastOperator = std::move(op); - } + void setLastOperator(std::shared_ptr op) { lastOperator = std::move(op); } - inline bool isEmpty() const { return lastOperator == nullptr; } + bool isEmpty() const { return lastOperator == nullptr; } - inline std::shared_ptr getLastOperator() const { return lastOperator; } - inline Schema* getSchema() const { return lastOperator->getSchema(); } + std::shared_ptr getLastOperator() const { return lastOperator; } + Schema* getSchema() const { return lastOperator->getSchema(); } - inline void setCardinality(uint64_t cardinality) { estCardinality = cardinality; } - inline uint64_t getCardinality() const { return estCardinality; } + void setCardinality(uint64_t cardinality) { estCardinality = cardinality; } + uint64_t getCardinality() const { return estCardinality; } - inline void setCost(uint64_t cost_) { cost = cost_; } - inline uint64_t getCost() const { return cost; } + void setCost(uint64_t cost_) { cost = cost_; } + uint64_t getCost() const { return cost; } - inline std::string toString() const { return lastOperator->toString(); } + std::string toString() const { return lastOperator->toString(); } - inline bool isProfile() const { - return lastOperator->getOperatorType() == LogicalOperatorType::EXPLAIN && - reinterpret_cast(lastOperator.get())->getExplainType() == - common::ExplainType::PROFILE; - } + bool isProfile() const; + bool hasUpdate() const; std::unique_ptr shallowCopy() const; diff --git a/src/include/planner/planner.h b/src/include/planner/planner.h index df62cd059a..594b78359f 100644 --- a/src/include/planner/planner.h +++ b/src/include/planner/planner.h @@ -225,7 +225,7 @@ class Planner { // Append Join operators void appendHashJoin(const binder::expression_vector& joinNodeIDs, common::JoinType joinType, - LogicalPlan& probePlan, LogicalPlan& buildPlan); + LogicalPlan& probePlan, LogicalPlan& buildPlan, LogicalPlan& resultPlan); void appendMarkJoin(const binder::expression_vector& joinNodeIDs, const std::shared_ptr& mark, LogicalPlan& probePlan, LogicalPlan& buildPlan); @@ -233,8 +233,8 @@ class Planner { binder::expression_vector& boundNodeIDs, LogicalPlan& probePlan, std::vector>& buildPlans); - void appendCrossProduct( - common::AccumulateType accumulateType, LogicalPlan& probePlan, LogicalPlan& buildPlan); + void appendCrossProduct(common::AccumulateType accumulateType, const LogicalPlan& probePlan, + const LogicalPlan& buildPlan, LogicalPlan& resultPlan); // Append accumulate void appendAccumulate(common::AccumulateType accumulateType, LogicalPlan& plan); diff --git a/src/planner/operator/logical_operator.cpp b/src/planner/operator/logical_operator.cpp index b2d60c5688..2544e9a175 100644 --- a/src/planner/operator/logical_operator.cpp +++ b/src/planner/operator/logical_operator.cpp @@ -108,6 +108,20 @@ std::string LogicalOperatorUtils::logicalOperatorTypeToString(LogicalOperatorTyp } } +bool LogicalOperatorUtils::isUpdate(LogicalOperatorType type) { + switch (type) { + case LogicalOperatorType::INSERT: + case LogicalOperatorType::DELETE_NODE: + case LogicalOperatorType::DELETE_REL: + case LogicalOperatorType::SET_NODE_PROPERTY: + case LogicalOperatorType::SET_REL_PROPERTY: + case LogicalOperatorType::MERGE: + return true; + default: + return false; + } +} + LogicalOperator::LogicalOperator( LogicalOperatorType operatorType, std::shared_ptr child) : operatorType{operatorType} { @@ -129,6 +143,18 @@ LogicalOperator::LogicalOperator( } } +bool LogicalOperator::hasUpdateRecursive() { + if (LogicalOperatorUtils::isUpdate(operatorType)) { + return true; + } + for (auto& child : children) { + if (child->hasUpdateRecursive()) { + return true; + } + } + return false; +} + std::string LogicalOperator::toString(uint64_t depth) const { auto padding = std::string(depth * 4, ' '); std::string result = padding; diff --git a/src/planner/operator/logical_plan.cpp b/src/planner/operator/logical_plan.cpp index 7a20fa8831..cbce328805 100644 --- a/src/planner/operator/logical_plan.cpp +++ b/src/planner/operator/logical_plan.cpp @@ -1,8 +1,20 @@ #include "planner/operator/logical_plan.h" +#include "planner/operator/logical_explain.h" + namespace kuzu { namespace planner { +bool LogicalPlan::isProfile() const { + return lastOperator->getOperatorType() == LogicalOperatorType::EXPLAIN && + reinterpret_cast(lastOperator.get())->getExplainType() == + common::ExplainType::PROFILE; +} + +bool LogicalPlan::hasUpdate() const { + return lastOperator->hasUpdateRecursive(); +} + std::unique_ptr LogicalPlan::shallowCopy() const { auto plan = std::make_unique(); plan->lastOperator = lastOperator; // shallow copy sub-plan diff --git a/src/planner/plan/append_cross_product.cpp b/src/planner/plan/append_cross_product.cpp index 80c7758c6f..5482a79d87 100644 --- a/src/planner/plan/append_cross_product.cpp +++ b/src/planner/plan/append_cross_product.cpp @@ -6,16 +6,16 @@ using namespace kuzu::common; namespace kuzu { namespace planner { -void Planner::appendCrossProduct( - AccumulateType accumulateType, LogicalPlan& probePlan, LogicalPlan& buildPlan) { +void Planner::appendCrossProduct(AccumulateType accumulateType, const LogicalPlan& probePlan, + const LogicalPlan& buildPlan, LogicalPlan& resultPlan) { auto crossProduct = make_shared( accumulateType, probePlan.getLastOperator(), buildPlan.getLastOperator()); crossProduct->computeFactorizedSchema(); // update cost - probePlan.setCost(probePlan.getCardinality() + buildPlan.getCardinality()); + resultPlan.setCost(probePlan.getCardinality() + buildPlan.getCardinality()); // update cardinality - probePlan.setCardinality(cardinalityEstimator.estimateCrossProduct(probePlan, buildPlan)); - probePlan.setLastOperator(std::move(crossProduct)); + resultPlan.setCardinality(cardinalityEstimator.estimateCrossProduct(probePlan, buildPlan)); + resultPlan.setLastOperator(std::move(crossProduct)); } } // namespace planner diff --git a/src/planner/plan/append_extend.cpp b/src/planner/plan/append_extend.cpp index 06b8094707..e41d735316 100644 --- a/src/planner/plan/append_extend.cpp +++ b/src/planner/plan/append_extend.cpp @@ -145,7 +145,8 @@ void Planner::appendNonRecursiveExtend(const std::shared_ptr& bo appendScanInternalID(rdfInfo->predicateID, rdfInfo->resourceTableIDs, *tmpPlan); appendScanNodeProperties( rdfInfo->predicateID, rdfInfo->resourceTableIDs, expression_vector{iri}, *tmpPlan); - appendHashJoin(expression_vector{rdfInfo->predicateID}, JoinType::INNER, plan, *tmpPlan); + appendHashJoin( + expression_vector{rdfInfo->predicateID}, JoinType::INNER, plan, *tmpPlan, plan); } } diff --git a/src/planner/plan/append_join.cpp b/src/planner/plan/append_join.cpp index b071e3d2bd..498d6b335c 100644 --- a/src/planner/plan/append_join.cpp +++ b/src/planner/plan/append_join.cpp @@ -9,7 +9,7 @@ namespace kuzu { namespace planner { void Planner::appendHashJoin(const binder::expression_vector& joinNodeIDs, JoinType joinType, - LogicalPlan& probePlan, LogicalPlan& buildPlan) { + LogicalPlan& probePlan, LogicalPlan& buildPlan, LogicalPlan& resultPlan) { std::vector joinConditions; for (auto& joinNodeID : joinNodeIDs) { joinConditions.emplace_back(joinNodeID, joinNodeID); @@ -32,11 +32,11 @@ void Planner::appendHashJoin(const binder::expression_vector& joinNodeIDs, JoinT hashJoin->setSIP(SidewaysInfoPassing::PROHIBIT_BUILD_TO_PROBE); } // Update cost - probePlan.setCost(CostModel::computeHashJoinCost(joinNodeIDs, probePlan, buildPlan)); + resultPlan.setCost(CostModel::computeHashJoinCost(joinNodeIDs, probePlan, buildPlan)); // Update cardinality - probePlan.setCardinality( + resultPlan.setCardinality( cardinalityEstimator.estimateHashJoin(joinNodeIDs, probePlan, buildPlan)); - probePlan.setLastOperator(std::move(hashJoin)); + resultPlan.setLastOperator(std::move(hashJoin)); } void Planner::appendMarkJoin(const binder::expression_vector& joinNodeIDs, diff --git a/src/planner/plan/plan_join_order.cpp b/src/planner/plan/plan_join_order.cpp index c6e66725ed..9ad0633231 100644 --- a/src/planner/plan/plan_join_order.cpp +++ b/src/planner/plan/plan_join_order.cpp @@ -561,8 +561,8 @@ void Planner::planInnerHashJoin(const SubqueryGraph& subgraph, const SubqueryGra if (CostModel::computeHashJoinCost(joinNodeIDs, *leftPlan, *rightPlan) < maxCost) { auto leftPlanProbeCopy = leftPlan->shallowCopy(); auto rightPlanBuildCopy = rightPlan->shallowCopy(); - appendHashJoin( - joinNodeIDs, JoinType::INNER, *leftPlanProbeCopy, *rightPlanBuildCopy); + appendHashJoin(joinNodeIDs, JoinType::INNER, *leftPlanProbeCopy, + *rightPlanBuildCopy, *leftPlanProbeCopy); appendFilters(predicates, *leftPlanProbeCopy); context.addPlan(newSubgraph, std::move(leftPlanProbeCopy)); } @@ -571,8 +571,8 @@ void Planner::planInnerHashJoin(const SubqueryGraph& subgraph, const SubqueryGra CostModel::computeHashJoinCost(joinNodeIDs, *rightPlan, *leftPlan) < maxCost) { auto leftPlanBuildCopy = leftPlan->shallowCopy(); auto rightPlanProbeCopy = rightPlan->shallowCopy(); - appendHashJoin( - joinNodeIDs, JoinType::INNER, *rightPlanProbeCopy, *leftPlanBuildCopy); + appendHashJoin(joinNodeIDs, JoinType::INNER, *rightPlanProbeCopy, + *leftPlanBuildCopy, *rightPlanProbeCopy); appendFilters(predicates, *rightPlanProbeCopy); context.addPlan(newSubgraph, std::move(rightPlanProbeCopy)); } @@ -588,7 +588,8 @@ std::vector> Planner::planCrossProduct( for (auto& rightPlan : rightPlans) { auto leftPlanCopy = leftPlan->shallowCopy(); auto rightPlanCopy = rightPlan->shallowCopy(); - appendCrossProduct(AccumulateType::REGULAR, *leftPlanCopy, *rightPlanCopy); + appendCrossProduct( + AccumulateType::REGULAR, *leftPlanCopy, *rightPlanCopy, *leftPlanCopy); result.push_back(std::move(leftPlanCopy)); } } diff --git a/src/planner/plan/plan_read.cpp b/src/planner/plan/plan_read.cpp index 01e106e7e0..a366e1c5d7 100644 --- a/src/planner/plan/plan_read.cpp +++ b/src/planner/plan/plan_read.cpp @@ -103,7 +103,7 @@ void Planner::planInQueryCall( if (!predicatesToPushDown.empty()) { appendFilters(predicatesToPushDown, *tmpPlan); } - appendCrossProduct(AccumulateType::REGULAR, *plan, *tmpPlan); + appendCrossProduct(AccumulateType::REGULAR, *plan, *tmpPlan, *plan); } else { appendInQueryCall(*readingClause, *plan); if (!predicatesToPushDown.empty()) { @@ -139,7 +139,7 @@ void Planner::planLoadFrom( if (!predicatesToPushDown.empty()) { appendFilters(predicatesToPushDown, *tmpPlan); } - appendCrossProduct(AccumulateType::REGULAR, *plan, *tmpPlan); + appendCrossProduct(AccumulateType::REGULAR, *plan, *tmpPlan, *plan); } else { appendScanFile(loadFrom->getInfo(), *plan); if (!predicatesToPushDown.empty()) { diff --git a/src/planner/plan/plan_subquery.cpp b/src/planner/plan/plan_subquery.cpp index 90c28fdaa6..416324af5e 100644 --- a/src/planner/plan/plan_subquery.cpp +++ b/src/planner/plan/plan_subquery.cpp @@ -39,7 +39,11 @@ void Planner::planOptionalMatch(const QueryGraphCollection& queryGraphCollection if (correlatedExpressions.empty()) { // No join condition, apply cross product. auto rightPlan = planQueryGraphCollection(queryGraphCollection, predicates); - appendCrossProduct(AccumulateType::OPTIONAL_, leftPlan, *rightPlan); + if (leftPlan.hasUpdate()) { + appendCrossProduct(AccumulateType::OPTIONAL_, *rightPlan, leftPlan, leftPlan); + } else { + appendCrossProduct(AccumulateType::OPTIONAL_, leftPlan, *rightPlan, leftPlan); + } return; } bool isInternalIDCorrelated = ExpressionUtil::isExpressionsWithDataType( @@ -57,7 +61,11 @@ void Planner::planOptionalMatch(const QueryGraphCollection& queryGraphCollection correlatedExpressions, leftPlan.getCardinality(), queryGraphCollection, predicates); appendAccumulate(AccumulateType::REGULAR, correlatedExpressions, leftPlan); } - appendHashJoin(correlatedExpressions, JoinType::LEFT, leftPlan, *rightPlan); + if (leftPlan.hasUpdate()) { + throw RuntimeException(stringFormat("Optional match after update is not supported. Missing " + "right outer join implementation.")); + } + appendHashJoin(correlatedExpressions, JoinType::LEFT, leftPlan, *rightPlan, leftPlan); } void Planner::planRegularMatch(const QueryGraphCollection& queryGraphCollection, @@ -76,18 +84,26 @@ void Planner::planRegularMatch(const QueryGraphCollection& queryGraphCollection, } auto joinNodeIDs = ExpressionUtil::getExpressionsWithDataType( correlatedExpressions, LogicalTypeID::INTERNAL_ID); - std::unique_ptr rightPlan; if (joinNodeIDs.empty()) { - rightPlan = planQueryGraphCollectionInNewContext(SubqueryType::NONE, correlatedExpressions, - leftPlan.getCardinality(), queryGraphCollection, predicatesToPushDown); - appendCrossProduct(AccumulateType::REGULAR, leftPlan, *rightPlan); + auto rightPlan = + planQueryGraphCollectionInNewContext(SubqueryType::NONE, correlatedExpressions, + leftPlan.getCardinality(), queryGraphCollection, predicatesToPushDown); + if (leftPlan.hasUpdate()) { + appendCrossProduct(AccumulateType::REGULAR, *rightPlan, leftPlan, leftPlan); + } else { + appendCrossProduct(AccumulateType::REGULAR, leftPlan, *rightPlan, leftPlan); + } } else { // TODO(Xiyang): there is a question regarding if we want to plan as a correlated subquery // Multi-part query is actually CTE and CTE can be considered as a subquery but does not // scan from outer. - rightPlan = planQueryGraphCollectionInNewContext(SubqueryType::INTERNAL_ID_CORRELATED, + auto rightPlan = planQueryGraphCollectionInNewContext(SubqueryType::INTERNAL_ID_CORRELATED, joinNodeIDs, leftPlan.getCardinality(), queryGraphCollection, predicatesToPushDown); - appendHashJoin(joinNodeIDs, JoinType::INNER, leftPlan, *rightPlan); + if (leftPlan.hasUpdate()) { + appendHashJoin(joinNodeIDs, JoinType::INNER, *rightPlan, leftPlan, leftPlan); + } else { + appendHashJoin(joinNodeIDs, JoinType::INNER, leftPlan, *rightPlan, leftPlan); + } } for (auto& predicate : predicatesToPullUp) { appendFilter(predicate, leftPlan); @@ -116,7 +132,7 @@ void Planner::planSubquery(const std::shared_ptr& expression, Logica default: KU_UNREACHABLE; } - appendCrossProduct(AccumulateType::REGULAR, outerPlan, *innerPlan); + appendCrossProduct(AccumulateType::REGULAR, outerPlan, *innerPlan, outerPlan); } else { auto isInternalIDCorrelated = ExpressionUtil::isExpressionsWithDataType(correlatedExprs, LogicalTypeID::INTERNAL_ID); @@ -137,7 +153,8 @@ void Planner::planSubquery(const std::shared_ptr& expression, Logica case common::SubqueryType::COUNT: { appendAggregate( correlatedExprs, expression_vector{subquery->getProjectionExpr()}, *innerPlan); - appendHashJoin(correlatedExprs, common::JoinType::COUNT, outerPlan, *innerPlan); + appendHashJoin( + correlatedExprs, common::JoinType::COUNT, outerPlan, *innerPlan, outerPlan); } break; default: KU_UNREACHABLE; diff --git a/test/test_files/exceptions/binder/binder_error.test b/test/test_files/exceptions/binder/binder_error.test index c6f3e8c2d6..b9381ee22c 100644 --- a/test/test_files/exceptions/binder/binder_error.test +++ b/test/test_files/exceptions/binder/binder_error.test @@ -241,14 +241,6 @@ Binder exception: p1.age has data type INT64 but (STRING) was expected. ---- error Binder exception: Union and union all can not be used together. --LOG ReadAfterUpdate --STATEMENT MATCH (a:person) SET a.age = 35 WITH a MATCH (a)-[:knows]->(b:person) RETURN a.age ----- error -Binder exception: Read after update is not supported. Try query with multiple statements. --STATEMENT MATCH (a:person) WHERE a.age = 35 DELETE a WITH a MATCH (a)-[:knows]->(b:person) RETURN a.age ----- error -Binder exception: Read after update is not supported. Try query with multiple statements. - -LOG SetDataTypeMisMatch -STATEMENT MATCH (a:person) SET a.age = 'hh' ---- error diff --git a/test/test_files/update_node/create_read_tinysnb.test b/test/test_files/update_node/create_read_tinysnb.test index 52453a173d..d97bfc34a9 100644 --- a/test/test_files/update_node/create_read_tinysnb.test +++ b/test/test_files/update_node/create_read_tinysnb.test @@ -4,6 +4,19 @@ -- -CASE CreateNodeRead1 +-STATEMENT CREATE (a:person {ID:91, fName:'dummy'}) WITH a MATCH (b:person) WHERE b.ID > 9 RETURN a.ID, a.fName, b.ID, b.fName; +---- 2 +91|dummy|10|Hubert Blaine Wolfeschlegelsteinhausenbergerdorff +91|dummy|91|dummy +-STATEMENT CREATE (a:person {ID:93, fName:'dummy'}) WITH a MATCH (b:person) WHERE b.ID = a.ID - 90 RETURN a.ID, a.fName, b.ID, b.fName; +---- 1 +93|dummy|3|Carol +-STATEMENT MATCH (a:person) WHERE a.ID < 3 CREATE (b:person {ID:a.ID+100, fName:a.fName}) WITH a, b MATCH (c:person) WHERE c.ID > 99 RETURN a.ID, a.fName, b.ID, b.fName, c.ID, c.fName; +---- 4 +0|Alice|100|Alice|100|Alice +0|Alice|100|Alice|102|Bob +2|Bob|102|Bob|100|Alice +2|Bob|102|Bob|102|Bob -STATEMENT CREATE (a:person {ID:80, isWorker:true,age:22,eyeSight:1.1}) RETURN a.ID, a.age, a.fName, a.eyeSight; ---- 1 80|22||1.100000 @@ -11,6 +24,18 @@ ---- 1 {_ID: 1:3, _LABEL: organisation, ID: 0, name: test}| + +-CASE OptionalMatchAfterInsert +-STATEMENT CREATE (a:person {ID:200, fName:'test'}) WITH a OPTIONAL MATCH (b:person) RETURN COUNT(*); +---- 1 +9 +-STATEMENT CREATE (a:person {ID:201, fName:'test'}) WITH a OPTIONAL MATCH (b:person) WHERE b.ID > a.ID RETURN COUNT(b); +---- error +Runtime exception: Optional match after update is not supported. Missing right outer join implementation. +-STATEMENT CREATE (a:person {ID:202, fName:'test'}) WITH a OPTIONAL MATCH (a)-[]->(b) RETURN COUNT(*); +---- error +Runtime exception: Optional match after update is not supported. Missing right outer join implementation. + -CASE CreateNodeRead2 -STATEMENT MATCH (a:person) WHERE a.ID < 3 CREATE (b:person {ID: a.ID + 11, fName: 'new', age:a.age * 2}) RETURN a.ID, a.fName, a.age, b.ID, b.fName, b.age diff --git a/test/test_files/update_node/delete_tinysnb.test b/test/test_files/update_node/delete_tinysnb.test index b20d4dc793..b4de091397 100644 --- a/test/test_files/update_node/delete_tinysnb.test +++ b/test/test_files/update_node/delete_tinysnb.test @@ -13,6 +13,11 @@ -STATEMENT MATCH (a:person) WHERE a.ID = 101 RETURN a.ID, a.fName, a.age ---- 1 101|| +-STATEMENT MATCH (a:person) WHERE a.ID = 0 DETACH DELETE a WITH a MATCH (b:person) WHERE b.ID < 6 RETURN a.ID, b.ID; +---- 3 +0|2 +0|3 +0|5 -CASE MixedDeleteInsertTest -STATEMENT CREATE (a:organisation {ID:30, mark:3.3}) diff --git a/test/test_files/update_node/set_read.test b/test/test_files/update_node/set_read.test index fcf4277d9c..fd4ca398bb 100644 --- a/test/test_files/update_node/set_read.test +++ b/test/test_files/update_node/set_read.test @@ -19,6 +19,15 @@ -STATEMENT MATCH (a:person) WHERE a.ID=0 RETURN a.age, a.gender, a.fName ---- 1 70|1| +-STATEMENT MATCH (a:person) WHERE a.ID=0 SET a.fName = 'XX' WITH a MATCH (b:person) WHERE b.ID < 6 RETURN a.ID, a.fName, b.ID, b.fName; +---- 4 +0|XX|0|XX +0|XX|2|Bob +0|XX|3|Carol +0|XX|5|Dan +-STATEMENT MATCH (a:person) WHERE a.ID=2 SET a.fName = 'BB' WITH a MATCH (b:person) WHERE b.ID=a.ID RETURN a.ID, a.fName, b.ID, b.fName; +---- 1 +2|BB|2|BB -CASE SetReadTest2 -STATEMENT CREATE NODE TABLE play(ID INT64, name STRING, PRIMARY KEY(ID)); diff --git a/test/test_files/update_rel/create_read_tinysnb.test b/test/test_files/update_rel/create_read_tinysnb.test index 6ba899b6b5..4d9fe9dd75 100644 --- a/test/test_files/update_rel/create_read_tinysnb.test +++ b/test/test_files/update_rel/create_read_tinysnb.test @@ -18,6 +18,18 @@ (0:0)-{_LABEL: knows, _ID: 3:0, date: 2021-06-30, meetTime: 1986-10-21 21:08:31.521, validInterval: 10 years 5 months 13:00:00.000024, comments: [rnme,m8sihsdnf2990nfiwf], summary: {locations: ['toronto','waterloo'], transfer: {day: 2021-01-02, amount: [100,200]}}, notes: 1, someMap: {a=b}}->(0:1) (0:0)-{_LABEL: knows, _ID: 3:14, date: 2023-03-03}->(0:1) (0:0)-{_LABEL: knows, _ID: 3:15, date: 2023-04-04}->(0:1) +-STATEMENT MATCH (a:person), (b:person) WHERE a.ID = 0 AND b.ID = 2 CREATE (a)-[e:knows {date:date('2024-01-01')}]-(b) WITH a MATCH (a)-[e:knows]->(b) RETURN a.fName, e.date, b.fName; +---- 6 +Alice|2021-06-30|Bob +Alice|2021-06-30|Carol +Alice|2021-06-30|Dan +Alice|2023-03-03|Bob +Alice|2023-04-04|Bob +Alice|2024-01-01|Bob +-STATEMENT MATCH (a:person), (b:person) WHERE a.ID = 0 AND b.ID = 2 CREATE (a)-[e:knows {date:date('2023-12-20')}]-(b) WITH a, e MATCH (a)-[e2:knows]->(b) WHERE e.date<=e2.date RETURN a.fName, e.date, e2.date, b.fName; +---- 2 +Alice|2023-12-20|2023-12-20|Bob +Alice|2023-12-20|2024-01-01|Bob -CASE CreateRelRead2 -STATEMENT MATCH (a:person)-[e:knows]->(b:person) WHERE a.ID = 0 CREATE (a)-[f:knows {date:date('2023-04-04')}]->(b) RETURN f; diff --git a/test/test_files/update_rel/delete_tinysnb.test b/test/test_files/update_rel/delete_tinysnb.test index afb7e44c24..acec18d874 100644 --- a/test/test_files/update_rel/delete_tinysnb.test +++ b/test/test_files/update_rel/delete_tinysnb.test @@ -14,6 +14,20 @@ -STATEMENT MATCH (a:person)-[e:knows]->(b:person) WHERE a.ID=0 RETURN COUNT(*) ---- 1 2 +-STATEMENT MATCH (a:person)-[e:knows]->(b:person) WHERE a.ID=2 AND b.ID = 0 DELETE e WITH e MATCH (a:person)-[e2:knows]->(b:person) RETURN e.date, a.fName, e2.date, b.fName; +---- 12 +2021-06-30|Alice|2021-06-30|Bob +2021-06-30|Alice|2021-06-30|Dan +2021-06-30|Bob|1950-05-14|Carol +2021-06-30|Bob|1950-05-14|Dan +2021-06-30|Carol|1950-05-14|Bob +2021-06-30|Carol|2000-01-01|Dan +2021-06-30|Carol|2021-06-30|Alice +2021-06-30|Dan|1950-05-14|Bob +2021-06-30|Dan|2000-01-01|Carol +2021-06-30|Dan|2021-06-30|Alice +2021-06-30|Elizabeth|1905-12-12|Farooq +2021-06-30|Elizabeth|1905-12-12|Greg -CASE DeleteFromKnows2 -STATEMENT MATCH (a:person)-[e:knows]->(b:person) WHERE a.ID=0 AND b.ID>=3 DELETE e diff --git a/test/test_files/update_rel/set_read_tinysnb.test b/test/test_files/update_rel/set_read_tinysnb.test index c4f8680b08..660f182264 100644 --- a/test/test_files/update_rel/set_read_tinysnb.test +++ b/test/test_files/update_rel/set_read_tinysnb.test @@ -18,6 +18,16 @@ -STATEMENT MATCH (a:person)-[e:knows]->(b:person) WHERE a.ID=0 AND b.ID=5 RETURN e.date; ---- 1 +-STATEMENT MATCH (a:person)-[e:knows]->(b:person) WHERE a.ID=0 AND b.ID=5 SET e.date=date('1999-01-02') WITH e MATCH (x:person)-[e2:knows]->(y:person) WHERE e2.date <= e.date RETURN e.date, x.fName, e2.date, y.fName; +---- 7 +1999-01-02|Alice|1999-01-02|Dan +1999-01-02|Bob|1950-05-14|Carol +1999-01-02|Bob|1950-05-14|Dan +1999-01-02|Carol|1950-05-14|Bob +1999-01-02|Dan|1950-05-14|Bob +1999-01-02|Elizabeth|1905-12-12|Farooq +1999-01-02|Elizabeth|1905-12-12|Greg + -CASE SetReadTest2 -STATEMENT CREATE REL TABLE play(FROM person TO person, date DATE, year INT64); ---- ok