From 6f83fff17173b5148e9c1e1b9d44a74f23b67962 Mon Sep 17 00:00:00 2001 From: Tonko Sabolcec <tonko.sabolcec@memgraph.io> Date: Fri, 6 Mar 2020 13:19:08 +0100 Subject: [PATCH] Implement durability functionality for unique constraints Summary: This diff contains a necessary functionality to save and restore unique constraint operations. The previous snapshot/WAL version is backward compatible. Integration tests for migration from older snapshot and WAL versions are also included. Reviewers: mferencevic Reviewed By: mferencevic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2680 --- src/storage/v2/durability.cpp | 198 +++++++++++++++--- src/storage/v2/durability.hpp | 31 ++- src/storage/v2/storage.cpp | 41 ++-- tests/integration/apollo_runs.yaml | 9 + tests/integration/durability/runner.py | 146 +++++++++++++ .../tests/v12/test_all/expected.cypher | 11 + .../tests/v12/test_all/snapshot.bin | Bin 0 -> 946 bytes .../durability/tests/v12/test_all/wal.bin | Bin 0 -> 1023 bytes .../v12/test_constraints/expected.cypher | 10 + .../tests/v12/test_constraints/snapshot.bin | Bin 0 -> 642 bytes .../tests/v12/test_constraints/wal.bin | Bin 0 -> 1437 bytes .../tests/v12/test_edges/expected.cypher | 45 ++++ .../tests/v12/test_edges/snapshot.bin | Bin 0 -> 3123 bytes .../durability/tests/v12/test_edges/wal.bin | Bin 0 -> 5226 bytes .../expected.cypher | 24 +++ .../test_edges_with_properties/snapshot.bin | Bin 0 -> 1606 bytes .../v12/test_edges_with_properties/wal.bin | Bin 0 -> 1111 bytes .../tests/v12/test_indices/expected.cypher | 16 ++ .../tests/v12/test_indices/snapshot.bin | Bin 0 -> 898 bytes .../durability/tests/v12/test_indices/wal.bin | Bin 0 -> 2425 bytes .../tests/v12/test_vertices/expected.cypher | 16 ++ .../tests/v12/test_vertices/snapshot.bin | Bin 0 -> 1575 bytes .../tests/v12/test_vertices/wal.bin | Bin 0 -> 1778 bytes .../tests/v13/test_all/expected.cypher | 16 ++ .../tests/v13/test_all/snapshot.bin | Bin 0 -> 1467 bytes .../durability/tests/v13/test_all/wal.bin | Bin 0 -> 1731 bytes .../v13/test_constraints/expected.cypher | 6 + .../tests/v13/test_constraints/snapshot.bin | Bin 0 -> 488 bytes .../tests/v13/test_constraints/wal.bin | Bin 0 -> 349 bytes .../tests/v13/test_edges/expected.cypher | 58 +++++ .../tests/v13/test_edges/snapshot.bin | Bin 0 -> 4124 bytes .../durability/tests/v13/test_edges/wal.bin | Bin 0 -> 3024 bytes .../tests/v13/test_indices/expected.cypher | 4 + .../tests/v13/test_indices/snapshot.bin | Bin 0 -> 343 bytes .../durability/tests/v13/test_indices/wal.bin | Bin 0 -> 357 bytes .../tests/v13/test_vertices/expected.cypher | 16 ++ .../tests/v13/test_vertices/snapshot.bin | Bin 0 -> 1584 bytes .../tests/v13/test_vertices/wal.bin | Bin 0 -> 1778 bytes tests/unit/storage_v2_decoder_encoder.cpp | 2 + tests/unit/storage_v2_durability.cpp | 36 +++- tests/unit/storage_v2_wal_file.cpp | 33 ++- 41 files changed, 658 insertions(+), 60 deletions(-) create mode 100755 tests/integration/durability/runner.py create mode 100644 tests/integration/durability/tests/v12/test_all/expected.cypher create mode 100644 tests/integration/durability/tests/v12/test_all/snapshot.bin create mode 100644 tests/integration/durability/tests/v12/test_all/wal.bin create mode 100644 tests/integration/durability/tests/v12/test_constraints/expected.cypher create mode 100644 tests/integration/durability/tests/v12/test_constraints/snapshot.bin create mode 100644 tests/integration/durability/tests/v12/test_constraints/wal.bin create mode 100644 tests/integration/durability/tests/v12/test_edges/expected.cypher create mode 100644 tests/integration/durability/tests/v12/test_edges/snapshot.bin create mode 100644 tests/integration/durability/tests/v12/test_edges/wal.bin create mode 100644 tests/integration/durability/tests/v12/test_edges_with_properties/expected.cypher create mode 100644 tests/integration/durability/tests/v12/test_edges_with_properties/snapshot.bin create mode 100644 tests/integration/durability/tests/v12/test_edges_with_properties/wal.bin create mode 100644 tests/integration/durability/tests/v12/test_indices/expected.cypher create mode 100644 tests/integration/durability/tests/v12/test_indices/snapshot.bin create mode 100644 tests/integration/durability/tests/v12/test_indices/wal.bin create mode 100644 tests/integration/durability/tests/v12/test_vertices/expected.cypher create mode 100644 tests/integration/durability/tests/v12/test_vertices/snapshot.bin create mode 100644 tests/integration/durability/tests/v12/test_vertices/wal.bin create mode 100644 tests/integration/durability/tests/v13/test_all/expected.cypher create mode 100644 tests/integration/durability/tests/v13/test_all/snapshot.bin create mode 100644 tests/integration/durability/tests/v13/test_all/wal.bin create mode 100644 tests/integration/durability/tests/v13/test_constraints/expected.cypher create mode 100644 tests/integration/durability/tests/v13/test_constraints/snapshot.bin create mode 100644 tests/integration/durability/tests/v13/test_constraints/wal.bin create mode 100644 tests/integration/durability/tests/v13/test_edges/expected.cypher create mode 100644 tests/integration/durability/tests/v13/test_edges/snapshot.bin create mode 100644 tests/integration/durability/tests/v13/test_edges/wal.bin create mode 100644 tests/integration/durability/tests/v13/test_indices/expected.cypher create mode 100644 tests/integration/durability/tests/v13/test_indices/snapshot.bin create mode 100644 tests/integration/durability/tests/v13/test_indices/wal.bin create mode 100644 tests/integration/durability/tests/v13/test_vertices/expected.cypher create mode 100644 tests/integration/durability/tests/v13/test_vertices/snapshot.bin create mode 100644 tests/integration/durability/tests/v13/test_vertices/wal.bin diff --git a/src/storage/v2/durability.cpp b/src/storage/v2/durability.cpp index 1767dbb35..9b0d38642 100644 --- a/src/storage/v2/durability.cpp +++ b/src/storage/v2/durability.cpp @@ -323,6 +323,8 @@ std::optional<PropertyValue> Decoder::ReadPropertyValue() { case Marker::DELTA_LABEL_PROPERTY_INDEX_DROP: case Marker::DELTA_EXISTENCE_CONSTRAINT_CREATE: case Marker::DELTA_EXISTENCE_CONSTRAINT_DROP: + case Marker::DELTA_UNIQUE_CONSTRAINT_CREATE: + case Marker::DELTA_UNIQUE_CONSTRAINT_DROP: case Marker::VALUE_FALSE: case Marker::VALUE_TRUE: return std::nullopt; @@ -416,6 +418,8 @@ bool Decoder::SkipPropertyValue() { case Marker::DELTA_LABEL_PROPERTY_INDEX_DROP: case Marker::DELTA_EXISTENCE_CONSTRAINT_CREATE: case Marker::DELTA_EXISTENCE_CONSTRAINT_DROP: + case Marker::DELTA_UNIQUE_CONSTRAINT_CREATE: + case Marker::DELTA_UNIQUE_CONSTRAINT_DROP: case Marker::VALUE_FALSE: case Marker::VALUE_TRUE: return false; @@ -438,7 +442,10 @@ namespace { // The current version of snapshot and WAL encoding / decoding. // IMPORTANT: Please bump this version for every snapshot and/or WAL format // change!!! -const uint64_t kVersion{12}; +const uint64_t kVersion{13}; + +const uint64_t kOldestSupportedVersion{12}; +const uint64_t kUniqueConstraintVersion{13}; // Snapshot format: // @@ -484,6 +491,9 @@ const uint64_t kVersion{12}; // * existence constraints // * label // * property +// * unique constraints (from version 13) +// * label +// * properties // // 8) Name to ID mapper data // * id to name mappings @@ -542,6 +552,9 @@ const uint64_t kVersion{12}; // existence constraint create, existence constraint drop // * label name // * property name +// * unique constraint create, unique constraint drop +// * label name +// * property names // This is the prefix used for Snapshot and WAL filenames. It is a timestamp // format that equals to: YYYYmmddHHMMSSffffff @@ -583,6 +596,10 @@ Marker OperationToMarker(StorageGlobalOperation operation) { return Marker::DELTA_EXISTENCE_CONSTRAINT_CREATE; case StorageGlobalOperation::EXISTENCE_CONSTRAINT_DROP: return Marker::DELTA_EXISTENCE_CONSTRAINT_DROP; + case StorageGlobalOperation::UNIQUE_CONSTRAINT_CREATE: + return Marker::DELTA_UNIQUE_CONSTRAINT_CREATE; + case StorageGlobalOperation::UNIQUE_CONSTRAINT_DROP: + return Marker::DELTA_UNIQUE_CONSTRAINT_DROP; } } @@ -647,6 +664,10 @@ WalDeltaData::Type MarkerToWalDeltaDataType(Marker marker) { return WalDeltaData::Type::EXISTENCE_CONSTRAINT_CREATE; case Marker::DELTA_EXISTENCE_CONSTRAINT_DROP: return WalDeltaData::Type::EXISTENCE_CONSTRAINT_DROP; + case Marker::DELTA_UNIQUE_CONSTRAINT_CREATE: + return WalDeltaData::Type::UNIQUE_CONSTRAINT_CREATE; + case Marker::DELTA_UNIQUE_CONSTRAINT_DROP: + return WalDeltaData::Type::UNIQUE_CONSTRAINT_DROP; case Marker::TYPE_NULL: case Marker::TYPE_BOOL: @@ -697,6 +718,8 @@ bool IsWalDeltaDataTypeTransactionEnd(WalDeltaData::Type type) { case WalDeltaData::Type::LABEL_PROPERTY_INDEX_DROP: case WalDeltaData::Type::EXISTENCE_CONSTRAINT_CREATE: case WalDeltaData::Type::EXISTENCE_CONSTRAINT_DROP: + case WalDeltaData::Type::UNIQUE_CONSTRAINT_CREATE: + case WalDeltaData::Type::UNIQUE_CONSTRAINT_DROP: return true; } } @@ -805,6 +828,29 @@ WalDeltaData ReadSkipWalDeltaData(Decoder *wal) { } break; } + case WalDeltaData::Type::UNIQUE_CONSTRAINT_CREATE: + case WalDeltaData::Type::UNIQUE_CONSTRAINT_DROP: { + if constexpr (read_data) { + auto label = wal->ReadString(); + if (!label) throw RecoveryFailure("Invalid WAL data!"); + delta.operation_label_properties.label = std::move(*label); + auto properties_count = wal->ReadUint(); + if (!properties_count) throw RecoveryFailure("Invalid WAL data!"); + for (uint64_t i = 0; i < *properties_count; ++i) { + auto property = wal->ReadString(); + if (!property) throw RecoveryFailure("Invalid WAL data!"); + delta.operation_label_properties.properties.emplace( + std::move(*property)); + } + } else { + if (!wal->SkipString()) throw RecoveryFailure("Invalid WAL data!"); + auto properties_count = wal->ReadUint(); + if (!properties_count) throw RecoveryFailure("Invalid WAL data!"); + for (uint64_t i = 0; i < *properties_count; ++i) { + if (!wal->SkipString()) throw RecoveryFailure("Invalid WAL data!"); + } + } + } } return delta; @@ -835,6 +881,10 @@ void RemoveRecoveredIndexConstraint(std::vector<TObj> *list, TObj obj, throw RecoveryFailure(error_message); } } + +bool IsVersionSupported(uint64_t version) { + return version >= kOldestSupportedVersion && version <= kVersion; +} } // namespace // Function used to read information about the snapshot file. @@ -844,7 +894,8 @@ SnapshotInfo ReadSnapshotInfo(const std::filesystem::path &path) { auto version = snapshot.Initialize(path, kSnapshotMagic); if (!version) throw RecoveryFailure("Couldn't read snapshot magic and/or version!"); - if (*version != kVersion) throw RecoveryFailure("Invalid snapshot version!"); + if (!IsVersionSupported(*version)) + throw RecoveryFailure("Invalid snapshot version!"); // Prepare return value. SnapshotInfo info; @@ -912,7 +963,8 @@ WalInfo ReadWalInfo(const std::filesystem::path &path) { auto version = wal.Initialize(path, kWalMagic); if (!version) throw RecoveryFailure("Couldn't read WAL magic and/or version!"); - if (*version != kVersion) throw RecoveryFailure("Invalid WAL version!"); + if (!IsVersionSupported(*version)) + throw RecoveryFailure("Invalid WAL version!"); // Prepare return value. WalInfo info; @@ -1045,6 +1097,12 @@ bool operator==(const WalDeltaData &a, const WalDeltaData &b) { b.operation_label_property.label && a.operation_label_property.property == b.operation_label_property.property; + case WalDeltaData::Type::UNIQUE_CONSTRAINT_CREATE: + case WalDeltaData::Type::UNIQUE_CONSTRAINT_DROP: + return a.operation_label_properties.label == + b.operation_label_properties.label && + a.operation_label_properties.properties == + b.operation_label_properties.properties; } } bool operator!=(const WalDeltaData &a, const WalDeltaData &b) { @@ -1063,14 +1121,14 @@ uint64_t ReadWalDeltaHeader(Decoder *wal) { return *timestamp; } -// Function used to either read the current WAL delta data. The WAL delta header -// must be read before calling this function. +// Function used to read the current WAL delta data. The WAL delta header must +// be read before calling this function. WalDeltaData ReadWalDeltaData(Decoder *wal) { return ReadSkipWalDeltaData<true>(wal); } -// Function used to either skip the current WAL delta data. The WAL delta header -// must be read before calling this function. +// Function used to skip the current WAL delta data. The WAL delta header must +// be read before calling this function. WalDeltaData::Type SkipWalDeltaData(Decoder *wal) { auto delta = ReadSkipWalDeltaData<false>(wal); return delta.type; @@ -1245,13 +1303,14 @@ void WalFile::AppendTransactionEnd(uint64_t timestamp) { } void WalFile::AppendOperation(StorageGlobalOperation operation, LabelId label, - std::optional<PropertyId> property, + const std::set<PropertyId> &properties, uint64_t timestamp) { wal_.WriteMarker(Marker::SECTION_DELTA); wal_.WriteUint(timestamp); switch (operation) { case StorageGlobalOperation::LABEL_INDEX_CREATE: case StorageGlobalOperation::LABEL_INDEX_DROP: { + CHECK(properties.empty()) << "Invalid function call!"; wal_.WriteMarker(OperationToMarker(operation)); wal_.WriteString(name_id_mapper_->IdToName(label.AsUint())); break; @@ -1260,10 +1319,22 @@ void WalFile::AppendOperation(StorageGlobalOperation operation, LabelId label, case StorageGlobalOperation::LABEL_PROPERTY_INDEX_DROP: case StorageGlobalOperation::EXISTENCE_CONSTRAINT_CREATE: case StorageGlobalOperation::EXISTENCE_CONSTRAINT_DROP: { - CHECK(property) << "Invalid function call!"; + CHECK(properties.size() == 1) << "Invalid function call!"; wal_.WriteMarker(OperationToMarker(operation)); wal_.WriteString(name_id_mapper_->IdToName(label.AsUint())); - wal_.WriteString(name_id_mapper_->IdToName(property->AsUint())); + wal_.WriteString( + name_id_mapper_->IdToName((*properties.begin()).AsUint())); + break; + } + case StorageGlobalOperation::UNIQUE_CONSTRAINT_CREATE: + case StorageGlobalOperation::UNIQUE_CONSTRAINT_DROP: { + CHECK(!properties.empty()) << "Invalid function call!"; + wal_.WriteMarker(OperationToMarker(operation)); + wal_.WriteString(name_id_mapper_->IdToName(label.AsUint())); + wal_.WriteUint(properties.size()); + for (const auto &property : properties) { + wal_.WriteString(name_id_mapper_->IdToName(property.AsUint())); + } break; } } @@ -1530,10 +1601,10 @@ void Durability::AppendToWal(const Transaction &transaction, } void Durability::AppendToWal(StorageGlobalOperation operation, LabelId label, - std::optional<PropertyId> property, + const std::set<PropertyId> &properties, uint64_t final_commit_timestamp) { if (!InitializeWalFile()) return; - wal_file_->AppendOperation(operation, label, property, + wal_file_->AppendOperation(operation, label, properties, final_commit_timestamp); FinalizeWalFile(); } @@ -1742,6 +1813,19 @@ void Durability::CreateSnapshot(Transaction *transaction) { write_mapping(item.second); } } + + // Write unique constraints. + { + auto unique = constraints_->unique_constraints.ListConstraints(); + snapshot.WriteUint(unique.size()); + for (const auto &item : unique) { + write_mapping(item.first); + snapshot.WriteUint(item.second.size()); + for (const auto &property : item.second) { + write_mapping(property); + } + } + } } // Write mapper data. @@ -1873,7 +1957,9 @@ void Durability::CreateSnapshot(Transaction *transaction) { } std::optional<Durability::RecoveryInfo> Durability::RecoverData() { - if (!utils::DirExists(snapshot_directory_)) return std::nullopt; + if (!utils::DirExists(snapshot_directory_) && + !utils::DirExists(wal_directory_)) + return std::nullopt; // Helper lambda used to recover all discovered indices and constraints. The // indices and constraints must be recovered after the data recovery is done @@ -1901,23 +1987,34 @@ std::optional<Durability::RecoveryInfo> Durability::RecoverData() { if (ret.HasError() || !ret.GetValue()) throw RecoveryFailure("The existence constraint must be created here!"); } + + // Recover unique constraints. + for (const auto &item : indices_constraints.constraints.unique) { + auto ret = constraints_->unique_constraints.CreateConstraint( + item.first, item.second, vertices_->access()); + if (ret.HasError() || + ret.GetValue() != UniqueConstraints::CreationStatus::SUCCESS) + throw RecoveryFailure("The unique constraint must be created here!"); + } }; // Array of all discovered snapshots, ordered by name. std::vector<std::pair<std::filesystem::path, std::string>> snapshot_files; std::error_code error_code; - for (const auto &item : - std::filesystem::directory_iterator(snapshot_directory_, error_code)) { - if (!item.is_regular_file()) continue; - try { - auto info = ReadSnapshotInfo(item.path()); - snapshot_files.emplace_back(item.path(), info.uuid); - } catch (const RecoveryFailure &) { - continue; + if (utils::DirExists(snapshot_directory_)) { + for (const auto &item : + std::filesystem::directory_iterator(snapshot_directory_, error_code)) { + if (!item.is_regular_file()) continue; + try { + auto info = ReadSnapshotInfo(item.path()); + snapshot_files.emplace_back(item.path(), info.uuid); + } catch (const RecoveryFailure &) { + continue; + } } + CHECK(!error_code) << "Couldn't recover data because an error occurred: " + << error_code.message() << "!"; } - CHECK(!error_code) << "Couldn't recover data because an error occurred: " - << error_code.message() << "!"; RecoveryInfo recovery_info; RecoveredIndicesAndConstraints indices_constraints; @@ -2066,7 +2163,8 @@ Durability::RecoveredSnapshot Durability::LoadSnapshot( auto version = snapshot.Initialize(path, kSnapshotMagic); if (!version) throw RecoveryFailure("Couldn't read snapshot magic and/or version!"); - if (*version != kVersion) throw RecoveryFailure("Invalid snapshot version!"); + if (!IsVersionSupported(*version)) + throw RecoveryFailure("Invalid snapshot version!"); // Cleanup of loaded data in case of failure. bool success = false; @@ -2448,6 +2546,29 @@ Durability::RecoveredSnapshot Durability::LoadSnapshot( "The existence constraint already exists!"); } } + + // Recover unique constraints. + // Snapshot version should be checked since unique constraints were + // implemented in later versions of snapshot. + if (*version >= kUniqueConstraintVersion) { + auto size = snapshot.ReadUint(); + if (!size) throw RecoveryFailure("Invalid snapshot data!"); + for (uint64_t i = 0; i < *size; ++i) { + auto label = snapshot.ReadUint(); + if (!label) throw RecoveryFailure("Invalid snapshot data!"); + auto properties_count = snapshot.ReadUint(); + if (!properties_count) throw RecoveryFailure("Invalid snapshot data!"); + std::set<PropertyId> properties; + for (uint64_t j = 0; j < *properties_count; ++j) { + auto property = snapshot.ReadUint(); + if (!property) throw RecoveryFailure("Invalid snapshot data!"); + properties.insert(get_property_from_id(*property)); + } + AddRecoveredIndexConstraint(&indices_constraints.constraints.unique, + {get_label_from_id(*label), properties}, + "The unique constraint already exists!"); + } + } } // Recover timestamp. @@ -2469,7 +2590,8 @@ Durability::RecoveryInfo Durability::LoadWal( auto version = wal.Initialize(path, kWalMagic); if (!version) throw RecoveryFailure("Couldn't read WAL magic and/or version!"); - if (*version != kVersion) throw RecoveryFailure("Invalid WAL version!"); + if (!IsVersionSupported(*version)) + throw RecoveryFailure("Invalid WAL version!"); // Read wal info. auto info = ReadWalInfo(path); @@ -2744,6 +2866,32 @@ Durability::RecoveryInfo Durability::LoadWal( "The existence constraint doesn't exist!"); break; } + case WalDeltaData::Type::UNIQUE_CONSTRAINT_CREATE: { + auto label_id = LabelId::FromUint(name_id_mapper_->NameToId( + delta.operation_label_properties.label)); + std::set<PropertyId> property_ids; + for (const auto &prop : delta.operation_label_properties.properties) { + property_ids.insert( + PropertyId::FromUint(name_id_mapper_->NameToId(prop))); + } + AddRecoveredIndexConstraint(&indices_constraints->constraints.unique, + {label_id, property_ids}, + "The unique constraint already exists!"); + break; + } + case WalDeltaData::Type::UNIQUE_CONSTRAINT_DROP: { + auto label_id = LabelId::FromUint(name_id_mapper_->NameToId( + delta.operation_label_properties.label)); + std::set<PropertyId> property_ids; + for (const auto &prop : delta.operation_label_properties.properties) { + property_ids.insert( + PropertyId::FromUint(name_id_mapper_->NameToId(prop))); + } + RemoveRecoveredIndexConstraint( + &indices_constraints->constraints.unique, + {label_id, property_ids}, "The unique constraint doesn't exist!"); + break; + } } ret.next_timestamp = std::max(ret.next_timestamp, timestamp + 1); ++deltas_applied; diff --git a/src/storage/v2/durability.hpp b/src/storage/v2/durability.hpp index ddc780814..ac6720a9d 100644 --- a/src/storage/v2/durability.hpp +++ b/src/storage/v2/durability.hpp @@ -73,6 +73,8 @@ enum class Marker : uint8_t { DELTA_LABEL_PROPERTY_INDEX_DROP = 0x5c, DELTA_EXISTENCE_CONSTRAINT_CREATE = 0x5d, DELTA_EXISTENCE_CONSTRAINT_DROP = 0x5e, + DELTA_UNIQUE_CONSTRAINT_CREATE = 0x5f, + DELTA_UNIQUE_CONSTRAINT_DROP = 0x60, VALUE_FALSE = 0x00, VALUE_TRUE = 0xff, @@ -112,6 +114,8 @@ static const Marker kMarkersAll[] = { Marker::DELTA_LABEL_PROPERTY_INDEX_DROP, Marker::DELTA_EXISTENCE_CONSTRAINT_CREATE, Marker::DELTA_EXISTENCE_CONSTRAINT_DROP, + Marker::DELTA_UNIQUE_CONSTRAINT_CREATE, + Marker::DELTA_UNIQUE_CONSTRAINT_DROP, Marker::VALUE_FALSE, Marker::VALUE_TRUE, }; @@ -233,6 +237,8 @@ struct WalDeltaData { LABEL_PROPERTY_INDEX_DROP, EXISTENCE_CONSTRAINT_CREATE, EXISTENCE_CONSTRAINT_DROP, + UNIQUE_CONSTRAINT_CREATE, + UNIQUE_CONSTRAINT_DROP, }; Type type{Type::TRANSACTION_END}; @@ -267,6 +273,11 @@ struct WalDeltaData { std::string label; std::string property; } operation_label_property; + + struct { + std::string label; + std::set<std::string> properties; + } operation_label_properties; }; bool operator==(const WalDeltaData &a, const WalDeltaData &b); @@ -277,15 +288,15 @@ bool operator!=(const WalDeltaData &a, const WalDeltaData &b); /// @throw RecoveryFailure uint64_t ReadWalDeltaHeader(Decoder *wal); -/// Function used to either read the current WAL delta data. The function -/// returns the read delta data. The WAL delta header must be read before -/// calling this function. +/// Function used to read the current WAL delta data. The function returns the +/// read delta data. The WAL delta header must be read before calling this +/// function. /// @throw RecoveryFailure WalDeltaData ReadWalDeltaData(Decoder *wal); -/// Function used to either skip the current WAL delta data. The function -/// returns the skipped delta type. The WAL delta header must be read before -/// calling this function. +/// Function used to skip the current WAL delta data. The function returns the +/// skipped delta type. The WAL delta header must be read before calling this +/// function. /// @throw RecoveryFailure WalDeltaData::Type SkipWalDeltaData(Decoder *wal); @@ -297,6 +308,8 @@ enum class StorageGlobalOperation { LABEL_PROPERTY_INDEX_DROP, EXISTENCE_CONSTRAINT_CREATE, EXISTENCE_CONSTRAINT_DROP, + UNIQUE_CONSTRAINT_CREATE, + UNIQUE_CONSTRAINT_DROP, }; /// Structure used to track indices and constraints during recovery. @@ -308,6 +321,7 @@ struct RecoveredIndicesAndConstraints { struct { std::vector<std::pair<LabelId, PropertyId>> existence; + std::vector<std::pair<LabelId, std::set<PropertyId>>> unique; } constraints; }; @@ -331,7 +345,8 @@ class WalFile { void AppendTransactionEnd(uint64_t timestamp); void AppendOperation(StorageGlobalOperation operation, LabelId label, - std::optional<PropertyId> property, uint64_t timestamp); + const std::set<PropertyId> &properties, + uint64_t timestamp); void Sync(); @@ -380,7 +395,7 @@ class Durability final { uint64_t final_commit_timestamp); void AppendToWal(StorageGlobalOperation operation, LabelId label, - std::optional<PropertyId> property, + const std::set<PropertyId> &properties, uint64_t final_commit_timestamp); private: diff --git a/src/storage/v2/storage.cpp b/src/storage/v2/storage.cpp index c3697571b..3ba75cd38 100644 --- a/src/storage/v2/storage.cpp +++ b/src/storage/v2/storage.cpp @@ -990,8 +990,8 @@ bool Storage::CreateIndex(LabelId label) { // next regular transaction after this operation. This prevents collisions of // commit timestamps between non-transactional operations and transactional // operations. - durability_.AppendToWal(StorageGlobalOperation::LABEL_INDEX_CREATE, label, - std::nullopt, timestamp_); + durability_.AppendToWal(StorageGlobalOperation::LABEL_INDEX_CREATE, label, {}, + timestamp_); return true; } @@ -1003,7 +1003,7 @@ bool Storage::CreateIndex(LabelId label, PropertyId property) { // For a description why using `timestamp_` is correct, see // `CreateIndex(LabelId label)`. durability_.AppendToWal(StorageGlobalOperation::LABEL_PROPERTY_INDEX_CREATE, - label, property, timestamp_); + label, {property}, timestamp_); return true; } @@ -1012,8 +1012,8 @@ bool Storage::DropIndex(LabelId label) { if (!indices_.label_index.DropIndex(label)) return false; // For a description why using `timestamp_` is correct, see // `CreateIndex(LabelId label)`. - durability_.AppendToWal(StorageGlobalOperation::LABEL_INDEX_DROP, label, - std::nullopt, timestamp_); + durability_.AppendToWal(StorageGlobalOperation::LABEL_INDEX_DROP, label, {}, + timestamp_); return true; } @@ -1023,7 +1023,7 @@ bool Storage::DropIndex(LabelId label, PropertyId property) { // For a description why using `timestamp_` is correct, see // `CreateIndex(LabelId label)`. durability_.AppendToWal(StorageGlobalOperation::LABEL_PROPERTY_INDEX_DROP, - label, property, timestamp_); + label, {property}, timestamp_); return true; } @@ -1042,7 +1042,7 @@ Storage::CreateExistenceConstraint(LabelId label, PropertyId property) { // For a description why using `timestamp_` is correct, see // `CreateIndex(LabelId label)`. durability_.AppendToWal(StorageGlobalOperation::EXISTENCE_CONSTRAINT_CREATE, - label, property, timestamp_); + label, {property}, timestamp_); return true; } @@ -1053,7 +1053,7 @@ bool Storage::DropExistenceConstraint(LabelId label, PropertyId property) { // For a description why using `timestamp_` is correct, see // `CreateIndex(LabelId label)`. durability_.AppendToWal(StorageGlobalOperation::EXISTENCE_CONSTRAINT_DROP, - label, property, timestamp_); + label, {property}, timestamp_); return true; } @@ -1061,16 +1061,31 @@ utils::BasicResult<ConstraintViolation, UniqueConstraints::CreationStatus> Storage::CreateUniqueConstraint(LabelId label, const std::set<PropertyId> &properties) { std::unique_lock<utils::RWLock> storage_guard(main_lock_); - // TODO(tsabolcec): Append action to the WAL. - return constraints_.unique_constraints.CreateConstraint(label, properties, - vertices_.access()); + auto ret = constraints_.unique_constraints.CreateConstraint( + label, properties, vertices_.access()); + if (ret.HasError() || + ret.GetValue() != UniqueConstraints::CreationStatus::SUCCESS) { + return ret; + } + // For a description why using `timestamp_` is correct, see + // `CreateIndex(LabelId label)`. + durability_.AppendToWal(StorageGlobalOperation::UNIQUE_CONSTRAINT_CREATE, + label, properties, timestamp_); + return UniqueConstraints::CreationStatus::SUCCESS; } UniqueConstraints::DeletionStatus Storage::DropUniqueConstraint( LabelId label, const std::set<PropertyId> &properties) { std::unique_lock<utils::RWLock> storage_guard(main_lock_); - // TODO(tsabolcec): Append action to the WAL. - return constraints_.unique_constraints.DropConstraint(label, properties); + auto ret = constraints_.unique_constraints.DropConstraint(label, properties); + if (ret != UniqueConstraints::DeletionStatus::SUCCESS) { + return ret; + } + // For a description why using `timestamp_` is correct, see + // `CreateIndex(LabelId label)`. + durability_.AppendToWal(StorageGlobalOperation::UNIQUE_CONSTRAINT_DROP, label, + properties, timestamp_); + return UniqueConstraints::DeletionStatus::SUCCESS; } ConstraintsInfo Storage::ListAllConstraints() const { diff --git a/tests/integration/apollo_runs.yaml b/tests/integration/apollo_runs.yaml index 56911d084..3cf4d6454 100644 --- a/tests/integration/apollo_runs.yaml +++ b/tests/integration/apollo_runs.yaml @@ -65,6 +65,15 @@ - ../../../build_debug/src/mg_import_csv # mg_import_csv binary - ../../../build_debug/tests/integration/mg_import_csv/tester # tester binary +- name: integration__durability + cd: durability + commands: ./runner.py + infiles: + - runner.py # runner script + - tests # tests directory + - ../../../build_debug/memgraph # memgraph binary + - ../../../build_debug/tools/src/mg_dump # memgraph dump binary + #- name: integration__ha_basic # cd: ha/basic # commands: TIMEOUT=480 ./runner.py diff --git a/tests/integration/durability/runner.py b/tests/integration/durability/runner.py new file mode 100755 index 000000000..2ab1762bd --- /dev/null +++ b/tests/integration/durability/runner.py @@ -0,0 +1,146 @@ +#!/usr/bin/python3 -u +import argparse +import atexit +import os +import shutil +import subprocess +import sys +import tempfile +import time + + +SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) +PROJECT_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, "..", "..", "..")) +TESTS_DIR = os.path.join(SCRIPT_DIR, "tests") + +SNAPSHOT_FILE_NAME = "snapshot.bin" +WAL_FILE_NAME = "wal.bin" +DUMP_FILE_NAME = "expected.cypher" + + +def wait_for_server(port, delay=0.1): + cmd = ["nc", "-z", "-w", "1", "127.0.0.1", str(port)] + while subprocess.call(cmd) != 0: + time.sleep(0.01) + time.sleep(delay) + + +def sorted_content(file_path): + with open(file_path, 'r') as fin: + return sorted(list(map(lambda x: x.strip(), fin.readlines()))) + + +def list_to_string(data): + ret = "[\n" + for row in data: + ret += " " + row + "\n" + ret += "]" + return ret + + +def execute_test(memgraph_binary, dump_binary, test_directory, test_type): + assert test_type in ["SNAPSHOT", "WAL"], \ + "Test type should be either 'SNAPSHOT' or 'WAL'." + print("\033[1;36m~~ Executing test {} ({}) ~~\033[0m" + .format(os.path.relpath(test_directory, TESTS_DIR), test_type)) + + working_data_directory = tempfile.TemporaryDirectory() + if test_type == "SNAPSHOT": + snapshots_dir = os.path.join(working_data_directory.name, "snapshots") + os.makedirs(snapshots_dir) + shutil.copy(os.path.join(test_directory, SNAPSHOT_FILE_NAME), + snapshots_dir) + else: + wal_dir = os.path.join(working_data_directory.name, "wal") + os.makedirs(wal_dir) + shutil.copy(os.path.join(test_directory, WAL_FILE_NAME), wal_dir) + + memgraph_args = [memgraph_binary, + "--storage-recover-on-startup", + "--storage-properties-on-edges", + "--data-directory", working_data_directory.name] + + # Start the memgraph binary + memgraph = subprocess.Popen(memgraph_args) + time.sleep(0.1) + assert memgraph.poll() is None, "Memgraph process died prematurely!" + wait_for_server(7687) + + # Register cleanup function + @atexit.register + def cleanup(): + if memgraph.poll() is None: + memgraph.terminate() + assert memgraph.wait() == 0, "Memgraph process didn't exit cleanly!" + + # Execute `database dump` + dump_output_file = tempfile.NamedTemporaryFile() + dump_args = [dump_binary, "--use-ssl=false"] + subprocess.run(dump_args, stdout=dump_output_file, check=True) + + # Shutdown the memgraph binary + memgraph.terminate() + assert memgraph.wait() == 0, "Memgraph process didn't exit cleanly!" + + # Compare dump files + expected_dump_file = os.path.join(test_directory, DUMP_FILE_NAME) + assert os.path.exists(expected_dump_file), \ + "Could not find expected dump path {}".format(expected_dump_file) + queries_got = sorted_content(dump_output_file.name) + queries_expected = sorted_content(expected_dump_file) + assert queries_got == queries_expected, "Expected\n{}\nto be equal to\n" \ + "{}".format(list_to_string(queries_got), + list_to_string(queries_expected)) + + print("\033[1;32m~~ Test successful ~~\033[0m\n") + + +def find_test_directories(directory): + """ + Finds all test directories. Test directory is a directory two levels below + the given directory which contains files 'snapshot.bin', 'wal.bin' and + 'expected.cypher'. + """ + test_dirs = [] + for entry_version in os.listdir(directory): + entry_version_path = os.path.join(directory, entry_version) + if not os.path.isdir(entry_version_path): + continue + for test_dir in os.listdir(entry_version_path): + test_dir_path = os.path.join(entry_version_path, test_dir) + if not os.path.isdir(test_dir_path): + continue + snapshot_file = os.path.join(test_dir_path, SNAPSHOT_FILE_NAME) + wal_file = os.path.join(test_dir_path, WAL_FILE_NAME) + dump_file = os.path.join(test_dir_path, DUMP_FILE_NAME) + if (os.path.isfile(snapshot_file) and os.path.isfile(dump_file) and + os.path.isfile(wal_file)): + test_dirs.append(test_dir_path) + else: + raise Exception("Missing data in test directory '{}'" + .format(test_dir_path)) + return test_dirs + + +if __name__ == "__main__": + memgraph_binary = os.path.join(PROJECT_DIR, "build", "memgraph") + if not os.path.exists(memgraph_binary): + memgraph_binary = os.path.join(PROJECT_DIR, "build_debug", "memgraph") + dump_binary = os.path.join(PROJECT_DIR, "build", "tools", "src", "mg_dump") + if not os.path.exists(dump_binary): + dump_binary = os.path.join(PROJECT_DIR, "build_debug", "tools", "src", + "mg_dump") + + parser = argparse.ArgumentParser() + parser.add_argument("--memgraph", default=memgraph_binary) + parser.add_argument("--dump", default=dump_binary) + args = parser.parse_args() + + test_directories = find_test_directories(TESTS_DIR) + assert len(test_directories) > 0, "No tests have been found!" + + for test_directory in test_directories: + execute_test(args.memgraph, args.dump, test_directory, "SNAPSHOT") + execute_test(args.memgraph, args.dump, test_directory, "WAL") + + sys.exit(0) diff --git a/tests/integration/durability/tests/v12/test_all/expected.cypher b/tests/integration/durability/tests/v12/test_all/expected.cypher new file mode 100644 index 000000000..d788a176b --- /dev/null +++ b/tests/integration/durability/tests/v12/test_all/expected.cypher @@ -0,0 +1,11 @@ +CREATE INDEX ON :label; +CREATE INDEX ON :label2(prop2); +CREATE INDEX ON :label2(prop); +CREATE CONSTRAINT ON (u:label) ASSERT EXISTS (u.ext); +CREATE INDEX ON :__mg_vertex__(__mg_id__); +CREATE (:__mg_vertex__:label2 {__mg_id__: 0, prop2: ["kaj", 2, Null, {prop4: -1.341}], prop: "joj", ext: 2}); +CREATE (:__mg_vertex__:label:label2 {__mg_id__: 1, prop: "joj", ext: 2}); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 0 CREATE (u)-[:link {prop: -1, ext: [false, {k: "l"}]}]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 1 CREATE (u)-[:link {prop: -1, ext: [false, {k: "l"}]}]->(v); +DROP INDEX ON :__mg_vertex__(__mg_id__); +MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__; diff --git a/tests/integration/durability/tests/v12/test_all/snapshot.bin b/tests/integration/durability/tests/v12/test_all/snapshot.bin new file mode 100644 index 0000000000000000000000000000000000000000..b687336fccdbe5ff1f79453f97ad8aa7499fa6cd GIT binary patch literal 946 zcmb_ZyH3ME5Io)u1uPe?O(2w(Y?BbVg@THYI6E$4TS){dC=g#3iQhotJ1A0tf_LVS zl7fP(MmMv&vpWy>Pt#-5zFWCdBrAZI-4@9b;2mJH*HS%Eu@R+=C}o^{>63j`xeiqB z)VT8<{k8zLBW!wt-+yo24X!zZ0QNc6b!gv$tbOHz;N;gT2fEYzaW)^gvyatlwOo9@ zg{!bfHLJp5*eq<#%hKARe$jq6sBu_wTPV19fA+x91oW^ukcO#Y6#=Mzu*WiXq<ZrQ zD_m`<M@$!jDLjTnERE00hP*{dGnv+mfZ}zADF<0}{ppc1Q8ro2nJ7<OJ})k9UzhYr kHcE%Xapa3Ei3UCyN4YQ4=(>n~9v3%0E;2bn%v>Mf8)AP!#sB~S literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v12/test_all/wal.bin b/tests/integration/durability/tests/v12/test_all/wal.bin new file mode 100644 index 0000000000000000000000000000000000000000..9d0e369003d6d69f81e3107c67bdfaac289e49d3 GIT binary patch literal 1023 zcmbVLJx{_=6fKHQ4Dx*VMg~o{CT#&tR~IJ}LO>vd;k9(2ZG;#So&04c{t$nM0~5C8 zemt}>#!k2Qo^#K+_qO+U&soFTKewU{k$8aMQeHu%z2L_0^%jnf0?+AcZ{dVG_MJ}T z>d=j@wHpNju$E2HRGvUG(_x%(VJ1(_f={^h(`xPif~y0qz%gu2{8C3A11f&m*rEn6 zeX_;50p-3KV#tsornn-JWR+B+4HHTnfr&~aE~y+|wNQp7Qk`%Mb*3(GCYrmTcGdaE z_BDCie7>{H#1?3zPpjbEfO6CdPK7K3ad<OsGUO)cz3{{S%N1cm1C6QtPCOs?&AsDl zMnA8REgw@7rm{()EupJJ3YM8NrZy5O<sf1QPbJixqJ@W6P0KhFN~KC-nEwqp+?dAr PgDmpt`zx0BFWJc#W+G!B literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v12/test_constraints/expected.cypher b/tests/integration/durability/tests/v12/test_constraints/expected.cypher new file mode 100644 index 000000000..a4443b090 --- /dev/null +++ b/tests/integration/durability/tests/v12/test_constraints/expected.cypher @@ -0,0 +1,10 @@ +CREATE CONSTRAINT ON (u:label) ASSERT EXISTS (u.prop); +CREATE CONSTRAINT ON (u:label2) ASSERT EXISTS (u.prop1); +CREATE CONSTRAINT ON (u:labellabel) ASSERT EXISTS (u.prop); +CREATE INDEX ON :__mg_vertex__(__mg_id__); +CREATE (:__mg_vertex__:label {__mg_id__: 0, prop: 1}); +CREATE (:__mg_vertex__:label {__mg_id__: 1, prop: false}); +CREATE (:__mg_vertex__:label2 {__mg_id__: 2, prop1: 1}); +CREATE (:__mg_vertex__:label2 {__mg_id__: 3, prop1: 2}); +DROP INDEX ON :__mg_vertex__(__mg_id__); +MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__; diff --git a/tests/integration/durability/tests/v12/test_constraints/snapshot.bin b/tests/integration/durability/tests/v12/test_constraints/snapshot.bin new file mode 100644 index 0000000000000000000000000000000000000000..f48244714588425b15cad6b2cdd2de06e66fa0dc GIT binary patch literal 642 zcmaJ+Jr9B~6vWTLxcJh<l_-u)fFRaY2Pc0-3++Ud=-^ND?-}iTuOt!}$mQ<d-Sv8T z-j<R7PH;vPKt1vlya>Dr9LHXY0k0xJDe7TJTKAafJ`Nh$hmuyEo)CenI%EEs3EGFp zkSnYjN@9(s@brWcgB@@<+xf|y#GWadc*zE4M3E*I$TZs6=J(?gLGJc(Z!@3*G4Gz_ wypHWYg@y^L`lf6}g2$z;TzV)gnOUu5p^cWx<f(MdnYwU$WmN$*jyr(DJ|BM|#sB~S literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v12/test_constraints/wal.bin b/tests/integration/durability/tests/v12/test_constraints/wal.bin new file mode 100644 index 0000000000000000000000000000000000000000..4ff53ec5a195188633b3630ea278ac1b9c2c7089 GIT binary patch literal 1437 zcmai!OH0E*6ou>ifr1W!F1t{&3p7onWb4M2P|{EeDuW@32oXyW_2)aS_m%c0?P_}S z<=k^-?&R^|r@rX@|L)BIR%Ql^8}AlYQI+KJD2jums)j*a<WZ0$MG?3~5r=o<a2Ssb zusxqvFWwOp>({)jy+io7Sbi=fbu6h7y|ltY$(>8COY+IP1k~D(H~U>Kzsg^0y)#*T zFRb;=9+W2JnrkW5bUl@9L}nsmtt}UQa-s5a+cb3Un*&OLdAOF6dRq#7TaX(mkjPj| zNwE(uxVPXNDX!g=pr*5r-6n=5u$EG-&Avi5CyE<nET;HJM=Fn9)>A2u0Ux*=Cv*%f zg|(Cdx8+dC%^ZkiET-hZt!6`lN4t=NHypKHj#J3-tXtLY5!#sYoU3=xH%NBgh%4r% oJaao0ar(C%_8W?rTF=M5JLjdLhzGt~kx1N#^*3tuHIg3Zf6JJj-2eap literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v12/test_edges/expected.cypher b/tests/integration/durability/tests/v12/test_edges/expected.cypher new file mode 100644 index 000000000..20df028e4 --- /dev/null +++ b/tests/integration/durability/tests/v12/test_edges/expected.cypher @@ -0,0 +1,45 @@ +CREATE INDEX ON :__mg_vertex__(__mg_id__); +CREATE (:__mg_vertex__ {__mg_id__: 0}); +CREATE (:__mg_vertex__ {__mg_id__: 1}); +CREATE (:__mg_vertex__ {__mg_id__: 2}); +CREATE (:__mg_vertex__ {__mg_id__: 3}); +CREATE (:__mg_vertex__:label {__mg_id__: 4}); +CREATE (:__mg_vertex__:label {__mg_id__: 5}); +CREATE (:__mg_vertex__:lab {__mg_id__: 6}); +CREATE (:__mg_vertex__:lab {__mg_id__: 7}); +CREATE (:__mg_vertex__:lab {__mg_id__: 8}); +CREATE (:__mg_vertex__:lab2 {__mg_id__: 9}); +CREATE (:__mg_vertex__:lab2 {__mg_id__: 10}); +CREATE (:__mg_vertex__:lab2 {__mg_id__: 11}); +CREATE (:__mg_vertex__ {__mg_id__: 12}); +CREATE (:__mg_vertex__ {__mg_id__: 13}); +CREATE (:__mg_vertex__ {__mg_id__: 14}); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE (u)-[:link]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 2 AND v.__mg_id__ = 3 CREATE (u)-[:link2]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 4 AND v.__mg_id__ = 5 CREATE (u)-[:link]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 4 AND v.__mg_id__ = 4 CREATE (u)-[:link]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 4 AND v.__mg_id__ = 5 CREATE (u)-[:link]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 4 AND v.__mg_id__ = 4 CREATE (u)-[:link2]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 4 AND v.__mg_id__ = 5 CREATE (u)-[:link2]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 4 CREATE (u)-[:link]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 5 CREATE (u)-[:link]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 4 CREATE (u)-[:link2]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 5 CREATE (u)-[:link2]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 8 AND v.__mg_id__ = 13 CREATE (u)-[:link88]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 8 AND v.__mg_id__ = 11 CREATE (u)-[:link88]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 8 AND v.__mg_id__ = 12 CREATE (u)-[:link88]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 9 AND v.__mg_id__ = 11 CREATE (u)-[:link88]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 9 AND v.__mg_id__ = 12 CREATE (u)-[:link88]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 9 AND v.__mg_id__ = 13 CREATE (u)-[:link88]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 9 CREATE (u)-[:link3]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 11 CREATE (u)-[:link88]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 12 CREATE (u)-[:link88]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 13 CREATE (u)-[:link88]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 12 CREATE (u)-[:link3]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 14 CREATE (u)-[:selfedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 11 CREATE (u)-[:selfedge2]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 12 AND v.__mg_id__ = 13 CREATE (u)-[:link3]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 12 AND v.__mg_id__ = 12 CREATE (u)-[:selfedge2]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 13 CREATE (u)-[:selfedge2]->(v); +DROP INDEX ON :__mg_vertex__(__mg_id__); +MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__; diff --git a/tests/integration/durability/tests/v12/test_edges/snapshot.bin b/tests/integration/durability/tests/v12/test_edges/snapshot.bin new file mode 100644 index 0000000000000000000000000000000000000000..3b86dd90fe6ade189c4a8f9b9f00f589750afa79 GIT binary patch literal 3123 zcmb8xOK(#_3<cmoc|&;&m-l8u%Q6xqk(zD+i4|M+c_KvAP5(73egdj`{oR1c6?9YU z_}D(4aXk0)r}MLWgUi47<wI+gk5@0P^7Bq>9NcY<!!Uld#;~+e1IDmiX|WxsO}WcD z$gFD_x}KpM8M>LFTN%2Yp|-<aU2SK(P#n@uLx(xn{R}<G(8CNp%1~5_|AS$9oLNnV zo@D50hMr~Ud4^tO=;fbKrLfOP6Vc5`TyX86wnldYeXfXCzLqp>e4RlN!#u`()dK2K zBwp2sSF9F;o!L_3s0z^|gG(_#0UU<8hlqku4`KA^Ap<!ZIEpfm1E~enb5b}&tHA|f za7D51Rv?xJGQ*~Th*H8GK#+SfyB<+8?=M;ll9O^#W-1xA(D;90^#4m3A$F;Csl1(v z!-b3!u38)x9ZpTNTo$Wy)CP&RcGh8gCmqEWr?r!)7#&$6tz5L~kk(ivB}?=uWQiV! zzetp*M<QiX6BNA`hetFQ(T;=5>q7>PYw?cd>fW<leShX!By#o)zfwn)TQv;1RhOU* zt!hK7GJ9PK6K=10l(Vq?;k97vkfGHqh-kx|TQyX)ilS9NTJ_aHm~aEQpHsUr(*{Gl zsn8hpK|YR|?7l4CfL?d2B?6mh;C#K^tXE&xC1f{23D39$w}}(pemna<v88{5$DX++ zeq|SIA1)nvzF2R4hBWc(*v=RBcoXlyMTXhTqt<1fFUCjBaeD}x>B)F;y4;Lb^XX(X gU5rmhvv<?gXm+xkPnOHg+x2QwzO>}>DvX%^U-f-Q$p8QV literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v12/test_edges/wal.bin b/tests/integration/durability/tests/v12/test_edges/wal.bin new file mode 100644 index 0000000000000000000000000000000000000000..af4a3b1209565a42b54fb7b05a5d57da136252e2 GIT binary patch literal 5226 zcmai%>uVE16vgZNt#w_E4@9II5eeEP(i9O<ks>12=~M~|OPg#;G5SG8{qw!8d(PR- z&UE@|xO0E!o|&DQ+q_@<GdsWV|L;|Hry6CSs=-ozuNqB<EB&3-@w7MD9u9iLo&IWX zv^<>jMl0j(!FW7<Qck8>mAla8=yQId3Kqq;-Qsuo<5&5&DDpG)=-#9#y4jH`v`$zt z$5>cTnO>Q#8Xe9lf2*4vV_{fO%+Xe?FlevLK;gEs6+XejN2`%yE<-F<7_?VrpfFUV zY=uv;Fx)HVxF4~?puI8!h1<$j7<ud2ajqCK$5^azAQPJ~+ukW>()ZE=ba4LmPg;j_ z$~SB0xF^t5%<)sjtbwj&LZ-5Y3@@#KH9)Fu)^LT^vy<F4#2jNWYk^FB8?z>LFQtI7 zc4aemiF<}Kp_t<-ix~r5%WN3Kyq8kISi7<ryG&!JxnqdA46&H8KxV@j=Dm~x#!y9W z%-9tg1EHAX^@te*UCYEhvQOEUl$R9h)XqOb$~R|M>Fg}G3^A7>7IPNJY&t`NmlSN& zoS}^Dn6nO@;SMS0cyD9QK-V&x&XC|G6>!!rZO(4c8G5K<E<-Hl4768fpfFUVY=v*J zFmAVEj_*aRFlevLK;gEs6~4{F7pswCE<-F<7_?VrpfFUVY=!Z!$tHVv&d<2_{VSvF z!a|I`TbbSDbdHyHW^|t|lr2q+;Y-BPPMgKygipH=qwkZ(D-xk0BgE(jq%j?#u^=Hv zKO~LmxrIZt))c`Q{)jl<{}>K=F^2bu<CSiD2UIH##OTMQ@svhr$OtidnKX{roOMvO z;$RFP5XYMo!=WR_@TbJ_aOBv*{R1}|v32kc3Nn6$Jnny#M*%^`uad_#j`AoV$oOaE zaU17(>?&VPLB>BPk1ihNRUeS?FUT)eqbQFB3NrpBc{J<B=NqsKu~CXK{55erEc4}Y zDr(CIWc(ZQXxUL-%Lioq8u`2$MR_eBknwNHqwmfwABTpmM~va`h+ji_T*05^Y+6os zORk$wxiC|BPX)AB{^Wx~UlMR5&Lloi;yO&=s_kE)0j<|=5E#Qh5=Wm6IKbvpOPon; zP~s*W;M8jo*#DXfa3<j^@JqNQ(h~m%>Smi2vVo(w5N4Hm=x^Rd<!z|@!Sz91{3=)G zp+9CDifo8UMP_0Tx#mtokxeqG$jpQydR%%RnVC?;K};($Gogs?kXB@7LXj;uxsS|D zDB=xBdt_!p5z}c!W?~RIJ$RSXip)$X;&DnVGBcrwS0}B=%!DF7aaxg?2}QPj0MVC% JnTINF{R4zc{F?v( literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v12/test_edges_with_properties/expected.cypher b/tests/integration/durability/tests/v12/test_edges_with_properties/expected.cypher new file mode 100644 index 000000000..43efdad8d --- /dev/null +++ b/tests/integration/durability/tests/v12/test_edges_with_properties/expected.cypher @@ -0,0 +1,24 @@ +CREATE INDEX ON :__mg_vertex__(__mg_id__); +CREATE (:__mg_vertex__ {__mg_id__: 0}); +CREATE (:__mg_vertex__ {__mg_id__: 1}); +CREATE (:__mg_vertex__ {__mg_id__: 2}); +CREATE (:__mg_vertex__ {__mg_id__: 3}); +CREATE (:__mg_vertex__ {__mg_id__: 4}); +CREATE (:__mg_vertex__ {__mg_id__: 5}); +CREATE (:__mg_vertex__ {__mg_id__: 6}); +CREATE (:__mg_vertex__ {__mg_id__: 7}); +CREATE (:__mg_vertex__ {__mg_id__: 8}); +CREATE (:__mg_vertex__ {__mg_id__: 9}); +CREATE (:__mg_vertex__ {__mg_id__: 10}); +CREATE (:__mg_vertex__ {__mg_id__: 11}); +CREATE (:__mg_vertex__ {__mg_id__: 12}); +CREATE (:__mg_vertex__ {__mg_id__: 13}); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE (u)-[:edge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 2 AND v.__mg_id__ = 3 CREATE (u)-[:edge {prop: 1}]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 4 AND v.__mg_id__ = 5 CREATE (u)-[:edge {prop: false}]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 6 AND v.__mg_id__ = 7 CREATE (u)-[:edge2]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 8 AND v.__mg_id__ = 9 CREATE (u)-[:edge2 {prop: -3.141}]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 11 CREATE (u)-[:edgelink {prop: 1, prop2: {prop4: 9}}]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 12 AND v.__mg_id__ = 13 CREATE (u)-[:edgelink {prop: [1, Null, false, ""]}]->(v); +DROP INDEX ON :__mg_vertex__(__mg_id__); +MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__; diff --git a/tests/integration/durability/tests/v12/test_edges_with_properties/snapshot.bin b/tests/integration/durability/tests/v12/test_edges_with_properties/snapshot.bin new file mode 100644 index 0000000000000000000000000000000000000000..bc434f4e6aa8503d0fd93994399e1b97977fcb7b GIT binary patch literal 1606 zcmb7EJ5Iwu6f}RrpNC=FT?nO%7%L<#6jXFvz{WO0pb&wmIS4iPpx`E)1BqDKnMHcR zf)v(hc4yw~dz-zycp7a))8Cn#dnD!D6iKn<!85=Mz?TP3yecAa;`NaRhLn?e)KlBd zZscSE%P8Pv5tLT!U46d2e%Q{t6Q3;71wwVYn*(^o(2XA-#+i|hFXm+V_p<wl6s*n8 zB)*A}+OjWg03pI2#aOm3Y(kN?CGG*Rp)sJ^92rCn#IlQj(sSObstuD>Yr&d9XoQJz zj}61uMp}>IQBPpQ(6td6I<hq@evK`*LW(j1t>;za8L`>QD!f54Z2)xS(Qu7_BqF7a zw4POGBwG7VA`WXEDrB@mswENitO`AkZw0RzsTqK0WV1JQB2q1hsApA`sH^KAlR6By z6~Y+puF9M0{&sZ7vGI9oR#JMzP&PE50kX|o#(ya-8a6$OyZ&r>o#lg*VV%_3<da?3 bbuui=D#?mrIw(%Fyx;4~HhRSSyycm1jPNlf literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v12/test_edges_with_properties/wal.bin b/tests/integration/durability/tests/v12/test_edges_with_properties/wal.bin new file mode 100644 index 0000000000000000000000000000000000000000..35bc2bd33fac8462710579d9eacce994885d7e29 GIT binary patch literal 1111 zcmZuwyH3ME5H!ya9uheWSAqme=K^yIahrw;iGssv9c&{+1Q87%L(P9s@F)BT=WsK7 z#-xbWb2GEE>+|*1+k7|r{Y})duhg9{PUACQ6}2hG7g?Ru#{HT5x=ymPtP(TJ((&xl z6r=Nz^4w^sR(yH$FNRa$S@Pm_FRjXliYN_5G99Wlc3JgRh~ZQ)OPh8VsA(vYyenC} zzAQ9hp}IgD41_5{45xxwh1TsrP}5K(c~`OwG;mu-1DB-7B%54~V5o#=IeQG3<^`k* zayp8}wdu3X7TXAhN_bXbv_EYir=w_c(LlVM_~7>Y<NfQT|0z_LXorcmnMg2H!n533 zj10}-^T+2W1qYgnEEicTRB$iQE1+VvP5x9L;XB|i8r<k$2k>x%?Qt-Cupx$1!7N{0 fcQ826RAjlxT7w<pNv^)$*MmsMK9{?H2r>NuMu$fi literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v12/test_indices/expected.cypher b/tests/integration/durability/tests/v12/test_indices/expected.cypher new file mode 100644 index 000000000..76f25401d --- /dev/null +++ b/tests/integration/durability/tests/v12/test_indices/expected.cypher @@ -0,0 +1,16 @@ +CREATE INDEX ON :label2; +CREATE INDEX ON :label1; +CREATE INDEX ON :label3; +CREATE INDEX ON :label(prop); +CREATE INDEX ON :label2(prop); +CREATE INDEX ON :__mg_vertex__(__mg_id__); +CREATE (:__mg_vertex__:label {__mg_id__: 0}); +CREATE (:__mg_vertex__:label {__mg_id__: 1}); +CREATE (:__mg_vertex__:label {__mg_id__: 2}); +CREATE (:__mg_vertex__:label {__mg_id__: 3, prop: 1}); +CREATE (:__mg_vertex__:label {__mg_id__: 4, prop: 2}); +CREATE (:__mg_vertex__:label {__mg_id__: 5, prop: 3}); +CREATE (:__mg_vertex__:label2 {__mg_id__: 6, prop2: 1}); +CREATE (:__mg_vertex__:label3 {__mg_id__: 7}); +DROP INDEX ON :__mg_vertex__(__mg_id__); +MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__; diff --git a/tests/integration/durability/tests/v12/test_indices/snapshot.bin b/tests/integration/durability/tests/v12/test_indices/snapshot.bin new file mode 100644 index 0000000000000000000000000000000000000000..cf751fc37e9261394ff60a4ff70551eb749dd553 GIT binary patch literal 898 zcmb7BI}U<C5XHZRjS~{ujbce6$O^VvP<aRttcVgljkOo`!_M1~B}7RGFEjH#GaQrq zWoFgSo)oa?pb#bf8qtHnlfiCKw}htkn5Mp#ZW$Fpm<BM0OuiuHZ)yHdG?liE27d-l zhqj=h@@pAl0kj+)*GMU7^p_GgXi*v)O6HlE-@V<$P(n(>7r)0P7T5dYOyDCDgm5pk zxUz!wg0LJ*+FU8qtSEilv02|Bl+6*U+qDW1u{j>V5#wKZ{7akdKPpSxRa+FYi1PRA gt+!|23*%ktWswZMG!Eq{4`f)#nC5&{tUguf1tvTqYybcN literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v12/test_indices/wal.bin b/tests/integration/durability/tests/v12/test_indices/wal.bin new file mode 100644 index 0000000000000000000000000000000000000000..ba46ff02dcc6efca20876b53eaac0685b2ac48cb GIT binary patch literal 2425 zcmah}O-sW-6s+HfiggL(=t0RX&@`>BS1+D~kS<bD2)0c`s91`qKi_TLd2iF~c6-{+ z&YL%reY+3$Kl9Vp|L;y*>XmrZi>u&Tuc~y`xw%dH@vs`Cac?*+<6$|S#e=Gvr2V9t z^pcLy+*U-Zr{GY7^Vdl^?>cu9=T6Ey&%uGI7VPVPi{<CSWYv2*X=l2;C_dhb?{fK7 z{uV`WY@+Y8qKL$fhBC^<SWZ}A)w42dF_<A)tKlM~J?4)iu}9WmlChkOyR4zwU~X6g zL$a2|TC=yZ2Bl2wYLkrRgt@Gt+F$}&j}mK1*0NY@_Ey%8=&K<gV>x|Omo-!y%nfT` zNY=7gYxXwy=4^-b@!6(I0m)2&%vesCD+Q_z=0*xIBx?a$UzyeA2x*Ud8+;gB-O!NE ziK!~Z37rMnGnUg3xl*9oU_wTZ2^f;KEGbaLy$yan1s@xvRSMkwOyKchEGNvB0@Vf+ zGI%#Zkt}A3fhulp@R4zK7+rIwpsjM8(`jIa8O!P3xN@M{V8YQlhkDCFkt}A((QK}~ zwY9ewdwbi<eW6Q6^m^KBBLU{tH@MBb5#33@BA&;l!?31^x5}?b%;vYNDPmUrip2CN i;x70ViRn?qSo#%-=~2YY`4x%jQN%p@6^Xf3ar_7CTLwh{ literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v12/test_vertices/expected.cypher b/tests/integration/durability/tests/v12/test_vertices/expected.cypher new file mode 100644 index 000000000..eeab7fb22 --- /dev/null +++ b/tests/integration/durability/tests/v12/test_vertices/expected.cypher @@ -0,0 +1,16 @@ +CREATE INDEX ON :__mg_vertex__(__mg_id__); +CREATE (:__mg_vertex__ {__mg_id__: 0}); +CREATE (:__mg_vertex__:label {__mg_id__: 1}); +CREATE (:__mg_vertex__:label {__mg_id__: 2, prop: false}); +CREATE (:__mg_vertex__:label {__mg_id__: 3, prop: true}); +CREATE (:__mg_vertex__:label2 {__mg_id__: 4, prop: 1}); +CREATE (:__mg_vertex__:label {__mg_id__: 5, prop2: 3.141}); +CREATE (:__mg_vertex__:label6 {__mg_id__: 6, prop2: -314000000, prop3: true}); +CREATE (:__mg_vertex__:label1:label2:label3 {__mg_id__: 7}); +CREATE (:__mg_vertex__:label {__mg_id__: 8, prop: 1, prop2: 2, prop3: "str"}); +CREATE (:__mg_vertex__:label1:label2 {__mg_id__: 9, prop: {prop_nes: "kaj je"}}); +CREATE (:__mg_vertex__:label {__mg_id__: 10, prop_array: [1, false, Null, "str", {prop2: 2}]}); +CREATE (:__mg_vertex__:label:label3 {__mg_id__: 11, prop: {prop: [1, false], prop2: {}, prop3: "test2", prop4: "test"}}); +CREATE (:__mg_vertex__ {__mg_id__: 12, prop: " \n\"\'\t\\%"}); +DROP INDEX ON :__mg_vertex__(__mg_id__); +MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__; diff --git a/tests/integration/durability/tests/v12/test_vertices/snapshot.bin b/tests/integration/durability/tests/v12/test_vertices/snapshot.bin new file mode 100644 index 0000000000000000000000000000000000000000..02c7b57840bea927fbd2c71eb25b77d2d0d8c6e5 GIT binary patch literal 1575 zcmb7EJx{_=6s_Nan#i^A2A3DaIFVr6h;Bv)UECZ^%oDmGK|)#F{2$K#3^#v=F>!Ws zHb|l8Jet-7JDuKp-?``9+t;g$sJH1%pEEg6kOd#h(~x`v`~bYKOjBBtQ_eg<%L1AZ z%yC}J;@`(e1fWVeTC^<vi(-_uqa`u#SQlAcRAGxU4z(oHn_*%R2q;+XTz|eizv;qh zo~G4|EvlqASXc7&digaPKNO%!xlY%>ilf3lWMvJdXMmP<5YZNkNR5=TBSTKv&0}QB zMKT%=@+MM1ODfH3M@x286@*+!?hhXNx4ke@F3#VZj_!ncJ&WiG7F)y!bg43}*!Cqy zZKs^g2ts)*$eWo6Ckdcs13Ks(IpMk}2tZZv$+!S<D^&)GUCj7yxoJ5SG)TpL#n)jN z4L!ca#DQX;IB7=(DlPac*&M{$k-2cBi`9d|&AzP#Pc0j=RaL=42wbdjvU)QZm`9F{ zA3@jLg<YTbAss)YJX(yosDpEuu<+wflX$%PERRV%{(sWtN4(6ba(~aQC4)HeJ>z-J j<GSw~QxE)Bt8TooU2iv<jUZ@-rrii+Hz}r8*-9e6#1~j1 literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v12/test_vertices/wal.bin b/tests/integration/durability/tests/v12/test_vertices/wal.bin new file mode 100644 index 0000000000000000000000000000000000000000..f5f44664a21ea8aedc15f8ec02e539fc6a216363 GIT binary patch literal 1778 zcmah~O;6iE5Dm0I!-pUnCo4`we4Zi&+iKcd)k}M+6s=Z>0}@&iUqS>?5(#nRe{kk! zaO*$mxxXPDJNss0r%`-bKfjsxW_EV{>&Mezw={dct0TWsSAKC4och(*D0E@i{?d#h z=bCZU?KWMQ^qRd^yA{Xn#PwRS^4!w7TJy`!f3WLw^?^TwD45~kV8hb}?k*V!gA(9N zN3DB~V1}_`0z_G1eDvYN(c@58$T>J9vc)dpS1`jh6h#nag^?GTN){3+vj|v%83ioZ z^t7xMp-`+)z|BhGCJL3rc32E$3TC+UVnv9ui;Pq&)K&ZE?e%w~{7tfv*dB|)ykJHF z%UAYUC{`#iJ5y6H??3-e=~>B*bY+f2miE~ac1bY9`<=IRxtc=zfy~RYL_#4S754GT zuLLs+SaBZ^WfvK~GjbSKa3dXHrpJ?!h|i^7HcrS)WmLHgOtD~wCwrlt2wO&>c7kFh zOU_Rn_eOwbr5(6^NXDAw?p?ppPb3!^uCd{5?mIA}fED`=h7|^=nW`I&+_SD>N;RK7 zx={2%uG3nKgTAnJkR!jE{o)J?X81H0{UU0Pfzy^<A2f@gSV@xaFWv$yQk%Ml0n63M z`m<8%-gGI4)5M|TNiv><{FVmAnsw5T;*WZ8$UVT1N-(Q{aeU@8!0~QuA77Mj&Lrac EA3$N0s{jB1 literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v13/test_all/expected.cypher b/tests/integration/durability/tests/v13/test_all/expected.cypher new file mode 100644 index 000000000..3faa3dad8 --- /dev/null +++ b/tests/integration/durability/tests/v13/test_all/expected.cypher @@ -0,0 +1,16 @@ +CREATE INDEX ON :label; +CREATE INDEX ON :label2(prop2); +CREATE INDEX ON :label2(prop); +CREATE CONSTRAINT ON (u:label) ASSERT EXISTS (u.ext); +CREATE CONSTRAINT ON (u:label2) ASSERT u.prop2, u.prop IS UNIQUE; +CREATE INDEX ON :__mg_vertex__(__mg_id__); +CREATE (:__mg_vertex__:label2 {__mg_id__: 0, prop2: ["kaj", 2, Null, {prop4: -1.341}], prop: "joj", ext: 2}); +CREATE (:__mg_vertex__:label:label2 {__mg_id__: 1, prop: "joj", ext: 2}); +CREATE (:__mg_vertex__:label2 {__mg_id__: 2, prop2: 2, prop: 1}); +CREATE (:__mg_vertex__:label2 {__mg_id__: 3, prop2: 2, prop: 2}); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 0 CREATE (u)-[:link {prop: -1, ext: [false, {k: "l"}]}]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 1 CREATE (u)-[:link {prop: -1, ext: [false, {k: "l"}]}]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 2 CREATE (u)-[:link {prop: -1, ext: [false, {k: "l"}]}]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 3 CREATE (u)-[:link {prop: -1, ext: [false, {k: "l"}]}]->(v); +DROP INDEX ON :__mg_vertex__(__mg_id__); +MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__; diff --git a/tests/integration/durability/tests/v13/test_all/snapshot.bin b/tests/integration/durability/tests/v13/test_all/snapshot.bin new file mode 100644 index 0000000000000000000000000000000000000000..9a0f2c8403dc292b6b2d1bd666980e605897b628 GIT binary patch literal 1467 zcmcJOJx;?w5QWWegOFfx*w%qiTCx@^F)b8S^t5rXC2T8+LJBI*!7aD|5(lB;5EN9P z;GJ)gY$6Kc>S^E1o7wl)SC@C=U9bMm)rCdsX{|=;deeeO0dE3kTQ${Hw#!rN7O4#j zwEDE4vTdI3NR{6A^r0Q(`2tD}th!20S0F8GtUy|>|L@X)^BQLWjj6hW>AsNVu+Xff z_kILm<MgJp$u!mnuOAP^^ZfnAWr;>HDO{Y0xvY2(thOx=L#V)2lY?+Alx(m6yaS^F z>>#E%FPTgYT>vQd1whG~c&3C3T5LiZAptHSl(ciD*~f{D<7xo{%lenj719ZC&EMzt zTs7u4g_<WKebUlTZj1#W1Q4MkBES=0UV1f5vkKl9{pxmH5*Njb-@>q!FHV1J7+gkV yQO-}otIh7`$9~T`k_=3en^79}&rXvtHaBsYrcpmkO+Lu8EJ{XErgm>7WM$pIHk4 literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v13/test_all/wal.bin b/tests/integration/durability/tests/v13/test_all/wal.bin new file mode 100644 index 0000000000000000000000000000000000000000..84be2a4096a8e44ec6ba381c1e1a1aaf1692987a GIT binary patch literal 1731 zcmb_cO-sW-5Ut;M5Ohu5qX(t8K#8S|S1+D~5~8h$NK;cV4Yo)@PyRB&AL8%u;7QYT z-<yqXrKmX#Gw;25GrOC!)5mnHH2F@{zKPU@2@ae?6Zs9#?|I$GtsfuxZq2)`xlvTD zyOGyx^y0YccdM~7*iwbjrL$&|X(x(PDO@@0cEKZV_%IlHKj2mkZHyDxHTiiTXT_l6 zr;&hezV%eg21Rn-+$pwLGN2}hswm_F=DtY_Bov6Pt7$PABs0<02%1i3BC+D!y)L7T zwCHydk<i<E5B>{-*Yxh&$8+*BdViHR?TUhtCdZasz#OH_L4#!>iYG22p@_iycogxU zuLyz`8ma7*Wj@i(ykj*@YlFxI%uO90AfZ4C&S+RVSWvJh=Rpa<X2v#nt1yJ*0<L_@ z7-*qDipFrIMa_cFH3r$t*rqXdK7DV~yZb&TXW6FkUcpq6g0fA4VZOQ|1!bFpjm@E; zER+__l9FFA^=ZDDQ0R<rR~bI^PRV*c7^trugxNn1D{OID3$z8F|Ajxah1mt#68aC* OLSliogd+I$wCykFTAv*N literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v13/test_constraints/expected.cypher b/tests/integration/durability/tests/v13/test_constraints/expected.cypher new file mode 100644 index 000000000..87a2720b6 --- /dev/null +++ b/tests/integration/durability/tests/v13/test_constraints/expected.cypher @@ -0,0 +1,6 @@ +CREATE CONSTRAINT ON (u:label2) ASSERT EXISTS (u.ext2); +CREATE CONSTRAINT ON (u:label) ASSERT EXISTS (u.ext); +CREATE CONSTRAINT ON (u:label2) ASSERT u.a, u.b IS UNIQUE; +CREATE CONSTRAINT ON (u:label) ASSERT u.a IS UNIQUE; +CREATE CONSTRAINT ON (u:label) ASSERT u.b IS UNIQUE; +CREATE CONSTRAINT ON (u:label) ASSERT u.c IS UNIQUE; diff --git a/tests/integration/durability/tests/v13/test_constraints/snapshot.bin b/tests/integration/durability/tests/v13/test_constraints/snapshot.bin new file mode 100644 index 0000000000000000000000000000000000000000..939d8da53240d23bcb0f7af8c41a02c4ec8a51d1 GIT binary patch literal 488 zcmaKny$-@K41`Phn^>$o0hBQkO{2D~Ffg&QJ0)Ai0t0WutAc3m7(jUX<U613<KcQ) zWJB#_pXdjiAmyWh<dL9|BxQuP<gimq?(8Ft;h6wv<~XG(Jd_wn&$skvYL)_*GDkF8 z2GN~>43@<E&GAoziC#nO9bYrp`|b-KHGo%$Adt&cFvV6_nIat*+fw7%HMUXLS|2NK UL%sFZX;sx)Z?p`7*YZHZ6BZ>IdH?_b literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v13/test_constraints/wal.bin b/tests/integration/durability/tests/v13/test_constraints/wal.bin new file mode 100644 index 0000000000000000000000000000000000000000..0c2351e4ae4d2e079140d7d1930acc98873e3a99 GIT binary patch literal 349 zcmeZuFVEp+fB+{Uc_>RL49ZXzQGv274APP<k`t446U_}Qbxq7nEOZks(@b<tP0W)` z3{8?u%`HrXplTV^;H+2?HmG1uVp3|3kq8Tvn_5v~gshAeS(ykkLKQ+$ya<XSAx5Yo b5f~$p1hq*ds7*#zi|QXCCJaZwos$Fre_bLL literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v13/test_edges/expected.cypher b/tests/integration/durability/tests/v13/test_edges/expected.cypher new file mode 100644 index 000000000..79ae46b83 --- /dev/null +++ b/tests/integration/durability/tests/v13/test_edges/expected.cypher @@ -0,0 +1,58 @@ +CREATE INDEX ON :__mg_vertex__(__mg_id__); +CREATE (:__mg_vertex__ {__mg_id__: 0}); +CREATE (:__mg_vertex__ {__mg_id__: 1}); +CREATE (:__mg_vertex__ {__mg_id__: 2}); +CREATE (:__mg_vertex__ {__mg_id__: 3}); +CREATE (:__mg_vertex__ {__mg_id__: 4}); +CREATE (:__mg_vertex__ {__mg_id__: 5}); +CREATE (:__mg_vertex__ {__mg_id__: 6}); +CREATE (:__mg_vertex__ {__mg_id__: 7}); +CREATE (:__mg_vertex__ {__mg_id__: 8}); +CREATE (:__mg_vertex__ {__mg_id__: 9}); +CREATE (:__mg_vertex__ {__mg_id__: 10}); +CREATE (:__mg_vertex__ {__mg_id__: 11}); +CREATE (:__mg_vertex__ {__mg_id__: 12}); +CREATE (:__mg_vertex__ {__mg_id__: 13}); +CREATE (:__mg_vertex__:label {__mg_id__: 14}); +CREATE (:__mg_vertex__:label {__mg_id__: 15}); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE (u)-[:edge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 2 AND v.__mg_id__ = 3 CREATE (u)-[:edge {prop: 11}]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 4 AND v.__mg_id__ = 5 CREATE (u)-[:edge {prop: true}]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 6 AND v.__mg_id__ = 7 CREATE (u)-[:edge2]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 8 AND v.__mg_id__ = 9 CREATE (u)-[:edge2 {prop: -3.141}]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 11 CREATE (u)-[:edgelink {prop: {prop: 1, prop2: {prop4: 9}}}]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 12 AND v.__mg_id__ = 13 CREATE (u)-[:edgelink {prop: [1, Null, false, "\n\n\n\n\\\"\"\n\t"]}]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 0 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 1 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 2 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 3 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 4 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 5 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 6 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 7 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 8 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 9 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 10 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 11 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 12 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 13 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 14 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 15 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 0 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 1 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 2 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 3 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 4 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 5 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 6 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 7 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 8 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 9 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 10 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 11 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 12 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 13 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 14 CREATE (u)-[:testedge]->(v); +MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 15 CREATE (u)-[:testedge]->(v); +DROP INDEX ON :__mg_vertex__(__mg_id__); +MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__; diff --git a/tests/integration/durability/tests/v13/test_edges/snapshot.bin b/tests/integration/durability/tests/v13/test_edges/snapshot.bin new file mode 100644 index 0000000000000000000000000000000000000000..0c771da1465e1961e73ca77411be5332c26c0946 GIT binary patch literal 4124 zcmb7{y>3%M5QWV@Hh;;+@#UYrb`oeT#g1bX(Lq5)M@fT;K?o~EByU5_dr<HOJOUL@ z0kLw=Hx4H^5^1tJGqdN+?#%4<tCt6RHyX!(&(n))m4071UZvGr)#8a3&$ReiEy`3y zHCUACoOZHBk*-y?GM%sYTK-d}O2p#67waQdUjO>`^+#@gFVm%ZuZ}p|+G_vH=lue~ zZN<*vVbPKP6Zoh|O_I1=Pqr$K*uPjRmK#M|;jN>;H{EWlS*9!6r~2P0(^ZEQxNz1j z_tFtBcwKidUHpRAP4}v<dGQvO+-un(ZL9xB39YzS+o4s5It~>M-FE1XLw6mz=g@tJ z9ys*Sp+^p7hf0UK4)q*bb7<Y6jenucEu6AYPect;2UI1;O+8>zkHpS~WEw1-Dv1s6 z^HY3i1y{zcyE0Ma7&U@|gY4-o=b((7gIrnI+{$OAh&S;FLMtuq%BW^nrtq9Jhazd& z;$WB=&Ov)Phl|z#g)7HAhLr``leBAPbh$FR)Rl=MCnmHYq(%13Lg%3EorCO2gQieE zIhxAp31fj)#U})@fP?Ad%EXoZ5jiD$;+=zWh7a^)&y~?ZQlEJ!gH_U^DTt@b6`y!& zTv@I*O0G;4Ing<SLI)hYUCvR~bC5j+PY3BXGv7MFiVitNcc2xM!lDQ`{BFH6QRGDD z2-1d)sC?dJ&%RgBLG~0Z9VGQR0Xe;#qB+ruiBAJpwpOo9TsaQS#mXs;fsYO+<9+o) zqdd76b_SuSk%7tjC5D9;15wl&eS?WZq$n|Q8Qd5qPZ<*_lLaPnCksq;6j@+W1x9ON zq7umh6Qw{Fn9jI3z8c9wQ8|VNx5Bw_g-i%G@{`)AykkRg#O&hRiJkG`;{_R*_~t+c zCLVoy1ru+z7YC1}7YA<~hrotBG9WB?8@xEOj>U_kl*x;Op+<?r)l-xg2gAUNgVz0@ zi(6BT#&4c=hq^JkbZGoYoV}e)XS{E19nW<4!*up>?-NspfVpMJH}Z+!rZ9ufof#Bt zemFfioLhQroyX?;-NEqPc+em3>}>Z(!_85D@^rG@-yTlJTa&?HYcL!o+UKzjSbhB$ D@^xKp literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v13/test_edges/wal.bin b/tests/integration/durability/tests/v13/test_edges/wal.bin new file mode 100644 index 0000000000000000000000000000000000000000..69bc837b59acb8c3892460932d67f5238582c6ff GIT binary patch literal 3024 zcmai$%~BIV5XU15nuth-B`ctO1k5c}?2^Rd%7qhEDT-k^aA74{7Ad8aDj&wP@8Q8W z@j-MF=HK06$LX9Vo&NR5{JW=ea{PV#qIUl|GF$#+&i!FG+4HCU57$}#evowshll-c zFW>KV%XekJ+t16vL78O-S>7|=x7IO_{Banx;aB#v<d;10=;-?6h^!Qlk&BLbOj+jq zN!su$+bo5d%>tSNGE!8L+pk}4Q}eu%(G@gL1)&RR!>??!2B~KY0!;xKDXPe{pyprp zag{u!N*1V4w&BEnmU>T6=0t$p0H=t|xHNT2^NiB44JY=q2CY3v1Dql<Q!oQ~+VRTS zpPxT|Z`XgZtE&jQ3{8V7VjE8EXX%a;)G%p${OR)zMWCRJ6^zXk)v5@3KQ0(~YI|bN z(^k;lOT{&yWY-%ub!Zl;H@4x#ewNg$_J#-)l(B-bnci0MEv2PMm(yk~O(1l`eJ<PW zMxBGXvR~4WEK%98of_EaX2w_LXv~xg(4+vs*mg?c9|i#B0<8D|mZq34<ZkrsZu-=q z4tjGWhS7*%P*P&Jmk5S=kr*a7g7M?Vy<t)!7!;J*FbNS1PlCiSZV?P`hs1Er5sY6a z?hV36FlZ#REqmc37&MX?Mkj(nBZ*;jA{aE17`lvL&`4s5-WxQM*s6{}BZ;+i3>rx+ z{VxV@T$=mWfQzu^m5zn>ZB55Q`?juQp?%xXvCzJ~*0IpOz0tAIzHRDQXy5QxusV~Z sTWH_5^=zSi+tIPmzO{8Mv~RmQ7TUKx9SiN-TOAASTSv#B1TW6TKZ_^e82|tP literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v13/test_indices/expected.cypher b/tests/integration/durability/tests/v13/test_indices/expected.cypher new file mode 100644 index 000000000..75a978130 --- /dev/null +++ b/tests/integration/durability/tests/v13/test_indices/expected.cypher @@ -0,0 +1,4 @@ +CREATE INDEX ON :label2; +CREATE INDEX ON :label(prop); +CREATE INDEX ON :label(prop2); +CREATE INDEX ON :label2(prop2); diff --git a/tests/integration/durability/tests/v13/test_indices/snapshot.bin b/tests/integration/durability/tests/v13/test_indices/snapshot.bin new file mode 100644 index 0000000000000000000000000000000000000000..85fd00b1981e52b72069e5576f99a219dbfb3f8c GIT binary patch literal 343 zcmeZuFV5p-fB+{UXDAB`V=7E)1&qPR2w|xRK^Y7P1~W_%lx7fO!VrP0foq3}s0zWg z!5B(HEHDFLszg|!A_YbH1x9cSL|_U*!f+Rfut62)BqpWi7{M)o8v+(r7EysJvotYF rGfgrv(KWM7G1oOoH8a(<v`9A6HL)-;H#9XgPqi>Lfg27t4bA`n-d-0# literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v13/test_indices/wal.bin b/tests/integration/durability/tests/v13/test_indices/wal.bin new file mode 100644 index 0000000000000000000000000000000000000000..096167b5111230e9585c64c4e3f7e98f245bb26f GIT binary patch literal 357 zcmeZuFVEp+fB+{Uc_>RL49ZXzQGv2d3{#U+QWMQ}&5V;$bxo4eOm!_%5{-3Jl2Q^= zlhZ7c%}tDiplTV^;H*dyR;XZ3Vp3`jvIHBlgb_j}3YV5>R0$Clr~w5<`2`5=(ITh@ W6RQm7PLOFxu0gjHhq4%awgLbGY%N>> literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v13/test_vertices/expected.cypher b/tests/integration/durability/tests/v13/test_vertices/expected.cypher new file mode 100644 index 000000000..eeab7fb22 --- /dev/null +++ b/tests/integration/durability/tests/v13/test_vertices/expected.cypher @@ -0,0 +1,16 @@ +CREATE INDEX ON :__mg_vertex__(__mg_id__); +CREATE (:__mg_vertex__ {__mg_id__: 0}); +CREATE (:__mg_vertex__:label {__mg_id__: 1}); +CREATE (:__mg_vertex__:label {__mg_id__: 2, prop: false}); +CREATE (:__mg_vertex__:label {__mg_id__: 3, prop: true}); +CREATE (:__mg_vertex__:label2 {__mg_id__: 4, prop: 1}); +CREATE (:__mg_vertex__:label {__mg_id__: 5, prop2: 3.141}); +CREATE (:__mg_vertex__:label6 {__mg_id__: 6, prop2: -314000000, prop3: true}); +CREATE (:__mg_vertex__:label1:label2:label3 {__mg_id__: 7}); +CREATE (:__mg_vertex__:label {__mg_id__: 8, prop: 1, prop2: 2, prop3: "str"}); +CREATE (:__mg_vertex__:label1:label2 {__mg_id__: 9, prop: {prop_nes: "kaj je"}}); +CREATE (:__mg_vertex__:label {__mg_id__: 10, prop_array: [1, false, Null, "str", {prop2: 2}]}); +CREATE (:__mg_vertex__:label:label3 {__mg_id__: 11, prop: {prop: [1, false], prop2: {}, prop3: "test2", prop4: "test"}}); +CREATE (:__mg_vertex__ {__mg_id__: 12, prop: " \n\"\'\t\\%"}); +DROP INDEX ON :__mg_vertex__(__mg_id__); +MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__; diff --git a/tests/integration/durability/tests/v13/test_vertices/snapshot.bin b/tests/integration/durability/tests/v13/test_vertices/snapshot.bin new file mode 100644 index 0000000000000000000000000000000000000000..e1b398cf27da301ff3c0b1ad6f10291bf7139ddc GIT binary patch literal 1584 zcmb7Fy-or_7{osm)I`>WEo?T3vAP5}xNx1(LK{0v6La7-NRV(ecD{$Tk74J7m{?d@ z`2fhl%`BHa6TE8p_S^YpX7`Tk%V@ZtTRi9DB0&axY%WUT2Oz(dRK60RMY1cm3eaK= zLkL#buf_V`+eieUN*P+T*!UO4)V8BVl0we7uys*|X|fz@k)*f8#3m3>usXc?e0_P> zg|jS8dCL}6QXH%+@%(o6J)J!iph}rex50|7f^*2q9!j@>7CRuKEjE!FOUgusHDyj7 zktvDAXfn=9q=1oBYHP<xPE-|?BqWc<kE6R`7%7SU4?$n|!>pM_%mj;RVg<TXnO4mB zQcfMDoGl4Ldn~BAln9dq&|()l=pAc<$3;N^s)8#c0pd}r3=(rNvv*6T<y0^rRd*GC zPQqx?<gb{xr8p;EG*N*{i#_;2iAfPC2}inEJuTdx*gQ46l+jfcI2i(o^PJy-U_1_< zm>+M$An1kzmoG-T-k?r;jkzd@fy`NW%hM!HegoJZlQj7uO1FD_t5fanv8*LA-uu4m k`$5ME>Y?Yf+U>T}se6Ir_uP7~)#!TNuqBSH?C~G*3q2ZF2><{9 literal 0 HcmV?d00001 diff --git a/tests/integration/durability/tests/v13/test_vertices/wal.bin b/tests/integration/durability/tests/v13/test_vertices/wal.bin new file mode 100644 index 0000000000000000000000000000000000000000..7c6f1e1a5e6e2db57e486aca2175e7656e7392d7 GIT binary patch literal 1778 zcmah}%T5A85X86mh%zorJV|_>NRS1>-i#N$n2^cDcrY=G?nQ|jATi$j56^yvH~+-5 z-=MwAR5!aw*wbuHbysyy&s?0nbk}pk?^Nx$mAZ6`J#XKw?t;J%g0NKyt5LI3Z!{W} zR<#*cf?L0OTR-kJJ5gOZZthUcxMlkv?CMa>@ivHp84mUq9IYF6qOLF~0e0GI)^P+g zjAaub$_iuNg+KS6o^^qogF_-q>=J$jGh9Pf1W{HPX^~&aLLzw<0ZTBWfO(6Kmb9WK z6e|>PlTx^eLM5?P7DJhW87@6r5u)rkBi5?vE&KiP{#DMONH!9~T(uR<3uYKgSJo7Y z6$;D_)zsVP`OhzXQ@Oq_jF8CE2G@^M63p;<r!5^%r_gyI^Q0`kP{>DteJs0`U`7GU zo&%!nIKy>DF2fYAuRY9kf6x>0k<^pM37M&kEiMC7ESTZR9&0DUCQ+!JpjgS0@>9aQ z;h|Y^2W}ptzGk_H@V<N>NiH&6WWy`mcVI>V%k~`%D-2K*)v(tKUv&{vs`>8GgQ7Q6 zoz`L;^s%FZ9QoDk7k5xF!?!u>7f~Y&+`#1dpjiaPN|JPc@#kR?+tfu2SgJ<WAC^*b zrgJHrCJq%3qW+-9e{oP<vrfDz{-_7r+=DefGhkK$<M_m<hwEKl**(f%A4ry~KcZ)p AZ2$lO literal 0 HcmV?d00001 diff --git a/tests/unit/storage_v2_decoder_encoder.cpp b/tests/unit/storage_v2_decoder_encoder.cpp index 63ad13394..d4a658e72 100644 --- a/tests/unit/storage_v2_decoder_encoder.cpp +++ b/tests/unit/storage_v2_decoder_encoder.cpp @@ -339,6 +339,8 @@ TEST_F(DecoderEncoderTest, PropertyValueInvalidMarker) { case storage::Marker::DELTA_LABEL_PROPERTY_INDEX_DROP: case storage::Marker::DELTA_EXISTENCE_CONSTRAINT_CREATE: case storage::Marker::DELTA_EXISTENCE_CONSTRAINT_DROP: + case storage::Marker::DELTA_UNIQUE_CONSTRAINT_CREATE: + case storage::Marker::DELTA_UNIQUE_CONSTRAINT_DROP: case storage::Marker::VALUE_FALSE: case storage::Marker::VALUE_TRUE: valid_marker = false; diff --git a/tests/unit/storage_v2_durability.cpp b/tests/unit/storage_v2_durability.cpp index cbfd29a7f..4a8817f30 100644 --- a/tests/unit/storage_v2_durability.cpp +++ b/tests/unit/storage_v2_durability.cpp @@ -65,6 +65,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> { auto label_indexed = store->NameToLabel("base_indexed"); auto label_unindexed = store->NameToLabel("base_unindexed"); auto property_id = store->NameToProperty("id"); + auto property_extra = store->NameToProperty("extra"); auto et1 = store->NameToEdgeType("base_et1"); auto et2 = store->NameToEdgeType("base_et2"); @@ -78,6 +79,12 @@ class DurabilityTest : public ::testing::TestWithParam<bool> { ASSERT_FALSE(store->CreateExistenceConstraint(label_unindexed, property_id) .HasError()); + // Create unique constraint. + ASSERT_FALSE(store + ->CreateUniqueConstraint(label_unindexed, + {property_id, property_extra}) + .HasError()); + // Create vertices. for (uint64_t i = 0; i < kNumBaseVertices; ++i) { auto acc = store->Access(); @@ -147,6 +154,10 @@ class DurabilityTest : public ::testing::TestWithParam<bool> { ASSERT_FALSE(store->CreateExistenceConstraint(label_unused, property_count) .HasError()); + // Create unique constraint. + ASSERT_FALSE(store->CreateUniqueConstraint(label_unused, {property_count}) + .HasError()); + // Storage accessor. std::optional<storage::Storage::Accessor> acc; if (single_transaction) acc.emplace(store->Access()); @@ -199,6 +210,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> { auto base_label_indexed = store->NameToLabel("base_indexed"); auto base_label_unindexed = store->NameToLabel("base_unindexed"); auto property_id = store->NameToProperty("id"); + auto property_extra = store->NameToProperty("extra"); auto et1 = store->NameToEdgeType("base_et1"); auto et2 = store->NameToEdgeType("base_et2"); @@ -247,11 +259,17 @@ class DurabilityTest : public ::testing::TestWithParam<bool> { case DatasetType::ONLY_BASE: ASSERT_THAT(info.existence, UnorderedElementsAre(std::make_pair( base_label_unindexed, property_id))); + ASSERT_THAT(info.unique, UnorderedElementsAre(std::make_pair( + base_label_unindexed, + std::set{property_id, property_extra}))); break; case DatasetType::ONLY_EXTENDED: ASSERT_THAT(info.existence, UnorderedElementsAre(std::make_pair(extended_label_unused, property_count))); + ASSERT_THAT(info.unique, + UnorderedElementsAre(std::make_pair( + extended_label_unused, std::set{property_count}))); break; case DatasetType::ONLY_BASE_WITH_EXTENDED_INDICES_AND_CONSTRAINTS: case DatasetType::ONLY_EXTENDED_WITH_BASE_INDICES_AND_CONSTRAINTS: @@ -261,6 +279,12 @@ class DurabilityTest : public ::testing::TestWithParam<bool> { UnorderedElementsAre( std::make_pair(base_label_unindexed, property_id), std::make_pair(extended_label_unused, property_count))); + ASSERT_THAT(info.unique, + UnorderedElementsAre( + std::make_pair(base_label_unindexed, + std::set{property_id, property_extra}), + std::make_pair(extended_label_unused, + std::set{property_count}))); break; } } @@ -1406,6 +1430,7 @@ TEST_P(DurabilityTest, WalCreateInSingleTransaction) { ASSERT_EQ(indices.label_property.size(), 0); auto constraints = store.ListAllConstraints(); ASSERT_EQ(constraints.existence.size(), 0); + ASSERT_EQ(constraints.unique.size(), 0); auto acc = store.Access(); { auto v1 = acc.FindVertex(gid_v1, storage::View::OLD); @@ -1509,17 +1534,21 @@ TEST_P(DurabilityTest, WalCreateAndRemoveEverything) { CreateBaseDataset(&store, GetParam()); CreateExtendedDataset(&store); auto indices = store.ListAllIndices(); - for (auto index : indices.label) { + for (const auto &index : indices.label) { ASSERT_TRUE(store.DropIndex(index)); } - for (auto index : indices.label_property) { + for (const auto &index : indices.label_property) { ASSERT_TRUE(store.DropIndex(index.first, index.second)); } auto constraints = store.ListAllConstraints(); - for (auto constraint : constraints.existence) { + for (const auto &constraint : constraints.existence) { ASSERT_TRUE( store.DropExistenceConstraint(constraint.first, constraint.second)); } + for (const auto &constraint : constraints.unique) { + ASSERT_EQ(store.DropUniqueConstraint(constraint.first, constraint.second), + storage::UniqueConstraints::DeletionStatus::SUCCESS); + } auto acc = store.Access(); for (auto vertex : acc.Vertices(storage::View::OLD)) { ASSERT_TRUE(acc.DetachDeleteVertex(&vertex).HasValue()); @@ -1542,6 +1571,7 @@ TEST_P(DurabilityTest, WalCreateAndRemoveEverything) { ASSERT_EQ(indices.label_property.size(), 0); auto constraints = store.ListAllConstraints(); ASSERT_EQ(constraints.existence.size(), 0); + ASSERT_EQ(constraints.unique.size(), 0); auto acc = store.Access(); uint64_t count = 0; auto iterable = acc.Vertices(storage::View::OLD); diff --git a/tests/unit/storage_v2_wal_file.cpp b/tests/unit/storage_v2_wal_file.cpp index 6b13bbf1e..fd2fd5443 100644 --- a/tests/unit/storage_v2_wal_file.cpp +++ b/tests/unit/storage_v2_wal_file.cpp @@ -28,6 +28,10 @@ storage::WalDeltaData::Type StorageGlobalOperationToWalDeltaDataType( return storage::WalDeltaData::Type::EXISTENCE_CONSTRAINT_CREATE; case storage::StorageGlobalOperation::EXISTENCE_CONSTRAINT_DROP: return storage::WalDeltaData::Type::EXISTENCE_CONSTRAINT_DROP; + case storage::StorageGlobalOperation::UNIQUE_CONSTRAINT_CREATE: + return storage::WalDeltaData::Type::UNIQUE_CONSTRAINT_CREATE; + case storage::StorageGlobalOperation::UNIQUE_CONSTRAINT_DROP: + return storage::WalDeltaData::Type::UNIQUE_CONSTRAINT_DROP; } } @@ -207,13 +211,14 @@ class DeltaGenerator final { void AppendOperation(storage::StorageGlobalOperation operation, const std::string &label, - std::optional<std::string> property = std::nullopt) { + const std::set<std::string> properties = {}) { auto label_id = storage::LabelId::FromUint(mapper_.NameToId(label)); - std::optional<storage::PropertyId> property_id; - if (property) { - property_id = storage::PropertyId::FromUint(mapper_.NameToId(*property)); + std::set<storage::PropertyId> property_ids; + for (const auto &property : properties) { + property_ids.insert( + storage::PropertyId::FromUint(mapper_.NameToId(property))); } - wal_file_.AppendOperation(operation, label_id, property_id, timestamp_); + wal_file_.AppendOperation(operation, label_id, property_ids, timestamp_); if (valid_) { UpdateStats(timestamp_, 1); storage::WalDeltaData data; @@ -228,7 +233,11 @@ class DeltaGenerator final { case storage::StorageGlobalOperation::EXISTENCE_CONSTRAINT_CREATE: case storage::StorageGlobalOperation::EXISTENCE_CONSTRAINT_DROP: data.operation_label_property.label = label; - data.operation_label_property.property = *property; + data.operation_label_property.property = *properties.begin(); + case storage::StorageGlobalOperation::UNIQUE_CONSTRAINT_CREATE: + case storage::StorageGlobalOperation::UNIQUE_CONSTRAINT_DROP: + data.operation_label_properties.label = label; + data.operation_label_properties.properties = properties; } data_.emplace_back(timestamp_, data); } @@ -515,10 +524,12 @@ GENERATE_SIMPLE_TEST(AllTransactionOperationsWithoutEnd, { GENERATE_SIMPLE_TEST(AllGlobalOperations, { OPERATION(LABEL_INDEX_CREATE, "hello"); OPERATION(LABEL_INDEX_DROP, "hello"); - OPERATION(LABEL_PROPERTY_INDEX_CREATE, "hello", "world"); - OPERATION(LABEL_PROPERTY_INDEX_DROP, "hello", "world"); - OPERATION(EXISTENCE_CONSTRAINT_CREATE, "hello", "world"); - OPERATION(EXISTENCE_CONSTRAINT_DROP, "hello", "world"); + OPERATION(LABEL_PROPERTY_INDEX_CREATE, "hello", {"world"}); + OPERATION(LABEL_PROPERTY_INDEX_DROP, "hello", {"world"}); + OPERATION(EXISTENCE_CONSTRAINT_CREATE, "hello", {"world"}); + OPERATION(EXISTENCE_CONSTRAINT_DROP, "hello", {"world"}); + OPERATION(UNIQUE_CONSTRAINT_CREATE, "hello", {"world", "and", "universe"}); + OPERATION(UNIQUE_CONSTRAINT_DROP, "hello", {"world", "and", "universe"}); }); // NOLINTNEXTLINE(hicpp-special-member-functions) @@ -578,7 +589,7 @@ TEST_P(WalFileTest, PartialData) { tx.AddLabel(vertex, "hello"); }); infos.emplace_back(gen.GetPosition(), gen.GetInfo()); - OPERATION(LABEL_PROPERTY_INDEX_CREATE, "hello", "world"); + OPERATION(LABEL_PROPERTY_INDEX_CREATE, "hello", {"world"}); infos.emplace_back(gen.GetPosition(), gen.GetInfo()); TRANSACTION(true, { auto vertex1 = tx.CreateVertex();