diff --git a/src/storage/v2/constraints.hpp b/src/storage/v2/constraints.hpp new file mode 100644 index 000000000..a7364c40d --- /dev/null +++ b/src/storage/v2/constraints.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include <optional> +#include <vector> + +#include "storage/v2/id_types.hpp" +#include "storage/v2/vertex.hpp" +#include "utils/result.hpp" +#include "utils/skip_list.hpp" + +namespace storage { + +struct Constraints { + std::vector<std::pair<LabelId, PropertyId>> existence_constraints; +}; + +struct ExistenceConstraintViolation { + LabelId label; + PropertyId property; +}; + +/// Adds a unique constraint to `constraints`. Returns true if the constraint +/// was successfuly added, false if it already exists and an +/// `ExistenceConstraintViolation` if there is an existing vertex violating the +/// constraint. +/// +/// @throw std::bad_alloc +/// @throw std::length_error +inline utils::BasicResult<ExistenceConstraintViolation, bool> +CreateExistenceConstraint(Constraints *constraints, LabelId label, + PropertyId property, + utils::SkipList<Vertex>::Accessor vertices) { + if (utils::Contains(constraints->existence_constraints, + std::make_pair(label, property))) { + return false; + } + for (const auto &vertex : vertices) { + if (!vertex.deleted && utils::Contains(vertex.labels, label) && + vertex.properties.find(property) == vertex.properties.end()) { + return ExistenceConstraintViolation{label, property}; + } + } + constraints->existence_constraints.emplace_back(label, property); + return true; +} + +/// Removes a unique constraint from `constraints`. Returns true if the +/// constraint was removed, and false if it doesn't exist. +inline bool DropExistenceConstraint(Constraints *constraints, LabelId label, + PropertyId property) { + auto it = std::find(constraints->existence_constraints.begin(), + constraints->existence_constraints.end(), + std::make_pair(label, property)); + if (it == constraints->existence_constraints.end()) { + return false; + } + constraints->existence_constraints.erase(it); + return true; +} + +/// Verifies that the given vertex satisfies all existence constraints. Returns +/// nullopt if all checks pass, and `ExistenceConstraintViolation` describing +/// the violated constraint otherwise. +[[nodiscard]] inline std::optional<ExistenceConstraintViolation> +ValidateExistenceConstraints(Vertex *vertex, Constraints *constraints) { + for (const auto &[label, property] : constraints->existence_constraints) { + if (!vertex->deleted && utils::Contains(vertex->labels, label) && + vertex->properties.find(property) == vertex->properties.end()) { + return ExistenceConstraintViolation{label, property}; + } + } + return std::nullopt; +} + +} // namespace storage diff --git a/src/storage/v2/storage.cpp b/src/storage/v2/storage.cpp index 682cbe8ea..174805358 100644 --- a/src/storage/v2/storage.cpp +++ b/src/storage/v2/storage.cpp @@ -580,7 +580,8 @@ EdgeTypeId Storage::Accessor::NameToEdgeType(const std::string &name) { void Storage::Accessor::AdvanceCommand() { ++transaction_.command_id; } -void Storage::Accessor::Commit() { +[[nodiscard]] std::optional<ExistenceConstraintViolation> +Storage::Accessor::Commit() { CHECK(is_transaction_active_) << "The transaction is already terminated!"; CHECK(!transaction_.must_abort) << "The transaction can't be committed!"; @@ -589,6 +590,23 @@ void Storage::Accessor::Commit() { // it. storage_->commit_log_.MarkFinished(transaction_.start_timestamp); } else { + // Validate that existence constraints are satisfied for all modified + // vertices. + for (const auto &delta : transaction_.deltas) { + auto prev = delta.prev.Get(); + if (prev.type != PreviousPtr::Type::VERTEX) { + continue; + } + // No need to take any locks here because we modified this vertex and no + // one else can touch it until we commit. + auto validation_result = + ValidateExistenceConstraints(prev.vertex, &storage_->constraints_); + if (validation_result) { + Abort(); + return *validation_result; + } + } + // Save these so we can mark them used in the commit log. uint64_t start_timestamp = transaction_.start_timestamp; uint64_t commit_timestamp; @@ -622,6 +640,8 @@ void Storage::Accessor::Commit() { if (storage_->gc_config_.type == StorageGcConfig::Type::ON_FINISH) { storage_->CollectGarbage(); } + + return std::nullopt; } void Storage::Accessor::Abort() { diff --git a/src/storage/v2/storage.hpp b/src/storage/v2/storage.hpp index e18fd3536..c87e361d8 100644 --- a/src/storage/v2/storage.hpp +++ b/src/storage/v2/storage.hpp @@ -4,6 +4,7 @@ #include <shared_mutex> #include "storage/v2/commit_log.hpp" +#include "storage/v2/constraints.hpp" #include "storage/v2/edge.hpp" #include "storage/v2/edge_accessor.hpp" #include "storage/v2/indices.hpp" @@ -215,7 +216,10 @@ class Storage final { void AdvanceCommand(); - void Commit(); + /// Commit returns `ExistenceConstraintViolation` if the changes made by + /// this transaction violate an existence constraint. In that case the + /// transaction is automatically aborted. Otherwise, nullopt is returned. + [[nodiscard]] std::optional<ExistenceConstraintViolation> Commit(); void Abort(); @@ -252,15 +256,36 @@ class Storage final { return indices_.label_property_index.IndexExists(label, property); } + /// Creates a unique constraint`. Returns true if the constraint was + /// successfuly added, false if it already exists and an + /// `ExistenceConstraintViolation` if there is an existing vertex violating + /// the constraint. + /// + /// @throw std::bad_alloc + /// @throw std::length_error + utils::BasicResult<ExistenceConstraintViolation, bool> + CreateExistenceConstraint(LabelId label, PropertyId property) { + std::unique_lock<utils::RWLock> storage_guard(main_lock_); + return ::storage::CreateExistenceConstraint(&constraints_, label, property, + vertices_.access()); + } + + /// Removes a unique constraint. Returns true if the constraint was removed, + /// and false if it doesn't exist. + bool DropExistenceConstraint(LabelId label, PropertyId property) { + std::unique_lock<utils::RWLock> storage_guard(main_lock_); + return ::storage::DropExistenceConstraint(&constraints_, label, property); + } + private: void CollectGarbage(); // Main storage lock. // // Accessors take a shared lock when starting, so it is possible to block - // creation of new accessors by taking a unique lock. This is used when - // building a label-property index because it is much simpler to do when there - // are no parallel reads and writes. + // creation of new accessors by taking a unique lock. This is used when doing + // operations on storage that affect the global state, for example index + // creation. utils::RWLock main_lock_{utils::RWLock::Priority::WRITE}; // Main object storage @@ -272,6 +297,7 @@ class Storage final { NameIdMapper name_id_mapper_; Indices indices_; + Constraints constraints_; // Transaction engine utils::SpinLock engine_lock_; diff --git a/tests/benchmark/storage_v2_gc.cpp b/tests/benchmark/storage_v2_gc.cpp index aafce71c9..bb7787cd3 100644 --- a/tests/benchmark/storage_v2_gc.cpp +++ b/tests/benchmark/storage_v2_gc.cpp @@ -43,7 +43,7 @@ void UpdateLabelFunc(int thread_id, storage::Storage *storage, << "Vertex with GID " << gid.AsUint() << " doesn't exist"; if (vertex->AddLabel(storage::LabelId::FromUint(label_dist(gen))) .HasValue()) { - acc.Commit(); + CHECK(acc.Commit() == std::nullopt); } else { acc.Abort(); } @@ -61,7 +61,7 @@ int main(int argc, char *argv[]) { for (int i = 0; i < FLAGS_num_vertices; ++i) { vertices.push_back(acc.CreateVertex().Gid()); } - acc.Commit(); + CHECK(acc.Commit() == std::nullopt); } utils::Timer timer; diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 804e26c19..c589e8788 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -310,6 +310,9 @@ target_link_libraries(${test_prefix}auth mg-auth kvstore_lib) add_unit_test(property_value_v2.cpp) target_link_libraries(${test_prefix}property_value_v2 mg-utils) +add_unit_test(storage_v2_constraints.cpp) +target_link_libraries(${test_prefix}storage_v2_constraints mg-storage-v2) + add_unit_test(storage_v2_edge.cpp) target_link_libraries(${test_prefix}storage_v2_edge mg-storage-v2) diff --git a/tests/unit/storage_v2.cpp b/tests/unit/storage_v2.cpp index be474f03b..d80a01223 100644 --- a/tests/unit/storage_v2.cpp +++ b/tests/unit/storage_v2.cpp @@ -25,7 +25,7 @@ TEST(StorageV2, Commit) { EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U); ASSERT_TRUE(acc.FindVertex(gid, storage::View::NEW).has_value()); EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 1U); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } { auto acc = store.Access(); @@ -45,7 +45,7 @@ TEST(StorageV2, Commit) { EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 1U); EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 0U); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } { auto acc = store.Access(); @@ -111,7 +111,7 @@ TEST(StorageV2, AdvanceCommandCommit) { ASSERT_TRUE(acc.FindVertex(gid1, storage::View::OLD).has_value()); ASSERT_TRUE(acc.FindVertex(gid1, storage::View::NEW).has_value()); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } { auto acc = store.Access(); @@ -185,7 +185,7 @@ TEST(StorageV2, SnapshotIsolation) { EXPECT_EQ(CountVertices(&acc1, storage::View::NEW), 1U); EXPECT_EQ(CountVertices(&acc2, storage::View::NEW), 0U); - acc1.Commit(); + ASSERT_EQ(acc1.Commit(), std::nullopt); ASSERT_FALSE(acc2.FindVertex(gid, storage::View::OLD).has_value()); EXPECT_EQ(CountVertices(&acc2, storage::View::OLD), 0U); @@ -224,7 +224,7 @@ TEST(StorageV2, AccessorMove) { ASSERT_TRUE(moved.FindVertex(gid, storage::View::NEW).has_value()); EXPECT_EQ(CountVertices(&moved, storage::View::NEW), 1U); - moved.Commit(); + ASSERT_EQ(moved.Commit(), std::nullopt); } { auto acc = store.Access(); @@ -253,7 +253,7 @@ TEST(StorageV2, VertexDeleteCommit) { EXPECT_EQ(CountVertices(&acc2, storage::View::OLD), 0U); ASSERT_TRUE(acc2.FindVertex(gid, storage::View::NEW).has_value()); EXPECT_EQ(CountVertices(&acc2, storage::View::NEW), 1U); - acc2.Commit(); + ASSERT_EQ(acc2.Commit(), std::nullopt); } auto acc3 = store.Access(); // read transaction @@ -283,7 +283,7 @@ TEST(StorageV2, VertexDeleteCommit) { EXPECT_EQ(CountVertices(&acc4, storage::View::OLD), 1U); EXPECT_EQ(CountVertices(&acc4, storage::View::NEW), 0U); - acc4.Commit(); + ASSERT_EQ(acc4.Commit(), std::nullopt); } auto acc5 = store.Access(); // read transaction @@ -324,7 +324,7 @@ TEST(StorageV2, VertexDeleteAbort) { EXPECT_EQ(CountVertices(&acc2, storage::View::OLD), 0U); ASSERT_TRUE(acc2.FindVertex(gid, storage::View::NEW).has_value()); EXPECT_EQ(CountVertices(&acc2, storage::View::NEW), 1U); - acc2.Commit(); + ASSERT_EQ(acc2.Commit(), std::nullopt); } auto acc3 = store.Access(); // read transaction @@ -390,7 +390,7 @@ TEST(StorageV2, VertexDeleteAbort) { EXPECT_EQ(CountVertices(&acc6, storage::View::OLD), 1U); EXPECT_EQ(CountVertices(&acc6, storage::View::NEW), 0U); - acc6.Commit(); + ASSERT_EQ(acc6.Commit(), std::nullopt); } auto acc7 = store.Access(); // read transaction @@ -420,10 +420,10 @@ TEST(StorageV2, VertexDeleteAbort) { EXPECT_EQ(CountVertices(&acc7, storage::View::NEW), 0U); // Commit all accessors - acc1.Commit(); - acc3.Commit(); - acc5.Commit(); - acc7.Commit(); + ASSERT_EQ(acc1.Commit(), std::nullopt); + ASSERT_EQ(acc3.Commit(), std::nullopt); + ASSERT_EQ(acc5.Commit(), std::nullopt); + ASSERT_EQ(acc7.Commit(), std::nullopt); } // NOLINTNEXTLINE(hicpp-special-member-functions) @@ -437,7 +437,7 @@ TEST(StorageV2, VertexDeleteSerializationError) { auto acc = store.Access(); auto vertex = acc.CreateVertex(); gid = vertex.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } auto acc1 = store.Access(); @@ -481,7 +481,7 @@ TEST(StorageV2, VertexDeleteSerializationError) { } // Finalize both accessors - acc1.Commit(); + ASSERT_EQ(acc1.Commit(), std::nullopt); acc2.Abort(); // Check whether the vertex exists @@ -491,7 +491,7 @@ TEST(StorageV2, VertexDeleteSerializationError) { ASSERT_FALSE(vertex); EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U); EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 0U); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } } @@ -535,7 +535,7 @@ TEST(StorageV2, VertexDeleteSpecialCases) { ASSERT_TRUE(res.GetValue()); EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U); EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 0U); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the vertices exist @@ -564,7 +564,7 @@ TEST(StorageV2, VertexDeleteLabel) { gid = vertex.Gid(); ASSERT_FALSE(acc.FindVertex(gid, storage::View::OLD).has_value()); ASSERT_TRUE(acc.FindVertex(gid, storage::View::NEW).has_value()); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Add label, delete the vertex and check the label API (same command) @@ -725,7 +725,7 @@ TEST(StorageV2, VertexDeleteProperty) { gid = vertex.Gid(); ASSERT_FALSE(acc.FindVertex(gid, storage::View::OLD).has_value()); ASSERT_TRUE(acc.FindVertex(gid, storage::View::NEW).has_value()); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Set property, delete the vertex and check the property API (same command) @@ -743,9 +743,8 @@ TEST(StorageV2, VertexDeleteProperty) { ASSERT_EQ(vertex->Properties(storage::View::NEW)->size(), 0); // Set property 5 to "nandare" - ASSERT_TRUE( - vertex->SetProperty(property, storage::PropertyValue("nandare")) - .GetValue()); + ASSERT_TRUE(vertex->SetProperty(property, storage::PropertyValue("nandare")) + .GetValue()); // Check whether property 5 exists ASSERT_TRUE(vertex->GetProperty(property, storage::View::OLD)->IsNull()); @@ -796,9 +795,8 @@ TEST(StorageV2, VertexDeleteProperty) { ASSERT_EQ(vertex->Properties(storage::View::NEW)->size(), 0); // Set property 5 to "nandare" - ASSERT_TRUE( - vertex->SetProperty(property, storage::PropertyValue("nandare")) - .GetValue()); + ASSERT_TRUE(vertex->SetProperty(property, storage::PropertyValue("nandare")) + .GetValue()); // Check whether property 5 exists ASSERT_TRUE(vertex->GetProperty(property, storage::View::OLD)->IsNull()); @@ -905,7 +903,7 @@ TEST(StorageV2, VertexLabelCommit) { ASSERT_FALSE(res.GetValue()); } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } { auto acc = store.Access(); @@ -964,7 +962,7 @@ TEST(StorageV2, VertexLabelCommit) { ASSERT_FALSE(res.GetValue()); } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } { auto acc = store.Access(); @@ -998,7 +996,7 @@ TEST(StorageV2, VertexLabelAbort) { auto acc = store.Access(); auto vertex = acc.CreateVertex(); gid = vertex.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Add label 5, but abort the transaction. @@ -1085,7 +1083,7 @@ TEST(StorageV2, VertexLabelAbort) { ASSERT_FALSE(res.GetValue()); } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check that label 5 exists. @@ -1211,7 +1209,7 @@ TEST(StorageV2, VertexLabelAbort) { ASSERT_FALSE(res.GetValue()); } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check that label 5 doesn't exist. @@ -1245,7 +1243,7 @@ TEST(StorageV2, VertexLabelSerializationError) { auto acc = store.Access(); auto vertex = acc.CreateVertex(); gid = vertex.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } auto acc1 = store.Access(); @@ -1313,7 +1311,7 @@ TEST(StorageV2, VertexLabelSerializationError) { } // Finalize both accessors. - acc1.Commit(); + ASSERT_EQ(acc1.Commit(), std::nullopt); acc2.Abort(); // Check which labels exist. @@ -1390,7 +1388,7 @@ TEST(StorageV2, VertexPropertyCommit) { ASSERT_EQ(properties[property].ValueString(), "nandare"); } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } { auto acc = store.Access(); @@ -1454,7 +1452,7 @@ TEST(StorageV2, VertexPropertyCommit) { ASSERT_TRUE(res.GetValue()); } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } { auto acc = store.Access(); @@ -1490,7 +1488,7 @@ TEST(StorageV2, VertexPropertyAbort) { auto acc = store.Access(); auto vertex = acc.CreateVertex(); gid = vertex.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Set property 5 to "nandare", but abort the transaction. @@ -1601,7 +1599,7 @@ TEST(StorageV2, VertexPropertyAbort) { ASSERT_EQ(properties[property].ValueString(), "nandare"); } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check that property 5 is "nandare". @@ -1757,7 +1755,7 @@ TEST(StorageV2, VertexPropertyAbort) { ASSERT_TRUE(vertex->GetProperty(property, storage::View::NEW)->IsNull()); ASSERT_EQ(vertex->Properties(storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check that property 5 is null. @@ -1793,7 +1791,7 @@ TEST(StorageV2, VertexPropertySerializationError) { auto acc = store.Access(); auto vertex = acc.CreateVertex(); gid = vertex.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } auto acc1 = store.Access(); @@ -1857,7 +1855,7 @@ TEST(StorageV2, VertexPropertySerializationError) { } // Finalize both accessors. - acc1.Commit(); + ASSERT_EQ(acc1.Commit(), std::nullopt); acc2.Abort(); // Check which properties exist. @@ -1942,7 +1940,7 @@ TEST(StorageV2, VertexLabelPropertyMixed) { // Set property 5 to "nandare" ASSERT_TRUE(vertex.SetProperty(property, storage::PropertyValue("nandare")) - .GetValue()); + .GetValue()); // Check whether label 5 and property 5 exist ASSERT_TRUE(vertex.HasLabel(label, storage::View::OLD).GetValue()); @@ -2000,7 +1998,7 @@ TEST(StorageV2, VertexLabelPropertyMixed) { // Set property 5 to "haihai" ASSERT_FALSE(vertex.SetProperty(property, storage::PropertyValue("haihai")) - .GetValue()); + .GetValue()); // Check whether label 5 and property 5 exist ASSERT_TRUE(vertex.HasLabel(label, storage::View::OLD).GetValue()); @@ -2143,5 +2141,5 @@ TEST(StorageV2, VertexLabelPropertyMixed) { ASSERT_EQ(vertex.Properties(storage::View::OLD)->size(), 0); ASSERT_EQ(vertex.Properties(storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } diff --git a/tests/unit/storage_v2_constraints.cpp b/tests/unit/storage_v2_constraints.cpp new file mode 100644 index 000000000..c3d5861f7 --- /dev/null +++ b/tests/unit/storage_v2_constraints.cpp @@ -0,0 +1,166 @@ +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "storage/v2/storage.hpp" + +// NOLINTNEXTLINE(google-build-using-namespace) +using namespace storage; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ASSERT_NO_ERROR(result) ASSERT_FALSE((result).HasError()) + +bool operator==(const ExistenceConstraintViolation &lhs, + const ExistenceConstraintViolation &rhs) { + return lhs.label == rhs.label && lhs.property == rhs.property; +} + +class ConstraintsTest : public testing::Test { + protected: + ConstraintsTest() + : prop1(storage.NameToProperty("prop1")), + prop2(storage.NameToProperty("prop2")), + label1(storage.NameToLabel("label1")), + label2(storage.NameToLabel("label2")) {} + + Storage storage; + PropertyId prop1; + PropertyId prop2; + LabelId label1; + LabelId label2; +}; + +// NOLINTNEXTLINE(hicpp-special-member-functions) +TEST_F(ConstraintsTest, CreateAndDrop) { + { + auto res = storage.CreateExistenceConstraint(label1, prop1); + EXPECT_TRUE(res.HasValue() && res.GetValue()); + } + { + auto res = storage.CreateExistenceConstraint(label1, prop1); + EXPECT_TRUE(res.HasValue() && !res.GetValue()); + } + { + auto res = storage.CreateExistenceConstraint(label2, prop1); + EXPECT_TRUE(res.HasValue() && res.GetValue()); + } + EXPECT_TRUE(storage.DropExistenceConstraint(label1, prop1)); + EXPECT_FALSE(storage.DropExistenceConstraint(label1, prop1)); + EXPECT_TRUE(storage.DropExistenceConstraint(label2, prop1)); + EXPECT_FALSE(storage.DropExistenceConstraint(label2, prop2)); + { + auto res = storage.CreateExistenceConstraint(label2, prop1); + EXPECT_TRUE(res.HasValue() && res.GetValue()); + } +} + +// NOLINTNEXTLINE(hicpp-special-member-functions) +TEST_F(ConstraintsTest, CreateFailure1) { + { + auto acc = storage.Access(); + auto vertex = acc.CreateVertex(); + ASSERT_NO_ERROR(vertex.AddLabel(label1)); + ASSERT_EQ(acc.Commit(), std::nullopt); + } + { + auto res = storage.CreateExistenceConstraint(label1, prop1); + EXPECT_TRUE( + res.HasError() && + (res.GetError() == ExistenceConstraintViolation{label1, prop1})); + } + { + auto acc = storage.Access(); + for (auto vertex : acc.Vertices(View::OLD)) { + ASSERT_NO_ERROR(acc.DeleteVertex(&vertex)); + } + ASSERT_EQ(acc.Commit(), std::nullopt); + } + { + auto res = storage.CreateExistenceConstraint(label1, prop1); + EXPECT_TRUE(res.HasValue() && res.GetValue()); + } +} + +// NOLINTNEXTLINE(hicpp-special-member-functions) +TEST_F(ConstraintsTest, CreateFailure2) { + { + auto acc = storage.Access(); + auto vertex = acc.CreateVertex(); + ASSERT_NO_ERROR(vertex.AddLabel(label1)); + ASSERT_EQ(acc.Commit(), std::nullopt); + } + { + auto res = storage.CreateExistenceConstraint(label1, prop1); + EXPECT_TRUE( + res.HasError() && + (res.GetError() == ExistenceConstraintViolation{label1, prop1})); + } + { + auto acc = storage.Access(); + for (auto vertex : acc.Vertices(View::OLD)) { + ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1))); + } + ASSERT_EQ(acc.Commit(), std::nullopt); + } + { + auto res = storage.CreateExistenceConstraint(label1, prop1); + EXPECT_TRUE(res.HasValue() && res.GetValue()); + } +} + +// NOLINTNEXTLINE(hicpp-special-member-functions) +TEST_F(ConstraintsTest, ViolationOnCommit) { + { + auto res = storage.CreateExistenceConstraint(label1, prop1); + ASSERT_TRUE(res.HasValue() && res.GetValue()); + } + + { + auto acc = storage.Access(); + auto vertex = acc.CreateVertex(); + ASSERT_NO_ERROR(vertex.AddLabel(label1)); + + auto res = acc.Commit(); + EXPECT_TRUE(res.has_value() && + (*res == ExistenceConstraintViolation{label1, prop1})); + } + + { + auto acc = storage.Access(); + auto vertex = acc.CreateVertex(); + ASSERT_NO_ERROR(vertex.AddLabel(label1)); + ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1))); + EXPECT_EQ(acc.Commit(), std::nullopt); + } + + { + auto acc = storage.Access(); + for (auto vertex : acc.Vertices(View::OLD)) { + ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue())); + } + + auto res = acc.Commit(); + EXPECT_TRUE(res.has_value() && + (*res == ExistenceConstraintViolation{label1, prop1})); + } + + { + auto acc = storage.Access(); + for (auto vertex : acc.Vertices(View::OLD)) { + ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue())); + } + for (auto vertex : acc.Vertices(View::OLD)) { + ASSERT_NO_ERROR(acc.DeleteVertex(&vertex)); + } + + EXPECT_EQ(acc.Commit(), std::nullopt); + } + + ASSERT_TRUE(storage.DropExistenceConstraint(label1, prop1)); + + { + auto acc = storage.Access(); + auto vertex = acc.CreateVertex(); + ASSERT_NO_ERROR(vertex.AddLabel(label1)); + EXPECT_EQ(acc.Commit(), std::nullopt); + } +} diff --git a/tests/unit/storage_v2_edge.cpp b/tests/unit/storage_v2_edge.cpp index a77c551e8..21d3f9f64 100644 --- a/tests/unit/storage_v2_edge.cpp +++ b/tests/unit/storage_v2_edge.cpp @@ -19,7 +19,7 @@ TEST(StorageV2, EdgeCreateFromSmallerCommit) { auto vertex_to = acc.CreateVertex(); gid_from = vertex_from.Gid(); gid_to = vertex_to.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Create edge @@ -77,7 +77,7 @@ TEST(StorageV2, EdgeCreateFromSmallerCommit) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -152,7 +152,7 @@ TEST(StorageV2, EdgeCreateFromSmallerCommit) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } } @@ -171,7 +171,7 @@ TEST(StorageV2, EdgeCreateFromLargerCommit) { auto vertex_from = acc.CreateVertex(); gid_to = vertex_to.Gid(); gid_from = vertex_from.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Create edge @@ -229,7 +229,7 @@ TEST(StorageV2, EdgeCreateFromLargerCommit) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -304,7 +304,7 @@ TEST(StorageV2, EdgeCreateFromLargerCommit) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } } @@ -319,7 +319,7 @@ TEST(StorageV2, EdgeCreateFromSameCommit) { auto acc = store.Access(); auto vertex = acc.CreateVertex(); gid_vertex = vertex.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Create edge @@ -369,7 +369,7 @@ TEST(StorageV2, EdgeCreateFromSameCommit) { ASSERT_EQ(vertex->InEdges({other_et}, storage::View::NEW)->size(), 0); ASSERT_EQ(vertex->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -434,7 +434,7 @@ TEST(StorageV2, EdgeCreateFromSameCommit) { ASSERT_EQ(vertex->OutEdges({et, other_et}, storage::View::OLD)->size(), 1); ASSERT_EQ(vertex->OutEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } } @@ -453,7 +453,7 @@ TEST(StorageV2, EdgeCreateFromSmallerAbort) { auto vertex_to = acc.CreateVertex(); gid_from = vertex_from.Gid(); gid_to = vertex_to.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Create edge, but abort the transaction @@ -532,7 +532,7 @@ TEST(StorageV2, EdgeCreateFromSmallerAbort) { ASSERT_EQ(vertex_to->OutEdges({}, storage::View::OLD)->size(), 0); ASSERT_EQ(vertex_to->OutEdges({}, storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Create edge @@ -590,7 +590,7 @@ TEST(StorageV2, EdgeCreateFromSmallerAbort) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -665,7 +665,7 @@ TEST(StorageV2, EdgeCreateFromSmallerAbort) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } } @@ -684,7 +684,7 @@ TEST(StorageV2, EdgeCreateFromLargerAbort) { auto vertex_from = acc.CreateVertex(); gid_to = vertex_to.Gid(); gid_from = vertex_from.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Create edge, but abort the transaction @@ -763,7 +763,7 @@ TEST(StorageV2, EdgeCreateFromLargerAbort) { ASSERT_EQ(vertex_to->OutEdges({}, storage::View::OLD)->size(), 0); ASSERT_EQ(vertex_to->OutEdges({}, storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Create edge @@ -821,7 +821,7 @@ TEST(StorageV2, EdgeCreateFromLargerAbort) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -896,7 +896,7 @@ TEST(StorageV2, EdgeCreateFromLargerAbort) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } } @@ -911,7 +911,7 @@ TEST(StorageV2, EdgeCreateFromSameAbort) { auto acc = store.Access(); auto vertex = acc.CreateVertex(); gid_vertex = vertex.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Create edge, but abort the transaction @@ -976,7 +976,7 @@ TEST(StorageV2, EdgeCreateFromSameAbort) { ASSERT_EQ(vertex->OutEdges({}, storage::View::OLD)->size(), 0); ASSERT_EQ(vertex->OutEdges({}, storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Create edge @@ -1026,7 +1026,7 @@ TEST(StorageV2, EdgeCreateFromSameAbort) { ASSERT_EQ(vertex->InEdges({other_et}, storage::View::NEW)->size(), 0); ASSERT_EQ(vertex->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -1091,7 +1091,7 @@ TEST(StorageV2, EdgeCreateFromSameAbort) { ASSERT_EQ(vertex->OutEdges({et, other_et}, storage::View::OLD)->size(), 1); ASSERT_EQ(vertex->OutEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } } @@ -1110,7 +1110,7 @@ TEST(StorageV2, EdgeDeleteFromSmallerCommit) { auto vertex_to = acc.CreateVertex(); gid_from = vertex_from.Gid(); gid_to = vertex_to.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Create edge @@ -1168,7 +1168,7 @@ TEST(StorageV2, EdgeDeleteFromSmallerCommit) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -1243,7 +1243,7 @@ TEST(StorageV2, EdgeDeleteFromSmallerCommit) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Delete edge @@ -1300,7 +1300,7 @@ TEST(StorageV2, EdgeDeleteFromSmallerCommit) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::OLD)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -1321,7 +1321,7 @@ TEST(StorageV2, EdgeDeleteFromSmallerCommit) { ASSERT_EQ(vertex_to->OutEdges({}, storage::View::OLD)->size(), 0); ASSERT_EQ(vertex_to->OutEdges({}, storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } } @@ -1340,7 +1340,7 @@ TEST(StorageV2, EdgeDeleteFromLargerCommit) { auto vertex_from = acc.CreateVertex(); gid_from = vertex_from.Gid(); gid_to = vertex_to.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Create edge @@ -1398,7 +1398,7 @@ TEST(StorageV2, EdgeDeleteFromLargerCommit) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -1473,7 +1473,7 @@ TEST(StorageV2, EdgeDeleteFromLargerCommit) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Delete edge @@ -1530,7 +1530,7 @@ TEST(StorageV2, EdgeDeleteFromLargerCommit) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::OLD)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -1551,7 +1551,7 @@ TEST(StorageV2, EdgeDeleteFromLargerCommit) { ASSERT_EQ(vertex_to->OutEdges({}, storage::View::OLD)->size(), 0); ASSERT_EQ(vertex_to->OutEdges({}, storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } } @@ -1566,7 +1566,7 @@ TEST(StorageV2, EdgeDeleteFromSameCommit) { auto acc = store.Access(); auto vertex = acc.CreateVertex(); gid_vertex = vertex.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Create edge @@ -1616,7 +1616,7 @@ TEST(StorageV2, EdgeDeleteFromSameCommit) { ASSERT_EQ(vertex->InEdges({other_et}, storage::View::NEW)->size(), 0); ASSERT_EQ(vertex->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -1681,7 +1681,7 @@ TEST(StorageV2, EdgeDeleteFromSameCommit) { ASSERT_EQ(vertex->OutEdges({et, other_et}, storage::View::OLD)->size(), 1); ASSERT_EQ(vertex->OutEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Delete edge @@ -1730,7 +1730,7 @@ TEST(StorageV2, EdgeDeleteFromSameCommit) { ASSERT_EQ(vertex->OutEdges({other_et}, storage::View::OLD)->size(), 0); ASSERT_EQ(vertex->OutEdges({et, other_et}, storage::View::OLD)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -1745,7 +1745,7 @@ TEST(StorageV2, EdgeDeleteFromSameCommit) { ASSERT_EQ(vertex->OutEdges({}, storage::View::OLD)->size(), 0); ASSERT_EQ(vertex->OutEdges({}, storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } } @@ -1764,7 +1764,7 @@ TEST(StorageV2, EdgeDeleteFromSmallerAbort) { auto vertex_to = acc.CreateVertex(); gid_from = vertex_from.Gid(); gid_to = vertex_to.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Create edge @@ -1822,7 +1822,7 @@ TEST(StorageV2, EdgeDeleteFromSmallerAbort) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -1897,7 +1897,7 @@ TEST(StorageV2, EdgeDeleteFromSmallerAbort) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Delete the edge, but abort the transaction @@ -2029,7 +2029,7 @@ TEST(StorageV2, EdgeDeleteFromSmallerAbort) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Delete the edge @@ -2086,7 +2086,7 @@ TEST(StorageV2, EdgeDeleteFromSmallerAbort) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::OLD)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -2107,7 +2107,7 @@ TEST(StorageV2, EdgeDeleteFromSmallerAbort) { ASSERT_EQ(vertex_to->OutEdges({}, storage::View::OLD)->size(), 0); ASSERT_EQ(vertex_to->OutEdges({}, storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } } @@ -2126,7 +2126,7 @@ TEST(StorageV2, EdgeDeleteFromLargerAbort) { auto vertex_to = acc.CreateVertex(); gid_from = vertex_from.Gid(); gid_to = vertex_to.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Create edge @@ -2184,7 +2184,7 @@ TEST(StorageV2, EdgeDeleteFromLargerAbort) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -2259,7 +2259,7 @@ TEST(StorageV2, EdgeDeleteFromLargerAbort) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Delete the edge, but abort the transaction @@ -2391,7 +2391,7 @@ TEST(StorageV2, EdgeDeleteFromLargerAbort) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Delete the edge @@ -2448,7 +2448,7 @@ TEST(StorageV2, EdgeDeleteFromLargerAbort) { ASSERT_EQ(vertex_to->InEdges({et, other_et}, storage::View::OLD)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -2469,7 +2469,7 @@ TEST(StorageV2, EdgeDeleteFromLargerAbort) { ASSERT_EQ(vertex_to->OutEdges({}, storage::View::OLD)->size(), 0); ASSERT_EQ(vertex_to->OutEdges({}, storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } } @@ -2484,7 +2484,7 @@ TEST(StorageV2, EdgeDeleteFromSameAbort) { auto acc = store.Access(); auto vertex = acc.CreateVertex(); gid_vertex = vertex.Gid(); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Create edge @@ -2534,7 +2534,7 @@ TEST(StorageV2, EdgeDeleteFromSameAbort) { ASSERT_EQ(vertex->InEdges({other_et}, storage::View::NEW)->size(), 0); ASSERT_EQ(vertex->InEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -2599,7 +2599,7 @@ TEST(StorageV2, EdgeDeleteFromSameAbort) { ASSERT_EQ(vertex->OutEdges({et, other_et}, storage::View::OLD)->size(), 1); ASSERT_EQ(vertex->OutEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Delete the edge, but abort the transaction @@ -2713,7 +2713,7 @@ TEST(StorageV2, EdgeDeleteFromSameAbort) { ASSERT_EQ(vertex->OutEdges({et, other_et}, storage::View::OLD)->size(), 1); ASSERT_EQ(vertex->OutEdges({et, other_et}, storage::View::NEW)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Delete the edge @@ -2762,7 +2762,7 @@ TEST(StorageV2, EdgeDeleteFromSameAbort) { ASSERT_EQ(vertex->OutEdges({other_et}, storage::View::OLD)->size(), 0); ASSERT_EQ(vertex->OutEdges({et, other_et}, storage::View::OLD)->size(), 1); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check whether the edge exists @@ -2777,7 +2777,7 @@ TEST(StorageV2, EdgeDeleteFromSameAbort) { ASSERT_EQ(vertex->OutEdges({}, storage::View::OLD)->size(), 0); ASSERT_EQ(vertex->OutEdges({}, storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } } @@ -2831,7 +2831,7 @@ TEST(StorageV2, VertexDetachDeleteSingleCommit) { } ASSERT_EQ(vertex_to.OutEdges({}, storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Detach delete vertex @@ -2888,7 +2888,7 @@ TEST(StorageV2, VertexDetachDeleteSingleCommit) { ASSERT_EQ(vertex_to->OutEdges({}, storage::View::OLD)->size(), 0); ASSERT_EQ(vertex_to->OutEdges({}, storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check dataset @@ -3043,7 +3043,7 @@ TEST(StorageV2, VertexDetachDeleteMultipleCommit) { } } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Detach delete vertex @@ -3183,7 +3183,7 @@ TEST(StorageV2, VertexDetachDeleteMultipleCommit) { ASSERT_EQ(e.ToVertex(), *vertex2); } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check dataset @@ -3290,7 +3290,7 @@ TEST(StorageV2, VertexDetachDeleteSingleAbort) { } ASSERT_EQ(vertex_to.OutEdges({}, storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Detach delete vertex, but abort the transaction @@ -3384,7 +3384,7 @@ TEST(StorageV2, VertexDetachDeleteSingleAbort) { } ASSERT_EQ(vertex_to->OutEdges({}, storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Detach delete vertex @@ -3441,7 +3441,7 @@ TEST(StorageV2, VertexDetachDeleteSingleAbort) { ASSERT_EQ(vertex_to->OutEdges({}, storage::View::OLD)->size(), 0); ASSERT_EQ(vertex_to->OutEdges({}, storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check dataset @@ -3596,7 +3596,7 @@ TEST(StorageV2, VertexDetachDeleteMultipleAbort) { } } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Detach delete vertex, but abort the transaction @@ -3922,7 +3922,7 @@ TEST(StorageV2, VertexDetachDeleteMultipleAbort) { } } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Detach delete vertex @@ -4062,7 +4062,7 @@ TEST(StorageV2, VertexDetachDeleteMultipleAbort) { ASSERT_EQ(e.ToVertex(), *vertex2); } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check dataset @@ -4168,7 +4168,7 @@ TEST(StorageV2, EdgePropertyCommit) { ASSERT_EQ(properties[property].ValueString(), "nandare"); } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } { auto acc = store.Access(); @@ -4232,7 +4232,7 @@ TEST(StorageV2, EdgePropertyCommit) { ASSERT_TRUE(res.GetValue()); } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } { auto acc = store.Access(); @@ -4272,7 +4272,7 @@ TEST(StorageV2, EdgePropertyAbort) { ASSERT_EQ(edge.EdgeType(), et); ASSERT_EQ(edge.FromVertex(), vertex); ASSERT_EQ(edge.ToVertex(), vertex); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Set property 5 to "nandare", but abort the transaction. @@ -4382,7 +4382,7 @@ TEST(StorageV2, EdgePropertyAbort) { ASSERT_EQ(properties[property].ValueString(), "nandare"); } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check that property 5 is "nandare". @@ -4538,7 +4538,7 @@ TEST(StorageV2, EdgePropertyAbort) { ASSERT_TRUE(edge.GetProperty(property, storage::View::NEW)->IsNull()); ASSERT_EQ(edge.Properties(storage::View::NEW)->size(), 0); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Check that property 5 is null. @@ -4578,7 +4578,7 @@ TEST(StorageV2, EdgePropertySerializationError) { ASSERT_EQ(edge.EdgeType(), et); ASSERT_EQ(edge.FromVertex(), vertex); ASSERT_EQ(edge.ToVertex(), vertex); - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } auto acc1 = store.Access(); @@ -4642,7 +4642,7 @@ TEST(StorageV2, EdgePropertySerializationError) { } // Finalize both accessors. - acc1.Commit(); + ASSERT_EQ(acc1.Commit(), std::nullopt); acc2.Abort(); // Check which properties exist. diff --git a/tests/unit/storage_v2_gc.cpp b/tests/unit/storage_v2_gc.cpp index fed5abe3e..b5238b027 100644 --- a/tests/unit/storage_v2_gc.cpp +++ b/tests/unit/storage_v2_gc.cpp @@ -47,7 +47,7 @@ TEST(StorageV2Gc, Sanity) { EXPECT_EQ(vertex_new.has_value(), i % 5 != 0); } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Verify existing vertices and add labels to some of them. @@ -90,7 +90,7 @@ TEST(StorageV2Gc, Sanity) { } } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } // Add and remove some edges. @@ -148,7 +148,7 @@ TEST(StorageV2Gc, Sanity) { } } - acc.Commit(); + ASSERT_EQ(acc.Commit(), std::nullopt); } } @@ -171,7 +171,7 @@ TEST(StorageV2Gc, Indices) { auto vertex = acc0.CreateVertex(); ASSERT_TRUE(*vertex.AddLabel(acc0.NameToLabel("label"))); } - acc0.Commit(); + ASSERT_EQ(acc0.Commit(), std::nullopt); } { auto acc1 = storage.Access(); @@ -180,7 +180,7 @@ TEST(StorageV2Gc, Indices) { for (auto vertex : acc2.Vertices(storage::View::OLD)) { ASSERT_TRUE(*vertex.RemoveLabel(acc2.NameToLabel("label"))); } - acc2.Commit(); + ASSERT_EQ(acc2.Commit(), std::nullopt); // Wait for GC. std::this_thread::sleep_for(std::chrono::milliseconds(300));