From 6237df39286009fa0cdf6b34911c1820fc403bda Mon Sep 17 00:00:00 2001 From: Guodong Jin Date: Wed, 15 Nov 2023 22:05:48 -0500 Subject: [PATCH] fix rel updates --- src/common/CMakeLists.txt | 1 - src/common/rel_direction.cpp | 22 -- src/include/common/enums/rel_direction.h | 10 - .../operator/persistent/set_executor.h | 26 +- .../storage/local_storage/local_node_table.h | 64 +++++ .../storage/local_storage/local_rel_table.h | 109 +++++++++ .../storage/local_storage/local_storage.h | 12 +- .../storage/local_storage/local_table.h | 86 +++---- .../storage/stats/property_statistics.h | 2 +- src/include/storage/store/column.h | 20 +- src/include/storage/store/node_table.h | 2 +- src/include/storage/store/rel_table.h | 6 +- src/include/storage/store/rel_table_data.h | 28 ++- src/include/storage/store/table.h | 3 +- src/include/storage/store/var_list_column.h | 2 +- src/processor/map/map_set.cpp | 18 +- src/processor/operator/persistent/merge.cpp | 4 +- src/processor/operator/persistent/set.cpp | 2 +- .../operator/persistent/set_executor.cpp | 18 +- src/storage/local_storage/CMakeLists.txt | 2 + .../local_storage/local_node_table.cpp | 165 +++++++++++++ src/storage/local_storage/local_rel_table.cpp | 231 ++++++++++++++++++ src/storage/local_storage/local_storage.cpp | 20 +- src/storage/local_storage/local_table.cpp | 196 +++------------ src/storage/stats/property_statistics.cpp | 4 +- src/storage/storage_manager.cpp | 2 +- src/storage/store/column.cpp | 54 ++-- src/storage/store/node_table.cpp | 4 +- src/storage/store/node_table_data.cpp | 34 ++- src/storage/store/rel_table.cpp | 15 +- src/storage/store/rel_table_data.cpp | 131 +++++++++- src/storage/store/string_column.cpp | 3 + src/storage/store/struct_column_chunk.cpp | 3 +- test/test_files/demo_db/demo_db_set_copy.test | 4 +- .../update_each_element_of_large_list.test | 1 - .../update_each_element_of_small_list.test | 1 - .../update_rel/update_int_prop.test | 1 - .../update_many_to_one_rel_table.test | 1 - .../update_rel/update_rels_two_hop.test | 1 - .../update_rel/update_str_prop.test | 1 - .../update_rel/set_read_tinysnb.test | 2 +- 41 files changed, 945 insertions(+), 366 deletions(-) delete mode 100644 src/common/rel_direction.cpp create mode 100644 src/include/storage/local_storage/local_node_table.h create mode 100644 src/include/storage/local_storage/local_rel_table.h create mode 100644 src/storage/local_storage/local_node_table.cpp create mode 100644 src/storage/local_storage/local_rel_table.cpp diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 43936b4b7b1..81ebd655375 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -9,7 +9,6 @@ add_subdirectory(vector) add_library(kuzu_common OBJECT - rel_direction.cpp expression_type.cpp file_utils.cpp in_mem_overflow_buffer.cpp diff --git a/src/common/rel_direction.cpp b/src/common/rel_direction.cpp deleted file mode 100644 index 19883823507..00000000000 --- a/src/common/rel_direction.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "common/enums/rel_direction.h" - -#include "common/assert.h" - -namespace kuzu { -namespace common { - -std::string RelDataDirectionUtils::relDataDirectionToString(RelDataDirection direction) { - switch (direction) { - case RelDataDirection::FWD: { - return "forward"; - } - case RelDataDirection::BWD: { - return "backward"; - } - default: - KU_UNREACHABLE; - } -} - -} // namespace common -} // namespace kuzu diff --git a/src/include/common/enums/rel_direction.h b/src/include/common/enums/rel_direction.h index 84543b2bfa9..50b5d551e8f 100644 --- a/src/include/common/enums/rel_direction.h +++ b/src/include/common/enums/rel_direction.h @@ -1,21 +1,11 @@ #pragma once #include -#include -#include namespace kuzu { namespace common { enum class RelDataDirection : uint8_t { FWD = 0, BWD = 1 }; -struct RelDataDirectionUtils { - static inline std::vector getRelDataDirections() { - return std::vector{RelDataDirection::FWD, RelDataDirection::BWD}; - } - - static std::string relDataDirectionToString(RelDataDirection direction); -}; - } // namespace common } // namespace kuzu diff --git a/src/include/processor/operator/persistent/set_executor.h b/src/include/processor/operator/persistent/set_executor.h index 517a4e229c2..b6e7a23e7a4 100644 --- a/src/include/processor/operator/persistent/set_executor.h +++ b/src/include/processor/operator/persistent/set_executor.h @@ -94,7 +94,7 @@ class RelSetExecutor { void init(ResultSet* resultSet, ExecutionContext* context); - virtual void set() = 0; + virtual void set(ExecutionContext* context) = 0; virtual std::unique_ptr copy() const = 0; @@ -118,17 +118,17 @@ class RelSetExecutor { class SingleLabelRelSetExecutor : public RelSetExecutor { public: - SingleLabelRelSetExecutor(storage::RelTable* table, common::property_id_t propertyID, + SingleLabelRelSetExecutor(storage::RelTable* table, common::column_id_t columnID, const DataPos& srcNodeIDPos, const DataPos& dstNodeIDPos, const DataPos& relIDPos, const DataPos& lhsVectorPos, std::unique_ptr evaluator) : RelSetExecutor{srcNodeIDPos, dstNodeIDPos, relIDPos, lhsVectorPos, std::move(evaluator)}, - table{table}, propertyID{propertyID} {} + table{table}, columnID{columnID} {} SingleLabelRelSetExecutor(const SingleLabelRelSetExecutor& other) : RelSetExecutor{other.srcNodeIDPos, other.dstNodeIDPos, other.relIDPos, other.lhsVectorPos, other.evaluator->clone()}, - table{other.table}, propertyID{other.propertyID} {} + table{other.table}, columnID{other.columnID} {} - void set() final; + void set(ExecutionContext* context) final; inline std::unique_ptr copy() const final { return std::make_unique(*this); @@ -136,32 +136,32 @@ class SingleLabelRelSetExecutor : public RelSetExecutor { private: storage::RelTable* table; - common::property_id_t propertyID; + common::column_id_t columnID; }; class MultiLabelRelSetExecutor : public RelSetExecutor { public: MultiLabelRelSetExecutor( - std::unordered_map> - tableIDToTableAndPropertyID, + std::unordered_map> + tableIDToTableAndColumnID, const DataPos& srcNodeIDPos, const DataPos& dstNodeIDPos, const DataPos& relIDPos, const DataPos& lhsVectorPos, std::unique_ptr evaluator) : RelSetExecutor{srcNodeIDPos, dstNodeIDPos, relIDPos, lhsVectorPos, std::move(evaluator)}, - tableIDToTableAndPropertyID{std::move(tableIDToTableAndPropertyID)} {} + tableIDToTableAndColumnID{std::move(tableIDToTableAndColumnID)} {} MultiLabelRelSetExecutor(const MultiLabelRelSetExecutor& other) : RelSetExecutor{other.srcNodeIDPos, other.dstNodeIDPos, other.relIDPos, other.lhsVectorPos, other.evaluator->clone()}, - tableIDToTableAndPropertyID{other.tableIDToTableAndPropertyID} {} + tableIDToTableAndColumnID{other.tableIDToTableAndColumnID} {} - void set() final; + void set(ExecutionContext* context) final; inline std::unique_ptr copy() const final { return std::make_unique(*this); } private: - std::unordered_map> - tableIDToTableAndPropertyID; + std::unordered_map> + tableIDToTableAndColumnID; }; } // namespace processor diff --git a/src/include/storage/local_storage/local_node_table.h b/src/include/storage/local_storage/local_node_table.h new file mode 100644 index 00000000000..8faa937f18a --- /dev/null +++ b/src/include/storage/local_storage/local_node_table.h @@ -0,0 +1,64 @@ +#pragma once + +#include "local_table.h" + +namespace kuzu { +namespace storage { + +class LocalNodeNG final : public LocalNodeGroup { +public: + LocalNodeNG(std::vector dataTypes, MemoryManager* mm) + : LocalNodeGroup{dataTypes, mm} { + insertInfo.resize(dataTypes.size()); + updateInfo.resize(dataTypes.size()); + } + + void scan(common::ValueVector* nodeIDVector, const std::vector& columnIDs, + const std::vector& outputVectors); + void lookup(common::offset_t nodeOffset, common::column_id_t columnID, + common::ValueVector* outputVector, common::sel_t posInOutputVector); + void insert(common::ValueVector* nodeIDVector, + const std::vector& propertyVectors); + void update(common::ValueVector* nodeIDVector, common::column_id_t columnID, + common::ValueVector* propertyVector); + void delete_(common::ValueVector* nodeIDVector); + + common::row_idx_t getRowIdx(common::column_id_t columnID, common::offset_t nodeOffset); + + inline const offset_to_row_idx_t& getInsertInfoRef(common::column_id_t columnID) { + KU_ASSERT(columnID < insertInfo.size()); + return insertInfo[columnID]; + } + inline const offset_to_row_idx_t& getUpdateInfoRef(common::column_id_t columnID) { + KU_ASSERT(columnID < updateInfo.size()); + return updateInfo[columnID]; + } + +private: + std::vector insertInfo; + std::vector updateInfo; +}; + +class LocalNodeTableData final : public LocalTableData { +public: + LocalNodeTableData(std::vector dataTypes, MemoryManager* mm, + common::ColumnDataFormat dataFormat) + : LocalTableData{dataTypes, mm, dataFormat} {} + + void scan(common::ValueVector* nodeIDVector, const std::vector& columnIDs, + const std::vector& outputVectors); + void lookup(common::ValueVector* nodeIDVector, + const std::vector& columnIDs, + const std::vector& outputVectors); + void insert(common::ValueVector* nodeIDVector, + const std::vector& propertyVectors); + void update(common::ValueVector* nodeIDVector, common::column_id_t columnID, + common::ValueVector* propertyVector); + void delete_(common::ValueVector* nodeIDVector); + +private: + LocalNodeGroup* getOrCreateLocalNodeGroup(common::ValueVector* nodeIDVector); +}; + +} // namespace storage +} // namespace kuzu diff --git a/src/include/storage/local_storage/local_rel_table.h b/src/include/storage/local_storage/local_rel_table.h new file mode 100644 index 00000000000..a2c86bf4ff7 --- /dev/null +++ b/src/include/storage/local_storage/local_rel_table.h @@ -0,0 +1,109 @@ +#pragma once + +#include "common/column_data_format.h" +#include "storage/local_storage/local_table.h" + +namespace kuzu { +namespace storage { + +static constexpr common::column_id_t REL_ID_COLUMN_ID = 0; + +struct RelNGInfo { + virtual ~RelNGInfo() = default; + + virtual void insert(common::offset_t srcNodeOffset, common::offset_t relOffset, + common::row_idx_t adjNodeRowIdx, + const std::vector& propertyNodesRowIdx) = 0; + virtual void update(common::offset_t srcNodeOffset, common::offset_t relOffset, + common::column_id_t columnID, common::row_idx_t rowIdx) = 0; + virtual bool delete_(common::offset_t srcNodeOffset, common::offset_t relOffset) = 0; + +protected: + inline static bool contains( + const std::unordered_set& set, common::offset_t value) { + return set.find(value) != set.end(); + } +}; + +// Info of node groups with regular chunks for rel tables. +struct RegularRelNGInfo final : public RelNGInfo { + // Note that adj chunk cannot be directly updated. It can only be inserted or deleted. + offset_to_row_idx_t adjInsertInfo; // insert info for adj chunk. + std::vector insertInfoPerChunk; // insert info for property chunks. + std::vector updateInfoPerChunk; // insert info for property chunks. + offset_set_t deleteInfo; // the set of deleted node offsets. + + RegularRelNGInfo(common::column_id_t numChunks) { + insertInfoPerChunk.resize(numChunks); + updateInfoPerChunk.resize(numChunks); + } + + void insert(common::offset_t srcNodeOffset, common::offset_t relOffset, + common::row_idx_t adjNodeRowIdx, const std::vector& propertyNodesRowIdx); + void update(common::offset_t srcNodeOffset, common::offset_t relOffset, + common::column_id_t columnID, common::row_idx_t rowIdx); + bool delete_(common::offset_t srcNodeOffset, common::offset_t relOffset); +}; + +// Info of node groups with CSR chunks for rel tables. +struct CSRRelNGInfo final : public RelNGInfo { + offset_to_offset_to_row_idx_t adjInsertInfo; + std::vector insertInfoPerChunk; + std::vector updateInfoPerChunk; + offset_to_offset_set_t deleteInfo; + + CSRRelNGInfo(common::column_id_t numChunks) { + insertInfoPerChunk.resize(numChunks); + updateInfoPerChunk.resize(numChunks); + } + + void insert(common::offset_t srcNodeOffset, common::offset_t relOffset, + common::row_idx_t adjNodeRowIdx, const std::vector& propertyNodesRowIdx); + void update(common::offset_t srcNodeOffset, common::offset_t relOffset, + common::column_id_t columnID, common::row_idx_t rowIdx); + bool delete_(common::offset_t srcNodeOffset, common::offset_t relOffset); +}; + +class LocalRelNG final : public LocalNodeGroup { +public: + LocalRelNG(common::ColumnDataFormat dataFormat, std::vector dataTypes, + MemoryManager* mm); + + void insert(common::ValueVector* srcNodeIDVector, common::ValueVector* dstNodeIDVector, + const std::vector& propertyVectors); + void update(common::ValueVector* srcNodeIDVector, common::ValueVector* relIDVector, + common::column_id_t columnID, common::ValueVector* propertyVector); + void delete_(common::ValueVector* srcNodeIDVector, common::ValueVector* relIDVector); + + inline LocalVectorCollection* getAdjChunk() { return adjChunk.get(); } + inline LocalVectorCollection* getPropertyChunk(common::column_id_t columnID) { + KU_ASSERT(columnID < chunks.size()); + return chunks[columnID].get(); + } + inline RelNGInfo* getRelNGInfo() { return relNGInfo.get(); } + +private: + std::unique_ptr adjChunk; + std::unique_ptr relNGInfo; +}; + +class LocalRelTableData final : public LocalTableData { + friend class RelTableData; + +public: + LocalRelTableData(std::vector dataTypes, MemoryManager* mm, + common::ColumnDataFormat dataFormat) + : LocalTableData{dataTypes, mm, dataFormat} {} + + void insert(common::ValueVector* srcNodeIDVector, common::ValueVector* dstNodeIDVector, + const std::vector& propertyVectors); + void update(common::ValueVector* srcNodeIDVector, common::ValueVector* relIDVector, + common::column_id_t columnID, common::ValueVector* propertyVector); + void delete_(common::ValueVector* srcNodeIDVector, common::ValueVector* relIDVector); + +private: + LocalNodeGroup* getOrCreateLocalNodeGroup(common::ValueVector* nodeIDVector); +}; + +} // namespace storage +} // namespace kuzu \ No newline at end of file diff --git a/src/include/storage/local_storage/local_storage.h b/src/include/storage/local_storage/local_storage.h index 70c786073f9..a87f4cf59e8 100644 --- a/src/include/storage/local_storage/local_storage.h +++ b/src/include/storage/local_storage/local_storage.h @@ -19,10 +19,14 @@ class LocalStorage { LocalStorage(storage::MemoryManager* mm); // This function will create the local table data if not exists. - LocalTableData* getOrCreateLocalTableData( - common::table_id_t tableID, const std::vector>& columns); - // This function will return nullptr if the local table data does not exist. - LocalTableData* getLocalTableData(common::table_id_t tableID); + LocalTableData* getOrCreateLocalTableData(common::table_id_t tableID, + const std::vector>& columns, + common::TableType tableType = common::TableType::NODE, + common::ColumnDataFormat dataFormat = common::ColumnDataFormat::REGULAR, + common::vector_idx_t dataIdx = 0); + LocalTable* getLocalTable(common::table_id_t tableID); + // This function will return nullptr if the local table does not exist. + LocalTableData* getLocalTableData(common::table_id_t tableID, common::vector_idx_t dataIdx = 0); std::unordered_set getTableIDsWithUpdates(); private: diff --git a/src/include/storage/local_storage/local_table.h b/src/include/storage/local_storage/local_table.h index ea62f8e9b70..bdff88340c2 100644 --- a/src/include/storage/local_storage/local_table.h +++ b/src/include/storage/local_storage/local_table.h @@ -2,6 +2,7 @@ #include +#include "common/column_data_format.h" #include "common/enums/table_type.h" #include "common/vector/value_vector.h" @@ -9,6 +10,11 @@ namespace kuzu { namespace storage { class TableData; +using offset_to_row_idx_t = std::map; +using offset_set_t = std::unordered_set; +using offset_to_offset_to_row_idx_t = std::map; +using offset_to_offset_set_t = std::map>; + // TODO(Guodong): Instead of using ValueVector, we should switch to ColumnChunk. // This class is used to store a chunk of local changes to a column in a node group. // Values are stored inside `vector`. @@ -16,7 +22,7 @@ class LocalVector { public: LocalVector(const common::LogicalType& dataType, MemoryManager* mm) : numValues{0} { vector = std::make_unique(dataType, mm); - vector->state = std::make_unique(); + vector->setState(std::make_shared()); vector->state->selVector->resetSelectorToValuePosBufferWithSize(1); } @@ -43,14 +49,6 @@ class LocalVectorCollection { void read(common::row_idx_t rowIdx, common::ValueVector* outputVector, common::sel_t posInOutputVector); - void insert(common::ValueVector* nodeIDVector, common::ValueVector* propertyVectors); - void update(common::ValueVector* nodeIDVector, common::ValueVector* propertyVector); - inline void delete_(common::offset_t nodeOffset) { - insertInfo.erase(nodeOffset); - updateInfo.erase(nodeOffset); - } - inline std::map& getInsertInfoRef() { return insertInfo; } - inline std::map& getUpdateInfoRef() { return updateInfo; } inline uint64_t getNumRows() { return numRows; } inline LocalVector* getLocalVector(common::row_idx_t rowIdx) { auto vectorIdx = rowIdx >> common::DEFAULT_VECTOR_CAPACITY_LOG_2; @@ -58,22 +56,16 @@ class LocalVectorCollection { return vectors[vectorIdx].get(); } - common::row_idx_t getRowIdx(common::offset_t nodeOffset); + common::row_idx_t append(common::ValueVector* vector); private: void prepareAppend(); - void append(common::ValueVector* vector); private: const common::LogicalType* dataType; MemoryManager* mm; std::vector> vectors; common::row_idx_t numRows; - // TODO: Do we need to differentiate between insert and update? - // New nodes to be inserted into the persistent storage. - std::map insertInfo; - // Nodes in the persistent storage to be updated. - std::map updateInfo; }; class LocalNodeGroup { @@ -81,58 +73,34 @@ class LocalNodeGroup { public: LocalNodeGroup(std::vector dataTypes, MemoryManager* mm); - - void scan(common::ValueVector* nodeIDVector, const std::vector& columnIDs, - const std::vector& outputVectors); - void lookup(common::offset_t nodeOffset, common::column_id_t columnID, - common::ValueVector* outputVector, common::sel_t posInOutputVector); - void insert(common::ValueVector* nodeIDVector, - const std::vector& propertyVectors); - void update(common::ValueVector* nodeIDVector, common::column_id_t columnID, - common::ValueVector* propertyVector); - void delete_(common::ValueVector* nodeIDVector); + virtual ~LocalNodeGroup() = default; inline LocalVectorCollection* getLocalColumnChunk(common::column_id_t columnID) { - return columns[columnID].get(); - } - -private: - inline common::row_idx_t getRowIdx(common::column_id_t columnID, common::offset_t nodeOffset) { - KU_ASSERT(columnID < columns.size()); - return columns[columnID]->getRowIdx(nodeOffset); + return chunks[columnID].get(); } -private: - std::vector> columns; +protected: + std::vector> chunks; }; class LocalTableData { friend class NodeTableData; public: - LocalTableData(std::vector dataTypes, MemoryManager* mm) - : dataTypes{std::move(dataTypes)}, mm{mm} {} - - void scan(common::ValueVector* nodeIDVector, const std::vector& columnIDs, - const std::vector& outputVectors); - void lookup(common::ValueVector* nodeIDVector, - const std::vector& columnIDs, - const std::vector& outputVectors); - void insert(common::ValueVector* nodeIDVector, - const std::vector& propertyVectors); - void update(common::ValueVector* nodeIDVector, common::column_id_t columnID, - common::ValueVector* propertyVector); - void delete_(common::ValueVector* nodeIDVector); + LocalTableData(std::vector dataTypes, MemoryManager* mm, + common::ColumnDataFormat dataFormat) + : dataTypes{std::move(dataTypes)}, mm{mm}, dataFormat{dataFormat} {} + virtual ~LocalTableData() = default; inline void clear() { nodeGroups.clear(); } -private: - common::node_group_idx_t initializeLocalNodeGroup(common::ValueVector* nodeIDVector); - common::node_group_idx_t initializeLocalNodeGroup(common::offset_t nodeOffset); +protected: + virtual LocalNodeGroup* getOrCreateLocalNodeGroup(common::ValueVector* nodeIDVector) = 0; -private: +protected: std::vector dataTypes; MemoryManager* mm; + common::ColumnDataFormat dataFormat; std::unordered_map> nodeGroups; }; @@ -142,14 +110,20 @@ class LocalTable { LocalTable(common::table_id_t tableID, common::TableType tableType) : tableID{tableID}, tableType{tableType} {}; - LocalTableData* getOrCreateLocalTableData( - const std::vector>& columns, MemoryManager* mm); - inline LocalTableData* getLocalTableData() { return localTableData.get(); } + LocalTableData* getOrCreateLocalTableData(const std::vector>& columns, + MemoryManager* mm, common::ColumnDataFormat dataFormat = common::ColumnDataFormat::REGULAR, + common::vector_idx_t dataIdx = 0); + inline LocalTableData* getLocalTableData(common::vector_idx_t dataIdx) { + KU_ASSERT(dataIdx < localTableDataCollection.size()); + return localTableDataCollection[dataIdx].get(); + } private: common::table_id_t tableID; common::TableType tableType; - std::unique_ptr localTableData; + // For a node table, it should only contain one LocalTableData, while a rel table should contain + // two, one for each direction. + std::vector> localTableDataCollection; }; } // namespace storage diff --git a/src/include/storage/stats/property_statistics.h b/src/include/storage/stats/property_statistics.h index 9fac2c5758f..a04d25dd620 100644 --- a/src/include/storage/stats/property_statistics.h +++ b/src/include/storage/stats/property_statistics.h @@ -38,7 +38,7 @@ class RWPropertyStats { RWPropertyStats(TablesStatistics* tablesStatistics, common::table_id_t tableID, common::property_id_t propertyID); - // This is used for columns that don't have nullColumn. For example, the serial column. + // This is used for chunks that don't have nullColumn. For example, the serial column. inline static RWPropertyStats empty() { return RWPropertyStats(nullptr, common::INVALID_PROPERTY_ID, common::INVALID_PROPERTY_ID); } diff --git a/src/include/storage/store/column.h b/src/include/storage/store/column.h index dc47b7cee9a..05b5a997671 100644 --- a/src/include/storage/store/column.h +++ b/src/include/storage/store/column.h @@ -65,7 +65,8 @@ class Column { void prepareCommitForChunk(transaction::Transaction* transaction, common::node_group_idx_t nodeGroupIdx, LocalVectorCollection* localColumnChunk, - bool isNewNodeGroup); + const offset_to_row_idx_t& insertInfo, const offset_to_row_idx_t& updateInfo, + const offset_set_t& deleteInfo); virtual void checkpointInMemory(); virtual void rollbackInMemory(); @@ -109,17 +110,20 @@ class Column { private: static bool containsVarList(common::LogicalType& dataType); bool canCommitInPlace(transaction::Transaction* transaction, - common::node_group_idx_t nodeGroupIdx, LocalVectorCollection* localChunk); - void commitLocalChunkInPlace(LocalVectorCollection* localChunk); + common::node_group_idx_t nodeGroupIdx, LocalVectorCollection* localChunk, + const offset_to_row_idx_t& insertInfo, const offset_to_row_idx_t& updateInfo); + void commitLocalChunkInPlace(transaction::Transaction* transaction, + LocalVectorCollection* localChunk, const offset_to_row_idx_t& insertInfo, + const offset_to_row_idx_t& updateInfo, const offset_set_t& deleteInfo); void commitLocalChunkOutOfPlace(transaction::Transaction* transaction, common::node_group_idx_t nodeGroupIdx, LocalVectorCollection* localChunk, - bool isNewNodeGroup); + bool isNewNodeGroup, const offset_to_row_idx_t& insertInfo, + const offset_to_row_idx_t& updateInfo, const offset_set_t& deleteInfo); void applyLocalChunkToColumnChunk(LocalVectorCollection* localChunk, ColumnChunk* columnChunk, - common::offset_t nodeGroupStartOffset, - const std::map& updateInfo); - void applyLocalChunkToColumn(LocalVectorCollection* localChunk, - const std::map& updateInfo); + common::offset_t nodeGroupStartOffset, const offset_to_row_idx_t& info); + void applyLocalChunkToColumn( + LocalVectorCollection* localChunk, const offset_to_row_idx_t& info); // check if val is in range [start, end) static inline bool isInRange(uint64_t val, uint64_t start, uint64_t end) { diff --git a/src/include/storage/store/node_table.h b/src/include/storage/store/node_table.h index 6ff45d6a5ca..d9e5369b888 100644 --- a/src/include/storage/store/node_table.h +++ b/src/include/storage/store/node_table.h @@ -67,7 +67,7 @@ class NodeTable : public Table { common::ValueVector* defaultValueVector) final; inline void dropColumn(common::column_id_t columnID) final { tableData->dropColumn(columnID); } - void prepareCommit(transaction::Transaction* transaction, LocalTableData* localTable) final; + void prepareCommit(transaction::Transaction* transaction, LocalTable* localTable) final; void prepareRollback(LocalTableData* localTable) final; void checkpointInMemory() final; void rollbackInMemory() final; diff --git a/src/include/storage/store/rel_table.h b/src/include/storage/store/rel_table.h index 677cc9e5c0e..2a81f2e5ecb 100644 --- a/src/include/storage/store/rel_table.h +++ b/src/include/storage/store/rel_table.h @@ -28,6 +28,10 @@ class RelTable : public Table { common::ValueVector* inNodeIDVector, const std::vector& outputVectors) final; + void update(transaction::Transaction* transaction, common::column_id_t columnID, + common::ValueVector* srcNodeIDVector, common::ValueVector* dstNodeIDVector, + common::ValueVector* relIDVector, common::ValueVector* propertyVector); + void addColumn(transaction::Transaction* transaction, const catalog::Property& property, common::ValueVector* defaultValueVector) final; inline void dropColumn(common::column_id_t columnID) final { @@ -44,7 +48,7 @@ class RelTable : public Table { bwdRelTableData->append(nodeGroup); } - void prepareCommit(transaction::Transaction* transaction, LocalTableData* localTable) final; + void prepareCommit(transaction::Transaction* transaction, LocalTable* localTable) final; void prepareRollback(LocalTableData* localTable) final; void checkpointInMemory() final; void rollbackInMemory() final; diff --git a/src/include/storage/store/rel_table_data.h b/src/include/storage/store/rel_table_data.h index 2fa35c8afc8..a7b46ed767a 100644 --- a/src/include/storage/store/rel_table_data.h +++ b/src/include/storage/store/rel_table_data.h @@ -33,10 +33,11 @@ struct RelDataReadState : public TableReadState { }; class RelsStoreStats; +class LocalRelTableData; +struct CSRRelNGInfo; +class LocalRelNG; class RelTableData final : public TableData { public: - static constexpr common::column_id_t REL_ID_COLUMN_ID = 0; - RelTableData(BMFileHandle* dataFH, BMFileHandle* metadataFH, BufferManager* bufferManager, WAL* wal, catalog::RelTableSchema* tableSchema, RelsStoreStats* relsStoreStats, common::RelDataDirection direction, bool enableCompression); @@ -55,11 +56,18 @@ class RelTableData final : public TableData { void lookup(transaction::Transaction* transaction, TableReadState& readState, common::ValueVector* inNodeIDVector, const std::vector& outputVectors); + + void update(transaction::Transaction* transaction, common::column_id_t columnID, + common::ValueVector* srcNodeIDVector, common::ValueVector* relIDVector, + common::ValueVector* propertyVector); + void append(NodeGroup* nodeGroup); inline Column* getAdjColumn() const { return adjColumn.get(); } inline common::ColumnDataFormat getDataFormat() const { return dataFormat; } + void prepareLocalTableToCommit( + transaction::Transaction* transaction, LocalTableData* localTable); void checkpointInMemory(); void rollbackInMemory(); @@ -71,17 +79,27 @@ class RelTableData final : public TableData { common::ValueVector* inNodeIDVector, const std::vector& outputVectors); + void prepareCommitForRegularColumns( + transaction::Transaction* transaction, LocalRelTableData* localTableData); + void prepareCommitForCSRColumns( + transaction::Transaction* transaction, LocalRelTableData* localTableData); + + void prepareCommitCSRNGWithoutSliding(transaction::Transaction* transaction, + common::node_group_idx_t nodeGroupIdx, CSRRelNGInfo* relNodeGroupInfo, + ColumnChunk* csrOffsetChunk, ColumnChunk* relIDChunk, LocalRelNG* localNodeGroup); + static inline common::ColumnDataFormat getDataFormatFromSchema( catalog::RelTableSchema* tableSchema, common::RelDataDirection direction) { return tableSchema->isSingleMultiplicityInDirection(direction) ? common::ColumnDataFormat::REGULAR : common::ColumnDataFormat::CSR; } - - void prepareLocalTableToCommit( - transaction::Transaction* transaction, LocalTableData* localTable); + static inline common::vector_idx_t getDataIdxFromDirection(common::RelDataDirection direction) { + return direction == common::RelDataDirection::FWD ? 0 : 1; + } private: + common::RelDataDirection direction; std::unique_ptr adjColumn; std::unique_ptr csrOffsetColumn; }; diff --git a/src/include/storage/store/table.h b/src/include/storage/store/table.h index 0545cf6eaef..829bb1c320d 100644 --- a/src/include/storage/store/table.h +++ b/src/include/storage/store/table.h @@ -31,8 +31,7 @@ class Table { common::ValueVector* defaultValueVector) = 0; virtual void dropColumn(common::column_id_t columnID) = 0; - virtual void prepareCommit( - transaction::Transaction* transaction, LocalTableData* localTable) = 0; + virtual void prepareCommit(transaction::Transaction* transaction, LocalTable* localTable) = 0; virtual void prepareRollback(LocalTableData* localTable) = 0; virtual void checkpointInMemory() = 0; virtual void rollbackInMemory() = 0; diff --git a/src/include/storage/store/var_list_column.h b/src/include/storage/store/var_list_column.h index 6b03654531a..7df361efbcc 100644 --- a/src/include/storage/store/var_list_column.h +++ b/src/include/storage/store/var_list_column.h @@ -2,7 +2,7 @@ #include "column.h" -// List is a nested data type which is stored as two columns: +// List is a nested data type which is stored as two chunks: // 1. Offset column (type: INT64). Using offset to partition the data column into multiple lists. // 2. Data column. Stores the actual data of the list. // Similar to other data types, nulls are stored in the null column. diff --git a/src/processor/map/map_set.cpp b/src/processor/map/map_set.cpp index 7137f40a8a0..e9c4f528fb4 100644 --- a/src/processor/map/map_set.cpp +++ b/src/processor/map/map_set.cpp @@ -75,27 +75,31 @@ std::unique_ptr PlanMapper::getRelSetExecutor( } auto evaluator = ExpressionMapper::getEvaluator(info->setItem.second, &inSchema); if (rel->isMultiLabeled()) { - std::unordered_map> - tableIDToTableAndPropertyID; + std::unordered_map> + tableIDToTableAndColumnID; for (auto tableID : rel->getTableIDs()) { if (!property->hasPropertyID(tableID)) { continue; } auto table = storageManager.getRelTable(tableID); auto propertyID = property->getPropertyID(tableID); - tableIDToTableAndPropertyID.insert({tableID, std::make_pair(table, propertyID)}); + auto columnID = + catalog->getReadOnlyVersion()->getTableSchema(tableID)->getColumnID(propertyID); + tableIDToTableAndColumnID.insert({tableID, std::make_pair(table, columnID)}); } - return std::make_unique(std::move(tableIDToTableAndPropertyID), + return std::make_unique(std::move(tableIDToTableAndColumnID), srcNodePos, dstNodePos, relIDPos, propertyPos, std::move(evaluator)); } else { auto tableID = rel->getSingleTableID(); auto table = storageManager.getRelTable(tableID); - auto propertyID = common::INVALID_PROPERTY_ID; + auto columnID = common::INVALID_COLUMN_ID; if (property->hasPropertyID(tableID)) { - propertyID = property->getPropertyID(tableID); + auto propertyID = property->getPropertyID(tableID); + columnID = + catalog->getReadOnlyVersion()->getTableSchema(tableID)->getColumnID(propertyID); } return std::make_unique( - table, propertyID, srcNodePos, dstNodePos, relIDPos, propertyPos, std::move(evaluator)); + table, columnID, srcNodePos, dstNodePos, relIDPos, propertyPos, std::move(evaluator)); } } diff --git a/src/processor/operator/persistent/merge.cpp b/src/processor/operator/persistent/merge.cpp index 28592036ea6..0d515dfc454 100644 --- a/src/processor/operator/persistent/merge.cpp +++ b/src/processor/operator/persistent/merge.cpp @@ -36,7 +36,7 @@ bool Merge::getNextTuplesInternal(ExecutionContext* context) { executor->set(context); } for (auto& executor : onMatchRelSetExecutors) { - executor->set(); + executor->set(context); } } else { for (auto& executor : nodeInsertExecutors) { @@ -49,7 +49,7 @@ bool Merge::getNextTuplesInternal(ExecutionContext* context) { executor->set(context); } for (auto& executor : onCreateRelSetExecutors) { - executor->set(); + executor->set(context); } } return true; diff --git a/src/processor/operator/persistent/set.cpp b/src/processor/operator/persistent/set.cpp index 4b70fc469b7..b54eea2d481 100644 --- a/src/processor/operator/persistent/set.cpp +++ b/src/processor/operator/persistent/set.cpp @@ -30,7 +30,7 @@ bool SetRelProperty::getNextTuplesInternal(ExecutionContext* context) { return false; } for (auto& executor : executors) { - executor->set(); + executor->set(context); } return true; } diff --git a/src/processor/operator/persistent/set_executor.cpp b/src/processor/operator/persistent/set_executor.cpp index 1248315e7ea..3550438f81f 100644 --- a/src/processor/operator/persistent/set_executor.cpp +++ b/src/processor/operator/persistent/set_executor.cpp @@ -111,8 +111,8 @@ static void writeToPropertyVector( writeToPropertyVector(relIDVector, propertyVector, propertyVectorPos, rhsVector, rhsVectorPos); } -void SingleLabelRelSetExecutor::set() { - if (propertyID == INVALID_PROPERTY_ID) { +void SingleLabelRelSetExecutor::set(ExecutionContext* context) { + if (columnID == INVALID_COLUMN_ID) { if (lhsVector != nullptr) { auto pos = relIDVector->state->selVector->selectedPositions[0]; lhsVector->setNull(pos, true); @@ -120,27 +120,27 @@ void SingleLabelRelSetExecutor::set() { return; } evaluator->evaluate(); - // TODO(Guodong): Fix set. - // table->updateRel(srcNodeIDVector, dstNodeIDVector, relIDVector, rhsVector, propertyID); + table->update(context->clientContext->getActiveTransaction(), columnID, srcNodeIDVector, + dstNodeIDVector, relIDVector, rhsVector); if (lhsVector != nullptr) { writeToPropertyVector(relIDVector, lhsVector, rhsVector); } } -void MultiLabelRelSetExecutor::set() { +void MultiLabelRelSetExecutor::set(ExecutionContext* context) { evaluator->evaluate(); KU_ASSERT(relIDVector->state->isFlat()); auto pos = relIDVector->state->selVector->selectedPositions[0]; auto relID = relIDVector->getValue(pos); - if (!tableIDToTableAndPropertyID.contains(relID.tableID)) { + if (!tableIDToTableAndColumnID.contains(relID.tableID)) { if (lhsVector != nullptr) { lhsVector->setNull(pos, true); } return; } - auto [table, propertyID] = tableIDToTableAndPropertyID.at(relID.tableID); - // TODO(Guodong): Fix set. - // table->updateRel(srcNodeIDVector, dstNodeIDVector, relIDVector, rhsVector, propertyID); + auto [table, propertyID] = tableIDToTableAndColumnID.at(relID.tableID); + table->update(context->clientContext->getActiveTransaction(), propertyID, srcNodeIDVector, + dstNodeIDVector, relIDVector, rhsVector); if (lhsVector != nullptr) { writeToPropertyVector(relIDVector, lhsVector, rhsVector); } diff --git a/src/storage/local_storage/CMakeLists.txt b/src/storage/local_storage/CMakeLists.txt index 5a6dcd59a71..13413a3f430 100644 --- a/src/storage/local_storage/CMakeLists.txt +++ b/src/storage/local_storage/CMakeLists.txt @@ -1,5 +1,7 @@ add_library(kuzu_storage_local_storage OBJECT + local_node_table.cpp + local_rel_table.cpp local_table.cpp local_storage.cpp) diff --git a/src/storage/local_storage/local_node_table.cpp b/src/storage/local_storage/local_node_table.cpp new file mode 100644 index 00000000000..4f30b3de0cf --- /dev/null +++ b/src/storage/local_storage/local_node_table.cpp @@ -0,0 +1,165 @@ +#include "storage/local_storage/local_node_table.h" + +#include "common/cast.h" +#include "storage/storage_utils.h" + +using namespace kuzu::common; + +namespace kuzu { +namespace storage { + +void LocalNodeNG::scan(ValueVector* nodeIDVector, const std::vector& columnIDs, + const std::vector& outputVectors) { + KU_ASSERT(columnIDs.size() == outputVectors.size()); + for (auto i = 0u; i < columnIDs.size(); i++) { + auto columnID = columnIDs[i]; + KU_ASSERT(columnID < chunks.size()); + for (auto pos = 0u; pos < nodeIDVector->state->selVector->selectedSize; pos++) { + auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[pos]; + auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; + auto posInOutputVector = outputVectors[i]->state->selVector->selectedPositions[pos]; + lookup(nodeOffset, columnID, outputVectors[i], posInOutputVector); + } + } +} + +void LocalNodeNG::lookup( + offset_t nodeOffset, column_id_t columnID, ValueVector* outputVector, sel_t posInOutputVector) { + KU_ASSERT(columnID < chunks.size()); + row_idx_t rowIdx = getRowIdx(columnID, nodeOffset); + if (rowIdx != INVALID_ROW_IDX) { + chunks[columnID]->read(rowIdx, outputVector, posInOutputVector); + } +} + +void LocalNodeNG::insert( + ValueVector* nodeIDVector, const std::vector& propertyVectors) { + KU_ASSERT(propertyVectors.size() == chunks.size() && + nodeIDVector->state->selVector->selectedSize == 1); + auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[0]; + if (nodeIDVector->isNull(nodeIDPos)) { + return; + } + auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; + for (auto columnID = 0u; columnID < chunks.size(); columnID++) { + auto rowIdx = chunks[columnID]->append(propertyVectors[columnID]); + KU_ASSERT(!updateInfo[columnID].contains(nodeOffset)); + insertInfo[columnID][nodeOffset] = rowIdx; + } +} + +void LocalNodeNG::update( + ValueVector* nodeIDVector, column_id_t columnID, ValueVector* propertyVector) { + KU_ASSERT(columnID < chunks.size() && nodeIDVector->state->selVector->selectedSize == 1); + auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[0]; + if (nodeIDVector->isNull(nodeIDPos)) { + return; + } + auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; + auto rowIdx = chunks[columnID]->append(propertyVector); + if (insertInfo[columnID].contains(nodeOffset)) { + // This node is in local storage, and had been newly inserted. + insertInfo.at(columnID)[nodeOffset] = rowIdx; + } else { + updateInfo[columnID][nodeOffset] = rowIdx; + } +} + +void LocalNodeNG::delete_(ValueVector* nodeIDVector) { + KU_ASSERT(nodeIDVector->state->selVector->selectedSize == 1); + auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[0]; + if (nodeIDVector->isNull(nodeIDPos)) { + return; + } + auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; + for (auto i = 0u; i < chunks.size(); i++) { + insertInfo[i].erase(nodeOffset); + updateInfo[i].erase(nodeOffset); + } +} + +row_idx_t LocalNodeNG::getRowIdx(column_id_t columnID, offset_t nodeOffset) { + KU_ASSERT(columnID < chunks.size()); + if (updateInfo[columnID].contains(nodeOffset)) { + // This node is in persistent storage, and had been updated. + return updateInfo[columnID][nodeOffset]; + } else if (insertInfo[columnID].contains(nodeOffset)) { + // This node is in local storage, and had been newly inserted. + return insertInfo[columnID][nodeOffset]; + } else { + return INVALID_ROW_IDX; + } +} + +void LocalNodeTableData::scan(ValueVector* nodeIDVector, const std::vector& columnIDs, + const std::vector& outputVectors) { + auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[0]; + auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; + auto nodeGroupIdx = StorageUtils::getNodeGroupIdx(nodeOffset); + if (!nodeGroups.contains(nodeGroupIdx)) { + return; + } + auto localNodeGroup = + ku_dynamic_cast(nodeGroups.at(nodeGroupIdx).get()); + KU_ASSERT(localNodeGroup); + localNodeGroup->scan(nodeIDVector, columnIDs, outputVectors); +} + +void LocalNodeTableData::lookup(ValueVector* nodeIDVector, + const std::vector& columnIDs, const std::vector& outputVectors) { + for (auto i = 0u; i < nodeIDVector->state->selVector->selectedSize; i++) { + auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[i]; + auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; + auto localNodeGroup = + ku_dynamic_cast(getOrCreateLocalNodeGroup(nodeIDVector)); + KU_ASSERT(localNodeGroup); + for (auto columnIdx = 0u; columnIdx < columnIDs.size(); columnIdx++) { + auto columnID = columnIDs[columnIdx]; + auto outputVector = outputVectors[columnIdx]; + auto outputVectorPos = outputVector->state->selVector->selectedPositions[i]; + localNodeGroup->lookup(nodeOffset, columnID, outputVector, outputVectorPos); + } + } +} + +void LocalNodeTableData::insert( + ValueVector* nodeIDVector, const std::vector& propertyVectors) { + KU_ASSERT(nodeIDVector->state->selVector->selectedSize == 1); + auto localNodeGroup = + ku_dynamic_cast(getOrCreateLocalNodeGroup(nodeIDVector)); + KU_ASSERT(localNodeGroup); + localNodeGroup->insert(nodeIDVector, propertyVectors); +} + +void LocalNodeTableData::update( + ValueVector* nodeIDVector, column_id_t columnID, ValueVector* propertyVector) { + auto localNodeGroup = + ku_dynamic_cast(getOrCreateLocalNodeGroup(nodeIDVector)); + KU_ASSERT(localNodeGroup); + localNodeGroup->update(nodeIDVector, columnID, propertyVector); +} + +void LocalNodeTableData::delete_(ValueVector* nodeIDVector) { + auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[0]; + auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; + auto nodeGroupIdx = StorageUtils::getNodeGroupIdx(nodeOffset); + if (!nodeGroups.contains(nodeGroupIdx)) { + return; + } + auto localNodeGroup = + ku_dynamic_cast(nodeGroups.at(nodeGroupIdx).get()); + localNodeGroup->delete_(nodeIDVector); +} + +LocalNodeGroup* LocalNodeTableData::getOrCreateLocalNodeGroup(common::ValueVector* nodeIDVector) { + auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[0]; + auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; + auto nodeGroupIdx = StorageUtils::getNodeGroupIdx(nodeOffset); + if (!nodeGroups.contains(nodeGroupIdx)) { + nodeGroups[nodeGroupIdx] = std::make_unique(dataTypes, mm); + } + return nodeGroups.at(nodeGroupIdx).get(); +} + +} // namespace storage +} // namespace kuzu diff --git a/src/storage/local_storage/local_rel_table.cpp b/src/storage/local_storage/local_rel_table.cpp new file mode 100644 index 00000000000..77c651ed376 --- /dev/null +++ b/src/storage/local_storage/local_rel_table.cpp @@ -0,0 +1,231 @@ +#include "storage/local_storage/local_rel_table.h" + +#include "common/cast.h" +#include "storage/storage_utils.h" + +using namespace kuzu::common; + +namespace kuzu { +namespace storage { + +void RegularRelNGInfo::insert(offset_t srcNodeOffset, offset_t /*relOffset*/, + row_idx_t adjNodeRowIdx, const std::vector& propertyNodesRowIdx) { + KU_ASSERT(propertyNodesRowIdx.size() == insertInfoPerChunk.size()); + if (deleteInfo.contains(srcNodeOffset)) { + // We choose to ignore the insert operation if the node is deleted. + return; + } + adjInsertInfo[srcNodeOffset] = adjNodeRowIdx; + for (auto i = 0u; i < propertyNodesRowIdx.size(); ++i) { + KU_ASSERT(!updateInfoPerChunk[i].contains(srcNodeOffset)); + insertInfoPerChunk[i][srcNodeOffset] = propertyNodesRowIdx[i]; + } +} + +void RegularRelNGInfo::update( + offset_t srcNodeOffset, offset_t relOffset, column_id_t columnID, row_idx_t rowIdx) { + if (deleteInfo.contains(srcNodeOffset)) { + // We choose to ignore the update operation if the node is deleted. + return; + } + KU_ASSERT(columnID != REL_ID_COLUMN_ID); // Rel ID is immutable. + KU_ASSERT(columnID < updateInfoPerChunk.size()); + if (insertInfoPerChunk[columnID].contains(srcNodeOffset)) { + // Update newly inserted value. + insertInfoPerChunk[columnID][relOffset] = rowIdx; + } else { + updateInfoPerChunk[columnID][relOffset] = rowIdx; + } +} + +bool RegularRelNGInfo::delete_(offset_t srcNodeOffset, offset_t /*relOffset*/) { + if (adjInsertInfo.contains(srcNodeOffset)) { + // Delete newly inserted tuple. + adjInsertInfo.erase(srcNodeOffset); + for (auto& insertInfo : insertInfoPerChunk) { + insertInfo.erase(srcNodeOffset); + } + } else { + if (deleteInfo.contains(srcNodeOffset)) { + // The node is already deleted. + return false; + } else { + deleteInfo.insert(srcNodeOffset); + } + } + return true; +} + +void CSRRelNGInfo::insert(offset_t srcNodeOffset, offset_t relOffset, row_idx_t adjNodeRowIdx, + const std::vector& propertyNodesRowIdx) { + KU_ASSERT(propertyNodesRowIdx.size() == insertInfoPerChunk.size()); + if (deleteInfo.contains(srcNodeOffset) && !contains(deleteInfo.at(srcNodeOffset), relOffset)) { + // We choose to ignore the insert operation if the node is deleted. + return; + } + if (adjInsertInfo.contains(srcNodeOffset)) { + adjInsertInfo.at(srcNodeOffset)[relOffset] = adjNodeRowIdx; + } else { + adjInsertInfo[srcNodeOffset] = {{relOffset, adjNodeRowIdx}}; + } + for (auto i = 0u; i < propertyNodesRowIdx.size(); ++i) { + if (insertInfoPerChunk[i].contains(srcNodeOffset)) { + insertInfoPerChunk[i].at(srcNodeOffset)[relOffset] = propertyNodesRowIdx[i]; + } else { + insertInfoPerChunk[i][srcNodeOffset] = {{relOffset, propertyNodesRowIdx[i]}}; + } + } +} + +void CSRRelNGInfo::update( + offset_t srcNodeOffset, offset_t relOffset, column_id_t columnID, row_idx_t rowIdx) { + // REL_ID_COLUMN_ID is immutable. + KU_ASSERT(columnID != REL_ID_COLUMN_ID && columnID < updateInfoPerChunk.size()); + if (deleteInfo.contains(srcNodeOffset) && !contains(deleteInfo.at(srcNodeOffset), relOffset)) { + // We choose to ignore the update operation if the node is deleted. + return; + } + if (insertInfoPerChunk[columnID].contains(srcNodeOffset) && + insertInfoPerChunk[columnID].at(srcNodeOffset).contains(relOffset)) { + // Update newly inserted value. + insertInfoPerChunk[columnID].at(srcNodeOffset)[relOffset] = rowIdx; + } else { + if (updateInfoPerChunk[columnID].contains(srcNodeOffset)) { + updateInfoPerChunk[columnID].at(srcNodeOffset)[relOffset] = rowIdx; + } else { + updateInfoPerChunk[columnID][srcNodeOffset] = {{relOffset, rowIdx}}; + } + } +} + +bool CSRRelNGInfo::delete_(offset_t srcNodeOffset, offset_t relOffset) { + if (adjInsertInfo.contains(srcNodeOffset) && + adjInsertInfo.at(srcNodeOffset).contains(relOffset)) { + // Delete newly inserted tuple. + adjInsertInfo.at(srcNodeOffset).erase(relOffset); + if (adjInsertInfo.at(srcNodeOffset).empty()) { + adjInsertInfo.erase(srcNodeOffset); + } + for (auto& insertInfo : insertInfoPerChunk) { + insertInfo.at(srcNodeOffset).erase(relOffset); + if (insertInfo.at(srcNodeOffset).empty()) { + insertInfo.erase(srcNodeOffset); + } + } + } else { + if (deleteInfo.contains(srcNodeOffset)) { + if (deleteInfo.at(srcNodeOffset).contains(relOffset)) { + // The node is already deleted. + return false; + } else { + deleteInfo.at(srcNodeOffset).insert(relOffset); + } + } else { + deleteInfo[srcNodeOffset] = {relOffset}; + } + } + return true; +} + +LocalRelNG::LocalRelNG(ColumnDataFormat dataFormat, std::vector dataTypes, + kuzu::storage::MemoryManager* mm) + : LocalNodeGroup{std::move(dataTypes), mm} { + switch (dataFormat) { + case ColumnDataFormat::REGULAR: { + relNGInfo = std::make_unique(chunks.size()); + } break; + case ColumnDataFormat::CSR: { + relNGInfo = std::make_unique(chunks.size()); + } break; + default: { + KU_UNREACHABLE; + } + } +} + +void LocalRelNG::insert(ValueVector* srcNodeIDVector, ValueVector* dstNodeIDVector, + const std::vector& propertyVectors) { + KU_ASSERT(propertyVectors.size() == chunks.size() && propertyVectors.size() > 1); + auto adjNodeIDRowIdx = adjChunk->append(dstNodeIDVector); + std::vector propertyValuesRowIdx; + for (auto i = 0u; i < propertyVectors.size(); ++i) { + propertyValuesRowIdx.push_back(chunks[i]->append(propertyVectors[i])); + } + auto srcNodeIDPos = srcNodeIDVector->state->selVector->selectedPositions[0]; + auto srcNodeOffset = srcNodeIDVector->getValue(srcNodeIDPos).offset; + auto relIDPos = propertyVectors[REL_ID_COLUMN_ID]->state->selVector->selectedPositions[0]; + auto relOffset = propertyVectors[REL_ID_COLUMN_ID]->getValue(relIDPos).offset; + relNGInfo->insert(srcNodeOffset, relOffset, adjNodeIDRowIdx, propertyValuesRowIdx); +} + +void LocalRelNG::update(ValueVector* srcNodeIDVector, ValueVector* relIDVector, + column_id_t columnID, ValueVector* propertyVector) { + KU_ASSERT(columnID < chunks.size()); + auto rowIdx = chunks[columnID]->append(propertyVector); + auto srcNodeIDPos = srcNodeIDVector->state->selVector->selectedPositions[0]; + auto srcNodeOffset = srcNodeIDVector->getValue(srcNodeIDPos).offset; + auto relIDPos = relIDVector->state->selVector->selectedPositions[0]; + auto relOffset = relIDVector->getValue(relIDPos).offset; + relNGInfo->update(srcNodeOffset, relOffset, columnID, rowIdx); +} + +void LocalRelNG::delete_(ValueVector* srcNodeIDVector, ValueVector* relIDVector) { + auto srcNodeIDPos = srcNodeIDVector->state->selVector->selectedPositions[0]; + auto srcNodeOffset = srcNodeIDVector->getValue(srcNodeIDPos).offset; + auto relIDPos = relIDVector->state->selVector->selectedPositions[0]; + auto relOffset = relIDVector->getValue(relIDPos).offset; + relNGInfo->delete_(srcNodeOffset, relOffset); +} + +void LocalRelTableData::insert(ValueVector* srcNodeIDVector, ValueVector* dstNodeIDVector, + const std::vector& propertyVectors) { + KU_ASSERT(srcNodeIDVector->state->selVector->selectedSize == 1 && + dstNodeIDVector->state->selVector->selectedSize == 1); + auto srcNodeIDPos = srcNodeIDVector->state->selVector->selectedPositions[0]; + auto dstNodeIDPos = dstNodeIDVector->state->selVector->selectedPositions[0]; + if (srcNodeIDVector->isNull(srcNodeIDPos) || dstNodeIDVector->isNull(dstNodeIDPos)) { + return; + } + auto localNodeGroup = + ku_dynamic_cast(getOrCreateLocalNodeGroup(srcNodeIDVector)); + KU_ASSERT(localNodeGroup); + localNodeGroup->insert(srcNodeIDVector, dstNodeIDVector, propertyVectors); +} + +void LocalRelTableData::update(ValueVector* srcNodeIDVector, ValueVector* relIDVector, + column_id_t columnID, ValueVector* propertyVector) { + KU_ASSERT(srcNodeIDVector->state->selVector->selectedSize == 1 && + relIDVector->state->selVector->selectedSize == 1); + auto srcNodeIDPos = srcNodeIDVector->state->selVector->selectedPositions[0]; + if (srcNodeIDVector->isNull(srcNodeIDPos)) { + return; + } + auto localNodeGroup = + ku_dynamic_cast(getOrCreateLocalNodeGroup(srcNodeIDVector)); + localNodeGroup->update(srcNodeIDVector, relIDVector, columnID, propertyVector); +} + +void LocalRelTableData::delete_(ValueVector* srcNodeIDVector, ValueVector* relIDVector) { + KU_ASSERT(srcNodeIDVector->state->selVector->selectedSize == 1 && + relIDVector->state->selVector->selectedSize == 1); + auto srcNodeIDPos = srcNodeIDVector->state->selVector->selectedPositions[0]; + if (srcNodeIDVector->isNull(srcNodeIDPos)) { + return; + } + auto localNodeGroup = + ku_dynamic_cast(getOrCreateLocalNodeGroup(srcNodeIDVector)); + localNodeGroup->delete_(srcNodeIDVector, relIDVector); +} + +LocalNodeGroup* LocalRelTableData::getOrCreateLocalNodeGroup(ValueVector* nodeIDVector) { + auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[0]; + auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; + auto nodeGroupIdx = StorageUtils::getNodeGroupIdx(nodeOffset); + if (!nodeGroups.contains(nodeGroupIdx)) { + nodeGroups[nodeGroupIdx] = std::make_unique(dataFormat, dataTypes, mm); + } + return nodeGroups.at(nodeGroupIdx).get(); +} + +} // namespace storage +} // namespace kuzu diff --git a/src/storage/local_storage/local_storage.cpp b/src/storage/local_storage/local_storage.cpp index bc3779f8433..d866530117a 100644 --- a/src/storage/local_storage/local_storage.cpp +++ b/src/storage/local_storage/local_storage.cpp @@ -11,19 +11,27 @@ namespace storage { LocalStorage::LocalStorage(MemoryManager* mm) : mm{mm} {} -LocalTableData* LocalStorage::getOrCreateLocalTableData( - common::table_id_t tableID, const std::vector>& columns) { +LocalTableData* LocalStorage::getOrCreateLocalTableData(table_id_t tableID, + const std::vector>& columns, TableType tableType, + ColumnDataFormat dataFormat, vector_idx_t dataIdx) { if (!tables.contains(tableID)) { - tables.emplace(tableID, std::make_unique(tableID, TableType::NODE)); + tables[tableID] = std::make_unique(tableID, tableType); } - return tables.at(tableID)->getOrCreateLocalTableData(columns, mm); + return tables.at(tableID)->getOrCreateLocalTableData(columns, mm, dataFormat, dataIdx); } -LocalTableData* LocalStorage::getLocalTableData(common::table_id_t tableID) { +LocalTable* LocalStorage::getLocalTable(table_id_t tableID) { if (!tables.contains(tableID)) { return nullptr; } - return tables.at(tableID)->getLocalTableData(); + return tables.at(tableID).get(); +} + +LocalTableData* LocalStorage::getLocalTableData(table_id_t tableID, vector_idx_t dataIdx) { + if (!tables.contains(tableID)) { + return nullptr; + } + return tables.at(tableID)->getLocalTableData(dataIdx); } std::unordered_set LocalStorage::getTableIDsWithUpdates() { diff --git a/src/storage/local_storage/local_table.cpp b/src/storage/local_storage/local_table.cpp index f8f6ee5a058..4c9fe68c07c 100644 --- a/src/storage/local_storage/local_table.cpp +++ b/src/storage/local_storage/local_table.cpp @@ -1,7 +1,8 @@ #include "storage/local_storage/local_table.h" #include "common/exception/message.h" -#include "storage/storage_utils.h" +#include "storage/local_storage/local_node_table.h" +#include "storage/local_storage/local_rel_table.h" #include "storage/store/column.h" using namespace kuzu::common; @@ -35,47 +36,12 @@ void LocalVectorCollection::read( vectors[vectorIdx]->read(offsetInVector, outputVector, posInOutputVector); } -void LocalVectorCollection::insert(ValueVector* nodeIDVector, ValueVector* propertyVector) { - KU_ASSERT(nodeIDVector->state->selVector->selectedSize == 1); - auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[0]; - auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; - KU_ASSERT(!updateInfo.contains(nodeOffset)); - append(propertyVector); - insertInfo[nodeOffset] = numRows; - numRows++; -} - -void LocalVectorCollection::update(ValueVector* nodeIDVector, ValueVector* propertyVector) { - KU_ASSERT(nodeIDVector->state->selVector->selectedSize == 1); - auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[0]; - auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; - append(propertyVector); - if (insertInfo.contains(nodeOffset)) { - // This node is in local storage, and had been newly inserted. - insertInfo[nodeOffset] = numRows; - } else { - updateInfo[nodeOffset] = numRows; - } - numRows++; -} - -row_idx_t LocalVectorCollection::getRowIdx(offset_t nodeOffset) { - row_idx_t rowIdx = INVALID_ROW_IDX; - if (updateInfo.contains(nodeOffset)) { - // This node is in persistent storage, and had been updated. - rowIdx = updateInfo[nodeOffset]; - } else if (insertInfo.contains(nodeOffset)) { - // This node is in local storage, and had been newly inserted. - rowIdx = insertInfo[nodeOffset]; - } - return rowIdx; -} - -void LocalVectorCollection::append(ValueVector* vector) { +row_idx_t LocalVectorCollection::append(ValueVector* vector) { prepareAppend(); auto lastVector = vectors.back().get(); KU_ASSERT(!lastVector->isFull()); lastVector->append(vector); + return numRows++; } void LocalVectorCollection::prepareAppend() { @@ -89,142 +55,52 @@ void LocalVectorCollection::prepareAppend() { } LocalNodeGroup::LocalNodeGroup(std::vector dataTypes, MemoryManager* mm) { - columns.resize(dataTypes.size()); + chunks.resize(dataTypes.size()); for (auto i = 0u; i < dataTypes.size(); ++i) { // To avoid unnecessary memory consumption, we chunk local changes of each column in the // node group into chunks of size DEFAULT_VECTOR_CAPACITY. - columns[i] = std::make_unique(dataTypes[i], mm); + chunks[i] = std::make_unique(dataTypes[i], mm); } } -void LocalNodeGroup::scan(ValueVector* nodeIDVector, const std::vector& columnIDs, - const std::vector& outputVectors) { - KU_ASSERT(columnIDs.size() == outputVectors.size()); - for (auto i = 0u; i < columnIDs.size(); i++) { - auto columnID = columnIDs[i]; - KU_ASSERT(columnID < columns.size()); - for (auto pos = 0u; pos < nodeIDVector->state->selVector->selectedSize; pos++) { - auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[pos]; - auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; - auto posInOutputVector = outputVectors[i]->state->selVector->selectedPositions[pos]; - lookup(nodeOffset, columnID, outputVectors[i], posInOutputVector); +LocalTableData* LocalTable::getOrCreateLocalTableData( + const std::vector>& columns, MemoryManager* mm, + ColumnDataFormat dataFormat, vector_idx_t dataIdx) { + if (localTableDataCollection.empty()) { + std::vector dataTypes; + for (auto& column : columns) { + dataTypes.push_back(column->getDataType()); + } + switch (tableType) { + case TableType::NODE: { + KU_ASSERT(dataFormat == ColumnDataFormat::REGULAR); + localTableDataCollection.reserve(1); + localTableDataCollection.push_back( + std::make_unique(std::move(dataTypes), mm, dataFormat)); + } break; + case TableType::REL: { + KU_ASSERT(dataIdx < 2); + localTableDataCollection.resize(2); + localTableDataCollection[dataIdx] = + std::make_unique(std::move(dataTypes), mm, dataFormat); + } break; + default: { + KU_UNREACHABLE; } - } -} - -void LocalNodeGroup::lookup( - offset_t nodeOffset, column_id_t columnID, ValueVector* outputVector, sel_t posInOutputVector) { - KU_ASSERT(columnID < columns.size()); - row_idx_t rowIdx = getRowIdx(columnID, nodeOffset); - if (rowIdx != INVALID_ROW_IDX) { - columns[columnID]->read(rowIdx, outputVector, posInOutputVector); - } -} - -void LocalNodeGroup::insert( - ValueVector* nodeIDVector, const std::vector& propertyVectors) { - KU_ASSERT(propertyVectors.size() == columns.size() && - nodeIDVector->state->selVector->selectedSize == 1); - auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[0]; - if (nodeIDVector->isNull(nodeIDPos)) { - return; - } - for (auto i = 0u; i < propertyVectors.size(); i++) { - columns[i]->insert(nodeIDVector, propertyVectors[i]); - } -} - -void LocalNodeGroup::update( - ValueVector* nodeIDVector, column_id_t columnID, ValueVector* propertyVector) { - KU_ASSERT(columnID < columns.size() && nodeIDVector->state->selVector->selectedSize == 1); - auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[0]; - if (nodeIDVector->isNull(nodeIDPos)) { - return; - } - columns[columnID]->update(nodeIDVector, propertyVector); -} - -void LocalNodeGroup::delete_(ValueVector* nodeIDVector) { - KU_ASSERT(nodeIDVector->state->selVector->selectedSize == 1); - auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[0]; - if (nodeIDVector->isNull(nodeIDPos)) { - return; - } - auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; - for (auto i = 0u; i < columns.size(); i++) { - columns[i]->delete_(nodeOffset); - } -} - -void LocalTableData::scan(ValueVector* nodeIDVector, const std::vector& columnIDs, - const std::vector& outputVectors) { - auto nodeGroupIdx = initializeLocalNodeGroup(nodeIDVector); - nodeGroups.at(nodeGroupIdx)->scan(nodeIDVector, columnIDs, outputVectors); -} - -void LocalTableData::lookup(ValueVector* nodeIDVector, const std::vector& columnIDs, - const std::vector& outputVectors) { - for (auto i = 0u; i < nodeIDVector->state->selVector->selectedSize; i++) { - auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[i]; - auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; - auto nodeGroupIdx = initializeLocalNodeGroup(nodeOffset); - for (auto columnIdx = 0u; columnIdx < columnIDs.size(); columnIdx++) { - auto columnID = columnIDs[columnIdx]; - auto outputVector = outputVectors[columnIdx]; - auto outputVectorPos = outputVector->state->selVector->selectedPositions[i]; - nodeGroups.at(nodeGroupIdx) - ->lookup(nodeOffset, columnID, outputVector, outputVectorPos); } } -} - -void LocalTableData::insert( - ValueVector* nodeIDVector, const std::vector& propertyVectors) { - KU_ASSERT(nodeIDVector->state->selVector->selectedSize == 1); - auto nodeGroupIdx = initializeLocalNodeGroup(nodeIDVector); - nodeGroups.at(nodeGroupIdx)->insert(nodeIDVector, propertyVectors); -} - -void LocalTableData::update( - ValueVector* nodeIDVector, column_id_t columnID, ValueVector* propertyVector) { - auto nodeGroupIdx = initializeLocalNodeGroup(nodeIDVector); - nodeGroups.at(nodeGroupIdx)->update(nodeIDVector, columnID, propertyVector); -} - -void LocalTableData::delete_(ValueVector* nodeIDVector) { - auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[0]; - auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; - auto nodeGroupIdx = StorageUtils::getNodeGroupIdx(nodeOffset); - if (!nodeGroups.contains(nodeGroupIdx)) { - return; - } - nodeGroups.at(nodeGroupIdx)->delete_(nodeIDVector); -} - -node_group_idx_t LocalTableData::initializeLocalNodeGroup(ValueVector* nodeIDVector) { - auto nodeIDPos = nodeIDVector->state->selVector->selectedPositions[0]; - auto nodeOffset = nodeIDVector->getValue(nodeIDPos).offset; - return initializeLocalNodeGroup(nodeOffset); -} - -node_group_idx_t LocalTableData::initializeLocalNodeGroup(offset_t nodeOffset) { - auto nodeGroupIdx = StorageUtils::getNodeGroupIdx(nodeOffset); - if (!nodeGroups.contains(nodeGroupIdx)) { - nodeGroups.emplace(nodeGroupIdx, std::make_unique(dataTypes, mm)); - } - return nodeGroupIdx; -} - -LocalTableData* LocalTable::getOrCreateLocalTableData( - const std::vector>& columns, MemoryManager* mm) { - if (!localTableData) { + KU_ASSERT(dataIdx < localTableDataCollection.size()); + if (!localTableDataCollection[dataIdx]) { + KU_ASSERT(tableType == TableType::REL); std::vector dataTypes; for (auto& column : columns) { dataTypes.push_back(column->getDataType()); } - localTableData = std::make_unique(std::move(dataTypes), mm); + localTableDataCollection[dataIdx] = + std::make_unique(std::move(dataTypes), mm, dataFormat); } - return localTableData.get(); + KU_ASSERT(localTableDataCollection[dataIdx] != nullptr); + return localTableDataCollection[dataIdx].get(); } } // namespace storage diff --git a/src/storage/stats/property_statistics.cpp b/src/storage/stats/property_statistics.cpp index 14f34d15d5e..e7c4e761d09 100644 --- a/src/storage/stats/property_statistics.cpp +++ b/src/storage/stats/property_statistics.cpp @@ -29,7 +29,7 @@ bool RWPropertyStats::mayHaveNull(const transaction::Transaction& transaction) { // tracked in statistics. For example, offset of var list column, csr offset column, etc. // TODO(Guodong): INVALID_PROPERTY_ID is used here because we have a column, i.e., adjColumn, // not exposed as property in table schema, but still have nullColumn. Should be fixed once we - // properly align properties and columns. + // properly align properties and chunks. if (propertyID == common::INVALID_PROPERTY_ID) { return true; } @@ -42,7 +42,7 @@ bool RWPropertyStats::mayHaveNull(const transaction::Transaction& transaction) { void RWPropertyStats::setHasNull(const transaction::Transaction& transaction) { // TODO(Guodong): INVALID_PROPERTY_ID is used here because we have a column, i.e., adjColumn, // not exposed as property in table schema, but still have nullColumn. Should be fixed once we - // properly align properties and columns. + // properly align properties and chunks. if (propertyID != common::INVALID_PROPERTY_ID) { KU_ASSERT(tablesStatistics); tablesStatistics->getPropertyStatisticsForTable(transaction, tableID, propertyID) diff --git a/src/storage/storage_manager.cpp b/src/storage/storage_manager.cpp index 90069fbe9ee..5755d625fab 100644 --- a/src/storage/storage_manager.cpp +++ b/src/storage/storage_manager.cpp @@ -93,7 +93,7 @@ void StorageManager::prepareCommit(transaction::Transaction* transaction) { auto localStorage = transaction->getLocalStorage(); for (auto tableID : localStorage->getTableIDsWithUpdates()) { KU_ASSERT(tables.contains(tableID)); - tables.at(tableID)->prepareCommit(transaction, localStorage->getLocalTableData(tableID)); + tables.at(tableID)->prepareCommit(transaction, localStorage->getLocalTable(tableID)); } if (nodesStatisticsAndDeletedIDs->hasUpdates()) { wal->logTableStatisticsRecord(true /* isNodeTable */); diff --git a/src/storage/store/column.cpp b/src/storage/store/column.cpp index 11ee8a511ad..66ebc30981a 100644 --- a/src/storage/store/column.cpp +++ b/src/storage/store/column.cpp @@ -356,9 +356,15 @@ void Column::scan( uint64_t numValuesPerPage = chunkMetadata.compMeta.numValues(BufferPoolConstants::PAGE_4KB_SIZE, *dataType); uint64_t numValuesScanned = 0u; - while (numValuesScanned < columnChunk->getCapacity()) { + auto numValuesToScan = columnChunk->getCapacity(); + if (chunkMetadata.numValues < numValuesToScan) { + numValuesToScan = chunkMetadata.numValues; + } + KU_ASSERT(chunkMetadata.numValues <= columnChunk->getCapacity()); + // TODO(Guodong): We should resize as needed here. + while (numValuesScanned < numValuesToScan) { auto numValuesToReadInPage = - std::min(numValuesPerPage, columnChunk->getCapacity() - numValuesScanned); + std::min(numValuesPerPage, numValuesToScan - numValuesScanned); KU_ASSERT(cursor.pageIdx < chunkMetadata.pageIdx + chunkMetadata.numPages); readFromPage(&DUMMY_READ_TRANSACTION, cursor.pageIdx, [&](uint8_t* frame) -> void { readToPageFunc(frame, cursor, columnChunk->getData(), numValuesScanned, @@ -538,17 +544,23 @@ void Column::setNull(offset_t nodeOffset) { } void Column::prepareCommitForChunk(Transaction* transaction, node_group_idx_t nodeGroupIdx, - LocalVectorCollection* localColumnChunk, bool isNewNodeGroup) { + LocalVectorCollection* localColumnChunk, const offset_to_row_idx_t& insertInfo, + const offset_to_row_idx_t& updateInfo, const offset_set_t& deleteInfo) { + auto currentNumNodeGroups = metadataDA->getNumElements(transaction->getType()); + auto isNewNodeGroup = nodeGroupIdx >= currentNumNodeGroups; if (isNewNodeGroup) { // If this is a new node group, updateInfo should be empty. We should perform out-of-place // commit with a new column chunk. - commitLocalChunkOutOfPlace(transaction, nodeGroupIdx, localColumnChunk, isNewNodeGroup); + commitLocalChunkOutOfPlace(transaction, nodeGroupIdx, localColumnChunk, isNewNodeGroup, + insertInfo, updateInfo, deleteInfo); } else { // If this is not a new node group, we should first check if we can perform in-place commit. - if (canCommitInPlace(transaction, nodeGroupIdx, localColumnChunk)) { - commitLocalChunkInPlace(localColumnChunk); + if (canCommitInPlace(transaction, nodeGroupIdx, localColumnChunk, insertInfo, updateInfo)) { + commitLocalChunkInPlace( + transaction, localColumnChunk, insertInfo, updateInfo, deleteInfo); } else { - commitLocalChunkOutOfPlace(transaction, nodeGroupIdx, localColumnChunk, isNewNodeGroup); + commitLocalChunkOutOfPlace(transaction, nodeGroupIdx, localColumnChunk, isNewNodeGroup, + insertInfo, updateInfo, deleteInfo); } } } @@ -572,8 +584,9 @@ bool Column::containsVarList(LogicalType& dataType) { } // TODO(Guodong): This should be moved inside `LocalVectorCollection`. -bool Column::canCommitInPlace( - Transaction* transaction, node_group_idx_t nodeGroupIdx, LocalVectorCollection* localChunk) { +bool Column::canCommitInPlace(Transaction* transaction, node_group_idx_t nodeGroupIdx, + LocalVectorCollection* localChunk, const offset_to_row_idx_t& insertInfo, + const offset_to_row_idx_t& updateInfo) { if (containsVarList(*dataType)) { // Always perform out of place commit for VAR_LIST data type. return false; @@ -583,10 +596,10 @@ bool Column::canCommitInPlace( return true; } std::vector rowIdxesToRead; - for (auto& [nodeOffset, rowIdx] : localChunk->getUpdateInfoRef()) { + for (auto& [nodeOffset, rowIdx] : updateInfo) { rowIdxesToRead.push_back(rowIdx); } - for (auto& [nodeOffset, rowIdx] : localChunk->getInsertInfoRef()) { + for (auto& [nodeOffset, rowIdx] : insertInfo) { rowIdxesToRead.push_back(rowIdx); } std::sort(rowIdxesToRead.begin(), rowIdxesToRead.end()); @@ -601,28 +614,31 @@ bool Column::canCommitInPlace( return true; } -void Column::commitLocalChunkInPlace(LocalVectorCollection* localChunk) { - applyLocalChunkToColumn(localChunk, localChunk->getUpdateInfoRef()); - applyLocalChunkToColumn(localChunk, localChunk->getInsertInfoRef()); +void Column::commitLocalChunkInPlace(Transaction* /*transaction*/, + LocalVectorCollection* localChunk, const offset_to_row_idx_t& insertInfo, + const offset_to_row_idx_t& updateInfo, const offset_set_t& /*deleteInfo*/) { + applyLocalChunkToColumn(localChunk, updateInfo); + applyLocalChunkToColumn(localChunk, insertInfo); } void Column::commitLocalChunkOutOfPlace(Transaction* transaction, node_group_idx_t nodeGroupIdx, - LocalVectorCollection* localChunk, bool isNewNodeGroup) { + LocalVectorCollection* localChunk, bool isNewNodeGroup, const offset_to_row_idx_t& insertInfo, + const offset_to_row_idx_t& updateInfo, const offset_set_t& /*deleteInfo*/) { auto columnChunk = ColumnChunkFactory::createColumnChunk(dataType->copy(), enableCompression); if (isNewNodeGroup) { - KU_ASSERT(localChunk->getUpdateInfoRef().empty()); + KU_ASSERT(updateInfo.empty()); // Apply inserts from the local chunk. applyLocalChunkToColumnChunk(localChunk, columnChunk.get(), - nodeGroupIdx << StorageConstants::NODE_GROUP_SIZE_LOG2, localChunk->getInsertInfoRef()); + nodeGroupIdx << StorageConstants::NODE_GROUP_SIZE_LOG2, insertInfo); } else { // First, scan the whole column chunk from persistent storage. scan(transaction, nodeGroupIdx, columnChunk.get()); // Then, apply updates from the local chunk. applyLocalChunkToColumnChunk(localChunk, columnChunk.get(), - nodeGroupIdx << StorageConstants::NODE_GROUP_SIZE_LOG2, localChunk->getUpdateInfoRef()); + nodeGroupIdx << StorageConstants::NODE_GROUP_SIZE_LOG2, updateInfo); // Lastly, apply inserts from the local chunk. applyLocalChunkToColumnChunk(localChunk, columnChunk.get(), - nodeGroupIdx << StorageConstants::NODE_GROUP_SIZE_LOG2, localChunk->getInsertInfoRef()); + nodeGroupIdx << StorageConstants::NODE_GROUP_SIZE_LOG2, insertInfo); } columnChunk->finalize(); append(columnChunk.get(), nodeGroupIdx); diff --git a/src/storage/store/node_table.cpp b/src/storage/store/node_table.cpp index 1aa41eef663..229413ddec5 100644 --- a/src/storage/store/node_table.cpp +++ b/src/storage/store/node_table.cpp @@ -108,11 +108,11 @@ void NodeTable::addColumn(transaction::Transaction* transaction, const catalog:: wal->addToUpdatedTables(tableID); } -void NodeTable::prepareCommit(Transaction* transaction, LocalTableData* localTable) { +void NodeTable::prepareCommit(Transaction* transaction, LocalTable* localTable) { if (pkIndex) { pkIndex->prepareCommit(); } - tableData->prepareLocalTableToCommit(transaction, localTable); + tableData->prepareLocalTableToCommit(transaction, localTable->getLocalTableData(0)); wal->addToUpdatedTables(tableID); } diff --git a/src/storage/store/node_table_data.cpp b/src/storage/store/node_table_data.cpp index 0f2225cb0f3..3198c94398e 100644 --- a/src/storage/store/node_table_data.cpp +++ b/src/storage/store/node_table_data.cpp @@ -1,5 +1,7 @@ #include "storage/store/node_table_data.h" +#include "common/cast.h" +#include "storage/local_storage/local_node_table.h" #include "storage/local_storage/local_table.h" #include "storage/stats/nodes_store_statistics.h" @@ -41,7 +43,10 @@ void NodeTableData::scan(Transaction* transaction, TableReadState& readState, if (transaction->isWriteTransaction()) { auto localTableData = transaction->getLocalStorage()->getLocalTableData(tableID); if (localTableData) { - localTableData->scan(nodeIDVector, readState.columnIDs, outputVectors); + auto localRelTableData = + ku_dynamic_cast(localTableData); + KU_ASSERT(localRelTableData); + localRelTableData->scan(nodeIDVector, readState.columnIDs, outputVectors); } } } @@ -58,22 +63,25 @@ void NodeTableData::insert(Transaction* transaction, ValueVector* nodeIDVector, newNodeGroup->finalize(currentNumNodeGroups); append(newNodeGroup.get()); } - auto localTableData = - transaction->getLocalStorage()->getOrCreateLocalTableData(tableID, columns); + auto localTableData = ku_dynamic_cast( + transaction->getLocalStorage()->getOrCreateLocalTableData(tableID, columns)); + KU_ASSERT(localTableData); localTableData->insert(nodeIDVector, propertyVectors); } void NodeTableData::update(Transaction* transaction, column_id_t columnID, ValueVector* nodeIDVector, ValueVector* propertyVector) { KU_ASSERT(columnID < columns.size()); - auto localTableData = - transaction->getLocalStorage()->getOrCreateLocalTableData(tableID, columns); + auto localTableData = ku_dynamic_cast( + transaction->getLocalStorage()->getOrCreateLocalTableData(tableID, columns)); + KU_ASSERT(localTableData); localTableData->update(nodeIDVector, columnID, propertyVector); } void NodeTableData::delete_(Transaction* transaction, ValueVector* nodeIDVector) { - auto localTableData = - transaction->getLocalStorage()->getOrCreateLocalTableData(tableID, columns); + auto localTableData = ku_dynamic_cast( + transaction->getLocalStorage()->getOrCreateLocalTableData(tableID, columns)); + KU_ASSERT(localTableData); localTableData->delete_(nodeIDVector); } @@ -92,7 +100,10 @@ void NodeTableData::lookup(Transaction* transaction, TableReadState& readState, if (transaction->isWriteTransaction()) { auto localTableData = transaction->getLocalStorage()->getLocalTableData(tableID); if (localTableData) { - localTableData->lookup(nodeIDVector, readState.columnIDs, outputVectors); + auto localRelTableData = + ku_dynamic_cast(localTableData); + KU_ASSERT(localRelTableData); + localRelTableData->lookup(nodeIDVector, readState.columnIDs, outputVectors); } } } @@ -107,7 +118,6 @@ void NodeTableData::append(kuzu::storage::NodeGroup* nodeGroup) { void NodeTableData::prepareLocalTableToCommit( Transaction* transaction, LocalTableData* localTable) { - auto numNodeGroups = getNumNodeGroups(&DUMMY_WRITE_TRANSACTION); for (auto& [nodeGroupIdx, nodeGroup] : localTable->nodeGroups) { for (auto columnID = 0; columnID < columns.size(); columnID++) { auto column = columns[columnID].get(); @@ -115,8 +125,10 @@ void NodeTableData::prepareLocalTableToCommit( if (columnChunk->getNumRows() == 0) { continue; } - column->prepareCommitForChunk( - transaction, nodeGroupIdx, columnChunk, nodeGroupIdx >= numNodeGroups); + auto localNodeGroup = ku_dynamic_cast(nodeGroup.get()); + column->prepareCommitForChunk(transaction, nodeGroupIdx, columnChunk, + localNodeGroup->getInsertInfoRef(columnID), + localNodeGroup->getUpdateInfoRef(columnID), {} /* deleteInfo */); } } } diff --git a/src/storage/store/rel_table.cpp b/src/storage/store/rel_table.cpp index d461787e2ef..89b5bd1501c 100644 --- a/src/storage/store/rel_table.cpp +++ b/src/storage/store/rel_table.cpp @@ -30,6 +30,13 @@ void RelTable::read(Transaction* transaction, TableReadState& readState, } } +void RelTable::update(transaction::Transaction* transaction, column_id_t columnID, + ValueVector* srcNodeIDVector, ValueVector* dstNodeIDVector, ValueVector* relIDVector, + ValueVector* propertyVector) { + fwdRelTableData->update(transaction, columnID, srcNodeIDVector, relIDVector, propertyVector); + bwdRelTableData->update(transaction, columnID, dstNodeIDVector, relIDVector, propertyVector); +} + void RelTable::scan(Transaction* transaction, RelDataReadState& scanState, ValueVector* inNodeIDVector, const std::vector& outputVectors) { auto tableData = getDirectedTableData(scanState.direction); @@ -61,12 +68,14 @@ void RelTable::addColumn( wal->addToUpdatedTables(tableID); } -void RelTable::prepareCommit(Transaction* /*transaction*/, LocalTableData* /*localTable*/) { +void RelTable::prepareCommit(Transaction* transaction, LocalTable* localTable) { wal->addToUpdatedTables(tableID); + fwdRelTableData->prepareLocalTableToCommit(transaction, localTable->getLocalTableData(0)); + bwdRelTableData->prepareLocalTableToCommit(transaction, localTable->getLocalTableData(1)); } -void RelTable::prepareRollback(LocalTableData* localTable) { - // DO NOTHING +void RelTable::prepareRollback(LocalTableData* localTableData) { + localTableData->clear(); } void RelTable::checkpointInMemory() { diff --git a/src/storage/store/rel_table_data.cpp b/src/storage/store/rel_table_data.cpp index 5e8086aacd9..bb7a1b3421f 100644 --- a/src/storage/store/rel_table_data.cpp +++ b/src/storage/store/rel_table_data.cpp @@ -1,6 +1,8 @@ #include "storage/store/rel_table_data.h" #include "common/assert.h" +#include "storage/local_storage/local_rel_table.h" +#include "storage/local_storage/local_table.h" #include "storage/stats/rels_store_statistics.h" using namespace kuzu::catalog; @@ -19,7 +21,7 @@ RelDataReadState::RelDataReadState(ColumnDataFormat dataFormat) } void RelDataReadState::populateCSRListEntries() { - auto csrOffsets = (common::offset_t*)csrOffsetChunk->getData(); + auto csrOffsets = (offset_t*)csrOffsetChunk->getData(); csrListEntries[0].offset = 0; csrListEntries[0].size = csrOffsets[0]; for (auto i = 1; i < numNodesInState; i++) { @@ -28,11 +30,11 @@ void RelDataReadState::populateCSRListEntries() { } } -std::pair RelDataReadState::getStartAndEndOffset() { +std::pair RelDataReadState::getStartAndEndOffset() { auto currCSRListEntry = csrListEntries[currentCSRNodeOffset - startNodeOffsetInState]; auto currCSRSize = currCSRListEntry.size; auto startOffset = currCSRListEntry.offset + posInCurrentCSR; - auto numRowsToRead = std::min(currCSRSize - posInCurrentCSR, common::DEFAULT_VECTOR_CAPACITY); + auto numRowsToRead = std::min(currCSRSize - posInCurrentCSR, DEFAULT_VECTOR_CAPACITY); posInCurrentCSR += numRowsToRead; return {startOffset, startOffset + numRowsToRead}; } @@ -42,7 +44,7 @@ RelTableData::RelTableData(BMFileHandle* dataFH, BMFileHandle* metadataFH, RelsStoreStats* relsStoreStats, RelDataDirection direction, bool enableCompression) : TableData{dataFH, metadataFH, tableSchema->tableID, bufferManager, wal, enableCompression, getDataFormatFromSchema(tableSchema, direction)}, - csrOffsetColumn{nullptr} { + direction{direction}, csrOffsetColumn{nullptr} { if (dataFormat == ColumnDataFormat::CSR) { auto csrOffsetMetadataDAHInfo = relsStoreStats->getCSROffsetMetadataDAHInfo( &DUMMY_WRITE_TRANSACTION, tableID, direction); @@ -73,7 +75,7 @@ RelTableData::RelTableData(BMFileHandle* dataFH, BMFileHandle* metadataFH, dynamic_cast(columns[REL_ID_COLUMN_ID].get())->setCommonTableID(tableID); } -void RelTableData::initializeReadState(Transaction* transaction, RelDataDirection direction, +void RelTableData::initializeReadState(Transaction* transaction, RelDataDirection /*direction*/, std::vector columnIDs, ValueVector* inNodeIDVector, RelDataReadState* readState) { readState->direction = direction; @@ -157,6 +159,15 @@ void RelTableData::lookup(Transaction* transaction, TableReadState& readState, } } +void RelTableData::update(transaction::Transaction* transaction, column_id_t columnID, + ValueVector* srcNodeIDVector, ValueVector* relIDVector, ValueVector* propertyVector) { + KU_ASSERT(columnID < columns.size() && columnID != REL_ID_COLUMN_ID); + auto localTableData = ku_dynamic_cast( + transaction->getLocalStorage()->getOrCreateLocalTableData( + tableID, columns, TableType::REL, dataFormat, getDataIdxFromDirection(direction))); + localTableData->update(srcNodeIDVector, relIDVector, columnID, propertyVector); +} + void RelTableData::append(NodeGroup* nodeGroup) { if (dataFormat == ColumnDataFormat::CSR) { auto csrNodeGroup = static_cast(nodeGroup); @@ -170,8 +181,114 @@ void RelTableData::append(NodeGroup* nodeGroup) { } void RelTableData::prepareLocalTableToCommit( - Transaction* /*transaction*/, LocalTableData* /*localTable*/) { - KU_UNREACHABLE; + Transaction* transaction, LocalTableData* localTableData) { + auto localRelTableData = ku_dynamic_cast(localTableData); + if (dataFormat == ColumnDataFormat::REGULAR) { + prepareCommitForRegularColumns(transaction, localRelTableData); + } else { + prepareCommitForCSRColumns(transaction, localRelTableData); + } +} + +void RelTableData::prepareCommitForRegularColumns( + transaction::Transaction* transaction, kuzu::storage::LocalRelTableData* localTableData) { + for (auto& [nodeGroupIdx, nodeGroup] : localTableData->nodeGroups) { + auto relNG = ku_dynamic_cast(nodeGroup.get()); + KU_ASSERT(relNG); + auto relNodeGroupInfo = + ku_dynamic_cast(relNG->getRelNGInfo()); + adjColumn->prepareCommitForChunk(transaction, nodeGroupIdx, relNG->getAdjChunk(), + relNodeGroupInfo->adjInsertInfo, {}, relNodeGroupInfo->deleteInfo); + for (auto columnID = 0u; columnID < columns.size(); columnID++) { + columns[columnID]->prepareCommitForChunk(transaction, nodeGroupIdx, + relNG->getPropertyChunk(columnID), relNodeGroupInfo->insertInfoPerChunk[columnID], + relNodeGroupInfo->updateInfoPerChunk[columnID], relNodeGroupInfo->deleteInfo); + } + } +} + +void RelTableData::prepareCommitForCSRColumns( + transaction::Transaction* transaction, kuzu::storage::LocalRelTableData* localTableData) { + for (auto& [nodeGroupIdx, nodeGroup] : localTableData->nodeGroups) { + auto relNG = ku_dynamic_cast(nodeGroup.get()); + KU_ASSERT(relNG); + auto relNodeGroupInfo = ku_dynamic_cast(relNG->getRelNGInfo()); + // First, scan the whole csr offset column chunk, whose size is NODE_GROUP_SIZE. + auto csrOffsetChunk = ColumnChunkFactory::createColumnChunk( + LogicalType::INT64(), false /* enableCompression */); + csrOffsetColumn->scan(transaction, nodeGroupIdx, csrOffsetChunk.get()); + // Next, scan the whole relID column chunk. + // TODO: We can only scan partial relID column chunk based on csr offset of the max + // nodeOffset. + auto numRels = csrOffsetChunk->getNumValues() == 0 ? + 0 : + csrOffsetChunk->getValue(csrOffsetChunk->getNumValues() - 1); + // NOTE: There is an implicit trick happening. Due to the mismatch of storage type and + // in-memory representation of INTERNAL_ID, we only store offset as INT64 on disk. Here + // we directly read relID's offset part from disk into an INT64 column chunk. + // TODO: The term of relID and relOffset is mixed. We should use relOffset instead. + auto relIDChunk = ColumnChunkFactory::createColumnChunk( + LogicalType::INT64(), false /* enableCompression */, numRels); + columns[REL_ID_COLUMN_ID]->scan(transaction, nodeGroupIdx, relIDChunk.get()); + if (relNodeGroupInfo->deleteInfo.empty() && relNodeGroupInfo->adjInsertInfo.empty()) { + // We don't need to update the csr offset column if there is no deletion or insertion. + // Thus, we can fall back to directly update the adj column and property columns based + // on csr offsets. + prepareCommitCSRNGWithoutSliding(transaction, nodeGroupIdx, relNodeGroupInfo, + csrOffsetChunk.get(), relIDChunk.get(), relNG); + } else { + // We need to update the csr offset column. Thus, we cannot simply fall back to directly + // update the adj column and property columns based on csr offsets. + KU_UNREACHABLE; + } + } +} + +static std::pair getCSRStartAndEndOffset( + offset_t nodeGroupStartOffset, offset_t* csrOffsets, offset_t nodeOffset) { + auto offsetInNodeGroup = nodeOffset - nodeGroupStartOffset; + return offsetInNodeGroup == 0 ? + std::make_pair((offset_t)0, csrOffsets[offsetInNodeGroup]) : + std::make_pair(csrOffsets[offsetInNodeGroup - 1], csrOffsets[offsetInNodeGroup]); +} + +static uint64_t findPosOfRelIDFromArray( + offset_t* relIDArray, uint64_t startPos, uint64_t endPos, offset_t relOffset) { + for (auto i = startPos; i < endPos; i++) { + if (relIDArray[i] == relOffset) { + return i; + } + } + return UINT64_MAX; +} + +void RelTableData::prepareCommitCSRNGWithoutSliding(Transaction* transaction, + node_group_idx_t nodeGroupIdx, CSRRelNGInfo* relNodeGroupInfo, ColumnChunk* csrOffsetChunk, + ColumnChunk* relIDChunk, LocalRelNG* localNodeGroup) { + // We can figure out the actual csr offset of each value to be updated based on csr and relID + // chunks. + auto csrOffsets = (offset_t*)csrOffsetChunk->getData(); + auto relIDs = (offset_t*)relIDChunk->getData(); + auto nodeGroupStartOffset = StorageUtils::getStartOffsetOfNodeGroup(nodeGroupIdx); + for (auto columnID = 0u; columnID < columns.size(); columnID++) { + std::map csrOffsetToRowIdx; + auto& updateInfo = relNodeGroupInfo->updateInfoPerChunk[columnID]; + for (auto& [nodeOffset, relIDToRowIdx] : updateInfo) { + for (auto [relID, rowIdx] : relIDToRowIdx) { + auto [startCSROffset, endCSROffset] = + getCSRStartAndEndOffset(nodeGroupStartOffset, csrOffsets, nodeOffset); + auto csrOffset = + findPosOfRelIDFromArray(relIDs, startCSROffset, endCSROffset, relID); + KU_ASSERT(csrOffset != UINT64_MAX); + csrOffsetToRowIdx[csrOffset] = rowIdx; + } + } + if (!csrOffsetToRowIdx.empty()) { + auto localChunk = localNodeGroup->getLocalColumnChunk(columnID); + columns[columnID]->prepareCommitForChunk(transaction, nodeGroupIdx, localChunk, + {} /* insertInfo */, csrOffsetToRowIdx, {} /* deleteInfo */); + } + } } void RelTableData::checkpointInMemory() { diff --git a/src/storage/store/string_column.cpp b/src/storage/store/string_column.cpp index 5cd9b4b11a0..6719a89f13c 100644 --- a/src/storage/store/string_column.cpp +++ b/src/storage/store/string_column.cpp @@ -59,6 +59,9 @@ void StringColumn::scan( Transaction* transaction, node_group_idx_t nodeGroupIdx, ColumnChunk* columnChunk) { Column::scan(transaction, nodeGroupIdx, columnChunk); auto stringColumnChunk = reinterpret_cast(columnChunk); + if (overflowMetadataDA->getNumElements(TransactionType::WRITE) <= nodeGroupIdx) { + return; + } auto overflowMetadata = overflowMetadataDA->get(nodeGroupIdx, TransactionType::WRITE); auto inMemOverflowFile = stringColumnChunk->getOverflowFile(); inMemOverflowFile->addNewPages(overflowMetadata.numPages); diff --git a/src/storage/store/struct_column_chunk.cpp b/src/storage/store/struct_column_chunk.cpp index ca83927472c..b72eeb7d16b 100644 --- a/src/storage/store/struct_column_chunk.cpp +++ b/src/storage/store/struct_column_chunk.cpp @@ -5,6 +5,8 @@ using namespace kuzu::common; namespace kuzu { namespace storage { +// TODO: need to handle this case, when the whole struct entry is null, should set all fields to +// null too. StructColumnChunk::StructColumnChunk( std::unique_ptr dataType, uint64_t capacity, bool enableCompression) : ColumnChunk{std::move(dataType), capacity} { @@ -58,7 +60,6 @@ void StructColumnChunk::write( nullChunk->setNull(offsetInChunk, vector->isNull(offsetInVector)); auto fields = StructVector::getFieldVectors(vector); for (auto i = 0u; i < fields.size(); i++) { - fields[i]->state = vector->state; childChunks[i]->write(fields[i].get(), offsetInVector, offsetInChunk); } } diff --git a/test/test_files/demo_db/demo_db_set_copy.test b/test/test_files/demo_db/demo_db_set_copy.test index 2dcf1284c8e..8320ea6bc3a 100644 --- a/test/test_files/demo_db/demo_db_set_copy.test +++ b/test/test_files/demo_db/demo_db_set_copy.test @@ -14,7 +14,7 @@ -LOG SetAgeNull -STATEMENT MATCH (u:User) WHERE u.name = 'Adam' SET u.age = NULL ---- ok --LOG ReturnNullAge +-LOG ReturnNullAge -STATEMENT MATCH (u:User) WHERE u.name='Adam' RETURN u.age ---- 1 @@ -35,7 +35,6 @@ Kitchener|0 Guelph|0 -CASE SetSingleLabelRelTest --SKIP -LOG SetRelSince -STATEMENT MATCH (u:User)-[f:Follows]->(u1:User) WHERE u.name = 'Adam' AND u1.name = 'Karissa' SET f.since=2012 ---- ok @@ -49,7 +48,6 @@ Guelph|0 ---- ok -CASE SetMultipleLabelRelTest --SKIP -LOG SetRelSince -STATEMENT MATCH (u0)-[f]->() WHERE u0.name = 'Adam' SET f.since = 1999 RETURN f; ---- 3 diff --git a/test/test_files/transaction/update_rel/update_each_element_of_large_list.test b/test/test_files/transaction/update_rel/update_each_element_of_large_list.test index 7eebf0963f8..de4d7838c0f 100644 --- a/test/test_files/transaction/update_rel/update_each_element_of_large_list.test +++ b/test/test_files/transaction/update_rel/update_each_element_of_large_list.test @@ -1,6 +1,5 @@ -GROUP UpdateRelTest_updateEachElementOfLargeList -DATASET CSV rel-update-tests --SKIP -- -CASE updateEachElementOfLargeListCommitNormalExecution diff --git a/test/test_files/transaction/update_rel/update_each_element_of_small_list.test b/test/test_files/transaction/update_rel/update_each_element_of_small_list.test index 242cac431ec..f03cbd45d21 100644 --- a/test/test_files/transaction/update_rel/update_each_element_of_small_list.test +++ b/test/test_files/transaction/update_rel/update_each_element_of_small_list.test @@ -1,6 +1,5 @@ -GROUP UpdateRelTest -DATASET CSV rel-update-tests --SKIP -- -CASE updateEachElementOfSmallListCommitNormalExecution diff --git a/test/test_files/transaction/update_rel/update_int_prop.test b/test/test_files/transaction/update_rel/update_int_prop.test index d0919aaedd8..1fc1752eada 100644 --- a/test/test_files/transaction/update_rel/update_int_prop.test +++ b/test/test_files/transaction/update_rel/update_int_prop.test @@ -1,6 +1,5 @@ -GROUP UpdateRelTest -DATASET CSV rel-update-tests --SKIP -- -DEFINE_STATEMENT_BLOCK UPDATE_INT_PROP [ diff --git a/test/test_files/transaction/update_rel/update_many_to_one_rel_table.test b/test/test_files/transaction/update_rel/update_many_to_one_rel_table.test index a2ea888a1c3..97e8ff79e48 100644 --- a/test/test_files/transaction/update_rel/update_many_to_one_rel_table.test +++ b/test/test_files/transaction/update_rel/update_many_to_one_rel_table.test @@ -1,6 +1,5 @@ -GROUP UpdateRelTest -DATASET CSV rel-update-tests --SKIP -- -DEFINE_STATEMENT_BLOCK UPDATE_MANY_TO_ONE_REL_TABLE [ -STATEMENT MATCH (p1:person)-[e:teaches]->(p2:person) WHERE p1.ID = 21 AND p2.ID = 2 SET e.length=null diff --git a/test/test_files/transaction/update_rel/update_rels_two_hop.test b/test/test_files/transaction/update_rel/update_rels_two_hop.test index 8930c2d44a4..420380d380b 100644 --- a/test/test_files/transaction/update_rel/update_rels_two_hop.test +++ b/test/test_files/transaction/update_rel/update_rels_two_hop.test @@ -1,6 +1,5 @@ -GROUP UpdateRelTest_updateRelsTwoHop -DATASET CSV rel-update-tests --SKIP -- -CASE updateRelsTwoHopCommitNormalExecution diff --git a/test/test_files/transaction/update_rel/update_str_prop.test b/test/test_files/transaction/update_rel/update_str_prop.test index 1551ac26123..1453753f371 100644 --- a/test/test_files/transaction/update_rel/update_str_prop.test +++ b/test/test_files/transaction/update_rel/update_str_prop.test @@ -1,6 +1,5 @@ -GROUP UpdateRelTest -DATASET CSV rel-update-tests --SKIP -- -DEFINE_STATEMENT_BLOCK UPDATE_STR_PROP [ diff --git a/test/test_files/update_rel/set_read_tinysnb.test b/test/test_files/update_rel/set_read_tinysnb.test index 1b9a433fc26..48dcc43013f 100644 --- a/test/test_files/update_rel/set_read_tinysnb.test +++ b/test/test_files/update_rel/set_read_tinysnb.test @@ -1,6 +1,5 @@ -GROUP TinySnbSetReadRelTest -DATASET CSV tinysnb --SKIP -- -CASE SetReadTest1 @@ -20,6 +19,7 @@ ---- 1 -CASE SetReadTest2 +-SKIP -STATEMENT CREATE REL TABLE play(FROM person TO person, date DATE, year INT64); ---- ok -STATEMENT MATCH (a:person), (b:person) WHERE a.ID=0 AND b.ID = 2 CREATE (a)-[e:play {date:date('2023-01-01'), year:2023}]->(b);