diff --git a/CMakeLists.txt b/CMakeLists.txt index 916389424..36e648e8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -182,7 +182,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # c99-designator is disabled because of required mixture of designated and # non-designated initializers in Python Query Module code (`py_module.cpp`). set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall \ - -Werror=switch -Werror=switch-bool -Werror=return-type \ + -Werror=switch -Werror=switch-bool -Werror=implicit-fallthrough \ + -Werror=return-type \ -Werror=return-stack-address \ -Wno-c99-designator \ -DBOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT") diff --git a/src/common/errors.hpp b/src/common/errors.hpp new file mode 100644 index 000000000..1a2e0ce85 --- /dev/null +++ b/src/common/errors.hpp @@ -0,0 +1,68 @@ +// Copyright 2022 Memgraph Ltd. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source +// License, and you may not use this file except in compliance with the Business Source License. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +#pragma once + +#include +#include + +namespace memgraph::common { + +enum class ErrorCode : uint8_t { + SERIALIZATION_ERROR, + NONEXISTENT_OBJECT, + DELETED_OBJECT, + VERTEX_HAS_EDGES, + PROPERTIES_DISABLED, + VERTEX_ALREADY_INSERTED, + // Schema Violations + SCHEMA_NO_SCHEMA_DEFINED_FOR_LABEL, + SCHEMA_VERTEX_PROPERTY_WRONG_TYPE, + SCHEMA_VERTEX_UPDATE_PRIMARY_KEY, + SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL, + SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY, + SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED, + + OBJECT_NOT_FOUND, +}; + +constexpr std::string_view ErrorCodeToString(const ErrorCode code) { + switch (code) { + case ErrorCode::SERIALIZATION_ERROR: + return "SERIALIZATION_ERROR"; + case ErrorCode::NONEXISTENT_OBJECT: + return "NONEXISTENT_OBJECT"; + case ErrorCode::DELETED_OBJECT: + return "DELETED_OBJECT"; + case ErrorCode::VERTEX_HAS_EDGES: + return "VERTEX_HAS_EDGES"; + case ErrorCode::PROPERTIES_DISABLED: + return "PROPERTIES_DISABLED"; + case ErrorCode::VERTEX_ALREADY_INSERTED: + return "VERTEX_ALREADY_INSERTED"; + case ErrorCode::SCHEMA_NO_SCHEMA_DEFINED_FOR_LABEL: + return "SCHEMA_NO_SCHEMA_DEFINED_FOR_LABEL"; + case ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE: + return "SCHEMA_VERTEX_PROPERTY_WRONG_TYPE"; + case ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY: + return "SCHEMA_VERTEX_UPDATE_PRIMARY_KEY"; + case ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL: + return "SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL"; + case ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY: + return "SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY"; + case ErrorCode::SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED: + return "SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED"; + case ErrorCode::OBJECT_NOT_FOUND: + return "OBJECT_NOT_FOUND"; + } +} + +} // namespace memgraph::common diff --git a/src/communication/bolt/v1/states/executing.hpp b/src/communication/bolt/v1/states/executing.hpp index 54504985b..de4b2a00e 100644 --- a/src/communication/bolt/v1/states/executing.hpp +++ b/src/communication/bolt/v1/states/executing.hpp @@ -74,7 +74,7 @@ State RunHandlerV4(Signature signature, TSession &session, State state, Marker m } case Signature::Route: { if constexpr (bolt_minor >= 3) { - if (signature == Signature::Route) return HandleRoute(session); + return HandleRoute(session); } else { spdlog::trace("Supported only in bolt v4.3"); return State::Close; diff --git a/src/expr/interpret/eval.hpp b/src/expr/interpret/eval.hpp index 4ad17437b..8fb300a83 100644 --- a/src/expr/interpret/eval.hpp +++ b/src/expr/interpret/eval.hpp @@ -101,6 +101,28 @@ class ExpressionEvaluator : public ExpressionVisitor { #undef BINARY_OPERATOR_VISITOR #undef UNARY_OPERATOR_VISITOR + void HandleObjectAccessError(Error &shard_error, const std::string_view accessed_object) { + switch (shard_error) { + case Error::DELETED_OBJECT: + throw ExpressionRuntimeException("Trying to access {} on a deleted object.", accessed_object); + case Error::NONEXISTENT_OBJECT: + throw ExpressionRuntimeException("Trying to access {} from a node object doesn't exist.", accessed_object); + case Error::SERIALIZATION_ERROR: + case Error::VERTEX_HAS_EDGES: + case Error::PROPERTIES_DISABLED: + case Error::VERTEX_ALREADY_INSERTED: + case Error::OBJECT_NOT_FOUND: + throw ExpressionRuntimeException("Unexpected error when accessing {}.", accessed_object); + case Error::SCHEMA_NO_SCHEMA_DEFINED_FOR_LABEL: + case Error::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE: + case Error::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY: + case Error::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL: + case Error::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY: + case Error::SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED: + throw ExpressionRuntimeException("Unexpected schema violation when accessing {}.", accessed_object); + } + } + TypedValue Visit(AndOperator &op) override { auto value1 = op.expression1_->Accept(*this); if (value1.IsBool() && !value1.ValueBool()) { @@ -397,17 +419,7 @@ class ExpressionEvaluator : public ExpressionVisitor { has_label = vertex.HasLabel(StorageView::NEW, GetLabel(label)); } if (has_label.HasError()) { - switch (has_label.GetError()) { - case Error::DELETED_OBJECT: - throw ExpressionRuntimeException("Trying to access labels on a deleted node."); - case Error::NONEXISTENT_OBJECT: - throw ExpressionRuntimeException("Trying to access labels from a node that doesn't exist."); - case Error::SERIALIZATION_ERROR: - case Error::VERTEX_HAS_EDGES: - case Error::PROPERTIES_DISABLED: - case Error::VERTEX_ALREADY_INSERTED: - throw ExpressionRuntimeException("Unexpected error when accessing labels."); - } + HandleObjectAccessError(has_label.GetError().code, "labels"); } return *has_label; } @@ -744,17 +756,7 @@ class ExpressionEvaluator : public ExpressionVisitor { maybe_prop = record_accessor.GetProperty(StorageView::NEW, ctx_->properties[prop.ix]); } if (maybe_prop.HasError()) { - switch (maybe_prop.GetError()) { - case Error::DELETED_OBJECT: - throw ExpressionRuntimeException("Trying to get a property from a deleted object."); - case Error::NONEXISTENT_OBJECT: - throw ExpressionRuntimeException("Trying to get a property from an object that doesn't exist."); - case Error::SERIALIZATION_ERROR: - case Error::VERTEX_HAS_EDGES: - case Error::PROPERTIES_DISABLED: - case Error::VERTEX_ALREADY_INSERTED: - throw ExpressionRuntimeException("Unexpected error when getting a property."); - } + HandleObjectAccessError(maybe_prop.GetError().code, "property"); } return conv_(*maybe_prop, ctx_->memory); } @@ -773,17 +775,7 @@ class ExpressionEvaluator : public ExpressionVisitor { maybe_prop = record_accessor.GetProperty(view_, dba_->NameToProperty(name)); } if (maybe_prop.HasError()) { - switch (maybe_prop.GetError()) { - case Error::DELETED_OBJECT: - throw ExpressionRuntimeException("Trying to get a property from a deleted object."); - case Error::NONEXISTENT_OBJECT: - throw ExpressionRuntimeException("Trying to get a property from an object that doesn't exist."); - case Error::SERIALIZATION_ERROR: - case Error::VERTEX_HAS_EDGES: - case Error::PROPERTIES_DISABLED: - case Error::VERTEX_ALREADY_INSERTED: - throw ExpressionRuntimeException("Unexpected error when getting a property."); - } + HandleObjectAccessError(maybe_prop.GetError().code, "property"); } return conv_(*maybe_prop, ctx_->memory); } diff --git a/src/glue/v2/communication.cpp b/src/glue/v2/communication.cpp index ebca8c23f..eedf699bb 100644 --- a/src/glue/v2/communication.cpp +++ b/src/glue/v2/communication.cpp @@ -15,13 +15,13 @@ #include #include +#include "common/errors.hpp" #include "coordinator/shard_map.hpp" #include "query/v2/accessors.hpp" #include "query/v2/requests.hpp" #include "query/v2/shard_request_manager.hpp" #include "storage/v3/edge_accessor.hpp" #include "storage/v3/id_types.hpp" -#include "storage/v3/result.hpp" #include "storage/v3/shard.hpp" #include "storage/v3/vertex_accessor.hpp" #include "storage/v3/view.hpp" @@ -71,9 +71,9 @@ query::v2::TypedValue ToTypedValue(const Value &value) { } } -storage::v3::Result ToBoltVertex( - const query::v2::accessors::VertexAccessor &vertex, const msgs::ShardRequestManagerInterface *shard_request_manager, - storage::v3::View /*view*/) { +communication::bolt::Vertex ToBoltVertex(const query::v2::accessors::VertexAccessor &vertex, + const msgs::ShardRequestManagerInterface *shard_request_manager, + storage::v3::View /*view*/) { auto id = communication::bolt::Id::FromUint(0); auto labels = vertex.Labels(); @@ -91,9 +91,9 @@ storage::v3::Result ToBoltVertex( return communication::bolt::Vertex{id, new_labels, new_properties}; } -storage::v3::Result ToBoltEdge( - const query::v2::accessors::EdgeAccessor &edge, const msgs::ShardRequestManagerInterface *shard_request_manager, - storage::v3::View /*view*/) { +communication::bolt::Edge ToBoltEdge(const query::v2::accessors::EdgeAccessor &edge, + const msgs::ShardRequestManagerInterface *shard_request_manager, + storage::v3::View /*view*/) { // TODO(jbajic) Fix bolt communication auto id = communication::bolt::Id::FromUint(0); auto from = communication::bolt::Id::FromUint(0); @@ -108,69 +108,64 @@ storage::v3::Result ToBoltEdge( return communication::bolt::Edge{id, from, to, type, new_properties}; } -storage::v3::Result ToBoltPath( - const query::v2::accessors::Path & /*edge*/, const msgs::ShardRequestManagerInterface * /*shard_request_manager*/, - storage::v3::View /*view*/) { +communication::bolt::Path ToBoltPath(const query::v2::accessors::Path & /*edge*/, + const msgs::ShardRequestManagerInterface * /*shard_request_manager*/, + storage::v3::View /*view*/) { // TODO(jbajic) Fix bolt communication - return {storage::v3::Error::DELETED_OBJECT}; + MG_ASSERT(false, "Path is unimplemented!"); + return {}; } -storage::v3::Result ToBoltValue(const query::v2::TypedValue &value, - const msgs::ShardRequestManagerInterface *shard_request_manager, - storage::v3::View view) { +Value ToBoltValue(const query::v2::TypedValue &value, const msgs::ShardRequestManagerInterface *shard_request_manager, + storage::v3::View view) { switch (value.type()) { case query::v2::TypedValue::Type::Null: - return Value(); + return {}; case query::v2::TypedValue::Type::Bool: - return Value(value.ValueBool()); + return {value.ValueBool()}; case query::v2::TypedValue::Type::Int: - return Value(value.ValueInt()); + return {value.ValueInt()}; case query::v2::TypedValue::Type::Double: - return Value(value.ValueDouble()); + return {value.ValueDouble()}; case query::v2::TypedValue::Type::String: - return Value(std::string(value.ValueString())); + return {std::string(value.ValueString())}; case query::v2::TypedValue::Type::List: { std::vector values; values.reserve(value.ValueList().size()); for (const auto &v : value.ValueList()) { - auto maybe_value = ToBoltValue(v, shard_request_manager, view); - if (maybe_value.HasError()) return maybe_value.GetError(); - values.emplace_back(std::move(*maybe_value)); + auto value = ToBoltValue(v, shard_request_manager, view); + values.emplace_back(std::move(value)); } - return Value(std::move(values)); + return {std::move(values)}; } case query::v2::TypedValue::Type::Map: { std::map map; for (const auto &kv : value.ValueMap()) { - auto maybe_value = ToBoltValue(kv.second, shard_request_manager, view); - if (maybe_value.HasError()) return maybe_value.GetError(); - map.emplace(kv.first, std::move(*maybe_value)); + auto value = ToBoltValue(kv.second, shard_request_manager, view); + map.emplace(kv.first, std::move(value)); } - return Value(std::move(map)); + return {std::move(map)}; } case query::v2::TypedValue::Type::Vertex: { - auto maybe_vertex = ToBoltVertex(value.ValueVertex(), shard_request_manager, view); - if (maybe_vertex.HasError()) return maybe_vertex.GetError(); - return Value(std::move(*maybe_vertex)); + auto vertex = ToBoltVertex(value.ValueVertex(), shard_request_manager, view); + return {std::move(vertex)}; } case query::v2::TypedValue::Type::Edge: { - auto maybe_edge = ToBoltEdge(value.ValueEdge(), shard_request_manager, view); - if (maybe_edge.HasError()) return maybe_edge.GetError(); - return Value(std::move(*maybe_edge)); + auto edge = ToBoltEdge(value.ValueEdge(), shard_request_manager, view); + return {std::move(edge)}; } case query::v2::TypedValue::Type::Path: { - auto maybe_path = ToBoltPath(value.ValuePath(), shard_request_manager, view); - if (maybe_path.HasError()) return maybe_path.GetError(); - return Value(std::move(*maybe_path)); + auto path = ToBoltPath(value.ValuePath(), shard_request_manager, view); + return {std::move(path)}; } case query::v2::TypedValue::Type::Date: - return Value(value.ValueDate()); + return {value.ValueDate()}; case query::v2::TypedValue::Type::LocalTime: - return Value(value.ValueLocalTime()); + return {value.ValueLocalTime()}; case query::v2::TypedValue::Type::LocalDateTime: - return Value(value.ValueLocalDateTime()); + return {value.ValueLocalDateTime()}; case query::v2::TypedValue::Type::Duration: - return Value(value.ValueDuration()); + return {value.ValueDuration()}; } } diff --git a/src/glue/v2/communication.hpp b/src/glue/v2/communication.hpp index ea9c6b4c9..a20162176 100644 --- a/src/glue/v2/communication.hpp +++ b/src/glue/v2/communication.hpp @@ -20,6 +20,7 @@ #include "storage/v3/result.hpp" #include "storage/v3/shard.hpp" #include "storage/v3/view.hpp" +#include "utils/result.hpp" namespace memgraph::storage::v3 { class EdgeAccessor; @@ -35,36 +36,36 @@ namespace memgraph::glue::v2 { /// @param storage::v3::View for deciding which vertex attributes are visible. /// /// @throw std::bad_alloc -storage::v3::Result ToBoltVertex( - const storage::v3::VertexAccessor &vertex, const msgs::ShardRequestManagerInterface *shard_request_manager, - storage::v3::View view); +communication::bolt::Vertex ToBoltVertex(const storage::v3::VertexAccessor &vertex, + const msgs::ShardRequestManagerInterface *shard_request_manager, + storage::v3::View view); /// @param storage::v3::EdgeAccessor for converting to communication::bolt::Edge. /// @param msgs::ShardRequestManagerInterface *shard_request_manager getting edge type and property names. /// @param storage::v3::View for deciding which edge attributes are visible. /// /// @throw std::bad_alloc -storage::v3::Result ToBoltEdge( - const storage::v3::EdgeAccessor &edge, const msgs::ShardRequestManagerInterface *shard_request_manager, - storage::v3::View view); +communication::bolt::Edge ToBoltEdge(const storage::v3::EdgeAccessor &edge, + const msgs::ShardRequestManagerInterface *shard_request_manager, + storage::v3::View view); /// @param query::v2::Path for converting to communication::bolt::Path. /// @param msgs::ShardRequestManagerInterface *shard_request_manager ToBoltVertex and ToBoltEdge. /// @param storage::v3::View for ToBoltVertex and ToBoltEdge. /// /// @throw std::bad_alloc -storage::v3::Result ToBoltPath( - const query::v2::accessors::Path &path, const msgs::ShardRequestManagerInterface *shard_request_manager, - storage::v3::View view); +communication::bolt::Path ToBoltPath(const query::v2::accessors::Path &path, + const msgs::ShardRequestManagerInterface *shard_request_manager, + storage::v3::View view); /// @param query::v2::TypedValue for converting to communication::bolt::Value. /// @param msgs::ShardRequestManagerInterface *shard_request_manager ToBoltVertex and ToBoltEdge. /// @param storage::v3::View for ToBoltVertex and ToBoltEdge. /// /// @throw std::bad_alloc -storage::v3::Result ToBoltValue( - const query::v2::TypedValue &value, const msgs::ShardRequestManagerInterface *shard_request_manager, - storage::v3::View view); +communication::bolt::Value ToBoltValue(const query::v2::TypedValue &value, + const msgs::ShardRequestManagerInterface *shard_request_manager, + storage::v3::View view); query::v2::TypedValue ToTypedValue(const communication::bolt::Value &value); diff --git a/src/memgraph.cpp b/src/memgraph.cpp index deaf9e568..5903e65ad 100644 --- a/src/memgraph.cpp +++ b/src/memgraph.cpp @@ -33,6 +33,7 @@ #include #include +#include "common/errors.hpp" #include "communication/bolt/v1/constants.hpp" #include "communication/websocket/auth.hpp" #include "communication/websocket/server.hpp" @@ -480,20 +481,9 @@ class BoltSession final : public memgraph::communication::bolt::Session decoded_summary; for (const auto &kv : summary) { - auto maybe_value = memgraph::glue::v2::ToBoltValue(kv.second, interpreter_.GetShardRequestManager(), - memgraph::storage::v3::View::NEW); - if (maybe_value.HasError()) { - switch (maybe_value.GetError()) { - case memgraph::storage::v3::Error::DELETED_OBJECT: - case memgraph::storage::v3::Error::SERIALIZATION_ERROR: - case memgraph::storage::v3::Error::VERTEX_HAS_EDGES: - case memgraph::storage::v3::Error::PROPERTIES_DISABLED: - case memgraph::storage::v3::Error::NONEXISTENT_OBJECT: - case memgraph::storage::v3::Error::VERTEX_ALREADY_INSERTED: - throw memgraph::communication::bolt::ClientError("Unexpected storage error when streaming summary."); - } - } - decoded_summary.emplace(kv.first, std::move(*maybe_value)); + auto bolt_value = memgraph::glue::v2::ToBoltValue(kv.second, interpreter_.GetShardRequestManager(), + memgraph::storage::v3::View::NEW); + decoded_summary.emplace(kv.first, std::move(bolt_value)); } return decoded_summary; } catch (const memgraph::query::v2::QueryException &e) { @@ -514,21 +504,8 @@ class BoltSession final : public memgraph::communication::bolt::Session decoded_values; decoded_values.reserve(values.size()); for (const auto &v : values) { - auto maybe_value = memgraph::glue::v2::ToBoltValue(v, shard_request_manager_, memgraph::storage::v3::View::NEW); - if (maybe_value.HasError()) { - switch (maybe_value.GetError()) { - case memgraph::storage::v3::Error::DELETED_OBJECT: - throw memgraph::communication::bolt::ClientError("Returning a deleted object as a result."); - case memgraph::storage::v3::Error::NONEXISTENT_OBJECT: - throw memgraph::communication::bolt::ClientError("Returning a nonexistent object as a result."); - case memgraph::storage::v3::Error::VERTEX_HAS_EDGES: - case memgraph::storage::v3::Error::SERIALIZATION_ERROR: - case memgraph::storage::v3::Error::PROPERTIES_DISABLED: - case memgraph::storage::v3::Error::VERTEX_ALREADY_INSERTED: - throw memgraph::communication::bolt::ClientError("Unexpected storage error when streaming results."); - } - } - decoded_values.emplace_back(std::move(*maybe_value)); + auto bolt_value = memgraph::glue::v2::ToBoltValue(v, shard_request_manager_, memgraph::storage::v3::View::NEW); + decoded_values.emplace_back(std::move(bolt_value)); } encoder_->MessageRecord(decoded_values); } diff --git a/src/query/v2/bindings/eval.hpp b/src/query/v2/bindings/eval.hpp index 380eb879a..2ea623ec0 100644 --- a/src/query/v2/bindings/eval.hpp +++ b/src/query/v2/bindings/eval.hpp @@ -21,6 +21,7 @@ #include "query/v2/requests.hpp" #include "storage/v3/conversions.hpp" #include "storage/v3/property_value.hpp" +#include "storage/v3/result.hpp" #include "storage/v3/view.hpp" namespace memgraph::msgs { @@ -43,6 +44,6 @@ class Callable { } // namespace detail using ExpressionEvaluator = memgraph::expr::ExpressionEvaluator< TypedValue, memgraph::query::v2::EvaluationContext, memgraph::msgs::ShardRequestManagerInterface, storage::v3::View, - storage::v3::LabelId, msgs::Value, detail::Callable, memgraph::storage::v3::Error, memgraph::expr::QueryEngineTag>; + storage::v3::LabelId, msgs::Value, detail::Callable, common::ErrorCode, memgraph::expr::QueryEngineTag>; } // namespace memgraph::query::v2 diff --git a/src/query/v2/common.hpp b/src/query/v2/common.hpp index 55c60ce10..0ee33c650 100644 --- a/src/query/v2/common.hpp +++ b/src/query/v2/common.hpp @@ -20,7 +20,6 @@ #include "query/v2/bindings/symbol.hpp" #include "query/v2/bindings/typed_value.hpp" -#include "query/v2/db_accessor.hpp" #include "query/v2/exceptions.hpp" #include "query/v2/frontend/ast/ast.hpp" #include "query/v2/path.hpp" @@ -28,7 +27,6 @@ #include "storage/v3/id_types.hpp" #include "storage/v3/property_value.hpp" #include "storage/v3/result.hpp" -#include "storage/v3/shard_operation_result.hpp" #include "storage/v3/view.hpp" #include "utils/exceptions.hpp" #include "utils/logging.hpp" @@ -82,39 +80,5 @@ inline void ExpectType(const Symbol &symbol, const TypedValue &value, TypedValue throw QueryRuntimeException("Expected a {} for '{}', but got {}.", expected, symbol.name(), value.type()); } -template -concept AccessorWithSetProperty = requires(T accessor, const storage::v3::PropertyId key, - const storage::v3::PropertyValue new_value) { - { accessor.SetProperty(key, new_value) } -> std::same_as>; -}; - -template -concept AccessorWithSetPropertyAndValidate = requires(T accessor, const storage::v3::PropertyId key, - const storage::v3::PropertyValue new_value) { - { - accessor.SetPropertyAndValidate(key, new_value) - } -> std::same_as>; -}; - -template -concept RecordAccessor = - AccessorWithSetProperty || AccessorWithSetPropertyAndValidate; - -inline void HandleErrorOnPropertyUpdate(const storage::v3::Error error) { - switch (error) { - case storage::v3::Error::SERIALIZATION_ERROR: - throw TransactionSerializationException(); - case storage::v3::Error::DELETED_OBJECT: - throw QueryRuntimeException("Trying to set properties on a deleted object."); - case storage::v3::Error::PROPERTIES_DISABLED: - throw QueryRuntimeException("Can't set property because properties on edges are disabled."); - case storage::v3::Error::VERTEX_HAS_EDGES: - case storage::v3::Error::NONEXISTENT_OBJECT: - case storage::v3::Error::VERTEX_ALREADY_INSERTED: - - throw QueryRuntimeException("Unexpected error when setting a property."); - } -} - int64_t QueryTimestamp(); } // namespace memgraph::query::v2 diff --git a/src/query/v2/db_accessor.hpp b/src/query/v2/db_accessor.hpp index 29bbbb7fd..0419a2bc8 100644 --- a/src/query/v2/db_accessor.hpp +++ b/src/query/v2/db_accessor.hpp @@ -23,7 +23,6 @@ #include "storage/v3/key_store.hpp" #include "storage/v3/property_value.hpp" #include "storage/v3/result.hpp" -#include "storage/v3/shard_operation_result.hpp" /////////////////////////////////////////////////////////// // Our communication layer and query engine don't mix @@ -65,24 +64,11 @@ class EdgeAccessor final { auto Properties(storage::v3::View view) const { return impl_.Properties(view); } - storage::v3::Result GetProperty(storage::v3::View view, - storage::v3::PropertyId key) const { + storage::v3::ShardResult GetProperty(storage::v3::View view, + storage::v3::PropertyId key) const { return impl_.GetProperty(key, view); } - storage::v3::Result SetProperty(storage::v3::PropertyId key, - const storage::v3::PropertyValue &value) { - return impl_.SetProperty(key, value); - } - - storage::v3::Result RemoveProperty(storage::v3::PropertyId key) { - return SetProperty(key, storage::v3::PropertyValue()); - } - - storage::v3::Result> ClearProperties() { - return impl_.ClearProperties(); - } - VertexAccessor To() const; VertexAccessor From() const; @@ -114,53 +100,19 @@ class VertexAccessor final { auto PrimaryKey(storage::v3::View view) const { return impl_.PrimaryKey(view); } - storage::v3::ShardOperationResult AddLabel(storage::v3::LabelId label) { - return impl_.AddLabelAndValidate(label); - } - - storage::v3::ShardOperationResult AddLabelAndValidate(storage::v3::LabelId label) { - return impl_.AddLabelAndValidate(label); - } - - storage::v3::ShardOperationResult RemoveLabel(storage::v3::LabelId label) { - return impl_.RemoveLabelAndValidate(label); - } - - storage::v3::ShardOperationResult RemoveLabelAndValidate(storage::v3::LabelId label) { - return impl_.RemoveLabelAndValidate(label); - } - - storage::v3::Result HasLabel(storage::v3::View view, storage::v3::LabelId label) const { + storage::v3::ShardResult HasLabel(storage::v3::View view, storage::v3::LabelId label) const { return impl_.HasLabel(label, view); } auto Properties(storage::v3::View view) const { return impl_.Properties(view); } - storage::v3::Result GetProperty(storage::v3::View view, - storage::v3::PropertyId key) const { + storage::v3::ShardResult GetProperty(storage::v3::View view, + storage::v3::PropertyId key) const { return impl_.GetProperty(key, view); } - storage::v3::ShardOperationResult SetProperty(storage::v3::PropertyId key, - const storage::v3::PropertyValue &value) { - return impl_.SetPropertyAndValidate(key, value); - } - - storage::v3::ShardOperationResult SetPropertyAndValidate( - storage::v3::PropertyId key, const storage::v3::PropertyValue &value) { - return impl_.SetPropertyAndValidate(key, value); - } - - storage::v3::ShardOperationResult RemovePropertyAndValidate(storage::v3::PropertyId key) { - return SetPropertyAndValidate(key, storage::v3::PropertyValue{}); - } - - storage::v3::Result> ClearProperties() { - return impl_.ClearProperties(); - } - auto InEdges(storage::v3::View view, const std::vector &edge_types) const - -> storage::v3::Result { + -> storage::v3::ShardResult { auto maybe_edges = impl_.InEdges(view, edge_types); if (maybe_edges.HasError()) return maybe_edges.GetError(); return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges)); @@ -170,7 +122,7 @@ class VertexAccessor final { auto InEdges(storage::v3::View view, const std::vector &edge_types, const VertexAccessor &dest) const - -> storage::v3::Result { + -> storage::v3::ShardResult { const auto dest_id = dest.impl_.Id(view).GetValue(); auto maybe_edges = impl_.InEdges(view, edge_types, &dest_id); if (maybe_edges.HasError()) return maybe_edges.GetError(); @@ -178,7 +130,7 @@ class VertexAccessor final { } auto OutEdges(storage::v3::View view, const std::vector &edge_types) const - -> storage::v3::Result { + -> storage::v3::ShardResult { auto maybe_edges = impl_.OutEdges(view, edge_types); if (maybe_edges.HasError()) return maybe_edges.GetError(); return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges)); @@ -188,16 +140,16 @@ class VertexAccessor final { auto OutEdges(storage::v3::View view, const std::vector &edge_types, const VertexAccessor &dest) const - -> storage::v3::Result { + -> storage::v3::ShardResult { const auto dest_id = dest.impl_.Id(view).GetValue(); auto maybe_edges = impl_.OutEdges(view, edge_types, &dest_id); if (maybe_edges.HasError()) return maybe_edges.GetError(); return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges)); } - storage::v3::Result InDegree(storage::v3::View view) const { return impl_.InDegree(view); } + storage::v3::ShardResult InDegree(storage::v3::View view) const { return impl_.InDegree(view); } - storage::v3::Result OutDegree(storage::v3::View view) const { return impl_.OutDegree(view); } + storage::v3::ShardResult OutDegree(storage::v3::View view) const { return impl_.OutDegree(view); } // TODO(jbajic) Fix Remove Gid static int64_t CypherId() { return 1; } diff --git a/src/query/v2/exceptions.hpp b/src/query/v2/exceptions.hpp index 959672eae..e0802a6cc 100644 --- a/src/query/v2/exceptions.hpp +++ b/src/query/v2/exceptions.hpp @@ -224,12 +224,4 @@ class VersionInfoInMulticommandTxException : public QueryException { : QueryException("Version info query not allowed in multicommand transactions.") {} }; -/** - * An exception for an illegal operation that violates schema - */ -class SchemaViolationException : public QueryRuntimeException { - public: - using QueryRuntimeException::QueryRuntimeException; -}; - } // namespace memgraph::query::v2 diff --git a/src/query/v2/plan/operator.cpp b/src/query/v2/plan/operator.cpp index 068ca9192..d82d83034 100644 --- a/src/query/v2/plan/operator.cpp +++ b/src/query/v2/plan/operator.cpp @@ -26,6 +26,7 @@ #include #include +#include "common/errors.hpp" #include "expr/ast/pretty_print_ast_to_original_expression.hpp" #include "expr/exceptions.hpp" #include "query/exceptions.hpp" @@ -563,27 +564,6 @@ UniqueCursorPtr ScanAllById::MakeCursor(utils::MemoryResource *mem) const { std::move(vertices), "ScanAllById"); } -namespace { - -template -auto UnwrapEdgesResult(storage::v3::Result &&result) { - if (result.HasError()) { - switch (result.GetError()) { - case storage::v3::Error::DELETED_OBJECT: - throw QueryRuntimeException("Trying to get relationships of a deleted node."); - case storage::v3::Error::NONEXISTENT_OBJECT: - throw query::v2::QueryRuntimeException("Trying to get relationships from a node that doesn't exist."); - case storage::v3::Error::VERTEX_HAS_EDGES: - case storage::v3::Error::SERIALIZATION_ERROR: - case storage::v3::Error::PROPERTIES_DISABLED: - throw QueryRuntimeException("Unexpected error when accessing relationships."); - } - } - return std::move(*result); -} - -} // namespace - Expand::Expand(const std::shared_ptr &input, Symbol input_symbol, Symbol node_symbol, Symbol edge_symbol, EdgeAtom::Direction direction, const std::vector &edge_types, bool existing_node, storage::v3::View view) @@ -836,45 +816,9 @@ std::vector SetProperties::ModifiedSymbols(const SymbolTable &table) con SetProperties::SetPropertiesCursor::SetPropertiesCursor(const SetProperties &self, utils::MemoryResource *mem) : self_(self), input_cursor_(self.input_->MakeCursor(mem)) {} -namespace { - -template -concept AccessorWithProperties = requires(T value, storage::v3::PropertyId property_id, - storage::v3::PropertyValue property_value) { - { - value.ClearProperties() - } -> std::same_as>>; - {value.SetProperty(property_id, property_value)}; -}; - -} // namespace - bool SetProperties::SetPropertiesCursor::Pull(Frame &frame, ExecutionContext &context) { SCOPED_PROFILE_OP("SetProperties"); return false; - // if (!input_cursor_->Pull(frame, context)) return false; - // - // TypedValue &lhs = frame[self_.input_symbol_]; - // - // // Set, just like Create needs to see the latest changes. - // ExpressionEvaluator evaluator(&frame, context.symbol_table, context.evaluation_context, context.db_accessor, - // storage::v3::View::NEW); - // TypedValue rhs = self_.rhs_->Accept(evaluator); - // - // switch (lhs.type()) { - // case TypedValue::Type::Vertex: - // SetPropertiesOnRecord(&lhs.ValueVertex(), rhs, self_.op_, &context); - // break; - // case TypedValue::Type::Edge: - // SetPropertiesOnRecord(&lhs.ValueEdge(), rhs, self_.op_, &context); - // break; - // case TypedValue::Type::Null: - // // Skip setting properties on Null (can occur in optional match). - // break; - // default: - // throw QueryRuntimeException("Properties can only be set on edges and vertices."); - // } - // return true; } void SetProperties::SetPropertiesCursor::Shutdown() { input_cursor_->Shutdown(); } diff --git a/src/query/v2/requests.hpp b/src/query/v2/requests.hpp index 2ea4efb57..491d6cf72 100644 --- a/src/query/v2/requests.hpp +++ b/src/query/v2/requests.hpp @@ -24,6 +24,7 @@ #include "coordinator/hybrid_logical_clock.hpp" #include "storage/v3/id_types.hpp" #include "storage/v3/property_value.hpp" +#include "storage/v3/result.hpp" namespace memgraph::msgs { @@ -317,6 +318,11 @@ struct Value { } }; +struct ShardError { + common::ErrorCode code; + std::string message; +}; + struct Expression { std::string expression; }; @@ -361,7 +367,7 @@ struct ScanResultRow { }; struct ScanVerticesResponse { - bool success; + std::optional error; std::optional next_start_id; std::vector results; }; @@ -381,7 +387,7 @@ struct GetPropertiesRequest { }; struct GetPropertiesResponse { - bool success; + std::optional error; }; enum class EdgeDirection : uint8_t { OUT = 1, IN = 2, BOTH = 3 }; @@ -403,7 +409,9 @@ struct ExpandOneRequest { std::vector vertex_expressions; std::vector edge_expressions; - std::optional> order_by; + std::vector order_by_vertices; + std::vector order_by_edges; + // Limit the edges or the vertices? std::optional limit; std::vector filters; @@ -446,14 +454,16 @@ struct ExpandOneResultRow { }; struct ExpandOneResponse { - bool success; + std::optional error; std::vector result; }; -struct UpdateVertexProp { +struct UpdateVertex { PrimaryKey primary_key; - // This should be a map - std::vector> property_updates; + // Labels are first added and then removed from vertices + std::vector add_labels; + std::vector remove_labels; + std::map property_updates; }; struct UpdateEdgeProp { @@ -480,7 +490,7 @@ struct CreateVerticesRequest { }; struct CreateVerticesResponse { - bool success; + std::optional error; }; struct DeleteVerticesRequest { @@ -491,16 +501,16 @@ struct DeleteVerticesRequest { }; struct DeleteVerticesResponse { - bool success; + std::optional error; }; struct UpdateVerticesRequest { Hlc transaction_id; - std::vector new_properties; + std::vector update_vertices; }; struct UpdateVerticesResponse { - bool success; + std::optional error; }; /* @@ -522,7 +532,7 @@ struct CreateExpandRequest { }; struct CreateExpandResponse { - bool success; + std::optional error; }; struct DeleteEdgesRequest { @@ -531,7 +541,7 @@ struct DeleteEdgesRequest { }; struct DeleteEdgesResponse { - bool success; + std::optional error; }; struct UpdateEdgesRequest { @@ -540,7 +550,7 @@ struct UpdateEdgesRequest { }; struct UpdateEdgesResponse { - bool success; + std::optional error; }; struct CommitRequest { @@ -549,7 +559,7 @@ struct CommitRequest { }; struct CommitResponse { - bool success; + std::optional error; }; using ReadRequests = std::variant; diff --git a/src/query/v2/shard_request_manager.hpp b/src/query/v2/shard_request_manager.hpp index ef2d3dc43..9c9c1d89e 100644 --- a/src/query/v2/shard_request_manager.hpp +++ b/src/query/v2/shard_request_manager.hpp @@ -209,7 +209,7 @@ class ShardRequestManager : public ShardRequestManagerInterface { } WriteResponses write_response_variant = commit_response.GetValue(); auto &response = std::get(write_response_variant); - if (!response.success) { + if (response.error) { throw std::runtime_error("Commit request did not succeed"); } } @@ -314,7 +314,7 @@ class ShardRequestManager : public ShardRequestManagerInterface { WriteResponses response_variant = write_response_result.GetValue(); CreateExpandResponse mapped_response = std::get(response_variant); - if (!mapped_response.success) { + if (mapped_response.error) { throw std::runtime_error("CreateExpand request did not succeed"); } responses.push_back(mapped_response); @@ -616,7 +616,7 @@ class ShardRequestManager : public ShardRequestManagerInterface { WriteResponses response_variant = poll_result->GetValue(); auto response = std::get(response_variant); - if (!response.success) { + if (response.error) { throw std::runtime_error("CreateVertices request did not succeed"); } responses.push_back(response); @@ -652,7 +652,7 @@ class ShardRequestManager : public ShardRequestManagerInterface { // Currently a boolean flag for signaling the overall success of the // ExpandOne request does not exist. But it should, so here we assume // that it is already in place. - if (!response.success) { + if (response.error) { throw std::runtime_error("ExpandOne request did not succeed"); } @@ -695,7 +695,7 @@ class ShardRequestManager : public ShardRequestManagerInterface { ReadResponses read_response_variant = await_result->GetValue(); auto response = std::get(read_response_variant); - if (!response.success) { + if (response.error) { throw std::runtime_error("ScanAll request did not succeed"); } diff --git a/src/storage/v2/constraints.cpp b/src/storage/v2/constraints.cpp index fab6ee4c4..2a71ee2b0 100644 --- a/src/storage/v2/constraints.cpp +++ b/src/storage/v2/constraints.cpp @@ -97,15 +97,15 @@ bool LastCommittedVersionHasLabelProperty(const Vertex &vertex, LabelId label, c if (delta->label == label) { MG_ASSERT(!has_label, "Invalid database state!"); has_label = true; - break; } + break; } case Delta::Action::REMOVE_LABEL: { if (delta->label == label) { MG_ASSERT(has_label, "Invalid database state!"); has_label = false; - break; } + break; } case Delta::Action::ADD_IN_EDGE: case Delta::Action::ADD_OUT_EDGE: diff --git a/src/storage/v3/bindings/db_accessor.hpp b/src/storage/v3/bindings/db_accessor.hpp index 2a4f91fe0..1a5e846fa 100644 --- a/src/storage/v3/bindings/db_accessor.hpp +++ b/src/storage/v3/bindings/db_accessor.hpp @@ -78,8 +78,8 @@ class DbAccessor final { return VerticesIterable(accessor_->Vertices(label, property, lower, upper, view)); } - storage::v3::Result InsertEdge(VertexAccessor *from, VertexAccessor *to, - const storage::v3::EdgeTypeId &edge_type) { + storage::v3::ShardResult InsertEdge(VertexAccessor *from, VertexAccessor *to, + const storage::v3::EdgeTypeId &edge_type) { static constexpr auto kDummyGid = storage::v3::Gid::FromUint(0); auto maybe_edge = accessor_->CreateEdge(from->Id(storage::v3::View::NEW).GetValue(), to->Id(storage::v3::View::NEW).GetValue(), edge_type, kDummyGid); @@ -87,8 +87,8 @@ class DbAccessor final { return EdgeAccessor(*maybe_edge); } - storage::v3::Result> RemoveEdge(EdgeAccessor *edge) { - auto res = accessor_->DeleteEdge(edge->From(), edge->To(), edge->Gid()); + storage::v3::ShardResult> RemoveEdge(EdgeAccessor *edge) { + auto res = accessor_->DeleteEdge(edge->FromVertex(), edge->ToVertex(), edge->Gid()); if (res.HasError()) { return res.GetError(); } @@ -101,7 +101,7 @@ class DbAccessor final { return std::make_optional(*value); } - storage::v3::Result>>> DetachRemoveVertex( + storage::v3::ShardResult>>> DetachRemoveVertex( VertexAccessor *vertex_accessor) { using ReturnType = std::pair>; @@ -125,7 +125,7 @@ class DbAccessor final { return std::make_optional(vertex, std::move(deleted_edges)); } - storage::v3::Result> RemoveVertex(VertexAccessor *vertex_accessor) { + storage::v3::ShardResult> RemoveVertex(VertexAccessor *vertex_accessor) { auto res = accessor_->DeleteVertex(vertex_accessor); if (res.HasError()) { return res.GetError(); diff --git a/src/storage/v3/bindings/eval.hpp b/src/storage/v3/bindings/eval.hpp index 7f93bb79c..c3024e006 100644 --- a/src/storage/v3/bindings/eval.hpp +++ b/src/storage/v3/bindings/eval.hpp @@ -21,6 +21,7 @@ #include "storage/v3/id_types.hpp" #include "storage/v3/property_store.hpp" #include "storage/v3/property_value.hpp" +#include "storage/v3/result.hpp" #include "storage/v3/view.hpp" #include "utils/memory.hpp" @@ -87,6 +88,6 @@ struct EvaluationContext { using ExpressionEvaluator = memgraph::expr::ExpressionEvaluator; + common::ErrorCode>; } // namespace memgraph::storage::v3 diff --git a/src/storage/v3/edge_accessor.cpp b/src/storage/v3/edge_accessor.cpp index 312a9c2d7..e591de7eb 100644 --- a/src/storage/v3/edge_accessor.cpp +++ b/src/storage/v3/edge_accessor.cpp @@ -15,6 +15,7 @@ #include "storage/v3/mvcc.hpp" #include "storage/v3/property_value.hpp" +#include "storage/v3/result.hpp" #include "storage/v3/schema_validator.hpp" #include "storage/v3/vertex_accessor.hpp" #include "utils/memory_tracker.hpp" @@ -54,13 +55,13 @@ const VertexId &EdgeAccessor::From() const { return from_vertex_; } const VertexId &EdgeAccessor::To() const { return to_vertex_; } -Result EdgeAccessor::SetProperty(PropertyId property, const PropertyValue &value) { +ShardResult EdgeAccessor::SetProperty(PropertyId property, const PropertyValue &value) { utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; - if (!config_.properties_on_edges) return Error::PROPERTIES_DISABLED; + if (!config_.properties_on_edges) return SHARD_ERROR(ErrorCode::PROPERTIES_DISABLED); - if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR; + if (!PrepareForWrite(transaction_, edge_.ptr)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); - if (edge_.ptr->deleted) return Error::DELETED_OBJECT; + if (edge_.ptr->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); auto current_value = edge_.ptr->properties.GetProperty(property); // We could skip setting the value if the previous one is the same to the new @@ -75,12 +76,12 @@ Result EdgeAccessor::SetProperty(PropertyId property, const Prope return std::move(current_value); } -Result> EdgeAccessor::ClearProperties() { - if (!config_.properties_on_edges) return Error::PROPERTIES_DISABLED; +ShardResult> EdgeAccessor::ClearProperties() { + if (!config_.properties_on_edges) return SHARD_ERROR(ErrorCode::PROPERTIES_DISABLED); - if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR; + if (!PrepareForWrite(transaction_, edge_.ptr)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); - if (edge_.ptr->deleted) return Error::DELETED_OBJECT; + if (edge_.ptr->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); auto properties = edge_.ptr->properties.Properties(); for (const auto &property : properties) { @@ -92,11 +93,11 @@ Result> EdgeAccessor::ClearProperties() { return std::move(properties); } -Result EdgeAccessor::GetProperty(View view, PropertyId property) const { +ShardResult EdgeAccessor::GetProperty(View view, PropertyId property) const { return GetProperty(property, view); } -Result EdgeAccessor::GetProperty(PropertyId property, View view) const { +ShardResult EdgeAccessor::GetProperty(PropertyId property, View view) const { if (!config_.properties_on_edges) return PropertyValue(); auto exists = true; auto deleted = edge_.ptr->deleted; @@ -128,12 +129,12 @@ Result EdgeAccessor::GetProperty(PropertyId property, View view) break; } }); - if (!exists) return Error::NONEXISTENT_OBJECT; - if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; + if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT); + if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); return std::move(value); } -Result> EdgeAccessor::Properties(View view) const { +ShardResult> EdgeAccessor::Properties(View view) const { if (!config_.properties_on_edges) return std::map{}; auto exists = true; auto deleted = edge_.ptr->deleted; @@ -174,8 +175,8 @@ Result> EdgeAccessor::Properties(View view) break; } }); - if (!exists) return Error::NONEXISTENT_OBJECT; - if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; + if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT); + if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); return std::move(properties); } diff --git a/src/storage/v3/edge_accessor.hpp b/src/storage/v3/edge_accessor.hpp index 0c9235b14..bc143c1e3 100644 --- a/src/storage/v3/edge_accessor.hpp +++ b/src/storage/v3/edge_accessor.hpp @@ -56,19 +56,19 @@ class EdgeAccessor final { /// Set a property value and return the old value. /// @throw std::bad_alloc - Result SetProperty(PropertyId property, const PropertyValue &value); + ShardResult SetProperty(PropertyId property, const PropertyValue &value); /// Remove all properties and return old values for each removed property. /// @throw std::bad_alloc - Result> ClearProperties(); + ShardResult> ClearProperties(); /// @throw std::bad_alloc - Result GetProperty(PropertyId property, View view) const; + ShardResult GetProperty(PropertyId property, View view) const; - Result GetProperty(View view, PropertyId property) const; + ShardResult GetProperty(View view, PropertyId property) const; /// @throw std::bad_alloc - Result> Properties(View view) const; + ShardResult> Properties(View view) const; Gid Gid() const noexcept { if (config_.properties_on_edges) { diff --git a/src/storage/v3/expr.cpp b/src/storage/v3/expr.cpp index 074e9f15f..36c0ff8e3 100644 --- a/src/storage/v3/expr.cpp +++ b/src/storage/v3/expr.cpp @@ -164,7 +164,7 @@ std::any ParseExpression(const std::string &expr, memgraph::expr::AstStorage &st return visitor.visit(ast); } -TypedValue ComputeExpression(DbAccessor &dba, const std::optional &v_acc, +TypedValue ComputeExpression(DbAccessor &dba, const memgraph::storage::v3::VertexAccessor &v_acc, const std::optional &e_acc, const std::string &expression, std::string_view node_name, std::string_view edge_name) { AstStorage storage; @@ -191,10 +191,11 @@ TypedValue ComputeExpression(DbAccessor &dba, const std::optional &position_symbol_pair) { return position_symbol_pair.second.name() == edge_name; diff --git a/src/storage/v3/expr.hpp b/src/storage/v3/expr.hpp index c3199abf1..d344d36b2 100644 --- a/src/storage/v3/expr.hpp +++ b/src/storage/v3/expr.hpp @@ -9,6 +9,8 @@ // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. +#pragma once + #include #include "db_accessor.hpp" @@ -48,8 +50,7 @@ auto Eval(TExpression *expr, EvaluationContext &ctx, AstStorage &storage, Expres std::any ParseExpression(const std::string &expr, AstStorage &storage); -TypedValue ComputeExpression(DbAccessor &dba, const std::optional &v_acc, - const std::optional &e_acc, const std::string &expression, - std::string_view node_name, std::string_view edge_name); +TypedValue ComputeExpression(DbAccessor &dba, const VertexAccessor &v_acc, const std::optional &e_acc, + const std::string &expression, std::string_view node_name, std::string_view edge_name); } // namespace memgraph::storage::v3 diff --git a/src/storage/v3/request_helper.cpp b/src/storage/v3/request_helper.cpp index bb1c8bca4..481741f60 100644 --- a/src/storage/v3/request_helper.cpp +++ b/src/storage/v3/request_helper.cpp @@ -13,54 +13,436 @@ #include -#include "pretty_print_ast_to_original_expression.hpp" #include "storage/v3/bindings/db_accessor.hpp" +#include "storage/v3/bindings/pretty_print_ast_to_original_expression.hpp" #include "storage/v3/expr.hpp" +#include "storage/v3/result.hpp" +#include "storage/v3/value_conversions.hpp" namespace memgraph::storage::v3 { +using msgs::Label; +using msgs::PropertyId; -std::vector OrderByElements(Shard::Accessor &acc, DbAccessor &dba, VerticesIterable &vertices_iterable, - std::vector &order_bys) { - std::vector ordered; - ordered.reserve(acc.ApproximateVertexCount()); - std::vector ordering; - ordering.reserve(order_bys.size()); - for (const auto &order : order_bys) { - switch (order.direction) { - case memgraph::msgs::OrderingDirection::ASCENDING: { - ordering.push_back(Ordering::ASC); - break; - } - case memgraph::msgs::OrderingDirection::DESCENDING: { - ordering.push_back(Ordering::DESC); - break; - } - } +using conversions::ConvertPropertyVector; +using conversions::FromPropertyValueToValue; +using conversions::ToMsgsVertexId; + +namespace { + +using AllEdgePropertyDataStucture = std::map; +using SpecificEdgePropertyDataStucture = std::vector; + +using AllEdgeProperties = std::tuple; +using SpecificEdgeProperties = std::tuple; + +using SpecificEdgePropertiesVector = std::vector; +using AllEdgePropertiesVector = std::vector; + +struct VertexIdCmpr { + bool operator()(const storage::v3::VertexId *lhs, const storage::v3::VertexId *rhs) const { return *lhs < *rhs; } +}; + +std::optional> PrimaryKeysFromAccessor(const VertexAccessor &acc, View view, + const Schemas::Schema &schema) { + std::map ret; + auto props = acc.Properties(view); + auto maybe_pk = acc.PrimaryKey(view); + if (maybe_pk.HasError()) { + spdlog::debug("Encountered an error while trying to get vertex primary key."); + return std::nullopt; } - auto compare_typed_values = TypedValueVectorCompare(ordering); - for (auto it = vertices_iterable.begin(); it != vertices_iterable.end(); ++it) { - std::vector properties_order_by; - properties_order_by.reserve(order_bys.size()); - - for (const auto &order_by : order_bys) { - const auto val = - ComputeExpression(dba, *it, std::nullopt, order_by.expression.expression, expr::identifier_node_symbol, ""); - properties_order_by.push_back(val); - } - ordered.push_back({std::move(properties_order_by), *it}); + auto &pk = maybe_pk.GetValue(); + MG_ASSERT(schema.second.size() == pk.size(), "PrimaryKey size does not match schema!"); + for (size_t i{0}; i < schema.second.size(); ++i) { + ret.emplace(schema.second[i].property_id, FromPropertyValueToValue(std::move(pk[i]))); } - std::sort(ordered.begin(), ordered.end(), [compare_typed_values](const auto &pair1, const auto &pair2) { - return compare_typed_values(pair1.properties_order_by, pair2.properties_order_by); + return ret; +} + +ShardResult> FillUpSourceVertexSecondaryLabels(const std::optional &v_acc, + const msgs::ExpandOneRequest &req) { + auto secondary_labels = v_acc->Labels(View::NEW); + if (secondary_labels.HasError()) { + spdlog::debug("Encountered an error while trying to get the secondary labels of a vertex. Transaction id: {}", + req.transaction_id.logical_id); + return secondary_labels.GetError(); + } + + auto &sec_labels = secondary_labels.GetValue(); + std::vector msgs_secondary_labels; + msgs_secondary_labels.reserve(sec_labels.size()); + + std::transform(sec_labels.begin(), sec_labels.end(), std::back_inserter(msgs_secondary_labels), + [](auto label_id) { return msgs::Label{.id = label_id}; }); + + return msgs_secondary_labels; +} + +ShardResult> FillUpSourceVertexProperties(const std::optional &v_acc, + const msgs::ExpandOneRequest &req, + storage::v3::View view, + const Schemas::Schema &schema) { + std::map src_vertex_properties; + + if (!req.src_vertex_properties) { + auto props = v_acc->Properties(View::NEW); + if (props.HasError()) { + spdlog::debug("Encountered an error while trying to access vertex properties. Transaction id: {}", + req.transaction_id.logical_id); + return props.GetError(); + } + + for (auto &[key, val] : props.GetValue()) { + src_vertex_properties.insert(std::make_pair(key, FromPropertyValueToValue(std::move(val)))); + } + auto pks = PrimaryKeysFromAccessor(*v_acc, view, schema); + if (pks) { + src_vertex_properties.merge(*pks); + } + + } else if (req.src_vertex_properties.value().empty()) { + // NOOP + } else { + for (const auto &prop : req.src_vertex_properties.value()) { + auto prop_val = v_acc->GetProperty(prop, View::OLD); + if (prop_val.HasError()) { + spdlog::debug("Encountered an error while trying to access vertex properties. Transaction id: {}", + req.transaction_id.logical_id); + return prop_val.GetError(); + } + src_vertex_properties.insert(std::make_pair(prop, FromPropertyValueToValue(std::move(prop_val.GetValue())))); + } + } + + return src_vertex_properties; +} + +ShardResult, 2>> FillUpConnectingEdges( + const std::optional &v_acc, const msgs::ExpandOneRequest &req, + const EdgeUniquenessFunction &maybe_filter_based_on_edge_uniqueness) { + std::vector edge_types{}; + edge_types.reserve(req.edge_types.size()); + std::transform(req.edge_types.begin(), req.edge_types.end(), std::back_inserter(edge_types), + [](const msgs::EdgeType &edge_type) { return edge_type.id; }); + + std::vector in_edges; + std::vector out_edges; + + switch (req.direction) { + case msgs::EdgeDirection::OUT: { + auto out_edges_result = v_acc->OutEdges(View::NEW, edge_types); + if (out_edges_result.HasError()) { + spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}", + req.transaction_id.logical_id); + return out_edges_result.GetError(); + } + out_edges = + maybe_filter_based_on_edge_uniqueness(std::move(out_edges_result.GetValue()), msgs::EdgeDirection::OUT); + break; + } + case msgs::EdgeDirection::IN: { + auto in_edges_result = v_acc->InEdges(View::NEW, edge_types); + if (in_edges_result.HasError()) { + spdlog::debug( + "Encountered an error while trying to get in-going EdgeAccessors. Transaction id: {}"[req.transaction_id + .logical_id]); + return in_edges_result.GetError(); + } + in_edges = maybe_filter_based_on_edge_uniqueness(std::move(in_edges_result.GetValue()), msgs::EdgeDirection::IN); + break; + } + case msgs::EdgeDirection::BOTH: { + auto in_edges_result = v_acc->InEdges(View::NEW, edge_types); + if (in_edges_result.HasError()) { + spdlog::debug("Encountered an error while trying to get in-going EdgeAccessors. Transaction id: {}", + req.transaction_id.logical_id); + return in_edges_result.GetError(); + } + in_edges = maybe_filter_based_on_edge_uniqueness(std::move(in_edges_result.GetValue()), msgs::EdgeDirection::IN); + auto out_edges_result = v_acc->OutEdges(View::NEW, edge_types); + if (out_edges_result.HasError()) { + spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}", + req.transaction_id.logical_id); + return out_edges_result.GetError(); + } + out_edges = + maybe_filter_based_on_edge_uniqueness(std::move(out_edges_result.GetValue()), msgs::EdgeDirection::OUT); + break; + } + } + return std::array, 2>{std::move(in_edges), std::move(out_edges)}; +} + +template +ShardResult FillEdges(const std::vector &edges, msgs::ExpandOneResultRow &row, + const EdgeFiller &edge_filler) { + for (const auto &edge : edges) { + if (const auto res = edge_filler(edge, are_in_edges, row); res.HasError()) { + return res.GetError(); + } + } + + return {}; +} + +}; // namespace + +ShardResult> CollectSpecificPropertiesFromAccessor(const VertexAccessor &acc, + const std::vector &props, + View view) { + std::map ret; + + for (const auto &prop : props) { + auto result = acc.GetProperty(prop, view); + if (result.HasError()) { + spdlog::debug("Encountered an Error while trying to get a vertex property."); + return result.GetError(); + } + auto &value = result.GetValue(); + ret.emplace(std::make_pair(prop, FromPropertyValueToValue(std::move(value)))); + } + + return ret; +} + +std::vector EvaluateVertexExpressions(DbAccessor &dba, const VertexAccessor &v_acc, + const std::vector &expressions, + std::string_view node_name) { + std::vector evaluated_expressions; + evaluated_expressions.reserve(expressions.size()); + + std::transform(expressions.begin(), expressions.end(), std::back_inserter(evaluated_expressions), + [&dba, &v_acc, &node_name](const auto &expression) { + return ComputeExpression(dba, v_acc, std::nullopt, expression, node_name, ""); + }); + + return evaluated_expressions; +} + +ShardResult> CollectAllPropertiesFromAccessor(const VertexAccessor &acc, View view, + const Schemas::Schema &schema) { + std::map ret; + auto props = acc.Properties(view); + if (props.HasError()) { + spdlog::debug("Encountered an error while trying to get vertex properties."); + return props.GetError(); + } + + auto &properties = props.GetValue(); + std::transform(properties.begin(), properties.end(), std::inserter(ret, ret.begin()), + [](std::pair &pair) { + return std::make_pair(pair.first, FromPropertyValueToValue(std::move(pair.second))); + }); + properties.clear(); + + auto pks = PrimaryKeysFromAccessor(acc, view, schema); + if (pks) { + ret.merge(*pks); + } + + return ret; +} + +EdgeUniquenessFunction InitializeEdgeUniquenessFunction(bool only_unique_neighbor_rows) { + // Functions to select connecting edges based on uniquness + EdgeUniquenessFunction maybe_filter_based_on_edge_uniquness; + + if (only_unique_neighbor_rows) { + maybe_filter_based_on_edge_uniquness = [](EdgeAccessors &&edges, + msgs::EdgeDirection edge_direction) -> EdgeAccessors { + std::function &, const storage::v3::EdgeAccessor &)> + is_edge_unique; + switch (edge_direction) { + case msgs::EdgeDirection::OUT: { + is_edge_unique = [](std::set &other_vertex_set, + const storage::v3::EdgeAccessor &edge_acc) { + auto [it, insertion_happened] = other_vertex_set.insert(&edge_acc.ToVertex()); + return insertion_happened; + }; + break; + } + case msgs::EdgeDirection::IN: { + is_edge_unique = [](std::set &other_vertex_set, + const storage::v3::EdgeAccessor &edge_acc) { + auto [it, insertion_happened] = other_vertex_set.insert(&edge_acc.FromVertex()); + return insertion_happened; + }; + break; + } + case msgs::EdgeDirection::BOTH: + MG_ASSERT(false, "This is should never happen, msgs::EdgeDirection::BOTH should not be passed here."); + } + + EdgeAccessors ret; + std::set other_vertex_set; + + for (const auto &edge : edges) { + if (is_edge_unique(other_vertex_set, edge)) { + ret.emplace_back(edge); + } + } + + return ret; + }; + } else { + maybe_filter_based_on_edge_uniquness = + [](EdgeAccessors &&edges, msgs::EdgeDirection /*edge_direction*/) -> EdgeAccessors { return std::move(edges); }; + } + + return maybe_filter_based_on_edge_uniquness; +} + +EdgeFiller InitializeEdgeFillerFunction(const msgs::ExpandOneRequest &req) { + EdgeFiller edge_filler; + + if (!req.edge_properties) { + edge_filler = [transaction_id = req.transaction_id.logical_id]( + const EdgeAccessor &edge, const bool is_in_edge, + msgs::ExpandOneResultRow &result_row) -> ShardResult { + auto properties_results = edge.Properties(View::NEW); + if (properties_results.HasError()) { + spdlog::debug("Encountered an error while trying to get edge properties. Transaction id: {}", transaction_id); + return properties_results.GetError(); + } + + std::map value_properties; + for (auto &[prop_key, prop_val] : properties_results.GetValue()) { + value_properties.insert(std::make_pair(prop_key, FromPropertyValueToValue(std::move(prop_val)))); + } + using EdgeWithAllProperties = msgs::ExpandOneResultRow::EdgeWithAllProperties; + EdgeWithAllProperties edges{ToMsgsVertexId(edge.FromVertex()), msgs::EdgeType{edge.EdgeType()}, + edge.Gid().AsUint(), std::move(value_properties)}; + if (is_in_edge) { + result_row.in_edges_with_all_properties.push_back(std::move(edges)); + } else { + result_row.out_edges_with_all_properties.push_back(std::move(edges)); + } + return {}; + }; + } else { + // TODO(gvolfing) - do we want to set the action_successful here? + edge_filler = [&req](const EdgeAccessor &edge, const bool is_in_edge, + msgs::ExpandOneResultRow &result_row) -> ShardResult { + std::vector value_properties; + value_properties.reserve(req.edge_properties.value().size()); + for (const auto &edge_prop : req.edge_properties.value()) { + auto property_result = edge.GetProperty(edge_prop, View::NEW); + if (property_result.HasError()) { + spdlog::debug("Encountered an error while trying to get edge properties. Transaction id: {}", + req.transaction_id.logical_id); + return property_result.GetError(); + } + value_properties.emplace_back(FromPropertyValueToValue(std::move(property_result.GetValue()))); + } + using EdgeWithSpecificProperties = msgs::ExpandOneResultRow::EdgeWithSpecificProperties; + EdgeWithSpecificProperties edges{ToMsgsVertexId(edge.FromVertex()), msgs::EdgeType{edge.EdgeType()}, + edge.Gid().AsUint(), std::move(value_properties)}; + if (is_in_edge) { + result_row.in_edges_with_specific_properties.push_back(std::move(edges)); + } else { + result_row.out_edges_with_specific_properties.push_back(std::move(edges)); + } + return {}; + }; + } + + return edge_filler; +} + +bool FilterOnVertex(DbAccessor &dba, const storage::v3::VertexAccessor &v_acc, const std::vector &filters, + const std::string_view node_name) { + return std::ranges::all_of(filters, [&node_name, &dba, &v_acc](const auto &filter_expr) { + auto res = ComputeExpression(dba, v_acc, std::nullopt, filter_expr, node_name, ""); + return res.IsBool() && res.ValueBool(); }); - return ordered; +} + +ShardResult GetExpandOneResult( + Shard::Accessor &acc, msgs::VertexId src_vertex, const msgs::ExpandOneRequest &req, + const EdgeUniquenessFunction &maybe_filter_based_on_edge_uniqueness, const EdgeFiller &edge_filler, + const Schemas::Schema &schema) { + /// Fill up source vertex + const auto primary_key = ConvertPropertyVector(src_vertex.second); + auto v_acc = acc.FindVertex(primary_key, View::NEW); + + msgs::Vertex source_vertex = {.id = src_vertex}; + auto maybe_secondary_labels = FillUpSourceVertexSecondaryLabels(v_acc, req); + if (maybe_secondary_labels.HasError()) { + return maybe_secondary_labels.GetError(); + } + source_vertex.labels = std::move(*maybe_secondary_labels); + + auto src_vertex_properties = FillUpSourceVertexProperties(v_acc, req, storage::v3::View::NEW, schema); + if (src_vertex_properties.HasError()) { + return src_vertex_properties.GetError(); + } + + /// Fill up connecting edges + auto fill_up_connecting_edges = FillUpConnectingEdges(v_acc, req, maybe_filter_based_on_edge_uniqueness); + if (fill_up_connecting_edges.HasError()) { + return fill_up_connecting_edges.GetError(); + } + auto [in_edges, out_edges] = fill_up_connecting_edges.GetValue(); + + msgs::ExpandOneResultRow result_row; + result_row.src_vertex = std::move(source_vertex); + result_row.src_vertex_properties = std::move(*src_vertex_properties); + static constexpr bool kInEdges = true; + static constexpr bool kOutEdges = false; + if (const auto fill_edges_res = FillEdges(in_edges, result_row, edge_filler); fill_edges_res.HasError()) { + return fill_edges_res.GetError(); + } + if (const auto fill_edges_res = FillEdges(out_edges, result_row, edge_filler); fill_edges_res.HasError()) { + return fill_edges_res.GetError(); + } + + return result_row; +} + +ShardResult GetExpandOneResult( + VertexAccessor v_acc, msgs::VertexId src_vertex, const msgs::ExpandOneRequest &req, + std::vector in_edge_accessors, std::vector out_edge_accessors, + const EdgeUniquenessFunction &maybe_filter_based_on_edge_uniqueness, const EdgeFiller &edge_filler, + const Schemas::Schema &schema) { + /// Fill up source vertex + msgs::Vertex source_vertex = {.id = src_vertex}; + auto maybe_secondary_labels = FillUpSourceVertexSecondaryLabels(v_acc, req); + if (maybe_secondary_labels.HasError()) { + return maybe_secondary_labels.GetError(); + } + source_vertex.labels = std::move(*maybe_secondary_labels); + + /// Fill up source vertex properties + auto src_vertex_properties = FillUpSourceVertexProperties(v_acc, req, storage::v3::View::NEW, schema); + if (src_vertex_properties.HasError()) { + return src_vertex_properties.GetError(); + } + + /// Fill up connecting edges + auto in_edges = maybe_filter_based_on_edge_uniqueness(std::move(in_edge_accessors), msgs::EdgeDirection::IN); + auto out_edges = maybe_filter_based_on_edge_uniqueness(std::move(out_edge_accessors), msgs::EdgeDirection::OUT); + + msgs::ExpandOneResultRow result_row; + result_row.src_vertex = std::move(source_vertex); + result_row.src_vertex_properties = std::move(*src_vertex_properties); + static constexpr bool kInEdges = true; + static constexpr bool kOutEdges = false; + if (const auto fill_edges_res = FillEdges(in_edges, result_row, edge_filler); fill_edges_res.HasError()) { + return fill_edges_res.GetError(); + } + if (const auto fill_edges_res = FillEdges(out_edges, result_row, edge_filler); fill_edges_res.HasError()) { + return fill_edges_res.GetError(); + } + + return result_row; } VerticesIterable::Iterator GetStartVertexIterator(VerticesIterable &vertex_iterable, - const std::vector &start_ids, const View view) { + const std::vector &primary_key, const View view) { auto it = vertex_iterable.begin(); while (it != vertex_iterable.end()) { - if (const auto &vertex = *it; start_ids <= vertex.PrimaryKey(view).GetValue()) { + if (const auto &vertex = *it; primary_key <= vertex.PrimaryKey(view).GetValue()) { break; } ++it; @@ -68,15 +450,80 @@ VerticesIterable::Iterator GetStartVertexIterator(VerticesIterable &vertex_itera return it; } -std::vector::const_iterator GetStartOrderedElementsIterator(const std::vector &ordered_elements, - const std::vector &start_ids, - const View view) { +std::vector>::const_iterator GetStartOrderedElementsIterator( + const std::vector> &ordered_elements, const std::vector &primary_key, + const View view) { for (auto it = ordered_elements.begin(); it != ordered_elements.end(); ++it) { - if (const auto &vertex = it->vertex_acc; start_ids <= vertex.PrimaryKey(view).GetValue()) { + if (const auto &vertex = it->object_acc; primary_key <= vertex.PrimaryKey(view).GetValue()) { return it; } } return ordered_elements.end(); } +std::array, 2> GetEdgesFromVertex(const VertexAccessor &vertex_accessor, + const msgs::EdgeDirection direction) { + std::vector in_edges; + std::vector out_edges; + + switch (direction) { + case memgraph::msgs::EdgeDirection::IN: { + auto edges = vertex_accessor.InEdges(View::OLD); + if (edges.HasValue()) { + in_edges = edges.GetValue(); + } + break; + } + case memgraph::msgs::EdgeDirection::OUT: { + auto edges = vertex_accessor.OutEdges(View::OLD); + if (edges.HasValue()) { + out_edges = edges.GetValue(); + } + break; + } + case memgraph::msgs::EdgeDirection::BOTH: { + auto maybe_in_edges = vertex_accessor.InEdges(View::OLD); + auto maybe_out_edges = vertex_accessor.OutEdges(View::OLD); + std::vector edges; + if (maybe_in_edges.HasValue()) { + in_edges = maybe_in_edges.GetValue(); + } + if (maybe_out_edges.HasValue()) { + out_edges = maybe_out_edges.GetValue(); + } + break; + } + } + + return std::array, 2>{std::move(in_edges), std::move(out_edges)}; +} + +std::vector> OrderByEdges(DbAccessor &dba, std::vector &iterable, + std::vector &order_by_edges, + const VertexAccessor &vertex_acc) { + std::vector ordering; + ordering.reserve(order_by_edges.size()); + std::transform(order_by_edges.begin(), order_by_edges.end(), std::back_inserter(ordering), + [](const auto &order_by) { return ConvertMsgsOrderByToOrdering(order_by.direction); }); + + std::vector> ordered; + for (auto it = iterable.begin(); it != iterable.end(); ++it) { + std::vector properties_order_by; + properties_order_by.reserve(order_by_edges.size()); + std::transform(order_by_edges.begin(), order_by_edges.end(), std::back_inserter(properties_order_by), + [&dba, &vertex_acc, &it](const auto &order_by) { + return ComputeExpression(dba, vertex_acc, *it, order_by.expression.expression, + expr::identifier_node_symbol, expr::identifier_edge_symbol); + }); + + ordered.push_back({std::move(properties_order_by), *it}); + } + + auto compare_typed_values = TypedValueVectorCompare(ordering); + std::sort(ordered.begin(), ordered.end(), [compare_typed_values](const auto &pair1, const auto &pair2) { + return compare_typed_values(pair1.properties_order_by, pair2.properties_order_by); + }); + return ordered; +} + } // namespace memgraph::storage::v3 diff --git a/src/storage/v3/request_helper.hpp b/src/storage/v3/request_helper.hpp index 24ed40f8c..1e8b7a108 100644 --- a/src/storage/v3/request_helper.hpp +++ b/src/storage/v3/request_helper.hpp @@ -9,14 +9,29 @@ // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. +#pragma once + #include -#include "ast/ast.hpp" +#include "query/v2/requests.hpp" +#include "storage/v3/bindings/ast/ast.hpp" +#include "storage/v3/bindings/pretty_print_ast_to_original_expression.hpp" #include "storage/v3/bindings/typed_value.hpp" +#include "storage/v3/edge_accessor.hpp" +#include "storage/v3/expr.hpp" #include "storage/v3/shard.hpp" #include "storage/v3/vertex_accessor.hpp" +#include "utils/template_utils.hpp" namespace memgraph::storage::v3 { +using EdgeAccessors = std::vector; +using EdgeUniquenessFunction = std::function; +using EdgeFiller = + std::function(const EdgeAccessor &edge, bool is_in_edge, msgs::ExpandOneResultRow &result_row)>; +using msgs::Value; + +template +concept ObjectAccessor = utils::SameAsAnyOf; inline bool TypedValueCompare(const TypedValue &a, const TypedValue &b) { // in ordering null comes after everything else @@ -72,6 +87,17 @@ inline bool TypedValueCompare(const TypedValue &a, const TypedValue &b) { } } +inline Ordering ConvertMsgsOrderByToOrdering(msgs::OrderingDirection ordering) { + switch (ordering) { + case memgraph::msgs::OrderingDirection::ASCENDING: + return memgraph::storage::v3::Ordering::ASC; + case memgraph::msgs::OrderingDirection::DESCENDING: + return memgraph::storage::v3::Ordering::DESC; + default: + LOG_FATAL("Unknown ordering direction"); + } +} + class TypedValueVectorCompare final { public: explicit TypedValueVectorCompare(const std::vector &ordering) : ordering_(ordering) {} @@ -99,18 +125,84 @@ class TypedValueVectorCompare final { std::vector ordering_; }; +template struct Element { std::vector properties_order_by; - VertexAccessor vertex_acc; + TObjectAccessor object_acc; }; -std::vector OrderByElements(Shard::Accessor &acc, DbAccessor &dba, VerticesIterable &vertices_iterable, - std::vector &order_bys); +template +concept VerticesIt = utils::SameAsAnyOf>; + +template +std::vector> OrderByVertices(DbAccessor &dba, TIterable &iterable, + std::vector &order_by_vertices) { + std::vector ordering; + ordering.reserve(order_by_vertices.size()); + std::transform(order_by_vertices.begin(), order_by_vertices.end(), std::back_inserter(ordering), + [](const auto &order_by) { return ConvertMsgsOrderByToOrdering(order_by.direction); }); + + std::vector> ordered; + for (auto it = iterable.begin(); it != iterable.end(); ++it) { + std::vector properties_order_by; + properties_order_by.reserve(order_by_vertices.size()); + + std::transform(order_by_vertices.begin(), order_by_vertices.end(), std::back_inserter(properties_order_by), + [&dba, &it](const auto &order_by) { + return ComputeExpression(dba, *it, std::nullopt /*e_acc*/, order_by.expression.expression, + expr::identifier_node_symbol, expr::identifier_edge_symbol); + }); + + ordered.push_back({std::move(properties_order_by), *it}); + } + + auto compare_typed_values = TypedValueVectorCompare(ordering); + std::sort(ordered.begin(), ordered.end(), [compare_typed_values](const auto &pair1, const auto &pair2) { + return compare_typed_values(pair1.properties_order_by, pair2.properties_order_by); + }); + return ordered; +} + +std::vector> OrderByEdges(DbAccessor &dba, std::vector &iterable, + std::vector &order_by_edges, + const VertexAccessor &vertex_acc); VerticesIterable::Iterator GetStartVertexIterator(VerticesIterable &vertex_iterable, - const std::vector &start_ids, View view); + const std::vector &primary_key, View view); -std::vector::const_iterator GetStartOrderedElementsIterator(const std::vector &ordered_elements, - const std::vector &start_ids, - View view); +std::vector>::const_iterator GetStartOrderedElementsIterator( + const std::vector> &ordered_elements, const std::vector &primary_key, + View view); + +std::array, 2> GetEdgesFromVertex(const VertexAccessor &vertex_accessor, + msgs::EdgeDirection direction); + +bool FilterOnVertex(DbAccessor &dba, const storage::v3::VertexAccessor &v_acc, const std::vector &filters, + std::string_view node_name); + +std::vector EvaluateVertexExpressions(DbAccessor &dba, const VertexAccessor &v_acc, + const std::vector &expressions, + std::string_view node_name); + +ShardResult> CollectSpecificPropertiesFromAccessor(const VertexAccessor &acc, + const std::vector &props, + View view); + +ShardResult> CollectAllPropertiesFromAccessor(const VertexAccessor &acc, View view, + const Schemas::Schema &schema); + +EdgeUniquenessFunction InitializeEdgeUniquenessFunction(bool only_unique_neighbor_rows); + +EdgeFiller InitializeEdgeFillerFunction(const msgs::ExpandOneRequest &req); + +ShardResult GetExpandOneResult( + Shard::Accessor &acc, msgs::VertexId src_vertex, const msgs::ExpandOneRequest &req, + const EdgeUniquenessFunction &maybe_filter_based_on_edge_uniqueness, const EdgeFiller &edge_filler, + const Schemas::Schema &schema); + +ShardResult GetExpandOneResult( + VertexAccessor v_acc, msgs::VertexId src_vertex, const msgs::ExpandOneRequest &req, + std::vector in_edge_accessors, std::vector out_edge_accessors, + const EdgeUniquenessFunction &maybe_filter_based_on_edge_uniqueness, const EdgeFiller &edge_filler, + const Schemas::Schema &schema); } // namespace memgraph::storage::v3 diff --git a/src/storage/v3/result.hpp b/src/storage/v3/result.hpp index 86fdb10d0..e3561fcc0 100644 --- a/src/storage/v3/result.hpp +++ b/src/storage/v3/result.hpp @@ -11,24 +11,43 @@ #pragma once +#include +#include +#include +#include #include +#include "common/errors.hpp" #include "utils/result.hpp" namespace memgraph::storage::v3 { static_assert(std::is_same_v); -enum class Error : uint8_t { - SERIALIZATION_ERROR, - NONEXISTENT_OBJECT, - DELETED_OBJECT, - VERTEX_HAS_EDGES, - PROPERTIES_DISABLED, - VERTEX_ALREADY_INSERTED +struct ShardError { + ShardError(common::ErrorCode code, std::string message, const std::experimental::source_location location) + : code{code}, message{std::move(message)}, source{fmt::format("{}:{}", location.file_name(), location.line())} {} + + ShardError(common::ErrorCode code, const std::experimental::source_location location) + : code{code}, source{fmt::format("{}:{}", location.file_name(), location.line())} {} + + common::ErrorCode code; + std::string message; + std::string source; + + inline friend bool operator==(const ShardError &lhs, const ShardError &rhs) { return lhs.code == rhs.code; } + + inline friend bool operator==(const ShardError &lhs, const common::ErrorCode rhs) { return lhs.code == rhs; } }; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SHARD_ERROR(error, ...) \ + ({ \ + using ErrorCode = memgraph::common::ErrorCode; \ + memgraph::storage::v3::ShardError(error, GET_MESSAGE(__VA_ARGS__), std::experimental::source_location::current()); \ + }) + template -using Result = utils::BasicResult; +using ShardResult = utils::BasicResult; } // namespace memgraph::storage::v3 diff --git a/src/storage/v3/schema_validator.cpp b/src/storage/v3/schema_validator.cpp index 748eda28d..d21043868 100644 --- a/src/storage/v3/schema_validator.cpp +++ b/src/storage/v3/schema_validator.cpp @@ -16,67 +16,58 @@ #include #include "common/types.hpp" +#include "storage/v3/name_id_mapper.hpp" +#include "storage/v3/result.hpp" #include "storage/v3/schemas.hpp" namespace memgraph::storage::v3 { -bool operator==(const SchemaViolation &lhs, const SchemaViolation &rhs) { - return lhs.status == rhs.status && lhs.label == rhs.label && - lhs.violated_schema_property == rhs.violated_schema_property && - lhs.violated_property_value == rhs.violated_property_value; -} +SchemaValidator::SchemaValidator(Schemas &schemas, const NameIdMapper &name_id_mapper) + : schemas_{&schemas}, name_id_mapper_{&name_id_mapper} {} -SchemaViolation::SchemaViolation(ValidationStatus status, LabelId label) : status{status}, label{label} {} - -SchemaViolation::SchemaViolation(ValidationStatus status, LabelId label, SchemaProperty violated_schema_property) - : status{status}, label{label}, violated_schema_property{violated_schema_property} {} - -SchemaViolation::SchemaViolation(ValidationStatus status, LabelId label, SchemaProperty violated_schema_property, - PropertyValue violated_property_value) - : status{status}, - label{label}, - violated_schema_property{violated_schema_property}, - violated_property_value{violated_property_value} {} - -SchemaValidator::SchemaValidator(Schemas &schemas) : schemas_{schemas} {} - -std::optional SchemaValidator::ValidateVertexCreate( - LabelId primary_label, const std::vector &labels, - const std::vector &primary_properties) const { +ShardResult SchemaValidator::ValidateVertexCreate(LabelId primary_label, const std::vector &labels, + const std::vector &primary_properties) const { // Schema on primary label - const auto *schema = schemas_.GetSchema(primary_label); + const auto *schema = schemas_->GetSchema(primary_label); if (schema == nullptr) { - return SchemaViolation(SchemaViolation::ValidationStatus::NO_SCHEMA_DEFINED_FOR_LABEL, primary_label); + return SHARD_ERROR(ErrorCode::SCHEMA_NO_SCHEMA_DEFINED_FOR_LABEL, "Schema not defined for label :{}", + name_id_mapper_->IdToName(primary_label.AsInt())); } // Is there another primary label among secondary labels for (const auto &secondary_label : labels) { - if (schemas_.GetSchema(secondary_label)) { - return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY, secondary_label); + if (schemas_->GetSchema(secondary_label)) { + return SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY, + "Cannot add label :{}, since it is defined as a primary label", + name_id_mapper_->IdToName(secondary_label.AsInt())); } } // Quick size check if (schema->second.size() != primary_properties.size()) { - return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PRIMARY_PROPERTIES_UNDEFINED, primary_label); + return SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED, + "Not all primary properties have been specified for :{} vertex", + name_id_mapper_->IdToName(primary_label.AsInt())); } // Check only properties defined by schema for (size_t i{0}; i < schema->second.size(); ++i) { // Check schema property type if (auto property_schema_type = PropertyTypeToSchemaType(primary_properties[i]); property_schema_type && *property_schema_type != schema->second[i].type) { - return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PROPERTY_WRONG_TYPE, primary_label, - schema->second[i], primary_properties[i]); + return SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE, + "Property {} is of wrong type, expected {}, actual {}", + name_id_mapper_->IdToName(schema->second[i].property_id.AsInt()), + SchemaTypeToString(schema->second[i].type), SchemaTypeToString(*property_schema_type)); } } - return std::nullopt; + return {}; } -std::optional SchemaValidator::ValidatePropertyUpdate(const LabelId primary_label, - const PropertyId property_id) const { +ShardResult SchemaValidator::ValidatePropertyUpdate(const LabelId primary_label, + const PropertyId property_id) const { // Verify existence of schema on primary label - const auto *schema = schemas_.GetSchema(primary_label); + const auto *schema = schemas_->GetSchema(primary_label); MG_ASSERT(schema, "Cannot validate against non existing schema!"); // Verify that updating property is not part of schema @@ -84,34 +75,37 @@ std::optional SchemaValidator::ValidatePropertyUpdate(const Lab schema->second, [property_id](const auto &schema_property) { return property_id == schema_property.property_id; }); schema_property != schema->second.end()) { - return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_KEY, primary_label, - *schema_property); + return SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY, + "Cannot update primary property {} of schema on label :{}", + name_id_mapper_->IdToName(schema_property->property_id.AsInt()), + name_id_mapper_->IdToName(primary_label.AsInt())); } - return std::nullopt; + return {}; } -std::optional SchemaValidator::ValidateLabelUpdate(const LabelId label) const { - const auto *schema = schemas_.GetSchema(label); +ShardResult SchemaValidator::ValidateLabelUpdate(const LabelId label) const { + const auto *schema = schemas_->GetSchema(label); if (schema) { - return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_LABEL, label); + return SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL, "Cannot add/remove primary label :{}", + name_id_mapper_->IdToName(label.AsInt())); } - return std::nullopt; + return {}; } -const Schemas::Schema *SchemaValidator::GetSchema(LabelId label) const { return schemas_.GetSchema(label); } +const Schemas::Schema *SchemaValidator::GetSchema(LabelId label) const { return schemas_->GetSchema(label); } VertexValidator::VertexValidator(const SchemaValidator &schema_validator, const LabelId primary_label) : schema_validator{&schema_validator}, primary_label_{primary_label} {} -std::optional VertexValidator::ValidatePropertyUpdate(PropertyId property_id) const { +ShardResult VertexValidator::ValidatePropertyUpdate(PropertyId property_id) const { return schema_validator->ValidatePropertyUpdate(primary_label_, property_id); }; -std::optional VertexValidator::ValidateAddLabel(LabelId label) const { +ShardResult VertexValidator::ValidateAddLabel(LabelId label) const { return schema_validator->ValidateLabelUpdate(label); } -std::optional VertexValidator::ValidateRemoveLabel(LabelId label) const { +ShardResult VertexValidator::ValidateRemoveLabel(LabelId label) const { return schema_validator->ValidateLabelUpdate(label); } diff --git a/src/storage/v3/schema_validator.hpp b/src/storage/v3/schema_validator.hpp index ce813fde2..637ca5f7e 100644 --- a/src/storage/v3/schema_validator.hpp +++ b/src/storage/v3/schema_validator.hpp @@ -11,68 +11,43 @@ #pragma once -#include #include +#include "storage/v2/result.hpp" #include "storage/v3/id_types.hpp" +#include "storage/v3/name_id_mapper.hpp" #include "storage/v3/property_value.hpp" #include "storage/v3/result.hpp" #include "storage/v3/schemas.hpp" namespace memgraph::storage::v3 { -struct SchemaViolation { - enum class ValidationStatus : uint8_t { - NO_SCHEMA_DEFINED_FOR_LABEL, - VERTEX_PROPERTY_WRONG_TYPE, - VERTEX_UPDATE_PRIMARY_KEY, - VERTEX_UPDATE_PRIMARY_LABEL, - VERTEX_SECONDARY_LABEL_IS_PRIMARY, - VERTEX_PRIMARY_PROPERTIES_UNDEFINED, - }; - - SchemaViolation(ValidationStatus status, LabelId label); - - SchemaViolation(ValidationStatus status, LabelId label, SchemaProperty violated_schema_property); - - SchemaViolation(ValidationStatus status, LabelId label, SchemaProperty violated_schema_property, - PropertyValue violated_property_value); - - friend bool operator==(const SchemaViolation &lhs, const SchemaViolation &rhs); - - ValidationStatus status; - LabelId label; - std::optional violated_schema_property; - std::optional violated_property_value; -}; - class SchemaValidator { public: - explicit SchemaValidator(Schemas &schemas); + explicit SchemaValidator(Schemas &schemas, const NameIdMapper &name_id_mapper); - [[nodiscard]] std::optional ValidateVertexCreate( - LabelId primary_label, const std::vector &labels, - const std::vector &primary_properties) const; + [[nodiscard]] ShardResult ValidateVertexCreate(LabelId primary_label, const std::vector &labels, + const std::vector &primary_properties) const; - [[nodiscard]] std::optional ValidatePropertyUpdate(LabelId primary_label, - PropertyId property_id) const; + [[nodiscard]] ShardResult ValidatePropertyUpdate(LabelId primary_label, PropertyId property_id) const; - [[nodiscard]] std::optional ValidateLabelUpdate(LabelId label) const; + [[nodiscard]] ShardResult ValidateLabelUpdate(LabelId label) const; const Schemas::Schema *GetSchema(LabelId label) const; private: - Schemas &schemas_; + Schemas *schemas_; + const NameIdMapper *name_id_mapper_; }; struct VertexValidator { explicit VertexValidator(const SchemaValidator &schema_validator, LabelId primary_label); - [[nodiscard]] std::optional ValidatePropertyUpdate(PropertyId property_id) const; + [[nodiscard]] ShardResult ValidatePropertyUpdate(PropertyId property_id) const; - [[nodiscard]] std::optional ValidateAddLabel(LabelId label) const; + [[nodiscard]] ShardResult ValidateAddLabel(LabelId label) const; - [[nodiscard]] std::optional ValidateRemoveLabel(LabelId label) const; + [[nodiscard]] ShardResult ValidateRemoveLabel(LabelId label) const; const SchemaValidator *schema_validator; diff --git a/src/storage/v3/shard.cpp b/src/storage/v3/shard.cpp index 49c692134..5dd8e7256 100644 --- a/src/storage/v3/shard.cpp +++ b/src/storage/v3/shard.cpp @@ -31,9 +31,10 @@ #include "storage/v3/indices.hpp" #include "storage/v3/key_store.hpp" #include "storage/v3/mvcc.hpp" +#include "storage/v3/name_id_mapper.hpp" #include "storage/v3/property_value.hpp" +#include "storage/v3/result.hpp" #include "storage/v3/schema_validator.hpp" -#include "storage/v3/shard_operation_result.hpp" #include "storage/v3/transaction.hpp" #include "storage/v3/vertex.hpp" #include "storage/v3/vertex_accessor.hpp" @@ -327,7 +328,7 @@ Shard::Shard(const LabelId primary_label, const PrimaryKey min_primary_key, : primary_label_{primary_label}, min_primary_key_{min_primary_key}, max_primary_key_{max_primary_key}, - schema_validator_{schemas_}, + schema_validator_{schemas_, name_id_mapper_}, vertex_validator_{schema_validator_, primary_label}, indices_{config.items, vertex_validator_}, isolation_level_{config.transaction.isolation_level}, @@ -344,7 +345,7 @@ Shard::~Shard() {} Shard::Accessor::Accessor(Shard &shard, Transaction &transaction) : shard_(&shard), transaction_(&transaction), config_(shard_->config_.items) {} -ShardOperationResult Shard::Accessor::CreateVertexAndValidate( +ShardResult Shard::Accessor::CreateVertexAndValidate( const std::vector &labels, const std::vector &primary_properties, const std::vector> &properties) { OOMExceptionEnabler oom_exception; @@ -352,8 +353,8 @@ ShardOperationResult Shard::Accessor::CreateVertexAndValidate( auto maybe_schema_violation = GetSchemaValidator().ValidateVertexCreate(shard_->primary_label_, labels, primary_properties); - if (maybe_schema_violation) { - return {std::move(*maybe_schema_violation)}; + if (maybe_schema_violation.HasError()) { + return {std::move(maybe_schema_violation.GetError())}; } auto acc = shard_->vertices_.access(); @@ -363,7 +364,7 @@ ShardOperationResult Shard::Accessor::CreateVertexAndValidate( VertexAccessor vertex_acc{&it->vertex, transaction_, &shard_->indices_, config_, shard_->vertex_validator_}; if (!inserted) { - return {Error::VERTEX_ALREADY_INSERTED}; + return SHARD_ERROR(ErrorCode::VERTEX_ALREADY_INSERTED); } MG_ASSERT(it != acc.end(), "Invalid Vertex accessor!"); @@ -394,19 +395,19 @@ std::optional Shard::Accessor::FindVertex(std::vectorvertex, transaction_, &shard_->indices_, config_, shard_->vertex_validator_, view); } -Result> Shard::Accessor::DeleteVertex(VertexAccessor *vertex) { +ShardResult> Shard::Accessor::DeleteVertex(VertexAccessor *vertex) { MG_ASSERT(vertex->transaction_ == transaction_, "VertexAccessor must be from the same transaction as the storage " "accessor when deleting a vertex!"); auto *vertex_ptr = vertex->vertex_; - if (!PrepareForWrite(transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR; + if (!PrepareForWrite(transaction_, vertex_ptr)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); if (vertex_ptr->deleted) { return std::optional{}; } - if (!vertex_ptr->in_edges.empty() || !vertex_ptr->out_edges.empty()) return Error::VERTEX_HAS_EDGES; + if (!vertex_ptr->in_edges.empty() || !vertex_ptr->out_edges.empty()) return SHARD_ERROR(ErrorCode::VERTEX_HAS_EDGES); CreateAndLinkDelta(transaction_, vertex_ptr, Delta::RecreateObjectTag()); vertex_ptr->deleted = true; @@ -415,7 +416,7 @@ Result> Shard::Accessor::DeleteVertex(VertexAccess shard_->vertex_validator_, true); } -Result>>> Shard::Accessor::DetachDeleteVertex( +ShardResult>>> Shard::Accessor::DetachDeleteVertex( VertexAccessor *vertex) { using ReturnType = std::pair>; @@ -428,7 +429,7 @@ Result>>> Shar std::vector out_edges; { - if (!PrepareForWrite(transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR; + if (!PrepareForWrite(transaction_, vertex_ptr)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); if (vertex_ptr->deleted) return std::optional{}; @@ -443,7 +444,7 @@ Result>>> Shar EdgeAccessor e(edge, edge_type, from_vertex, vertex_id, transaction_, &shard_->indices_, config_); auto ret = DeleteEdge(e.From(), e.To(), e.Gid()); if (ret.HasError()) { - MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!"); + MG_ASSERT(ret.GetError() == common::ErrorCode::SERIALIZATION_ERROR, "Invalid database state!"); return ret.GetError(); } @@ -456,7 +457,7 @@ Result>>> Shar EdgeAccessor e(edge, edge_type, vertex_id, to_vertex, transaction_, &shard_->indices_, config_); auto ret = DeleteEdge(e.From(), e.To(), e.Gid()); if (ret.HasError()) { - MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!"); + MG_ASSERT(ret.GetError() == common::ErrorCode::SERIALIZATION_ERROR, "Invalid database state!"); return ret.GetError(); } @@ -469,7 +470,7 @@ Result>>> Shar // vertex. Some other transaction could have modified the vertex in the // meantime if we didn't have any edges to delete. - if (!PrepareForWrite(transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR; + if (!PrepareForWrite(transaction_, vertex_ptr)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); MG_ASSERT(!vertex_ptr->deleted, "Invalid database state!"); @@ -481,8 +482,8 @@ Result>>> Shar std::move(deleted_edges)); } -Result Shard::Accessor::CreateEdge(VertexId from_vertex_id, VertexId to_vertex_id, - const EdgeTypeId edge_type, const Gid gid) { +ShardResult Shard::Accessor::CreateEdge(VertexId from_vertex_id, VertexId to_vertex_id, + const EdgeTypeId edge_type, const Gid gid) { OOMExceptionEnabler oom_exception; Vertex *from_vertex{nullptr}; Vertex *to_vertex{nullptr}; @@ -506,12 +507,12 @@ Result Shard::Accessor::CreateEdge(VertexId from_vertex_id, Vertex } if (from_is_local) { - if (!PrepareForWrite(transaction_, from_vertex)) return Error::SERIALIZATION_ERROR; - if (from_vertex->deleted) return Error::DELETED_OBJECT; + if (!PrepareForWrite(transaction_, from_vertex)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); + if (from_vertex->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); } if (to_is_local && to_vertex != from_vertex) { - if (!PrepareForWrite(transaction_, to_vertex)) return Error::SERIALIZATION_ERROR; - if (to_vertex->deleted) return Error::DELETED_OBJECT; + if (!PrepareForWrite(transaction_, to_vertex)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); + if (to_vertex->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); } EdgeRef edge(gid); @@ -540,8 +541,8 @@ Result Shard::Accessor::CreateEdge(VertexId from_vertex_id, Vertex &shard_->indices_, config_); } -Result> Shard::Accessor::DeleteEdge(VertexId from_vertex_id, VertexId to_vertex_id, - const Gid edge_id) { +ShardResult> Shard::Accessor::DeleteEdge(VertexId from_vertex_id, VertexId to_vertex_id, + const Gid edge_id) { Vertex *from_vertex{nullptr}; Vertex *to_vertex{nullptr}; @@ -566,13 +567,13 @@ Result> Shard::Accessor::DeleteEdge(VertexId from_ve if (from_is_local) { if (!PrepareForWrite(transaction_, from_vertex)) { - return Error::SERIALIZATION_ERROR; + return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); } MG_ASSERT(!from_vertex->deleted, "Invalid database state!"); } if (to_is_local && to_vertex != from_vertex) { if (!PrepareForWrite(transaction_, to_vertex)) { - return Error::SERIALIZATION_ERROR; + return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); } MG_ASSERT(!to_vertex->deleted, "Invalid database state!"); } diff --git a/src/storage/v3/shard.hpp b/src/storage/v3/shard.hpp index 778c92abd..5c025ae57 100644 --- a/src/storage/v3/shard.hpp +++ b/src/storage/v3/shard.hpp @@ -38,7 +38,6 @@ #include "storage/v3/result.hpp" #include "storage/v3/schema_validator.hpp" #include "storage/v3/schemas.hpp" -#include "storage/v3/shard_operation_result.hpp" #include "storage/v3/transaction.hpp" #include "storage/v3/vertex.hpp" #include "storage/v3/vertex_accessor.hpp" @@ -207,7 +206,7 @@ class Shard final { public: /// @throw std::bad_alloc - ShardOperationResult CreateVertexAndValidate( + ShardResult CreateVertexAndValidate( const std::vector &labels, const std::vector &primary_properties, const std::vector> &properties); @@ -262,19 +261,19 @@ class Shard final { /// @return Accessor to the deleted vertex if a deletion took place, std::nullopt otherwise /// @throw std::bad_alloc - Result> DeleteVertex(VertexAccessor *vertex); + ShardResult> DeleteVertex(VertexAccessor *vertex); /// @return Accessor to the deleted vertex and deleted edges if a deletion took place, std::nullopt otherwise /// @throw std::bad_alloc - Result>>> DetachDeleteVertex( + ShardResult>>> DetachDeleteVertex( VertexAccessor *vertex); /// @throw std::bad_alloc - Result CreateEdge(VertexId from_vertex_id, VertexId to_vertex_id, EdgeTypeId edge_type, Gid gid); + ShardResult CreateEdge(VertexId from_vertex_id, VertexId to_vertex_id, EdgeTypeId edge_type, Gid gid); /// Accessor to the deleted edge if a deletion took place, std::nullopt otherwise /// @throw std::bad_alloc - Result> DeleteEdge(VertexId from_vertex_id, VertexId to_vertex_id, Gid edge_id); + ShardResult> DeleteEdge(VertexId from_vertex_id, VertexId to_vertex_id, Gid edge_id); LabelId NameToLabel(std::string_view name) const; diff --git a/src/storage/v3/shard_rsm.cpp b/src/storage/v3/shard_rsm.cpp index 37a516b8a..1548b56c7 100644 --- a/src/storage/v3/shard_rsm.cpp +++ b/src/storage/v3/shard_rsm.cpp @@ -34,6 +34,7 @@ #include "storage/v3/key_store.hpp" #include "storage/v3/property_value.hpp" #include "storage/v3/request_helper.hpp" +#include "storage/v3/result.hpp" #include "storage/v3/schemas.hpp" #include "storage/v3/shard.hpp" #include "storage/v3/shard_rsm.hpp" @@ -41,443 +42,38 @@ #include "storage/v3/vertex_accessor.hpp" #include "storage/v3/vertex_id.hpp" #include "storage/v3/view.hpp" +#include "utils/logging.hpp" namespace memgraph::storage::v3 { using msgs::Label; using msgs::PropertyId; using msgs::Value; +using conversions::ConvertPropertyMap; using conversions::ConvertPropertyVector; using conversions::ConvertValueVector; +using conversions::FromMap; using conversions::FromPropertyValueToValue; using conversions::ToMsgsVertexId; using conversions::ToPropertyValue; -namespace { -namespace msgs = msgs; - -using AllEdgePropertyDataSructure = std::map; -using SpecificEdgePropertyDataSructure = std::vector; - -using AllEdgeProperties = std::tuple; -using SpecificEdgeProperties = std::tuple; - -using SpecificEdgePropertiesVector = std::vector; -using AllEdgePropertiesVector = std::vector; - -using EdgeAccessors = std::vector; - -using EdgeFiller = std::function; -using EdgeUniqunessFunction = std::function; - -struct VertexIdCmpr { - bool operator()(const storage::v3::VertexId *lhs, const storage::v3::VertexId *rhs) const { return *lhs < *rhs; } -}; - -std::vector> ConvertPropertyMap( - std::vector> &&properties) { - std::vector> ret; - ret.reserve(properties.size()); - - std::transform(std::make_move_iterator(properties.begin()), std::make_move_iterator(properties.end()), - std::back_inserter(ret), [](std::pair &&property) { - return std::make_pair(property.first, ToPropertyValue(std::move(property.second))); - }); - - return ret; +auto CreateErrorResponse(const ShardError &shard_error, const auto transaction_id, const std::string_view action) { + msgs::ShardError message_shard_error{shard_error.code, shard_error.message}; + spdlog::debug("{} In transaction {} {} failed: {}: {}", shard_error.source, transaction_id.logical_id, action, + ErrorCodeToString(shard_error.code), shard_error.message); + return message_shard_error; } -std::vector> FromMap(const std::map &properties) { - std::vector> ret; - ret.reserve(properties.size()); - - std::transform(properties.begin(), properties.end(), std::back_inserter(ret), - [](const auto &property) { return std::make_pair(property.first, property.second); }); - - return ret; -} - -std::optional> CollectSpecificPropertiesFromAccessor(const VertexAccessor &acc, - const std::vector &props, - View view) { - std::map ret; - - for (const auto &prop : props) { - auto result = acc.GetProperty(prop, view); - if (result.HasError()) { - spdlog::debug("Encountered an Error while trying to get a vertex property."); - return std::nullopt; - } - auto &value = result.GetValue(); - ret.emplace(std::make_pair(prop, FromPropertyValueToValue(std::move(value)))); - } - - return ret; -} - -std::optional> PrimaryKeysFromAccessor(const VertexAccessor &acc, View view, - const Schemas::Schema *schema) { - std::map ret; - auto props = acc.Properties(view); - auto maybe_pk = acc.PrimaryKey(view); - if (maybe_pk.HasError()) { - spdlog::debug("Encountered an error while trying to get vertex primary key."); - return std::nullopt; - } - auto &pk = maybe_pk.GetValue(); - MG_ASSERT(schema->second.size() == pk.size(), "PrimaryKey size does not match schema!"); - for (size_t i{0}; i < schema->second.size(); ++i) { - ret.emplace(schema->second[i].property_id, FromPropertyValueToValue(std::move(pk[i]))); - } - - return ret; -} - -std::optional> CollectAllPropertiesFromAccessor(const VertexAccessor &acc, View view, - const Schemas::Schema *schema) { - std::map ret; - auto props = acc.Properties(view); - if (props.HasError()) { - spdlog::debug("Encountered an error while trying to get vertex properties."); - return std::nullopt; - } - - auto &properties = props.GetValue(); - std::transform(properties.begin(), properties.end(), std::inserter(ret, ret.begin()), - [](std::pair &pair) { - return std::make_pair(pair.first, FromPropertyValueToValue(std::move(pair.second))); - }); - properties.clear(); - - auto pks = PrimaryKeysFromAccessor(acc, view, schema); - if (pks) { - ret.merge(*pks); - } - - return ret; -} - -bool FilterOnVertex(DbAccessor &dba, const storage::v3::VertexAccessor &v_acc, const std::vector &filters, - const std::string_view node_name) { - return std::ranges::all_of(filters, [&node_name, &dba, &v_acc](const auto &filter_expr) { - auto res = ComputeExpression(dba, v_acc, std::nullopt, filter_expr, node_name, ""); - return res.IsBool() && res.ValueBool(); - }); -} - -std::vector EvaluateVertexExpressions(DbAccessor &dba, const VertexAccessor &v_acc, - const std::vector &expressions, - std::string_view node_name) { - std::vector evaluated_expressions; - evaluated_expressions.reserve(expressions.size()); - - std::transform(expressions.begin(), expressions.end(), std::back_inserter(evaluated_expressions), - [&dba, &v_acc, &node_name](const auto &expression) { - return ComputeExpression(dba, v_acc, std::nullopt, expression, node_name, ""); - }); - - return evaluated_expressions; -} - -struct LocalError {}; - -std::optional> FillUpSourceVertexSecondaryLabels(const std::optional &v_acc, - const msgs::ExpandOneRequest &req) { - auto secondary_labels = v_acc->Labels(View::NEW); - if (secondary_labels.HasError()) { - spdlog::debug("Encountered an error while trying to get the secondary labels of a vertex. Transaction id: {}", - req.transaction_id.logical_id); - return std::nullopt; - } - - auto &sec_labels = secondary_labels.GetValue(); - std::vector msgs_secondary_labels; - msgs_secondary_labels.reserve(sec_labels.size()); - - std::transform(sec_labels.begin(), sec_labels.end(), std::back_inserter(msgs_secondary_labels), - [](auto label_id) { return msgs::Label{.id = label_id}; }); - - return msgs_secondary_labels; -} - -std::optional> FillUpSourceVertexProperties(const std::optional &v_acc, - const msgs::ExpandOneRequest &req, - storage::v3::View view, - const Schemas::Schema *schema) { - std::map src_vertex_properties; - - if (!req.src_vertex_properties) { - auto props = v_acc->Properties(View::NEW); - if (props.HasError()) { - spdlog::debug("Encountered an error while trying to access vertex properties. Transaction id: {}", - req.transaction_id.logical_id); - return std::nullopt; - } - - for (auto &[key, val] : props.GetValue()) { - src_vertex_properties.insert(std::make_pair(key, FromPropertyValueToValue(std::move(val)))); - } - auto pks = PrimaryKeysFromAccessor(*v_acc, view, schema); - if (pks) { - src_vertex_properties.merge(*pks); - } - - } else if (req.src_vertex_properties.value().empty()) { - // NOOP - } else { - for (const auto &prop : req.src_vertex_properties.value()) { - auto prop_val = v_acc->GetProperty(prop, View::OLD); - if (prop_val.HasError()) { - spdlog::debug("Encountered an error while trying to access vertex properties. Transaction id: {}", - req.transaction_id.logical_id); - return std::nullopt; - } - src_vertex_properties.insert(std::make_pair(prop, FromPropertyValueToValue(std::move(prop_val.GetValue())))); - } - } - - return src_vertex_properties; -} - -std::optional, 2>> FillUpConnectingEdges( - const std::optional &v_acc, const msgs::ExpandOneRequest &req, - const EdgeUniqunessFunction &maybe_filter_based_on_edge_uniquness) { - std::vector edge_types{}; - edge_types.reserve(req.edge_types.size()); - std::transform(req.edge_types.begin(), req.edge_types.end(), std::back_inserter(edge_types), - [](const msgs::EdgeType &edge_type) { return edge_type.id; }); - - std::vector in_edges; - std::vector out_edges; - - switch (req.direction) { - case msgs::EdgeDirection::OUT: { - auto out_edges_result = v_acc->OutEdges(View::NEW, edge_types); - if (out_edges_result.HasError()) { - spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}", - req.transaction_id.logical_id); - return std::nullopt; - } - out_edges = - maybe_filter_based_on_edge_uniquness(std::move(out_edges_result.GetValue()), msgs::EdgeDirection::OUT); - break; - } - case msgs::EdgeDirection::IN: { - auto in_edges_result = v_acc->InEdges(View::NEW, edge_types); - if (in_edges_result.HasError()) { - spdlog::debug( - "Encountered an error while trying to get in-going EdgeAccessors. Transaction id: {}"[req.transaction_id - .logical_id]); - return std::nullopt; - } - in_edges = maybe_filter_based_on_edge_uniquness(std::move(in_edges_result.GetValue()), msgs::EdgeDirection::IN); - break; - } - case msgs::EdgeDirection::BOTH: { - auto in_edges_result = v_acc->InEdges(View::NEW, edge_types); - if (in_edges_result.HasError()) { - spdlog::debug("Encountered an error while trying to get in-going EdgeAccessors. Transaction id: {}", - req.transaction_id.logical_id); - return std::nullopt; - } - in_edges = maybe_filter_based_on_edge_uniquness(std::move(in_edges_result.GetValue()), msgs::EdgeDirection::IN); - auto out_edges_result = v_acc->OutEdges(View::NEW, edge_types); - if (out_edges_result.HasError()) { - spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}", - req.transaction_id.logical_id); - return std::nullopt; - } - out_edges = - maybe_filter_based_on_edge_uniquness(std::move(out_edges_result.GetValue()), msgs::EdgeDirection::OUT); - break; - } - } - return std::array, 2>{in_edges, out_edges}; -} - -using AllEdgePropertyDataSructure = std::map; -using SpecificEdgePropertyDataSructure = std::vector; - -using AllEdgeProperties = std::tuple; -using SpecificEdgeProperties = std::tuple; - -using SpecificEdgePropertiesVector = std::vector; -using AllEdgePropertiesVector = std::vector; - -using EdgeFiller = std::function; - -template -bool FillEdges(const std::vector &edges, msgs::ExpandOneResultRow &row, const EdgeFiller &edge_filler) { - for (const auto &edge : edges) { - if (!edge_filler(edge, are_in_edges, row)) { - return false; - } - } - - return true; -} - -std::optional GetExpandOneResult( - Shard::Accessor &acc, msgs::VertexId src_vertex, const msgs::ExpandOneRequest &req, - const EdgeUniqunessFunction &maybe_filter_based_on_edge_uniquness, const EdgeFiller &edge_filler, - const Schemas::Schema *schema) { - /// Fill up source vertex - const auto primary_key = ConvertPropertyVector(src_vertex.second); - auto v_acc = acc.FindVertex(primary_key, View::NEW); - - msgs::Vertex source_vertex = {.id = src_vertex}; - if (const auto maybe_secondary_labels = FillUpSourceVertexSecondaryLabels(v_acc, req); maybe_secondary_labels) { - source_vertex.labels = *maybe_secondary_labels; - } else { - return std::nullopt; - } - - std::optional> src_vertex_properties; - src_vertex_properties = FillUpSourceVertexProperties(v_acc, req, storage::v3::View::NEW, schema); - - if (!src_vertex_properties) { - return std::nullopt; - } - - /// Fill up connecting edges - auto fill_up_connecting_edges = FillUpConnectingEdges(v_acc, req, maybe_filter_based_on_edge_uniquness); - if (!fill_up_connecting_edges) { - return std::nullopt; - } - - auto [in_edges, out_edges] = fill_up_connecting_edges.value(); - - msgs::ExpandOneResultRow result_row; - result_row.src_vertex = std::move(source_vertex); - result_row.src_vertex_properties = std::move(*src_vertex_properties); - static constexpr bool kInEdges = true; - static constexpr bool kOutEdges = false; - if (!in_edges.empty() && !FillEdges(in_edges, result_row, edge_filler)) { - return std::nullopt; - } - if (!out_edges.empty() && !FillEdges(out_edges, result_row, edge_filler)) { - return std::nullopt; - } - - return result_row; -} - -EdgeUniqunessFunction InitializeEdgeUniqunessFunction(bool only_unique_neighbor_rows) { - // Functions to select connecting edges based on uniquness - EdgeUniqunessFunction maybe_filter_based_on_edge_uniquness; - - if (only_unique_neighbor_rows) { - maybe_filter_based_on_edge_uniquness = [](EdgeAccessors &&edges, - msgs::EdgeDirection edge_direction) -> EdgeAccessors { - std::function &, const storage::v3::EdgeAccessor &)> - is_edge_unique; - switch (edge_direction) { - case msgs::EdgeDirection::OUT: { - is_edge_unique = [](std::set &other_vertex_set, - const storage::v3::EdgeAccessor &edge_acc) { - auto [it, insertion_happened] = other_vertex_set.insert(&edge_acc.To()); - return insertion_happened; - }; - break; - } - case msgs::EdgeDirection::IN: { - is_edge_unique = [](std::set &other_vertex_set, - const storage::v3::EdgeAccessor &edge_acc) { - auto [it, insertion_happened] = other_vertex_set.insert(&edge_acc.From()); - return insertion_happened; - }; - break; - } - case msgs::EdgeDirection::BOTH: - MG_ASSERT(false, "This is should never happen, msgs::EdgeDirection::BOTH should not be passed here."); - } - - EdgeAccessors ret; - std::set other_vertex_set; - - for (const auto &edge : edges) { - if (is_edge_unique(other_vertex_set, edge)) { - ret.emplace_back(edge); - } - } - - return ret; - }; - } else { - maybe_filter_based_on_edge_uniquness = - [](EdgeAccessors &&edges, msgs::EdgeDirection /*edge_direction*/) -> EdgeAccessors { return std::move(edges); }; - } - - return maybe_filter_based_on_edge_uniquness; -} - -EdgeFiller InitializeEdgeFillerFunction(const msgs::ExpandOneRequest &req) { - EdgeFiller edge_filler; - - if (!req.edge_properties) { - edge_filler = [transaction_id = req.transaction_id.logical_id](const EdgeAccessor &edge, const bool is_in_edge, - msgs::ExpandOneResultRow &result_row) -> bool { - auto properties_results = edge.Properties(View::NEW); - if (properties_results.HasError()) { - spdlog::debug("Encountered an error while trying to get edge properties. Transaction id: {}", transaction_id); - return false; - } - - std::map value_properties; - for (auto &[prop_key, prop_val] : properties_results.GetValue()) { - value_properties.insert(std::make_pair(prop_key, FromPropertyValueToValue(std::move(prop_val)))); - } - using EdgeWithAllProperties = msgs::ExpandOneResultRow::EdgeWithAllProperties; - EdgeWithAllProperties edges{ToMsgsVertexId(edge.From()), msgs::EdgeType{edge.EdgeType()}, edge.Gid().AsUint(), - std::move(value_properties)}; - if (is_in_edge) { - result_row.in_edges_with_all_properties.push_back(std::move(edges)); - } else { - result_row.out_edges_with_all_properties.push_back(std::move(edges)); - } - return true; - }; - } else { - // TODO(gvolfing) - do we want to set the action_successful here? - edge_filler = [&req](const EdgeAccessor &edge, const bool is_in_edge, - msgs::ExpandOneResultRow &result_row) -> bool { - std::vector value_properties; - value_properties.reserve(req.edge_properties.value().size()); - for (const auto &edge_prop : req.edge_properties.value()) { - auto property_result = edge.GetProperty(edge_prop, View::NEW); - if (property_result.HasError()) { - spdlog::debug("Encountered an error while trying to get edge properties. Transaction id: {}", - req.transaction_id.logical_id); - return false; - } - value_properties.emplace_back(FromPropertyValueToValue(std::move(property_result.GetValue()))); - } - using EdgeWithSpecificProperties = msgs::ExpandOneResultRow::EdgeWithSpecificProperties; - EdgeWithSpecificProperties edges{ToMsgsVertexId(edge.From()), msgs::EdgeType{edge.EdgeType()}, - edge.Gid().AsUint(), std::move(value_properties)}; - if (is_in_edge) { - result_row.in_edges_with_specific_properties.push_back(std::move(edges)); - } else { - result_row.out_edges_with_specific_properties.push_back(std::move(edges)); - } - return true; - }; - } - - return edge_filler; -} - -}; // namespace msgs::WriteResponses ShardRsm::ApplyWrite(msgs::CreateVerticesRequest &&req) { auto acc = shard_->Access(req.transaction_id); - bool action_successful = true; + std::optional shard_error; for (auto &new_vertex : req.new_vertices) { /// TODO(gvolfing) Consider other methods than converting. Change either /// the way that the property map is stored in the messages, or the /// signature of CreateVertexAndValidate. - auto converted_property_map = ConvertPropertyMap(std::move(new_vertex.properties)); + auto converted_property_map = ConvertPropertyMap(new_vertex.properties); // TODO(gvolfing) make sure if this conversion is actually needed. std::vector converted_label_ids; @@ -492,163 +88,96 @@ msgs::WriteResponses ShardRsm::ApplyWrite(msgs::CreateVerticesRequest &&req) { auto result_schema = acc.CreateVertexAndValidate(converted_label_ids, transformed_pk, converted_property_map); if (result_schema.HasError()) { - auto &error = result_schema.GetError(); - - std::visit( - [](T &&error) { - using ErrorType = std::remove_cvref_t; - if constexpr (std::is_same_v) { - spdlog::debug("Creating vertex failed with error: SchemaViolation"); - } else if constexpr (std::is_same_v) { - switch (error) { - case Error::DELETED_OBJECT: - spdlog::debug("Creating vertex failed with error: DELETED_OBJECT"); - break; - case Error::NONEXISTENT_OBJECT: - spdlog::debug("Creating vertex failed with error: NONEXISTENT_OBJECT"); - break; - case Error::SERIALIZATION_ERROR: - spdlog::debug("Creating vertex failed with error: SERIALIZATION_ERROR"); - break; - case Error::PROPERTIES_DISABLED: - spdlog::debug("Creating vertex failed with error: PROPERTIES_DISABLED"); - break; - case Error::VERTEX_HAS_EDGES: - spdlog::debug("Creating vertex failed with error: VERTEX_HAS_EDGES"); - break; - case Error::VERTEX_ALREADY_INSERTED: - spdlog::debug("Creating vertex failed with error: VERTEX_ALREADY_INSERTED"); - break; - } - } else { - static_assert(kAlwaysFalse, "Missing type from variant visitor"); - } - }, - error); - - action_successful = false; + shard_error.emplace(CreateErrorResponse(result_schema.GetError(), req.transaction_id, "creating vertices")); break; } } - return msgs::CreateVerticesResponse{.success = action_successful}; + return msgs::CreateVerticesResponse{std::move(shard_error)}; } msgs::WriteResponses ShardRsm::ApplyWrite(msgs::UpdateVerticesRequest &&req) { auto acc = shard_->Access(req.transaction_id); - bool action_successful = true; - - for (auto &vertex : req.new_properties) { - if (!action_successful) { + std::optional shard_error; + for (auto &vertex : req.update_vertices) { + auto vertex_to_update = acc.FindVertex(ConvertPropertyVector(std::move(vertex.primary_key)), View::OLD); + if (!vertex_to_update) { + shard_error.emplace(msgs::ShardError{common::ErrorCode::OBJECT_NOT_FOUND}); + spdlog::debug("In transaction {} vertex could not be found while trying to update its properties.", + req.transaction_id.logical_id); break; } - auto vertex_to_update = acc.FindVertex(ConvertPropertyVector(std::move(vertex.primary_key)), View::OLD); - if (!vertex_to_update) { - action_successful = false; - spdlog::debug("Vertex could not be found while trying to update its properties. Transaction id: {}", - req.transaction_id.logical_id); - continue; + for (const auto label : vertex.add_labels) { + if (const auto maybe_error = vertex_to_update->AddLabelAndValidate(label); maybe_error.HasError()) { + shard_error.emplace(CreateErrorResponse(maybe_error.GetError(), req.transaction_id, "adding label")); + break; + } + } + for (const auto label : vertex.remove_labels) { + if (const auto maybe_error = vertex_to_update->RemoveLabelAndValidate(label); maybe_error.HasError()) { + shard_error.emplace(CreateErrorResponse(maybe_error.GetError(), req.transaction_id, "adding label")); + break; + } } for (auto &update_prop : vertex.property_updates) { - auto result_schema = - vertex_to_update->SetPropertyAndValidate(update_prop.first, ToPropertyValue(std::move(update_prop.second))); - if (result_schema.HasError()) { - auto &error = result_schema.GetError(); - - std::visit( - [](T &&error) { - using ErrorType = std::remove_cvref_t; - if constexpr (std::is_same_v) { - spdlog::debug("Updating vertex failed with error: SchemaViolation"); - } else if constexpr (std::is_same_v) { - switch (error) { - case Error::DELETED_OBJECT: - spdlog::debug("Updating vertex failed with error: DELETED_OBJECT"); - break; - case Error::NONEXISTENT_OBJECT: - spdlog::debug("Updating vertex failed with error: NONEXISTENT_OBJECT"); - break; - case Error::SERIALIZATION_ERROR: - spdlog::debug("Updating vertex failed with error: SERIALIZATION_ERROR"); - break; - case Error::PROPERTIES_DISABLED: - spdlog::debug("Updating vertex failed with error: PROPERTIES_DISABLED"); - break; - case Error::VERTEX_HAS_EDGES: - spdlog::debug("Updating vertex failed with error: VERTEX_HAS_EDGES"); - break; - case Error::VERTEX_ALREADY_INSERTED: - spdlog::debug("Updating vertex failed with error: VERTEX_ALREADY_INSERTED"); - break; - } - } else { - static_assert(kAlwaysFalse, "Missing type from variant visitor"); - } - }, - error); - - action_successful = false; - + if (const auto result_schema = vertex_to_update->SetPropertyAndValidate( + update_prop.first, ToPropertyValue(std::move(update_prop.second))); + result_schema.HasError()) { + shard_error.emplace(CreateErrorResponse(result_schema.GetError(), req.transaction_id, "adding label")); break; } } } - return msgs::UpdateVerticesResponse{.success = action_successful}; + return msgs::UpdateVerticesResponse{std::move(shard_error)}; } msgs::WriteResponses ShardRsm::ApplyWrite(msgs::DeleteVerticesRequest &&req) { - bool action_successful = true; + std::optional shard_error; auto acc = shard_->Access(req.transaction_id); for (auto &propval : req.primary_keys) { - if (!action_successful) { - break; - } - auto vertex_acc = acc.FindVertex(ConvertPropertyVector(std::move(propval)), View::OLD); if (!vertex_acc) { - spdlog::debug("Error while trying to delete vertex. Vertex to delete does not exist. Transaction id: {}", + shard_error.emplace(msgs::ShardError{common::ErrorCode::OBJECT_NOT_FOUND}); + spdlog::debug("In transaction {} vertex could not be found while trying to delete it.", req.transaction_id.logical_id); - action_successful = false; - } else { - // TODO(gvolfing) - // Since we will not have different kinds of deletion types in one transaction, - // we dont have to enter the switch statement on every iteration. Optimize this. - switch (req.deletion_type) { - case msgs::DeleteVerticesRequest::DeletionType::DELETE: { - auto result = acc.DeleteVertex(&vertex_acc.value()); - if (result.HasError() || !(result.GetValue().has_value())) { - action_successful = false; - spdlog::debug("Error while trying to delete vertex. Transaction id: {}", req.transaction_id.logical_id); - } - - break; - } - case msgs::DeleteVerticesRequest::DeletionType::DETACH_DELETE: { - auto result = acc.DetachDeleteVertex(&vertex_acc.value()); - if (result.HasError() || !(result.GetValue().has_value())) { - action_successful = false; - spdlog::debug("Error while trying to detach and delete vertex. Transaction id: {}", - req.transaction_id.logical_id); - } - - break; + break; + } + // TODO(gvolfing) + // Since we will not have different kinds of deletion types in one transaction, + // we dont have to enter the switch statement on every iteration. Optimize this. + switch (req.deletion_type) { + case msgs::DeleteVerticesRequest::DeletionType::DELETE: { + auto result = acc.DeleteVertex(&vertex_acc.value()); + if (result.HasError() || !(result.GetValue().has_value())) { + shard_error.emplace(CreateErrorResponse(result.GetError(), req.transaction_id, "deleting vertices")); } + break; } + case msgs::DeleteVerticesRequest::DeletionType::DETACH_DELETE: { + auto result = acc.DetachDeleteVertex(&vertex_acc.value()); + if (result.HasError() || !(result.GetValue().has_value())) { + shard_error.emplace(CreateErrorResponse(result.GetError(), req.transaction_id, "deleting vertices")); + } + break; + } + } + if (shard_error) { + break; } } - return msgs::DeleteVerticesResponse{.success = action_successful}; + return msgs::DeleteVerticesResponse{std::move(shard_error)}; } msgs::WriteResponses ShardRsm::ApplyWrite(msgs::CreateExpandRequest &&req) { auto acc = shard_->Access(req.transaction_id); - bool action_successful = true; + std::optional shard_error; for (auto &new_expand : req.new_expands) { const auto from_vertex_id = @@ -658,7 +187,8 @@ msgs::WriteResponses ShardRsm::ApplyWrite(msgs::CreateExpandRequest &&req) { VertexId{new_expand.dest_vertex.first.id, ConvertPropertyVector(std::move(new_expand.dest_vertex.second))}; if (!(shard_->IsVertexBelongToShard(from_vertex_id) || shard_->IsVertexBelongToShard(to_vertex_id))) { - action_successful = false; + shard_error = msgs::ShardError{common::ErrorCode::OBJECT_NOT_FOUND, + "Error while trying to insert edge, none of the vertices belong to this shard"}; spdlog::debug("Error while trying to insert edge, none of the vertices belong to this shard. Transaction id: {}", req.transaction_id.logical_id); break; @@ -670,18 +200,18 @@ msgs::WriteResponses ShardRsm::ApplyWrite(msgs::CreateExpandRequest &&req) { if (!new_expand.properties.empty()) { for (const auto &[property, value] : new_expand.properties) { if (const auto maybe_error = edge.SetProperty(property, ToPropertyValue(value)); maybe_error.HasError()) { - action_successful = false; - spdlog::debug("Setting edge property was not successful. Transaction id: {}", - req.transaction_id.logical_id); - break; - } - if (!action_successful) { + shard_error.emplace( + CreateErrorResponse(maybe_error.GetError(), req.transaction_id, "setting edge property")); break; } } + if (shard_error) { + break; + } } } else { - action_successful = false; + // TODO Code for this + shard_error = msgs::ShardError{common::ErrorCode::OBJECT_NOT_FOUND}; spdlog::debug("Creating edge was not successful. Transaction id: {}", req.transaction_id.logical_id); break; } @@ -691,24 +221,22 @@ msgs::WriteResponses ShardRsm::ApplyWrite(msgs::CreateExpandRequest &&req) { for (auto &[edge_prop_key, edge_prop_val] : new_expand.properties) { auto set_result = edge_acc->SetProperty(edge_prop_key, ToPropertyValue(std::move(edge_prop_val))); if (set_result.HasError()) { - action_successful = false; - spdlog::debug("Adding property to edge was not successful. Transaction id: {}", - req.transaction_id.logical_id); + shard_error.emplace(CreateErrorResponse(set_result.GetError(), req.transaction_id, "adding edge property")); break; } } } } - return msgs::CreateExpandResponse{.success = action_successful}; + return msgs::CreateExpandResponse{std::move(shard_error)}; } msgs::WriteResponses ShardRsm::ApplyWrite(msgs::DeleteEdgesRequest &&req) { - bool action_successful = true; + std::optional shard_error; auto acc = shard_->Access(req.transaction_id); for (auto &edge : req.edges) { - if (!action_successful) { + if (shard_error) { break; } @@ -716,41 +244,38 @@ msgs::WriteResponses ShardRsm::ApplyWrite(msgs::DeleteEdgesRequest &&req) { VertexId(edge.dst.first.id, ConvertPropertyVector(std::move(edge.dst.second))), Gid::FromUint(edge.id.gid)); if (edge_acc.HasError() || !edge_acc.HasValue()) { - spdlog::debug("Error while trying to delete edge. Transaction id: {}", req.transaction_id.logical_id); - action_successful = false; + shard_error.emplace(CreateErrorResponse(edge_acc.GetError(), req.transaction_id, "delete edge")); continue; } } - return msgs::DeleteEdgesResponse{.success = action_successful}; + return msgs::DeleteEdgesResponse{std::move(shard_error)}; } msgs::WriteResponses ShardRsm::ApplyWrite(msgs::UpdateEdgesRequest &&req) { // TODO(antaljanosbenjamin): handle when the vertex is the destination vertex auto acc = shard_->Access(req.transaction_id); - bool action_successful = true; + std::optional shard_error; for (auto &edge : req.new_properties) { - if (!action_successful) { + if (shard_error) { break; } auto vertex_acc = acc.FindVertex(ConvertPropertyVector(std::move(edge.src.second)), View::OLD); if (!vertex_acc) { - action_successful = false; + shard_error = msgs::ShardError{common::ErrorCode::OBJECT_NOT_FOUND, "Source vertex was not found"}; spdlog::debug("Encountered an error while trying to acquire VertexAccessor with transaction id: {}", req.transaction_id.logical_id); continue; } - // Since we are using the source vertex of the edge we are only intrested + // Since we are using the source vertex of the edge we are only interested // in the vertex's out-going edges auto edges_res = vertex_acc->OutEdges(View::OLD); if (edges_res.HasError()) { - action_successful = false; - spdlog::debug("Encountered an error while trying to acquire EdgeAccessor with transaction id: {}", - req.transaction_id.logical_id); + shard_error.emplace(CreateErrorResponse(edges_res.GetError(), req.transaction_id, "update edge")); continue; } @@ -766,27 +291,28 @@ msgs::WriteResponses ShardRsm::ApplyWrite(msgs::UpdateEdgesRequest &&req) { // Check if the property was set if SetProperty does not do that itself. auto res = edge_accessor.SetProperty(key, ToPropertyValue(std::move(value))); if (res.HasError()) { - spdlog::debug("Encountered an error while trying to set the property of an Edge with transaction id: {}", - req.transaction_id.logical_id); + // TODO(jbajic) why not set action unsuccessful here? + shard_error.emplace(CreateErrorResponse(edges_res.GetError(), req.transaction_id, "update edge")); } } } } if (!edge_accessor_did_match) { - action_successful = false; + // TODO(jbajic) Do we need this + shard_error = msgs::ShardError{common::ErrorCode::OBJECT_NOT_FOUND, "Edge was not found"}; spdlog::debug("Could not find the Edge with the specified Gid. Transaction id: {}", req.transaction_id.logical_id); continue; } } - return msgs::UpdateEdgesResponse{.success = action_successful}; + return msgs::UpdateEdgesResponse{std::move(shard_error)}; } msgs::ReadResponses ShardRsm::HandleRead(msgs::ScanVerticesRequest &&req) { auto acc = shard_->Access(req.transaction_id); - bool action_successful = true; + std::optional shard_error; std::vector results; if (req.batch_limit) { @@ -811,24 +337,24 @@ msgs::ReadResponses ShardRsm::HandleRead(msgs::ScanVerticesRequest &&req) { EvaluateVertexExpressions(dba, vertex, req.vertex_expressions, expr::identifier_node_symbol)); } - std::optional> found_props; - - if (req.props_to_return) { - found_props = CollectSpecificPropertiesFromAccessor(vertex, req.props_to_return.value(), view); - } else { + auto found_props = std::invoke([&]() { + if (req.props_to_return) { + return CollectSpecificPropertiesFromAccessor(vertex, req.props_to_return.value(), view); + } const auto *schema = shard_->GetSchema(shard_->PrimaryLabel()); - found_props = CollectAllPropertiesFromAccessor(vertex, view, schema); - } + MG_ASSERT(schema); + return CollectAllPropertiesFromAccessor(vertex, view, *schema); + }); // TODO(gvolfing) -VERIFY- // Vertex is separated from the properties in the response. // Is it useful to return just a vertex without the properties? - if (!found_props) { - action_successful = false; + if (found_props.HasError()) { + shard_error = msgs::ShardError{common::ErrorCode::OBJECT_NOT_FOUND, "Requested properties were not found!"}; } results.emplace_back(msgs::ScanResultRow{.vertex = ConstructValueVertex(vertex, view).vertex_v, - .props = FromMap(found_props.value()), + .props = FromMap(found_props.GetValue()), .evaluated_vertex_expressions = std::move(expression_results)}); }; @@ -836,18 +362,18 @@ msgs::ReadResponses ShardRsm::HandleRead(msgs::ScanVerticesRequest &&req) { uint64_t sample_counter{0}; auto vertex_iterable = acc.Vertices(view); if (!req.order_bys.empty()) { - const auto ordered = OrderByElements(acc, dba, vertex_iterable, req.order_bys); + const auto ordered = OrderByVertices(dba, vertex_iterable, req.order_bys); // we are traversing Elements auto it = GetStartOrderedElementsIterator(ordered, start_id, View(req.storage_view)); for (; it != ordered.end(); ++it) { - emplace_scan_result(it->vertex_acc); + emplace_scan_result(it->object_acc); ++sample_counter; if (req.batch_limit && sample_counter == req.batch_limit) { // Reached the maximum specified batch size. // Get the next element before exiting. ++it; if (it != ordered.end()) { - const auto &next_vertex = it->vertex_acc; + const auto &next_vertex = it->object_acc; next_start_id = ConstructValueVertex(next_vertex, view).vertex_v.id; } @@ -872,10 +398,8 @@ msgs::ReadResponses ShardRsm::HandleRead(msgs::ScanVerticesRequest &&req) { } } - msgs::ScanVerticesResponse resp{}; - resp.success = action_successful; - - if (action_successful) { + msgs::ScanVerticesResponse resp{.error = std::move(shard_error)}; + if (!resp.error) { resp.next_start_id = next_start_id; resp.results = std::move(results); } @@ -885,45 +409,96 @@ msgs::ReadResponses ShardRsm::HandleRead(msgs::ScanVerticesRequest &&req) { msgs::ReadResponses ShardRsm::HandleRead(msgs::ExpandOneRequest &&req) { auto acc = shard_->Access(req.transaction_id); - bool action_successful = true; + std::optional shard_error; std::vector results; + const auto batch_limit = req.limit; + auto dba = DbAccessor{&acc}; - auto maybe_filter_based_on_edge_uniquness = InitializeEdgeUniqunessFunction(req.only_unique_neighbor_rows); + auto maybe_filter_based_on_edge_uniqueness = InitializeEdgeUniquenessFunction(req.only_unique_neighbor_rows); auto edge_filler = InitializeEdgeFillerFunction(req); + std::vector vertex_accessors; + vertex_accessors.reserve(req.src_vertices.size()); for (auto &src_vertex : req.src_vertices) { // Get Vertex acc auto src_vertex_acc_opt = acc.FindVertex(ConvertPropertyVector((src_vertex.second)), View::NEW); if (!src_vertex_acc_opt) { - action_successful = false; + shard_error = msgs::ShardError{common::ErrorCode::OBJECT_NOT_FOUND, "Source vertex was not found."}; spdlog::debug("Encountered an error while trying to obtain VertexAccessor. Transaction id: {}", req.transaction_id.logical_id); break; } - if (!req.filters.empty()) { // NOTE - DbAccessor might get removed in the future. - auto dba = DbAccessor{&acc}; const bool eval = FilterOnVertex(dba, src_vertex_acc_opt.value(), req.filters, expr::identifier_node_symbol); if (!eval) { continue; } } - auto result = GetExpandOneResult(acc, src_vertex, req, maybe_filter_based_on_edge_uniquness, edge_filler, - shard_->GetSchema(shard_->PrimaryLabel())); - if (!result) { - action_successful = false; + vertex_accessors.emplace_back(src_vertex_acc_opt.value()); + } + + if (!req.order_by_vertices.empty()) { + // Can we do differently to avoid this? We need OrderByElements but currently it returns vector, so this + // workaround is here to avoid more duplication later + auto local_sorted_vertices = OrderByVertices(dba, vertex_accessors, req.order_by_vertices); + vertex_accessors.clear(); + std::transform(local_sorted_vertices.begin(), local_sorted_vertices.end(), std::back_inserter(vertex_accessors), + [](auto &vertex) { return vertex.object_acc; }); + } + + for (const auto &src_vertex_acc : vertex_accessors) { + auto label_id = src_vertex_acc.PrimaryLabel(View::NEW); + if (label_id.HasError()) { + shard_error.emplace(CreateErrorResponse(label_id.GetError(), req.transaction_id, "getting label")); + } + + auto primary_key = src_vertex_acc.PrimaryKey(View::NEW); + if (primary_key.HasError()) { + shard_error.emplace(CreateErrorResponse(primary_key.GetError(), req.transaction_id, "getting primary key")); break; } - results.emplace_back(result.value()); + msgs::VertexId src_vertex(msgs::Label{.id = *label_id}, conversions::ConvertValueVector(*primary_key)); + + auto maybe_result = std::invoke([&]() { + if (req.order_by_edges.empty()) { + const auto *schema = shard_->GetSchema(shard_->PrimaryLabel()); + MG_ASSERT(schema); + return GetExpandOneResult(acc, src_vertex, req, maybe_filter_based_on_edge_uniqueness, edge_filler, *schema); + } + auto [in_edge_accessors, out_edge_accessors] = GetEdgesFromVertex(src_vertex_acc, req.direction); + const auto in_ordered_edges = OrderByEdges(dba, in_edge_accessors, req.order_by_edges, src_vertex_acc); + const auto out_ordered_edges = OrderByEdges(dba, out_edge_accessors, req.order_by_edges, src_vertex_acc); + + std::vector in_edge_ordered_accessors; + std::transform(in_ordered_edges.begin(), in_ordered_edges.end(), std::back_inserter(in_edge_ordered_accessors), + [](const auto &edge_element) { return edge_element.object_acc; }); + + std::vector out_edge_ordered_accessors; + std::transform(out_ordered_edges.begin(), out_ordered_edges.end(), std::back_inserter(out_edge_ordered_accessors), + [](const auto &edge_element) { return edge_element.object_acc; }); + const auto *schema = shard_->GetSchema(shard_->PrimaryLabel()); + MG_ASSERT(schema); + return GetExpandOneResult(src_vertex_acc, src_vertex, req, in_edge_ordered_accessors, out_edge_ordered_accessors, + maybe_filter_based_on_edge_uniqueness, edge_filler, *schema); + }); + + if (maybe_result.HasError()) { + shard_error.emplace(CreateErrorResponse(primary_key.GetError(), req.transaction_id, "getting primary key")); + break; + } + + results.emplace_back(std::move(maybe_result.GetValue())); + if (batch_limit.has_value() && results.size() >= batch_limit.value()) { + break; + } } - msgs::ExpandOneResponse resp{}; - resp.success = action_successful; - if (action_successful) { + msgs::ExpandOneResponse resp{.error = std::move(shard_error)}; + if (!resp.error) { resp.result = std::move(results); } @@ -932,7 +507,7 @@ msgs::ReadResponses ShardRsm::HandleRead(msgs::ExpandOneRequest &&req) { msgs::WriteResponses ShardRsm::ApplyWrite(msgs::CommitRequest &&req) { shard_->Access(req.transaction_id).Commit(req.commit_timestamp); - return msgs::CommitResponse{true}; + return msgs::CommitResponse{}; }; // NOLINTNEXTLINE(readability-convert-member-functions-to-static) diff --git a/src/storage/v3/shard_rsm.hpp b/src/storage/v3/shard_rsm.hpp index 95cad99fd..d301bf40b 100644 --- a/src/storage/v3/shard_rsm.hpp +++ b/src/storage/v3/shard_rsm.hpp @@ -21,9 +21,6 @@ namespace memgraph::storage::v3 { -template -constexpr auto kAlwaysFalse = false; - class ShardRsm { std::unique_ptr shard_; diff --git a/src/storage/v3/value_conversions.hpp b/src/storage/v3/value_conversions.hpp index 05fd1394b..53374e1ed 100644 --- a/src/storage/v3/value_conversions.hpp +++ b/src/storage/v3/value_conversions.hpp @@ -129,4 +129,27 @@ inline std::vector ConvertValueVector(const std::vector> ConvertPropertyMap( + std::vector> &properties) { + std::vector> ret; + ret.reserve(properties.size()); + + std::transform(std::make_move_iterator(properties.begin()), std::make_move_iterator(properties.end()), + std::back_inserter(ret), [](std::pair &&property) { + return std::make_pair(property.first, ToPropertyValue(std::move(property.second))); + }); + + return ret; +} + +inline std::vector> FromMap(const std::map &properties) { + std::vector> ret; + ret.reserve(properties.size()); + + std::transform(properties.begin(), properties.end(), std::back_inserter(ret), + [](const auto &property) { return std::make_pair(property.first, property.second); }); + + return ret; +} } // namespace memgraph::storage::conversions diff --git a/src/storage/v3/vertex_accessor.cpp b/src/storage/v3/vertex_accessor.cpp index 543caa88a..1b0ba6340 100644 --- a/src/storage/v3/vertex_accessor.cpp +++ b/src/storage/v3/vertex_accessor.cpp @@ -21,8 +21,8 @@ #include "storage/v3/key_store.hpp" #include "storage/v3/mvcc.hpp" #include "storage/v3/property_value.hpp" +#include "storage/v3/result.hpp" #include "storage/v3/shard.hpp" -#include "storage/v3/shard_operation_result.hpp" #include "storage/v3/vertex.hpp" #include "utils/logging.hpp" #include "utils/memory_tracker.hpp" @@ -80,12 +80,12 @@ bool VertexAccessor::IsVisible(View view) const { return exists && (for_deleted_ || !deleted); } -Result VertexAccessor::AddLabel(LabelId label) { +ShardResult VertexAccessor::AddLabel(LabelId label) { utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; - if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR; + if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); - if (vertex_->deleted) return Error::DELETED_OBJECT; + if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end()) return false; @@ -98,15 +98,15 @@ Result VertexAccessor::AddLabel(LabelId label) { return true; } -ShardOperationResult VertexAccessor::AddLabelAndValidate(LabelId label) { - if (const auto maybe_violation_error = vertex_validator_->ValidateAddLabel(label); maybe_violation_error) { - return {*maybe_violation_error}; +ShardResult VertexAccessor::AddLabelAndValidate(LabelId label) { + if (const auto maybe_violation_error = vertex_validator_->ValidateAddLabel(label); maybe_violation_error.HasError()) { + return {maybe_violation_error.GetError()}; } utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; - if (!PrepareForWrite(transaction_, vertex_)) return {Error::SERIALIZATION_ERROR}; + if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); - if (vertex_->deleted) return {Error::DELETED_OBJECT}; + if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end()) return false; @@ -119,10 +119,10 @@ ShardOperationResult VertexAccessor::AddLabelAndValidate(LabelId label) { return true; } -Result VertexAccessor::RemoveLabel(LabelId label) { - if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR; +ShardResult VertexAccessor::RemoveLabel(LabelId label) { + if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); - if (vertex_->deleted) return Error::DELETED_OBJECT; + if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label); if (it == vertex_->labels.end()) return false; @@ -134,14 +134,15 @@ Result VertexAccessor::RemoveLabel(LabelId label) { return true; } -ShardOperationResult VertexAccessor::RemoveLabelAndValidate(LabelId label) { - if (const auto maybe_violation_error = vertex_validator_->ValidateRemoveLabel(label); maybe_violation_error) { - return {*maybe_violation_error}; +ShardResult VertexAccessor::RemoveLabelAndValidate(LabelId label) { + if (const auto maybe_violation_error = vertex_validator_->ValidateRemoveLabel(label); + maybe_violation_error.HasError()) { + return {maybe_violation_error.GetError()}; } - if (!PrepareForWrite(transaction_, vertex_)) return {Error::SERIALIZATION_ERROR}; + if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); - if (vertex_->deleted) return {Error::DELETED_OBJECT}; + if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label); if (it == vertex_->labels.end()) return false; @@ -153,9 +154,9 @@ ShardOperationResult VertexAccessor::RemoveLabelAndValidate(LabelId label) return true; } -Result VertexAccessor::HasLabel(View view, LabelId label) const { return HasLabel(label, view); } +ShardResult VertexAccessor::HasLabel(View view, LabelId label) const { return HasLabel(label, view); } -Result VertexAccessor::HasLabel(LabelId label, View view) const { +ShardResult VertexAccessor::HasLabel(LabelId label, View view) const { bool exists = true; bool deleted = false; bool has_label = false; @@ -197,12 +198,12 @@ Result VertexAccessor::HasLabel(LabelId label, View view) const { break; } }); - if (!exists) return Error::NONEXISTENT_OBJECT; - if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; + if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT); + if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); return has_label; } -Result VertexAccessor::PrimaryLabel(const View view) const { +ShardResult VertexAccessor::PrimaryLabel(const View view) const { if (const auto result = CheckVertexExistence(view); result.HasError()) { return result.GetError(); } @@ -210,21 +211,21 @@ Result VertexAccessor::PrimaryLabel(const View view) const { return vertex_validator_->primary_label_; } -Result VertexAccessor::PrimaryKey(const View view) const { +ShardResult VertexAccessor::PrimaryKey(const View view) const { if (const auto result = CheckVertexExistence(view); result.HasError()) { return result.GetError(); } return vertex_->keys.Keys(); } -Result VertexAccessor::Id(View view) const { +ShardResult VertexAccessor::Id(View view) const { if (const auto result = CheckVertexExistence(view); result.HasError()) { return result.GetError(); } return VertexId{vertex_validator_->primary_label_, vertex_->keys.Keys()}; }; -Result> VertexAccessor::Labels(View view) const { +ShardResult> VertexAccessor::Labels(View view) const { bool exists = true; bool deleted = false; std::vector labels; @@ -267,17 +268,17 @@ Result> VertexAccessor::Labels(View view) const { break; } }); - if (!exists) return Error::NONEXISTENT_OBJECT; - if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; + if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT); + if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); return std::move(labels); } -Result VertexAccessor::SetProperty(PropertyId property, const PropertyValue &value) { +ShardResult VertexAccessor::SetProperty(PropertyId property, const PropertyValue &value) { utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; - if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR; + if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); - if (vertex_->deleted) return Error::DELETED_OBJECT; + if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); auto current_value = vertex_->properties.GetProperty(property); // We could skip setting the value if the previous one is the same to the new @@ -294,7 +295,7 @@ Result VertexAccessor::SetProperty(PropertyId property, const Pro return std::move(current_value); } -Result VertexAccessor::CheckVertexExistence(View view) const { +ShardResult VertexAccessor::CheckVertexExistence(View view) const { bool exists = true; bool deleted = false; Delta *delta = nullptr; @@ -323,27 +324,27 @@ Result VertexAccessor::CheckVertexExistence(View view) const { } }); if (!exists) { - return Error::NONEXISTENT_OBJECT; + return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT); } if (!for_deleted_ && deleted) { - return Error::DELETED_OBJECT; + return SHARD_ERROR(ErrorCode::DELETED_OBJECT); } return {}; } -ShardOperationResult VertexAccessor::SetPropertyAndValidate(PropertyId property, - const PropertyValue &value) { - if (auto maybe_violation_error = vertex_validator_->ValidatePropertyUpdate(property); maybe_violation_error) { - return {*maybe_violation_error}; +ShardResult VertexAccessor::SetPropertyAndValidate(PropertyId property, const PropertyValue &value) { + if (auto maybe_violation_error = vertex_validator_->ValidatePropertyUpdate(property); + maybe_violation_error.HasError()) { + return {maybe_violation_error.GetError()}; } utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; if (!PrepareForWrite(transaction_, vertex_)) { - return {Error::SERIALIZATION_ERROR}; + return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); } if (vertex_->deleted) { - return {Error::DELETED_OBJECT}; + return SHARD_ERROR(ErrorCode::DELETED_OBJECT); } auto current_value = vertex_->properties.GetProperty(property); @@ -361,10 +362,10 @@ ShardOperationResult VertexAccessor::SetPropertyAndValidate(Prope return std::move(current_value); } -Result> VertexAccessor::ClearProperties() { - if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR; +ShardResult> VertexAccessor::ClearProperties() { + if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR); - if (vertex_->deleted) return Error::DELETED_OBJECT; + if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); auto properties = vertex_->properties.Properties(); for (const auto &property : properties) { @@ -377,7 +378,7 @@ Result> VertexAccessor::ClearProperties() { return std::move(properties); } -Result VertexAccessor::GetProperty(View view, PropertyId property) const { +ShardResult VertexAccessor::GetProperty(View view, PropertyId property) const { return GetProperty(property, view).GetValue(); } @@ -407,7 +408,7 @@ PropertyValue VertexAccessor::GetPropertyValue(PropertyId property, View view) c return value; } -Result VertexAccessor::GetProperty(PropertyId property, View view) const { +ShardResult VertexAccessor::GetProperty(PropertyId property, View view) const { bool exists = true; bool deleted = false; PropertyValue value; @@ -442,12 +443,12 @@ Result VertexAccessor::GetProperty(PropertyId property, View view break; } }); - if (!exists) return Error::NONEXISTENT_OBJECT; - if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; + if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT); + if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); return std::move(value); } -Result> VertexAccessor::Properties(View view) const { +ShardResult> VertexAccessor::Properties(View view) const { bool exists = true; bool deleted = false; std::map properties; @@ -492,13 +493,13 @@ Result> VertexAccessor::Properties(View view break; } }); - if (!exists) return Error::NONEXISTENT_OBJECT; - if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; + if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT); + if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); return std::move(properties); } -Result> VertexAccessor::InEdges(View view, const std::vector &edge_types, - const VertexId *destination_id) const { +ShardResult> VertexAccessor::InEdges(View view, const std::vector &edge_types, + const VertexId *destination_id) const { bool exists = true; bool deleted = false; std::vector in_edges; @@ -564,8 +565,8 @@ Result> VertexAccessor::InEdges(View view, const std:: break; } }); - if (!exists) return Error::NONEXISTENT_OBJECT; - if (deleted) return Error::DELETED_OBJECT; + if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT); + if (deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); std::vector ret; if (in_edges.empty()) { return ret; @@ -579,8 +580,8 @@ Result> VertexAccessor::InEdges(View view, const std:: return ret; } -Result> VertexAccessor::OutEdges(View view, const std::vector &edge_types, - const VertexId *destination_id) const { +ShardResult> VertexAccessor::OutEdges(View view, const std::vector &edge_types, + const VertexId *destination_id) const { bool exists = true; bool deleted = false; std::vector out_edges; @@ -644,8 +645,8 @@ Result> VertexAccessor::OutEdges(View view, const std: break; } }); - if (!exists) return Error::NONEXISTENT_OBJECT; - if (deleted) return Error::DELETED_OBJECT; + if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT); + if (deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); std::vector ret; if (out_edges.empty()) { return ret; @@ -659,7 +660,7 @@ Result> VertexAccessor::OutEdges(View view, const std: return ret; } -Result VertexAccessor::InDegree(View view) const { +ShardResult VertexAccessor::InDegree(View view) const { bool exists = true; bool deleted = false; size_t degree = 0; @@ -691,12 +692,12 @@ Result VertexAccessor::InDegree(View view) const { break; } }); - if (!exists) return Error::NONEXISTENT_OBJECT; - if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; + if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT); + if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); return degree; } -Result VertexAccessor::OutDegree(View view) const { +ShardResult VertexAccessor::OutDegree(View view) const { bool exists = true; bool deleted = false; size_t degree = 0; @@ -728,8 +729,8 @@ Result VertexAccessor::OutDegree(View view) const { break; } }); - if (!exists) return Error::NONEXISTENT_OBJECT; - if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; + if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT); + if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT); return degree; } diff --git a/src/storage/v3/vertex_accessor.hpp b/src/storage/v3/vertex_accessor.hpp index 682a04e39..20ee9955b 100644 --- a/src/storage/v3/vertex_accessor.hpp +++ b/src/storage/v3/vertex_accessor.hpp @@ -17,7 +17,7 @@ #include "storage/v3/id_types.hpp" #include "storage/v3/key_store.hpp" #include "storage/v3/result.hpp" -#include "storage/v3/shard_operation_result.hpp" +#include "storage/v3/schema_validator.hpp" #include "storage/v3/transaction.hpp" #include "storage/v3/vertex.hpp" #include "storage/v3/vertex_id.hpp" @@ -55,61 +55,61 @@ class VertexAccessor final { /// `false` is returned if the label already existed, or SchemaViolation /// if adding the label has violated one of the schema constraints. /// @throw std::bad_alloc - ShardOperationResult AddLabelAndValidate(LabelId label); + ShardResult AddLabelAndValidate(LabelId label); /// Remove a label and return `true` if deletion took place. /// `false` is returned if the vertex did not have a label already. or SchemaViolation /// if adding the label has violated one of the schema constraints. /// @throw std::bad_alloc - ShardOperationResult RemoveLabelAndValidate(LabelId label); + ShardResult RemoveLabelAndValidate(LabelId label); - Result HasLabel(View view, LabelId label) const; + ShardResult HasLabel(View view, LabelId label) const; - Result HasLabel(LabelId label, View view) const; + ShardResult HasLabel(LabelId label, View view) const; /// @throw std::bad_alloc /// @throw std::length_error if the resulting vector exceeds /// std::vector::max_size(). - Result> Labels(View view) const; + ShardResult> Labels(View view) const; - Result PrimaryLabel(View view) const; + ShardResult PrimaryLabel(View view) const; - Result PrimaryKey(View view) const; + ShardResult PrimaryKey(View view) const; - Result Id(View view) const; + ShardResult Id(View view) const; /// Set a property value and return the old value or error. /// @throw std::bad_alloc - ShardOperationResult SetPropertyAndValidate(PropertyId property, const PropertyValue &value); + ShardResult SetPropertyAndValidate(PropertyId property, const PropertyValue &value); /// Remove all properties and return the values of the removed properties. /// @throw std::bad_alloc - Result> ClearProperties(); + ShardResult> ClearProperties(); /// @throw std::bad_alloc - Result GetProperty(PropertyId property, View view) const; + ShardResult GetProperty(PropertyId property, View view) const; // TODO Remove this - Result GetProperty(View view, PropertyId property) const; + ShardResult GetProperty(View view, PropertyId property) const; /// @throw std::bad_alloc - Result> Properties(View view) const; + ShardResult> Properties(View view) const; /// @throw std::bad_alloc /// @throw std::length_error if the resulting vector exceeds /// std::vector::max_size(). - Result> InEdges(View view, const std::vector &edge_types = {}, - const VertexId *destination_id = nullptr) const; + ShardResult> InEdges(View view, const std::vector &edge_types = {}, + const VertexId *destination_id = nullptr) const; /// @throw std::bad_alloc /// @throw std::length_error if the resulting vector exceeds /// std::vector::max_size(). - Result> OutEdges(View view, const std::vector &edge_types = {}, - const VertexId *destination_id = nullptr) const; + ShardResult> OutEdges(View view, const std::vector &edge_types = {}, + const VertexId *destination_id = nullptr) const; - Result InDegree(View view) const; + ShardResult InDegree(View view) const; - Result OutDegree(View view) const; + ShardResult OutDegree(View view) const; const SchemaValidator *GetSchemaValidator() const; @@ -122,20 +122,20 @@ class VertexAccessor final { /// Add a label and return `true` if insertion took place. /// `false` is returned if the label already existed. /// @throw std::bad_alloc - Result AddLabel(LabelId label); + ShardResult AddLabel(LabelId label); /// Remove a label and return `true` if deletion took place. /// `false` is returned if the vertex did not have a label already. /// @throw std::bad_alloc - Result RemoveLabel(LabelId label); + ShardResult RemoveLabel(LabelId label); /// Set a property value and return the old value. /// @throw std::bad_alloc - Result SetProperty(PropertyId property, const PropertyValue &value); + ShardResult SetProperty(PropertyId property, const PropertyValue &value); PropertyValue GetPropertyValue(PropertyId property, View view) const; - Result CheckVertexExistence(View view) const; + ShardResult CheckVertexExistence(View view) const; Vertex *vertex_; Transaction *transaction_; diff --git a/src/storage/v3/shard_operation_result.hpp b/src/utils/template_utils.hpp similarity index 62% rename from src/storage/v3/shard_operation_result.hpp rename to src/utils/template_utils.hpp index 8de800b83..4065283b2 100644 --- a/src/storage/v3/shard_operation_result.hpp +++ b/src/utils/template_utils.hpp @@ -11,16 +11,9 @@ #pragma once -#include +namespace memgraph::utils { -#include "storage/v3/result.hpp" -#include "storage/v3/schema_validator.hpp" +template +constexpr auto kAlwaysFalse{false}; -namespace memgraph::storage::v3 { - -using ResultErrorType = std::variant; - -template -using ShardOperationResult = utils::BasicResult; - -} // namespace memgraph::storage::v3 +} // namespace memgraph::utils diff --git a/tests/simulation/shard_rsm.cpp b/tests/simulation/shard_rsm.cpp index 230bd9222..1577d4e0f 100644 --- a/tests/simulation/shard_rsm.cpp +++ b/tests/simulation/shard_rsm.cpp @@ -137,7 +137,7 @@ void Commit(ShardClient &client, const coordinator::Hlc &transaction_timestamp) auto write_response_result = write_res.GetValue(); auto write_response = std::get(write_response_result); - MG_ASSERT(write_response.success, "Commit expected to be successful, but it is failed"); + MG_ASSERT(!write_response.error.has_value(), "Commit expected to be successful, but it is failed"); break; } @@ -156,7 +156,7 @@ bool AttemptToCreateVertex(ShardClient &client, int64_t value) { create_req.transaction_id.logical_id = GetTransactionId(); auto write_res = client.SendWriteRequest(create_req); - MG_ASSERT(write_res.HasValue() && std::get(write_res.GetValue()).success, + MG_ASSERT(write_res.HasValue() && !std::get(write_res.GetValue()).error.has_value(), "Unexpected failure"); Commit(client, create_req.transaction_id); @@ -179,23 +179,26 @@ bool AttemptToDeleteVertex(ShardClient &client, int64_t value) { auto write_response = std::get(write_response_result); Commit(client, delete_req.transaction_id); - return write_response.success; + return !write_response.error.has_value(); } } -bool AttemptToUpdateVertex(ShardClient &client, int64_t value) { - auto vertex_id = GetValuePrimaryKeysWithValue(value)[0]; +bool AttemptToUpdateVertex(ShardClient &client, int64_t vertex_primary_key, std::vector add_labels = {}, + std::vector remove_labels = {}) { + auto vertex_id = GetValuePrimaryKeysWithValue(vertex_primary_key)[0]; std::vector> property_updates; auto property_update = std::make_pair(PropertyId::FromUint(5), msgs::Value(static_cast(10000))); - auto vertex_prop = msgs::UpdateVertexProp{}; - vertex_prop.primary_key = vertex_id; - vertex_prop.property_updates = {property_update}; + msgs::UpdateVertex update_vertex; + update_vertex.primary_key = vertex_id; + update_vertex.property_updates = {property_update}; + update_vertex.add_labels = add_labels; + update_vertex.remove_labels = remove_labels; - auto update_req = msgs::UpdateVerticesRequest{}; + msgs::UpdateVerticesRequest update_req; update_req.transaction_id.logical_id = GetTransactionId(); - update_req.new_properties = {vertex_prop}; + update_req.update_vertices = {update_vertex}; while (true) { auto write_res = client.SendWriteRequest(update_req); @@ -207,7 +210,38 @@ bool AttemptToUpdateVertex(ShardClient &client, int64_t value) { auto write_response = std::get(write_response_result); Commit(client, update_req.transaction_id); - return write_response.success; + return !write_response.error.has_value(); + } +} + +bool AttemptToRemoveVertexProperty(ShardClient &client, int64_t primary_key, std::vector add_labels = {}, + std::vector remove_labels = {}) { + auto vertex_id = GetValuePrimaryKeysWithValue(primary_key)[0]; + + std::vector> property_updates; + auto property_update = std::make_pair(PropertyId::FromUint(5), msgs::Value()); + + msgs::UpdateVertex update_vertex; + update_vertex.primary_key = vertex_id; + update_vertex.property_updates = {property_update}; + update_vertex.add_labels = add_labels; + update_vertex.remove_labels = remove_labels; + + msgs::UpdateVerticesRequest update_req; + update_req.transaction_id.logical_id = GetTransactionId(); + update_req.update_vertices = {update_vertex}; + + while (true) { + auto write_res = client.SendWriteRequest(update_req); + if (write_res.HasError()) { + continue; + } + + auto write_response_result = write_res.GetValue(); + auto write_response = std::get(write_response_result); + + Commit(client, update_req.transaction_id); + return !write_response.error.has_value(); } } @@ -244,7 +278,7 @@ bool AttemptToAddEdge(ShardClient &client, int64_t value_of_vertex_1, int64_t va Commit(client, create_req.transaction_id); - return write_response.success; + return !write_response.error.has_value(); } return true; } @@ -276,7 +310,7 @@ bool AttemptToAddEdgeWithProperties(ShardClient &client, int64_t value_of_vertex create_req.transaction_id.logical_id = GetTransactionId(); auto write_res = client.SendWriteRequest(create_req); - MG_ASSERT(write_res.HasValue() && std::get(write_res.GetValue()).success, + MG_ASSERT(write_res.HasValue() && !std::get(write_res.GetValue()).error.has_value(), "Unexpected failure"); Commit(client, create_req.transaction_id); @@ -316,7 +350,7 @@ bool AttemptToDeleteEdge(ShardClient &client, int64_t value_of_vertex_1, int64_t auto write_response = std::get(write_response_result); Commit(client, delete_req.transaction_id); - return write_response.success; + return !write_response.error.has_value(); } } @@ -356,7 +390,7 @@ bool AttemptToUpdateEdge(ShardClient &client, int64_t value_of_vertex_1, int64_t auto write_response = std::get(write_response_result); Commit(client, update_req.transaction_id); - return write_response.success; + return !write_response.error.has_value(); } } @@ -379,7 +413,7 @@ std::tuple> AttemptToScanAllWithoutBatchLi auto write_response_result = read_res.GetValue(); auto write_response = std::get(write_response_result); - MG_ASSERT(write_response.success); + MG_ASSERT(write_response.error == std::nullopt); return {write_response.results.size(), write_response.next_start_id}; } @@ -405,7 +439,7 @@ std::tuple> AttemptToScanAllWithBatchLimit auto write_response_result = read_res.GetValue(); auto write_response = std::get(write_response_result); - MG_ASSERT(write_response.success); + MG_ASSERT(!write_response.error.has_value()); return {write_response.results.size(), write_response.next_start_id}; } @@ -439,7 +473,7 @@ std::tuple> AttemptToScanAllWithExpression auto write_response_result = read_res.GetValue(); auto write_response = std::get(write_response_result); - MG_ASSERT(write_response.success); + MG_ASSERT(!write_response.error.has_value()); MG_ASSERT(!write_response.results.empty(), "There are no results!"); MG_ASSERT(write_response.results[0].evaluated_vertex_expressions[0].int_v == 4); return {write_response.results.size(), write_response.next_start_id}; @@ -464,7 +498,7 @@ void AttemptToScanAllWithOrderByOnPrimaryProperty(ShardClient &client, msgs::Ver auto write_response_result = read_res.GetValue(); auto write_response = std::get(write_response_result); - MG_ASSERT(write_response.success); + MG_ASSERT(!write_response.error.has_value()); MG_ASSERT(write_response.results.size() == 5, "Expecting 5 results!"); for (int64_t i{0}; i < 5; ++i) { const auto expected_primary_key = std::vector{msgs::Value(1023 - i)}; @@ -494,7 +528,7 @@ void AttemptToScanAllWithOrderByOnSecondaryProperty(ShardClient &client, msgs::V auto write_response_result = read_res.GetValue(); auto write_response = std::get(write_response_result); - MG_ASSERT(write_response.success); + MG_ASSERT(!write_response.error.has_value()); MG_ASSERT(write_response.results.size() == 5, "Expecting 5 results!"); for (int64_t i{0}; i < 5; ++i) { const auto expected_prop4 = std::vector{msgs::Value(1023 - i)}; @@ -531,7 +565,8 @@ void AttemptToExpandOneWithWrongEdgeType(ShardClient &client, uint64_t src_verte std::optional> edge_properties = {}; std::vector expressions; - std::optional> order_by = {}; + std::vector order_by_vertices = {}; + std::vector order_by_edges = {}; std::optional limit = {}; std::vector filter = {}; @@ -543,7 +578,8 @@ void AttemptToExpandOneWithWrongEdgeType(ShardClient &client, uint64_t src_verte expand_one_req.vertex_expressions = expressions; expand_one_req.filters = filter; expand_one_req.limit = limit; - expand_one_req.order_by = order_by; + expand_one_req.order_by_vertices = order_by_vertices; + expand_one_req.order_by_edges = order_by_edges; expand_one_req.src_vertex_properties = src_vertex_properties; expand_one_req.src_vertices = {src_vertex}; expand_one_req.transaction_id.logical_id = GetTransactionId(); @@ -586,7 +622,8 @@ void AttemptToExpandOneSimple(ShardClient &client, uint64_t src_vertex_val, Edge std::optional> edge_properties = {}; std::vector expressions; - std::optional> order_by = {}; + std::vector order_by_vertices = {}; + std::vector order_by_edges = {}; std::optional limit = {}; std::vector filter = {}; @@ -598,7 +635,8 @@ void AttemptToExpandOneSimple(ShardClient &client, uint64_t src_vertex_val, Edge expand_one_req.vertex_expressions = expressions; expand_one_req.filters = filter; expand_one_req.limit = limit; - expand_one_req.order_by = order_by; + expand_one_req.order_by_vertices = order_by_vertices; + expand_one_req.order_by_edges = order_by_edges; expand_one_req.src_vertex_properties = src_vertex_properties; expand_one_req.src_vertices = {src_vertex}; expand_one_req.transaction_id.logical_id = GetTransactionId(); @@ -642,7 +680,8 @@ void AttemptToExpandOneWithUniqueEdges(ShardClient &client, uint64_t src_vertex_ std::optional> edge_properties = {}; std::vector expressions; - std::optional> order_by = {}; + std::vector order_by_vertices = {}; + std::vector order_by_edges = {}; std::optional limit = {}; std::vector filter = {}; @@ -654,7 +693,8 @@ void AttemptToExpandOneWithUniqueEdges(ShardClient &client, uint64_t src_vertex_ expand_one_req.vertex_expressions = expressions; expand_one_req.filters = filter; expand_one_req.limit = limit; - expand_one_req.order_by = order_by; + expand_one_req.order_by_vertices = order_by_vertices; + expand_one_req.order_by_edges = order_by_edges; expand_one_req.src_vertex_properties = src_vertex_properties; expand_one_req.src_vertices = {src_vertex}; expand_one_req.only_unique_neighbor_rows = true; @@ -680,6 +720,88 @@ void AttemptToExpandOneWithUniqueEdges(ShardClient &client, uint64_t src_vertex_ } } +void AttemptToExpandOneLimitAndOrderBy(ShardClient &client, uint64_t src_vertex_val, uint64_t other_src_vertex_val, + EdgeTypeId edge_type_id) { + // Source vertex + msgs::Label label = {.id = get_primary_label()}; + auto src_vertex = std::make_pair(label, GetPrimaryKey(src_vertex_val)); + auto other_src_vertex = std::make_pair(label, GetPrimaryKey(other_src_vertex_val)); + + // Edge type + auto edge_type = msgs::EdgeType{}; + edge_type.id = edge_type_id; + + // Edge direction + auto edge_direction = msgs::EdgeDirection::OUT; + + // Source Vertex properties to look for + std::optional> src_vertex_properties = {}; + + // Edge properties to look for + std::optional> edge_properties = {}; + + std::vector order_by_vertices = { + {msgs::Expression{"MG_SYMBOL_NODE.prop1"}, msgs::OrderingDirection::ASCENDING}}; + std::vector order_by_edges = { + {msgs::Expression{"MG_SYMBOL_EDGE.prop4"}, msgs::OrderingDirection::DESCENDING}}; + + size_t limit = 1; + std::vector filters = {"MG_SYMBOL_NODE.prop1 != -1"}; + + msgs::ExpandOneRequest expand_one_req{}; + + expand_one_req.direction = edge_direction; + expand_one_req.edge_properties = edge_properties; + expand_one_req.edge_types = {edge_type}; + expand_one_req.filters = filters; + expand_one_req.limit = limit; + expand_one_req.order_by_vertices = order_by_vertices; + expand_one_req.order_by_edges = order_by_edges; + expand_one_req.src_vertex_properties = src_vertex_properties; + expand_one_req.src_vertices = {src_vertex, other_src_vertex}; + expand_one_req.transaction_id.logical_id = GetTransactionId(); + + while (true) { + auto read_res = client.SendReadRequest(expand_one_req); + if (read_res.HasError()) { + continue; + } + + auto write_response_result = read_res.GetValue(); + auto write_response = std::get(write_response_result); + + // We check that we do not have more results than the limit. Based on the data in the graph, we know that we should + // receive exactly limit responses. + auto expected_number_of_rows = std::min(expand_one_req.src_vertices.size(), limit); + MG_ASSERT(expected_number_of_rows == 1); + MG_ASSERT(write_response.result.size() == expected_number_of_rows); + + // We know there are 1 out-going edges from V1->V2 + // We know there are 10 out-going edges from V2->V3 + // Since we sort on prop1 and limit 1, we will have a single response + // with two edges corresponding to V1->V2 and V1->V3 + const auto expected_number_of_edges = 2; + MG_ASSERT(write_response.result[0].out_edges_with_all_properties.size() == expected_number_of_edges); + MG_ASSERT(write_response.result[0] + .out_edges_with_specific_properties.empty()); // We are not asking for specific properties + + // We also check that the vertices are ordered by prop1 DESC + + auto is_sorted = std::is_sorted(write_response.result.cbegin(), write_response.result.cend(), + [](const auto &vertex, const auto &other_vertex) { + const auto primary_key = vertex.src_vertex.id.second; + const auto other_primary_key = other_vertex.src_vertex.id.second; + + MG_ASSERT(primary_key.size() == 1); + MG_ASSERT(other_primary_key.size() == 1); + return primary_key[0].int_v > other_primary_key[0].int_v; + }); + + MG_ASSERT(is_sorted); + break; + } +} + void AttemptToExpandOneWithSpecifiedSrcVertexProperties(ShardClient &client, uint64_t src_vertex_val, EdgeTypeId edge_type_id) { // Source vertex @@ -701,7 +823,8 @@ void AttemptToExpandOneWithSpecifiedSrcVertexProperties(ShardClient &client, uin std::optional> edge_properties = {}; std::vector expressions; - std::optional> order_by = {}; + std::vector order_by_vertices = {}; + std::vector order_by_edges = {}; std::optional limit = {}; std::vector filter = {}; @@ -713,7 +836,8 @@ void AttemptToExpandOneWithSpecifiedSrcVertexProperties(ShardClient &client, uin expand_one_req.vertex_expressions = expressions; expand_one_req.filters = filter; expand_one_req.limit = limit; - expand_one_req.order_by = order_by; + expand_one_req.order_by_vertices = order_by_vertices; + expand_one_req.order_by_edges = order_by_edges; expand_one_req.src_vertex_properties = src_vertex_properties; expand_one_req.src_vertices = {src_vertex}; expand_one_req.transaction_id.logical_id = GetTransactionId(); @@ -761,7 +885,8 @@ void AttemptToExpandOneWithSpecifiedEdgeProperties(ShardClient &client, uint64_t std::optional> edge_properties = {specified_edge_prop}; std::vector expressions; - std::optional> order_by = {}; + std::vector order_by_vertices = {}; + std::vector order_by_edges = {}; std::optional limit = {}; std::vector filter = {}; @@ -773,7 +898,8 @@ void AttemptToExpandOneWithSpecifiedEdgeProperties(ShardClient &client, uint64_t expand_one_req.vertex_expressions = expressions; expand_one_req.filters = filter; expand_one_req.limit = limit; - expand_one_req.order_by = order_by; + expand_one_req.order_by_vertices = order_by_vertices; + expand_one_req.order_by_edges = order_by_edges; expand_one_req.src_vertex_properties = src_vertex_properties; expand_one_req.src_vertices = {src_vertex}; expand_one_req.transaction_id.logical_id = GetTransactionId(); @@ -820,7 +946,8 @@ void AttemptToExpandOneWithFilters(ShardClient &client, uint64_t src_vertex_val, std::optional> edge_properties = {}; std::vector expressions; - std::optional> order_by = {}; + std::vector order_by_vertices = {}; + std::vector order_by_edges = {}; std::optional limit = {}; std::vector filter = {}; @@ -832,7 +959,8 @@ void AttemptToExpandOneWithFilters(ShardClient &client, uint64_t src_vertex_val, expand_one_req.vertex_expressions = expressions; expand_one_req.filters = {filter_expr1}; expand_one_req.limit = limit; - expand_one_req.order_by = order_by; + expand_one_req.order_by_vertices = order_by_vertices; + expand_one_req.order_by_edges = order_by_edges; expand_one_req.src_vertex_properties = src_vertex_properties; expand_one_req.src_vertices = {src_vertex}; expand_one_req.transaction_id.logical_id = GetTransactionId(); @@ -872,7 +1000,9 @@ void TestCreateAndUpdateVertices(ShardClient &client) { auto unique_prop_val = GetUniqueInteger(); MG_ASSERT(AttemptToCreateVertex(client, unique_prop_val)); - MG_ASSERT(AttemptToUpdateVertex(client, unique_prop_val)); + MG_ASSERT(AttemptToUpdateVertex(client, unique_prop_val, {LabelId::FromInt(3)}, {})); + MG_ASSERT(AttemptToUpdateVertex(client, unique_prop_val, {}, {LabelId::FromInt(3)})); + MG_ASSERT(AttemptToRemoveVertexProperty(client, unique_prop_val)); } void TestCreateEdge(ShardClient &client) { @@ -1021,6 +1151,9 @@ void TestExpandOneGraphOne(ShardClient &client) { auto edge_prop_id = GetUniqueInteger(); auto edge_prop_val = GetUniqueInteger(); + std::vector edges_ids(10); + std::generate(edges_ids.begin(), edges_ids.end(), GetUniqueInteger); + // (V1)-[edge_type_id]->(V2) MG_ASSERT(AttemptToAddEdgeWithProperties(client, unique_prop_val_1, unique_prop_val_2, edge_gid_1, edge_prop_id, edge_prop_val, {edge_type_id})); @@ -1028,7 +1161,14 @@ void TestExpandOneGraphOne(ShardClient &client) { MG_ASSERT(AttemptToAddEdgeWithProperties(client, unique_prop_val_1, unique_prop_val_3, edge_gid_2, edge_prop_id, edge_prop_val, {edge_type_id})); + // (V2)-[edge_type_id]->(V3) x 10 + std::for_each(edges_ids.begin(), edges_ids.end(), [&](const auto &edge_id) { + MG_ASSERT(AttemptToAddEdgeWithProperties(client, unique_prop_val_2, unique_prop_val_3, edge_id, edge_prop_id, + edge_prop_val, {edge_type_id})); + }); + AttemptToExpandOneSimple(client, unique_prop_val_1, edge_type_id); + AttemptToExpandOneLimitAndOrderBy(client, unique_prop_val_1, unique_prop_val_2, edge_type_id); AttemptToExpandOneWithWrongEdgeType(client, unique_prop_val_1, wrong_edge_type_id); AttemptToExpandOneWithSpecifiedSrcVertexProperties(client, unique_prop_val_1, edge_type_id); AttemptToExpandOneWithSpecifiedEdgeProperties(client, unique_prop_val_1, edge_type_id, edge_prop_id); diff --git a/tests/simulation/test_cluster.hpp b/tests/simulation/test_cluster.hpp index 0ac7ca1da..096009b7a 100644 --- a/tests/simulation/test_cluster.hpp +++ b/tests/simulation/test_cluster.hpp @@ -177,7 +177,7 @@ void ExecuteOp(msgs::ShardRequestManager &shard_request_mana auto result = shard_request_manager.Request(state, std::move(new_vertices)); RC_ASSERT(result.size() == 1); - RC_ASSERT(result[0].success); + RC_ASSERT(!result[0].error.has_value()); correctness_model.emplace(std::make_pair(create_vertex.first, create_vertex.second)); } diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 2f16ab8c2..188c6c1b0 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -343,6 +343,9 @@ target_link_libraries(${test_prefix}storage_v3_edge mg-storage-v3) add_unit_test(storage_v3_isolation_level.cpp) target_link_libraries(${test_prefix}storage_v3_isolation_level mg-storage-v3) +add_unit_test(storage_v3_shard_rsm.cpp) +target_link_libraries(${test_prefix}storage_v3_shard_rsm mg-storage-v3) + add_unit_test(replication_persistence_helper.cpp) target_link_libraries(${test_prefix}replication_persistence_helper mg-storage-v2) diff --git a/tests/unit/high_density_shard_create_scan.cpp b/tests/unit/high_density_shard_create_scan.cpp index 9c2d1cfd7..a0c0a0c28 100644 --- a/tests/unit/high_density_shard_create_scan.cpp +++ b/tests/unit/high_density_shard_create_scan.cpp @@ -187,7 +187,7 @@ void ExecuteOp(msgs::ShardRequestManager &shard_request_manager, auto result = shard_request_manager.Request(state, std::move(new_vertices)); MG_ASSERT(result.size() == 1); - MG_ASSERT(result[0].success); + MG_ASSERT(!result[0].error.has_value()); correctness_model.emplace(std::make_pair(create_vertex.first, create_vertex.second)); } diff --git a/tests/unit/machine_manager.cpp b/tests/unit/machine_manager.cpp index 1d69da5c5..110220eda 100644 --- a/tests/unit/machine_manager.cpp +++ b/tests/unit/machine_manager.cpp @@ -131,6 +131,7 @@ void TestCreateVertices(msgs::ShardRequestManagerInterface &shard_request_manage auto result = shard_request_manager.Request(state, std::move(new_vertices)); EXPECT_EQ(result.size(), 1); + EXPECT_FALSE(result[0].error.has_value()) << result[0].error->message; } void TestCreateExpand(msgs::ShardRequestManagerInterface &shard_request_manager) { @@ -151,7 +152,7 @@ void TestCreateExpand(msgs::ShardRequestManagerInterface &shard_request_manager) auto responses = shard_request_manager.Request(state, std::move(new_expands)); MG_ASSERT(responses.size() == 1); - MG_ASSERT(responses[0].success); + MG_ASSERT(!responses[0].error.has_value()); } void TestExpandOne(msgs::ShardRequestManagerInterface &shard_request_manager) { diff --git a/tests/unit/storage_v3.cpp b/tests/unit/storage_v3.cpp index b107786e8..6f6902d1c 100644 --- a/tests/unit/storage_v3.cpp +++ b/tests/unit/storage_v3.cpp @@ -17,6 +17,7 @@ #include #include +#include "common/errors.hpp" #include "coordinator/hybrid_logical_clock.hpp" #include "io/time.hpp" #include "storage/v3/delta.hpp" @@ -579,7 +580,7 @@ TEST_P(StorageV3, VertexDeleteSerializationError) { EXPECT_EQ(CountVertices(acc2, View::NEW), 1U); auto res = acc2.DeleteVertex(&*vertex); ASSERT_TRUE(res.HasError()); - ASSERT_EQ(res.GetError(), Error::SERIALIZATION_ERROR); + ASSERT_EQ(res.GetError(), SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR)); EXPECT_EQ(CountVertices(acc2, View::OLD), 1U); EXPECT_EQ(CountVertices(acc2, View::NEW), 1U); acc2.AdvanceCommand(); @@ -660,12 +661,11 @@ TEST_P(StorageV3, VertexDeleteSpecialCases) { } } -template -void AssertErrorInVariant(TResultHolder &holder, TError error_type) { - ASSERT_TRUE(holder.HasError()); - const auto error = holder.GetError(); - ASSERT_TRUE(std::holds_alternative(error)); - ASSERT_EQ(std::get(error), error_type); +template +void AssertShardErrorEqual(const ShardResult &lhs, const ShardError &rhs) { + ASSERT_TRUE(lhs.HasError()); + const auto error = lhs.GetError(); + ASSERT_EQ(error, rhs); } // NOLINTNEXTLINE(hicpp-special-member-functions) @@ -711,20 +711,20 @@ TEST_P(StorageV3, VertexDeleteLabel) { // Check whether label 5 exists ASSERT_FALSE(vertex->HasLabel(label5, View::OLD).GetValue()); - ASSERT_EQ(vertex->HasLabel(label5, View::NEW).GetError(), Error::DELETED_OBJECT); + ASSERT_EQ(vertex->HasLabel(label5, View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); ASSERT_EQ(vertex->Labels(View::OLD)->size(), 0); - ASSERT_EQ(vertex->Labels(View::NEW).GetError(), Error::DELETED_OBJECT); + ASSERT_EQ(vertex->Labels(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); // Try to add the label { auto ret = vertex->AddLabelAndValidate(label5); - AssertErrorInVariant(ret, Error::DELETED_OBJECT); + AssertShardErrorEqual(ret, SHARD_ERROR(ErrorCode::DELETED_OBJECT)); } // Try to remove the label { auto ret = vertex->RemoveLabelAndValidate(label5); - AssertErrorInVariant(ret, Error::DELETED_OBJECT); + AssertShardErrorEqual(ret, SHARD_ERROR(ErrorCode::DELETED_OBJECT)); } acc.Abort(); @@ -779,33 +779,33 @@ TEST_P(StorageV3, VertexDeleteLabel) { // Check whether label 5 exists ASSERT_TRUE(vertex->HasLabel(label5, View::OLD).GetValue()); - ASSERT_EQ(vertex->HasLabel(label5, View::NEW).GetError(), Error::DELETED_OBJECT); + ASSERT_EQ(vertex->HasLabel(label5, View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); { auto labels = vertex->Labels(View::OLD).GetValue(); ASSERT_EQ(labels.size(), 1); ASSERT_EQ(labels[0], label5); } - ASSERT_EQ(vertex->Labels(View::NEW).GetError(), Error::DELETED_OBJECT); + ASSERT_EQ(vertex->Labels(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); // Advance command acc.AdvanceCommand(); // Check whether label 5 exists - ASSERT_EQ(vertex->HasLabel(label5, View::OLD).GetError(), Error::DELETED_OBJECT); - ASSERT_EQ(vertex->HasLabel(label5, View::NEW).GetError(), Error::DELETED_OBJECT); - ASSERT_EQ(vertex->Labels(View::OLD).GetError(), Error::DELETED_OBJECT); - ASSERT_EQ(vertex->Labels(View::NEW).GetError(), Error::DELETED_OBJECT); + ASSERT_EQ(vertex->HasLabel(label5, View::OLD).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); + ASSERT_EQ(vertex->HasLabel(label5, View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); + ASSERT_EQ(vertex->Labels(View::OLD).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); + ASSERT_EQ(vertex->Labels(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); // Try to add the label { auto ret = vertex->AddLabelAndValidate(label5); - AssertErrorInVariant(ret, Error::DELETED_OBJECT); + AssertShardErrorEqual(ret, SHARD_ERROR(ErrorCode::DELETED_OBJECT)); } // Try to remove the label { auto ret = vertex->RemoveLabelAndValidate(label5); - AssertErrorInVariant(ret, Error::DELETED_OBJECT); + AssertShardErrorEqual(ret, SHARD_ERROR(ErrorCode::DELETED_OBJECT)); } acc.Abort(); @@ -855,14 +855,14 @@ TEST_P(StorageV3, VertexDeleteProperty) { // Check whether label 5 exists ASSERT_TRUE(vertex->GetProperty(property5, View::OLD)->IsNull()); - ASSERT_EQ(vertex->GetProperty(property5, View::NEW).GetError(), Error::DELETED_OBJECT); + ASSERT_EQ(vertex->GetProperty(property5, View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); ASSERT_EQ(vertex->Properties(View::OLD)->size(), 0); - ASSERT_EQ(vertex->Properties(View::NEW).GetError(), Error::DELETED_OBJECT); + ASSERT_EQ(vertex->Properties(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); // Try to set the property5 { auto ret = vertex->SetPropertyAndValidate(property5, PropertyValue("haihai")); - AssertErrorInVariant(ret, Error::DELETED_OBJECT); + AssertShardErrorEqual(ret, SHARD_ERROR(ErrorCode::DELETED_OBJECT)); } acc.Abort(); @@ -918,27 +918,27 @@ TEST_P(StorageV3, VertexDeleteProperty) { // Check whether property 5 exists ASSERT_EQ(vertex->GetProperty(property5, View::OLD)->ValueString(), "nandare"); - ASSERT_EQ(vertex->GetProperty(property5, View::NEW).GetError(), Error::DELETED_OBJECT); + ASSERT_EQ(vertex->GetProperty(property5, View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); { auto properties = vertex->Properties(View::OLD).GetValue(); ASSERT_EQ(properties.size(), 1); ASSERT_EQ(properties[property5].ValueString(), "nandare"); } - ASSERT_EQ(vertex->Properties(View::NEW).GetError(), Error::DELETED_OBJECT); + ASSERT_EQ(vertex->Properties(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); // Advance command acc.AdvanceCommand(); // Check whether property 5 exists - ASSERT_EQ(vertex->GetProperty(property5, View::OLD).GetError(), Error::DELETED_OBJECT); - ASSERT_EQ(vertex->GetProperty(property5, View::NEW).GetError(), Error::DELETED_OBJECT); - ASSERT_EQ(vertex->Properties(View::OLD).GetError(), Error::DELETED_OBJECT); - ASSERT_EQ(vertex->Properties(View::NEW).GetError(), Error::DELETED_OBJECT); + ASSERT_EQ(vertex->GetProperty(property5, View::OLD).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); + ASSERT_EQ(vertex->GetProperty(property5, View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); + ASSERT_EQ(vertex->Properties(View::OLD).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); + ASSERT_EQ(vertex->Properties(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); // Try to set the property { auto ret = vertex->SetPropertyAndValidate(property5, PropertyValue("haihai")); - AssertErrorInVariant(ret, Error::DELETED_OBJECT); + AssertShardErrorEqual(ret, SHARD_ERROR(ErrorCode::DELETED_OBJECT)); } acc.Abort(); @@ -1371,7 +1371,7 @@ TEST_P(StorageV3, VertexLabelSerializationError) { { auto res = vertex->AddLabelAndValidate(label1); - AssertErrorInVariant(res, Error::SERIALIZATION_ERROR); + AssertShardErrorEqual(res, SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR)); } } @@ -1865,7 +1865,7 @@ TEST_P(StorageV3, VertexPropertySerializationError) { { auto res = vertex->SetPropertyAndValidate(property2, PropertyValue("nandare")); - AssertErrorInVariant(res, Error::SERIALIZATION_ERROR); + AssertShardErrorEqual(res, SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR)); } } @@ -2255,14 +2255,14 @@ TEST_P(StorageV3, VertexNonexistentLabelPropertyEdgeAPI) { auto vertex = CreateVertexAndValidate(acc, {}, PropertyValue{0}, {}); // Check state before (OLD view). - ASSERT_EQ(vertex.Labels(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); - ASSERT_EQ(vertex.HasLabel(label1, View::OLD).GetError(), Error::NONEXISTENT_OBJECT); - ASSERT_EQ(vertex.Properties(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); - ASSERT_EQ(vertex.GetProperty(property1, View::OLD).GetError(), Error::NONEXISTENT_OBJECT); - ASSERT_EQ(vertex.InEdges(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); - ASSERT_EQ(vertex.OutEdges(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); - ASSERT_EQ(vertex.InDegree(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); - ASSERT_EQ(vertex.OutDegree(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); + ASSERT_EQ(vertex.Labels(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); + ASSERT_EQ(vertex.HasLabel(label1, View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); + ASSERT_EQ(vertex.Properties(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); + ASSERT_EQ(vertex.GetProperty(property1, View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); + ASSERT_EQ(vertex.InEdges(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); + ASSERT_EQ(vertex.OutEdges(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); + ASSERT_EQ(vertex.InDegree(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); + ASSERT_EQ(vertex.OutDegree(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); // Check state before (NEW view). ASSERT_EQ(vertex.Labels(View::NEW)->size(), 0); @@ -2282,14 +2282,14 @@ TEST_P(StorageV3, VertexNonexistentLabelPropertyEdgeAPI) { .HasValue()); // Check state after (OLD view). - ASSERT_EQ(vertex.Labels(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); - ASSERT_EQ(vertex.HasLabel(label1, View::OLD).GetError(), Error::NONEXISTENT_OBJECT); - ASSERT_EQ(vertex.Properties(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); - ASSERT_EQ(vertex.GetProperty(property1, View::OLD).GetError(), Error::NONEXISTENT_OBJECT); - ASSERT_EQ(vertex.InEdges(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); - ASSERT_EQ(vertex.OutEdges(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); - ASSERT_EQ(vertex.InDegree(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); - ASSERT_EQ(vertex.OutDegree(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); + ASSERT_EQ(vertex.Labels(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); + ASSERT_EQ(vertex.HasLabel(label1, View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); + ASSERT_EQ(vertex.Properties(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); + ASSERT_EQ(vertex.GetProperty(property1, View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); + ASSERT_EQ(vertex.InEdges(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); + ASSERT_EQ(vertex.OutEdges(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); + ASSERT_EQ(vertex.InDegree(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); + ASSERT_EQ(vertex.OutDegree(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); // Check state after (NEW view). ASSERT_EQ(vertex.Labels(View::NEW)->size(), 1); @@ -2657,42 +2657,31 @@ TEST_P(StorageV3, TestCreateVertexAndValidate) { ASSERT_TRUE(vertex2.HasError()); auto error = vertex2.GetError(); - auto error_ptr = std::get_if(&error); - ASSERT_TRUE(error_ptr); - ASSERT_TRUE(*error_ptr == storage::v3::Error::VERTEX_ALREADY_INSERTED); + ASSERT_TRUE(error == common::ErrorCode::VERTEX_ALREADY_INSERTED); } { auto acc = store.Access(GetNextHlc()); auto vertex = acc.CreateVertexAndValidate({primary_label}, {PropertyValue{0}}, {}); ASSERT_TRUE(vertex.HasError()); - ASSERT_TRUE(std::holds_alternative(vertex.GetError())); - EXPECT_EQ(std::get(vertex.GetError()), - SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY, primary_label)); + EXPECT_EQ(vertex.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY)); } { auto acc = store.Access(GetNextHlc()); auto vertex = acc.CreateVertexAndValidate({primary_label}, {PropertyValue{0}}, {}); ASSERT_TRUE(vertex.HasError()); - ASSERT_TRUE(std::holds_alternative(vertex.GetError())); - EXPECT_EQ(std::get(vertex.GetError()), - SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY, primary_label)); + EXPECT_EQ(vertex.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY)); } { auto acc = store.Access(GetNextHlc()); auto vertex = acc.CreateVertexAndValidate({}, {}, {}); ASSERT_TRUE(vertex.HasError()); - ASSERT_TRUE(std::holds_alternative(vertex.GetError())); - EXPECT_EQ(std::get(vertex.GetError()), - SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PRIMARY_PROPERTIES_UNDEFINED, primary_label)); + EXPECT_EQ(vertex.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED)); } { auto acc = store.Access(GetNextHlc()); auto vertex = acc.CreateVertexAndValidate({}, {PropertyValue{"test"}}, {}); ASSERT_TRUE(vertex.HasError()); - ASSERT_TRUE(std::holds_alternative(vertex.GetError())); - EXPECT_EQ(std::get(vertex.GetError()), - SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PROPERTY_WRONG_TYPE, primary_label, - {primary_property, common::SchemaType::INT}, PropertyValue("test"))); + EXPECT_EQ(vertex.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE)); } } } // namespace memgraph::storage::v3::tests diff --git a/tests/unit/storage_v3_edge.cpp b/tests/unit/storage_v3_edge.cpp index 99b981b7b..3d2ab8bbd 100644 --- a/tests/unit/storage_v3_edge.cpp +++ b/tests/unit/storage_v3_edge.cpp @@ -18,7 +18,6 @@ #include "storage/v3/name_id_mapper.hpp" #include "storage/v3/property_value.hpp" #include "storage/v3/shard.hpp" -#include "storage/v3/shard_operation_result.hpp" namespace memgraph::storage::v3::tests { using testing::UnorderedElementsAre; @@ -39,7 +38,7 @@ class StorageEdgeTest : public ::testing::TestWithParam { return store.NameToEdgeType(edge_type_name); } - static ShardOperationResult CreateVertex(Shard::Accessor &acc, const PropertyValue &key) { + static ShardResult CreateVertex(Shard::Accessor &acc, const PropertyValue &key) { return acc.CreateVertexAndValidate({}, {key}, {}); } @@ -1157,7 +1156,7 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { { auto ret = acc.DeleteVertex(&vertex_from.value()); ASSERT_TRUE(ret.HasError()); - ASSERT_EQ(ret.GetError(), Error::VERTEX_HAS_EDGES); + ASSERT_EQ(ret.GetError(), SHARD_ERROR(ErrorCode::VERTEX_HAS_EDGES)); } // Detach delete vertex @@ -1170,8 +1169,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // Check edges ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); - ASSERT_EQ(vertex_from->InEdges(View::NEW).GetError(), Error::DELETED_OBJECT); - ASSERT_EQ(vertex_from->InDegree(View::NEW).GetError(), Error::DELETED_OBJECT); + ASSERT_EQ(vertex_from->InEdges(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); + ASSERT_EQ(vertex_from->InDegree(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); { auto ret = vertex_from->OutEdges(View::OLD); ASSERT_TRUE(ret.HasValue()); @@ -1184,8 +1183,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { ASSERT_EQ(e.From(), from_id); ASSERT_EQ(e.To(), to_id); } - ASSERT_EQ(vertex_from->OutEdges(View::NEW).GetError(), Error::DELETED_OBJECT); - ASSERT_EQ(vertex_from->OutDegree(View::NEW).GetError(), Error::DELETED_OBJECT); + ASSERT_EQ(vertex_from->OutEdges(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); + ASSERT_EQ(vertex_from->OutDegree(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT)); { auto ret = vertex_to->InEdges(View::OLD); ASSERT_TRUE(ret.HasValue()); diff --git a/tests/unit/storage_v3_schema.cpp b/tests/unit/storage_v3_schema.cpp index 5c0d6f1dc..a378485cf 100644 --- a/tests/unit/storage_v3_schema.cpp +++ b/tests/unit/storage_v3_schema.cpp @@ -14,10 +14,10 @@ #include #include -#include #include #include +#include "common/errors.hpp" #include "common/types.hpp" #include "storage/v3/id_types.hpp" #include "storage/v3/property_value.hpp" @@ -150,7 +150,15 @@ TEST_F(SchemaTest, TestSchemaDrop) { class SchemaValidatorTest : public testing::Test { private: - NameIdMapper id_mapper_{{{1, "label1"}, {2, "label2"}, {3, "prop1"}, {4, "prop2"}, {5, "prop3"}}}; + NameIdMapper id_mapper_{{{1, "label1"}, + {2, "label2"}, + {3, "prop1"}, + {4, "prop2"}, + {5, "prop3"}, + {6, "label4"}, + {7, "label5"}, + {8, "label6"}, + {9, "test"}}}; protected: void SetUp() override { @@ -162,9 +170,8 @@ class SchemaValidatorTest : public testing::Test { PropertyId NameToProperty(const std::string &name) { return PropertyId::FromUint(id_mapper_.NameToId(name)); } - protected: Schemas schemas; - SchemaValidator schema_validator{schemas}; + SchemaValidator schema_validator{schemas, id_mapper_}; PropertyId prop_string{NameToProperty("prop1")}; PropertyId prop_int{NameToProperty("prop2")}; PropertyId prop_duration{NameToProperty("prop3")}; @@ -179,100 +186,92 @@ TEST_F(SchemaValidatorTest, TestSchemaValidateVertexCreate) { // Validate against secondary label { const auto schema_violation = schema_validator.ValidateVertexCreate(NameToLabel("test"), {}, {PropertyValue(1)}); - ASSERT_NE(schema_violation, std::nullopt); - EXPECT_EQ(*schema_violation, - SchemaViolation(SchemaViolation::ValidationStatus::NO_SCHEMA_DEFINED_FOR_LABEL, NameToLabel("test"))); + ASSERT_TRUE(schema_violation.HasError()); + EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_NO_SCHEMA_DEFINED_FOR_LABEL)); } { const auto schema_violation = schema_validator.ValidateVertexCreate(label2, {}, {}); - ASSERT_NE(schema_violation, std::nullopt); - EXPECT_EQ(*schema_violation, - SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PRIMARY_PROPERTIES_UNDEFINED, label2)); + ASSERT_TRUE(schema_violation.HasError()); + EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED)); } // Validate wrong secondary label { const auto schema_violation = schema_validator.ValidateVertexCreate(label1, {label1}, {PropertyValue("test")}); - ASSERT_NE(schema_violation, std::nullopt); - EXPECT_EQ(*schema_violation, - SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY, label1)); + ASSERT_TRUE(schema_violation.HasError()); + EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY)); } { const auto schema_violation = schema_validator.ValidateVertexCreate(label1, {label2}, {PropertyValue("test")}); - ASSERT_NE(schema_violation, std::nullopt); - EXPECT_EQ(*schema_violation, - SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY, label2)); + ASSERT_TRUE(schema_violation.HasError()); + EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY)); } // Validate wrong property type { const auto schema_violation = schema_validator.ValidateVertexCreate(label1, {}, {PropertyValue(1)}); - ASSERT_NE(schema_violation, std::nullopt); - EXPECT_EQ(*schema_violation, SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PROPERTY_WRONG_TYPE, label1, - schema_prop_string, PropertyValue(1))); + ASSERT_TRUE(schema_violation.HasError()); + EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE)); } { const auto schema_violation = schema_validator.ValidateVertexCreate(label2, {}, {PropertyValue("test"), PropertyValue(12), PropertyValue(1)}); - ASSERT_NE(schema_violation, std::nullopt); - EXPECT_EQ(*schema_violation, SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PROPERTY_WRONG_TYPE, label2, - schema_prop_duration, PropertyValue(1))); + ASSERT_TRUE(schema_violation.HasError()); + EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE)); } { const auto wrong_prop = PropertyValue(TemporalData(TemporalType::Date, 1234)); const auto schema_violation = schema_validator.ValidateVertexCreate(label2, {}, {PropertyValue("test"), PropertyValue(12), wrong_prop}); - ASSERT_NE(schema_violation, std::nullopt); - EXPECT_EQ(*schema_violation, SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PROPERTY_WRONG_TYPE, label2, - schema_prop_duration, wrong_prop)); + ASSERT_TRUE(schema_violation.HasError()); + EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE)); } // Passing validations - EXPECT_EQ(schema_validator.ValidateVertexCreate(label1, {}, {PropertyValue("test")}), std::nullopt); - EXPECT_EQ(schema_validator.ValidateVertexCreate(label1, {NameToLabel("label3"), NameToLabel("label4")}, - {PropertyValue("test")}), - std::nullopt); - EXPECT_EQ(schema_validator.ValidateVertexCreate( - label2, {}, - {PropertyValue("test"), PropertyValue(122), PropertyValue(TemporalData(TemporalType::Duration, 1234))}), - std::nullopt); - EXPECT_EQ(schema_validator.ValidateVertexCreate(label2, {NameToLabel("label5"), NameToLabel("label6")}, - {PropertyValue("test123"), PropertyValue(122221), - PropertyValue(TemporalData(TemporalType::Duration, 12344321))}), - std::nullopt); + EXPECT_FALSE(schema_validator.ValidateVertexCreate(label1, {}, {PropertyValue("test")}).HasError()); + EXPECT_FALSE( + schema_validator + .ValidateVertexCreate(label1, {NameToLabel("label3"), NameToLabel("label4")}, {PropertyValue("test")}) + .HasError()); + EXPECT_FALSE(schema_validator + .ValidateVertexCreate(label2, {}, + {PropertyValue("test"), PropertyValue(122), + PropertyValue(TemporalData(TemporalType::Duration, 1234))}) + .HasError()); + EXPECT_FALSE(schema_validator + .ValidateVertexCreate(label2, {NameToLabel("label5"), NameToLabel("label6")}, + {PropertyValue("test123"), PropertyValue(122221), + PropertyValue(TemporalData(TemporalType::Duration, 12344321))}) + .HasError()); } TEST_F(SchemaValidatorTest, TestSchemaValidatePropertyUpdate) { // Validate updating of primary key { const auto schema_violation = schema_validator.ValidatePropertyUpdate(label1, prop_string); - ASSERT_NE(schema_violation, std::nullopt); - EXPECT_EQ(*schema_violation, SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_KEY, label1, - schema_prop_string)); + ASSERT_TRUE(schema_violation.HasError()); + EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY)); } { const auto schema_violation = schema_validator.ValidatePropertyUpdate(label2, prop_duration); - ASSERT_NE(schema_violation, std::nullopt); - EXPECT_EQ(*schema_violation, SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_KEY, label2, - schema_prop_duration)); + ASSERT_TRUE(schema_violation.HasError()); + EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY)); } - EXPECT_EQ(schema_validator.ValidatePropertyUpdate(label1, prop_int), std::nullopt); - EXPECT_EQ(schema_validator.ValidatePropertyUpdate(label1, prop_duration), std::nullopt); - EXPECT_EQ(schema_validator.ValidatePropertyUpdate(label2, NameToProperty("test")), std::nullopt); + EXPECT_FALSE(schema_validator.ValidatePropertyUpdate(label1, prop_int).HasError()); + EXPECT_FALSE(schema_validator.ValidatePropertyUpdate(label1, prop_duration).HasError()); + EXPECT_FALSE(schema_validator.ValidatePropertyUpdate(label2, NameToProperty("test")).HasError()); } TEST_F(SchemaValidatorTest, TestSchemaValidatePropertyUpdateLabel) { // Validate adding primary label { const auto schema_violation = schema_validator.ValidateLabelUpdate(label1); - ASSERT_NE(schema_violation, std::nullopt); - EXPECT_EQ(*schema_violation, - SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_LABEL, label1)); + ASSERT_TRUE(schema_violation.HasError()); + EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL)); } { const auto schema_violation = schema_validator.ValidateLabelUpdate(label2); - ASSERT_NE(schema_violation, std::nullopt); - EXPECT_EQ(*schema_violation, - SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_LABEL, label2)); + ASSERT_TRUE(schema_violation.HasError()); + EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL)); } - EXPECT_EQ(schema_validator.ValidateLabelUpdate(NameToLabel("test")), std::nullopt); + EXPECT_FALSE(schema_validator.ValidateLabelUpdate(NameToLabel("test")).HasError()); } } // namespace memgraph::storage::v3::tests diff --git a/tests/unit/storage_v3_shard_rsm.cpp b/tests/unit/storage_v3_shard_rsm.cpp new file mode 100644 index 000000000..60aa3b7f1 --- /dev/null +++ b/tests/unit/storage_v3_shard_rsm.cpp @@ -0,0 +1,316 @@ +// Copyright 2022 Memgraph Ltd. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source +// License, and you may not use this file except in compliance with the Business Source License. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "common/types.hpp" +#include "query/v2/requests.hpp" +#include "storage/v3/id_types.hpp" +#include "storage/v3/property_value.hpp" +#include "storage/v3/schema_validator.hpp" +#include "storage/v3/schemas.hpp" +#include "storage/v3/shard_rsm.hpp" +#include "storage/v3/temporal.hpp" +#include "storage/v3/vertex_id.hpp" + +using testing::Pair; +using testing::UnorderedElementsAre; +using SchemaType = memgraph::common::SchemaType; + +namespace memgraph::storage::v3::tests { + +uint64_t GetTransactionId() { + static uint64_t transaction_id = 0; + return transaction_id++; +} + +class ShardRSMTest : public testing::Test { + private: + NameIdMapper id_mapper_{{{1, "primary_label"}, + {2, "primary_label2"}, + {3, "label"}, + {4, "primary_prop1"}, + {5, "primary_prop2"}, + {6, "prop"}}}; + + protected: + ShardRSMTest() { + PropertyValue min_pk(static_cast(0)); + std::vector min_prim_key = {min_pk}; + PropertyValue max_pk(static_cast(10000000)); + std::vector max_prim_key = {max_pk}; + + auto shard_ptr1 = std::make_unique(primary_label, min_prim_key, max_prim_key, std::vector{schema_prop}); + shard_ptr1->StoreMapping({{1, "primary_label"}, + {2, "primary_label2"}, + {3, "label"}, + {4, "primary_prop1"}, + {5, "primary_prop2"}, + {6, "prop"}}); + shard_ptr1->CreateSchema(primary_label2, {{primary_property2, SchemaType::INT}}); + shard_rsm = std::make_unique(std::move(shard_ptr1)); + } + + LabelId NameToLabel(const std::string &name) { return LabelId::FromUint(id_mapper_.NameToId(name)); } + + PropertyId NameToProperty(const std::string &name) { return PropertyId::FromUint(id_mapper_.NameToId(name)); } + + auto Commit(const auto &req) { + const coordinator::Hlc commit_timestamp{GetTransactionId()}; + msgs::CommitRequest commit_req; + commit_req.transaction_id = req.transaction_id; + commit_req.commit_timestamp = commit_timestamp; + return shard_rsm->Apply(commit_req); + } + + void CreateVertex(const msgs::PrimaryKey &primary_key, const std::vector labels, + const std::vector> &properties) { + msgs::NewVertex vertex = {labels, primary_key, properties}; + msgs::CreateVerticesRequest create_req; + create_req.new_vertices = {vertex}; + create_req.new_vertices = {vertex}; + create_req.transaction_id.logical_id = GetTransactionId(); + + auto write_res = shard_rsm->Apply(create_req); + ASSERT_TRUE(std::holds_alternative(write_res)); + + auto commit_res = Commit(create_req); + ASSERT_TRUE(std::holds_alternative(commit_res)); + ASSERT_FALSE(std::get(commit_res).error.has_value()); + } + + void AssertVertexExists(const msgs::PrimaryKey &primary_key, const std::vector &labels, + const std::vector> &properties) { + msgs::ScanVerticesRequest scan_req; + scan_req.props_to_return = std::nullopt; + scan_req.start_id = msgs::VertexId{msgs::Label{.id = primary_label}, primary_key}; + scan_req.storage_view = msgs::StorageView::OLD; + scan_req.transaction_id.logical_id = GetTransactionId(); + + // Make request + auto maybe_read_res = shard_rsm->Read(scan_req); + ASSERT_TRUE(std::holds_alternative(maybe_read_res)); + const auto read_res = std::get(maybe_read_res); + EXPECT_FALSE(read_res.error.has_value()); + EXPECT_EQ(read_res.results.size(), 1); + + // Read results + const auto res = read_res.results[0]; + const auto vtx_id = msgs::VertexId{msgs::Label{.id = primary_label}, primary_key}; + EXPECT_EQ(res.vertex.id, vtx_id); + EXPECT_EQ(res.vertex.labels, labels); + EXPECT_EQ(res.props, properties); + } + + LabelId primary_label{NameToLabel("primary_label")}; + LabelId primary_label2{NameToLabel("primary_label2")}; + LabelId label{NameToLabel("label")}; + PropertyId primary_property1{NameToProperty("primary_prop1")}; + PropertyId primary_property2{NameToProperty("primary_prop2")}; + PropertyId prop{NameToProperty("prop")}; + SchemaProperty schema_prop{primary_property1, SchemaType::INT}; + std::unique_ptr shard_rsm; +}; + +TEST_F(ShardRSMTest, TestUpdateVertexSecondaryProperty) { + const msgs::Value primary_key_val{static_cast(1)}; + const msgs::PrimaryKey pk{primary_key_val}; + + // Create Vertex + CreateVertex(pk, {}, {}); + + // Add property prop + static constexpr int64_t updated_vertex_id{10}; + { + msgs::UpdateVerticesRequest update_req; + update_req.transaction_id.logical_id = GetTransactionId(); + + update_req.update_vertices = + std::vector{{pk, {}, {}, {{msgs::PropertyId(prop), msgs::Value(updated_vertex_id)}}}}; + + const auto write_res = shard_rsm->Apply(update_req); + ASSERT_TRUE(std::holds_alternative(write_res)); + EXPECT_FALSE(std::get(write_res).error.has_value()); + + const auto commit_res = Commit(update_req); + ASSERT_TRUE(std::holds_alternative(commit_res)); + EXPECT_FALSE(std::get(commit_res).error.has_value()); + } + AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}, {prop, msgs::Value(updated_vertex_id)}}); + + // Update property prop + static constexpr int64_t updated_vertex_id_2{101}; + { + msgs::UpdateVerticesRequest update_req; + update_req.transaction_id.logical_id = GetTransactionId(); + + update_req.update_vertices = + std::vector{{pk, {}, {}, {{msgs::PropertyId(prop), msgs::Value(updated_vertex_id_2)}}}}; + + const auto write_res = shard_rsm->Apply(update_req); + ASSERT_TRUE(std::holds_alternative(write_res)); + EXPECT_FALSE(std::get(write_res).error.has_value()); + + const auto commit_res = Commit(update_req); + ASSERT_TRUE(std::holds_alternative(commit_res)); + EXPECT_FALSE(std::get(commit_res).error.has_value()); + AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}, {prop, msgs::Value(updated_vertex_id_2)}}); + } + AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}, {prop, msgs::Value(updated_vertex_id_2)}}); + + // Remove property prop + { + msgs::UpdateVerticesRequest update_req; + update_req.transaction_id.logical_id = GetTransactionId(); + + update_req.update_vertices = + std::vector{{pk, {}, {}, {{msgs::PropertyId(prop), msgs::Value()}}}}; + + const auto write_res = shard_rsm->Apply(update_req); + ASSERT_TRUE(std::holds_alternative(write_res)); + EXPECT_FALSE(std::get(write_res).error.has_value()); + + const auto commit_res = Commit(update_req); + ASSERT_TRUE(std::holds_alternative(commit_res)); + EXPECT_FALSE(std::get(commit_res).error.has_value()); + } + AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}}); +} + +TEST_F(ShardRSMTest, TestUpdateVertexPrimaryProperty) { + const msgs::Value primary_key_val{static_cast(1)}; + const msgs::PrimaryKey pk{primary_key_val}; + + // Create Vertex + CreateVertex(pk, {}, {}); + + // Try to update primary property + static constexpr int64_t updated_vertex_id{10}; + { + msgs::UpdateVerticesRequest update_req; + update_req.transaction_id.logical_id = GetTransactionId(); + + update_req.update_vertices = std::vector{ + {pk, {}, {}, {{msgs::PropertyId(primary_property1), msgs::Value(updated_vertex_id)}}}}; + + const auto write_res = shard_rsm->Apply(update_req); + ASSERT_TRUE(std::holds_alternative(write_res)); + EXPECT_TRUE(std::get(write_res).error.has_value()); + } + AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}}); + // Try to update primary property of another schema + { + msgs::UpdateVerticesRequest update_req; + update_req.transaction_id.logical_id = GetTransactionId(); + + update_req.update_vertices = std::vector{ + {pk, {}, {}, {{msgs::PropertyId(primary_property2), msgs::Value(updated_vertex_id)}}}}; + + const auto write_res = shard_rsm->Apply(update_req); + ASSERT_TRUE(std::holds_alternative(write_res)); + EXPECT_FALSE(std::get(write_res).error.has_value()); + + const auto commit_res = Commit(update_req); + ASSERT_TRUE(std::holds_alternative(commit_res)); + EXPECT_FALSE(std::get(commit_res).error.has_value()); + } + AssertVertexExists(pk, {}, + {{primary_property1, primary_key_val}, {primary_property2, msgs::Value(updated_vertex_id)}}); +} + +TEST_F(ShardRSMTest, TestUpdateSecondaryLabel) { + const msgs::Value primary_key_val{static_cast(1)}; + const msgs::PrimaryKey pk{primary_key_val}; + + // Create Vertex + CreateVertex(pk, {}, {}); + + // Add label label + const msgs::Label secondary_label{label}; + { + msgs::UpdateVerticesRequest update_req; + update_req.transaction_id.logical_id = GetTransactionId(); + + update_req.update_vertices = std::vector{{pk, {label}, {}, {}}}; + + const auto write_res = shard_rsm->Apply(update_req); + ASSERT_TRUE(std::holds_alternative(write_res)); + EXPECT_FALSE(std::get(write_res).error.has_value()); + + const auto commit_res = Commit(update_req); + ASSERT_TRUE(std::holds_alternative(commit_res)); + EXPECT_FALSE(std::get(commit_res).error.has_value()); + } + AssertVertexExists(pk, {secondary_label}, {{primary_property1, primary_key_val}}); + + // Remove primary label + { + msgs::UpdateVerticesRequest update_req; + update_req.transaction_id.logical_id = GetTransactionId(); + + update_req.update_vertices = std::vector{{pk, {}, {label}, {}}}; + + const auto write_res = shard_rsm->Apply(update_req); + ASSERT_TRUE(std::holds_alternative(write_res)); + EXPECT_FALSE(std::get(write_res).error.has_value()); + + const auto commit_res = Commit(update_req); + ASSERT_TRUE(std::holds_alternative(commit_res)); + EXPECT_FALSE(std::get(commit_res).error.has_value()); + } + AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}}); +} + +TEST_F(ShardRSMTest, TestUpdatePrimaryLabel) { + const msgs::Value primary_key_val{static_cast(1)}; + const msgs::PrimaryKey pk{primary_key_val}; + + // Create Vertex + CreateVertex(pk, {}, {}); + + // Remove primary label + { + msgs::UpdateVerticesRequest update_req; + update_req.transaction_id.logical_id = GetTransactionId(); + + update_req.update_vertices = std::vector{{pk, {}, {primary_label}, {}}}; + + const auto write_res = shard_rsm->Apply(update_req); + ASSERT_TRUE(std::holds_alternative(write_res)); + EXPECT_TRUE(std::get(write_res).error.has_value()); + } + AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}}); + + // Add different primary label + { + msgs::UpdateVerticesRequest update_req; + update_req.transaction_id.logical_id = GetTransactionId(); + + update_req.update_vertices = std::vector{{pk, {primary_label2}, {}, {}}}; + + const auto write_res = shard_rsm->Apply(update_req); + ASSERT_TRUE(std::holds_alternative(write_res)); + EXPECT_TRUE(std::get(write_res).error.has_value()); + } + AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}}); +} + +} // namespace memgraph::storage::v3::tests diff --git a/tests/unit/storage_v3_vertex_accessors.cpp b/tests/unit/storage_v3_vertex_accessors.cpp index 683f58a56..9a155efb4 100644 --- a/tests/unit/storage_v3_vertex_accessors.cpp +++ b/tests/unit/storage_v3_vertex_accessors.cpp @@ -16,6 +16,7 @@ #include #include +#include "common/errors.hpp" #include "common/types.hpp" #include "storage/v3/delta.hpp" #include "storage/v3/id_types.hpp" @@ -76,7 +77,7 @@ TEST_F(StorageV3Accessor, TestPrimaryLabel) { ASSERT_TRUE(vertex.PrimaryLabel(View::OLD).HasError()); const auto error_primary_label = vertex.PrimaryLabel(View::OLD).GetError(); ASSERT_FALSE(vertex.PrimaryLabel(View::NEW).HasError()); - EXPECT_EQ(error_primary_label, Error::NONEXISTENT_OBJECT); + EXPECT_EQ(error_primary_label, SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT)); } { auto acc = storage.Access(GetNextHlc()); @@ -127,9 +128,7 @@ TEST_F(StorageV3Accessor, TestAddLabels) { const auto label1 = NameToLabelId("label"); auto vertex = acc.CreateVertexAndValidate({label1}, {PropertyValue{2}}, {}); ASSERT_TRUE(vertex.HasError()); - ASSERT_TRUE(std::holds_alternative(vertex.GetError())); - EXPECT_EQ(std::get(vertex.GetError()), - SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY, label1)); + EXPECT_EQ(vertex.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY)); } { auto acc = storage.Access(GetNextHlc()); @@ -138,9 +137,7 @@ TEST_F(StorageV3Accessor, TestAddLabels) { ASSERT_TRUE(vertex.HasValue()); const auto schema_violation = vertex->AddLabelAndValidate(label1); ASSERT_TRUE(schema_violation.HasError()); - ASSERT_TRUE(std::holds_alternative(schema_violation.GetError())); - EXPECT_EQ(std::get(schema_violation.GetError()), - SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_LABEL, label1)); + EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL)); } } @@ -184,9 +181,7 @@ TEST_F(StorageV3Accessor, TestRemoveLabels) { auto vertex = CreateVertexAndValidate(acc, {}, PropertyValue{2}); const auto res1 = vertex.RemoveLabelAndValidate(primary_label); ASSERT_TRUE(res1.HasError()); - ASSERT_TRUE(std::holds_alternative(res1.GetError())); - EXPECT_EQ(std::get(res1.GetError()), - SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_LABEL, primary_label)); + EXPECT_EQ(res1.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL)); } } @@ -205,20 +200,14 @@ TEST_F(StorageV3Accessor, TestSetKeysAndProperties) { auto vertex = CreateVertexAndValidate(acc, {}, PropertyValue{1}); const auto res = vertex.SetPropertyAndValidate(primary_property, PropertyValue(1)); ASSERT_TRUE(res.HasError()); - ASSERT_TRUE(std::holds_alternative(res.GetError())); - EXPECT_EQ(std::get(res.GetError()), - SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_KEY, primary_label, - SchemaProperty{primary_property, common::SchemaType::INT})); + EXPECT_EQ(res.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY)); } { auto acc = storage.Access(GetNextHlc()); auto vertex = CreateVertexAndValidate(acc, {}, PropertyValue{2}); const auto res = vertex.SetPropertyAndValidate(primary_property, PropertyValue()); ASSERT_TRUE(res.HasError()); - ASSERT_TRUE(std::holds_alternative(res.GetError())); - EXPECT_EQ(std::get(res.GetError()), - SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_KEY, primary_label, - SchemaProperty{primary_property, common::SchemaType::INT})); + EXPECT_EQ(res.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY)); } }