diff --git a/src/include/planner/logical_plan/persistent/logical_create.h b/src/include/planner/logical_plan/persistent/logical_create.h index 83d0e850fe..ccc2904d31 100644 --- a/src/include/planner/logical_plan/persistent/logical_create.h +++ b/src/include/planner/logical_plan/persistent/logical_create.h @@ -9,12 +9,16 @@ namespace planner { struct LogicalCreateNodeInfo { std::shared_ptr node; std::shared_ptr primaryKey; + binder::expression_vector propertiesToReturn; LogicalCreateNodeInfo(std::shared_ptr node, - std::shared_ptr primaryKey) - : node{std::move(node)}, primaryKey{std::move(primaryKey)} {} + std::shared_ptr primaryKey, + binder::expression_vector propertiesToReturn) + : node{std::move(node)}, primaryKey{std::move(primaryKey)}, propertiesToReturn{std::move( + propertiesToReturn)} {} LogicalCreateNodeInfo(const LogicalCreateNodeInfo& other) - : node{other.node}, primaryKey{other.primaryKey} {} + : node{other.node}, primaryKey{other.primaryKey}, propertiesToReturn{ + other.propertiesToReturn} {} inline std::unique_ptr copy() const { return std::make_unique(*this); @@ -27,12 +31,14 @@ struct LogicalCreateNodeInfo { struct LogicalCreateRelInfo { std::shared_ptr rel; std::vector setItems; + binder::expression_vector propertiesToReturn; - LogicalCreateRelInfo( - std::shared_ptr rel, std::vector setItems) - : rel{std::move(rel)}, setItems{std::move(setItems)} {} + LogicalCreateRelInfo(std::shared_ptr rel, + std::vector setItems, binder::expression_vector propertiesToReturn) + : rel{std::move(rel)}, setItems{std::move(setItems)}, propertiesToReturn{ + std::move(propertiesToReturn)} {} LogicalCreateRelInfo(const LogicalCreateRelInfo& other) - : rel{other.rel}, setItems{other.setItems} {} + : rel{other.rel}, setItems{other.setItems}, propertiesToReturn{other.propertiesToReturn} {} inline std::unique_ptr copy() const { return std::make_unique(*this); @@ -76,8 +82,8 @@ class LogicalCreateRel : public LogicalOperator { : LogicalOperator{LogicalOperatorType::CREATE_REL, std::move(child)}, infos{std::move( infos)} {} - inline void computeFactorizedSchema() final { copyChildSchema(0); } - inline void computeFlatSchema() final { copyChildSchema(0); } + void computeFactorizedSchema() final; + void computeFlatSchema() final; std::string getExpressionsForPrinting() const final; diff --git a/src/include/planner/query_planner.h b/src/include/planner/query_planner.h index e3fbedc3a6..15384e1103 100644 --- a/src/include/planner/query_planner.h +++ b/src/include/planner/query_planner.h @@ -165,7 +165,7 @@ class QueryPlanner { // Append scan operators void appendScanNodeID(std::shared_ptr& node, LogicalPlan& plan); - void appendScanNodePropIfNecessary(const expression_vector& propertyExpressions, + void appendScanNodeProperties(const expression_vector& propertyExpressions, std::shared_ptr node, LogicalPlan& plan); // Append extend operators @@ -219,8 +219,7 @@ class QueryPlanner { static std::vector> getInitialEmptyPlans(); - expression_vector getPropertiesForNode(NodeExpression& node); - expression_vector getPropertiesForRel(RelExpression& rel); + expression_vector getProperties(const binder::Expression& nodeOrRel); std::unique_ptr enterContext( binder::expression_vector nodeIDsToScanFromInnerAndOuter); diff --git a/src/include/processor/operator/persistent/insert_executor.h b/src/include/processor/operator/persistent/insert_executor.h index 4140e5cb16..9377f18630 100644 --- a/src/include/processor/operator/persistent/insert_executor.h +++ b/src/include/processor/operator/persistent/insert_executor.h @@ -42,9 +42,11 @@ class RelInsertExecutor { public: RelInsertExecutor(storage::RelsStatistics& relsStatistics, storage::RelTable* table, const DataPos& srcNodePos, const DataPos& dstNodePos, + std::vector lhsVectorPositions, std::vector> evaluators) : relsStatistics{relsStatistics}, table{table}, srcNodePos{srcNodePos}, - dstNodePos{dstNodePos}, evaluators{std::move(evaluators)} {} + dstNodePos{dstNodePos}, lhsVectorPositions{std::move(lhsVectorPositions)}, + evaluators{std::move(evaluators)} {} RelInsertExecutor(const RelInsertExecutor& other); void init(ResultSet* resultSet, ExecutionContext* context); @@ -63,11 +65,13 @@ class RelInsertExecutor { storage::RelTable* table; DataPos srcNodePos; DataPos dstNodePos; + std::vector lhsVectorPositions; std::vector> evaluators; common::ValueVector* srcNodeIDVector = nullptr; common::ValueVector* dstNodeIDVector = nullptr; - std::vector propertyVectors; + std::vector lhsVectors; + std::vector rhsVectors; }; } // namespace processor diff --git a/src/include/processor/plan_mapper.h b/src/include/processor/plan_mapper.h index 0be394c65e..369d40cb03 100644 --- a/src/include/processor/plan_mapper.h +++ b/src/include/processor/plan_mapper.h @@ -113,7 +113,8 @@ class PlanMapper { storage::RelsStore* relsStore, planner::LogicalCreateNodeInfo* info, const planner::Schema& inSchema, const planner::Schema& outSchema); std::unique_ptr getRelInsertExecutor(storage::RelsStore* relsStore, - planner::LogicalCreateRelInfo* info, const planner::Schema& inSchema); + planner::LogicalCreateRelInfo* info, const planner::Schema& inSchema, + const planner::Schema& outSchema); std::unique_ptr getNodeSetExecutor(storage::NodesStore* store, planner::LogicalSetPropertyInfo* info, const planner::Schema& inSchema); std::unique_ptr getRelSetExecutor(storage::RelsStore* store, diff --git a/src/planner/operator/persistent/logical_create.cpp b/src/planner/operator/persistent/logical_create.cpp index 7d852b09ee..4ebc327e52 100644 --- a/src/planner/operator/persistent/logical_create.cpp +++ b/src/planner/operator/persistent/logical_create.cpp @@ -30,14 +30,20 @@ void LogicalCreateNode::computeFactorizedSchema() { for (auto& info : infos) { auto groupPos = schema->createGroup(); schema->setGroupAsSingleState(groupPos); - schema->insertToGroupAndScope(info->node->getInternalIDProperty(), groupPos); + for (auto& property : info->propertiesToReturn) { + schema->insertToGroupAndScope(property, groupPos); + } + schema->insertToGroupAndScopeMayRepeat(info->node->getInternalIDProperty(), groupPos); } } void LogicalCreateNode::computeFlatSchema() { copyChildSchema(0); for (auto& info : infos) { - schema->insertToGroupAndScope(info->node->getInternalIDProperty(), 0); + for (auto& property : info->propertiesToReturn) { + schema->insertToGroupAndScope(property, 0); + } + schema->insertToGroupAndScopeMayRepeat(info->node->getInternalIDProperty(), 0); } } @@ -57,6 +63,26 @@ f_group_pos_set LogicalCreateNode::getGroupsPosToFlatten() { childSchema->getGroupsPosInScope(), childSchema); } +void LogicalCreateRel::computeFactorizedSchema() { + copyChildSchema(0); + for (auto& info : infos) { + auto groupPos = schema->createGroup(); + schema->setGroupAsSingleState(groupPos); + for (auto& property : info->propertiesToReturn) { + schema->insertToGroupAndScope(property, groupPos); + } + } +} + +void LogicalCreateRel::computeFlatSchema() { + copyChildSchema(0); + for (auto& info : infos) { + for (auto& property : info->propertiesToReturn) { + schema->insertToGroupAndScope(property, 0); + } + } +} + std::string LogicalCreateRel::getExpressionsForPrinting() const { std::string result; for (auto& info : infos) { diff --git a/src/planner/plan/append_create.cpp b/src/planner/plan/append_create.cpp index 3e0f0083f3..44189ccfc9 100644 --- a/src/planner/plan/append_create.cpp +++ b/src/planner/plan/append_create.cpp @@ -11,14 +11,18 @@ namespace planner { std::unique_ptr QueryPlanner::createLogicalCreateNodeInfo( BoundCreateInfo* boundCreateInfo) { auto node = std::static_pointer_cast(boundCreateInfo->nodeOrRel); + auto propertiesToReturn = getProperties(*node); auto extraInfo = (ExtraCreateNodeInfo*)boundCreateInfo->extraInfo.get(); - return std::make_unique(node, extraInfo->primaryKey); + return std::make_unique( + node, extraInfo->primaryKey, std::move(propertiesToReturn)); } std::unique_ptr QueryPlanner::createLogicalCreateRelInfo( BoundCreateInfo* boundCreateInfo) { auto rel = std::static_pointer_cast(boundCreateInfo->nodeOrRel); - return std::make_unique(rel, boundCreateInfo->setItems); + auto propertiesToReturn = getProperties(*rel); + return std::make_unique( + rel, boundCreateInfo->setItems, std::move(propertiesToReturn)); } std::vector> QueryPlanner::createLogicalSetPropertyInfo( diff --git a/src/planner/plan/append_extend.cpp b/src/planner/plan/append_extend.cpp index 64793eb597..47a8c2d426 100644 --- a/src/planner/plan/append_extend.cpp +++ b/src/planner/plan/append_extend.cpp @@ -176,7 +176,7 @@ void QueryPlanner::createPathNodePropertyScanPlan( for (auto& property : recursiveNode->getPropertyExpressions()) { properties.push_back(property->copy()); } - appendScanNodePropIfNecessary(properties, recursiveNode, plan); + appendScanNodeProperties(properties, recursiveNode, plan); auto expressionsToProject = properties; expressionsToProject.push_back(recursiveNode->getInternalIDProperty()); expressionsToProject.push_back(recursiveNode->getLabelExpression()); diff --git a/src/planner/plan/append_scan_node.cpp b/src/planner/plan/append_scan_node.cpp index 3c4b8814d1..ec1841247d 100644 --- a/src/planner/plan/append_scan_node.cpp +++ b/src/planner/plan/append_scan_node.cpp @@ -14,7 +14,7 @@ void QueryPlanner::appendScanNodeID(std::shared_ptr& node, Logic plan.setLastOperator(std::move(scan)); } -void QueryPlanner::appendScanNodePropIfNecessary(const expression_vector& propertyExpressions, +void QueryPlanner::appendScanNodeProperties(const expression_vector& propertyExpressions, std::shared_ptr node, LogicalPlan& plan) { expression_vector propertyExpressionToScan; for (auto& propertyExpression : propertyExpressions) { diff --git a/src/planner/plan/plan_join_order.cpp b/src/planner/plan/plan_join_order.cpp index 095221d39c..a06e80bb12 100644 --- a/src/planner/plan/plan_join_order.cpp +++ b/src/planner/plan/plan_join_order.cpp @@ -146,8 +146,8 @@ void QueryPlanner::planNodeScan(uint32_t nodePos) { // query, we only scan internal ID of "a". if (!context->nodeToScanFromInnerAndOuter(node.get())) { appendScanNodeID(node, *plan); - auto properties = getPropertiesForNode(*node); - appendScanNodePropIfNecessary(properties, node, *plan); + auto properties = getProperties(*node); + appendScanNodeProperties(properties, node, *plan); auto predicates = getNewlyMatchedExpressions( context->getEmptySubqueryGraph(), newSubgraph, context->getWhereExpressions()); appendFilters(predicates, *plan); @@ -188,7 +188,7 @@ void QueryPlanner::appendExtendAndFilter(std::shared_ptr boundNo ExtendDirection direction, const expression_vector& predicates, LogicalPlan& plan) { switch (rel->getRelType()) { case common::QueryRelType::NON_RECURSIVE: { - auto properties = getPropertiesForRel(*rel); + auto properties = getProperties(*rel); appendNonRecursiveExtend(boundNode, nbrNode, rel, direction, properties, plan); } break; case common::QueryRelType::VARIABLE_LENGTH: diff --git a/src/planner/query_planner.cpp b/src/planner/query_planner.cpp index 899a5a8500..cf17631cb9 100644 --- a/src/planner/query_planner.cpp +++ b/src/planner/query_planner.cpp @@ -94,22 +94,15 @@ std::vector> QueryPlanner::getInitialEmptyPlans() { return plans; } -expression_vector QueryPlanner::getPropertiesForNode(NodeExpression& node) { - expression_vector result; - for (auto& expression : propertiesToScan) { - auto property = (PropertyExpression*)expression.get(); - if (property->getVariableName() == node.getUniqueName()) { - result.push_back(expression); - } +expression_vector QueryPlanner::getProperties(const binder::Expression& nodeOrRel) { + auto typeID = nodeOrRel.getDataType().getLogicalTypeID(); + if (typeID != common::LogicalTypeID::NODE && typeID != common::LogicalTypeID::REL) { + throw common::NotImplementedException("QueryPlanner::getProperties"); } - return result; -} - -expression_vector QueryPlanner::getPropertiesForRel(RelExpression& rel) { expression_vector result; for (auto& expression : propertiesToScan) { auto property = (PropertyExpression*)expression.get(); - if (property->getVariableName() == rel.getUniqueName()) { + if (property->getVariableName() == nodeOrRel.getUniqueName()) { result.push_back(expression); } } diff --git a/src/processor/map/map_create.cpp b/src/processor/map/map_create.cpp index 25990c80b3..3b9291f57a 100644 --- a/src/processor/map/map_create.cpp +++ b/src/processor/map/map_create.cpp @@ -48,7 +48,8 @@ std::unique_ptr PlanMapper::mapCreateNode(LogicalOperator* log } std::unique_ptr PlanMapper::getRelInsertExecutor(storage::RelsStore* relsStore, - planner::LogicalCreateRelInfo* info, const planner::Schema& inSchema) { + planner::LogicalCreateRelInfo* info, const planner::Schema& inSchema, + const planner::Schema& outSchema) { auto rel = info->rel; auto relTableID = rel->getSingleTableID(); auto table = relsStore->getRelTable(relTableID); @@ -56,22 +57,29 @@ std::unique_ptr PlanMapper::getRelInsertExecutor(storage::Rel auto dstNode = rel->getDstNode(); auto srcNodePos = DataPos(inSchema.getExpressionPos(*srcNode->getInternalIDProperty())); auto dstNodePos = DataPos(inSchema.getExpressionPos(*dstNode->getInternalIDProperty())); + std::vector lhsVectorPositions; std::vector> evaluators; for (auto& [lhs, rhs] : info->setItems) { + if (outSchema.isExpressionInScope(*lhs)) { + lhsVectorPositions.emplace_back(outSchema.getExpressionPos(*lhs)); + } else { + lhsVectorPositions.emplace_back(INVALID_DATA_CHUNK_POS, INVALID_DATA_CHUNK_POS); + } evaluators.push_back(expressionMapper.mapExpression(rhs, inSchema)); } - return std::make_unique( - relsStore->getRelsStatistics(), table, srcNodePos, dstNodePos, std::move(evaluators)); + return std::make_unique(relsStore->getRelsStatistics(), table, srcNodePos, + dstNodePos, std::move(lhsVectorPositions), std::move(evaluators)); } std::unique_ptr PlanMapper::mapCreateRel(LogicalOperator* logicalOperator) { auto logicalCreateRel = (LogicalCreateRel*)logicalOperator; auto inSchema = logicalCreateRel->getChild(0)->getSchema(); + auto outSchema = logicalCreateRel->getSchema(); auto prevOperator = mapOperator(logicalOperator->getChild(0).get()); std::vector> executors; for (auto& info : logicalCreateRel->getInfosRef()) { - executors.push_back( - getRelInsertExecutor(&storageManager.getRelsStore(), info.get(), *inSchema)); + executors.push_back(getRelInsertExecutor( + &storageManager.getRelsStore(), info.get(), *inSchema, *outSchema)); } return std::make_unique(std::move(executors), std::move(prevOperator), getOperatorID(), logicalCreateRel->getExpressionsForPrinting()); diff --git a/src/processor/map/map_merge.cpp b/src/processor/map/map_merge.cpp index 15db574f6a..7c0af9916c 100644 --- a/src/processor/map/map_merge.cpp +++ b/src/processor/map/map_merge.cpp @@ -26,7 +26,8 @@ std::unique_ptr PlanMapper::mapMerge(planner::LogicalOperator* } std::vector> relInsertExecutors; for (auto& info : logicalMerge->getCreateRelInfosRef()) { - relInsertExecutors.push_back(getRelInsertExecutor(relsStore, info.get(), *inSchema)); + relInsertExecutors.push_back( + getRelInsertExecutor(relsStore, info.get(), *inSchema, *outSchema)); } std::vector> onCreateNodeSetExecutors; for (auto& info : logicalMerge->getOnCreateSetNodeInfosRef()) { diff --git a/src/processor/operator/persistent/insert_executor.cpp b/src/processor/operator/persistent/insert_executor.cpp index c6917c2d76..a75d1d2c3e 100644 --- a/src/processor/operator/persistent/insert_executor.cpp +++ b/src/processor/operator/persistent/insert_executor.cpp @@ -1,5 +1,7 @@ #include "processor/operator/persistent/insert_executor.h" +using namespace kuzu::common; + namespace kuzu { namespace processor { @@ -45,7 +47,7 @@ std::vector> NodeInsertExecutor::copy( RelInsertExecutor::RelInsertExecutor(const RelInsertExecutor& other) : relsStatistics{other.relsStatistics}, table{other.table}, srcNodePos{other.srcNodePos}, - dstNodePos{other.dstNodePos} { + dstNodePos{other.dstNodePos}, lhsVectorPositions{other.lhsVectorPositions} { for (auto& evaluator : other.evaluators) { evaluators.push_back(evaluator->clone()); } @@ -54,21 +56,45 @@ RelInsertExecutor::RelInsertExecutor(const RelInsertExecutor& other) void RelInsertExecutor::init(ResultSet* resultSet, ExecutionContext* context) { srcNodeIDVector = resultSet->getValueVector(srcNodePos).get(); dstNodeIDVector = resultSet->getValueVector(dstNodePos).get(); + for (auto& pos : lhsVectorPositions) { + if (pos.dataChunkPos != INVALID_DATA_CHUNK_POS) { + lhsVectors.push_back(resultSet->getValueVector(pos).get()); + } else { + lhsVectors.push_back(nullptr); + } + } for (auto& evaluator : evaluators) { evaluator->init(*resultSet, context->memoryManager); - propertyVectors.push_back(evaluator->resultVector.get()); + rhsVectors.push_back(evaluator->resultVector.get()); } } void RelInsertExecutor::insert(transaction::Transaction* tx) { auto offset = relsStatistics.getNextRelOffset(tx, table->getRelTableID()); - propertyVectors[0]->setValue(0, offset); - propertyVectors[0]->setNull(0, false); + rhsVectors[0]->setValue(0, offset); + rhsVectors[0]->setNull(0, false); for (auto i = 1; i < evaluators.size(); ++i) { evaluators[i]->evaluate(); } - table->insertRel(srcNodeIDVector, dstNodeIDVector, propertyVectors); + table->insertRel(srcNodeIDVector, dstNodeIDVector, rhsVectors); relsStatistics.updateNumRelsByValue(table->getRelTableID(), 1); + for (auto i = 0u; i < lhsVectors.size(); ++i) { + auto lhsVector = lhsVectors[i]; + auto rhsVector = rhsVectors[i]; + if (lhsVector == nullptr) { + continue; + } + assert(lhsVector->state->selVector->selectedSize == 1 && + rhsVector->state->selVector->selectedSize == 1); + auto lhsPos = lhsVector->state->selVector->selectedPositions[0]; + auto rhsPos = rhsVector->state->selVector->selectedPositions[0]; + if (rhsVector->isNull(rhsPos)) { + lhsVector->setNull(lhsPos, true); + } else { + lhsVector->setNull(lhsPos, false); + lhsVector->copyFromVectorData(lhsPos, rhsVector, rhsPos); + } + } } std::vector> RelInsertExecutor::copy( diff --git a/test/test_files/tinysnb/update_node/create_read.test b/test/test_files/tinysnb/update_node/create_read.test new file mode 100644 index 0000000000..5fa8f71950 --- /dev/null +++ b/test/test_files/tinysnb/update_node/create_read.test @@ -0,0 +1,19 @@ +-GROUP TinySnbUpdateTest +-DATASET CSV tinysnb + +-- + +-CASE CreateNodeRead1 +-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 +-STATEMENT CREATE (a:organisation {ID:0, name:'test'}) RETURN a, a.history; +---- 1 +{_ID: 1:3, _LABEL: organisation, ID: 0, name: test}| + +-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 +---- 2 +0|Alice|35|11|new|70 +2|Bob|30|13|new|60 diff --git a/test/test_files/tinysnb/update_rel/create_read.test b/test/test_files/tinysnb/update_rel/create_read.test new file mode 100644 index 0000000000..c9fa210333 --- /dev/null +++ b/test/test_files/tinysnb/update_rel/create_read.test @@ -0,0 +1,33 @@ +-GROUP TinySnbUpdateTest +-DATASET CSV tinysnb + +-- + +-CASE CreateRelRead1 +-SKIP +-STATEMENT MATCH (a:person), (b:person) WHERE a.ID = 0 AND b.ID = 2 CREATE (a)-[e:knows {date:date('2023-03-03')}]->(b) RETURN id(e), e.date; +---- 1 +0:14|2023-03-03 +-STATEMENT MATCH (a:person), (b:person) WHERE a.ID = 0 AND b.ID = 2 CREATE (a)-[e:knows {date:date('2023-04-04')}]->(b) RETURN e; +---- 1 +(0:0)-{_LABEL: knows, _ID: 0:15, date: 2023-04-04}->(0:1) +-STATEMENT MATCH (a:person)-[e:knows]->(b:person) WHERE a.ID = 0 AND b.ID = 2 RETURN e; +---- 3 +(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]}->(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) + +-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; +---- 3 +(0:0)-{_LABEL: knows, _ID: 0:14, date: 2023-04-04}->(0:1) +(0:0)-{_LABEL: knows, _ID: 0:15, date: 2023-04-04}->(0:2) +(0:0)-{_LABEL: knows, _ID: 0:16, date: 2023-04-04}->(0:3) +-STATEMENT MATCH (a:person)-[e:knows]->(b:person) WHERE a.ID = 0 RETURN e; +---- 6 +(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]}->(0:1) +(0:0)-{_LABEL: knows, _ID: 3:1, date: 2021-06-30, meetTime: 1946-08-25 19:07:22, validInterval: 20 years 30 days 48:00:00, comments: [njnojppo9u0jkmf,fjiojioh9h9h89hph]}->(0:2) +(0:0)-{_LABEL: knows, _ID: 3:2, date: 2021-06-30, meetTime: 2012-12-11 20:07:22, validInterval: 10 days, comments: [ioji232,jifhe8w99u43434]}->(0:3) +(0:0)-{_LABEL: knows, _ID: 3:14, date: 2023-04-04}->(0:1) +(0:0)-{_LABEL: knows, _ID: 3:15, date: 2023-04-04}->(0:2) +(0:0)-{_LABEL: knows, _ID: 3:16, date: 2023-04-04}->(0:3)