diff --git a/.github/workflows/diff.yaml b/.github/workflows/diff.yaml index 8b47b3bb3..a7faa7d22 100644 --- a/.github/workflows/diff.yaml +++ b/.github/workflows/diff.yaml @@ -99,7 +99,7 @@ jobs: echo ${file} if [[ ${file} == *.py ]]; then python3 -m black --check --diff ${file} - python3 -m isort --check-only --diff ${file} + python3 -m isort --check-only --profile "black" --diff ${file} fi done diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5abd746c9..26b7c8e05 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,6 +14,7 @@ repos: hooks: - id: isort name: isort (python) + args: ["--profile", "black"] - repo: https://github.com/pre-commit/mirrors-clang-format rev: v13.0.0 hooks: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8b84bb26a..81fc61836 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,6 +21,7 @@ add_subdirectory(auth) add_subdirectory(parser) add_subdirectory(expr) add_subdirectory(coordinator) +add_subdirectory(functions) if (MG_ENTERPRISE) add_subdirectory(audit) diff --git a/src/expr/CMakeLists.txt b/src/expr/CMakeLists.txt index e529512b7..31cbfa493 100644 --- a/src/expr/CMakeLists.txt +++ b/src/expr/CMakeLists.txt @@ -17,4 +17,4 @@ target_include_directories(mg-expr PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(mg-expr PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/ast) target_include_directories(mg-expr PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/interpret) target_include_directories(mg-expr PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/semantic) -target_link_libraries(mg-expr cppitertools Boost::headers mg-utils mg-parser) +target_link_libraries(mg-expr cppitertools Boost::headers mg-utils mg-parser mg-functions) diff --git a/src/expr/interpret/eval.hpp b/src/expr/interpret/eval.hpp index a7d027ede..8fb300a83 100644 --- a/src/expr/interpret/eval.hpp +++ b/src/expr/interpret/eval.hpp @@ -24,6 +24,7 @@ #include "expr/exceptions.hpp" #include "expr/interpret/frame.hpp" #include "expr/semantic/symbol_table.hpp" +#include "functions/awesome_memgraph_functions.hpp" #include "utils/exceptions.hpp" namespace memgraph::expr { @@ -427,8 +428,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> { typename TReturnType = std::enable_if_t<std::is_same_v<TTag, QueryEngineTag>, bool>> TReturnType HasLabelImpl(const VertexAccessor &vertex, const LabelIx &label_ix, QueryEngineTag /*tag*/) { auto label = typename VertexAccessor::Label{LabelId::FromUint(label_ix.ix)}; - auto has_label = vertex.HasLabel(label); - return !has_label; + return vertex.HasLabel(label); } TypedValue Visit(LabelsTest &labels_test) override { @@ -491,7 +491,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> { } TypedValue Visit(Function &function) override { - FunctionContext function_ctx{dba_, ctx_->memory, ctx_->timestamp, &ctx_->counters, view_}; + functions::FunctionContext<DbAccessor> function_ctx{dba_, ctx_->memory, ctx_->timestamp, &ctx_->counters, view_}; // Stack allocate evaluated arguments when there's a small number of them. if (function.arguments_.size() <= 8) { TypedValue arguments[8] = {TypedValue(ctx_->memory), TypedValue(ctx_->memory), TypedValue(ctx_->memory), diff --git a/src/functions/CMakeLists.txt b/src/functions/CMakeLists.txt new file mode 100644 index 000000000..3a3d430cd --- /dev/null +++ b/src/functions/CMakeLists.txt @@ -0,0 +1 @@ +add_library(mg-functions INTERFACE) diff --git a/src/functions/awesome_memgraph_functions.hpp b/src/functions/awesome_memgraph_functions.hpp new file mode 100644 index 000000000..7e716d970 --- /dev/null +++ b/src/functions/awesome_memgraph_functions.hpp @@ -0,0 +1,1423 @@ +// 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 <functional> +#include <string> +#include <type_traits> +#include <unordered_map> + +#include "storage/v3/result.hpp" +#include "storage/v3/shard.hpp" +#include "storage/v3/view.hpp" +#include "utils/algorithm.hpp" +#include "utils/cast.hpp" +#include "utils/concepts.hpp" +#include "utils/memory.hpp" +#include "utils/pmr/string.hpp" +#include "utils/string.hpp" +#include "utils/temporal.hpp" + +namespace memgraph::functions { + +class FunctionRuntimeException : public utils::BasicException { + using utils::BasicException::BasicException; +}; + +template <typename TAccessor> +struct FunctionContext { + TAccessor *db_accessor; + utils::MemoryResource *memory; + int64_t timestamp; + std::unordered_map<std::string, int64_t> *counters; + storage::v3::View view; +}; + +// Tags for the NameToFunction() function template +struct StorageEngineTag {}; +struct QueryEngineTag {}; + +/// Return the function implementation with the given name. +/// +/// Note, returned function signature uses C-style access to an array to allow +/// having an array stored anywhere the caller likes, as long as it is +/// contiguous in memory. Since most functions don't take many arguments, it's +/// convenient to have them stored in the calling stack frame. +template <typename TypedValueT, typename FunctionContextT, typename Tag, typename Conv> +std::function<TypedValueT(const TypedValueT *arguments, int64_t num_arguments, const FunctionContextT &context)> +NameToFunction(const std::string &function_name); + +inline constexpr char kStartsWith[] = "STARTSWITH"; +inline constexpr char kEndsWith[] = "ENDSWITH"; +inline constexpr char kContains[] = "CONTAINS"; +inline constexpr char kId[] = "ID"; + +} // namespace memgraph::functions + +namespace memgraph::functions::impl { + +//////////////////////////////////////////////////////////////////////////////// +// eDSL using template magic for describing a type of an awesome memgraph +// function and checking if the passed in arguments match the description. +// +// To use the type checking eDSL, you should put a `FType` invocation in the +// body of your awesome Memgraph function. `FType` takes type arguments as the +// description of the function type signature. Each runtime argument will be +// checked in order corresponding to given compile time type arguments. These +// type arguments can come in two forms: +// +// * final, primitive type descriptor and +// * combinator type descriptor. +// +// The primitive type descriptors are defined as empty structs, they are right +// below this documentation. +// +// Combinator type descriptors are defined as structs taking additional type +// parameters, you can find these further below in the implementation. Of +// primary interest are `Or` and `Optional` type combinators. +// +// With `Or` you can describe that an argument can be any of the types listed in +// `Or`. For example, `Or<Null, Bool, Integer>` allows an argument to be either +// `Null` or a boolean or an integer. +// +// The `Optional` combinator is used to define optional arguments to a function. +// These must come as the last positional arguments. Naturally, you can use `Or` +// inside `Optional`. So for example, `Optional<Or<Null, Bool>, Integer>` +// describes that a function takes 2 optional arguments. The 1st one must be +// either a `Null` or a boolean, while the 2nd one must be an integer. The type +// signature check will succeed in the following cases. +// +// * No optional arguments were supplied. +// * One argument was supplied and it passes `Or<Null, Bool>` check. +// * Two arguments were supplied, the 1st one passes `Or<Null, Bool>` check +// and the 2nd one passes `Integer` check. +// +// Runtime arguments to `FType` are: function name, pointer to arguments and the +// number of received arguments. +// +// Full example. +// +// FType<Or<Null, String>, NonNegativeInteger, +// Optional<NonNegativeInteger>>("substring", args, nargs); +// +// The above will check that `substring` function received the 2 required +// arguments. Optionally, the function may take a 3rd argument. The 1st argument +// must be either a `Null` or a character string. The 2nd argument is required +// to be a non-negative integer. If the 3rd argument was supplied, it will also +// be checked that it is a non-negative integer. If any of these checks fail, +// `FType` will throw a `FunctionRuntimeException` with an appropriate error +// message. +//////////////////////////////////////////////////////////////////////////////// + +struct Null {}; +struct Bool {}; +struct Integer {}; +struct PositiveInteger {}; +struct NonZeroInteger {}; +struct NonNegativeInteger {}; +struct Double {}; +struct Number {}; +struct List {}; +struct String {}; +struct Map {}; +struct Edge {}; +struct Vertex {}; +struct Path {}; +struct Date {}; +struct LocalTime {}; +struct LocalDateTime {}; +struct Duration {}; + +template <typename ArgType, typename TypedValueT> +bool ArgIsType(const TypedValueT &arg) { + if constexpr (std::is_same_v<ArgType, Null>) { + return arg.IsNull(); + } else if constexpr (std::is_same_v<ArgType, Bool>) { + return arg.IsBool(); + } else if constexpr (std::is_same_v<ArgType, Integer>) { + return arg.IsInt(); + } else if constexpr (std::is_same_v<ArgType, PositiveInteger>) { + return arg.IsInt() && arg.ValueInt() > 0; + } else if constexpr (std::is_same_v<ArgType, NonZeroInteger>) { + return arg.IsInt() && arg.ValueInt() != 0; + } else if constexpr (std::is_same_v<ArgType, NonNegativeInteger>) { + return arg.IsInt() && arg.ValueInt() >= 0; + } else if constexpr (std::is_same_v<ArgType, Double>) { + return arg.IsDouble(); + } else if constexpr (std::is_same_v<ArgType, Number>) { + return arg.IsNumeric(); + } else if constexpr (std::is_same_v<ArgType, List>) { + return arg.IsList(); + } else if constexpr (std::is_same_v<ArgType, String>) { + return arg.IsString(); + } else if constexpr (std::is_same_v<ArgType, Map>) { + return arg.IsMap(); + } else if constexpr (std::is_same_v<ArgType, Vertex>) { + return arg.IsVertex(); + } else if constexpr (std::is_same_v<ArgType, Edge>) { + return arg.IsEdge(); + } else if constexpr (std::is_same_v<ArgType, Path>) { + return arg.IsPath(); + } else if constexpr (std::is_same_v<ArgType, Date>) { + return arg.IsDate(); + } else if constexpr (std::is_same_v<ArgType, LocalTime>) { + return arg.IsLocalTime(); + } else if constexpr (std::is_same_v<ArgType, LocalDateTime>) { + return arg.IsLocalDateTime(); + } else if constexpr (std::is_same_v<ArgType, Duration>) { + return arg.IsDuration(); + } else if constexpr (std::is_same_v<ArgType, void>) { + return true; + } else { + static_assert(std::is_same_v<ArgType, Null>, "Unknown ArgType"); + } + return false; +} + +template <typename ArgType> +constexpr const char *ArgTypeName() { + // The type names returned should be standardized openCypher type names. + // https://github.com/opencypher/openCypher/blob/master/docs/openCypher9.pdf + if constexpr (std::is_same_v<ArgType, Null>) { + return "null"; + } else if constexpr (std::is_same_v<ArgType, Bool>) { + return "boolean"; + } else if constexpr (std::is_same_v<ArgType, Integer>) { + return "integer"; + } else if constexpr (std::is_same_v<ArgType, PositiveInteger>) { + return "positive integer"; + } else if constexpr (std::is_same_v<ArgType, NonZeroInteger>) { + return "non-zero integer"; + } else if constexpr (std::is_same_v<ArgType, NonNegativeInteger>) { + return "non-negative integer"; + } else if constexpr (std::is_same_v<ArgType, Double>) { + return "float"; + } else if constexpr (std::is_same_v<ArgType, Number>) { + return "number"; + } else if constexpr (std::is_same_v<ArgType, List>) { + return "list"; + } else if constexpr (std::is_same_v<ArgType, String>) { + return "string"; + } else if constexpr (std::is_same_v<ArgType, Map>) { + return "map"; + } else if constexpr (std::is_same_v<ArgType, Vertex>) { + return "node"; + } else if constexpr (std::is_same_v<ArgType, Edge>) { + return "relationship"; + } else if constexpr (std::is_same_v<ArgType, Path>) { + return "path"; + } else if constexpr (std::is_same_v<ArgType, void>) { + return "void"; + } else if constexpr (std::is_same_v<ArgType, Date>) { + return "Date"; + } else if constexpr (std::is_same_v<ArgType, LocalTime>) { + return "LocalTime"; + } else if constexpr (std::is_same_v<ArgType, LocalDateTime>) { + return "LocalDateTime"; + } else if constexpr (std::is_same_v<ArgType, Duration>) { + return "Duration"; + } else { + static_assert(std::is_same_v<ArgType, Null>, "Unknown ArgType"); + } + return "<unknown-type>"; +} + +template <typename... ArgType> +struct Or; + +template <typename ArgType> +struct Or<ArgType> { + template <typename TypedValueT> + static bool Check(const TypedValueT &arg) { + return ArgIsType<ArgType>(arg); + } + + static std::string TypeNames() { return ArgTypeName<ArgType>(); } +}; + +template <typename ArgType, typename... ArgTypes> +struct Or<ArgType, ArgTypes...> { + template <typename TypedValueT> + static bool Check(const TypedValueT &arg) { + if (ArgIsType<ArgType>(arg)) return true; + return Or<ArgTypes...>::Check(arg); + } + + static std::string TypeNames() { + if constexpr (sizeof...(ArgTypes) > 1) { + return fmt::format("'{}', {}", ArgTypeName<ArgType>(), Or<ArgTypes...>::TypeNames()); + } else { + return fmt::format("'{}' or '{}'", ArgTypeName<ArgType>(), Or<ArgTypes...>::TypeNames()); + } + } +}; + +template <class T> +struct IsOrType { + static constexpr bool value = false; +}; + +template <class... ArgTypes> +struct IsOrType<Or<ArgTypes...>> { + static constexpr bool value = true; +}; + +template <typename... ArgTypes> +struct Optional; + +template <typename ArgType> +struct Optional<ArgType> { + static constexpr size_t size = 1; + + template <typename TypedValueT> + static void Check(const char *name, const TypedValueT *args, int64_t nargs, int64_t pos) { + if (nargs == 0) return; + const TypedValueT &arg = args[0]; + if constexpr (IsOrType<ArgType>::value) { + if (!ArgType::Check(arg)) { + throw FunctionRuntimeException("Optional '{}' argument at position {} must be either {}.", name, pos, + ArgType::TypeNames()); + } + } else { + if (!ArgIsType<ArgType>(arg)) + throw FunctionRuntimeException("Optional '{}' argument at position {} must be '{}'.", name, pos, + ArgTypeName<ArgType>()); + } + } +}; + +template <class ArgType, class... ArgTypes> +struct Optional<ArgType, ArgTypes...> { + static constexpr size_t size = 1 + sizeof...(ArgTypes); + + template <typename TypedValueT> + static void Check(const char *name, const TypedValueT *args, int64_t nargs, int64_t pos) { + if (nargs == 0) return; + Optional<ArgType>::Check(name, args, nargs, pos); + Optional<ArgTypes...>::Check(name, args + 1, nargs - 1, pos + 1); + } +}; + +template <class T> +struct IsOptional { + static constexpr bool value = false; +}; + +template <class... ArgTypes> +struct IsOptional<Optional<ArgTypes...>> { + static constexpr bool value = true; +}; + +template <class ArgType, class... ArgTypes> +constexpr size_t FTypeRequiredArgs() { + if constexpr (IsOptional<ArgType>::value) { + static_assert(sizeof...(ArgTypes) == 0, "Optional arguments must be last!"); + return 0; + } else if constexpr (sizeof...(ArgTypes) == 0) { + return 1; + } else { + return 1U + FTypeRequiredArgs<ArgTypes...>(); + } +} + +template <class ArgType, class... ArgTypes> +constexpr size_t FTypeOptionalArgs() { + if constexpr (IsOptional<ArgType>::value) { + static_assert(sizeof...(ArgTypes) == 0, "Optional arguments must be last!"); + return ArgType::size; + } else if constexpr (sizeof...(ArgTypes) == 0) { + return 0; + } else { + return FTypeOptionalArgs<ArgTypes...>(); + } +} + +template <typename TypedValueT, typename ArgType, typename... ArgTypes> +void FType(const char *name, const TypedValueT *args, int64_t nargs, int64_t pos = 1) { + if constexpr (std::is_same_v<ArgType, void>) { + if (nargs != 0) { + throw FunctionRuntimeException("'{}' requires no arguments.", name); + } + return; + } + static constexpr int64_t required_args = FTypeRequiredArgs<ArgType, ArgTypes...>(); + static constexpr int64_t optional_args = FTypeOptionalArgs<ArgType, ArgTypes...>(); + static constexpr int64_t total_args = required_args + optional_args; + if constexpr (optional_args > 0) { + if (nargs < required_args || nargs > total_args) { + throw FunctionRuntimeException("'{}' requires between {} and {} arguments.", name, required_args, total_args); + } + } else { + if (nargs != required_args) { + throw FunctionRuntimeException("'{}' requires exactly {} {}.", name, required_args, + required_args == 1 ? "argument" : "arguments"); + } + } + const TypedValueT &arg = args[0]; + if constexpr (IsOrType<ArgType>::value) { + if (!ArgType::Check(arg)) { + throw FunctionRuntimeException("'{}' argument at position {} must be either {}.", name, pos, + ArgType::TypeNames()); + } + } else if constexpr (IsOptional<ArgType>::value) { + static_assert(sizeof...(ArgTypes) == 0, "Optional arguments must be last!"); + ArgType::Check(name, args, nargs, pos); + } else { + if (!ArgIsType<ArgType>(arg)) { + throw FunctionRuntimeException("'{}' argument at position {} must be '{}'", name, pos, ArgTypeName<ArgType>()); + } + } + if constexpr (sizeof...(ArgTypes) > 0) { + FType<TypedValueT, ArgTypes...>(name, args + 1, nargs - 1, pos + 1); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// END function type description eDSL +//////////////////////////////////////////////////////////////////////////////// + +// Predicate functions. +// Neo4j has all, any, exists, none, single +// Those functions are a little bit different since they take a filterExpression +// as an argument. +// There is all, any, none and single productions in opencypher grammar, but it +// will be trivial to also add exists. +// TODO: Implement this. + +// Scalar functions. +// We don't have a way to implement id function since we don't store any. If it +// is really neccessary we could probably map vlist* to id. +// TODO: Implement length (it works on a path, but we didn't define path +// structure yet). +// TODO: Implement size(pattern), for example size((a)-[:X]-()) should return +// number of results of this pattern. I don't think we will ever do this. +// TODO: Implement rest of the list functions. +// TODO: Implement degrees, haversin, radians +// TODO: Implement spatial functions + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT EndNode(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Edge>>("endNode", args, nargs); + if (args[0].IsNull()) { + return TypedValueT(ctx.memory); + } + return TypedValueT(args[0].ValueEdge().To(), ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Head(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, List>>("head", args, nargs); + if (args[0].IsNull()) return TypedValueT(ctx.memory); + const auto &list = args[0].ValueList(); + if (list.empty()) return TypedValueT(ctx.memory); + return TypedValueT(list[0], ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Last(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, List>>("last", args, nargs); + if (args[0].IsNull()) return TypedValueT(ctx.memory); + const auto &list = args[0].ValueList(); + if (list.empty()) return TypedValueT(ctx.memory); + return TypedValueT(list.back(), ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT, typename Tag, typename Conv> +TypedValueT Properties(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Vertex, Edge>>("properties", args, nargs); + auto *dba = ctx.db_accessor; + auto get_properties = [&](const auto &record_accessor) { + typename TypedValueT::TMap properties(ctx.memory); + Conv conv; + if constexpr (std::is_same_v<Tag, StorageEngineTag>) { + auto maybe_props = record_accessor.Properties(ctx.view); + if (maybe_props.HasError()) { + switch (maybe_props.GetError().code) { + case common::ErrorCode::DELETED_OBJECT: + throw functions::FunctionRuntimeException("Trying to get properties from a deleted object."); + case common::ErrorCode::NONEXISTENT_OBJECT: + throw functions::FunctionRuntimeException("Trying to get properties from an object that doesn't exist."); + case common::ErrorCode::SERIALIZATION_ERROR: + case common::ErrorCode::VERTEX_HAS_EDGES: + case common::ErrorCode::PROPERTIES_DISABLED: + case common::ErrorCode::VERTEX_ALREADY_INSERTED: + case common::ErrorCode::SCHEMA_NO_SCHEMA_DEFINED_FOR_LABEL: + case common::ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE: + case common::ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY: + case common::ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL: + case common::ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY: + case common::ErrorCode::SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED: + case common::ErrorCode::OBJECT_NOT_FOUND: + throw functions::FunctionRuntimeException("Unexpected error when getting properties."); + } + } + for (const auto &property : *maybe_props) { + properties.emplace(dba->PropertyToName(property.first), conv(property.second)); + } + } else { + for (const auto &property : record_accessor.Properties()) { + properties.emplace(utils::pmr::string(dba->PropertyToName(property.first), ctx.memory), + conv(property.second, dba)); + } + } + return TypedValueT(std::move(properties)); + }; + + const auto &value = args[0]; + if (value.IsNull()) { + return TypedValueT(ctx.memory); + } + if (value.IsVertex()) { + return get_properties(value.ValueVertex()); + } + return get_properties(value.ValueEdge()); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Size(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, List, String, Map, Path>>("size", args, nargs); + const auto &value = args[0]; + if (value.IsNull()) { + return TypedValueT(ctx.memory); + } + if (value.IsList()) { + return TypedValueT(static_cast<int64_t>(value.ValueList().size()), ctx.memory); + } + if (value.IsString()) { + return TypedValueT(static_cast<int64_t>(value.ValueString().size()), ctx.memory); + } + if (value.IsMap()) { + // neo4j doesn't implement size for map, but I don't see a good reason not + // to do it. + return TypedValueT(static_cast<int64_t>(value.ValueMap().size()), ctx.memory); + } + return TypedValueT(static_cast<int64_t>(value.ValuePath().edges().size()), ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT, typename Conv> +TypedValueT StartNode(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Edge>>("startNode", args, nargs); + if (args[0].IsNull()) { + return TypedValueT(ctx.memory); + } + return TypedValueT(args[0].ValueEdge().From(), ctx.memory); +} + +// This is needed because clang-tidy fails to identify the use of this function in the if-constexpr branch +// NOLINTNEXTLINE(clang-diagnostic-unused-function) +inline size_t UnwrapDegreeResult(storage::v3::ShardResult<size_t> maybe_degree) { + if (maybe_degree.HasError()) { + switch (maybe_degree.GetError().code) { + case common::ErrorCode::DELETED_OBJECT: + throw functions::FunctionRuntimeException("Trying to get degree of a deleted node."); + case common::ErrorCode::NONEXISTENT_OBJECT: + throw functions::FunctionRuntimeException("Trying to get degree of a node that doesn't exist."); + case common::ErrorCode::SERIALIZATION_ERROR: + case common::ErrorCode::VERTEX_HAS_EDGES: + case common::ErrorCode::PROPERTIES_DISABLED: + case common::ErrorCode::VERTEX_ALREADY_INSERTED: + case common::ErrorCode::SCHEMA_NO_SCHEMA_DEFINED_FOR_LABEL: + case common::ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE: + case common::ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY: + case common::ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL: + case common::ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY: + case common::ErrorCode::SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED: + case common::ErrorCode::OBJECT_NOT_FOUND: + throw functions::FunctionRuntimeException("Unexpected error when getting node degree."); + } + } + return *maybe_degree; +} + +template <typename TypedValueT, typename FunctionContextT, typename Tag> +TypedValueT Degree(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Vertex>>("degree", args, nargs); + if (args[0].IsNull()) return TypedValueT(ctx.memory); + const auto &vertex = args[0].ValueVertex(); + size_t out_degree = 0; + size_t in_degree = 0; + if constexpr (std::same_as<Tag, StorageEngineTag>) { + out_degree = UnwrapDegreeResult(vertex.OutDegree(ctx.view)); + in_degree = UnwrapDegreeResult(vertex.InDegree(ctx.view)); + } else { + out_degree = vertex.OutDegree(); + in_degree = vertex.InDegree(); + } + return TypedValueT(static_cast<int64_t>(out_degree + in_degree), ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT, typename Tag> +TypedValueT InDegree(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Vertex>>("inDegree", args, nargs); + if (args[0].IsNull()) return TypedValueT(ctx.memory); + const auto &vertex = args[0].ValueVertex(); + size_t in_degree = 0; + if constexpr (std::same_as<Tag, StorageEngineTag>) { + in_degree = UnwrapDegreeResult(vertex.InDegree(ctx.view)); + } else { + in_degree = vertex.InDegree(); + } + return TypedValueT(static_cast<int64_t>(in_degree), ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT, typename Tag> +TypedValueT OutDegree(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Vertex>>("outDegree", args, nargs); + if (args[0].IsNull()) return TypedValueT(ctx.memory); + const auto &vertex = args[0].ValueVertex(); + size_t out_degree = 0; + if constexpr (std::same_as<Tag, StorageEngineTag>) { + out_degree = UnwrapDegreeResult(vertex.OutDegree(ctx.view)); + } else { + out_degree = vertex.OutDegree(); + } + return TypedValueT(static_cast<int64_t>(out_degree), ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT ToBoolean(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Bool, Integer, String>>("toBoolean", args, nargs); + const auto &value = args[0]; + if (value.IsNull()) { + return TypedValueT(ctx.memory); + } + if (value.IsBool()) { + return TypedValueT(value.ValueBool(), ctx.memory); + } + if (value.IsInt()) { + return TypedValueT(value.ValueInt() != 0L, ctx.memory); + } + auto s = utils::ToUpperCase(utils::Trim(value.ValueString())); + if (s == "TRUE") return TypedValueT(true, ctx.memory); + if (s == "FALSE") return TypedValueT(false, ctx.memory); + // I think this is just stupid and that exception should be thrown, but + // neo4j does it this way... + return TypedValueT(ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT ToFloat(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Number, String>>("toFloat", args, nargs); + const auto &value = args[0]; + if (value.IsNull()) { + return TypedValueT(ctx.memory); + } + if (value.IsInt()) { + return TypedValueT(static_cast<double>(value.ValueInt()), ctx.memory); + } + if (value.IsDouble()) { + return TypedValueT(value, ctx.memory); + } + try { + return TypedValueT(utils::ParseDouble(utils::Trim(value.ValueString())), ctx.memory); + } catch (const utils::BasicException &) { + return TypedValueT(ctx.memory); + } +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT ToInteger(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Bool, Number, String>>("toInteger", args, nargs); + const auto &value = args[0]; + if (value.IsNull()) { + return TypedValueT(ctx.memory); + } + if (value.IsBool()) { + return TypedValueT(value.ValueBool() ? 1L : 0L, ctx.memory); + } + if (value.IsInt()) { + return TypedValueT(value, ctx.memory); + } + if (value.IsDouble()) { + return TypedValueT(static_cast<int64_t>(value.ValueDouble()), ctx.memory); + } + try { + // Yup, this is correct. String is valid if it has floating point + // number, then it is parsed and converted to int. + return TypedValueT(static_cast<int64_t>(utils::ParseDouble(utils::Trim(value.ValueString()))), ctx.memory); + } catch (const utils::BasicException &) { + return TypedValueT(ctx.memory); + } +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Type(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Edge>>("type", args, nargs); + auto *dba = ctx.db_accessor; + if (args[0].IsNull()) return TypedValueT(ctx.memory); + return TypedValueT(dba->EdgeTypeToName(args[0].ValueEdge().EdgeType()), ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT ValueType(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Bool, Integer, Double, String, List, Map, Vertex, Edge, Path>>("type", args, nargs); + // The type names returned should be standardized openCypher type names. + // https://github.com/opencypher/openCypher/blob/master/docs/openCypher9.pdf + switch (args[0].type()) { + case TypedValueT::Type::Null: + return TypedValueT("NULL", ctx.memory); + case TypedValueT::Type::Bool: + return TypedValueT("BOOLEAN", ctx.memory); + case TypedValueT::Type::Int: + return TypedValueT("INTEGER", ctx.memory); + case TypedValueT::Type::Double: + return TypedValueT("FLOAT", ctx.memory); + case TypedValueT::Type::String: + return TypedValueT("STRING", ctx.memory); + case TypedValueT::Type::List: + return TypedValueT("LIST", ctx.memory); + case TypedValueT::Type::Map: + return TypedValueT("MAP", ctx.memory); + case TypedValueT::Type::Vertex: + return TypedValueT("NODE", ctx.memory); + case TypedValueT::Type::Edge: + return TypedValueT("RELATIONSHIP", ctx.memory); + case TypedValueT::Type::Path: + return TypedValueT("PATH", ctx.memory); + case TypedValueT::Type::Date: + return TypedValueT("DATE", ctx.memory); + case TypedValueT::Type::LocalTime: + return TypedValueT("LOCAL_TIME", ctx.memory); + case TypedValueT::Type::LocalDateTime: + return TypedValueT("LOCAL_DATE_TIME", ctx.memory); + case TypedValueT::Type::Duration: + return TypedValueT("DURATION", ctx.memory); + } +} + +template <typename TypedValueT, typename FunctionContextT, typename Tag> +TypedValueT Labels(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Vertex>>("labels", args, nargs); + auto *dba = ctx.db_accessor; + if (args[0].IsNull()) return TypedValueT(ctx.memory); + typename TypedValueT::TVector labels(ctx.memory); + if constexpr (std::is_same_v<Tag, StorageEngineTag>) { + auto maybe_labels = args[0].ValueVertex().Labels(ctx.view); + if (maybe_labels.HasError()) { + switch (maybe_labels.GetError().code) { + case common::ErrorCode::DELETED_OBJECT: + throw functions::FunctionRuntimeException("Trying to get labels from a deleted node."); + case common::ErrorCode::NONEXISTENT_OBJECT: + throw functions::FunctionRuntimeException("Trying to get labels from a node that doesn't exist."); + case common::ErrorCode::SERIALIZATION_ERROR: + case common::ErrorCode::VERTEX_HAS_EDGES: + case common::ErrorCode::PROPERTIES_DISABLED: + case common::ErrorCode::VERTEX_ALREADY_INSERTED: + case common::ErrorCode::SCHEMA_NO_SCHEMA_DEFINED_FOR_LABEL: + case common::ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE: + case common::ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY: + case common::ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL: + case common::ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY: + case common::ErrorCode::SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED: + case common::ErrorCode::OBJECT_NOT_FOUND: + throw functions::FunctionRuntimeException("Unexpected error when getting labels."); + } + } + for (const auto &label : *maybe_labels) { + labels.emplace_back(dba->LabelToName(label)); + } + } else { + auto vertex = args[0].ValueVertex(); + for (const auto &label : vertex.Labels()) { + labels.emplace_back(dba->LabelToName(label.id)); + } + } + return TypedValueT(std::move(labels)); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Nodes(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Path>>("nodes", args, nargs); + if (args[0].IsNull()) return TypedValueT(ctx.memory); + const auto &vertices = args[0].ValuePath().vertices(); + typename TypedValueT::TVector values(ctx.memory); + values.reserve(vertices.size()); + for (const auto &v : vertices) values.emplace_back(v); + return TypedValueT(std::move(values)); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Relationships(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Path>>("relationships", args, nargs); + if (args[0].IsNull()) return TypedValueT(ctx.memory); + const auto &edges = args[0].ValuePath().edges(); + typename TypedValueT::TVector values(ctx.memory); + values.reserve(edges.size()); + for (const auto &e : edges) values.emplace_back(e); + return TypedValueT(std::move(values)); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Range(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Integer>, Or<Null, Integer>, Optional<Or<Null, NonZeroInteger>>>("range", args, nargs); + for (int64_t i = 0; i < nargs; ++i) + if (args[i].IsNull()) return TypedValueT(ctx.memory); + auto lbound = args[0].ValueInt(); + auto rbound = args[1].ValueInt(); + int64_t step = nargs == 3 ? args[2].ValueInt() : 1; + typename TypedValueT::TVector list(ctx.memory); + if (lbound <= rbound && step > 0) { + for (auto i = lbound; i <= rbound; i += step) { + list.emplace_back(i); + } + } else if (lbound >= rbound && step < 0) { + for (auto i = lbound; i >= rbound; i += step) { + list.emplace_back(i); + } + } + return TypedValueT(std::move(list)); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Tail(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, List>>("tail", args, nargs); + if (args[0].IsNull()) return TypedValueT(ctx.memory); + typename TypedValueT::TVector list(args[0].ValueList(), ctx.memory); + if (list.empty()) return TypedValueT(std::move(list)); + list.erase(list.begin()); + return TypedValueT(std::move(list)); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT UniformSample(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, List>, Or<Null, NonNegativeInteger>>("uniformSample", args, nargs); + static thread_local std::mt19937 pseudo_rand_gen_{std::random_device{}()}; + if (args[0].IsNull() || args[1].IsNull()) return TypedValueT(ctx.memory); + const auto &population = args[0].ValueList(); + auto population_size = population.size(); + if (population_size == 0) return TypedValueT(ctx.memory); + auto desired_length = args[1].ValueInt(); + std::uniform_int_distribution<uint64_t> rand_dist{0, population_size - 1}; + typename TypedValueT::TVector sampled(ctx.memory); + sampled.reserve(desired_length); + for (int64_t i = 0; i < desired_length; ++i) { + sampled.emplace_back(population[rand_dist(pseudo_rand_gen_)]); + } + return TypedValueT(std::move(sampled)); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Abs(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Number>>("abs", args, nargs); + const auto &value = args[0]; + if (value.IsNull()) { + return TypedValueT(ctx.memory); + } + if (value.IsInt()) { + return TypedValueT(std::abs(value.ValueInt()), ctx.memory); + } + return TypedValueT(std::abs(value.ValueDouble()), ctx.memory); +} + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define WRAP_CMATH_FLOAT_FUNCTION(name, lowercased_name) \ + template <typename TypedValueT, typename FunctionContextT> \ + TypedValueT name(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { \ + FType<TypedValueT, Or<Null, Number>>(#lowercased_name, args, nargs); \ + const auto &value = args[0]; \ + if (value.IsNull()) { \ + return TypedValueT(ctx.memory); \ + } \ + if (value.IsInt()) { \ + return TypedValueT(lowercased_name(value.ValueInt()), ctx.memory); \ + } \ + return TypedValueT(lowercased_name(value.ValueDouble()), ctx.memory); \ + } + +WRAP_CMATH_FLOAT_FUNCTION(Ceil, ceil) +WRAP_CMATH_FLOAT_FUNCTION(Floor, floor) +// We are not completely compatible with neoj4 in this function because, +// neo4j rounds -0.5, -1.5, -2.5... to 0, -1, -2... +WRAP_CMATH_FLOAT_FUNCTION(Round, round) +WRAP_CMATH_FLOAT_FUNCTION(Exp, exp) +WRAP_CMATH_FLOAT_FUNCTION(Log, log) +WRAP_CMATH_FLOAT_FUNCTION(Log10, log10) +WRAP_CMATH_FLOAT_FUNCTION(Sqrt, sqrt) +WRAP_CMATH_FLOAT_FUNCTION(Acos, acos) +WRAP_CMATH_FLOAT_FUNCTION(Asin, asin) +WRAP_CMATH_FLOAT_FUNCTION(Atan, atan) +WRAP_CMATH_FLOAT_FUNCTION(Cos, cos) +WRAP_CMATH_FLOAT_FUNCTION(Sin, sin) +WRAP_CMATH_FLOAT_FUNCTION(Tan, tan) + +#undef WRAP_CMATH_FLOAT_FUNCTION + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Atan2(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Number>, Or<Null, Number>>("atan2", args, nargs); + if (args[0].IsNull() || args[1].IsNull()) return TypedValueT(ctx.memory); + auto to_double = [](const TypedValueT &t) -> double { + if (t.IsInt()) { + return t.ValueInt(); + } + return t.ValueDouble(); + }; + double y = to_double(args[0]); + double x = to_double(args[1]); + return TypedValueT(atan2(y, x), ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Sign(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Number>>("sign", args, nargs); + auto sign = [&](auto x) { return TypedValueT((0 < x) - (x < 0), ctx.memory); }; + const auto &value = args[0]; + if (value.IsNull()) { + return TypedValueT(ctx.memory); + } + if (value.IsInt()) { + return sign(value.ValueInt()); + } + return sign(value.ValueDouble()); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT E(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, void>("e", args, nargs); + return TypedValueT(M_E, ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Pi(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, void>("pi", args, nargs); + return TypedValueT(M_PI, ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Rand(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, void>("rand", args, nargs); + static thread_local std::mt19937 pseudo_rand_gen_{std::random_device{}()}; + static thread_local std::uniform_real_distribution<> rand_dist_{0, 1}; + return TypedValueT(rand_dist_(pseudo_rand_gen_), ctx.memory); +} + +template <class TPredicate, typename TypedValueT, typename FunctionContextT> +TypedValueT StringMatchOperator(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, String>, Or<Null, String>>(TPredicate::name, args, nargs); + if (args[0].IsNull() || args[1].IsNull()) return TypedValueT(ctx.memory); + const auto &s1 = args[0].ValueString(); + const auto &s2 = args[1].ValueString(); + return TypedValueT(TPredicate{}(s1, s2), ctx.memory); +} + +// Check if s1 starts with s2. +template <typename TypedValueT> +struct StartsWithPredicate { + static constexpr const char *name = "startsWith"; + bool operator()(const typename TypedValueT::TString &s1, const typename TypedValueT::TString &s2) const { + if (s1.size() < s2.size()) return false; + return std::equal(s2.begin(), s2.end(), s1.begin()); + } +}; + +template <typename TypedValueT, typename FunctionContextT> +inline const auto StartsWith = StringMatchOperator<StartsWithPredicate<TypedValueT>, TypedValueT, FunctionContextT>; + +// Check if s1 ends with s2. +template <typename TypedValueT> +struct EndsWithPredicate { + static constexpr const char *name = "endsWith"; + bool operator()(const typename TypedValueT::TString &s1, const typename TypedValueT::TString &s2) const { + if (s1.size() < s2.size()) return false; + return std::equal(s2.rbegin(), s2.rend(), s1.rbegin()); + } +}; + +template <typename TypedValueT, typename FunctionContextT> +inline const auto EndsWith = StringMatchOperator<EndsWithPredicate<TypedValueT>, TypedValueT, FunctionContextT>; + +// Check if s1 contains s2. +template <typename TypedValueT> +struct ContainsPredicate { + static constexpr const char *name = "contains"; + bool operator()(const typename TypedValueT::TString &s1, const typename TypedValueT::TString &s2) const { + if (s1.size() < s2.size()) return false; + return s1.find(s2) != std::string::npos; + } +}; + +template <typename TypedValueT, typename FunctionContextT> +inline const auto Contains = StringMatchOperator<ContainsPredicate<TypedValueT>, TypedValueT, FunctionContextT>; + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Assert(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Bool, Optional<String>>("assert", args, nargs); + if (!args[0].ValueBool()) { + std::string message("Assertion failed"); + if (nargs == 2) { + message += ": "; + message += args[1].ValueString(); + } + message += "."; + throw FunctionRuntimeException(message); + } + return TypedValueT(args[0], ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Counter(const TypedValueT *args, int64_t nargs, const FunctionContextT &context) { + FType<TypedValueT, String, Integer, Optional<NonZeroInteger>>("counter", args, nargs); + int64_t step = 1; + if (nargs == 3) { + step = args[2].ValueInt(); + } + + auto [it, inserted] = context.counters->emplace(args[0].ValueString(), args[1].ValueInt()); + auto value = it->second; + it->second += step; + + return TypedValueT(value, context.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Id(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, Edge>>("id", args, nargs); + const auto &arg = args[0]; + if (arg.IsNull()) { + return TypedValueT(ctx.memory); + } + return TypedValueT(static_cast<int64_t>(arg.ValueEdge().CypherId()), ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT ToString(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, String, Number, Date, LocalTime, LocalDateTime, Duration, Bool>>("toString", args, nargs); + const auto &arg = args[0]; + if (arg.IsNull()) { + return TypedValueT(ctx.memory); + } + if (arg.IsString()) { + return TypedValueT(arg, ctx.memory); + } + if (arg.IsInt()) { + // TODO: This is making a pointless copy of std::string, we may want to + // use a different conversion to string + return TypedValueT(std::to_string(arg.ValueInt()), ctx.memory); + } + if (arg.IsDouble()) { + return TypedValueT(std::to_string(arg.ValueDouble()), ctx.memory); + } + if (arg.IsDate()) { + return TypedValueT(arg.ValueDate().ToString(), ctx.memory); + } + if (arg.IsLocalTime()) { + return TypedValueT(arg.ValueLocalTime().ToString(), ctx.memory); + } + if (arg.IsLocalDateTime()) { + return TypedValueT(arg.ValueLocalDateTime().ToString(), ctx.memory); + } + if (arg.IsDuration()) { + return TypedValueT(arg.ValueDuration().ToString(), ctx.memory); + } + + return TypedValueT(arg.ValueBool() ? "true" : "false", ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Timestamp(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Optional<Or<Date, LocalTime, LocalDateTime, Duration>>>("timestamp", args, nargs); + const auto &arg = *args; + if (arg.IsDate()) { + return TypedValueT(arg.ValueDate().MicrosecondsSinceEpoch(), ctx.memory); + } + if (arg.IsLocalTime()) { + return TypedValueT(arg.ValueLocalTime().MicrosecondsSinceEpoch(), ctx.memory); + } + if (arg.IsLocalDateTime()) { + return TypedValueT(arg.ValueLocalDateTime().MicrosecondsSinceEpoch(), ctx.memory); + } + if (arg.IsDuration()) { + return TypedValueT(arg.ValueDuration().microseconds, ctx.memory); + } + return TypedValueT(ctx.timestamp, ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Left(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, String>, Or<Null, NonNegativeInteger>>("left", args, nargs); + if (args[0].IsNull() || args[1].IsNull()) return TypedValueT(ctx.memory); + return TypedValueT(utils::Substr(args[0].ValueString(), 0, args[1].ValueInt()), ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Right(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, String>, Or<Null, NonNegativeInteger>>("right", args, nargs); + if (args[0].IsNull() || args[1].IsNull()) return TypedValueT(ctx.memory); + const auto &str = args[0].ValueString(); + auto len = args[1].ValueInt(); + return len <= str.size() ? TypedValueT(utils::Substr(str, str.size() - len, len), ctx.memory) + : TypedValueT(str, ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT CallStringFunction( + const TypedValueT *args, int64_t nargs, utils::MemoryResource *memory, const char *name, + std::function<typename TypedValueT::TString(const typename TypedValueT::TString &)> fun) { + FType<TypedValueT, Or<Null, String>>(name, args, nargs); + if (args[0].IsNull()) return TypedValueT(memory); + return TypedValueT(fun(args[0].ValueString()), memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT LTrim(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + return CallStringFunction<TypedValueT, FunctionContextT>(args, nargs, ctx.memory, "lTrim", [&](const auto &str) { + return typename TypedValueT::TString(utils::LTrim(str), ctx.memory); + }); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT RTrim(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + return CallStringFunction<TypedValueT, FunctionContextT>(args, nargs, ctx.memory, "rTrim", [&](const auto &str) { + return typename TypedValueT::TString(utils::RTrim(str), ctx.memory); + }); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Trim(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + return CallStringFunction<TypedValueT, FunctionContextT>(args, nargs, ctx.memory, "trim", [&](const auto &str) { + return typename TypedValueT::TString(utils::Trim(str), ctx.memory); + }); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Reverse(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + return CallStringFunction<TypedValueT, FunctionContextT>( + args, nargs, ctx.memory, "reverse", [&](const auto &str) { return utils::Reversed(str, ctx.memory); }); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT ToLower(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + return CallStringFunction<TypedValueT, FunctionContextT>(args, nargs, ctx.memory, "toLower", [&](const auto &str) { + typename TypedValueT::TString res(ctx.memory); + utils::ToLowerCase(&res, str); + return res; + }); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT ToUpper(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + return CallStringFunction<TypedValueT, FunctionContextT>(args, nargs, ctx.memory, "toUpper", [&](const auto &str) { + typename TypedValueT::TString res(ctx.memory); + utils::ToUpperCase(&res, str); + return res; + }); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Replace(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, String>, Or<Null, String>, Or<Null, String>>("replace", args, nargs); + if (args[0].IsNull() || args[1].IsNull() || args[2].IsNull()) { + return TypedValueT(ctx.memory); + } + typename TypedValueT::TString replaced(ctx.memory); + utils::Replace(&replaced, args[0].ValueString(), args[1].ValueString(), args[2].ValueString()); + return TypedValueT(std::move(replaced)); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Split(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, String>, Or<Null, String>>("split", args, nargs); + if (args[0].IsNull() || args[1].IsNull()) { + return TypedValueT(ctx.memory); + } + typename TypedValueT::TVector result(ctx.memory); + utils::Split(&result, args[0].ValueString(), args[1].ValueString()); + return TypedValueT(std::move(result)); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Substring(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<Null, String>, NonNegativeInteger, Optional<NonNegativeInteger>>("substring", args, nargs); + if (args[0].IsNull()) return TypedValueT(ctx.memory); + const auto &str = args[0].ValueString(); + auto start = args[1].ValueInt(); + if (nargs == 2) return TypedValueT(utils::Substr(str, start), ctx.memory); + auto len = args[2].ValueInt(); + return TypedValueT(utils::Substr(str, start, len), ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT ToByteString(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, String>("toByteString", args, nargs); + const auto &str = args[0].ValueString(); + if (str.empty()) return TypedValueT("", ctx.memory); + if (!utils::StartsWith(str, "0x") && !utils::StartsWith(str, "0X")) { + throw FunctionRuntimeException("'toByteString' argument must start with '0x'"); + } + const auto &hex_str = utils::Substr(str, 2); + auto read_hex = [](const char ch) -> unsigned char { + if (ch >= '0' && ch <= '9') return ch - '0'; + if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; + if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; + throw FunctionRuntimeException("'toByteString' argument has an invalid character '{}'", ch); + }; + utils::pmr::string bytes(ctx.memory); + bytes.reserve((1 + hex_str.size()) / 2); + size_t i = 0; + // Treat odd length hex string as having a leading zero. + if (hex_str.size() % 2) bytes.append(1, read_hex(hex_str[i++])); + for (; i < hex_str.size(); i += 2) { + unsigned char byte = read_hex(hex_str[i]) * 16U + read_hex(hex_str[i + 1]); + // MemcpyCast in case we are converting to a signed value, so as to avoid + // undefined behaviour. + bytes.append(1, utils::MemcpyCast<decltype(bytes)::value_type>(byte)); + } + return TypedValueT(std::move(bytes)); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT FromByteString(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, String, Optional<PositiveInteger>>("fromByteString", args, nargs); + const auto &bytes = args[0].ValueString(); + if (bytes.empty()) return TypedValueT("", ctx.memory); + size_t min_length = bytes.size(); + if (nargs == 2) min_length = std::max(min_length, static_cast<size_t>(args[1].ValueInt())); + utils::pmr::string str(ctx.memory); + str.reserve(min_length * 2 + 2); + str.append("0x"); + for (size_t pad = 0; pad < min_length - bytes.size(); ++pad) str.append(2, '0'); + // Convert the bytes to a character string in hex representation. + // Unfortunately, we don't know whether the default `char` is signed or + // unsigned, so we have to work around any potential undefined behaviour when + // conversions between the 2 occur. That's why this function is more + // complicated than it should be. + auto to_hex = [](const unsigned char val) -> char { + unsigned char ch = val < 10U ? static_cast<unsigned char>('0') + val : static_cast<unsigned char>('a') + val - 10U; + return utils::MemcpyCast<char>(ch); + }; + for (unsigned char byte : bytes) { + str.append(1, to_hex(byte / 16U)); + str.append(1, to_hex(byte % 16U)); + } + return TypedValueT(std::move(str)); +} + +template <typename T> +concept IsNumberOrInteger = utils::SameAsAnyOf<T, Number, Integer>; + +template <IsNumberOrInteger ArgType> +void MapNumericParameters(auto ¶meter_mappings, const auto &input_parameters) { + for (const auto &[key, value] : input_parameters) { + if (auto it = parameter_mappings.find(key); it != parameter_mappings.end()) { + if (value.IsInt()) { + *it->second = value.ValueInt(); + } else if (std::is_same_v<ArgType, Number> && value.IsDouble()) { + *it->second = value.ValueDouble(); + } else { + std::string_view error = std::is_same_v<ArgType, Integer> ? "an integer." : "a numeric value."; + throw FunctionRuntimeException("Invalid value for key '{}'. Expected {}", key, error); + } + } else { + throw FunctionRuntimeException("Unknown key '{}'.", key); + } + } +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Date(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Optional<Or<String, Map>>>("date", args, nargs); + if (nargs == 0) { + return TypedValueT(utils::LocalDateTime(ctx.timestamp).date, ctx.memory); + } + + if (args[0].IsString()) { + const auto &[date_parameters, is_extended] = utils::ParseDateParameters(args[0].ValueString()); + return TypedValueT(utils::Date(date_parameters), ctx.memory); + } + + utils::DateParameters date_parameters; + + using namespace std::literals; + std::unordered_map parameter_mappings = {std::pair{"year"sv, &date_parameters.year}, + std::pair{"month"sv, &date_parameters.month}, + std::pair{"day"sv, &date_parameters.day}}; + + MapNumericParameters<Integer>(parameter_mappings, args[0].ValueMap()); + return TypedValueT(utils::Date(date_parameters), ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT LocalTime(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Optional<Or<String, Map>>>("localtime", args, nargs); + + if (nargs == 0) { + return TypedValueT(utils::LocalDateTime(ctx.timestamp).local_time, ctx.memory); + } + + if (args[0].IsString()) { + const auto &[local_time_parameters, is_extended] = utils::ParseLocalTimeParameters(args[0].ValueString()); + return TypedValueT(utils::LocalTime(local_time_parameters), ctx.memory); + } + + utils::LocalTimeParameters local_time_parameters; + + using namespace std::literals; + std::unordered_map parameter_mappings{ + std::pair{"hour"sv, &local_time_parameters.hour}, + std::pair{"minute"sv, &local_time_parameters.minute}, + std::pair{"second"sv, &local_time_parameters.second}, + std::pair{"millisecond"sv, &local_time_parameters.millisecond}, + std::pair{"microsecond"sv, &local_time_parameters.microsecond}, + }; + + MapNumericParameters<Integer>(parameter_mappings, args[0].ValueMap()); + return TypedValueT(utils::LocalTime(local_time_parameters), ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT LocalDateTime(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Optional<Or<String, Map>>>("localdatetime", args, nargs); + + if (nargs == 0) { + return TypedValueT(utils::LocalDateTime(ctx.timestamp), ctx.memory); + } + + if (args[0].IsString()) { + const auto &[date_parameters, local_time_parameters] = ParseLocalDateTimeParameters(args[0].ValueString()); + return TypedValueT(utils::LocalDateTime(date_parameters, local_time_parameters), ctx.memory); + } + + utils::DateParameters date_parameters; + utils::LocalTimeParameters local_time_parameters; + using namespace std::literals; + std::unordered_map parameter_mappings{ + std::pair{"year"sv, &date_parameters.year}, + std::pair{"month"sv, &date_parameters.month}, + std::pair{"day"sv, &date_parameters.day}, + std::pair{"hour"sv, &local_time_parameters.hour}, + std::pair{"minute"sv, &local_time_parameters.minute}, + std::pair{"second"sv, &local_time_parameters.second}, + std::pair{"millisecond"sv, &local_time_parameters.millisecond}, + std::pair{"microsecond"sv, &local_time_parameters.microsecond}, + }; + + MapNumericParameters<Integer>(parameter_mappings, args[0].ValueMap()); + return TypedValueT(utils::LocalDateTime(date_parameters, local_time_parameters), ctx.memory); +} + +template <typename TypedValueT, typename FunctionContextT> +TypedValueT Duration(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType<TypedValueT, Or<String, Map>>("duration", args, nargs); + + if (args[0].IsString()) { + return TypedValueT(utils::Duration(ParseDurationParameters(args[0].ValueString())), ctx.memory); + } + + utils::DurationParameters duration_parameters; + using namespace std::literals; + std::unordered_map parameter_mappings{std::pair{"day"sv, &duration_parameters.day}, + std::pair{"hour"sv, &duration_parameters.hour}, + std::pair{"minute"sv, &duration_parameters.minute}, + std::pair{"second"sv, &duration_parameters.second}, + std::pair{"millisecond"sv, &duration_parameters.millisecond}, + std::pair{"microsecond"sv, &duration_parameters.microsecond}}; + MapNumericParameters<Number>(parameter_mappings, args[0].ValueMap()); + return TypedValueT(utils::Duration(duration_parameters), ctx.memory); +} + +} // namespace memgraph::functions::impl + +namespace memgraph::functions { + +template <typename TypedValueT, typename FunctionContextT, typename Tag, typename Conv> +std::function<TypedValueT(const TypedValueT *arguments, int64_t num_arguments, const FunctionContextT &context)> +NameToFunction(const std::string &function_name) { + // Scalar functions + if (function_name == "DEGREE") return functions::impl::Degree<TypedValueT, FunctionContextT, Tag>; + if (function_name == "INDEGREE") return functions::impl::InDegree<TypedValueT, FunctionContextT, Tag>; + if (function_name == "OUTDEGREE") return functions::impl::OutDegree<TypedValueT, FunctionContextT, Tag>; + if (function_name == "HEAD") return functions::impl::Head<TypedValueT, FunctionContextT>; + if (function_name == kId) return functions::impl::Id<TypedValueT, FunctionContextT>; + if (function_name == "LAST") return functions::impl::Last<TypedValueT, FunctionContextT>; + if (function_name == "PROPERTIES") return functions::impl::Properties<TypedValueT, FunctionContextT, Tag, Conv>; + if (function_name == "SIZE") return functions::impl::Size<TypedValueT, FunctionContextT>; + if (function_name == "TIMESTAMP") return functions::impl::Timestamp<TypedValueT, FunctionContextT>; + if (function_name == "TOBOOLEAN") return functions::impl::ToBoolean<TypedValueT, FunctionContextT>; + if (function_name == "TOFLOAT") return functions::impl::ToFloat<TypedValueT, FunctionContextT>; + if (function_name == "TOINTEGER") return functions::impl::ToInteger<TypedValueT, FunctionContextT>; + if (function_name == "TYPE") return functions::impl::Type<TypedValueT, FunctionContextT>; + if (function_name == "VALUETYPE") return functions::impl::ValueType<TypedValueT, FunctionContextT>; + // Only on QE + if constexpr (std::is_same_v<Tag, QueryEngineTag>) { + if (function_name == "STARTNODE") return functions::impl::StartNode<TypedValueT, FunctionContextT, Conv>; + if (function_name == "ENDNODE") return functions::impl::EndNode<TypedValueT, FunctionContextT>; + } + + // List functions + if (function_name == "LABELS") return functions::impl::Labels<TypedValueT, FunctionContextT, Tag>; + if (function_name == "NODES") return functions::impl::Nodes<TypedValueT, FunctionContextT>; + if (function_name == "RANGE") return functions::impl::Range<TypedValueT, FunctionContextT>; + if (function_name == "RELATIONSHIPS") return functions::impl::Relationships<TypedValueT, FunctionContextT>; + if (function_name == "TAIL") return functions::impl::Tail<TypedValueT, FunctionContextT>; + if (function_name == "UNIFORMSAMPLE") return functions::impl::UniformSample<TypedValueT, FunctionContextT>; + + // Mathematical functions - numeric + if (function_name == "ABS") return functions::impl::Abs<TypedValueT, FunctionContextT>; + if (function_name == "CEIL") return functions::impl::Ceil<TypedValueT, FunctionContextT>; + if (function_name == "FLOOR") return functions::impl::Floor<TypedValueT, FunctionContextT>; + if (function_name == "RAND") return functions::impl::Rand<TypedValueT, FunctionContextT>; + if (function_name == "ROUND") return functions::impl::Round<TypedValueT, FunctionContextT>; + if (function_name == "SIGN") return functions::impl::Sign<TypedValueT, FunctionContextT>; + + // Mathematical functions - logarithmic + if (function_name == "E") return functions::impl::E<TypedValueT, FunctionContextT>; + if (function_name == "EXP") return functions::impl::Exp<TypedValueT, FunctionContextT>; + if (function_name == "LOG") return functions::impl::Log<TypedValueT, FunctionContextT>; + if (function_name == "LOG10") return functions::impl::Log10<TypedValueT, FunctionContextT>; + if (function_name == "SQRT") return functions::impl::Sqrt<TypedValueT, FunctionContextT>; + + // Mathematical functions - trigonometric + if (function_name == "ACOS") return functions::impl::Acos<TypedValueT, FunctionContextT>; + if (function_name == "ASIN") return functions::impl::Asin<TypedValueT, FunctionContextT>; + if (function_name == "ATAN") return functions::impl::Atan<TypedValueT, FunctionContextT>; + if (function_name == "ATAN2") return functions::impl::Atan2<TypedValueT, FunctionContextT>; + if (function_name == "COS") return functions::impl::Cos<TypedValueT, FunctionContextT>; + if (function_name == "PI") return functions::impl::Pi<TypedValueT, FunctionContextT>; + if (function_name == "SIN") return functions::impl::Sin<TypedValueT, FunctionContextT>; + if (function_name == "TAN") return functions::impl::Tan<TypedValueT, FunctionContextT>; + + // String functions + if (function_name == kContains) return functions::impl::Contains<TypedValueT, FunctionContextT>; + if (function_name == kEndsWith) return functions::impl::EndsWith<TypedValueT, FunctionContextT>; + if (function_name == "LEFT") return functions::impl::Left<TypedValueT, FunctionContextT>; + if (function_name == "LTRIM") return functions::impl::LTrim<TypedValueT, FunctionContextT>; + if (function_name == "REPLACE") return functions::impl::Replace<TypedValueT, FunctionContextT>; + if (function_name == "REVERSE") return functions::impl::Reverse<TypedValueT, FunctionContextT>; + if (function_name == "RIGHT") return functions::impl::Right<TypedValueT, FunctionContextT>; + if (function_name == "RTRIM") return functions::impl::RTrim<TypedValueT, FunctionContextT>; + if (function_name == "SPLIT") return functions::impl::Split<TypedValueT, FunctionContextT>; + if (function_name == kStartsWith) return functions::impl::StartsWith<TypedValueT, FunctionContextT>; + if (function_name == "SUBSTRING") return functions::impl::Substring<TypedValueT, FunctionContextT>; + if (function_name == "TOLOWER") return functions::impl::ToLower<TypedValueT, FunctionContextT>; + if (function_name == "TOSTRING") return functions::impl::ToString<TypedValueT, FunctionContextT>; + if (function_name == "TOUPPER") return functions::impl::ToUpper<TypedValueT, FunctionContextT>; + if (function_name == "TRIM") return functions::impl::Trim<TypedValueT, FunctionContextT>; + + // Memgraph specific functions + if (function_name == "ASSERT") return functions::impl::Assert<TypedValueT, FunctionContextT>; + if (function_name == "TOBYTESTRING") return functions::impl::ToByteString<TypedValueT, FunctionContextT>; + if (function_name == "FROMBYTESTRING") return functions::impl::FromByteString<TypedValueT, FunctionContextT>; + // Only on QE + if constexpr (std::is_same_v<Tag, QueryEngineTag>) { + if (function_name == "COUNTER") return functions::impl::Counter<TypedValueT, FunctionContextT>; + } + + // Functions for temporal types + if (function_name == "DATE") return functions::impl::Date<TypedValueT, FunctionContextT>; + if (function_name == "LOCALTIME") return functions::impl::LocalTime<TypedValueT, FunctionContextT>; + if (function_name == "LOCALDATETIME") return functions::impl::LocalDateTime<TypedValueT, FunctionContextT>; + if (function_name == "DURATION") return functions::impl::Duration<TypedValueT, FunctionContextT>; + + return nullptr; +} + +} // namespace memgraph::functions diff --git a/src/query/v2/CMakeLists.txt b/src/query/v2/CMakeLists.txt index 3c3f780c8..3ecaf63f9 100644 --- a/src/query/v2/CMakeLists.txt +++ b/src/query/v2/CMakeLists.txt @@ -11,7 +11,6 @@ set(mg_query_v2_sources cypher_query_interpreter.cpp frontend/semantic/required_privileges.cpp frontend/stripped.cpp - interpret/awesome_memgraph_functions.cpp interpreter.cpp metadata.cpp plan/operator.cpp @@ -34,7 +33,7 @@ target_include_directories(mg-query-v2 PUBLIC ${CMAKE_SOURCE_DIR}/include) target_include_directories(mg-query-v2 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bindings) target_link_libraries(mg-query-v2 dl cppitertools Boost::headers) target_link_libraries(mg-query-v2 mg-integrations-pulsar mg-integrations-kafka mg-storage-v3 mg-license mg-utils mg-kvstore mg-memory mg-coordinator) -target_link_libraries(mg-query-v2 mg-expr) +target_link_libraries(mg-query-v2 mg-expr mg-functions) if(NOT "${MG_PYTHON_PATH}" STREQUAL "") set(Python3_ROOT_DIR "${MG_PYTHON_PATH}") diff --git a/src/query/v2/accessors.cpp b/src/query/v2/accessors.cpp index 4f6e0d0d7..a784b0103 100644 --- a/src/query/v2/accessors.cpp +++ b/src/query/v2/accessors.cpp @@ -23,11 +23,12 @@ EdgeTypeId EdgeAccessor::EdgeType() const { return edge.type.id; } const std::vector<std::pair<PropertyId, Value>> &EdgeAccessor::Properties() const { return edge.properties; } Value EdgeAccessor::GetProperty(const std::string &prop_name) const { - auto prop_id = manager_->NameToProperty(prop_name); - auto it = std::find_if(edge.properties.begin(), edge.properties.end(), [&](auto &pr) { return prop_id == pr.first; }); - if (it == edge.properties.end()) { + auto maybe_prop = manager_->MaybeNameToProperty(prop_name); + if (!maybe_prop) { return {}; } + const auto prop_id = *maybe_prop; + auto it = std::find_if(edge.properties.begin(), edge.properties.end(), [&](auto &pr) { return prop_id == pr.first; }); return it->second; } @@ -35,6 +36,8 @@ const Edge &EdgeAccessor::GetEdge() const { return edge; } bool EdgeAccessor::IsCycle() const { return edge.src == edge.dst; }; +size_t EdgeAccessor::CypherId() const { return edge.id.gid; } + VertexAccessor EdgeAccessor::To() const { return VertexAccessor(Vertex{edge.dst}, std::vector<std::pair<PropertyId, msgs::Value>>{}, manager_); } @@ -88,7 +91,11 @@ Value VertexAccessor::GetProperty(PropertyId prop_id) const { // NOLINTNEXTLINE(readability-convert-member-functions-to-static) Value VertexAccessor::GetProperty(const std::string &prop_name) const { - return GetProperty(manager_->NameToProperty(prop_name)); + auto maybe_prop = manager_->MaybeNameToProperty(prop_name); + if (!maybe_prop) { + return {}; + } + return GetProperty(*maybe_prop); } msgs::Vertex VertexAccessor::GetVertex() const { return vertex; } diff --git a/src/query/v2/accessors.hpp b/src/query/v2/accessors.hpp index ca7ec999d..48578d217 100644 --- a/src/query/v2/accessors.hpp +++ b/src/query/v2/accessors.hpp @@ -53,12 +53,7 @@ class EdgeAccessor final { [[nodiscard]] bool IsCycle() const; - // Dummy function - // NOLINTNEXTLINE(readability-convert-member-functions-to-static) - [[nodiscard]] size_t CypherId() const { return 10; } - - // bool HasSrcAccessor const { return src == nullptr; } - // bool HasDstAccessor const { return dst == nullptr; } + [[nodiscard]] size_t CypherId() const; [[nodiscard]] VertexAccessor To() const; [[nodiscard]] VertexAccessor From() const; @@ -98,48 +93,11 @@ class VertexAccessor final { [[nodiscard]] msgs::Vertex GetVertex() const; - // Dummy function // NOLINTNEXTLINE(readability-convert-member-functions-to-static) - [[nodiscard]] size_t CypherId() const { return 10; } + [[nodiscard]] size_t InDegree() const { throw utils::NotYetImplemented("InDegree() not yet implemented"); } - // auto InEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types) const - // -> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.InEdges(view)))> { - // auto maybe_edges = impl_.InEdges(view, edge_types); - // if (maybe_edges.HasError()) return maybe_edges.GetError(); - // return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges)); - // } - // - // auto InEdges(storage::View view) const { return InEdges(view, {}); } - // - // auto InEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types, const VertexAccessor &dest) - // const - // -> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.InEdges(view)))> { - // auto maybe_edges = impl_.InEdges(view, edge_types, &dest.impl_); - // if (maybe_edges.HasError()) return maybe_edges.GetError(); - // return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges)); - // } - // - // auto OutEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types) const - // -> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.OutEdges(view)))> { - // auto maybe_edges = impl_.OutEdges(view, edge_types); - // if (maybe_edges.HasError()) return maybe_edges.GetError(); - // return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges)); - // } - // - // auto OutEdges(storage::View view) const { return OutEdges(view, {}); } - // - // auto OutEdges(storage::View view, const std::vector<storage::EdgeTypeId> &edge_types, - // const VertexAccessor &dest) const - // -> storage::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.OutEdges(view)))> { - // auto maybe_edges = impl_.OutEdges(view, edge_types, &dest.impl_); - // if (maybe_edges.HasError()) return maybe_edges.GetError(); - // return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges)); - // } - - // storage::Result<size_t> InDegree(storage::View view) const { return impl_.InDegree(view); } - // - // storage::Result<size_t> OutDegree(storage::View view) const { return impl_.OutDegree(view); } - // + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) + [[nodiscard]] size_t OutDegree() const { throw utils::NotYetImplemented("OutDegree() not yet implemented"); } friend bool operator==(const VertexAccessor &lhs, const VertexAccessor &rhs) { return lhs.vertex == rhs.vertex && lhs.properties == rhs.properties; @@ -153,10 +111,6 @@ class VertexAccessor final { const ShardRequestManagerInterface *manager_; }; -// inline VertexAccessor EdgeAccessor::To() const { return VertexAccessor(impl_.ToVertex()); } - -// inline VertexAccessor EdgeAccessor::From() const { return VertexAccessor(impl_.FromVertex()); } - // Highly mocked interface. Won't work if used. class Path { public: @@ -197,7 +151,14 @@ class Path { friend bool operator==(const Path & /*lhs*/, const Path & /*rhs*/) { return true; }; utils::MemoryResource *GetMemoryResource() { return mem; } + auto &vertices() { return vertices_; } + auto &edges() { return edges_; } + const auto &vertices() const { return vertices_; } + const auto &edges() const { return edges_; } + private: + std::vector<VertexAccessor> vertices_; + std::vector<EdgeAccessor> edges_; utils::MemoryResource *mem = utils::NewDeleteResource(); }; } // namespace memgraph::query::v2::accessors diff --git a/src/query/v2/bindings/eval.hpp b/src/query/v2/bindings/eval.hpp index 584e88922..02e27b22a 100644 --- a/src/query/v2/bindings/eval.hpp +++ b/src/query/v2/bindings/eval.hpp @@ -28,7 +28,6 @@ namespace memgraph::query::v2 { class ShardRequestManagerInterface; -inline const auto lam = [](const auto &val) { return ValueToTypedValue(val); }; namespace detail { class Callable { public: diff --git a/src/query/v2/conversions.hpp b/src/query/v2/conversions.hpp index c18ba8cc0..a1db5ed17 100644 --- a/src/query/v2/conversions.hpp +++ b/src/query/v2/conversions.hpp @@ -56,6 +56,10 @@ inline TypedValue ValueToTypedValue(const msgs::Value &value, ShardRequestManage throw std::runtime_error("Incorrect type in conversion"); } +inline const auto ValueToTypedValueFunctor = [](const msgs::Value &value, ShardRequestManagerInterface *manager) { + return ValueToTypedValue(value, manager); +}; + inline msgs::Value TypedValueToValue(const TypedValue &value) { using Value = msgs::Value; switch (value.type()) { diff --git a/src/query/v2/frontend/ast/ast.lcp b/src/query/v2/frontend/ast/ast.lcp index e30e623ea..dadcc9fa7 100644 --- a/src/query/v2/frontend/ast/ast.lcp +++ b/src/query/v2/frontend/ast/ast.lcp @@ -20,11 +20,13 @@ #include "query/v2/bindings/ast_visitor.hpp" #include "common/types.hpp" #include "query/v2/bindings/symbol.hpp" -#include "query/v2/interpret/awesome_memgraph_functions.hpp" +#include "functions/awesome_memgraph_functions.hpp" #include "query/v2/bindings/typed_value.hpp" #include "query/v2/db_accessor.hpp" #include "query/v2/path.hpp" +#include "query/v2/shard_request_manager.hpp" #include "utils/typeinfo.hpp" +#include "query/v2/conversions.hpp" cpp<# @@ -836,13 +838,15 @@ cpp<# :slk-load (slk-load-ast-vector "Expression")) (function-name "std::string" :scope :public) (function "std::function<TypedValue(const TypedValue *, int64_t, - const FunctionContext &)>" + const functions::FunctionContext<ShardRequestManagerInterface> &)>" :scope :public :dont-save t :clone :copy :slk-load (lambda (member) #>cpp - self->${member} = query::v2::NameToFunction(self->function_name_); + self->${member} = functions::NameToFunction<TypedValue, + functions::FunctionContext<ShardRequestManagerInterface>, + functions::QueryEngineTag, decltype(ValueToTypedValueFunctor)>(self->function_name_); cpp<#))) (:public #>cpp @@ -865,7 +869,8 @@ cpp<# const std::vector<Expression *> &arguments) : arguments_(arguments), function_name_(function_name), - function_(NameToFunction(function_name_)) { + function_(functions::NameToFunction<TypedValue, functions::FunctionContext<ShardRequestManagerInterface>, + functions::QueryEngineTag, decltype(ValueToTypedValueFunctor)>(function_name_)) { if (!function_) { throw SemanticException("Function '{}' doesn't exist.", function_name); } diff --git a/src/query/v2/interpret/awesome_memgraph_functions.cpp b/src/query/v2/interpret/awesome_memgraph_functions.cpp deleted file mode 100644 index 8768736b2..000000000 --- a/src/query/v2/interpret/awesome_memgraph_functions.cpp +++ /dev/null @@ -1,1108 +0,0 @@ -// 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 "query/v2/interpret/awesome_memgraph_functions.hpp" - -#include <algorithm> -#include <cctype> -#include <cmath> -#include <cstdlib> -#include <functional> -#include <random> -#include <string_view> -#include <type_traits> - -#include "query/v2/bindings/typed_value.hpp" -#include "query/v2/conversions.hpp" -#include "query/v2/exceptions.hpp" -#include "query/v2/shard_request_manager.hpp" -#include "storage/v3/conversions.hpp" -#include "utils/string.hpp" -#include "utils/temporal.hpp" - -namespace memgraph::query::v2 { -namespace { - -//////////////////////////////////////////////////////////////////////////////// -// eDSL using template magic for describing a type of an awesome memgraph -// function and checking if the passed in arguments match the description. -// -// To use the type checking eDSL, you should put a `FType` invocation in the -// body of your awesome Memgraph function. `FType` takes type arguments as the -// description of the function type signature. Each runtime argument will be -// checked in order corresponding to given compile time type arguments. These -// type arguments can come in two forms: -// -// * final, primitive type descriptor and -// * combinator type descriptor. -// -// The primitive type descriptors are defined as empty structs, they are right -// below this documentation. -// -// Combinator type descriptors are defined as structs taking additional type -// parameters, you can find these further below in the implementation. Of -// primary interest are `Or` and `Optional` type combinators. -// -// With `Or` you can describe that an argument can be any of the types listed in -// `Or`. For example, `Or<Null, Bool, Integer>` allows an argument to be either -// `Null` or a boolean or an integer. -// -// The `Optional` combinator is used to define optional arguments to a function. -// These must come as the last positional arguments. Naturally, you can use `Or` -// inside `Optional`. So for example, `Optional<Or<Null, Bool>, Integer>` -// describes that a function takes 2 optional arguments. The 1st one must be -// either a `Null` or a boolean, while the 2nd one must be an integer. The type -// signature check will succeed in the following cases. -// -// * No optional arguments were supplied. -// * One argument was supplied and it passes `Or<Null, Bool>` check. -// * Two arguments were supplied, the 1st one passes `Or<Null, Bool>` check -// and the 2nd one passes `Integer` check. -// -// Runtime arguments to `FType` are: function name, pointer to arguments and the -// number of received arguments. -// -// Full example. -// -// FType<Or<Null, String>, NonNegativeInteger, -// Optional<NonNegativeInteger>>("substring", args, nargs); -// -// The above will check that `substring` function received the 2 required -// arguments. Optionally, the function may take a 3rd argument. The 1st argument -// must be either a `Null` or a character string. The 2nd argument is required -// to be a non-negative integer. If the 3rd argument was supplied, it will also -// be checked that it is a non-negative integer. If any of these checks fail, -// `FType` will throw a `QueryRuntimeException` with an appropriate error -// message. -//////////////////////////////////////////////////////////////////////////////// - -struct Null {}; -struct Bool {}; -struct Integer {}; -struct PositiveInteger {}; -struct NonZeroInteger {}; -struct NonNegativeInteger {}; -struct Double {}; -struct Number {}; -struct List {}; -struct String {}; -struct Map {}; -struct Edge {}; -struct Vertex {}; -struct Path {}; -struct Date {}; -struct LocalTime {}; -struct LocalDateTime {}; -struct Duration {}; - -template <class ArgType> -bool ArgIsType(const TypedValue &arg) { - if constexpr (std::is_same_v<ArgType, Null>) { - return arg.IsNull(); - } else if constexpr (std::is_same_v<ArgType, Bool>) { - return arg.IsBool(); - } else if constexpr (std::is_same_v<ArgType, Integer>) { - return arg.IsInt(); - } else if constexpr (std::is_same_v<ArgType, PositiveInteger>) { - return arg.IsInt() && arg.ValueInt() > 0; - } else if constexpr (std::is_same_v<ArgType, NonZeroInteger>) { - return arg.IsInt() && arg.ValueInt() != 0; - } else if constexpr (std::is_same_v<ArgType, NonNegativeInteger>) { - return arg.IsInt() && arg.ValueInt() >= 0; - } else if constexpr (std::is_same_v<ArgType, Double>) { - return arg.IsDouble(); - } else if constexpr (std::is_same_v<ArgType, Number>) { - return arg.IsNumeric(); - } else if constexpr (std::is_same_v<ArgType, List>) { - return arg.IsList(); - } else if constexpr (std::is_same_v<ArgType, String>) { - return arg.IsString(); - } else if constexpr (std::is_same_v<ArgType, Map>) { - return arg.IsMap(); - } else if constexpr (std::is_same_v<ArgType, Vertex>) { - return arg.IsVertex(); - } else if constexpr (std::is_same_v<ArgType, Edge>) { - return arg.IsEdge(); - } else if constexpr (std::is_same_v<ArgType, Path>) { - return arg.IsPath(); - } else if constexpr (std::is_same_v<ArgType, Date>) { - return arg.IsDate(); - } else if constexpr (std::is_same_v<ArgType, LocalTime>) { - return arg.IsLocalTime(); - } else if constexpr (std::is_same_v<ArgType, LocalDateTime>) { - return arg.IsLocalDateTime(); - } else if constexpr (std::is_same_v<ArgType, Duration>) { - return arg.IsDuration(); - } else if constexpr (std::is_same_v<ArgType, void>) { - return true; - } else { - static_assert(std::is_same_v<ArgType, Null>, "Unknown ArgType"); - } - return false; -} - -template <class ArgType> -constexpr const char *ArgTypeName() { - // The type names returned should be standardized openCypher type names. - // https://github.com/opencypher/openCypher/blob/master/docs/openCypher9.pdf - if constexpr (std::is_same_v<ArgType, Null>) { - return "null"; - } else if constexpr (std::is_same_v<ArgType, Bool>) { - return "boolean"; - } else if constexpr (std::is_same_v<ArgType, Integer>) { - return "integer"; - } else if constexpr (std::is_same_v<ArgType, PositiveInteger>) { - return "positive integer"; - } else if constexpr (std::is_same_v<ArgType, NonZeroInteger>) { - return "non-zero integer"; - } else if constexpr (std::is_same_v<ArgType, NonNegativeInteger>) { - return "non-negative integer"; - } else if constexpr (std::is_same_v<ArgType, Double>) { - return "float"; - } else if constexpr (std::is_same_v<ArgType, Number>) { - return "number"; - } else if constexpr (std::is_same_v<ArgType, List>) { - return "list"; - } else if constexpr (std::is_same_v<ArgType, String>) { - return "string"; - } else if constexpr (std::is_same_v<ArgType, Map>) { - return "map"; - } else if constexpr (std::is_same_v<ArgType, Vertex>) { - return "node"; - } else if constexpr (std::is_same_v<ArgType, Edge>) { - return "relationship"; - } else if constexpr (std::is_same_v<ArgType, Path>) { - return "path"; - } else if constexpr (std::is_same_v<ArgType, void>) { - return "void"; - } else if constexpr (std::is_same_v<ArgType, Date>) { - return "Date"; - } else if constexpr (std::is_same_v<ArgType, LocalTime>) { - return "LocalTime"; - } else if constexpr (std::is_same_v<ArgType, LocalDateTime>) { - return "LocalDateTime"; - } else if constexpr (std::is_same_v<ArgType, Duration>) { - return "Duration"; - } else { - static_assert(std::is_same_v<ArgType, Null>, "Unknown ArgType"); - } - return "<unknown-type>"; -} - -template <class... ArgType> -struct Or; - -template <class ArgType> -struct Or<ArgType> { - static bool Check(const TypedValue &arg) { return ArgIsType<ArgType>(arg); } - - static std::string TypeNames() { return ArgTypeName<ArgType>(); } -}; - -template <class ArgType, class... ArgTypes> -struct Or<ArgType, ArgTypes...> { - static bool Check(const TypedValue &arg) { - if (ArgIsType<ArgType>(arg)) return true; - return Or<ArgTypes...>::Check(arg); - } - - static std::string TypeNames() { - if constexpr (sizeof...(ArgTypes) > 1) { - return fmt::format("'{}', {}", ArgTypeName<ArgType>(), Or<ArgTypes...>::TypeNames()); - } else { - return fmt::format("'{}' or '{}'", ArgTypeName<ArgType>(), Or<ArgTypes...>::TypeNames()); - } - } -}; - -template <class T> -struct IsOrType { - static constexpr bool value = false; -}; - -template <class... ArgTypes> -struct IsOrType<Or<ArgTypes...>> { - static constexpr bool value = true; -}; - -template <class... ArgTypes> -struct Optional; - -template <class ArgType> -struct Optional<ArgType> { - static constexpr size_t size = 1; - - static void Check(const char *name, const TypedValue *args, int64_t nargs, int64_t pos) { - if (nargs == 0) return; - const TypedValue &arg = args[0]; - if constexpr (IsOrType<ArgType>::value) { - if (!ArgType::Check(arg)) { - throw QueryRuntimeException("Optional '{}' argument at position {} must be either {}.", name, pos, - ArgType::TypeNames()); - } - } else { - if (!ArgIsType<ArgType>(arg)) - throw QueryRuntimeException("Optional '{}' argument at position {} must be '{}'.", name, pos, - ArgTypeName<ArgType>()); - } - } -}; - -template <class ArgType, class... ArgTypes> -struct Optional<ArgType, ArgTypes...> { - static constexpr size_t size = 1 + sizeof...(ArgTypes); - - static void Check(const char *name, const TypedValue *args, int64_t nargs, int64_t pos) { - if (nargs == 0) return; - Optional<ArgType>::Check(name, args, nargs, pos); - Optional<ArgTypes...>::Check(name, args + 1, nargs - 1, pos + 1); - } -}; - -template <class T> -struct IsOptional { - static constexpr bool value = false; -}; - -template <class... ArgTypes> -struct IsOptional<Optional<ArgTypes...>> { - static constexpr bool value = true; -}; - -template <class ArgType, class... ArgTypes> -constexpr size_t FTypeRequiredArgs() { - if constexpr (IsOptional<ArgType>::value) { - static_assert(sizeof...(ArgTypes) == 0, "Optional arguments must be last!"); - return 0; - } else if constexpr (sizeof...(ArgTypes) == 0) { - return 1; - } else { - return 1U + FTypeRequiredArgs<ArgTypes...>(); - } -} - -template <class ArgType, class... ArgTypes> -constexpr size_t FTypeOptionalArgs() { - if constexpr (IsOptional<ArgType>::value) { - static_assert(sizeof...(ArgTypes) == 0, "Optional arguments must be last!"); - return ArgType::size; - } else if constexpr (sizeof...(ArgTypes) == 0) { - return 0; - } else { - return FTypeOptionalArgs<ArgTypes...>(); - } -} - -template <class ArgType, class... ArgTypes> -void FType(const char *name, const TypedValue *args, int64_t nargs, int64_t pos = 1) { - if constexpr (std::is_same_v<ArgType, void>) { - if (nargs != 0) { - throw QueryRuntimeException("'{}' requires no arguments.", name); - } - return; - } - static constexpr int64_t required_args = FTypeRequiredArgs<ArgType, ArgTypes...>(); - static constexpr int64_t optional_args = FTypeOptionalArgs<ArgType, ArgTypes...>(); - static constexpr int64_t total_args = required_args + optional_args; - if constexpr (optional_args > 0) { - if (nargs < required_args || nargs > total_args) { - throw QueryRuntimeException("'{}' requires between {} and {} arguments.", name, required_args, total_args); - } - } else { - if (nargs != required_args) { - throw QueryRuntimeException("'{}' requires exactly {} {}.", name, required_args, - required_args == 1 ? "argument" : "arguments"); - } - } - const TypedValue &arg = args[0]; - if constexpr (IsOrType<ArgType>::value) { - if (!ArgType::Check(arg)) { - throw QueryRuntimeException("'{}' argument at position {} must be either {}.", name, pos, ArgType::TypeNames()); - } - } else if constexpr (IsOptional<ArgType>::value) { - static_assert(sizeof...(ArgTypes) == 0, "Optional arguments must be last!"); - ArgType::Check(name, args, nargs, pos); - } else { - if (!ArgIsType<ArgType>(arg)) { - throw QueryRuntimeException("'{}' argument at position {} must be '{}'", name, pos, ArgTypeName<ArgType>()); - } - } - if constexpr (sizeof...(ArgTypes) > 0) { - FType<ArgTypes...>(name, args + 1, nargs - 1, pos + 1); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// END function type description eDSL -//////////////////////////////////////////////////////////////////////////////// - -// Predicate functions. -// Neo4j has all, any, exists, none, single -// Those functions are a little bit different since they take a filterExpression -// as an argument. -// There is all, any, none and single productions in opencypher grammar, but it -// will be trivial to also add exists. -// TODO: Implement this. - -// Scalar functions. -// We don't have a way to implement id function since we don't store any. If it -// is really neccessary we could probably map vlist* to id. -// TODO: Implement length (it works on a path, but we didn't define path -// structure yet). -// TODO: Implement size(pattern), for example size((a)-[:X]-()) should return -// number of results of this pattern. I don't think we will ever do this. -// TODO: Implement rest of the list functions. -// TODO: Implement degrees, haversin, radians -// TODO: Implement spatial functions - -TypedValue EndNode(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, Edge>>("endNode", args, nargs); - if (args[0].IsNull()) return TypedValue(ctx.memory); - return TypedValue(args[0].ValueEdge().To(), ctx.memory); -} - -TypedValue Head(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, List>>("head", args, nargs); - if (args[0].IsNull()) return TypedValue(ctx.memory); - const auto &list = args[0].ValueList(); - if (list.empty()) return TypedValue(ctx.memory); - return TypedValue(list[0], ctx.memory); -} - -TypedValue Last(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, List>>("last", args, nargs); - if (args[0].IsNull()) return TypedValue(ctx.memory); - const auto &list = args[0].ValueList(); - if (list.empty()) return TypedValue(ctx.memory); - return TypedValue(list.back(), ctx.memory); -} - -TypedValue Properties(const TypedValue * /*args*/, int64_t /*nargs*/, const FunctionContext & /*ctx*/) { return {}; } - -TypedValue Size(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, List, String, Map, Path>>("size", args, nargs); - const auto &value = args[0]; - if (value.IsNull()) { - return TypedValue(ctx.memory); - } else if (value.IsList()) { - return TypedValue(static_cast<int64_t>(value.ValueList().size()), ctx.memory); - } else if (value.IsString()) { - return TypedValue(static_cast<int64_t>(value.ValueString().size()), ctx.memory); - } else if (value.IsMap()) { - // neo4j doesn't implement size for map, but I don't see a good reason not - // to do it. - return TypedValue(static_cast<int64_t>(value.ValueMap().size()), ctx.memory); - } else { - // TODO(kostasrim) Fix the dummy return - return TypedValue(int64_t(0), ctx.memory); - } -} - -TypedValue StartNode(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, Edge>>("startNode", args, nargs); - if (args[0].IsNull()) return TypedValue(ctx.memory); - return TypedValue(args[0].ValueEdge().From(), ctx.memory); -} - -TypedValue ToBoolean(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, Bool, Integer, String>>("toBoolean", args, nargs); - const auto &value = args[0]; - if (value.IsNull()) { - return TypedValue(ctx.memory); - } else if (value.IsBool()) { - return TypedValue(value.ValueBool(), ctx.memory); - } else if (value.IsInt()) { - return TypedValue(value.ValueInt() != 0L, ctx.memory); - } else { - auto s = utils::ToUpperCase(utils::Trim(value.ValueString())); - if (s == "TRUE") return TypedValue(true, ctx.memory); - if (s == "FALSE") return TypedValue(false, ctx.memory); - // I think this is just stupid and that exception should be thrown, but - // neo4j does it this way... - return TypedValue(ctx.memory); - } -} - -TypedValue ToFloat(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, Number, String>>("toFloat", args, nargs); - const auto &value = args[0]; - if (value.IsNull()) { - return TypedValue(ctx.memory); - } else if (value.IsInt()) { - return TypedValue(static_cast<double>(value.ValueInt()), ctx.memory); - } else if (value.IsDouble()) { - return TypedValue(value, ctx.memory); - } else { - try { - return TypedValue(utils::ParseDouble(utils::Trim(value.ValueString())), ctx.memory); - } catch (const utils::BasicException &) { - return TypedValue(ctx.memory); - } - } -} - -TypedValue ToInteger(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, Bool, Number, String>>("toInteger", args, nargs); - const auto &value = args[0]; - if (value.IsNull()) { - return TypedValue(ctx.memory); - } else if (value.IsBool()) { - return TypedValue(value.ValueBool() ? 1L : 0L, ctx.memory); - } else if (value.IsInt()) { - return TypedValue(value, ctx.memory); - } else if (value.IsDouble()) { - return TypedValue(static_cast<int64_t>(value.ValueDouble()), ctx.memory); - } else { - try { - // Yup, this is correct. String is valid if it has floating point - // number, then it is parsed and converted to int. - return TypedValue(static_cast<int64_t>(utils::ParseDouble(utils::Trim(value.ValueString()))), ctx.memory); - } catch (const utils::BasicException &) { - return TypedValue(ctx.memory); - } - } -} - -TypedValue Type(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, Edge>>("type", args, nargs); - if (args[0].IsNull()) return TypedValue(ctx.memory); - return TypedValue(args[0].ValueEdge().EdgeType().AsInt(), ctx.memory); -} - -TypedValue ValueType(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, Bool, Integer, Double, String, List, Map, Vertex, Edge, Path>>("type", args, nargs); - // The type names returned should be standardized openCypher type names. - // https://github.com/opencypher/openCypher/blob/master/docs/openCypher9.pdf - switch (args[0].type()) { - case TypedValue::Type::Null: - return TypedValue("NULL", ctx.memory); - case TypedValue::Type::Bool: - return TypedValue("BOOLEAN", ctx.memory); - case TypedValue::Type::Int: - return TypedValue("INTEGER", ctx.memory); - case TypedValue::Type::Double: - return TypedValue("FLOAT", ctx.memory); - case TypedValue::Type::String: - return TypedValue("STRING", ctx.memory); - case TypedValue::Type::List: - return TypedValue("LIST", ctx.memory); - case TypedValue::Type::Map: - return TypedValue("MAP", ctx.memory); - case TypedValue::Type::Vertex: - return TypedValue("NODE", ctx.memory); - case TypedValue::Type::Edge: - return TypedValue("RELATIONSHIP", ctx.memory); - case TypedValue::Type::Path: - return TypedValue("PATH", ctx.memory); - case TypedValue::Type::Date: - return TypedValue("DATE", ctx.memory); - case TypedValue::Type::LocalTime: - return TypedValue("LOCAL_TIME", ctx.memory); - case TypedValue::Type::LocalDateTime: - return TypedValue("LOCAL_DATE_TIME", ctx.memory); - case TypedValue::Type::Duration: - return TypedValue("DURATION", ctx.memory); - } -} - -TypedValue Range(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, Integer>, Or<Null, Integer>, Optional<Or<Null, NonZeroInteger>>>("range", args, nargs); - for (int64_t i = 0; i < nargs; ++i) - if (args[i].IsNull()) return TypedValue(ctx.memory); - auto lbound = args[0].ValueInt(); - auto rbound = args[1].ValueInt(); - int64_t step = nargs == 3 ? args[2].ValueInt() : 1; - TypedValue::TVector list(ctx.memory); - if (lbound <= rbound && step > 0) { - for (auto i = lbound; i <= rbound; i += step) { - list.emplace_back(i); - } - } else if (lbound >= rbound && step < 0) { - for (auto i = lbound; i >= rbound; i += step) { - list.emplace_back(i); - } - } - return TypedValue(std::move(list)); -} - -TypedValue Tail(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, List>>("tail", args, nargs); - if (args[0].IsNull()) return TypedValue(ctx.memory); - TypedValue::TVector list(args[0].ValueList(), ctx.memory); - if (list.empty()) return TypedValue(std::move(list)); - list.erase(list.begin()); - return TypedValue(std::move(list)); -} - -TypedValue UniformSample(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, List>, Or<Null, NonNegativeInteger>>("uniformSample", args, nargs); - static thread_local std::mt19937 pseudo_rand_gen_{std::random_device{}()}; - if (args[0].IsNull() || args[1].IsNull()) return TypedValue(ctx.memory); - const auto &population = args[0].ValueList(); - auto population_size = population.size(); - if (population_size == 0) return TypedValue(ctx.memory); - auto desired_length = args[1].ValueInt(); - std::uniform_int_distribution<uint64_t> rand_dist{0, population_size - 1}; - TypedValue::TVector sampled(ctx.memory); - sampled.reserve(desired_length); - for (int64_t i = 0; i < desired_length; ++i) { - sampled.emplace_back(population[rand_dist(pseudo_rand_gen_)]); - } - return TypedValue(std::move(sampled)); -} - -TypedValue Abs(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, Number>>("abs", args, nargs); - const auto &value = args[0]; - if (value.IsNull()) { - return TypedValue(ctx.memory); - } else if (value.IsInt()) { - return TypedValue(std::abs(value.ValueInt()), ctx.memory); - } else { - return TypedValue(std::abs(value.ValueDouble()), ctx.memory); - } -} - -#define WRAP_CMATH_FLOAT_FUNCTION(name, lowercased_name) \ - TypedValue name(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { \ - FType<Or<Null, Number>>(#lowercased_name, args, nargs); \ - const auto &value = args[0]; \ - if (value.IsNull()) { \ - return TypedValue(ctx.memory); \ - } else if (value.IsInt()) { \ - return TypedValue(lowercased_name(value.ValueInt()), ctx.memory); \ - } else { \ - return TypedValue(lowercased_name(value.ValueDouble()), ctx.memory); \ - } \ - } - -WRAP_CMATH_FLOAT_FUNCTION(Ceil, ceil) -WRAP_CMATH_FLOAT_FUNCTION(Floor, floor) -// We are not completely compatible with neoj4 in this function because, -// neo4j rounds -0.5, -1.5, -2.5... to 0, -1, -2... -WRAP_CMATH_FLOAT_FUNCTION(Round, round) -WRAP_CMATH_FLOAT_FUNCTION(Exp, exp) -WRAP_CMATH_FLOAT_FUNCTION(Log, log) -WRAP_CMATH_FLOAT_FUNCTION(Log10, log10) -WRAP_CMATH_FLOAT_FUNCTION(Sqrt, sqrt) -WRAP_CMATH_FLOAT_FUNCTION(Acos, acos) -WRAP_CMATH_FLOAT_FUNCTION(Asin, asin) -WRAP_CMATH_FLOAT_FUNCTION(Atan, atan) -WRAP_CMATH_FLOAT_FUNCTION(Cos, cos) -WRAP_CMATH_FLOAT_FUNCTION(Sin, sin) -WRAP_CMATH_FLOAT_FUNCTION(Tan, tan) - -#undef WRAP_CMATH_FLOAT_FUNCTION - -TypedValue Atan2(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, Number>, Or<Null, Number>>("atan2", args, nargs); - if (args[0].IsNull() || args[1].IsNull()) return TypedValue(ctx.memory); - auto to_double = [](const TypedValue &t) -> double { - if (t.IsInt()) { - return t.ValueInt(); - } else { - return t.ValueDouble(); - } - }; - double y = to_double(args[0]); - double x = to_double(args[1]); - return TypedValue(atan2(y, x), ctx.memory); -} - -TypedValue Sign(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, Number>>("sign", args, nargs); - auto sign = [&](auto x) { return TypedValue((0 < x) - (x < 0), ctx.memory); }; - const auto &value = args[0]; - if (value.IsNull()) { - return TypedValue(ctx.memory); - } else if (value.IsInt()) { - return sign(value.ValueInt()); - } else { - return sign(value.ValueDouble()); - } -} - -TypedValue E(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<void>("e", args, nargs); - return TypedValue(M_E, ctx.memory); -} - -TypedValue Pi(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<void>("pi", args, nargs); - return TypedValue(M_PI, ctx.memory); -} - -TypedValue Rand(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<void>("rand", args, nargs); - static thread_local std::mt19937 pseudo_rand_gen_{std::random_device{}()}; - static thread_local std::uniform_real_distribution<> rand_dist_{0, 1}; - return TypedValue(rand_dist_(pseudo_rand_gen_), ctx.memory); -} - -template <class TPredicate> -TypedValue StringMatchOperator(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, String>, Or<Null, String>>(TPredicate::name, args, nargs); - if (args[0].IsNull() || args[1].IsNull()) return TypedValue(ctx.memory); - const auto &s1 = args[0].ValueString(); - const auto &s2 = args[1].ValueString(); - return TypedValue(TPredicate{}(s1, s2), ctx.memory); -} - -// Check if s1 starts with s2. -struct StartsWithPredicate { - static constexpr const char *name = "startsWith"; - bool operator()(const TypedValue::TString &s1, const TypedValue::TString &s2) const { - if (s1.size() < s2.size()) return false; - return std::equal(s2.begin(), s2.end(), s1.begin()); - } -}; -auto StartsWith = StringMatchOperator<StartsWithPredicate>; - -// Check if s1 ends with s2. -struct EndsWithPredicate { - static constexpr const char *name = "endsWith"; - bool operator()(const TypedValue::TString &s1, const TypedValue::TString &s2) const { - if (s1.size() < s2.size()) return false; - return std::equal(s2.rbegin(), s2.rend(), s1.rbegin()); - } -}; -auto EndsWith = StringMatchOperator<EndsWithPredicate>; - -// Check if s1 contains s2. -struct ContainsPredicate { - static constexpr const char *name = "contains"; - bool operator()(const TypedValue::TString &s1, const TypedValue::TString &s2) const { - if (s1.size() < s2.size()) return false; - return s1.find(s2) != std::string::npos; - } -}; -auto Contains = StringMatchOperator<ContainsPredicate>; - -TypedValue Assert(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Bool, Optional<String>>("assert", args, nargs); - if (!args[0].ValueBool()) { - std::string message("Assertion failed"); - if (nargs == 2) { - message += ": "; - message += args[1].ValueString(); - } - message += "."; - throw QueryRuntimeException(message); - } - return TypedValue(args[0], ctx.memory); -} - -TypedValue Counter(const TypedValue *args, int64_t nargs, const FunctionContext &context) { - FType<String, Integer, Optional<NonZeroInteger>>("counter", args, nargs); - int64_t step = 1; - if (nargs == 3) { - step = args[2].ValueInt(); - } - - auto [it, inserted] = context.counters->emplace(args[0].ValueString(), args[1].ValueInt()); - auto value = it->second; - it->second += step; - - return TypedValue(value, context.memory); -} - -TypedValue Id(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, Vertex, Edge>>("id", args, nargs); - const auto &arg = args[0]; - if (arg.IsNull()) { - return TypedValue(ctx.memory); - } else if (arg.IsVertex()) { - return TypedValue(int64_t(arg.ValueVertex().CypherId()), ctx.memory); - } else { - return TypedValue(int64_t(arg.ValueEdge().CypherId()), ctx.memory); - } -} - -TypedValue ToString(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, String, Number, Bool>>("toString", args, nargs); - const auto &arg = args[0]; - if (arg.IsNull()) { - return TypedValue(ctx.memory); - } else if (arg.IsString()) { - return TypedValue(arg, ctx.memory); - } else if (arg.IsInt()) { - // TODO: This is making a pointless copy of std::string, we may want to - // use a different conversion to string - return TypedValue(std::to_string(arg.ValueInt()), ctx.memory); - } else if (arg.IsDouble()) { - return TypedValue(std::to_string(arg.ValueDouble()), ctx.memory); - } else { - return TypedValue(arg.ValueBool() ? "true" : "false", ctx.memory); - } -} - -TypedValue Timestamp(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Optional<Or<Date, LocalTime, LocalDateTime, Duration>>>("timestamp", args, nargs); - const auto &arg = *args; - if (arg.IsDate()) { - return TypedValue(arg.ValueDate().MicrosecondsSinceEpoch(), ctx.memory); - } - if (arg.IsLocalTime()) { - return TypedValue(arg.ValueLocalTime().MicrosecondsSinceEpoch(), ctx.memory); - } - if (arg.IsLocalDateTime()) { - return TypedValue(arg.ValueLocalDateTime().MicrosecondsSinceEpoch(), ctx.memory); - } - if (arg.IsDuration()) { - return TypedValue(arg.ValueDuration().microseconds, ctx.memory); - } - return TypedValue(ctx.timestamp, ctx.memory); -} - -TypedValue Left(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, String>, Or<Null, NonNegativeInteger>>("left", args, nargs); - if (args[0].IsNull() || args[1].IsNull()) return TypedValue(ctx.memory); - return TypedValue(utils::Substr(args[0].ValueString(), 0, args[1].ValueInt()), ctx.memory); -} - -TypedValue Right(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, String>, Or<Null, NonNegativeInteger>>("right", args, nargs); - if (args[0].IsNull() || args[1].IsNull()) return TypedValue(ctx.memory); - const auto &str = args[0].ValueString(); - auto len = args[1].ValueInt(); - return len <= str.size() ? TypedValue(utils::Substr(str, str.size() - len, len), ctx.memory) - : TypedValue(str, ctx.memory); -} - -TypedValue CallStringFunction(const TypedValue *args, int64_t nargs, utils::MemoryResource *memory, const char *name, - std::function<TypedValue::TString(const TypedValue::TString &)> fun) { - FType<Or<Null, String>>(name, args, nargs); - if (args[0].IsNull()) return TypedValue(memory); - return TypedValue(fun(args[0].ValueString()), memory); -} - -TypedValue LTrim(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - return CallStringFunction(args, nargs, ctx.memory, "lTrim", - [&](const auto &str) { return TypedValue::TString(utils::LTrim(str), ctx.memory); }); -} - -TypedValue RTrim(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - return CallStringFunction(args, nargs, ctx.memory, "rTrim", - [&](const auto &str) { return TypedValue::TString(utils::RTrim(str), ctx.memory); }); -} - -TypedValue Trim(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - return CallStringFunction(args, nargs, ctx.memory, "trim", - [&](const auto &str) { return TypedValue::TString(utils::Trim(str), ctx.memory); }); -} - -TypedValue Reverse(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - return CallStringFunction(args, nargs, ctx.memory, "reverse", - [&](const auto &str) { return utils::Reversed(str, ctx.memory); }); -} - -TypedValue ToLower(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - return CallStringFunction(args, nargs, ctx.memory, "toLower", [&](const auto &str) { - TypedValue::TString res(ctx.memory); - utils::ToLowerCase(&res, str); - return res; - }); -} - -TypedValue ToUpper(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - return CallStringFunction(args, nargs, ctx.memory, "toUpper", [&](const auto &str) { - TypedValue::TString res(ctx.memory); - utils::ToUpperCase(&res, str); - return res; - }); -} - -TypedValue Replace(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, String>, Or<Null, String>, Or<Null, String>>("replace", args, nargs); - if (args[0].IsNull() || args[1].IsNull() || args[2].IsNull()) { - return TypedValue(ctx.memory); - } - TypedValue::TString replaced(ctx.memory); - utils::Replace(&replaced, args[0].ValueString(), args[1].ValueString(), args[2].ValueString()); - return TypedValue(std::move(replaced)); -} - -TypedValue Split(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, String>, Or<Null, String>>("split", args, nargs); - if (args[0].IsNull() || args[1].IsNull()) { - return TypedValue(ctx.memory); - } - TypedValue::TVector result(ctx.memory); - utils::Split(&result, args[0].ValueString(), args[1].ValueString()); - return TypedValue(std::move(result)); -} - -TypedValue Substring(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<Null, String>, NonNegativeInteger, Optional<NonNegativeInteger>>("substring", args, nargs); - if (args[0].IsNull()) return TypedValue(ctx.memory); - const auto &str = args[0].ValueString(); - auto start = args[1].ValueInt(); - if (nargs == 2) return TypedValue(utils::Substr(str, start), ctx.memory); - auto len = args[2].ValueInt(); - return TypedValue(utils::Substr(str, start, len), ctx.memory); -} - -TypedValue ToByteString(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<String>("toByteString", args, nargs); - const auto &str = args[0].ValueString(); - if (str.empty()) return TypedValue("", ctx.memory); - if (!utils::StartsWith(str, "0x") && !utils::StartsWith(str, "0X")) { - throw QueryRuntimeException("'toByteString' argument must start with '0x'"); - } - const auto &hex_str = utils::Substr(str, 2); - auto read_hex = [](const char ch) -> unsigned char { - if (ch >= '0' && ch <= '9') return ch - '0'; - if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; - if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; - throw QueryRuntimeException("'toByteString' argument has an invalid character '{}'", ch); - }; - utils::pmr::string bytes(ctx.memory); - bytes.reserve((1 + hex_str.size()) / 2); - size_t i = 0; - // Treat odd length hex string as having a leading zero. - if (hex_str.size() % 2) bytes.append(1, read_hex(hex_str[i++])); - for (; i < hex_str.size(); i += 2) { - unsigned char byte = read_hex(hex_str[i]) * 16U + read_hex(hex_str[i + 1]); - // MemcpyCast in case we are converting to a signed value, so as to avoid - // undefined behaviour. - bytes.append(1, utils::MemcpyCast<decltype(bytes)::value_type>(byte)); - } - return TypedValue(std::move(bytes)); -} - -TypedValue FromByteString(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<String, Optional<PositiveInteger>>("fromByteString", args, nargs); - const auto &bytes = args[0].ValueString(); - if (bytes.empty()) return TypedValue("", ctx.memory); - size_t min_length = bytes.size(); - if (nargs == 2) min_length = std::max(min_length, static_cast<size_t>(args[1].ValueInt())); - utils::pmr::string str(ctx.memory); - str.reserve(min_length * 2 + 2); - str.append("0x"); - for (size_t pad = 0; pad < min_length - bytes.size(); ++pad) str.append(2, '0'); - // Convert the bytes to a character string in hex representation. - // Unfortunately, we don't know whether the default `char` is signed or - // unsigned, so we have to work around any potential undefined behaviour when - // conversions between the 2 occur. That's why this function is more - // complicated than it should be. - auto to_hex = [](const unsigned char val) -> char { - unsigned char ch = val < 10U ? static_cast<unsigned char>('0') + val : static_cast<unsigned char>('a') + val - 10U; - return utils::MemcpyCast<char>(ch); - }; - for (unsigned char byte : bytes) { - str.append(1, to_hex(byte / 16U)); - str.append(1, to_hex(byte % 16U)); - } - return TypedValue(std::move(str)); -} - -template <typename T> -concept IsNumberOrInteger = utils::SameAsAnyOf<T, Number, Integer>; - -template <IsNumberOrInteger ArgType> -void MapNumericParameters(auto ¶meter_mappings, const auto &input_parameters) { - for (const auto &[key, value] : input_parameters) { - if (auto it = parameter_mappings.find(key); it != parameter_mappings.end()) { - if (value.IsInt()) { - *it->second = value.ValueInt(); - } else if (std::is_same_v<ArgType, Number> && value.IsDouble()) { - *it->second = value.ValueDouble(); - } else { - std::string_view error = std::is_same_v<ArgType, Integer> ? "an integer." : "a numeric value."; - throw QueryRuntimeException("Invalid value for key '{}'. Expected {}", key, error); - } - } else { - throw QueryRuntimeException("Unknown key '{}'.", key); - } - } -} - -TypedValue Date(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Optional<Or<String, Map>>>("date", args, nargs); - if (nargs == 0) { - return TypedValue(utils::LocalDateTime(ctx.timestamp).date, ctx.memory); - } - - if (args[0].IsString()) { - const auto &[date_parameters, is_extended] = utils::ParseDateParameters(args[0].ValueString()); - return TypedValue(utils::Date(date_parameters), ctx.memory); - } - - utils::DateParameters date_parameters; - - using namespace std::literals; - std::unordered_map parameter_mappings = {std::pair{"year"sv, &date_parameters.year}, - std::pair{"month"sv, &date_parameters.month}, - std::pair{"day"sv, &date_parameters.day}}; - - MapNumericParameters<Integer>(parameter_mappings, args[0].ValueMap()); - return TypedValue(utils::Date(date_parameters), ctx.memory); -} - -TypedValue LocalTime(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Optional<Or<String, Map>>>("localtime", args, nargs); - - if (nargs == 0) { - return TypedValue(utils::LocalDateTime(ctx.timestamp).local_time, ctx.memory); - } - - if (args[0].IsString()) { - const auto &[local_time_parameters, is_extended] = utils::ParseLocalTimeParameters(args[0].ValueString()); - return TypedValue(utils::LocalTime(local_time_parameters), ctx.memory); - } - - utils::LocalTimeParameters local_time_parameters; - - using namespace std::literals; - std::unordered_map parameter_mappings{ - std::pair{"hour"sv, &local_time_parameters.hour}, - std::pair{"minute"sv, &local_time_parameters.minute}, - std::pair{"second"sv, &local_time_parameters.second}, - std::pair{"millisecond"sv, &local_time_parameters.millisecond}, - std::pair{"microsecond"sv, &local_time_parameters.microsecond}, - }; - - MapNumericParameters<Integer>(parameter_mappings, args[0].ValueMap()); - return TypedValue(utils::LocalTime(local_time_parameters), ctx.memory); -} - -TypedValue LocalDateTime(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Optional<Or<String, Map>>>("localdatetime", args, nargs); - - if (nargs == 0) { - return TypedValue(utils::LocalDateTime(ctx.timestamp), ctx.memory); - } - - if (args[0].IsString()) { - const auto &[date_parameters, local_time_parameters] = ParseLocalDateTimeParameters(args[0].ValueString()); - return TypedValue(utils::LocalDateTime(date_parameters, local_time_parameters), ctx.memory); - } - - utils::DateParameters date_parameters; - utils::LocalTimeParameters local_time_parameters; - using namespace std::literals; - std::unordered_map parameter_mappings{ - std::pair{"year"sv, &date_parameters.year}, - std::pair{"month"sv, &date_parameters.month}, - std::pair{"day"sv, &date_parameters.day}, - std::pair{"hour"sv, &local_time_parameters.hour}, - std::pair{"minute"sv, &local_time_parameters.minute}, - std::pair{"second"sv, &local_time_parameters.second}, - std::pair{"millisecond"sv, &local_time_parameters.millisecond}, - std::pair{"microsecond"sv, &local_time_parameters.microsecond}, - }; - - MapNumericParameters<Integer>(parameter_mappings, args[0].ValueMap()); - return TypedValue(utils::LocalDateTime(date_parameters, local_time_parameters), ctx.memory); -} - -TypedValue Duration(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType<Or<String, Map>>("duration", args, nargs); - - if (args[0].IsString()) { - return TypedValue(utils::Duration(ParseDurationParameters(args[0].ValueString())), ctx.memory); - } - - utils::DurationParameters duration_parameters; - using namespace std::literals; - std::unordered_map parameter_mappings{std::pair{"day"sv, &duration_parameters.day}, - std::pair{"hour"sv, &duration_parameters.hour}, - std::pair{"minute"sv, &duration_parameters.minute}, - std::pair{"second"sv, &duration_parameters.second}, - std::pair{"millisecond"sv, &duration_parameters.millisecond}, - std::pair{"microsecond"sv, &duration_parameters.microsecond}}; - MapNumericParameters<Number>(parameter_mappings, args[0].ValueMap()); - return TypedValue(utils::Duration(duration_parameters), ctx.memory); -} - -} // namespace - -std::function<TypedValue(const TypedValue *, int64_t, const FunctionContext &ctx)> NameToFunction( - const std::string &function_name) { - // Scalar functions - if (function_name == "ENDNODE") return EndNode; - if (function_name == "HEAD") return Head; - if (function_name == kId) return Id; - if (function_name == "LAST") return Last; - if (function_name == "PROPERTIES") return Properties; - if (function_name == "SIZE") return Size; - if (function_name == "STARTNODE") return StartNode; - if (function_name == "TIMESTAMP") return Timestamp; - if (function_name == "TOBOOLEAN") return ToBoolean; - if (function_name == "TOFLOAT") return ToFloat; - if (function_name == "TOINTEGER") return ToInteger; - if (function_name == "TYPE") return Type; - if (function_name == "VALUETYPE") return ValueType; - - // List functions - if (function_name == "RANGE") return Range; - if (function_name == "TAIL") return Tail; - if (function_name == "UNIFORMSAMPLE") return UniformSample; - - // Mathematical functions - numeric - if (function_name == "ABS") return Abs; - if (function_name == "CEIL") return Ceil; - if (function_name == "FLOOR") return Floor; - if (function_name == "RAND") return Rand; - if (function_name == "ROUND") return Round; - if (function_name == "SIGN") return Sign; - - // Mathematical functions - logarithmic - if (function_name == "E") return E; - if (function_name == "EXP") return Exp; - if (function_name == "LOG") return Log; - if (function_name == "LOG10") return Log10; - if (function_name == "SQRT") return Sqrt; - - // Mathematical functions - trigonometric - if (function_name == "ACOS") return Acos; - if (function_name == "ASIN") return Asin; - if (function_name == "ATAN") return Atan; - if (function_name == "ATAN2") return Atan2; - if (function_name == "COS") return Cos; - if (function_name == "PI") return Pi; - if (function_name == "SIN") return Sin; - if (function_name == "TAN") return Tan; - - // String functions - if (function_name == kContains) return Contains; - if (function_name == kEndsWith) return EndsWith; - if (function_name == "LEFT") return Left; - if (function_name == "LTRIM") return LTrim; - if (function_name == "REPLACE") return Replace; - if (function_name == "REVERSE") return Reverse; - if (function_name == "RIGHT") return Right; - if (function_name == "RTRIM") return RTrim; - if (function_name == "SPLIT") return Split; - if (function_name == kStartsWith) return StartsWith; - if (function_name == "SUBSTRING") return Substring; - if (function_name == "TOLOWER") return ToLower; - // TODO(kostasrim) fix function lookup here - if (function_name == "TOSTRING") return ToString; - if (function_name == "TOUPPER") return ToUpper; - if (function_name == "TRIM") return Trim; - - // Memgraph specific functions - if (function_name == "ASSERT") return Assert; - if (function_name == "COUNTER") return Counter; - if (function_name == "TOBYTESTRING") return ToByteString; - if (function_name == "FROMBYTESTRING") return FromByteString; - - // Functions for temporal types - if (function_name == "DATE") return Date; - if (function_name == "LOCALTIME") return LocalTime; - if (function_name == "LOCALDATETIME") return LocalDateTime; - if (function_name == "DURATION") return Duration; - - return nullptr; -} - -} // namespace memgraph::query::v2 diff --git a/src/query/v2/plan/preprocess.cpp b/src/query/v2/plan/preprocess.cpp index 1037f3fe6..765066079 100644 --- a/src/query/v2/plan/preprocess.cpp +++ b/src/query/v2/plan/preprocess.cpp @@ -398,7 +398,7 @@ void Filters::AnalyzeAndStoreFilter(Expression *expr, const SymbolTable &symbol_ auto add_id_equal = [&](auto *maybe_id_fun, auto *val_expr) -> bool { auto *id_fun = utils::Downcast<Function>(maybe_id_fun); if (!id_fun) return false; - if (id_fun->function_name_ != kId) return false; + if (id_fun->function_name_ != functions::kId) return false; if (id_fun->arguments_.size() != 1U) return false; auto *ident = utils::Downcast<Identifier>(id_fun->arguments_.front()); if (!ident) return false; diff --git a/src/query/v2/shard_request_manager.hpp b/src/query/v2/shard_request_manager.hpp index 68a10f384..581dfbcab 100644 --- a/src/query/v2/shard_request_manager.hpp +++ b/src/query/v2/shard_request_manager.hpp @@ -125,9 +125,12 @@ class ShardRequestManagerInterface { virtual storage::v3::EdgeTypeId NameToEdgeType(const std::string &name) const = 0; virtual storage::v3::PropertyId NameToProperty(const std::string &name) const = 0; virtual storage::v3::LabelId NameToLabel(const std::string &name) const = 0; - virtual const std::string &PropertyToName(storage::v3::PropertyId prop) const = 0; - virtual const std::string &LabelToName(storage::v3::LabelId label) const = 0; - virtual const std::string &EdgeTypeToName(storage::v3::EdgeTypeId type) const = 0; + virtual const std::string &PropertyToName(memgraph::storage::v3::PropertyId prop) const = 0; + virtual const std::string &LabelToName(memgraph::storage::v3::LabelId label) const = 0; + virtual const std::string &EdgeTypeToName(memgraph::storage::v3::EdgeTypeId type) const = 0; + virtual std::optional<storage::v3::PropertyId> MaybeNameToProperty(const std::string &name) const = 0; + virtual std::optional<storage::v3::EdgeTypeId> MaybeNameToEdgeType(const std::string &name) const = 0; + virtual std::optional<storage::v3::LabelId> MaybeNameToLabel(const std::string &name) const = 0; virtual bool IsPrimaryLabel(storage::v3::LabelId label) const = 0; virtual bool IsPrimaryKey(storage::v3::LabelId primary_label, storage::v3::PropertyId property) const = 0; }; @@ -352,6 +355,18 @@ class ShardRequestManager : public ShardRequestManagerInterface { return result_rows; } + std::optional<storage::v3::PropertyId> MaybeNameToProperty(const std::string &name) const override { + return shards_map_.GetPropertyId(name); + } + + std::optional<storage::v3::EdgeTypeId> MaybeNameToEdgeType(const std::string &name) const override { + return shards_map_.GetEdgeTypeId(name); + } + + std::optional<storage::v3::LabelId> MaybeNameToLabel(const std::string &name) const override { + return shards_map_.GetLabelId(name); + } + private: enum class PaginatedResponseState { Pending, PartiallyFinished }; diff --git a/src/storage/v3/CMakeLists.txt b/src/storage/v3/CMakeLists.txt index 5a824534b..2b3a7c861 100644 --- a/src/storage/v3/CMakeLists.txt +++ b/src/storage/v3/CMakeLists.txt @@ -31,4 +31,4 @@ target_link_libraries(mg-storage-v3 Threads::Threads mg-utils gflags) target_include_directories(mg-storage-v3 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bindings) add_dependencies(mg-storage-v3 generate_lcp_storage) -target_link_libraries(mg-storage-v3 mg-slk mg-expr mg-io) +target_link_libraries(mg-storage-v3 mg-slk mg-expr mg-io mg-functions) diff --git a/src/storage/v3/bindings/ast/ast.lcp b/src/storage/v3/bindings/ast/ast.lcp index 3be1830ae..8a3484a58 100644 --- a/src/storage/v3/bindings/ast/ast.lcp +++ b/src/storage/v3/bindings/ast/ast.lcp @@ -24,6 +24,8 @@ #include "storage/v3/conversions.hpp" #include "storage/v3/path.hpp" #include "utils/typeinfo.hpp" +#include "utils/exceptions.hpp" +#include "functions/awesome_memgraph_functions.hpp" cpp<# @@ -178,14 +180,6 @@ cpp<# (:serialize (:slk :load-args '((storage "storage::v3::AstStorage *"))))) #>cpp -struct FunctionContext { - DbAccessor *db_accessor; - utils::MemoryResource *memory; - int64_t timestamp; - std::unordered_map<std::string, int64_t> *counters; - View view; -}; - inline bool operator==(const LabelIx &a, const LabelIx &b) { return a.ix == b.ix && a.name == b.name; } @@ -845,16 +839,24 @@ cpp<# :slk-load (slk-load-ast-vector "Expression")) (function-name "std::string" :scope :public) (function "std::function<TypedValue(const TypedValue *, int64_t, - const FunctionContext &)>" + const functions::FunctionContext<memgraph::storage::v3::DbAccessor> &)>" :scope :public :dont-save t :clone :copy :slk-load (lambda (member) #>cpp + self->${member} = functions::NameToFunction<memgraph::storage::v3::TypedValue, + functions::FunctionContext<memgraph::storage::v3::DbAccessor>, + functions::StorageEngineTag, Conv>(self->function_name_); cpp<#))) (:public #>cpp Function() = default; + using Conv = decltype(PropertyToTypedValueFunctor<TypedValue>); + class SemanticException : public memgraph::utils::BasicException { + using utils::BasicException::BasicException; + }; + DEFVISITABLE(ExpressionVisitor<TypedValue>); DEFVISITABLE(ExpressionVisitor<void>); @@ -872,7 +874,13 @@ cpp<# Function(const std::string &function_name, const std::vector<Expression *> &arguments) : arguments_(arguments), - function_name_(function_name) { + function_name_(function_name), + function_(functions::NameToFunction<memgraph::storage::v3::TypedValue, + functions::FunctionContext<memgraph::storage::v3::DbAccessor>, + functions::StorageEngineTag, Conv>(function_name_)) { + if (!function_) { + throw SemanticException("Function '{}' doesn't exist.", function_name); + } } cpp<#) (:private diff --git a/src/storage/v3/bindings/db_accessor.hpp b/src/storage/v3/bindings/db_accessor.hpp index 1a5e846fa..6392ed18f 100644 --- a/src/storage/v3/bindings/db_accessor.hpp +++ b/src/storage/v3/bindings/db_accessor.hpp @@ -88,7 +88,7 @@ class DbAccessor final { } storage::v3::ShardResult<std::optional<EdgeAccessor>> RemoveEdge(EdgeAccessor *edge) { - auto res = accessor_->DeleteEdge(edge->FromVertex(), edge->ToVertex(), edge->Gid()); + auto res = accessor_->DeleteEdge(edge->From(), edge->To(), edge->Gid()); if (res.HasError()) { return res.GetError(); } diff --git a/src/storage/v3/conversions.hpp b/src/storage/v3/conversions.hpp index 3ff804b6a..dfb190ac1 100644 --- a/src/storage/v3/conversions.hpp +++ b/src/storage/v3/conversions.hpp @@ -69,6 +69,10 @@ TTypedValue PropertyToTypedValue(const PropertyValue &value) { LOG_FATAL("Unsupported type"); } +template <typename TypedValueT> +inline const auto PropertyToTypedValueFunctor = + [](const PropertyValue &value) { return PropertyToTypedValue<TypedValueT>(value); }; + template <typename TTypedValue> TTypedValue PropertyToTypedValue(const PropertyValue &value, utils::MemoryResource *mem) { switch (value.type()) { diff --git a/src/storage/v3/edge_accessor.cpp b/src/storage/v3/edge_accessor.cpp index 95dd6875a..e591de7eb 100644 --- a/src/storage/v3/edge_accessor.cpp +++ b/src/storage/v3/edge_accessor.cpp @@ -51,9 +51,9 @@ bool EdgeAccessor::IsVisible(const View view) const { return exists && (for_deleted_ || !deleted); } -const VertexId &EdgeAccessor::FromVertex() const { return from_vertex_; } +const VertexId &EdgeAccessor::From() const { return from_vertex_; } -const VertexId &EdgeAccessor::ToVertex() const { return to_vertex_; } +const VertexId &EdgeAccessor::To() const { return to_vertex_; } ShardResult<PropertyValue> EdgeAccessor::SetProperty(PropertyId property, const PropertyValue &value) { utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; @@ -180,4 +180,7 @@ ShardResult<std::map<PropertyId, PropertyValue>> EdgeAccessor::Properties(View v return std::move(properties); } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) +size_t EdgeAccessor::CypherId() const { return Gid().AsUint(); } + } // namespace memgraph::storage::v3 diff --git a/src/storage/v3/edge_accessor.hpp b/src/storage/v3/edge_accessor.hpp index 105d28fa4..bc143c1e3 100644 --- a/src/storage/v3/edge_accessor.hpp +++ b/src/storage/v3/edge_accessor.hpp @@ -48,9 +48,9 @@ class EdgeAccessor final { /// @return true if the object is visible from the current transaction bool IsVisible(View view) const; - const VertexId &FromVertex() const; + const VertexId &From() const; - const VertexId &ToVertex() const; + const VertexId &To() const; EdgeTypeId EdgeType() const { return edge_type_; } @@ -84,6 +84,9 @@ class EdgeAccessor final { } bool operator!=(const EdgeAccessor &other) const noexcept { return !(*this == other); } + // Dummy function + size_t CypherId() const; + private: EdgeRef edge_; EdgeTypeId edge_type_; diff --git a/src/storage/v3/expr.cpp b/src/storage/v3/expr.cpp index 8062a2662..36c0ff8e3 100644 --- a/src/storage/v3/expr.cpp +++ b/src/storage/v3/expr.cpp @@ -52,13 +52,12 @@ msgs::Value ConstructValueEdge(const EdgeAccessor &acc, View view) { msgs::EdgeType type = {.id = acc.EdgeType()}; msgs::EdgeId gid = {.gid = acc.Gid().AsUint()}; - msgs::Label src_prim_label = {.id = acc.FromVertex().primary_label}; + msgs::Label src_prim_label = {.id = acc.From().primary_label}; memgraph::msgs::VertexId src_vertex = - std::make_pair(src_prim_label, conversions::ConvertValueVector(acc.FromVertex().primary_key)); + std::make_pair(src_prim_label, conversions::ConvertValueVector(acc.From().primary_key)); - msgs::Label dst_prim_label = {.id = acc.ToVertex().primary_label}; - msgs::VertexId dst_vertex = - std::make_pair(dst_prim_label, conversions::ConvertValueVector(acc.ToVertex().primary_key)); + msgs::Label dst_prim_label = {.id = acc.To().primary_label}; + msgs::VertexId dst_vertex = std::make_pair(dst_prim_label, conversions::ConvertValueVector(acc.To().primary_key)); auto properties = acc.Properties(view); diff --git a/src/storage/v3/request_helper.cpp b/src/storage/v3/request_helper.cpp index 481741f60..3b0c18326 100644 --- a/src/storage/v3/request_helper.cpp +++ b/src/storage/v3/request_helper.cpp @@ -257,7 +257,7 @@ EdgeUniquenessFunction InitializeEdgeUniquenessFunction(bool only_unique_neighbo case msgs::EdgeDirection::OUT: { is_edge_unique = [](std::set<const storage::v3::VertexId *, VertexIdCmpr> &other_vertex_set, const storage::v3::EdgeAccessor &edge_acc) { - auto [it, insertion_happened] = other_vertex_set.insert(&edge_acc.ToVertex()); + auto [it, insertion_happened] = other_vertex_set.insert(&edge_acc.To()); return insertion_happened; }; break; @@ -265,7 +265,7 @@ EdgeUniquenessFunction InitializeEdgeUniquenessFunction(bool only_unique_neighbo case msgs::EdgeDirection::IN: { is_edge_unique = [](std::set<const storage::v3::VertexId *, VertexIdCmpr> &other_vertex_set, const storage::v3::EdgeAccessor &edge_acc) { - auto [it, insertion_happened] = other_vertex_set.insert(&edge_acc.FromVertex()); + auto [it, insertion_happened] = other_vertex_set.insert(&edge_acc.From()); return insertion_happened; }; break; @@ -311,8 +311,8 @@ EdgeFiller InitializeEdgeFillerFunction(const msgs::ExpandOneRequest &req) { 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)}; + 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 { @@ -336,7 +336,7 @@ EdgeFiller InitializeEdgeFillerFunction(const msgs::ExpandOneRequest &req) { value_properties.emplace_back(FromPropertyValueToValue(std::move(property_result.GetValue()))); } using EdgeWithSpecificProperties = msgs::ExpandOneResultRow::EdgeWithSpecificProperties; - EdgeWithSpecificProperties edges{ToMsgsVertexId(edge.FromVertex()), msgs::EdgeType{edge.EdgeType()}, + 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)); diff --git a/src/storage/v3/shard.cpp b/src/storage/v3/shard.cpp index 79093449c..5dd8e7256 100644 --- a/src/storage/v3/shard.cpp +++ b/src/storage/v3/shard.cpp @@ -442,7 +442,7 @@ ShardResult<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> for (const auto &item : in_edges) { auto [edge_type, from_vertex, edge] = item; EdgeAccessor e(edge, edge_type, from_vertex, vertex_id, transaction_, &shard_->indices_, config_); - auto ret = DeleteEdge(e.FromVertex(), e.ToVertex(), e.Gid()); + auto ret = DeleteEdge(e.From(), e.To(), e.Gid()); if (ret.HasError()) { MG_ASSERT(ret.GetError() == common::ErrorCode::SERIALIZATION_ERROR, "Invalid database state!"); return ret.GetError(); @@ -455,7 +455,7 @@ ShardResult<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> for (const auto &item : out_edges) { auto [edge_type, to_vertex, edge] = item; EdgeAccessor e(edge, edge_type, vertex_id, to_vertex, transaction_, &shard_->indices_, config_); - auto ret = DeleteEdge(e.FromVertex(), e.ToVertex(), e.Gid()); + auto ret = DeleteEdge(e.From(), e.To(), e.Gid()); if (ret.HasError()) { MG_ASSERT(ret.GetError() == common::ErrorCode::SERIALIZATION_ERROR, "Invalid database state!"); return ret.GetError(); diff --git a/tests/e2e/distributed_queries/CMakeLists.txt b/tests/e2e/distributed_queries/CMakeLists.txt index 455e6ad45..5a7a8d81b 100644 --- a/tests/e2e/distributed_queries/CMakeLists.txt +++ b/tests/e2e/distributed_queries/CMakeLists.txt @@ -9,3 +9,4 @@ distributed_queries_e2e_python_files(order_by_and_limit.py) distributed_queries_e2e_python_files(distinct.py) distributed_queries_e2e_python_files(optional_match.py) distributed_queries_e2e_python_files(common.py) +distributed_queries_e2e_python_files(awesome_memgraph_functions.py) diff --git a/tests/e2e/distributed_queries/awesome_memgraph_functions.py b/tests/e2e/distributed_queries/awesome_memgraph_functions.py new file mode 100644 index 000000000..0bdaa07a4 --- /dev/null +++ b/tests/e2e/distributed_queries/awesome_memgraph_functions.py @@ -0,0 +1,93 @@ +# 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. + +import sys + +import mgclient +import pytest + +from common import ( + connection, + execute_and_fetch_all, + has_n_result_row, + wait_for_shard_manager_to_initialize, +) + + +def test_awesome_memgraph_functions(connection): + wait_for_shard_manager_to_initialize() + cursor = connection.cursor() + + assert has_n_result_row(cursor, "CREATE (n :label {property:1})", 0) + assert has_n_result_row(cursor, "CREATE (n :label {property:2})", 0) + assert has_n_result_row(cursor, "CREATE (n :label {property:3})", 0) + assert has_n_result_row(cursor, "CREATE (n :label {property:4})", 0) + assert has_n_result_row(cursor, "CREATE (n :label {property:10})", 0) + + results = execute_and_fetch_all(cursor, "MATCH (n) WITH COLLECT(n) as nn RETURN SIZE(nn)") + assert len(results) == 1 + assert results[0][0] == 5 + + results = execute_and_fetch_all(cursor, "MATCH (n) WITH COLLECT(n.property) as nn RETURN ALL(i IN nn WHERE i > 0)") + assert len(results) == 1 + assert results[0][0] == True + + results = execute_and_fetch_all(cursor, """RETURN CONTAINS("Pineapple", "P")""") + assert len(results) == 1 + assert results[0][0] == True + + results = execute_and_fetch_all(cursor, """RETURN ENDSWITH("Pineapple", "e")""") + assert len(results) == 1 + assert results[0][0] == True + + results = execute_and_fetch_all(cursor, """RETURN LEFT("Pineapple", 1)""") + assert len(results) == 1 + assert results[0][0] == "P" + + results = execute_and_fetch_all(cursor, """RETURN RIGHT("Pineapple", 1)""") + assert len(results) == 1 + assert results[0][0] == "e" + + results = execute_and_fetch_all(cursor, """RETURN REVERSE("Apple")""") + assert len(results) == 1 + assert results[0][0] == "elppA" + + results = execute_and_fetch_all(cursor, """RETURN REPLACE("Apple", "A", "a")""") + assert len(results) == 1 + assert results[0][0] == "apple" + + results = execute_and_fetch_all(cursor, """RETURN TOLOWER("Apple")""") + assert len(results) == 1 + assert results[0][0] == "apple" + + results = execute_and_fetch_all(cursor, """RETURN TOUPPER("Apple")""") + assert len(results) == 1 + assert results[0][0] == "APPLE" + + results = execute_and_fetch_all(cursor, """RETURN TRIM(" Apple")""") + assert len(results) == 1 + assert results[0][0] == "Apple" + + results = execute_and_fetch_all(cursor, """RETURN SPLIT("Apple.Apple", ".")""") + assert len(results) == 1 + assert results[0][0] == ["Apple", "Apple"] + + results = execute_and_fetch_all(cursor, """RETURN LOG10(100)""") + assert len(results) == 1 + assert results[0][0] == 2 + + results = execute_and_fetch_all(cursor, """RETURN SQRT(4)""") + assert len(results) == 1 + assert results[0][0] == 2 + + +if __name__ == "__main__": + sys.exit(pytest.main([__file__, "-rA"])) diff --git a/tests/e2e/distributed_queries/workloads.yaml b/tests/e2e/distributed_queries/workloads.yaml index 741d6d939..eacc3e713 100644 --- a/tests/e2e/distributed_queries/workloads.yaml +++ b/tests/e2e/distributed_queries/workloads.yaml @@ -36,3 +36,8 @@ workloads: binary: "tests/e2e/pytest_runner.sh" args: ["distributed_queries/optional_match.py"] <<: *template_cluster + + - name: "Awesome memgraph functions" + binary: "tests/e2e/pytest_runner.sh" + args: ["distributed_queries/awesome_memgraph_functions.py"] + <<: *template_cluster diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index b3c8ee06e..188c6c1b0 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -406,3 +406,7 @@ target_link_libraries(${test_prefix}coordinator_shard_map mg-coordinator) # Tests for many shards, many creates, scan add_unit_test(high_density_shard_create_scan.cpp) target_link_libraries(${test_prefix}high_density_shard_create_scan mg-io mg-coordinator mg-storage-v3 mg-query-v2) + +# Tests for awesome_memgraph_functions +add_unit_test(query_v2_expression_evaluator.cpp) +target_link_libraries(${test_prefix}query_v2_expression_evaluator mg-query-v2) diff --git a/tests/unit/query_v2_expression_evaluator.cpp b/tests/unit/query_v2_expression_evaluator.cpp new file mode 100644 index 000000000..1fb4cf6dd --- /dev/null +++ b/tests/unit/query_v2_expression_evaluator.cpp @@ -0,0 +1,1729 @@ +// 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 <chrono> +#include <cmath> +#include <iterator> +#include <memory> +#include <unordered_map> +#include <vector> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "coordinator/shard_map.hpp" +#include "functions/awesome_memgraph_functions.hpp" +#include "parser/opencypher/parser.hpp" +#include "query/v2/accessors.hpp" +#include "query/v2/bindings/eval.hpp" +#include "query/v2/bindings/frame.hpp" +#include "query/v2/context.hpp" +#include "query/v2/frontend/ast/ast.hpp" +#include "query/v2/requests.hpp" +#include "query/v2/shard_request_manager.hpp" +#include "query_v2_query_common.hpp" +#include "storage/v3/property_value.hpp" +#include "storage/v3/shard.hpp" +#include "utils/exceptions.hpp" +#include "utils/string.hpp" + +using memgraph::query::v2::test_common::ToIntList; +using testing::ElementsAre; +using testing::UnorderedElementsAre; + +using memgraph::io::Time; +using memgraph::io::TimedOut; +using memgraph::io::simulator::Simulator; +using memgraph::io::simulator::SimulatorConfig; +using memgraph::io::simulator::SimulatorTransport; +using memgraph::msgs::CreateExpandRequest; +using memgraph::msgs::CreateExpandResponse; +using memgraph::msgs::CreateVerticesRequest; +using memgraph::msgs::CreateVerticesResponse; +using memgraph::msgs::ExpandOneRequest; +using memgraph::msgs::ExpandOneResponse; +using memgraph::msgs::ExpandOneResultRow; +using memgraph::msgs::NewExpand; +using memgraph::msgs::NewVertex; +using memgraph::msgs::ScanVerticesRequest; +using memgraph::msgs::ScanVerticesResponse; +using ShardMap = memgraph::coordinator::ShardMap; +using LabelId = memgraph::storage::v3::LabelId; +using PropertyId = memgraph::storage::v3::PropertyId; +using memgraph::coordinator::Shards; +using CompoundKey = memgraph::coordinator::PrimaryKey; +using memgraph::expr::ExpressionRuntimeException; +using memgraph::functions::FunctionRuntimeException; + +namespace memgraph::query::v2::tests { + +class MockedShardRequestManager : public ShardRequestManagerInterface { + public: + using VertexAccessor = accessors::VertexAccessor; + explicit MockedShardRequestManager(ShardMap shard_map) : shards_map_(std::move(shard_map)) { SetUpNameIdMappers(); } + memgraph::storage::v3::EdgeTypeId NameToEdgeType(const std::string &name) const override { + return shards_map_.GetEdgeTypeId(name).value(); + } + + memgraph::storage::v3::PropertyId NameToProperty(const std::string &name) const override { + return shards_map_.GetPropertyId(name).value(); + } + + memgraph::storage::v3::LabelId NameToLabel(const std::string &name) const override { + return shards_map_.GetLabelId(name).value(); + } + void StartTransaction() override {} + void Commit() override {} + std::vector<VertexAccessor> Request(ExecutionState<memgraph::msgs::ScanVerticesRequest> &state) override { + return {}; + } + + std::vector<CreateVerticesResponse> Request(ExecutionState<CreateVerticesRequest> &state, + std::vector<memgraph::msgs::NewVertex> new_vertices) override { + return {}; + } + + std::vector<ExpandOneResultRow> Request(ExecutionState<ExpandOneRequest> &state, ExpandOneRequest request) override { + return {}; + } + + std::vector<CreateExpandResponse> Request(ExecutionState<CreateExpandRequest> &state, + std::vector<NewExpand> new_edges) override { + return {}; + } + + const std::string &PropertyToName(memgraph::storage::v3::PropertyId id) const override { + return properties_.IdToName(id.AsUint()); + } + + const std::string &LabelToName(memgraph::storage::v3::LabelId id) const override { + return labels_.IdToName(id.AsUint()); + } + + const std::string &EdgeTypeToName(memgraph::storage::v3::EdgeTypeId id) const override { + return edge_types_.IdToName(id.AsUint()); + } + + std::optional<storage::v3::PropertyId> MaybeNameToProperty(const std::string &name) const override { + return shards_map_.GetPropertyId(name); + } + + std::optional<storage::v3::EdgeTypeId> MaybeNameToEdgeType(const std::string &name) const override { + return shards_map_.GetEdgeTypeId(name); + } + + std::optional<storage::v3::LabelId> MaybeNameToLabel(const std::string &name) const override { + return shards_map_.GetLabelId(name); + } + + bool IsPrimaryLabel(LabelId label) const override { return true; } + + bool IsPrimaryKey(LabelId primary_label, PropertyId property) const override { return true; } + + private: + void SetUpNameIdMappers() { + std::unordered_map<uint64_t, std::string> id_to_name; + for (const auto &[name, id] : shards_map_.labels) { + id_to_name.emplace(id.AsUint(), name); + } + labels_.StoreMapping(std::move(id_to_name)); + id_to_name.clear(); + for (const auto &[name, id] : shards_map_.properties) { + id_to_name.emplace(id.AsUint(), name); + } + properties_.StoreMapping(std::move(id_to_name)); + id_to_name.clear(); + for (const auto &[name, id] : shards_map_.edge_types) { + id_to_name.emplace(id.AsUint(), name); + } + edge_types_.StoreMapping(std::move(id_to_name)); + } + + ShardMap shards_map_; + memgraph::storage::v3::NameIdMapper properties_; + memgraph::storage::v3::NameIdMapper edge_types_; + memgraph::storage::v3::NameIdMapper labels_; +}; + +ShardMap CreateDummyShardmap() { + static const std::string label_name = std::string("label1"); + ShardMap sm; + + // register new properties + const std::vector<std::string> property_names = {"prop", "property_2", "age", "height", "a", "b", "c"}; + const auto properties = sm.AllocatePropertyIds(property_names); + const auto property_id_1 = properties.at("prop"); + const auto property_id_2 = properties.at("property_2"); + const auto property_id_3 = properties.at("age"); + const auto property_id_4 = properties.at("height"); + const auto property_id_5 = properties.at("a"); + const auto property_id_6 = properties.at("b"); + const auto property_id_7 = properties.at("c"); + const auto type_1 = memgraph::common::SchemaType::INT; + + using SchemaProperty = memgraph::coordinator::SchemaProperty; + // register new label space + std::vector<SchemaProperty> schema = { + SchemaProperty{.property_id = property_id_1, .type = type_1}, + SchemaProperty{.property_id = property_id_2, .type = type_1}, + SchemaProperty{.property_id = property_id_3, .type = type_1}, + SchemaProperty{.property_id = property_id_4, .type = type_1}, + SchemaProperty{.property_id = property_id_5, .type = type_1}, + SchemaProperty{.property_id = property_id_6, .type = type_1}, + SchemaProperty{.property_id = property_id_7, .type = type_1}, + }; + + auto label_success = sm.InitializeNewLabel(label_name, schema, 1, sm.shard_map_version); + MG_ASSERT(label_success); + + const LabelId label_id = sm.labels.at(label_name); + auto &label_space = sm.label_spaces.at(label_id); + Shards &shards_for_label = label_space.shards; + shards_for_label.clear(); + + auto key1 = memgraph::storage::v3::PropertyValue(0); + auto key2 = memgraph::storage::v3::PropertyValue(0); + CompoundKey compound_key_1 = {key1, key2}; + shards_for_label[compound_key_1] = {}; + + auto key3 = memgraph::storage::v3::PropertyValue(12); + auto key4 = memgraph::storage::v3::PropertyValue(13); + CompoundKey compound_key_2 = {key3, key4}; + shards_for_label[compound_key_2] = {}; + + sm.AllocateEdgeTypeIds(std::vector<memgraph::coordinator::EdgeTypeName>{"edge_type"}); + + return sm; +} + +class ExpressionEvaluatorTest : public ::testing::Test { + public: + ExpressionEvaluatorTest() {} + + protected: + AstStorage storage; + memgraph::utils::MonotonicBufferResource mem{1024}; + EvaluationContext ctx{.memory = &mem, .timestamp = QueryTimestamp()}; + SymbolTable symbol_table; + + Frame frame{128}; + std::unique_ptr<ShardRequestManagerInterface> shard_manager = + std::make_unique<MockedShardRequestManager>(CreateDummyShardmap()); + ExpressionEvaluator eval{&frame, symbol_table, ctx, shard_manager.get(), memgraph::storage::v3::View::OLD}; + + Identifier *CreateIdentifierWithValue(std::string name, const TypedValue &value) { + auto id = storage.Create<Identifier>(name, true); + auto symbol = symbol_table.CreateSymbol(name, true); + id->MapTo(symbol); + frame[symbol] = value; + return id; + } + + template <class TExpression> + auto Eval(TExpression *expr) { + ctx.properties = NamesToProperties(storage.properties_, shard_manager.get()); + ctx.labels = NamesToLabels(storage.labels_, shard_manager.get()); + auto value = expr->Accept(eval); + EXPECT_EQ(value.GetMemoryResource(), &mem) << "ExpressionEvaluator must use the MemoryResource from " + "EvaluationContext for allocations!"; + return value; + } +}; + +TEST_F(ExpressionEvaluatorTest, OrOperator) { + auto *op = + storage.Create<OrOperator>(storage.Create<PrimitiveLiteral>(true), storage.Create<PrimitiveLiteral>(false)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), true); + op = storage.Create<OrOperator>(storage.Create<PrimitiveLiteral>(true), storage.Create<PrimitiveLiteral>(true)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), true); +} + +TEST_F(ExpressionEvaluatorTest, XorOperator) { + auto *op = + storage.Create<XorOperator>(storage.Create<PrimitiveLiteral>(true), storage.Create<PrimitiveLiteral>(false)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), true); + op = storage.Create<XorOperator>(storage.Create<PrimitiveLiteral>(true), storage.Create<PrimitiveLiteral>(true)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), false); +} + +TEST_F(ExpressionEvaluatorTest, AndOperator) { + auto *op = + storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(true), storage.Create<PrimitiveLiteral>(true)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), true); + op = storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(false), storage.Create<PrimitiveLiteral>(true)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), false); +} + +TEST_F(ExpressionEvaluatorTest, AndOperatorShortCircuit) { + { + auto *op = + storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(false), storage.Create<PrimitiveLiteral>(5)); + auto value = Eval(op); + EXPECT_EQ(value.ValueBool(), false); + } + { + auto *op = + storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(5), storage.Create<PrimitiveLiteral>(false)); + // We are evaluating left to right, so we don't short circuit here and + // raise due to `5`. This differs from neo4j, where they evaluate both + // sides and return `false` without checking for type of the first + // expression. + EXPECT_THROW(Eval(op), ExpressionRuntimeException); + } +} + +TEST_F(ExpressionEvaluatorTest, AndOperatorNull) { + { + // Null doesn't short circuit + auto *op = storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(TypedValue()), + storage.Create<PrimitiveLiteral>(5)); + EXPECT_THROW(Eval(op), ExpressionRuntimeException); + } + { + auto *op = storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(TypedValue()), + storage.Create<PrimitiveLiteral>(true)); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + auto *op = storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(TypedValue()), + storage.Create<PrimitiveLiteral>(false)); + auto value = Eval(op); + ASSERT_TRUE(value.IsBool()); + EXPECT_EQ(value.ValueBool(), false); + } +} + +TEST_F(ExpressionEvaluatorTest, AdditionOperator) { + auto *op = storage.Create<AdditionOperator>(storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>(3)); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), 5); +} + +TEST_F(ExpressionEvaluatorTest, SubtractionOperator) { + auto *op = + storage.Create<SubtractionOperator>(storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>(3)); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), -1); +} + +TEST_F(ExpressionEvaluatorTest, MultiplicationOperator) { + auto *op = + storage.Create<MultiplicationOperator>(storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>(3)); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), 6); +} + +TEST_F(ExpressionEvaluatorTest, DivisionOperator) { + auto *op = + storage.Create<DivisionOperator>(storage.Create<PrimitiveLiteral>(50), storage.Create<PrimitiveLiteral>(10)); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), 5); +} + +TEST_F(ExpressionEvaluatorTest, ModOperator) { + auto *op = storage.Create<ModOperator>(storage.Create<PrimitiveLiteral>(65), storage.Create<PrimitiveLiteral>(10)); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), 5); +} + +TEST_F(ExpressionEvaluatorTest, EqualOperator) { + auto *op = storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(10), storage.Create<PrimitiveLiteral>(15)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), false); + op = storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(15), storage.Create<PrimitiveLiteral>(15)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), true); + op = storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(20), storage.Create<PrimitiveLiteral>(15)); + auto val3 = Eval(op); + ASSERT_EQ(val3.ValueBool(), false); +} + +TEST_F(ExpressionEvaluatorTest, NotEqualOperator) { + auto *op = + storage.Create<NotEqualOperator>(storage.Create<PrimitiveLiteral>(10), storage.Create<PrimitiveLiteral>(15)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), true); + op = storage.Create<NotEqualOperator>(storage.Create<PrimitiveLiteral>(15), storage.Create<PrimitiveLiteral>(15)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), false); + op = storage.Create<NotEqualOperator>(storage.Create<PrimitiveLiteral>(20), storage.Create<PrimitiveLiteral>(15)); + auto val3 = Eval(op); + ASSERT_EQ(val3.ValueBool(), true); +} + +TEST_F(ExpressionEvaluatorTest, LessOperator) { + auto *op = storage.Create<LessOperator>(storage.Create<PrimitiveLiteral>(10), storage.Create<PrimitiveLiteral>(15)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), true); + op = storage.Create<LessOperator>(storage.Create<PrimitiveLiteral>(15), storage.Create<PrimitiveLiteral>(15)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), false); + op = storage.Create<LessOperator>(storage.Create<PrimitiveLiteral>(20), storage.Create<PrimitiveLiteral>(15)); + auto val3 = Eval(op); + ASSERT_EQ(val3.ValueBool(), false); +} + +TEST_F(ExpressionEvaluatorTest, GreaterOperator) { + auto *op = + storage.Create<GreaterOperator>(storage.Create<PrimitiveLiteral>(10), storage.Create<PrimitiveLiteral>(15)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), false); + op = storage.Create<GreaterOperator>(storage.Create<PrimitiveLiteral>(15), storage.Create<PrimitiveLiteral>(15)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), false); + op = storage.Create<GreaterOperator>(storage.Create<PrimitiveLiteral>(20), storage.Create<PrimitiveLiteral>(15)); + auto val3 = Eval(op); + ASSERT_EQ(val3.ValueBool(), true); +} + +TEST_F(ExpressionEvaluatorTest, LessEqualOperator) { + auto *op = + storage.Create<LessEqualOperator>(storage.Create<PrimitiveLiteral>(10), storage.Create<PrimitiveLiteral>(15)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), true); + op = storage.Create<LessEqualOperator>(storage.Create<PrimitiveLiteral>(15), storage.Create<PrimitiveLiteral>(15)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), true); + op = storage.Create<LessEqualOperator>(storage.Create<PrimitiveLiteral>(20), storage.Create<PrimitiveLiteral>(15)); + auto val3 = Eval(op); + ASSERT_EQ(val3.ValueBool(), false); +} + +TEST_F(ExpressionEvaluatorTest, GreaterEqualOperator) { + auto *op = + storage.Create<GreaterEqualOperator>(storage.Create<PrimitiveLiteral>(10), storage.Create<PrimitiveLiteral>(15)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), false); + op = storage.Create<GreaterEqualOperator>(storage.Create<PrimitiveLiteral>(15), storage.Create<PrimitiveLiteral>(15)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), true); + op = storage.Create<GreaterEqualOperator>(storage.Create<PrimitiveLiteral>(20), storage.Create<PrimitiveLiteral>(15)); + auto val3 = Eval(op); + ASSERT_EQ(val3.ValueBool(), true); +} + +TEST_F(ExpressionEvaluatorTest, InListOperator) { + auto *list_literal = storage.Create<ListLiteral>(std::vector<Expression *>{ + storage.Create<PrimitiveLiteral>(1), storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>("a")}); + { + // Element exists in list. + auto *op = storage.Create<InListOperator>(storage.Create<PrimitiveLiteral>(2), list_literal); + auto value = Eval(op); + EXPECT_EQ(value.ValueBool(), true); + } + { + // Element doesn't exist in list. + auto *op = storage.Create<InListOperator>(storage.Create<PrimitiveLiteral>("x"), list_literal); + auto value = Eval(op); + EXPECT_EQ(value.ValueBool(), false); + } + { + auto *list_literal = storage.Create<ListLiteral>( + std::vector<Expression *>{storage.Create<PrimitiveLiteral>(TypedValue()), storage.Create<PrimitiveLiteral>(2), + storage.Create<PrimitiveLiteral>("a")}); + // Element doesn't exist in list with null element. + auto *op = storage.Create<InListOperator>(storage.Create<PrimitiveLiteral>("x"), list_literal); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + // Null list. + auto *op = storage.Create<InListOperator>(storage.Create<PrimitiveLiteral>("x"), + storage.Create<PrimitiveLiteral>(TypedValue())); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + // Null literal. + auto *op = storage.Create<InListOperator>(storage.Create<PrimitiveLiteral>(TypedValue()), list_literal); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + // Null literal, empty list. + auto *op = storage.Create<InListOperator>(storage.Create<PrimitiveLiteral>(TypedValue()), + storage.Create<ListLiteral>(std::vector<Expression *>())); + auto value = Eval(op); + EXPECT_FALSE(value.ValueBool()); + } +} + +TEST_F(ExpressionEvaluatorTest, ListIndexing) { + auto *list_literal = storage.Create<ListLiteral>( + std::vector<Expression *>{storage.Create<PrimitiveLiteral>(1), storage.Create<PrimitiveLiteral>(2), + storage.Create<PrimitiveLiteral>(3), storage.Create<PrimitiveLiteral>(4)}); + { + // Legal indexing. + auto *op = storage.Create<SubscriptOperator>(list_literal, storage.Create<PrimitiveLiteral>(2)); + auto value = Eval(op); + EXPECT_EQ(value.ValueInt(), 3); + } + { + // Out of bounds indexing. + auto *op = storage.Create<SubscriptOperator>(list_literal, storage.Create<PrimitiveLiteral>(4)); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + // Out of bounds indexing with negative bound. + auto *op = storage.Create<SubscriptOperator>(list_literal, storage.Create<PrimitiveLiteral>(-100)); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + // Legal indexing with negative index. + auto *op = storage.Create<SubscriptOperator>(list_literal, storage.Create<PrimitiveLiteral>(-2)); + auto value = Eval(op); + EXPECT_EQ(value.ValueInt(), 3); + } + { + // Indexing with one operator being null. + auto *op = storage.Create<SubscriptOperator>(storage.Create<PrimitiveLiteral>(TypedValue()), + storage.Create<PrimitiveLiteral>(-2)); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + // Indexing with incompatible type. + auto *op = storage.Create<SubscriptOperator>(list_literal, storage.Create<PrimitiveLiteral>("bla")); + EXPECT_THROW(Eval(op), ExpressionRuntimeException); + } +} + +TEST_F(ExpressionEvaluatorTest, MapIndexing) { + auto *map_literal = storage.Create<MapLiteral>( + std::unordered_map<PropertyIx, Expression *>{{storage.GetPropertyIx("a"), storage.Create<PrimitiveLiteral>(1)}, + {storage.GetPropertyIx("b"), storage.Create<PrimitiveLiteral>(2)}, + {storage.GetPropertyIx("c"), storage.Create<PrimitiveLiteral>(3)}}); + { + // Legal indexing. + auto *op = storage.Create<SubscriptOperator>(map_literal, storage.Create<PrimitiveLiteral>("b")); + auto value = Eval(op); + EXPECT_EQ(value.ValueInt(), 2); + } + { + // Legal indexing, non-existing key. + auto *op = storage.Create<SubscriptOperator>(map_literal, storage.Create<PrimitiveLiteral>("z")); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + // Wrong key type. + auto *op = storage.Create<SubscriptOperator>(map_literal, storage.Create<PrimitiveLiteral>(42)); + EXPECT_THROW(Eval(op), ExpressionRuntimeException); + } + { + // Indexing with Null. + auto *op = storage.Create<SubscriptOperator>(map_literal, storage.Create<PrimitiveLiteral>(TypedValue())); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } +} + +using Vertex = memgraph::msgs::Vertex; +using Edge = memgraph::msgs::Edge; +using EdgeType = memgraph::msgs::EdgeType; +using EdgeId = memgraph::msgs::EdgeId; +using Value = memgraph::msgs::Value; +using VertexId = memgraph::msgs::VertexId; +using Label = memgraph::msgs::Label; + +accessors::VertexAccessor CreateVertex(std::vector<std::pair<PropertyId, Value>> props, + const ShardRequestManagerInterface *manager, Vertex v = {}) { + return {std::move(v), std::move(props), manager}; +} + +accessors::EdgeAccessor CreateEdge(std::vector<std::pair<PropertyId, Value>> props, + const ShardRequestManagerInterface *manager, EdgeId edge_id = {}, VertexId src = {}, + VertexId dst = {}) { + auto edge = Edge{.src = std::move(src), + .dst = std::move(dst), + .properties = std::move(props), + .id = edge_id, + .type = EdgeType{manager->NameToEdgeType("edge_type")}}; + return accessors::EdgeAccessor{std::move(edge), manager}; +} + +TEST_F(ExpressionEvaluatorTest, VertexAndEdgeIndexing) { + auto prop = shard_manager->NameToProperty("prop"); + auto vertex = CreateVertex({{prop, Value(static_cast<int64_t>(42))}}, shard_manager.get(), {}); + auto edge = CreateEdge({{prop, Value(static_cast<int64_t>(43))}}, shard_manager.get(), {}, {}, {}); + + auto *vertex_id = CreateIdentifierWithValue("v1", TypedValue(vertex)); + auto *edge_id = CreateIdentifierWithValue("e11", TypedValue(edge)); + { + // Legal indexing. + auto *op1 = storage.Create<SubscriptOperator>(vertex_id, storage.Create<PrimitiveLiteral>("prop")); + auto value1 = Eval(op1); + EXPECT_EQ(value1.ValueInt(), 42); + + auto *op2 = storage.Create<SubscriptOperator>(edge_id, storage.Create<PrimitiveLiteral>("prop")); + auto value2 = Eval(op2); + EXPECT_EQ(value2.ValueInt(), 43); + } + + { + // Legal indexing, non-existing key. + auto *op1 = storage.Create<SubscriptOperator>(vertex_id, storage.Create<PrimitiveLiteral>("blah")); + auto value1 = Eval(op1); + EXPECT_TRUE(value1.IsNull()); + auto *op2 = storage.Create<SubscriptOperator>(edge_id, storage.Create<PrimitiveLiteral>("blah")); + auto value2 = Eval(op2); + EXPECT_TRUE(value2.IsNull()); + } + { + // Wrong key type. + auto *op1 = storage.Create<SubscriptOperator>(vertex_id, storage.Create<PrimitiveLiteral>(1)); + EXPECT_THROW(Eval(op1), ExpressionRuntimeException); + + auto *op2 = storage.Create<SubscriptOperator>(edge_id, storage.Create<PrimitiveLiteral>(1)); + EXPECT_THROW(Eval(op2), ExpressionRuntimeException); + } + { + // Indexing with Null. + auto *op1 = storage.Create<SubscriptOperator>(vertex_id, storage.Create<PrimitiveLiteral>(TypedValue{})); + auto value1 = Eval(op1); + EXPECT_TRUE(value1.IsNull()); + + auto *op2 = storage.Create<SubscriptOperator>(edge_id, storage.Create<PrimitiveLiteral>(TypedValue{})); + auto value2 = Eval(op2); + EXPECT_TRUE(value2.IsNull()); + } +} + +TEST_F(ExpressionEvaluatorTest, ListSlicingOperator) { + auto *list_literal = storage.Create<ListLiteral>( + std::vector<Expression *>{storage.Create<PrimitiveLiteral>(1), storage.Create<PrimitiveLiteral>(2), + storage.Create<PrimitiveLiteral>(3), storage.Create<PrimitiveLiteral>(4)}); + + auto extract_ints = [](TypedValue list) { + std::vector<int64_t> int_list; + for (auto x : list.ValueList()) { + int_list.push_back(x.ValueInt()); + } + return int_list; + }; + { + // Legal slicing with both bounds defined. + auto *op = storage.Create<ListSlicingOperator>(list_literal, storage.Create<PrimitiveLiteral>(2), + storage.Create<PrimitiveLiteral>(4)); + auto value = Eval(op); + EXPECT_THAT(extract_ints(value), ElementsAre(3, 4)); + } + { + // Legal slicing with negative bound. + auto *op = storage.Create<ListSlicingOperator>(list_literal, storage.Create<PrimitiveLiteral>(2), + storage.Create<PrimitiveLiteral>(-1)); + auto value = Eval(op); + EXPECT_THAT(extract_ints(value), ElementsAre(3)); + } + { + // Lower bound larger than upper bound. + auto *op = storage.Create<ListSlicingOperator>(list_literal, storage.Create<PrimitiveLiteral>(2), + storage.Create<PrimitiveLiteral>(-4)); + auto value = Eval(op); + EXPECT_THAT(extract_ints(value), ElementsAre()); + } + { + // Bounds ouf or range. + auto *op = storage.Create<ListSlicingOperator>(list_literal, storage.Create<PrimitiveLiteral>(-100), + storage.Create<PrimitiveLiteral>(10)); + auto value = Eval(op); + EXPECT_THAT(extract_ints(value), ElementsAre(1, 2, 3, 4)); + } + { + // Lower bound undefined. + auto *op = storage.Create<ListSlicingOperator>(list_literal, nullptr, storage.Create<PrimitiveLiteral>(3)); + auto value = Eval(op); + EXPECT_THAT(extract_ints(value), ElementsAre(1, 2, 3)); + } + { + // Upper bound undefined. + auto *op = storage.Create<ListSlicingOperator>(list_literal, storage.Create<PrimitiveLiteral>(-2), nullptr); + auto value = Eval(op); + EXPECT_THAT(extract_ints(value), ElementsAre(3, 4)); + } + { + // Bound of illegal type and null value bound. + auto *op = storage.Create<ListSlicingOperator>(list_literal, storage.Create<PrimitiveLiteral>(TypedValue()), + storage.Create<PrimitiveLiteral>("mirko")); + EXPECT_THROW(Eval(op), ExpressionRuntimeException); + } + { + // List of illegal type. + auto *op = storage.Create<ListSlicingOperator>(storage.Create<PrimitiveLiteral>("a"), + storage.Create<PrimitiveLiteral>(-2), nullptr); + EXPECT_THROW(Eval(op), ExpressionRuntimeException); + } + { + // Null value list with undefined upper bound. + auto *op = storage.Create<ListSlicingOperator>(storage.Create<PrimitiveLiteral>(TypedValue()), + storage.Create<PrimitiveLiteral>(-2), nullptr); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + ; + } + { + // Null value index. + auto *op = storage.Create<ListSlicingOperator>(list_literal, storage.Create<PrimitiveLiteral>(-2), + storage.Create<PrimitiveLiteral>(TypedValue())); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + ; + } +} + +TEST_F(ExpressionEvaluatorTest, IfOperator) { + auto *then_expression = storage.Create<PrimitiveLiteral>(10); + auto *else_expression = storage.Create<PrimitiveLiteral>(20); + { + auto *condition_true = + storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>(2)); + auto *op = storage.Create<IfOperator>(condition_true, then_expression, else_expression); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), 10); + } + { + auto *condition_false = + storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>(3)); + auto *op = storage.Create<IfOperator>(condition_false, then_expression, else_expression); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), 20); + } + { + auto *condition_exception = + storage.Create<AdditionOperator>(storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>(3)); + auto *op = storage.Create<IfOperator>(condition_exception, then_expression, else_expression); + ASSERT_THROW(Eval(op), ExpressionRuntimeException); + } +} + +TEST_F(ExpressionEvaluatorTest, NotOperator) { + auto *op = storage.Create<NotOperator>(storage.Create<PrimitiveLiteral>(false)); + auto value = Eval(op); + ASSERT_EQ(value.ValueBool(), true); +} + +TEST_F(ExpressionEvaluatorTest, UnaryPlusOperator) { + auto *op = storage.Create<UnaryPlusOperator>(storage.Create<PrimitiveLiteral>(5)); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), 5); +} + +TEST_F(ExpressionEvaluatorTest, UnaryMinusOperator) { + auto *op = storage.Create<UnaryMinusOperator>(storage.Create<PrimitiveLiteral>(5)); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), -5); +} + +TEST_F(ExpressionEvaluatorTest, IsNullOperator) { + auto *op = storage.Create<IsNullOperator>(storage.Create<PrimitiveLiteral>(1)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), false); + op = storage.Create<IsNullOperator>(storage.Create<PrimitiveLiteral>(TypedValue())); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), true); +} + +TEST_F(ExpressionEvaluatorTest, LabelsTest) { + Label label{shard_manager->NameToLabel("label1")}; + Vertex vertex = {{}, {label}}; + auto v1 = CreateVertex({}, shard_manager.get(), vertex); + auto *identifier = storage.Create<Identifier>("n"); + auto node_symbol = symbol_table.CreateSymbol("n", true); + identifier->MapTo(node_symbol); + frame[node_symbol] = TypedValue(v1); + { + auto *op = storage.Create<LabelsTest>(identifier, std::vector<LabelIx>{LabelIx{"label1", label.id.AsInt()}}); + auto value = Eval(op); + EXPECT_EQ(value.ValueBool(), true); + } + { + auto *op = storage.Create<LabelsTest>(identifier, std::vector<LabelIx>{LabelIx{"label2", 10}}); + auto value = Eval(op); + EXPECT_EQ(value.ValueBool(), false); + } + { + auto *op = storage.Create<LabelsTest>(identifier, std::vector<LabelIx>{LabelIx{"label2", 10}}); + frame[node_symbol] = TypedValue(); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } +} + +TEST_F(ExpressionEvaluatorTest, Aggregation) { + auto aggr = storage.Create<Aggregation>(storage.Create<PrimitiveLiteral>(42), nullptr, Aggregation::Op::COUNT); + auto aggr_sym = symbol_table.CreateSymbol("aggr", true); + aggr->MapTo(aggr_sym); + frame[aggr_sym] = TypedValue(1); + auto value = Eval(aggr); + EXPECT_EQ(value.ValueInt(), 1); +} + +TEST_F(ExpressionEvaluatorTest, ListLiteral) { + auto *list_literal = storage.Create<ListLiteral>(std::vector<Expression *>{storage.Create<PrimitiveLiteral>(1), + storage.Create<PrimitiveLiteral>("bla"), + storage.Create<PrimitiveLiteral>(true)}); + TypedValue result = Eval(list_literal); + ASSERT_TRUE(result.IsList()); + auto &result_elems = result.ValueList(); + ASSERT_EQ(3, result_elems.size()); + EXPECT_TRUE(result_elems[0].IsInt()); + EXPECT_TRUE(result_elems[1].IsString()); + EXPECT_TRUE(result_elems[2].IsBool()); +} + +TEST_F(ExpressionEvaluatorTest, ParameterLookup) { + ctx.parameters.Add(0, memgraph::storage::v3::PropertyValue(42)); + auto *param_lookup = storage.Create<ParameterLookup>(0); + auto value = Eval(param_lookup); + ASSERT_TRUE(value.IsInt()); + EXPECT_EQ(value.ValueInt(), 42); +} + +TEST_F(ExpressionEvaluatorTest, FunctionAll1) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *all = ALL("x", LIST(LITERAL(1), LITERAL(1)), WHERE(EQ(ident_x, LITERAL(1)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + all->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(all); + ASSERT_TRUE(value.IsBool()); + EXPECT_TRUE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionAll2) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *all = ALL("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + all->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(all); + ASSERT_TRUE(value.IsBool()); + EXPECT_FALSE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionAllNullList) { + AstStorage storage; + auto *all = ALL("x", LITERAL(TypedValue()), WHERE(LITERAL(true))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + all->identifier_->MapTo(x_sym); + auto value = Eval(all); + EXPECT_TRUE(value.IsNull()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionAllNullElementInList1) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *all = ALL("x", LIST(LITERAL(1), LITERAL(TypedValue())), WHERE(EQ(ident_x, LITERAL(1)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + all->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(all); + ASSERT_TRUE(value.IsBool()); + EXPECT_FALSE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionAllNullElementInList2) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *all = ALL("x", LIST(LITERAL(2), LITERAL(TypedValue())), WHERE(EQ(ident_x, LITERAL(1)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + all->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(all); + ASSERT_TRUE(value.IsBool()); + EXPECT_FALSE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionAllWhereWrongType) { + AstStorage storage; + auto *all = ALL("x", LIST(LITERAL(1)), WHERE(LITERAL(2))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + all->identifier_->MapTo(x_sym); + EXPECT_THROW(Eval(all), ExpressionRuntimeException); +} + +TEST_F(ExpressionEvaluatorTest, FunctionSingle1) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *single = SINGLE("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + single->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(single); + ASSERT_TRUE(value.IsBool()); + EXPECT_TRUE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionSingle2) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *single = SINGLE("x", LIST(LITERAL(1), LITERAL(2)), WHERE(GREATER(ident_x, LITERAL(0)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + single->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(single); + ASSERT_TRUE(value.IsBool()); + EXPECT_FALSE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionSingleNullList) { + AstStorage storage; + auto *single = SINGLE("x", LITERAL(TypedValue()), WHERE(LITERAL(true))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + single->identifier_->MapTo(x_sym); + auto value = Eval(single); + EXPECT_TRUE(value.IsNull()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionSingleNullElementInList1) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *single = SINGLE("x", LIST(LITERAL(1), LITERAL(TypedValue())), WHERE(EQ(ident_x, LITERAL(1)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + single->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(single); + ASSERT_TRUE(value.IsBool()); + EXPECT_TRUE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionSingleNullElementInList2) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *single = SINGLE("x", LIST(LITERAL(2), LITERAL(TypedValue())), WHERE(EQ(ident_x, LITERAL(1)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + single->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(single); + ASSERT_TRUE(value.IsBool()); + EXPECT_FALSE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionAny1) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *any = ANY("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + any->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(any); + ASSERT_TRUE(value.IsBool()); + EXPECT_TRUE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionAny2) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *any = ANY("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(0)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + any->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(any); + ASSERT_TRUE(value.IsBool()); + EXPECT_FALSE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionAnyNullList) { + AstStorage storage; + auto *any = ANY("x", LITERAL(TypedValue()), WHERE(LITERAL(true))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + any->identifier_->MapTo(x_sym); + auto value = Eval(any); + EXPECT_TRUE(value.IsNull()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionAnyNullElementInList1) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *any = ANY("x", LIST(LITERAL(0), LITERAL(TypedValue())), WHERE(EQ(ident_x, LITERAL(0)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + any->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(any); + EXPECT_TRUE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionAnyNullElementInList2) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *any = ANY("x", LIST(LITERAL(1), LITERAL(TypedValue())), WHERE(EQ(ident_x, LITERAL(0)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + any->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(any); + EXPECT_FALSE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionAnyWhereWrongType) { + AstStorage storage; + auto *any = ANY("x", LIST(LITERAL(1)), WHERE(LITERAL(2))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + any->identifier_->MapTo(x_sym); + EXPECT_THROW(Eval(any), ExpressionRuntimeException); +} + +TEST_F(ExpressionEvaluatorTest, FunctionNone1) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *none = NONE("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(0)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + none->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(none); + ASSERT_TRUE(value.IsBool()); + EXPECT_TRUE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionNone2) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *none = NONE("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + none->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(none); + ASSERT_TRUE(value.IsBool()); + EXPECT_FALSE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionNoneNullList) { + AstStorage storage; + auto *none = NONE("x", LITERAL(TypedValue()), WHERE(LITERAL(true))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + none->identifier_->MapTo(x_sym); + auto value = Eval(none); + EXPECT_TRUE(value.IsNull()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionNoneNullElementInList1) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *any = NONE("x", LIST(LITERAL(1), LITERAL(TypedValue())), WHERE(EQ(ident_x, LITERAL(0)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + any->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(any); + EXPECT_TRUE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionNoneNullElementInList2) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *none = NONE("x", LIST(LITERAL(0), LITERAL(TypedValue())), WHERE(EQ(ident_x, LITERAL(0)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + none->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(none); + EXPECT_FALSE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionNoneWhereWrongType) { + AstStorage storage; + auto *none = NONE("x", LIST(LITERAL(1)), WHERE(LITERAL(2))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + none->identifier_->MapTo(x_sym); + EXPECT_THROW(Eval(none), ExpressionRuntimeException); +} + +TEST_F(ExpressionEvaluatorTest, FunctionReduce) { + AstStorage storage; + auto *ident_sum = IDENT("sum"); + auto *ident_x = IDENT("x"); + auto *reduce = REDUCE("sum", LITERAL(0), "x", LIST(LITERAL(1), LITERAL(2)), ADD(ident_sum, ident_x)); + const auto sum_sym = symbol_table.CreateSymbol("sum", true); + reduce->accumulator_->MapTo(sum_sym); + ident_sum->MapTo(sum_sym); + const auto x_sym = symbol_table.CreateSymbol("x", true); + reduce->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(reduce); + ASSERT_TRUE(value.IsInt()); + EXPECT_EQ(value.ValueInt(), 3); +} + +TEST_F(ExpressionEvaluatorTest, FunctionExtract) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *extract = EXTRACT("x", LIST(LITERAL(1), LITERAL(2), LITERAL(TypedValue())), ADD(ident_x, LITERAL(1))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + extract->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(extract); + EXPECT_TRUE(value.IsList()); + ; + auto result = value.ValueList(); + EXPECT_EQ(result[0].ValueInt(), 2); + EXPECT_EQ(result[1].ValueInt(), 3); + EXPECT_TRUE(result[2].IsNull()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionExtractNull) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *extract = EXTRACT("x", LITERAL(TypedValue()), ADD(ident_x, LITERAL(1))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + extract->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + auto value = Eval(extract); + EXPECT_TRUE(value.IsNull()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionExtractExceptions) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *extract = EXTRACT("x", LITERAL("bla"), ADD(ident_x, LITERAL(1))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + extract->identifier_->MapTo(x_sym); + ident_x->MapTo(x_sym); + EXPECT_THROW(Eval(extract), ExpressionRuntimeException); +} + +TEST_F(ExpressionEvaluatorTest, RegexMatchInvalidArguments) { + EXPECT_TRUE(Eval(storage.Create<RegexMatch>(LITERAL(TypedValue()), LITERAL("regex"))).IsNull()); + EXPECT_TRUE(Eval(storage.Create<RegexMatch>(LITERAL(3), LITERAL("regex"))).IsNull()); + EXPECT_TRUE(Eval(storage.Create<RegexMatch>(LIST(LITERAL("string")), LITERAL("regex"))).IsNull()); + EXPECT_TRUE(Eval(storage.Create<RegexMatch>(LITERAL("string"), LITERAL(TypedValue()))).IsNull()); + EXPECT_THROW(Eval(storage.Create<RegexMatch>(LITERAL("string"), LITERAL(42))), ExpressionRuntimeException); + EXPECT_THROW(Eval(storage.Create<RegexMatch>(LITERAL("string"), LIST(LITERAL("regex")))), ExpressionRuntimeException); +} + +TEST_F(ExpressionEvaluatorTest, RegexMatchInvalidRegex) { + EXPECT_THROW(Eval(storage.Create<RegexMatch>(LITERAL("text"), LITERAL("*ext"))), ExpressionRuntimeException); + EXPECT_THROW(Eval(storage.Create<RegexMatch>(LITERAL("text"), LITERAL("[ext"))), ExpressionRuntimeException); +} + +TEST_F(ExpressionEvaluatorTest, RegexMatch) { + EXPECT_FALSE(Eval(storage.Create<RegexMatch>(LITERAL("text"), LITERAL(".*ex"))).ValueBool()); + EXPECT_TRUE(Eval(storage.Create<RegexMatch>(LITERAL("text"), LITERAL(".*ext"))).ValueBool()); + EXPECT_FALSE(Eval(storage.Create<RegexMatch>(LITERAL("text"), LITERAL("[ext]"))).ValueBool()); + EXPECT_TRUE(Eval(storage.Create<RegexMatch>(LITERAL("text"), LITERAL(".+[ext]"))).ValueBool()); +} + +class ExpressionEvaluatorPropertyLookup : public ExpressionEvaluatorTest { + protected: + std::pair<std::string, memgraph::storage::v3::PropertyId> prop_age = + std::make_pair("age", shard_manager->NameToProperty("age")); + std::pair<std::string, memgraph::storage::v3::PropertyId> prop_height = + std::make_pair("height", shard_manager->NameToProperty("height")); + Identifier *identifier = storage.Create<Identifier>("element"); + Symbol symbol = symbol_table.CreateSymbol("element", true); + + void SetUp() { identifier->MapTo(symbol); } + + auto Value(std::pair<std::string, memgraph::storage::v3::PropertyId> property) { + auto *op = storage.Create<PropertyLookup>(identifier, storage.GetPropertyIx(property.first)); + return Eval(op); + } +}; + +TEST_F(ExpressionEvaluatorPropertyLookup, Null) { + frame[symbol] = TypedValue(); + EXPECT_TRUE(Value(prop_age).IsNull()); +} + +TEST_F(ExpressionEvaluatorPropertyLookup, MapLiteral) { + frame[symbol] = TypedValue(std::map<std::string, TypedValue>{{prop_age.first, TypedValue(10)}}); + EXPECT_EQ(Value(prop_age).ValueInt(), 10); + EXPECT_TRUE(Value(prop_height).IsNull()); +} + +class FunctionTest : public ExpressionEvaluatorTest { + protected: + std::vector<Expression *> ExpressionsFromTypedValues(const std::vector<TypedValue> &tvs) { + std::vector<Expression *> expressions; + expressions.reserve(tvs.size()); + + for (size_t i = 0; i < tvs.size(); ++i) { + auto *ident = storage.Create<Identifier>("arg_" + std::to_string(i), true); + auto sym = symbol_table.CreateSymbol("arg_" + std::to_string(i), true); + ident->MapTo(sym); + frame[sym] = tvs[i]; + expressions.push_back(ident); + } + + return expressions; + } + + TypedValue EvaluateFunctionWithExprs(const std::string &function_name, const std::vector<Expression *> &expressions) { + auto *op = storage.Create<Function>(function_name, expressions); + return Eval(op); + } + + template <class... TArgs> + TypedValue EvaluateFunction(const std::string &function_name, std::tuple<TArgs...> args) { + std::vector<TypedValue> tv_args; + tv_args.reserve(args.size()); + for (auto &arg : args) tv_args.emplace_back(std::move(arg)); + return EvaluateFunctionWithExprs(function_name, ExpressionsFromTypedValues(tv_args)); + } + + template <class... TArgs> + TypedValue EvaluateFunction(const std::string &function_name, TArgs &&...args) { + return EvaluateFunctionWithExprs(function_name, + ExpressionsFromTypedValues(std::vector<TypedValue>{TypedValue(args)...})); + } +}; + +template <class... TArgs> +static TypedValue MakeTypedValueList(TArgs &&...args) { + return TypedValue(std::vector<TypedValue>{TypedValue(args)...}); +} + +TEST_F(FunctionTest, EndNode) { + ASSERT_THROW(EvaluateFunction("ENDNODE"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("ENDNODE", TypedValue()).IsNull()); + Label l{shard_manager->NameToLabel("label1")}; + EdgeId e_id{10}; + VertexId dst{l, {msgs::Value(static_cast<int64_t>(2))}}; + auto e = CreateEdge({}, shard_manager.get(), e_id, {}, dst); + const auto res = EvaluateFunction("ENDNODE", e).ValueVertex().Id(); + ASSERT_EQ(res, dst); + ASSERT_THROW(EvaluateFunction("ENDNODE", 2), FunctionRuntimeException); +} + +TEST_F(FunctionTest, Head) { + ASSERT_THROW(EvaluateFunction("HEAD"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("HEAD", TypedValue()).IsNull()); + auto argument = MakeTypedValueList(3, 4, 5); + ASSERT_EQ(EvaluateFunction("HEAD", argument).ValueInt(), 3); + argument.ValueList().clear(); + ASSERT_TRUE(EvaluateFunction("HEAD", argument).IsNull()); + ASSERT_THROW(EvaluateFunction("HEAD", 2), FunctionRuntimeException); +} + +TEST_F(FunctionTest, Properties) { + ASSERT_THROW(EvaluateFunction("PROPERTIES"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("PROPERTIES", TypedValue()).IsNull()); + const auto height = shard_manager->NameToProperty("height"); + const auto age = shard_manager->NameToProperty("age"); + auto v1 = CreateVertex({{height, Value(static_cast<int64_t>(5))}, {age, Value(static_cast<int64_t>(10))}}, + shard_manager.get()); + auto e = CreateEdge({{height, Value(static_cast<int64_t>(3))}, {age, Value(static_cast<int64_t>(15))}}, + shard_manager.get()); + + auto prop_values_to_int = [](TypedValue t) { + std::unordered_map<std::string, int> properties; + for (auto property : t.ValueMap()) { + properties[std::string(property.first)] = property.second.ValueInt(); + } + return properties; + }; + + ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", v1)), + UnorderedElementsAre(testing::Pair("height", 5), testing::Pair("age", 10))); + ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", e)), + UnorderedElementsAre(testing::Pair("height", 3), testing::Pair("age", 15))); + ASSERT_THROW(EvaluateFunction("PROPERTIES", 2), FunctionRuntimeException); +} + +TEST_F(FunctionTest, Last) { + ASSERT_THROW(EvaluateFunction("LAST"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("LAST", TypedValue()).IsNull()); + auto argument = MakeTypedValueList(3, 4, 5); + ASSERT_EQ(EvaluateFunction("LAST", argument).ValueInt(), 5); + argument.ValueList().clear(); + ASSERT_TRUE(EvaluateFunction("LAST", argument).IsNull()); + ASSERT_THROW(EvaluateFunction("LAST", 5), FunctionRuntimeException); +} + +TEST_F(FunctionTest, Size) { + ASSERT_THROW(EvaluateFunction("SIZE"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("SIZE", TypedValue()).IsNull()); + auto argument = MakeTypedValueList(3, 4, 5); + ASSERT_EQ(EvaluateFunction("SIZE", argument).ValueInt(), 3); + ASSERT_EQ(EvaluateFunction("SIZE", "john").ValueInt(), 4); + ASSERT_EQ(EvaluateFunction("SIZE", + std::map<std::string, TypedValue>{ + {"a", TypedValue(5)}, {"b", TypedValue(true)}, {"c", TypedValue("123")}}) + .ValueInt(), + 3); + ASSERT_THROW(EvaluateFunction("SIZE", 5), FunctionRuntimeException); +} + +TEST_F(FunctionTest, StartNode) { + ASSERT_THROW(EvaluateFunction("STARTNODE"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("STARTNODE", TypedValue()).IsNull()); + Label l{shard_manager->NameToLabel("label1")}; + EdgeId e_id{5}; + VertexId src{l, {msgs::Value(static_cast<int64_t>(4))}}; + auto e = CreateEdge({}, shard_manager.get(), e_id, src); + const auto res = EvaluateFunction("STARTNODE", e).ValueVertex().Id(); + ASSERT_EQ(res, src); + ASSERT_THROW(EvaluateFunction("STARTNODE", 2), FunctionRuntimeException); +} + +TEST_F(FunctionTest, ToBoolean) { + ASSERT_THROW(EvaluateFunction("TOBOOLEAN"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("TOBOOLEAN", TypedValue()).IsNull()); + ASSERT_EQ(EvaluateFunction("TOBOOLEAN", 123).ValueBool(), true); + ASSERT_EQ(EvaluateFunction("TOBOOLEAN", -213).ValueBool(), true); + ASSERT_EQ(EvaluateFunction("TOBOOLEAN", 0).ValueBool(), false); + ASSERT_EQ(EvaluateFunction("TOBOOLEAN", " trUE \n\t").ValueBool(), true); + ASSERT_EQ(EvaluateFunction("TOBOOLEAN", "\n\tFalsE").ValueBool(), false); + ASSERT_TRUE(EvaluateFunction("TOBOOLEAN", "\n\tFALSEA ").IsNull()); + ASSERT_EQ(EvaluateFunction("TOBOOLEAN", true).ValueBool(), true); + ASSERT_EQ(EvaluateFunction("TOBOOLEAN", false).ValueBool(), false); +} + +TEST_F(FunctionTest, ToFloat) { + ASSERT_THROW(EvaluateFunction("TOFLOAT"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("TOFLOAT", TypedValue()).IsNull()); + ASSERT_EQ(EvaluateFunction("TOFLOAT", " -3.5 \n\t").ValueDouble(), -3.5); + ASSERT_EQ(EvaluateFunction("TOFLOAT", "\n\t0.5e-1").ValueDouble(), 0.05); + ASSERT_TRUE(EvaluateFunction("TOFLOAT", "\n\t3.4e-3X ").IsNull()); + ASSERT_EQ(EvaluateFunction("TOFLOAT", -3.5).ValueDouble(), -3.5); + ASSERT_EQ(EvaluateFunction("TOFLOAT", -3).ValueDouble(), -3.0); + ASSERT_THROW(EvaluateFunction("TOFLOAT", true), FunctionRuntimeException); +} + +TEST_F(FunctionTest, ToInteger) { + ASSERT_THROW(EvaluateFunction("TOINTEGER"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("TOINTEGER", TypedValue()).IsNull()); + ASSERT_EQ(EvaluateFunction("TOINTEGER", false).ValueInt(), 0); + ASSERT_EQ(EvaluateFunction("TOINTEGER", true).ValueInt(), 1); + ASSERT_EQ(EvaluateFunction("TOINTEGER", "\n\t3").ValueInt(), 3); + ASSERT_EQ(EvaluateFunction("TOINTEGER", " -3.5 \n\t").ValueInt(), -3); + ASSERT_TRUE(EvaluateFunction("TOINTEGER", "\n\t3X ").IsNull()); + ASSERT_EQ(EvaluateFunction("TOINTEGER", -3.5).ValueInt(), -3); + ASSERT_EQ(EvaluateFunction("TOINTEGER", 3.5).ValueInt(), 3); +} + +TEST_F(FunctionTest, Type) { + ASSERT_THROW(EvaluateFunction("TYPE"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("TYPE", TypedValue()).IsNull()); + auto e = CreateEdge({}, shard_manager.get()); + ASSERT_EQ(EvaluateFunction("TYPE", e).ValueString(), "edge_type"); + ASSERT_THROW(EvaluateFunction("TYPE", 2), FunctionRuntimeException); +} + +TEST_F(FunctionTest, ValueType) { + ASSERT_THROW(EvaluateFunction("VALUETYPE"), FunctionRuntimeException); + ASSERT_THROW(EvaluateFunction("VALUETYPE", TypedValue(), TypedValue()), FunctionRuntimeException); + ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue()).ValueString(), "NULL"); + ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(true)).ValueString(), "BOOLEAN"); + ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(1)).ValueString(), "INTEGER"); + ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(1.1)).ValueString(), "FLOAT"); + ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue("test")).ValueString(), "STRING"); + ASSERT_EQ( + EvaluateFunction("VALUETYPE", TypedValue(std::vector<TypedValue>{TypedValue(1), TypedValue(2)})).ValueString(), + "LIST"); + ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(std::map<std::string, TypedValue>{{"test", TypedValue(1)}})) + .ValueString(), + "MAP"); + auto v1 = CreateVertex({}, shard_manager.get()); + ASSERT_EQ(EvaluateFunction("VALUETYPE", v1).ValueString(), "NODE"); + auto e = CreateEdge({}, shard_manager.get()); + ASSERT_EQ(EvaluateFunction("VALUETYPE", e).ValueString(), "RELATIONSHIP"); +} + +TEST_F(FunctionTest, Labels) { + ASSERT_THROW(EvaluateFunction("LABELS"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("LABELS", TypedValue()).IsNull()); + Label label{shard_manager->NameToLabel("label1")}; + auto v = CreateVertex({}, shard_manager.get(), {{}, {label}}); + std::vector<std::string> labels; + auto evaluated_labels = EvaluateFunction("LABELS", v).ValueList(); + labels.reserve(evaluated_labels.size()); + for (auto label : evaluated_labels) { + labels.emplace_back(label.ValueString()); + } + ASSERT_THAT(labels, UnorderedElementsAre("label1")); + ASSERT_THROW(EvaluateFunction("LABELS", 2), FunctionRuntimeException); +} + +TEST_F(FunctionTest, Range) { + EXPECT_THROW(EvaluateFunction("RANGE"), FunctionRuntimeException); + EXPECT_TRUE(EvaluateFunction("RANGE", 1, 2, TypedValue()).IsNull()); + EXPECT_THROW(EvaluateFunction("RANGE", 1, TypedValue(), 1.3), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("RANGE", 1, 2, 0), FunctionRuntimeException); + EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 1, 3)), ElementsAre(1, 2, 3)); + EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", -1, 5, 2)), ElementsAre(-1, 1, 3, 5)); + EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 2, 10, 3)), ElementsAre(2, 5, 8)); + EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 2, 2, 2)), ElementsAre(2)); + EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 3, 0, 5)), ElementsAre()); + EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 5, 1, -2)), ElementsAre(5, 3, 1)); + EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 6, 1, -2)), ElementsAre(6, 4, 2)); + EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 2, 2, -3)), ElementsAre(2)); + EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", -2, 4, -1)), ElementsAre()); +} + +TEST_F(FunctionTest, Tail) { + ASSERT_THROW(EvaluateFunction("TAIL"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("TAIL", TypedValue()).IsNull()); + auto argument = MakeTypedValueList(); + ASSERT_EQ(EvaluateFunction("TAIL", argument).ValueList().size(), 0U); + argument = MakeTypedValueList(3, 4, true, "john"); + auto list = EvaluateFunction("TAIL", argument).ValueList(); + ASSERT_EQ(list.size(), 3U); + ASSERT_EQ(list[0].ValueInt(), 4); + ASSERT_EQ(list[1].ValueBool(), true); + ASSERT_EQ(list[2].ValueString(), "john"); + ASSERT_THROW(EvaluateFunction("TAIL", 2), FunctionRuntimeException); +} + +TEST_F(FunctionTest, UniformSample) { + ASSERT_THROW(EvaluateFunction("UNIFORMSAMPLE"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("UNIFORMSAMPLE", TypedValue(), TypedValue()).IsNull()); + ASSERT_TRUE(EvaluateFunction("UNIFORMSAMPLE", TypedValue(), 1).IsNull()); + ASSERT_TRUE(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), TypedValue()).IsNull()); + ASSERT_TRUE(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), 1).IsNull()); + ASSERT_THROW(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), -1), FunctionRuntimeException); + ASSERT_EQ(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 0).ValueList().size(), 0); + ASSERT_EQ(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 2).ValueList().size(), 2); + ASSERT_EQ(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 3).ValueList().size(), 3); + ASSERT_EQ(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 5).ValueList().size(), 5); +} + +TEST_F(FunctionTest, Abs) { + ASSERT_THROW(EvaluateFunction("ABS"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("ABS", TypedValue()).IsNull()); + ASSERT_EQ(EvaluateFunction("ABS", -2).ValueInt(), 2); + ASSERT_EQ(EvaluateFunction("ABS", -2.5).ValueDouble(), 2.5); + ASSERT_THROW(EvaluateFunction("ABS", true), FunctionRuntimeException); +} + +//// Test if log works. If it does then all functions wrapped with +//// WRAP_CMATH_FLOAT_FUNCTION macro should work and are not gonna be tested for +//// correctnes.. +TEST_F(FunctionTest, Log) { + ASSERT_THROW(EvaluateFunction("LOG"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("LOG", TypedValue()).IsNull()); + ASSERT_DOUBLE_EQ(EvaluateFunction("LOG", 2).ValueDouble(), log(2)); + ASSERT_DOUBLE_EQ(EvaluateFunction("LOG", 1.5).ValueDouble(), log(1.5)); + // Not portable, but should work on most platforms. + ASSERT_TRUE(std::isnan(EvaluateFunction("LOG", -1.5).ValueDouble())); + ASSERT_THROW(EvaluateFunction("LOG", true), FunctionRuntimeException); +} + +//// Function Round wraps round from cmath and will work if FunctionTest.Log test +//// passes. This test is used to show behavior of round since it differs from +//// neo4j's round. +TEST_F(FunctionTest, Round) { + ASSERT_THROW(EvaluateFunction("ROUND"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("ROUND", TypedValue()).IsNull()); + ASSERT_EQ(EvaluateFunction("ROUND", -2).ValueDouble(), -2); + ASSERT_EQ(EvaluateFunction("ROUND", -2.4).ValueDouble(), -2); + ASSERT_EQ(EvaluateFunction("ROUND", -2.5).ValueDouble(), -3); + ASSERT_EQ(EvaluateFunction("ROUND", -2.6).ValueDouble(), -3); + ASSERT_EQ(EvaluateFunction("ROUND", 2.4).ValueDouble(), 2); + ASSERT_EQ(EvaluateFunction("ROUND", 2.5).ValueDouble(), 3); + ASSERT_EQ(EvaluateFunction("ROUND", 2.6).ValueDouble(), 3); + ASSERT_THROW(EvaluateFunction("ROUND", true), FunctionRuntimeException); +} + +// Check if wrapped functions are callable (check if everything was spelled +// correctly...). Wrapper correctnes is checked in FunctionTest.Log function +// test. +TEST_F(FunctionTest, WrappedMathFunctions) { + for (auto function_name : + {"FLOOR", "CEIL", "ROUND", "EXP", "LOG", "LOG10", "SQRT", "ACOS", "ASIN", "ATAN", "COS", "SIN", "TAN"}) { + EvaluateFunction(function_name, 0.5); + } +} + +TEST_F(FunctionTest, Atan2) { + ASSERT_THROW(EvaluateFunction("ATAN2"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("ATAN2", TypedValue(), 1).IsNull()); + ASSERT_TRUE(EvaluateFunction("ATAN2", 1, TypedValue()).IsNull()); + ASSERT_DOUBLE_EQ(EvaluateFunction("ATAN2", 2, -1.0).ValueDouble(), atan2(2, -1)); + ASSERT_THROW(EvaluateFunction("ATAN2", 3.0, true), FunctionRuntimeException); +} + +TEST_F(FunctionTest, Sign) { + ASSERT_THROW(EvaluateFunction("SIGN"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("SIGN", TypedValue()).IsNull()); + ASSERT_EQ(EvaluateFunction("SIGN", -2).ValueInt(), -1); + ASSERT_EQ(EvaluateFunction("SIGN", -0.2).ValueInt(), -1); + ASSERT_EQ(EvaluateFunction("SIGN", 0.0).ValueInt(), 0); + ASSERT_EQ(EvaluateFunction("SIGN", 2.5).ValueInt(), 1); + ASSERT_THROW(EvaluateFunction("SIGN", true), FunctionRuntimeException); +} + +TEST_F(FunctionTest, E) { + ASSERT_THROW(EvaluateFunction("E", 1), FunctionRuntimeException); + ASSERT_DOUBLE_EQ(EvaluateFunction("E").ValueDouble(), M_E); +} + +TEST_F(FunctionTest, Pi) { + ASSERT_THROW(EvaluateFunction("PI", 1), FunctionRuntimeException); + ASSERT_DOUBLE_EQ(EvaluateFunction("PI").ValueDouble(), M_PI); +} + +TEST_F(FunctionTest, Rand) { + ASSERT_THROW(EvaluateFunction("RAND", 1), FunctionRuntimeException); + ASSERT_GE(EvaluateFunction("RAND").ValueDouble(), 0.0); + ASSERT_LT(EvaluateFunction("RAND").ValueDouble(), 1.0); +} + +using memgraph::functions::kStartsWith; +TEST_F(FunctionTest, StartsWith) { + EXPECT_THROW(EvaluateFunction(kStartsWith), FunctionRuntimeException); + EXPECT_TRUE(EvaluateFunction(kStartsWith, "a", TypedValue()).IsNull()); + EXPECT_THROW(EvaluateFunction(kStartsWith, TypedValue(), 1.3), FunctionRuntimeException); + EXPECT_TRUE(EvaluateFunction(kStartsWith, "abc", "abc").ValueBool()); + EXPECT_TRUE(EvaluateFunction(kStartsWith, "abcdef", "abc").ValueBool()); + EXPECT_FALSE(EvaluateFunction(kStartsWith, "abcdef", "aBc").ValueBool()); + EXPECT_FALSE(EvaluateFunction(kStartsWith, "abc", "abcd").ValueBool()); +} + +using memgraph::functions::kEndsWith; +TEST_F(FunctionTest, EndsWith) { + EXPECT_THROW(EvaluateFunction(kEndsWith), FunctionRuntimeException); + EXPECT_TRUE(EvaluateFunction(kEndsWith, "a", TypedValue()).IsNull()); + EXPECT_THROW(EvaluateFunction(kEndsWith, TypedValue(), 1.3), FunctionRuntimeException); + EXPECT_TRUE(EvaluateFunction(kEndsWith, "abc", "abc").ValueBool()); + EXPECT_TRUE(EvaluateFunction(kEndsWith, "abcdef", "def").ValueBool()); + EXPECT_FALSE(EvaluateFunction(kEndsWith, "abcdef", "dEf").ValueBool()); + EXPECT_FALSE(EvaluateFunction(kEndsWith, "bcd", "abcd").ValueBool()); +} + +using memgraph::functions::kContains; +TEST_F(FunctionTest, Contains) { + EXPECT_THROW(EvaluateFunction(kContains), FunctionRuntimeException); + EXPECT_TRUE(EvaluateFunction(kContains, "a", TypedValue()).IsNull()); + EXPECT_THROW(EvaluateFunction(kContains, TypedValue(), 1.3), FunctionRuntimeException); + EXPECT_TRUE(EvaluateFunction(kContains, "abc", "abc").ValueBool()); + EXPECT_TRUE(EvaluateFunction(kContains, "abcde", "bcd").ValueBool()); + EXPECT_FALSE(EvaluateFunction(kContains, "cde", "abcdef").ValueBool()); + EXPECT_FALSE(EvaluateFunction(kContains, "abcdef", "dEf").ValueBool()); +} + +TEST_F(FunctionTest, Assert) { + // Invalid calls. + ASSERT_THROW(EvaluateFunction("ASSERT"), FunctionRuntimeException); + ASSERT_THROW(EvaluateFunction("ASSERT", false, false), FunctionRuntimeException); + ASSERT_THROW(EvaluateFunction("ASSERT", "string", false), FunctionRuntimeException); + ASSERT_THROW(EvaluateFunction("ASSERT", false, "reason", true), FunctionRuntimeException); + + // Valid calls, assertion fails. + ASSERT_THROW(EvaluateFunction("ASSERT", false), FunctionRuntimeException); + ASSERT_THROW(EvaluateFunction("ASSERT", false, "message"), FunctionRuntimeException); + try { + EvaluateFunction("ASSERT", false, "bbgba"); + } catch (FunctionRuntimeException &e) { + ASSERT_TRUE(std::string(e.what()).find("bbgba") != std::string::npos); + } + + // Valid calls, assertion passes. + ASSERT_TRUE(EvaluateFunction("ASSERT", true).ValueBool()); + ASSERT_TRUE(EvaluateFunction("ASSERT", true, "message").ValueBool()); +} + +TEST_F(FunctionTest, Counter) { + EXPECT_THROW(EvaluateFunction("COUNTER"), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("COUNTER", "a"), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("COUNTER", "a", "b"), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("COUNTER", "a", "b", "c"), FunctionRuntimeException); + + EXPECT_EQ(EvaluateFunction("COUNTER", "c1", 0).ValueInt(), 0); + EXPECT_EQ(EvaluateFunction("COUNTER", "c1", 0).ValueInt(), 1); + EXPECT_EQ(EvaluateFunction("COUNTER", "c2", 0).ValueInt(), 0); + EXPECT_EQ(EvaluateFunction("COUNTER", "c1", 0).ValueInt(), 2); + EXPECT_EQ(EvaluateFunction("COUNTER", "c2", 0).ValueInt(), 1); + + EXPECT_EQ(EvaluateFunction("COUNTER", "c3", -1).ValueInt(), -1); + EXPECT_EQ(EvaluateFunction("COUNTER", "c3", -1).ValueInt(), 0); + EXPECT_EQ(EvaluateFunction("COUNTER", "c3", -1).ValueInt(), 1); + + EXPECT_EQ(EvaluateFunction("COUNTER", "c4", 0, 5).ValueInt(), 0); + EXPECT_EQ(EvaluateFunction("COUNTER", "c4", 0, 5).ValueInt(), 5); + EXPECT_EQ(EvaluateFunction("COUNTER", "c4", 0, 5).ValueInt(), 10); + + EXPECT_EQ(EvaluateFunction("COUNTER", "c5", 0, -5).ValueInt(), 0); + EXPECT_EQ(EvaluateFunction("COUNTER", "c5", 0, -5).ValueInt(), -5); + EXPECT_EQ(EvaluateFunction("COUNTER", "c5", 0, -5).ValueInt(), -10); + + EXPECT_THROW(EvaluateFunction("COUNTER", "c6", 0, 0), FunctionRuntimeException); +} + +TEST_F(FunctionTest, Id) { + auto v = CreateVertex({}, shard_manager.get()); + EXPECT_THROW(EvaluateFunction("ID", v), FunctionRuntimeException); + auto e = CreateEdge({}, shard_manager.get(), EdgeId{10}); + EXPECT_EQ(EvaluateFunction("ID", e).ValueInt(), 10); +} + +TEST_F(FunctionTest, ToStringNull) { EXPECT_TRUE(EvaluateFunction("TOSTRING", TypedValue()).IsNull()); } + +TEST_F(FunctionTest, ToStringString) { + EXPECT_EQ(EvaluateFunction("TOSTRING", "").ValueString(), ""); + EXPECT_EQ(EvaluateFunction("TOSTRING", "this is a string").ValueString(), "this is a string"); +} + +TEST_F(FunctionTest, ToStringInteger) { + EXPECT_EQ(EvaluateFunction("TOSTRING", -23321312).ValueString(), "-23321312"); + EXPECT_EQ(EvaluateFunction("TOSTRING", 0).ValueString(), "0"); + EXPECT_EQ(EvaluateFunction("TOSTRING", 42).ValueString(), "42"); +} + +TEST_F(FunctionTest, ToStringDouble) { + EXPECT_EQ(EvaluateFunction("TOSTRING", -42.42).ValueString(), "-42.420000"); + EXPECT_EQ(EvaluateFunction("TOSTRING", 0.0).ValueString(), "0.000000"); + EXPECT_EQ(EvaluateFunction("TOSTRING", 238910.2313217).ValueString(), "238910.231322"); +} + +TEST_F(FunctionTest, ToStringBool) { + EXPECT_EQ(EvaluateFunction("TOSTRING", true).ValueString(), "true"); + EXPECT_EQ(EvaluateFunction("TOSTRING", false).ValueString(), "false"); +} + +TEST_F(FunctionTest, ToStringExceptions) { + EXPECT_THROW(EvaluateFunction("TOSTRING", 1, 2, 3), FunctionRuntimeException); +} + +TEST_F(FunctionTest, Left) { + EXPECT_THROW(EvaluateFunction("LEFT"), FunctionRuntimeException); + + EXPECT_TRUE(EvaluateFunction("LEFT", TypedValue(), TypedValue()).IsNull()); + EXPECT_TRUE(EvaluateFunction("LEFT", TypedValue(), 10).IsNull()); + EXPECT_THROW(EvaluateFunction("LEFT", TypedValue(), -10), FunctionRuntimeException); + + EXPECT_EQ(EvaluateFunction("LEFT", "memgraph", 0).ValueString(), ""); + EXPECT_EQ(EvaluateFunction("LEFT", "memgraph", 3).ValueString(), "mem"); + EXPECT_EQ(EvaluateFunction("LEFT", "memgraph", 1000).ValueString(), "memgraph"); + EXPECT_THROW(EvaluateFunction("LEFT", "memgraph", -10), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("LEFT", "memgraph", "graph"), FunctionRuntimeException); + + EXPECT_THROW(EvaluateFunction("LEFT", 132, 10), FunctionRuntimeException); +} + +TEST_F(FunctionTest, Right) { + EXPECT_THROW(EvaluateFunction("RIGHT"), FunctionRuntimeException); + + EXPECT_TRUE(EvaluateFunction("RIGHT", TypedValue(), TypedValue()).IsNull()); + EXPECT_TRUE(EvaluateFunction("RIGHT", TypedValue(), 10).IsNull()); + EXPECT_THROW(EvaluateFunction("RIGHT", TypedValue(), -10), FunctionRuntimeException); + + EXPECT_EQ(EvaluateFunction("RIGHT", "memgraph", 0).ValueString(), ""); + EXPECT_EQ(EvaluateFunction("RIGHT", "memgraph", 3).ValueString(), "aph"); + EXPECT_EQ(EvaluateFunction("RIGHT", "memgraph", 1000).ValueString(), "memgraph"); + EXPECT_THROW(EvaluateFunction("RIGHT", "memgraph", -10), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("RIGHT", "memgraph", "graph"), FunctionRuntimeException); + + EXPECT_THROW(EvaluateFunction("RIGHT", 132, 10), FunctionRuntimeException); +} + +TEST_F(FunctionTest, Trimming) { + EXPECT_TRUE(EvaluateFunction("LTRIM", TypedValue()).IsNull()); + EXPECT_TRUE(EvaluateFunction("RTRIM", TypedValue()).IsNull()); + EXPECT_TRUE(EvaluateFunction("TRIM", TypedValue()).IsNull()); + + EXPECT_EQ(EvaluateFunction("LTRIM", " abc ").ValueString(), "abc "); + EXPECT_EQ(EvaluateFunction("RTRIM", " abc ").ValueString(), " abc"); + EXPECT_EQ(EvaluateFunction("TRIM", "abc").ValueString(), "abc"); + + EXPECT_THROW(EvaluateFunction("LTRIM", "x", "y"), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("RTRIM", "x", "y"), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("TRIM", "x", "y"), FunctionRuntimeException); +} + +TEST_F(FunctionTest, Reverse) { + EXPECT_TRUE(EvaluateFunction("REVERSE", TypedValue()).IsNull()); + EXPECT_EQ(EvaluateFunction("REVERSE", "abc").ValueString(), "cba"); + EXPECT_THROW(EvaluateFunction("REVERSE", "x", "y"), FunctionRuntimeException); +} + +TEST_F(FunctionTest, Replace) { + EXPECT_THROW(EvaluateFunction("REPLACE"), FunctionRuntimeException); + EXPECT_TRUE(EvaluateFunction("REPLACE", TypedValue(), "l", "w").IsNull()); + EXPECT_TRUE(EvaluateFunction("REPLACE", "hello", TypedValue(), "w").IsNull()); + EXPECT_TRUE(EvaluateFunction("REPLACE", "hello", "l", TypedValue()).IsNull()); + EXPECT_EQ(EvaluateFunction("REPLACE", "hello", "l", "w").ValueString(), "hewwo"); + + EXPECT_THROW(EvaluateFunction("REPLACE", 1, "l", "w"), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("REPLACE", "hello", 1, "w"), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("REPLACE", "hello", "l", 1), FunctionRuntimeException); +} + +TEST_F(FunctionTest, Split) { + EXPECT_THROW(EvaluateFunction("SPLIT"), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("SPLIT", "one,two", 1), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("SPLIT", 1, "one,two"), FunctionRuntimeException); + + EXPECT_TRUE(EvaluateFunction("SPLIT", TypedValue(), TypedValue()).IsNull()); + EXPECT_TRUE(EvaluateFunction("SPLIT", "one,two", TypedValue()).IsNull()); + EXPECT_TRUE(EvaluateFunction("SPLIT", TypedValue(), ",").IsNull()); + + auto result = EvaluateFunction("SPLIT", "one,two", ","); + EXPECT_TRUE(result.IsList()); + EXPECT_EQ(result.ValueList()[0].ValueString(), "one"); + EXPECT_EQ(result.ValueList()[1].ValueString(), "two"); +} + +TEST_F(FunctionTest, Substring) { + EXPECT_THROW(EvaluateFunction("SUBSTRING"), FunctionRuntimeException); + + EXPECT_TRUE(EvaluateFunction("SUBSTRING", TypedValue(), 0, 10).IsNull()); + EXPECT_THROW(EvaluateFunction("SUBSTRING", TypedValue(), TypedValue()), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("SUBSTRING", TypedValue(), -10), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("SUBSTRING", TypedValue(), 0, TypedValue()), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("SUBSTRING", TypedValue(), 0, -10), FunctionRuntimeException); + + EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 2).ValueString(), "llo"); + EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 10).ValueString(), ""); + EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 2, 0).ValueString(), ""); + EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 1, 3).ValueString(), "ell"); + EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 1, 4).ValueString(), "ello"); + EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 1, 10).ValueString(), "ello"); +} + +TEST_F(FunctionTest, ToLower) { + EXPECT_THROW(EvaluateFunction("TOLOWER"), FunctionRuntimeException); + EXPECT_TRUE(EvaluateFunction("TOLOWER", TypedValue()).IsNull()); + EXPECT_EQ(EvaluateFunction("TOLOWER", "Ab__C").ValueString(), "ab__c"); +} + +TEST_F(FunctionTest, ToUpper) { + EXPECT_THROW(EvaluateFunction("TOUPPER"), FunctionRuntimeException); + EXPECT_TRUE(EvaluateFunction("TOUPPER", TypedValue()).IsNull()); + EXPECT_EQ(EvaluateFunction("TOUPPER", "Ab__C").ValueString(), "AB__C"); +} + +TEST_F(FunctionTest, ToByteString) { + EXPECT_THROW(EvaluateFunction("TOBYTESTRING"), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("TOBYTESTRING", 42), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("TOBYTESTRING", TypedValue()), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("TOBYTESTRING", "", 42), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("TOBYTESTRING", "ff"), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("TOBYTESTRING", "00"), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("TOBYTESTRING", "0xG"), FunctionRuntimeException); + EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "").ValueString(), ""); + EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "0x").ValueString(), ""); + EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "0X").ValueString(), ""); + EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "0x0123456789aAbBcCdDeEfF").ValueString(), + "\x01\x23\x45\x67\x89\xAA\xBB\xCC\xDD\xEE\xFF"); + EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "0x042").ValueString().size(), 2); + EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "0x042").ValueString(), + memgraph::utils::pmr::string("\x00\x42", 2, memgraph::utils::NewDeleteResource())); +} + +TEST_F(FunctionTest, FromByteString) { + EXPECT_THROW(EvaluateFunction("FROMBYTESTRING"), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("FROMBYTESTRING", 42), FunctionRuntimeException); + EXPECT_THROW(EvaluateFunction("FROMBYTESTRING", TypedValue()), FunctionRuntimeException); + EXPECT_EQ(EvaluateFunction("FROMBYTESTRING", "").ValueString(), ""); + auto bytestring = EvaluateFunction("TOBYTESTRING", "0x123456789aAbBcCdDeEfF"); + EXPECT_EQ(EvaluateFunction("FROMBYTESTRING", bytestring).ValueString(), "0x0123456789aabbccddeeff"); + EXPECT_EQ(EvaluateFunction("FROMBYTESTRING", std::string("\x00\x42", 2)).ValueString(), "0x0042"); +} + +} // namespace memgraph::query::v2::tests diff --git a/tests/unit/storage_v3_edge.cpp b/tests/unit/storage_v3_edge.cpp index 64f7f4e2e..3d2ab8bbd 100644 --- a/tests/unit/storage_v3_edge.cpp +++ b/tests/unit/storage_v3_edge.cpp @@ -97,8 +97,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { auto edge = res.GetValue(); ASSERT_EQ(edge.EdgeType(), et); ASSERT_EQ(edge.Gid(), edge_id); - ASSERT_EQ(edge.FromVertex(), from_id); - ASSERT_EQ(edge.ToVertex(), to_id); + ASSERT_EQ(edge.From(), from_id); + ASSERT_EQ(edge.To(), to_id); // Check edges without filters ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); @@ -116,8 +116,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); @@ -130,8 +130,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -187,8 +187,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } { auto ret = vertex_from->OutEdges(View::NEW); @@ -199,8 +199,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } { auto ret = vertex_to->InEdges(View::OLD); @@ -211,8 +211,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } { auto ret = vertex_to->InEdges(View::NEW); @@ -223,8 +223,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -278,592 +278,6 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { } } -// // NOLINTNEXTLINE(hicpp-special-member-functions) -// TEST_P(StorageEdgeTest, EdgeCreateFromLargerCommit) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}}); -// memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); -// memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); - -// // Create vertices -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_to = acc.CreateVertex(); -// auto vertex_from = acc.CreateVertex(); -// gid_to = vertex_to.Gid(); -// gid_from = vertex_from.Gid(); -// acc.Commit(GetNextHlc()); -// } - -// // Create edge -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// auto res = acc.CreateEdge(&from_id, &to_id, et); -// ASSERT_TRUE(res.HasValue()); -// auto edge = res.GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), *vertex_from); -// ASSERT_EQ(edge.ToVertex(), *vertex_to); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 0); -// { -// auto ret = vertex_from->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); -// { -// auto ret = vertex_to->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &to_id)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// { -// auto ret = vertex_from->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_from->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_to->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_to->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &to_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &to_id)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } -// } - -// // NOLINTNEXTLINE(hicpp-special-member-functions) -// TEST_P(StorageEdgeTest, EdgeCreateFromSameCommit) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}}); -// memgraph::storage::Gid gid_vertex = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); - -// // Create vertex -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.CreateVertex(); -// gid_vertex = vertex.Gid(); -// acc.Commit(GetNextHlc()); -// } - -// // Create edge -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// auto et = acc.NameToEdgeType("et5"); - -// auto res = acc.CreateEdge(&*vertex, &*vertex, et); -// ASSERT_TRUE(res.HasValue()); -// auto edge = res.GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), *vertex); -// ASSERT_EQ(edge.ToVertex(), *vertex); - -// // Check edges without filters -// ASSERT_EQ(vertex->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 0); -// { -// auto ret = vertex->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// ASSERT_EQ(vertex->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 0); -// { -// auto ret = vertex->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et}, &*vertex)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// auto et = acc.NameToEdgeType("et5"); - -// // Check edges without filters -// { -// auto ret = vertex->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// { -// auto ret = vertex->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// { -// auto ret = vertex->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// { -// auto ret = vertex->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et}, &*vertex)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } -// } - -// // NOLINTNEXTLINE(hicpp-special-member-functions) -// TEST_P(StorageEdgeTest, EdgeCreateFromSmallerAbort) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}}); -// memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); -// memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); - -// // Create vertices -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.CreateVertex(); -// auto vertex_to = acc.CreateVertex(); -// gid_from = vertex_from.Gid(); -// gid_to = vertex_to.Gid(); -// acc.Commit(GetNextHlc()); -// } - -// // Create edge, but abort the transaction -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// auto res = acc.CreateEdge(&from_id, &to_id, et); -// ASSERT_TRUE(res.HasValue()); -// auto edge = res.GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), *vertex_from); -// ASSERT_EQ(edge.ToVertex(), *vertex_to); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 0); -// { -// auto ret = vertex_from->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); -// { -// auto ret = vertex_to->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &to_id)->size(), 0); - -// acc.Abort(); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Create edge -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// auto res = acc.CreateEdge(&from_id, &to_id, et); -// ASSERT_TRUE(res.HasValue()); -// auto edge = res.GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), *vertex_from); -// ASSERT_EQ(edge.ToVertex(), *vertex_to); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 0); -// { -// auto ret = vertex_from->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); -// { -// auto ret = vertex_to->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &to_id)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// { -// auto ret = vertex_from->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_from->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_to->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_to->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &to_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &to_id)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } -// } - // NOLINTNEXTLINE(hicpp-special-member-functions) TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { // Create vertices @@ -900,8 +314,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { auto edge = res.GetValue(); ASSERT_EQ(edge.EdgeType(), et); ASSERT_EQ(edge.Gid(), edge_id); - ASSERT_EQ(edge.FromVertex(), from_id); - ASSERT_EQ(edge.ToVertex(), to_id); + ASSERT_EQ(edge.From(), from_id); + ASSERT_EQ(edge.To(), to_id); // Check edges without filters ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); @@ -919,8 +333,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); @@ -933,8 +347,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -1012,8 +426,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { auto edge = res.GetValue(); ASSERT_EQ(edge.EdgeType(), et); ASSERT_EQ(edge.Gid(), edge_id); - ASSERT_EQ(edge.FromVertex(), from_id); - ASSERT_EQ(edge.ToVertex(), to_id); + ASSERT_EQ(edge.From(), from_id); + ASSERT_EQ(edge.To(), to_id); // Check edges without filters ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); @@ -1031,8 +445,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); @@ -1045,8 +459,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -1104,8 +518,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } { auto ret = vertex_from->OutEdges(View::NEW); @@ -1116,8 +530,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } { auto ret = vertex_to->InEdges(View::OLD); @@ -1128,8 +542,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } { auto ret = vertex_to->InEdges(View::NEW); @@ -1140,8 +554,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -1173,232 +587,6 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { } } -// // NOLINTNEXTLINE(hicpp-special-member-functions) -// TEST_P(StorageEdgeTest, EdgeCreateFromSameAbort) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}}); -// memgraph::storage::Gid gid_vertex = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); - -// // Create vertex -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.CreateVertex(); -// gid_vertex = vertex.Gid(); -// acc.Commit(GetNextHlc()); -// } - -// // Create edge, but abort the transaction -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// auto et = acc.NameToEdgeType("et5"); - -// auto res = acc.CreateEdge(&*vertex, &*vertex, et); -// ASSERT_TRUE(res.HasValue()); -// auto edge = res.GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), *vertex); -// ASSERT_EQ(edge.ToVertex(), *vertex); - -// // Check edges without filters -// ASSERT_EQ(vertex->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 0); -// { -// auto ret = vertex->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// ASSERT_EQ(vertex->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 0); -// { -// auto ret = vertex->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et}, &*vertex)->size(), 0); - -// acc.Abort(); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// // Check edges without filters -// ASSERT_EQ(vertex->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Create edge -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// auto et = acc.NameToEdgeType("et5"); - -// auto res = acc.CreateEdge(&*vertex, &*vertex, et); -// ASSERT_TRUE(res.HasValue()); -// auto edge = res.GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), *vertex); -// ASSERT_EQ(edge.ToVertex(), *vertex); - -// // Check edges without filters -// ASSERT_EQ(vertex->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 0); -// { -// auto ret = vertex->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// ASSERT_EQ(vertex->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 0); -// { -// auto ret = vertex->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et}, &*vertex)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// auto et = acc.NameToEdgeType("et5"); - -// // Check edges without filters -// { -// auto ret = vertex->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// { -// auto ret = vertex->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// { -// auto ret = vertex->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// { -// auto ret = vertex->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::OLD, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::OLD, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et}, &*vertex)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } -// } - // NOLINTNEXTLINE(hicpp-special-member-functions) TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // Create vertex @@ -1432,8 +620,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { auto edge = res.GetValue(); ASSERT_EQ(edge.EdgeType(), et); ASSERT_EQ(edge.Gid(), edge_id); - ASSERT_EQ(edge.FromVertex(), from_id); - ASSERT_EQ(edge.ToVertex(), to_id); + ASSERT_EQ(edge.From(), from_id); + ASSERT_EQ(edge.To(), to_id); // Check edges without filters ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); @@ -1451,8 +639,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } // Check edges with filters @@ -1490,8 +678,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } { auto ret = vertex_from->OutEdges(View::NEW); @@ -1502,8 +690,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } // Check edges with filters @@ -1530,7 +718,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { const auto edge = vertex_from->OutEdges(View::NEW).GetValue()[0]; - const auto res = acc.DeleteEdge(edge.FromVertex(), edge.ToVertex(), edge.Gid()); + const auto res = acc.DeleteEdge(edge.From(), edge.To(), edge.Gid()); ASSERT_TRUE(res.HasValue()); ASSERT_TRUE(res.GetValue()); @@ -1548,8 +736,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_from->OutEdges(View::NEW)->size(), 0); ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 0); @@ -1589,921 +777,6 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { } } -// // NOLINTNEXTLINE(hicpp-special-member-functions) -// TEST_P(StorageEdgeTest, EdgeDeleteFromLargerCommit) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}}); -// memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); -// memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); - -// // Create vertices -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_to = acc.CreateVertex(); -// auto vertex_from = acc.CreateVertex(); -// gid_from = vertex_from.Gid(); -// gid_to = vertex_to.Gid(); -// acc.Commit(GetNextHlc()); -// } - -// // Create edge -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// auto res = acc.CreateEdge(&from_id, &to_id, et); -// ASSERT_TRUE(res.HasValue()); -// auto edge = res.GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), *vertex_from); -// ASSERT_EQ(edge.ToVertex(), *vertex_to); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 0); -// { -// auto ret = vertex_from->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); -// { -// auto ret = vertex_to->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &to_id)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// { -// auto ret = vertex_from->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_from->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_to->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_to->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &to_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &to_id)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Delete edge -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// auto edge = vertex_from->OutEdges(View::NEW).GetValue()[0]; - -// auto res = acc.DeleteEdge(&edge); -// ASSERT_TRUE(res.HasValue()); -// ASSERT_TRUE(res.GetValue()); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// { -// auto ret = vertex_from->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_from->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 0); -// { -// auto ret = vertex_to->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &to_id)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// acc.Commit(GetNextHlc()); -// } -// } - -// // NOLINTNEXTLINE(hicpp-special-member-functions) -// TEST_P(StorageEdgeTest, EdgeDeleteFromSameCommit) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}}); -// memgraph::storage::Gid gid_vertex = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); - -// // Create vertex -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.CreateVertex(); -// gid_vertex = vertex.Gid(); -// acc.Commit(GetNextHlc()); -// } - -// // Create edge -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// auto et = acc.NameToEdgeType("et5"); - -// auto res = acc.CreateEdge(&*vertex, &*vertex, et); -// ASSERT_TRUE(res.HasValue()); -// auto edge = res.GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), *vertex); -// ASSERT_EQ(edge.ToVertex(), *vertex); - -// // Check edges without filters -// ASSERT_EQ(vertex->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 0); -// { -// auto ret = vertex->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// ASSERT_EQ(vertex->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 0); -// { -// auto ret = vertex->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et}, &*vertex)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// auto et = acc.NameToEdgeType("et5"); - -// // Check edges without filters -// { -// auto ret = vertex->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// { -// auto ret = vertex->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// { -// auto ret = vertex->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// { -// auto ret = vertex->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::OLD, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::OLD, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et}, &*vertex)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Delete edge -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// auto et = acc.NameToEdgeType("et5"); - -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto res = acc.DeleteEdge(&edge); -// ASSERT_TRUE(res.HasValue()); -// ASSERT_TRUE(res.GetValue()); - -// // Check edges without filters -// { -// auto ret = vertex->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// ASSERT_EQ(vertex->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 0); -// { -// auto ret = vertex->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// ASSERT_EQ(vertex->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::OLD, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::OLD, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {other_et}, &*vertex)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// // Check edges without filters -// ASSERT_EQ(vertex->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 0); - -// acc.Commit(GetNextHlc()); -// } -// } - -// // NOLINTNEXTLINE(hicpp-special-member-functions) -// TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerAbort) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}}); -// memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); -// memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); - -// // Create vertices -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.CreateVertex(); -// auto vertex_to = acc.CreateVertex(); -// gid_from = vertex_from.Gid(); -// gid_to = vertex_to.Gid(); -// acc.Commit(GetNextHlc()); -// } - -// // Create edge -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// auto res = acc.CreateEdge(&from_id, &to_id, et); -// ASSERT_TRUE(res.HasValue()); -// auto edge = res.GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), *vertex_from); -// ASSERT_EQ(edge.ToVertex(), *vertex_to); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 0); -// { -// auto ret = vertex_from->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); -// { -// auto ret = vertex_to->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &to_id)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// { -// auto ret = vertex_from->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_from->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_to->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_to->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &to_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &to_id)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Delete the edge, but abort the transaction -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// auto edge = vertex_from->OutEdges(View::NEW).GetValue()[0]; - -// auto res = acc.DeleteEdge(&edge); -// ASSERT_TRUE(res.HasValue()); -// ASSERT_TRUE(res.GetValue()); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// { -// auto ret = vertex_from->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_from->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 0); -// { -// auto ret = vertex_to->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &to_id)->size(), 0); - -// acc.Abort(); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// { -// auto ret = vertex_from->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_from->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_to->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_to->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &to_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW, {}, &to_id)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Delete the edge -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// auto edge = vertex_from->OutEdges(View::NEW).GetValue()[0]; - -// auto res = acc.DeleteEdge(&edge); -// ASSERT_TRUE(res.HasValue()); -// ASSERT_TRUE(res.GetValue()); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// { -// auto ret = vertex_from->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_from->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 0); -// { -// auto ret = vertex_to->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &to_id)->size(), 1); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD, {}, &from_id)->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &from_id)->size(), 1); -// ASSERT_EQ(vertex_to->InEdges(View::OLD, {}, &to_id)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// // Check edges without filters -// ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_from->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 0); -// ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// acc.Commit(GetNextHlc()); -// } -// } - // NOLINTNEXTLINE(hicpp-special-member-functions) TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // Create vertex @@ -2532,8 +805,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { auto edge = res.GetValue(); ASSERT_EQ(edge.EdgeType(), et); ASSERT_EQ(edge.Gid(), edge_id); - ASSERT_EQ(edge.FromVertex(), from_id); - ASSERT_EQ(edge.ToVertex(), to_id); + ASSERT_EQ(edge.From(), from_id); + ASSERT_EQ(edge.To(), to_id); // Check edges without filters ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); @@ -2547,8 +820,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -2585,8 +858,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } { auto ret = vertex_to->InEdges(View::NEW); @@ -2597,8 +870,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -2638,7 +911,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { const auto edge = vertex_to->InEdges(View::NEW).GetValue()[0]; - auto res = acc.DeleteEdge(edge.FromVertex(), edge.ToVertex(), edge.Gid()); + auto res = acc.DeleteEdge(edge.From(), edge.To(), edge.Gid()); ASSERT_TRUE(res.HasValue()); ASSERT_TRUE(res.GetValue()); @@ -2653,8 +926,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); @@ -2692,8 +965,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } { auto ret = vertex_to->InEdges(View::NEW); @@ -2704,8 +977,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -2745,7 +1018,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { const auto edge = vertex_to->InEdges(View::NEW).GetValue()[0]; - auto res = acc.DeleteEdge(edge.FromVertex(), edge.ToVertex(), edge.Gid()); + auto res = acc.DeleteEdge(edge.From(), edge.To(), edge.Gid()); ASSERT_TRUE(res.HasValue()); ASSERT_TRUE(res.GetValue()); @@ -2758,8 +1031,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { ASSERT_EQ(*vertex_to->InDegree(View::OLD), 1); auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); @@ -2813,365 +1086,6 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { } } -// // NOLINTNEXTLINE(hicpp-special-member-functions) -// TEST_P(StorageEdgeTest, EdgeDeleteFromSameAbort) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}}); -// memgraph::storage::Gid gid_vertex = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); - -// // Create vertex -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.CreateVertex(); -// gid_vertex = vertex.Gid(); -// acc.Commit(GetNextHlc()); -// } - -// // Create edge -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// auto et = acc.NameToEdgeType("et5"); - -// auto res = acc.CreateEdge(&*vertex, &*vertex, et); -// ASSERT_TRUE(res.HasValue()); -// auto edge = res.GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), *vertex); -// ASSERT_EQ(edge.ToVertex(), *vertex); - -// // Check edges without filters -// ASSERT_EQ(vertex->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 0); -// { -// auto ret = vertex->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// ASSERT_EQ(vertex->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 0); -// { -// auto ret = vertex->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et}, &*vertex)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// auto et = acc.NameToEdgeType("et5"); - -// // Check edges without filters -// { -// auto ret = vertex->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// { -// auto ret = vertex->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// { -// auto ret = vertex->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// { -// auto ret = vertex->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::OLD, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::OLD, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et}, &*vertex)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Delete the edge, but abort the transaction -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// auto et = acc.NameToEdgeType("et5"); - -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto res = acc.DeleteEdge(&edge); -// ASSERT_TRUE(res.HasValue()); -// ASSERT_TRUE(res.GetValue()); - -// // Check edges without filters -// { -// auto ret = vertex->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// ASSERT_EQ(vertex->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 0); -// { -// auto ret = vertex->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// ASSERT_EQ(vertex->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::OLD, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::OLD, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {other_et}, &*vertex)->size(), 0); - -// acc.Abort(); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// auto et = acc.NameToEdgeType("et5"); - -// // Check edges without filters -// { -// auto ret = vertex->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// { -// auto ret = vertex->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// { -// auto ret = vertex->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// { -// auto ret = vertex->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::OLD, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::OLD, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW, {other_et}, &*vertex)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Delete the edge -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// auto et = acc.NameToEdgeType("et5"); - -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto res = acc.DeleteEdge(&edge); -// ASSERT_TRUE(res.HasValue()); -// ASSERT_TRUE(res.GetValue()); - -// // Check edges without filters -// { -// auto ret = vertex->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// ASSERT_EQ(vertex->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 0); -// { -// auto ret = vertex->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex); -// ASSERT_EQ(e.ToVertex(), *vertex); -// } -// ASSERT_EQ(vertex->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 0); - -// auto other_et = acc.NameToEdgeType("other"); - -// // Check edges with filters -// ASSERT_EQ(vertex->InEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->InEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::OLD, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->InEdges(View::OLD, {other_et}, &*vertex)->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {other_et})->size(), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {et, other_et})->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {}, &*vertex)->size(), 1); -// ASSERT_EQ(vertex->OutEdges(View::OLD, {other_et}, &*vertex)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Check whether the edge exists -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid_vertex, View::NEW); -// ASSERT_TRUE(vertex); - -// // Check edges without filters -// ASSERT_EQ(vertex->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex->OutDegree(View::NEW), 0); - -// acc.Commit(GetNextHlc()); -// } -// } - // NOLINTNEXTLINE(hicpp-special-member-functions) TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // Create vertices @@ -3195,8 +1109,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { auto edge = res.GetValue(); ASSERT_EQ(edge.EdgeType(), et); ASSERT_EQ(edge.Gid(), edge_id); - ASSERT_EQ(edge.FromVertex(), from_id); - ASSERT_EQ(edge.ToVertex(), to_id); + ASSERT_EQ(edge.From(), from_id); + ASSERT_EQ(edge.To(), to_id); // Check edges ASSERT_EQ(vertex_from.InEdges(View::NEW)->size(), 0); @@ -3210,8 +1124,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } { auto ret = vertex_to.InEdges(View::NEW); @@ -3221,8 +1135,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { ASSERT_EQ(*vertex_to.InDegree(View::NEW), 1); auto e = edges[0]; ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_to.OutEdges(View::NEW)->size(), 0); ASSERT_EQ(*vertex_to.OutDegree(View::NEW), 0); @@ -3266,8 +1180,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } 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)); @@ -3280,8 +1194,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { auto e = edges[0]; ASSERT_EQ(e.EdgeType(), et); ASSERT_EQ(e.Gid(), edge_id); - ASSERT_EQ(e.FromVertex(), from_id); - ASSERT_EQ(e.ToVertex(), to_id); + ASSERT_EQ(e.From(), from_id); + ASSERT_EQ(e.To(), to_id); } ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); @@ -3312,1964 +1226,4 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); } } - -// // NOLINTNEXTLINE(hicpp-special-member-functions) -// TEST_P(StorageEdgeTest, VertexDetachDeleteMultipleCommit) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}}); -// memgraph::storage::Gid gid_vertex1 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); -// memgraph::storage::Gid gid_vertex2 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); - -// // Create dataset -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex1 = acc.CreateVertex(); -// auto vertex2 = acc.CreateVertex(); - -// gid_vertex1 = vertex1.Gid(); -// gid_vertex2 = vertex2.Gid(); - -// auto et1 = acc.NameToEdgeType("et1"); -// auto et2 = acc.NameToEdgeType("et2"); -// auto et3 = acc.NameToEdgeType("et3"); -// auto et4 = acc.NameToEdgeType("et4"); - -// auto res1 = acc.CreateEdge(&vertex1, &vertex2, et1); -// ASSERT_TRUE(res1.HasValue()); -// auto edge1 = res1.GetValue(); -// ASSERT_EQ(edge1.EdgeType(), et1); -// ASSERT_EQ(edge1.FromVertex(), vertex1); -// ASSERT_EQ(edge1.ToVertex(), vertex2); - -// auto res2 = acc.CreateEdge(&vertex2, &vertex1, et2); -// ASSERT_TRUE(res2.HasValue()); -// auto edge2 = res2.GetValue(); -// ASSERT_EQ(edge2.EdgeType(), et2); -// ASSERT_EQ(edge2.FromVertex(), vertex2); -// ASSERT_EQ(edge2.ToVertex(), vertex1); - -// auto res3 = acc.CreateEdge(&vertex1, &vertex1, et3); -// ASSERT_TRUE(res3.HasValue()); -// auto edge3 = res3.GetValue(); -// ASSERT_EQ(edge3.EdgeType(), et3); -// ASSERT_EQ(edge3.FromVertex(), vertex1); -// ASSERT_EQ(edge3.ToVertex(), vertex1); - -// auto res4 = acc.CreateEdge(&vertex2, &vertex2, et4); -// ASSERT_TRUE(res4.HasValue()); -// auto edge4 = res4.GetValue(); -// ASSERT_EQ(edge4.EdgeType(), et4); -// ASSERT_EQ(edge4.FromVertex(), vertex2); -// ASSERT_EQ(edge4.ToVertex(), vertex2); - -// // Check edges -// { -// auto ret = vertex1.InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex1.InDegree(View::NEW), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex1); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex1); -// } -// } -// { -// auto ret = vertex1.OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex1.OutDegree(View::NEW), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex2); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex1); -// } -// } -// { -// auto ret = vertex2.InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex2.InDegree(View::NEW), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex2); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex2); -// } -// } -// { -// auto ret = vertex2.OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex2.OutDegree(View::NEW), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex1); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex2); -// } -// } - -// acc.Commit(GetNextHlc()); -// } - -// // Detach delete vertex -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex1 = acc.FindVertex(gid_vertex1, View::NEW); -// auto vertex2 = acc.FindVertex(gid_vertex2, View::NEW); -// ASSERT_TRUE(vertex1); -// ASSERT_TRUE(vertex2); - -// auto et1 = acc.NameToEdgeType("et1"); -// auto et2 = acc.NameToEdgeType("et2"); -// auto et3 = acc.NameToEdgeType("et3"); -// auto et4 = acc.NameToEdgeType("et4"); - -// // Delete must fail -// { -// auto ret = acc.DeleteVertex(&*vertex1); -// ASSERT_TRUE(ret.HasError()); -// ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES); -// } - -// // Detach delete vertex -// { -// auto ret = acc.DetachDeleteVertex(&*vertex1); -// ASSERT_TRUE(ret.HasValue()); -// ASSERT_TRUE(*ret); -// } - -// // Check edges -// { -// auto ret = vertex1->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex1->InDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// } -// ASSERT_EQ(vertex1->InEdges(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// ASSERT_EQ(vertex1->InDegree(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// { -// auto ret = vertex1->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex1->OutDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// } -// ASSERT_EQ(vertex1->OutEdges(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// ASSERT_EQ(vertex1->OutDegree(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// { -// auto ret = vertex2->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex2->InDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// } -// { -// auto ret = vertex2->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex2->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto ret = vertex2->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex2->OutDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// } -// { -// auto ret = vertex2->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex2->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } - -// acc.Commit(GetNextHlc()); -// } - -// // Check dataset -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex1 = acc.FindVertex(gid_vertex1, View::NEW); -// auto vertex2 = acc.FindVertex(gid_vertex2, View::NEW); -// ASSERT_FALSE(vertex1); -// ASSERT_TRUE(vertex2); - -// auto et4 = acc.NameToEdgeType("et4"); - -// // Check edges -// { -// auto ret = vertex2->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex2->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto ret = vertex2->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex2->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto ret = vertex2->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex2->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto ret = vertex2->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex2->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// } -// } - -// // NOLINTNEXTLINE(hicpp-special-member-functions) -// TEST_P(StorageEdgeTest, VertexDetachDeleteSingleAbort) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}}); -// memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); -// memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); - -// // Create dataset -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.CreateVertex(); -// auto vertex_to = acc.CreateVertex(); - -// auto et = acc.NameToEdgeType("et5"); - -// auto res = acc.CreateEdge(&vertex_from, &vertex_to, et); -// ASSERT_TRUE(res.HasValue()); -// auto edge = res.GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), vertex_from); -// ASSERT_EQ(edge.ToVertex(), vertex_to); - -// gid_from = vertex_from.Gid(); -// gid_to = vertex_to.Gid(); - -// // Check edges -// ASSERT_EQ(vertex_from.InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from.InDegree(View::NEW), 0); -// { -// auto ret = vertex_from.OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from.OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), vertex_from); -// ASSERT_EQ(e.ToVertex(), vertex_to); -// } -// { -// auto ret = vertex_to.InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to.InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), vertex_from); -// ASSERT_EQ(e.ToVertex(), vertex_to); -// } -// ASSERT_EQ(vertex_to.OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to.OutDegree(View::NEW), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Detach delete vertex, but abort the transaction -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// // Delete must fail -// { -// auto ret = acc.DeleteVertex(&from_id); -// ASSERT_TRUE(ret.HasError()); -// ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES); -// } - -// // Detach delete vertex -// { -// auto ret = acc.DetachDeleteVertex(&from_id); -// ASSERT_TRUE(ret.HasValue()); -// ASSERT_TRUE(*ret); -// } - -// // 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(), memgraph::storage::Error::DELETED_OBJECT); -// ASSERT_EQ(vertex_from->InDegree(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// { -// auto ret = vertex_from->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_from->OutEdges(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// ASSERT_EQ(vertex_from->OutDegree(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// { -// auto ret = vertex_to->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// acc.Abort(); -// } - -// // Check dataset -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// // Check edges -// ASSERT_EQ(vertex_from->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_from->InDegree(View::NEW), 0); -// { -// auto ret = vertex_from->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// { -// auto ret = vertex_to->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Detach delete vertex -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_TRUE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// auto et = acc.NameToEdgeType("et5"); - -// // Delete must fail -// { -// auto ret = acc.DeleteVertex(&from_id); -// ASSERT_TRUE(ret.HasError()); -// ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES); -// } - -// // Detach delete vertex -// { -// auto ret = acc.DetachDeleteVertex(&from_id); -// ASSERT_TRUE(ret.HasValue()); -// ASSERT_TRUE(*ret); -// } - -// // 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(), memgraph::storage::Error::DELETED_OBJECT); -// ASSERT_EQ(vertex_from->InDegree(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// { -// auto ret = vertex_from->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_from->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_from->OutEdges(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// ASSERT_EQ(vertex_from->OutDegree(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// { -// auto ret = vertex_to->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et); -// ASSERT_EQ(e.FromVertex(), *vertex_from); -// ASSERT_EQ(e.ToVertex(), *vertex_to); -// } -// ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Check dataset -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex_from = acc.FindVertex(gid_from, View::NEW); -// auto vertex_to = acc.FindVertex(gid_to, View::NEW); -// ASSERT_FALSE(vertex_from); -// ASSERT_TRUE(vertex_to); - -// // Check edges -// ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); -// ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); -// ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); -// } -// } - -// // NOLINTNEXTLINE(hicpp-special-member-functions) -// TEST_P(StorageEdgeTest, VertexDetachDeleteMultipleAbort) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}}); -// memgraph::storage::Gid gid_vertex1 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); -// memgraph::storage::Gid gid_vertex2 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); - -// // Create dataset -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex1 = acc.CreateVertex(); -// auto vertex2 = acc.CreateVertex(); - -// gid_vertex1 = vertex1.Gid(); -// gid_vertex2 = vertex2.Gid(); - -// auto et1 = acc.NameToEdgeType("et1"); -// auto et2 = acc.NameToEdgeType("et2"); -// auto et3 = acc.NameToEdgeType("et3"); -// auto et4 = acc.NameToEdgeType("et4"); - -// auto res1 = acc.CreateEdge(&vertex1, &vertex2, et1); -// ASSERT_TRUE(res1.HasValue()); -// auto edge1 = res1.GetValue(); -// ASSERT_EQ(edge1.EdgeType(), et1); -// ASSERT_EQ(edge1.FromVertex(), vertex1); -// ASSERT_EQ(edge1.ToVertex(), vertex2); - -// auto res2 = acc.CreateEdge(&vertex2, &vertex1, et2); -// ASSERT_TRUE(res2.HasValue()); -// auto edge2 = res2.GetValue(); -// ASSERT_EQ(edge2.EdgeType(), et2); -// ASSERT_EQ(edge2.FromVertex(), vertex2); -// ASSERT_EQ(edge2.ToVertex(), vertex1); - -// auto res3 = acc.CreateEdge(&vertex1, &vertex1, et3); -// ASSERT_TRUE(res3.HasValue()); -// auto edge3 = res3.GetValue(); -// ASSERT_EQ(edge3.EdgeType(), et3); -// ASSERT_EQ(edge3.FromVertex(), vertex1); -// ASSERT_EQ(edge3.ToVertex(), vertex1); - -// auto res4 = acc.CreateEdge(&vertex2, &vertex2, et4); -// ASSERT_TRUE(res4.HasValue()); -// auto edge4 = res4.GetValue(); -// ASSERT_EQ(edge4.EdgeType(), et4); -// ASSERT_EQ(edge4.FromVertex(), vertex2); -// ASSERT_EQ(edge4.ToVertex(), vertex2); - -// // Check edges -// { -// auto ret = vertex1.InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex1.InDegree(View::NEW), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex1); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex1); -// } -// } -// { -// auto ret = vertex1.OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex1.OutDegree(View::NEW), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex2); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex1); -// } -// } -// { -// auto ret = vertex2.InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex2.InDegree(View::NEW), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex2); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex2); -// } -// } -// { -// auto ret = vertex2.OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex2.OutDegree(View::NEW), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex1); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex2); -// } -// } - -// acc.Commit(GetNextHlc()); -// } - -// // Detach delete vertex, but abort the transaction -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex1 = acc.FindVertex(gid_vertex1, View::NEW); -// auto vertex2 = acc.FindVertex(gid_vertex2, View::NEW); -// ASSERT_TRUE(vertex1); -// ASSERT_TRUE(vertex2); - -// auto et1 = acc.NameToEdgeType("et1"); -// auto et2 = acc.NameToEdgeType("et2"); -// auto et3 = acc.NameToEdgeType("et3"); -// auto et4 = acc.NameToEdgeType("et4"); - -// // Delete must fail -// { -// auto ret = acc.DeleteVertex(&*vertex1); -// ASSERT_TRUE(ret.HasError()); -// ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES); -// } - -// // Detach delete vertex -// { -// auto ret = acc.DetachDeleteVertex(&*vertex1); -// ASSERT_TRUE(ret.HasValue()); -// ASSERT_TRUE(*ret); -// } - -// // Check edges -// { -// auto ret = vertex1->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex1->InDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// } -// ASSERT_EQ(vertex1->InEdges(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// ASSERT_EQ(vertex1->InDegree(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// { -// auto ret = vertex1->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex1->OutDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// } -// ASSERT_EQ(vertex1->OutEdges(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// ASSERT_EQ(vertex1->OutDegree(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// { -// auto ret = vertex2->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex2->InDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// } -// { -// auto ret = vertex2->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex2->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto ret = vertex2->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex2->OutDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// } -// { -// auto ret = vertex2->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex2->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } - -// acc.Abort(); -// } - -// // Check dataset -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex1 = acc.FindVertex(gid_vertex1, View::NEW); -// auto vertex2 = acc.FindVertex(gid_vertex2, View::NEW); -// ASSERT_TRUE(vertex1); -// ASSERT_TRUE(vertex2); - -// auto et1 = acc.NameToEdgeType("et1"); -// auto et2 = acc.NameToEdgeType("et2"); -// auto et3 = acc.NameToEdgeType("et3"); -// auto et4 = acc.NameToEdgeType("et4"); - -// // Check edges -// { -// auto ret = vertex1->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex1->InDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// } -// { -// auto ret = vertex1->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex1->InDegree(View::NEW), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// } -// { -// auto ret = vertex1->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex1->OutDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// } -// { -// auto ret = vertex1->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex1->OutDegree(View::NEW), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// } -// { -// auto ret = vertex2->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex2->InDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// } -// { -// auto ret = vertex2->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex2->InDegree(View::NEW), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// } -// { -// auto ret = vertex2->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex2->OutDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// } -// { -// auto ret = vertex2->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex2->OutDegree(View::NEW), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// } - -// acc.Commit(GetNextHlc()); -// } - -// // Detach delete vertex -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex1 = acc.FindVertex(gid_vertex1, View::NEW); -// auto vertex2 = acc.FindVertex(gid_vertex2, View::NEW); -// ASSERT_TRUE(vertex1); -// ASSERT_TRUE(vertex2); - -// auto et1 = acc.NameToEdgeType("et1"); -// auto et2 = acc.NameToEdgeType("et2"); -// auto et3 = acc.NameToEdgeType("et3"); -// auto et4 = acc.NameToEdgeType("et4"); - -// // Delete must fail -// { -// auto ret = acc.DeleteVertex(&*vertex1); -// ASSERT_TRUE(ret.HasError()); -// ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES); -// } - -// // Detach delete vertex -// { -// auto ret = acc.DetachDeleteVertex(&*vertex1); -// ASSERT_TRUE(ret.HasValue()); -// ASSERT_TRUE(*ret); -// } - -// // Check edges -// { -// auto ret = vertex1->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex1->InDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// } -// ASSERT_EQ(vertex1->InEdges(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// ASSERT_EQ(vertex1->InDegree(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// { -// auto ret = vertex1->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex1->OutDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// } -// ASSERT_EQ(vertex1->OutEdges(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// ASSERT_EQ(vertex1->OutDegree(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); -// { -// auto ret = vertex2->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex2->InDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// } -// { -// auto ret = vertex2->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex2->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto ret = vertex2->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); -// }); ASSERT_EQ(edges.size(), 2); ASSERT_EQ(*vertex2->OutDegree(View::OLD), 2); -// { -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); -// } -// { -// auto e = edges[1]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// } -// { -// auto ret = vertex2->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex2->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } - -// acc.Commit(GetNextHlc()); -// } - -// // Check dataset -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex1 = acc.FindVertex(gid_vertex1, View::NEW); -// auto vertex2 = acc.FindVertex(gid_vertex2, View::NEW); -// ASSERT_FALSE(vertex1); -// ASSERT_TRUE(vertex2); - -// auto et4 = acc.NameToEdgeType("et4"); - -// // Check edges -// { -// auto ret = vertex2->InEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex2->InDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto ret = vertex2->InEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex2->InDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto ret = vertex2->OutEdges(View::OLD); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex2->OutDegree(View::OLD), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// { -// auto ret = vertex2->OutEdges(View::NEW); -// ASSERT_TRUE(ret.HasValue()); -// auto edges = ret.GetValue(); -// ASSERT_EQ(edges.size(), 1); -// ASSERT_EQ(*vertex2->OutDegree(View::NEW), 1); -// auto e = edges[0]; -// ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); -// } -// } -// } - -// // NOLINTNEXTLINE(hicpp-special-member-functions) -// TEST(StorageWithProperties, EdgePropertyCommit) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = true}}); -// memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.CreateVertex(); -// gid = vertex.Gid(); -// auto et = acc.NameToEdgeType("et5"); -// auto edge = acc.CreateEdge(&vertex, &vertex, et).GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), vertex); -// ASSERT_EQ(edge.ToVertex(), vertex); - -// auto property = acc.NameToProperty("property5"); - -// ASSERT_TRUE(edge.GetProperty(property, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::NEW)->size(), 0); - -// { -// auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue("temporary")); -// ASSERT_TRUE(old_value.HasValue()); -// ASSERT_TRUE(old_value->IsNull()); -// } - -// ASSERT_EQ(edge.GetProperty(property, View::NEW)->ValueString(), "temporary"); -// { -// auto properties = edge.Properties(View::NEW).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "temporary"); -// } - -// { -// auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue("nandare")); -// ASSERT_TRUE(old_value.HasValue()); -// ASSERT_FALSE(old_value->IsNull()); -// } - -// ASSERT_EQ(edge.GetProperty(property, View::NEW)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::NEW).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// acc.Commit(GetNextHlc()); -// } -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property = acc.NameToProperty("property5"); - -// ASSERT_EQ(edge.GetProperty(property, View::OLD)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::OLD).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// ASSERT_EQ(edge.GetProperty(property, View::NEW)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::NEW).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// auto other_property = acc.NameToProperty("other"); - -// ASSERT_TRUE(edge.GetProperty(other_property, View::OLD)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(other_property, View::NEW)->IsNull()); - -// acc.Abort(); -// } -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property = acc.NameToProperty("property5"); - -// { -// auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue()); -// ASSERT_TRUE(old_value.HasValue()); -// ASSERT_FALSE(old_value->IsNull()); -// } - -// ASSERT_EQ(edge.GetProperty(property, View::OLD)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::OLD).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// ASSERT_TRUE(edge.GetProperty(property, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::NEW)->size(), 0); - -// { -// auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue()); -// ASSERT_TRUE(old_value.HasValue()); -// ASSERT_TRUE(old_value->IsNull()); -// } - -// acc.Commit(GetNextHlc()); -// } -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property = acc.NameToProperty("property5"); - -// ASSERT_TRUE(edge.GetProperty(property, View::OLD)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(property, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::OLD)->size(), 0); -// ASSERT_EQ(edge.Properties(View::NEW)->size(), 0); - -// auto other_property = acc.NameToProperty("other"); - -// ASSERT_TRUE(edge.GetProperty(other_property, View::OLD)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(other_property, View::NEW)->IsNull()); - -// acc.Abort(); -// } -// } - -// // NOLINTNEXTLINE(hicpp-special-member-functions) -// TEST(StorageWithProperties, EdgePropertyAbort) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = true}}); -// memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); - -// // Create the vertex. -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.CreateVertex(); -// gid = vertex.Gid(); -// auto et = acc.NameToEdgeType("et5"); -// auto edge = acc.CreateEdge(&vertex, &vertex, et).GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), vertex); -// ASSERT_EQ(edge.ToVertex(), vertex); -// acc.Commit(GetNextHlc()); -// } - -// // Set property 5 to "nandare", but abort the transaction. -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property = acc.NameToProperty("property5"); - -// ASSERT_TRUE(edge.GetProperty(property, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::NEW)->size(), 0); - -// { -// auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue("temporary")); -// ASSERT_TRUE(old_value.HasValue()); -// ASSERT_TRUE(old_value->IsNull()); -// } - -// ASSERT_EQ(edge.GetProperty(property, View::NEW)->ValueString(), "temporary"); -// { -// auto properties = edge.Properties(View::NEW).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "temporary"); -// } - -// { -// auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue("nandare")); -// ASSERT_TRUE(old_value.HasValue()); -// ASSERT_FALSE(old_value->IsNull()); -// } - -// ASSERT_EQ(edge.GetProperty(property, View::NEW)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::NEW).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// acc.Abort(); -// } - -// // Check that property 5 is null. -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property = acc.NameToProperty("property5"); - -// ASSERT_TRUE(edge.GetProperty(property, View::OLD)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(property, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::OLD)->size(), 0); -// ASSERT_EQ(edge.Properties(View::NEW)->size(), 0); - -// auto other_property = acc.NameToProperty("other"); - -// ASSERT_TRUE(edge.GetProperty(other_property, View::OLD)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(other_property, View::NEW)->IsNull()); - -// acc.Abort(); -// } - -// // Set property 5 to "nandare". -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property = acc.NameToProperty("property5"); - -// ASSERT_TRUE(edge.GetProperty(property, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::NEW)->size(), 0); - -// { -// auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue("temporary")); -// ASSERT_TRUE(old_value.HasValue()); -// ASSERT_TRUE(old_value->IsNull()); -// } - -// ASSERT_EQ(edge.GetProperty(property, View::NEW)->ValueString(), "temporary"); -// { -// auto properties = edge.Properties(View::NEW).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "temporary"); -// } - -// { -// auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue("nandare")); -// ASSERT_TRUE(old_value.HasValue()); -// ASSERT_FALSE(old_value->IsNull()); -// } - -// ASSERT_EQ(edge.GetProperty(property, View::NEW)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::NEW).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// acc.Commit(GetNextHlc()); -// } - -// // Check that property 5 is "nandare". -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property = acc.NameToProperty("property5"); - -// ASSERT_EQ(edge.GetProperty(property, View::OLD)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::OLD).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// ASSERT_EQ(edge.GetProperty(property, View::NEW)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::NEW).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// auto other_property = acc.NameToProperty("other"); - -// ASSERT_TRUE(edge.GetProperty(other_property, View::OLD)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(other_property, View::NEW)->IsNull()); - -// acc.Abort(); -// } - -// // Set property 5 to null, but abort the transaction. -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property = acc.NameToProperty("property5"); - -// ASSERT_EQ(edge.GetProperty(property, View::OLD)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::OLD).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// ASSERT_EQ(edge.GetProperty(property, View::NEW)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::NEW).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// { -// auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue()); -// ASSERT_TRUE(old_value.HasValue()); -// ASSERT_FALSE(old_value->IsNull()); -// } - -// ASSERT_EQ(edge.GetProperty(property, View::OLD)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::OLD).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// ASSERT_TRUE(edge.GetProperty(property, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::NEW)->size(), 0); - -// acc.Abort(); -// } - -// // Check that property 5 is "nandare". -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property = acc.NameToProperty("property5"); - -// ASSERT_EQ(edge.GetProperty(property, View::OLD)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::OLD).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// ASSERT_EQ(edge.GetProperty(property, View::NEW)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::NEW).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// auto other_property = acc.NameToProperty("other"); - -// ASSERT_TRUE(edge.GetProperty(other_property, View::OLD)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(other_property, View::NEW)->IsNull()); - -// acc.Abort(); -// } - -// // Set property 5 to null. -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property = acc.NameToProperty("property5"); - -// ASSERT_EQ(edge.GetProperty(property, View::OLD)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::OLD).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// ASSERT_EQ(edge.GetProperty(property, View::NEW)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::NEW).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// { -// auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue()); -// ASSERT_TRUE(old_value.HasValue()); -// ASSERT_FALSE(old_value->IsNull()); -// } - -// ASSERT_EQ(edge.GetProperty(property, View::OLD)->ValueString(), "nandare"); -// { -// auto properties = edge.Properties(View::OLD).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property].ValueString(), "nandare"); -// } - -// ASSERT_TRUE(edge.GetProperty(property, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::NEW)->size(), 0); - -// acc.Commit(GetNextHlc()); -// } - -// // Check that property 5 is null. -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property = acc.NameToProperty("property5"); - -// ASSERT_TRUE(edge.GetProperty(property, View::OLD)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(property, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::OLD)->size(), 0); -// ASSERT_EQ(edge.Properties(View::NEW)->size(), 0); - -// auto other_property = acc.NameToProperty("other"); - -// ASSERT_TRUE(edge.GetProperty(other_property, View::OLD)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(other_property, View::NEW)->IsNull()); - -// acc.Abort(); -// } -// } - -// // NOLINTNEXTLINE(hicpp-special-member-functions) -// TEST(StorageWithProperties, EdgePropertySerializationError) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = true}}); -// memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.CreateVertex(); -// gid = vertex.Gid(); -// auto et = acc.NameToEdgeType("et5"); -// auto edge = acc.CreateEdge(&vertex, &vertex, et).GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), vertex); -// ASSERT_EQ(edge.ToVertex(), vertex); -// acc.Commit(GetNextHlc()); -// } - -// auto acc1 = store.Access(GetNextHlc()); -// auto acc2 = store.Access(GetNextHlc()); - -// // Set property 1 to 123 in accessor 1. -// { -// auto vertex = acc1.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property1 = acc1.NameToProperty("property1"); -// auto property2 = acc1.NameToProperty("property2"); - -// ASSERT_TRUE(edge.GetProperty(property1, View::OLD)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(property1, View::NEW)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(property2, View::OLD)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(property2, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::OLD)->size(), 0); -// ASSERT_EQ(edge.Properties(View::NEW)->size(), 0); - -// { -// auto old_value = edge.SetProperty(property1, memgraph::storage::PropertyValue(123)); -// ASSERT_TRUE(old_value.HasValue()); -// ASSERT_TRUE(old_value->IsNull()); -// } - -// ASSERT_TRUE(edge.GetProperty(property1, View::OLD)->IsNull()); -// ASSERT_EQ(edge.GetProperty(property1, View::NEW)->ValueInt(), 123); -// ASSERT_TRUE(edge.GetProperty(property2, View::OLD)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(property2, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::OLD)->size(), 0); -// { -// auto properties = edge.Properties(View::NEW).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property1].ValueInt(), 123); -// } -// } - -// // Set property 2 to "nandare" in accessor 2. -// { -// auto vertex = acc2.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property1 = acc2.NameToProperty("property1"); -// auto property2 = acc2.NameToProperty("property2"); - -// ASSERT_TRUE(edge.GetProperty(property1, View::OLD)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(property1, View::NEW)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(property2, View::OLD)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(property2, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::OLD)->size(), 0); -// ASSERT_EQ(edge.Properties(View::NEW)->size(), 0); - -// { -// auto res = edge.SetProperty(property2, memgraph::storage::PropertyValue("nandare")); -// ASSERT_TRUE(res.HasError()); -// ASSERT_EQ(res.GetError(), memgraph::storage::Error::SERIALIZATION_ERROR); -// } -// } - -// // Finalize both accessors. -// ASSERT_FALSE(acc1.Commit(GetNextHlc()); -// acc2.Abort(); - -// // Check which properties exist. -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property1 = acc.NameToProperty("property1"); -// auto property2 = acc.NameToProperty("property2"); - -// ASSERT_EQ(edge.GetProperty(property1, View::OLD)->ValueInt(), 123); -// ASSERT_TRUE(edge.GetProperty(property2, View::OLD)->IsNull()); -// { -// auto properties = edge.Properties(View::OLD).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property1].ValueInt(), 123); -// } - -// ASSERT_EQ(edge.GetProperty(property1, View::NEW)->ValueInt(), 123); -// ASSERT_TRUE(edge.GetProperty(property2, View::NEW)->IsNull()); -// { -// auto properties = edge.Properties(View::NEW).GetValue(); -// ASSERT_EQ(properties.size(), 1); -// ASSERT_EQ(properties[property1].ValueInt(), 123); -// } - -// acc.Abort(); -// } -// } - -// TEST(StorageWithProperties, EdgePropertyClear) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = true}}); -// memgraph::storage::Gid gid; -// auto property1 = store.NameToProperty("property1"); -// auto property2 = store.NameToProperty("property2"); -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.CreateVertex(); -// gid = vertex.Gid(); -// auto et = acc.NameToEdgeType("et5"); -// auto edge = acc.CreateEdge(&vertex, &vertex, et).GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), vertex); -// ASSERT_EQ(edge.ToVertex(), vertex); - -// auto old_value = edge.SetProperty(property1, memgraph::storage::PropertyValue("value")); -// ASSERT_TRUE(old_value.HasValue()); -// ASSERT_TRUE(old_value->IsNull()); - -// acc.Commit(GetNextHlc()); -// } -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// ASSERT_EQ(edge.GetProperty(property1, View::OLD)->ValueString(), "value"); -// ASSERT_TRUE(edge.GetProperty(property2, View::OLD)->IsNull()); -// ASSERT_THAT(edge.Properties(View::OLD).GetValue(), -// UnorderedElementsAre(std::pair(property1, memgraph::storage::PropertyValue("value")))); - -// { -// auto old_values = edge.ClearProperties(); -// ASSERT_TRUE(old_values.HasValue()); -// ASSERT_FALSE(old_values->empty()); -// } - -// ASSERT_TRUE(edge.GetProperty(property1, View::NEW)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(property2, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::NEW).GetValue().size(), 0); - -// { -// auto old_values = edge.ClearProperties(); -// ASSERT_TRUE(old_values.HasValue()); -// ASSERT_TRUE(old_values->empty()); -// } - -// ASSERT_TRUE(edge.GetProperty(property1, View::NEW)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(property2, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::NEW).GetValue().size(), 0); - -// acc.Abort(); -// } -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto old_value = edge.SetProperty(property2, memgraph::storage::PropertyValue(42)); -// ASSERT_TRUE(old_value.HasValue()); -// ASSERT_TRUE(old_value->IsNull()); - -// acc.Commit(GetNextHlc()); -// } -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// ASSERT_EQ(edge.GetProperty(property1, View::OLD)->ValueString(), "value"); -// ASSERT_EQ(edge.GetProperty(property2, View::OLD)->ValueInt(), 42); -// ASSERT_THAT(edge.Properties(View::OLD).GetValue(), -// UnorderedElementsAre(std::pair(property1, memgraph::storage::PropertyValue("value")), -// std::pair(property2, memgraph::storage::PropertyValue(42)))); - -// { -// auto old_values = edge.ClearProperties(); -// ASSERT_TRUE(old_values.HasValue()); -// ASSERT_FALSE(old_values->empty()); -// } - -// ASSERT_TRUE(edge.GetProperty(property1, View::NEW)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(property2, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::NEW).GetValue().size(), 0); - -// { -// auto old_values = edge.ClearProperties(); -// ASSERT_TRUE(old_values.HasValue()); -// ASSERT_TRUE(old_values->empty()); -// } - -// ASSERT_TRUE(edge.GetProperty(property1, View::NEW)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(property2, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::NEW).GetValue().size(), 0); - -// acc.Commit(GetNextHlc()); -// } -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// ASSERT_TRUE(edge.GetProperty(property1, View::NEW)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(property2, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::NEW).GetValue().size(), 0); - -// acc.Abort(); -// } -// } - -// // NOLINTNEXTLINE(hicpp-special-member-functions) -// TEST(StorageWithoutProperties, EdgePropertyAbort) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = false}}); -// memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max()); -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.CreateVertex(); -// gid = vertex.Gid(); -// auto et = acc.NameToEdgeType("et5"); -// auto edge = acc.CreateEdge(&vertex, &vertex, et).GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), vertex); -// ASSERT_EQ(edge.ToVertex(), vertex); -// acc.Commit(GetNextHlc()); -// } -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property = acc.NameToProperty("property5"); - -// ASSERT_TRUE(edge.GetProperty(property, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::NEW)->size(), 0); - -// { -// auto res = edge.SetProperty(property, memgraph::storage::PropertyValue("temporary")); -// ASSERT_TRUE(res.HasError()); -// ASSERT_EQ(res.GetError(), memgraph::storage::Error::PROPERTIES_DISABLED); -// } - -// ASSERT_TRUE(edge.GetProperty(property, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::NEW)->size(), 0); - -// { -// auto res = edge.SetProperty(property, memgraph::storage::PropertyValue("nandare")); -// ASSERT_TRUE(res.HasError()); -// ASSERT_EQ(res.GetError(), memgraph::storage::Error::PROPERTIES_DISABLED); -// } - -// ASSERT_TRUE(edge.GetProperty(property, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::NEW)->size(), 0); - -// acc.Abort(); -// } -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// auto property = acc.NameToProperty("property5"); - -// ASSERT_TRUE(edge.GetProperty(property, View::OLD)->IsNull()); -// ASSERT_EQ(edge.Properties(View::OLD)->size(), 0); - -// ASSERT_TRUE(edge.GetProperty(property, View::NEW)->IsNull()); -// ASSERT_EQ(edge.Properties(View::NEW)->size(), 0); - -// auto other_property = acc.NameToProperty("other"); - -// ASSERT_TRUE(edge.GetProperty(other_property, View::OLD)->IsNull()); -// ASSERT_TRUE(edge.GetProperty(other_property, View::NEW)->IsNull()); - -// acc.Abort(); -// } -// } - -// TEST(StorageWithoutProperties, EdgePropertyClear) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = false}}); -// memgraph::storage::Gid gid; -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.CreateVertex(); -// gid = vertex.Gid(); -// auto et = acc.NameToEdgeType("et5"); -// auto edge = acc.CreateEdge(&vertex, &vertex, et).GetValue(); -// ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), vertex); -// ASSERT_EQ(edge.ToVertex(), vertex); -// acc.Commit(GetNextHlc()); -// } -// { -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.FindVertex(gid, View::OLD); -// ASSERT_TRUE(vertex); -// auto edge = vertex->OutEdges(View::NEW).GetValue()[0]; - -// ASSERT_EQ(edge.ClearProperties().GetError(), memgraph::storage::Error::PROPERTIES_DISABLED); - -// acc.Abort(); -// } -// } - -// TEST(StorageWithProperties, EdgeNonexistentPropertyAPI) { -// memgraph::storage::Storage store({.items = {.properties_on_edges = true}}); - -// auto property = store.NameToProperty("property"); - -// auto acc = store.Access(GetNextHlc()); -// auto vertex = acc.CreateVertex(); -// auto edge = acc.CreateEdge(&vertex, &vertex, acc.NameToEdgeType("edge")); -// ASSERT_TRUE(edge.HasValue()); - -// // Check state before (OLD view). -// ASSERT_EQ(edge->Properties(View::OLD).GetError(), memgraph::storage::Error::NONEXISTENT_OBJECT); -// ASSERT_EQ(edge->GetProperty(property, View::OLD).GetError(), memgraph::storage::Error::NONEXISTENT_OBJECT); - -// // Check state before (NEW view). -// ASSERT_EQ(edge->Properties(View::NEW)->size(), 0); -// ASSERT_EQ(*edge->GetProperty(property, View::NEW), memgraph::storage::PropertyValue()); - -// // Modify edge. -// ASSERT_TRUE(edge->SetProperty(property, memgraph::storage::PropertyValue("value"))->IsNull()); - -// // Check state after (OLD view). -// ASSERT_EQ(edge->Properties(View::OLD).GetError(), memgraph::storage::Error::NONEXISTENT_OBJECT); -// ASSERT_EQ(edge->GetProperty(property, View::OLD).GetError(), memgraph::storage::Error::NONEXISTENT_OBJECT); - -// // Check state after (NEW view). -// ASSERT_EQ(edge->Properties(View::NEW)->size(), 1); -// ASSERT_EQ(*edge->GetProperty(property, View::NEW), memgraph::storage::PropertyValue("value")); - -// acc.Commit(GetNextHlc()); -// } } // namespace memgraph::storage::v3::tests