From 02ef954e5134d79e0831d28796bec50c96721de8 Mon Sep 17 00:00:00 2001 From: Kostas Kyrimis Date: Mon, 7 Nov 2022 18:28:35 +0200 Subject: [PATCH 01/30] Add mg-functions lib and integrated with SE and QE --- src/CMakeLists.txt | 1 + src/expr/CMakeLists.txt | 2 +- src/expr/interpret/eval.hpp | 3 +- src/functions/CMakeLists.txt | 2 + src/functions/awesome_memgraph_functions.hpp | 1463 +++++++++++++++++ src/query/v2/CMakeLists.txt | 3 +- src/query/v2/accessors.hpp | 19 +- src/query/v2/conversions.hpp | 4 + src/query/v2/frontend/ast/ast.lcp | 13 +- .../interpret/awesome_memgraph_functions.cpp | 1108 ------------- .../interpret/awesome_memgraph_functions.hpp | 55 - src/query/v2/plan/preprocess.cpp | 2 +- src/storage/v3/CMakeLists.txt | 2 +- src/storage/v3/bindings/ast/ast.lcp | 28 +- src/storage/v3/bindings/db_accessor.hpp | 2 +- src/storage/v3/conversions.hpp | 4 + src/storage/v3/edge_accessor.cpp | 7 +- src/storage/v3/edge_accessor.hpp | 7 +- src/storage/v3/expr.cpp | 9 +- src/storage/v3/shard.cpp | 4 +- src/storage/v3/shard_rsm.cpp | 10 +- src/storage/v3/vertex_accessor.cpp | 3 + src/storage/v3/vertex_accessor.hpp | 3 + tests/unit/storage_v3_edge.cpp | 846 +++++----- 24 files changed, 1970 insertions(+), 1630 deletions(-) create mode 100644 src/functions/CMakeLists.txt create mode 100644 src/functions/awesome_memgraph_functions.hpp delete mode 100644 src/query/v2/interpret/awesome_memgraph_functions.cpp delete mode 100644 src/query/v2/interpret/awesome_memgraph_functions.hpp 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 70127c023..139e37514 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 { @@ -479,7 +480,7 @@ class ExpressionEvaluator : public ExpressionVisitor { } TypedValue Visit(Function &function) override { - FunctionContext function_ctx{dba_, ctx_->memory, ctx_->timestamp, &ctx_->counters, view_}; + functions::FunctionContext 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..09720c862 --- /dev/null +++ b/src/functions/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(mg-functions INTERFACE) +target_include_directories(mg-functions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/functions/awesome_memgraph_functions.hpp b/src/functions/awesome_memgraph_functions.hpp new file mode 100644 index 000000000..ca415c69b --- /dev/null +++ b/src/functions/awesome_memgraph_functions.hpp @@ -0,0 +1,1463 @@ +// Copyright 2022 Memgraph Ltd. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source +// License, and you may not use this file except in compliance with the Business Source License. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +#pragma once + +#include +#include +#include +#include + +#include "storage/v3/result.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 +struct FunctionContext { + TAccessor *db_accessor; + utils::MemoryResource *memory; + int64_t timestamp; + std::unordered_map *counters; + storage::v3::View view; +}; + +// Tags for the NameToFunction() function template +struct StorageEngineTag {}; +struct QueryEngineTag {}; + +namespace impl { +struct SinkCallable { + void operator()() {} +}; +} // namespace impl + +/// 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 +std::function +NameToFunction(const std::string &function_name); + +namespace { +const char kStartsWith[] = "STARTSWITH"; +const char kEndsWith[] = "ENDSWITH"; +const char kContains[] = "CONTAINS"; +const char kId[] = "ID"; +} // namespace + +} // 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` 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, 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` check. +// * Two arguments were supplied, the 1st one passes `Or` 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, NonNegativeInteger, +// Optional>("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 +bool ArgIsType(const TypedValueT &arg) { + if constexpr (std::is_same_v) { + return arg.IsNull(); + } else if constexpr (std::is_same_v) { + return arg.IsBool(); + } else if constexpr (std::is_same_v) { + return arg.IsInt(); + } else if constexpr (std::is_same_v) { + return arg.IsInt() && arg.ValueInt() > 0; + } else if constexpr (std::is_same_v) { + return arg.IsInt() && arg.ValueInt() != 0; + } else if constexpr (std::is_same_v) { + return arg.IsInt() && arg.ValueInt() >= 0; + } else if constexpr (std::is_same_v) { + return arg.IsDouble(); + } else if constexpr (std::is_same_v) { + return arg.IsNumeric(); + } else if constexpr (std::is_same_v) { + return arg.IsList(); + } else if constexpr (std::is_same_v) { + return arg.IsString(); + } else if constexpr (std::is_same_v) { + return arg.IsMap(); + } else if constexpr (std::is_same_v) { + return arg.IsVertex(); + } else if constexpr (std::is_same_v) { + return arg.IsEdge(); + } else if constexpr (std::is_same_v) { + return arg.IsPath(); + } else if constexpr (std::is_same_v) { + return arg.IsDate(); + } else if constexpr (std::is_same_v) { + return arg.IsLocalTime(); + } else if constexpr (std::is_same_v) { + return arg.IsLocalDateTime(); + } else if constexpr (std::is_same_v) { + return arg.IsDuration(); + } else if constexpr (std::is_same_v) { + return true; + } else { + static_assert(std::is_same_v, "Unknown ArgType"); + } + return false; +} + +template +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) { + return "null"; + } else if constexpr (std::is_same_v) { + return "boolean"; + } else if constexpr (std::is_same_v) { + return "integer"; + } else if constexpr (std::is_same_v) { + return "positive integer"; + } else if constexpr (std::is_same_v) { + return "non-zero integer"; + } else if constexpr (std::is_same_v) { + return "non-negative integer"; + } else if constexpr (std::is_same_v) { + return "float"; + } else if constexpr (std::is_same_v) { + return "number"; + } else if constexpr (std::is_same_v) { + return "list"; + } else if constexpr (std::is_same_v) { + return "string"; + } else if constexpr (std::is_same_v) { + return "map"; + } else if constexpr (std::is_same_v) { + return "node"; + } else if constexpr (std::is_same_v) { + return "relationship"; + } else if constexpr (std::is_same_v) { + return "path"; + } else if constexpr (std::is_same_v) { + return "void"; + } else if constexpr (std::is_same_v) { + return "Date"; + } else if constexpr (std::is_same_v) { + return "LocalTime"; + } else if constexpr (std::is_same_v) { + return "LocalDateTime"; + } else if constexpr (std::is_same_v) { + return "Duration"; + } else { + static_assert(std::is_same_v, "Unknown ArgType"); + } + return ""; +} + +template +struct Or; + +template +struct Or { + template + static bool Check(const TypedValueT &arg) { + return ArgIsType(arg); + } + + static std::string TypeNames() { return ArgTypeName(); } +}; + +template +struct Or { + template + static bool Check(const TypedValueT &arg) { + if (ArgIsType(arg)) return true; + return Or::Check(arg); + } + + static std::string TypeNames() { + if constexpr (sizeof...(ArgTypes) > 1) { + return fmt::format("'{}', {}", ArgTypeName(), Or::TypeNames()); + } else { + return fmt::format("'{}' or '{}'", ArgTypeName(), Or::TypeNames()); + } + } +}; + +template +struct IsOrType { + static constexpr bool value = false; +}; + +template +struct IsOrType> { + static constexpr bool value = true; +}; + +template +struct Optional; + +template +struct Optional { + static constexpr size_t size = 1; + + template + 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::value) { + if (!ArgType::Check(arg)) { + throw FunctionRuntimeException("Optional '{}' argument at position {} must be either {}.", name, pos, + ArgType::TypeNames()); + } + } else { + if (!ArgIsType(arg)) + throw FunctionRuntimeException("Optional '{}' argument at position {} must be '{}'.", name, pos, + ArgTypeName()); + } + } +}; + +template +struct Optional { + static constexpr size_t size = 1 + sizeof...(ArgTypes); + + template + static void Check(const char *name, const TypedValueT *args, int64_t nargs, int64_t pos) { + if (nargs == 0) return; + Optional::Check(name, args, nargs, pos); + Optional::Check(name, args + 1, nargs - 1, pos + 1); + } +}; + +template +struct IsOptional { + static constexpr bool value = false; +}; + +template +struct IsOptional> { + static constexpr bool value = true; +}; + +template +constexpr size_t FTypeRequiredArgs() { + if constexpr (IsOptional::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(); + } +} + +template +constexpr size_t FTypeOptionalArgs() { + if constexpr (IsOptional::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(); + } +} + +template +void FType(const char *name, const TypedValueT *args, int64_t nargs, int64_t pos = 1) { + if constexpr (std::is_same_v) { + if (nargs != 0) { + throw FunctionRuntimeException("'{}' requires no arguments.", name); + } + return; + } + static constexpr int64_t required_args = FTypeRequiredArgs(); + static constexpr int64_t optional_args = FTypeOptionalArgs(); + 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::value) { + if (!ArgType::Check(arg)) { + throw FunctionRuntimeException("'{}' argument at position {} must be either {}.", name, pos, + ArgType::TypeNames()); + } + } else if constexpr (IsOptional::value) { + static_assert(sizeof...(ArgTypes) == 0, "Optional arguments must be last!"); + ArgType::Check(name, args, nargs, pos); + } else { + if (!ArgIsType(arg)) { + throw FunctionRuntimeException("'{}' argument at position {} must be '{}'", name, pos, ArgTypeName()); + } + } + if constexpr (sizeof...(ArgTypes) > 0) { + FType(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 +TypedValueT EndNode(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("endNode", args, nargs); + if (args[0].IsNull()) return TypedValueT(ctx.memory); + if constexpr (std::is_same_v) { + const auto to = args[0].ValueEdge().To(); + auto pk = to.primary_key; + auto maybe_vertex_accessor = ctx.db_accessor->FindVertex(pk, ctx.view); + if (!maybe_vertex_accessor) { + throw functions::FunctionRuntimeException("Trying to get properties from a deleted object."); + } + return TypedValueT(*maybe_vertex_accessor, ctx.memory); + } else { + return TypedValueT(args[0].ValueEdge().To(), ctx.memory); + } +} + +template +TypedValueT Head(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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 +TypedValueT Last(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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 +TypedValueT Properties(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("properties", args, nargs); + auto *dba = ctx.db_accessor; + auto get_properties = [&](const auto &record_accessor) { + typename TypedValueT::TMap properties(ctx.memory); + if constexpr (std::is_same_v) { + auto maybe_props = record_accessor.Properties(ctx.view); + if (maybe_props.HasError()) { + switch (maybe_props.GetError()) { + case storage::v3::Error::DELETED_OBJECT: + throw functions::FunctionRuntimeException("Trying to get properties from a deleted object."); + case storage::v3::Error::NONEXISTENT_OBJECT: + throw functions::FunctionRuntimeException("Trying to get properties from an object that doesn't exist."); + case storage::v3::Error::SERIALIZATION_ERROR: + case storage::v3::Error::VERTEX_HAS_EDGES: + case storage::v3::Error::PROPERTIES_DISABLED: + case storage::v3::Error::VERTEX_ALREADY_INSERTED: + throw functions::FunctionRuntimeException("Unexpected error when getting properties."); + } + } + for (const auto &property : *maybe_props) { + Conv conv; + properties.emplace(dba->PropertyToName(property.first), conv(property.second)); + } + } else { + Conv conv; + 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); + } else if (value.IsVertex()) { + return get_properties(value.ValueVertex()); + } else { + return get_properties(value.ValueEdge()); + } +} + +template +TypedValueT Size(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("size", args, nargs); + const auto &value = args[0]; + if (value.IsNull()) { + return TypedValueT(ctx.memory); + } else if (value.IsList()) { + return TypedValueT(static_cast(value.ValueList().size()), ctx.memory); + } else if (value.IsString()) { + return TypedValueT(static_cast(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 TypedValueT(static_cast(value.ValueMap().size()), ctx.memory); + } else { + return TypedValueT(static_cast(value.ValuePath().edges().size()), ctx.memory); + } +} + +template +TypedValueT StartNode(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("startNode", args, nargs); + if (args[0].IsNull()) return TypedValueT(ctx.memory); + if constexpr (std::is_same_v) { + const auto to = args[0].ValueEdge().From(); + auto pk = to.primary_key; + auto maybe_vertex_accessor = ctx.db_accessor->FindVertex(pk, ctx.view); + if (!maybe_vertex_accessor) { + throw functions::FunctionRuntimeException("Trying to get properties from a deleted object."); + } + return TypedValueT(*maybe_vertex_accessor, ctx.memory); + } else { + return TypedValueT(args[0].ValueEdge().From(), ctx.memory); + } +} + +namespace { + +size_t UnwrapDegreeResult(storage::v3::Result maybe_degree) { + if (maybe_degree.HasError()) { + switch (maybe_degree.GetError()) { + case storage::v3::Error::DELETED_OBJECT: + throw functions::FunctionRuntimeException("Trying to get degree of a deleted node."); + case storage::v3::Error::NONEXISTENT_OBJECT: + throw functions::FunctionRuntimeException("Trying to get degree of a node that doesn't exist."); + case storage::v3::Error::SERIALIZATION_ERROR: + case storage::v3::Error::VERTEX_HAS_EDGES: + case storage::v3::Error::PROPERTIES_DISABLED: + case storage::v3::Error::VERTEX_ALREADY_INSERTED: + throw functions::FunctionRuntimeException("Unexpected error when getting node degree."); + } + } + return *maybe_degree; +} + +} // namespace + +template +TypedValueT Degree(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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) { + 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(out_degree + in_degree), ctx.memory); +} + +template +TypedValueT InDegree(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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) { + in_degree = UnwrapDegreeResult(vertex.InDegree(ctx.view)); + } else { + in_degree = vertex.InDegree(); + } + return TypedValueT(static_cast(in_degree), ctx.memory); +} + +template +TypedValueT OutDegree(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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) { + out_degree = UnwrapDegreeResult(vertex.OutDegree(ctx.view)); + } else { + out_degree = vertex.OutDegree(); + } + return TypedValueT(static_cast(out_degree), ctx.memory); +} + +template +TypedValueT ToBoolean(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("toBoolean", args, nargs); + const auto &value = args[0]; + if (value.IsNull()) { + return TypedValueT(ctx.memory); + } else if (value.IsBool()) { + return TypedValueT(value.ValueBool(), ctx.memory); + } else if (value.IsInt()) { + return TypedValueT(value.ValueInt() != 0L, ctx.memory); + } else { + 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 +TypedValueT ToFloat(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("toFloat", args, nargs); + const auto &value = args[0]; + if (value.IsNull()) { + return TypedValueT(ctx.memory); + } else if (value.IsInt()) { + return TypedValueT(static_cast(value.ValueInt()), ctx.memory); + } else if (value.IsDouble()) { + return TypedValueT(value, ctx.memory); + } else { + try { + return TypedValueT(utils::ParseDouble(utils::Trim(value.ValueString())), ctx.memory); + } catch (const utils::BasicException &) { + return TypedValueT(ctx.memory); + } + } +} + +template +TypedValueT ToInteger(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("toInteger", args, nargs); + const auto &value = args[0]; + if (value.IsNull()) { + return TypedValueT(ctx.memory); + } else if (value.IsBool()) { + return TypedValueT(value.ValueBool() ? 1L : 0L, ctx.memory); + } else if (value.IsInt()) { + return TypedValueT(value, ctx.memory); + } else if (value.IsDouble()) { + return TypedValueT(static_cast(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 TypedValueT(static_cast(utils::ParseDouble(utils::Trim(value.ValueString()))), ctx.memory); + } catch (const utils::BasicException &) { + return TypedValueT(ctx.memory); + } + } +} + +template +TypedValueT Type(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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 +TypedValueT ValueType(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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); + } +} + +// TODO: How is Keys different from Properties function? +template +TypedValueT Keys(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("keys", args, nargs); + auto *dba = ctx.db_accessor; + auto get_keys = [&](const auto &record_accessor) { + typename TypedValueT::TVector keys(ctx.memory); + if constexpr (std::same_as) { + auto maybe_props = record_accessor.Properties(ctx.view); + if (maybe_props.HasError()) { + switch (maybe_props.GetError()) { + case storage::v3::Error::DELETED_OBJECT: + throw functions::FunctionRuntimeException("Trying to get keys from a deleted object."); + case storage::v3::Error::NONEXISTENT_OBJECT: + throw functions::FunctionRuntimeException("Trying to get keys from an object that doesn't exist."); + case storage::v3::Error::SERIALIZATION_ERROR: + case storage::v3::Error::VERTEX_HAS_EDGES: + case storage::v3::Error::PROPERTIES_DISABLED: + case storage::v3::Error::VERTEX_ALREADY_INSERTED: + throw functions::FunctionRuntimeException("Unexpected error when getting keys."); + } + } + for (const auto &property : *maybe_props) { + keys.emplace_back(dba->PropertyToName(property.first)); + } + } else { + for (const auto &property : record_accessor.Properties()) { + keys.emplace_back(dba->PropertyToName(property.first)); + } + } + return TypedValueT(std::move(keys)); + }; + + const auto &value = args[0]; + if (value.IsNull()) { + return TypedValueT(ctx.memory); + } else if (value.IsVertex()) { + return get_keys(value.ValueVertex()); + } else { + return get_keys(value.ValueEdge()); + } +} + +template +TypedValueT Labels(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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) { + auto maybe_labels = args[0].ValueVertex().Labels(ctx.view); + if (maybe_labels.HasError()) { + switch (maybe_labels.GetError()) { + case storage::v3::Error::DELETED_OBJECT: + throw functions::FunctionRuntimeException("Trying to get labels from a deleted node."); + case storage::v3::Error::NONEXISTENT_OBJECT: + throw functions::FunctionRuntimeException("Trying to get labels from a node that doesn't exist."); + case storage::v3::Error::SERIALIZATION_ERROR: + case storage::v3::Error::VERTEX_HAS_EDGES: + case storage::v3::Error::PROPERTIES_DISABLED: + case storage::v3::Error::VERTEX_ALREADY_INSERTED: + 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 +TypedValueT Nodes(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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 +TypedValueT Relationships(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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 +TypedValueT Range(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType, Or, Optional>>("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 +TypedValueT Tail(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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 +TypedValueT UniformSample(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType, Or>("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 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 +TypedValueT Abs(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("abs", args, nargs); + const auto &value = args[0]; + if (value.IsNull()) { + return TypedValueT(ctx.memory); + } else if (value.IsInt()) { + return TypedValueT(std::abs(value.ValueInt()), ctx.memory); + } else { + return TypedValueT(std::abs(value.ValueDouble()), ctx.memory); + } +} + +#define WRAP_CMATH_FLOAT_FUNCTION(name, lowercased_name) \ + template \ + TypedValueT name(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { \ + FType>(#lowercased_name, args, nargs); \ + const auto &value = args[0]; \ + if (value.IsNull()) { \ + return TypedValueT(ctx.memory); \ + } else if (value.IsInt()) { \ + return TypedValueT(lowercased_name(value.ValueInt()), ctx.memory); \ + } else { \ + 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 +TypedValueT Atan2(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType, Or>("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(); + } else { + return t.ValueDouble(); + } + }; + double y = to_double(args[0]); + double x = to_double(args[1]); + return TypedValueT(atan2(y, x), ctx.memory); +} + +template +TypedValueT Sign(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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); + } else if (value.IsInt()) { + return sign(value.ValueInt()); + } else { + return sign(value.ValueDouble()); + } +} + +template +TypedValueT E(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType("e", args, nargs); + return TypedValueT(M_E, ctx.memory); +} + +template +TypedValueT Pi(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType("pi", args, nargs); + return TypedValueT(M_PI, ctx.memory); +} + +template +TypedValueT Rand(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType("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 +TypedValueT StringMatchOperator(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType, Or>(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 +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 +inline auto StartsWith = StringMatchOperator, TypedValueT, FunctionContextT>; + +// Check if s1 ends with s2. +template +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 +inline auto EndsWith = StringMatchOperator, TypedValueT, FunctionContextT>; + +// Check if s1 contains s2. +template +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 +inline auto Contains = StringMatchOperator, TypedValueT, FunctionContextT>; + +template +TypedValueT Assert(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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 +TypedValueT Counter(const TypedValueT *args, int64_t nargs, const FunctionContextT &context) { + FType>("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 +TypedValueT Id(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("id", args, nargs); + const auto &arg = args[0]; + if (arg.IsNull()) { + return TypedValueT(ctx.memory); + } else if (arg.IsVertex()) { + return TypedValueT(static_cast(arg.ValueVertex().CypherId()), ctx.memory); + } else { + return TypedValueT(static_cast(arg.ValueEdge().CypherId()), ctx.memory); + } +} + +template +TypedValueT ToString(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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 +TypedValueT Timestamp(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>>("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 +TypedValueT Left(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType, Or>("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 +TypedValueT Right(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType, Or>("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 +TypedValueT CallStringFunction( + const TypedValueT *args, int64_t nargs, utils::MemoryResource *memory, const char *name, + std::function fun) { + FType>(name, args, nargs); + if (args[0].IsNull()) return TypedValueT(memory); + return TypedValueT(fun(args[0].ValueString()), memory); +} + +template +TypedValueT LTrim(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + return CallStringFunction(args, nargs, ctx.memory, "lTrim", [&](const auto &str) { + return typename TypedValueT::TString(utils::LTrim(str), ctx.memory); + }); +} + +template +TypedValueT RTrim(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + return CallStringFunction(args, nargs, ctx.memory, "rTrim", [&](const auto &str) { + return typename TypedValueT::TString(utils::RTrim(str), ctx.memory); + }); +} + +template +TypedValueT Trim(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + return CallStringFunction(args, nargs, ctx.memory, "trim", [&](const auto &str) { + return typename TypedValueT::TString(utils::Trim(str), ctx.memory); + }); +} + +template +TypedValueT Reverse(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + return CallStringFunction( + args, nargs, ctx.memory, "reverse", [&](const auto &str) { return utils::Reversed(str, ctx.memory); }); +} + +template +TypedValueT ToLower(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + return CallStringFunction(args, nargs, ctx.memory, "toLower", [&](const auto &str) { + typename TypedValueT::TString res(ctx.memory); + utils::ToLowerCase(&res, str); + return res; + }); +} + +template +TypedValueT ToUpper(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + return CallStringFunction(args, nargs, ctx.memory, "toUpper", [&](const auto &str) { + typename TypedValueT::TString res(ctx.memory); + utils::ToUpperCase(&res, str); + return res; + }); +} + +template +TypedValueT Replace(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType, Or, Or>("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 +TypedValueT Split(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType, Or>("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 +TypedValueT Substring(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType, NonNegativeInteger, Optional>("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 +TypedValueT ToByteString(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType("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(byte)); + } + return TypedValueT(std::move(bytes)); +} + +template +TypedValueT FromByteString(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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(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('0') + val : static_cast('a') + val - 10U; + return utils::MemcpyCast(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 +concept IsNumberOrInteger = utils::SameAsAnyOf; + +template +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 && value.IsDouble()) { + *it->second = value.ValueDouble(); + } else { + std::string_view error = std::is_same_v ? "an integer." : "a numeric value."; + throw FunctionRuntimeException("Invalid value for key '{}'. Expected {}", key, error); + } + } else { + throw FunctionRuntimeException("Unknown key '{}'.", key); + } + } +} + +template +TypedValueT Date(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>>("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(parameter_mappings, args[0].ValueMap()); + return TypedValueT(utils::Date(date_parameters), ctx.memory); +} + +template +TypedValueT LocalTime(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>>("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(parameter_mappings, args[0].ValueMap()); + return TypedValueT(utils::LocalTime(local_time_parameters), ctx.memory); +} + +template +TypedValueT LocalDateTime(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>>("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(parameter_mappings, args[0].ValueMap()); + return TypedValueT(utils::LocalDateTime(date_parameters, local_time_parameters), ctx.memory); +} + +template +TypedValueT Duration(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { + FType>("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(parameter_mappings, args[0].ValueMap()); + return TypedValueT(utils::Duration(duration_parameters), ctx.memory); +} + +} // namespace memgraph::functions::impl + +namespace memgraph::functions { + +template +std::function +NameToFunction(const std::string &function_name) { + // Scalar functions + if (function_name == "DEGREE") return functions::impl::Degree; + if (function_name == "INDEGREE") return functions::impl::InDegree; + if (function_name == "OUTDEGREE") return functions::impl::OutDegree; + if (function_name == "ENDNODE") return functions::impl::EndNode; + if (function_name == "HEAD") return functions::impl::Head; + if (function_name == kId) return functions::impl::Id; + if (function_name == "LAST") return functions::impl::Last; + if (function_name == "PROPERTIES") return functions::impl::Properties; + if (function_name == "SIZE") return functions::impl::Size; + if (function_name == "STARTNODE") return functions::impl::StartNode; + if (function_name == "TIMESTAMP") return functions::impl::Timestamp; + if (function_name == "TOBOOLEAN") return functions::impl::ToBoolean; + if (function_name == "TOFLOAT") return functions::impl::ToFloat; + if (function_name == "TOINTEGER") return functions::impl::ToInteger; + if (function_name == "TYPE") return functions::impl::Type; + if (function_name == "VALUETYPE") return functions::impl::ValueType; + + // List functions + if (function_name == "KEYS") return functions::impl::Keys; + if (function_name == "LABELS") return functions::impl::Labels; + if (function_name == "NODES") return functions::impl::Nodes; + if (function_name == "RANGE") return functions::impl::Range; + if (function_name == "RELATIONSHIPS") return functions::impl::Relationships; + if (function_name == "TAIL") return functions::impl::Tail; + if (function_name == "UNIFORMSAMPLE") return functions::impl::UniformSample; + + // Mathematical functions - numeric + if (function_name == "ABS") return functions::impl::Abs; + if (function_name == "CEIL") return functions::impl::Ceil; + if (function_name == "FLOOR") return functions::impl::Floor; + if (function_name == "RAND") return functions::impl::Rand; + if (function_name == "ROUND") return functions::impl::Round; + if (function_name == "SIGN") return functions::impl::Sign; + + // Mathematical functions - logarithmic + if (function_name == "E") return functions::impl::E; + if (function_name == "EXP") return functions::impl::Exp; + if (function_name == "LOG") return functions::impl::Log; + if (function_name == "LOG10") return functions::impl::Log10; + if (function_name == "SQRT") return functions::impl::Sqrt; + + // Mathematical functions - trigonometric + if (function_name == "ACOS") return functions::impl::Acos; + if (function_name == "ASIN") return functions::impl::Asin; + if (function_name == "ATAN") return functions::impl::Atan; + if (function_name == "ATAN2") return functions::impl::Atan2; + if (function_name == "COS") return functions::impl::Cos; + if (function_name == "PI") return functions::impl::Pi; + if (function_name == "SIN") return functions::impl::Sin; + if (function_name == "TAN") return functions::impl::Tan; + + // String functions + if (function_name == kContains) return functions::impl::Contains; + if (function_name == kEndsWith) return functions::impl::EndsWith; + if (function_name == "LEFT") return functions::impl::Left; + if (function_name == "LTRIM") return functions::impl::LTrim; + if (function_name == "REPLACE") return functions::impl::Replace; + if (function_name == "REVERSE") return functions::impl::Reverse; + if (function_name == "RIGHT") return functions::impl::Right; + if (function_name == "RTRIM") return functions::impl::RTrim; + if (function_name == "SPLIT") return functions::impl::Split; + if (function_name == kStartsWith) return functions::impl::StartsWith; + if (function_name == "SUBSTRING") return functions::impl::Substring; + if (function_name == "TOLOWER") return functions::impl::ToLower; + if (function_name == "TOSTRING") return functions::impl::ToString; + if (function_name == "TOUPPER") return functions::impl::ToUpper; + if (function_name == "TRIM") return functions::impl::Trim; + + // Memgraph specific functions + if (function_name == "ASSERT") return functions::impl::Assert; + if (function_name == "COUNTER") return functions::impl::Counter; + if (function_name == "TOBYTESTRING") return functions::impl::ToByteString; + if (function_name == "FROMBYTESTRING") return functions::impl::FromByteString; + + // Functions for temporal types + if (function_name == "DATE") return functions::impl::Date; + if (function_name == "LOCALTIME") return functions::impl::LocalTime; + if (function_name == "LOCALDATETIME") return functions::impl::LocalDateTime; + if (function_name == "DURATION") return functions::impl::Duration; + + 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.hpp b/src/query/v2/accessors.hpp index 8e10c865d..73cc24ba2 100644 --- a/src/query/v2/accessors.hpp +++ b/src/query/v2/accessors.hpp @@ -136,9 +136,11 @@ class VertexAccessor final { // return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges)); // } - // storage::Result InDegree(storage::View view) const { return impl_.InDegree(view); } - // - // storage::Result OutDegree(storage::View view) const { return impl_.OutDegree(view); } + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) + [[nodiscard]] size_t InDegree() const { return 0; } + + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) + [[nodiscard]] size_t OutDegree() const { return 0; } // friend bool operator==(const VertexAccessor &lhs, const VertexAccessor &rhs) { @@ -153,10 +155,6 @@ class VertexAccessor final { const msgs::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 +195,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 vertices_; + std::vector edges_; utils::MemoryResource *mem = utils::NewDeleteResource(); }; } // namespace memgraph::query::v2::accessors diff --git a/src/query/v2/conversions.hpp b/src/query/v2/conversions.hpp index 10299c919..4556e46bc 100644 --- a/src/query/v2/conversions.hpp +++ b/src/query/v2/conversions.hpp @@ -56,6 +56,10 @@ inline TypedValue ValueToTypedValue(const msgs::Value &value, msgs::ShardRequest throw std::runtime_error("Incorrect type in conversion"); } +inline const auto ValueToTypedValueFunctor = [](const msgs::Value &value, msgs::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..209bdfd50 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" + const functions::FunctionContext &)>" :scope :public :dont-save t :clone :copy :slk-load (lambda (member) #>cpp - self->${member} = query::v2::NameToFunction(self->function_name_); + self->${member} = functions::NameToFunction, + functions::QueryEngineTag, decltype(ValueToTypedValueFunctor)>(self->function_name_); cpp<#))) (:public #>cpp @@ -865,7 +869,8 @@ cpp<# const std::vector &arguments) : arguments_(arguments), function_name_(function_name), - function_(NameToFunction(function_name_)) { + function_(functions::NameToFunction, + 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 -#include -#include -#include -#include -#include -#include -#include - -#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` 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, 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` check. -// * Two arguments were supplied, the 1st one passes `Or` 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, NonNegativeInteger, -// Optional>("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 -bool ArgIsType(const TypedValue &arg) { - if constexpr (std::is_same_v) { - return arg.IsNull(); - } else if constexpr (std::is_same_v) { - return arg.IsBool(); - } else if constexpr (std::is_same_v) { - return arg.IsInt(); - } else if constexpr (std::is_same_v) { - return arg.IsInt() && arg.ValueInt() > 0; - } else if constexpr (std::is_same_v) { - return arg.IsInt() && arg.ValueInt() != 0; - } else if constexpr (std::is_same_v) { - return arg.IsInt() && arg.ValueInt() >= 0; - } else if constexpr (std::is_same_v) { - return arg.IsDouble(); - } else if constexpr (std::is_same_v) { - return arg.IsNumeric(); - } else if constexpr (std::is_same_v) { - return arg.IsList(); - } else if constexpr (std::is_same_v) { - return arg.IsString(); - } else if constexpr (std::is_same_v) { - return arg.IsMap(); - } else if constexpr (std::is_same_v) { - return arg.IsVertex(); - } else if constexpr (std::is_same_v) { - return arg.IsEdge(); - } else if constexpr (std::is_same_v) { - return arg.IsPath(); - } else if constexpr (std::is_same_v) { - return arg.IsDate(); - } else if constexpr (std::is_same_v) { - return arg.IsLocalTime(); - } else if constexpr (std::is_same_v) { - return arg.IsLocalDateTime(); - } else if constexpr (std::is_same_v) { - return arg.IsDuration(); - } else if constexpr (std::is_same_v) { - return true; - } else { - static_assert(std::is_same_v, "Unknown ArgType"); - } - return false; -} - -template -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) { - return "null"; - } else if constexpr (std::is_same_v) { - return "boolean"; - } else if constexpr (std::is_same_v) { - return "integer"; - } else if constexpr (std::is_same_v) { - return "positive integer"; - } else if constexpr (std::is_same_v) { - return "non-zero integer"; - } else if constexpr (std::is_same_v) { - return "non-negative integer"; - } else if constexpr (std::is_same_v) { - return "float"; - } else if constexpr (std::is_same_v) { - return "number"; - } else if constexpr (std::is_same_v) { - return "list"; - } else if constexpr (std::is_same_v) { - return "string"; - } else if constexpr (std::is_same_v) { - return "map"; - } else if constexpr (std::is_same_v) { - return "node"; - } else if constexpr (std::is_same_v) { - return "relationship"; - } else if constexpr (std::is_same_v) { - return "path"; - } else if constexpr (std::is_same_v) { - return "void"; - } else if constexpr (std::is_same_v) { - return "Date"; - } else if constexpr (std::is_same_v) { - return "LocalTime"; - } else if constexpr (std::is_same_v) { - return "LocalDateTime"; - } else if constexpr (std::is_same_v) { - return "Duration"; - } else { - static_assert(std::is_same_v, "Unknown ArgType"); - } - return ""; -} - -template -struct Or; - -template -struct Or { - static bool Check(const TypedValue &arg) { return ArgIsType(arg); } - - static std::string TypeNames() { return ArgTypeName(); } -}; - -template -struct Or { - static bool Check(const TypedValue &arg) { - if (ArgIsType(arg)) return true; - return Or::Check(arg); - } - - static std::string TypeNames() { - if constexpr (sizeof...(ArgTypes) > 1) { - return fmt::format("'{}', {}", ArgTypeName(), Or::TypeNames()); - } else { - return fmt::format("'{}' or '{}'", ArgTypeName(), Or::TypeNames()); - } - } -}; - -template -struct IsOrType { - static constexpr bool value = false; -}; - -template -struct IsOrType> { - static constexpr bool value = true; -}; - -template -struct Optional; - -template -struct Optional { - 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::value) { - if (!ArgType::Check(arg)) { - throw QueryRuntimeException("Optional '{}' argument at position {} must be either {}.", name, pos, - ArgType::TypeNames()); - } - } else { - if (!ArgIsType(arg)) - throw QueryRuntimeException("Optional '{}' argument at position {} must be '{}'.", name, pos, - ArgTypeName()); - } - } -}; - -template -struct Optional { - 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::Check(name, args, nargs, pos); - Optional::Check(name, args + 1, nargs - 1, pos + 1); - } -}; - -template -struct IsOptional { - static constexpr bool value = false; -}; - -template -struct IsOptional> { - static constexpr bool value = true; -}; - -template -constexpr size_t FTypeRequiredArgs() { - if constexpr (IsOptional::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(); - } -} - -template -constexpr size_t FTypeOptionalArgs() { - if constexpr (IsOptional::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(); - } -} - -template -void FType(const char *name, const TypedValue *args, int64_t nargs, int64_t pos = 1) { - if constexpr (std::is_same_v) { - if (nargs != 0) { - throw QueryRuntimeException("'{}' requires no arguments.", name); - } - return; - } - static constexpr int64_t required_args = FTypeRequiredArgs(); - static constexpr int64_t optional_args = FTypeOptionalArgs(); - 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::value) { - if (!ArgType::Check(arg)) { - throw QueryRuntimeException("'{}' argument at position {} must be either {}.", name, pos, ArgType::TypeNames()); - } - } else if constexpr (IsOptional::value) { - static_assert(sizeof...(ArgTypes) == 0, "Optional arguments must be last!"); - ArgType::Check(name, args, nargs, pos); - } else { - if (!ArgIsType(arg)) { - throw QueryRuntimeException("'{}' argument at position {} must be '{}'", name, pos, ArgTypeName()); - } - } - if constexpr (sizeof...(ArgTypes) > 0) { - FType(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>("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>("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>("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>("size", args, nargs); - const auto &value = args[0]; - if (value.IsNull()) { - return TypedValue(ctx.memory); - } else if (value.IsList()) { - return TypedValue(static_cast(value.ValueList().size()), ctx.memory); - } else if (value.IsString()) { - return TypedValue(static_cast(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(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>("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>("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>("toFloat", args, nargs); - const auto &value = args[0]; - if (value.IsNull()) { - return TypedValue(ctx.memory); - } else if (value.IsInt()) { - return TypedValue(static_cast(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>("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(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(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>("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>("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, Optional>>("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>("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>("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 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>("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>(#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>("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>("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("e", args, nargs); - return TypedValue(M_E, ctx.memory); -} - -TypedValue Pi(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType("pi", args, nargs); - return TypedValue(M_PI, ctx.memory); -} - -TypedValue Rand(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType("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 -TypedValue StringMatchOperator(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType, Or>(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; - -// 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; - -// 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; - -TypedValue Assert(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType>("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>("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>("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>("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>>("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>("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>("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 fun) { - FType>(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, Or>("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>("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, NonNegativeInteger, Optional>("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("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(byte)); - } - return TypedValue(std::move(bytes)); -} - -TypedValue FromByteString(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { - FType>("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(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('0') + val : static_cast('a') + val - 10U; - return utils::MemcpyCast(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 -concept IsNumberOrInteger = utils::SameAsAnyOf; - -template -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 && value.IsDouble()) { - *it->second = value.ValueDouble(); - } else { - std::string_view error = std::is_same_v ? "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>>("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(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>>("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(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>>("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(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>("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(parameter_mappings, args[0].ValueMap()); - return TypedValue(utils::Duration(duration_parameters), ctx.memory); -} - -} // namespace - -std::function 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/interpret/awesome_memgraph_functions.hpp b/src/query/v2/interpret/awesome_memgraph_functions.hpp deleted file mode 100644 index 134f05d7d..000000000 --- a/src/query/v2/interpret/awesome_memgraph_functions.hpp +++ /dev/null @@ -1,55 +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. - -#pragma once - -#include -#include -#include - -#include "query/v2/bindings/typed_value.hpp" -#include "query/v2/db_accessor.hpp" -#include "storage/v3/view.hpp" -#include "utils/memory.hpp" - -namespace memgraph::msgs { -class ShardRequestManagerInterface; -} // namespace memgraph::msgs - -namespace memgraph::query::v2 { - -namespace { -const char kStartsWith[] = "STARTSWITH"; -const char kEndsWith[] = "ENDSWITH"; -const char kContains[] = "CONTAINS"; -const char kId[] = "ID"; -} // namespace - -struct FunctionContext { - // TODO(kostasrim) consider optional here. ShardRequestManager does not exist on the storage. - // DbAccessor *db_accessor; - msgs::ShardRequestManagerInterface *manager; - utils::MemoryResource *memory; - int64_t timestamp; - std::unordered_map *counters; - storage::v3::View view; -}; - -/// 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. -std::function -NameToFunction(const std::string &function_name); - -} // 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(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(id_fun->arguments_.front()); if (!ident) return false; diff --git a/src/storage/v3/CMakeLists.txt b/src/storage/v3/CMakeLists.txt index 0f359c702..dccbe2356 100644 --- a/src/storage/v3/CMakeLists.txt +++ b/src/storage/v3/CMakeLists.txt @@ -33,4 +33,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 *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" + const functions::FunctionContext &)>" :scope :public :dont-save t :clone :copy :slk-load (lambda (member) #>cpp + self->${member} = functions::NameToFunction, + functions::StorageEngineTag, Conv>(self->function_name_); cpp<#))) (:public #>cpp Function() = default; + using Conv = decltype(PropertyToTypedValueFunctor); + class SemanticException : public memgraph::utils::BasicException { + using utils::BasicException::BasicException; + }; + DEFVISITABLE(ExpressionVisitor); DEFVISITABLE(ExpressionVisitor); @@ -872,7 +874,13 @@ cpp<# Function(const std::string &function_name, const std::vector &arguments) : arguments_(arguments), - function_name_(function_name) { + function_name_(function_name), + function_(functions::NameToFunction, + 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 186a1b5c5..2a4f91fe0 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::Result> 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 +inline const auto PropertyToTypedValueFunctor = + [](const PropertyValue &value) { return PropertyToTypedValue(value); }; + template 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 ed4abd1fe..d60cdeebc 100644 --- a/src/storage/v3/edge_accessor.cpp +++ b/src/storage/v3/edge_accessor.cpp @@ -50,9 +50,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_; } Result EdgeAccessor::SetProperty(PropertyId property, const PropertyValue &value) { utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; @@ -179,4 +179,7 @@ Result> EdgeAccessor::Properties(View view) return std::move(properties); } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) +size_t EdgeAccessor::CypherId() const { return 10; } + } // namespace memgraph::storage::v3 diff --git a/src/storage/v3/edge_accessor.hpp b/src/storage/v3/edge_accessor.hpp index 546a09200..0c9235b14 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 1d6739433..074e9f15f 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/shard.cpp b/src/storage/v3/shard.cpp index 0e596172d..49c692134 100644 --- a/src/storage/v3/shard.cpp +++ b/src/storage/v3/shard.cpp @@ -441,7 +441,7 @@ Result>>> Shar 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() == Error::SERIALIZATION_ERROR, "Invalid database state!"); return ret.GetError(); @@ -454,7 +454,7 @@ Result>>> Shar 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() == Error::SERIALIZATION_ERROR, "Invalid database state!"); return ret.GetError(); diff --git a/src/storage/v3/shard_rsm.cpp b/src/storage/v3/shard_rsm.cpp index f288e5e27..781ab67ed 100644 --- a/src/storage/v3/shard_rsm.cpp +++ b/src/storage/v3/shard_rsm.cpp @@ -376,7 +376,7 @@ EdgeUniqunessFunction InitializeEdgeUniqunessFunction(bool only_unique_neighbor_ case msgs::EdgeDirection::OUT: { is_edge_unique = [](std::set &other_vertex_set, const storage::v3::EdgeAccessor &edge_acc) { - auto [it, insertion_happened] = other_vertex_set.insert(&edge_acc.ToVertex()); + auto [it, insertion_happened] = other_vertex_set.insert(&edge_acc.To()); return insertion_happened; }; break; @@ -384,7 +384,7 @@ EdgeUniqunessFunction InitializeEdgeUniqunessFunction(bool only_unique_neighbor_ case msgs::EdgeDirection::IN: { is_edge_unique = [](std::set &other_vertex_set, const storage::v3::EdgeAccessor &edge_acc) { - auto [it, insertion_happened] = other_vertex_set.insert(&edge_acc.FromVertex()); + auto [it, insertion_happened] = other_vertex_set.insert(&edge_acc.From()); return insertion_happened; }; break; @@ -429,8 +429,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 { @@ -454,7 +454,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/vertex_accessor.cpp b/src/storage/v3/vertex_accessor.cpp index 543caa88a..10c39dba6 100644 --- a/src/storage/v3/vertex_accessor.cpp +++ b/src/storage/v3/vertex_accessor.cpp @@ -733,4 +733,7 @@ Result VertexAccessor::OutDegree(View view) const { return degree; } +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) +size_t VertexAccessor::CypherId() const { return 10; } + } // namespace memgraph::storage::v3 diff --git a/src/storage/v3/vertex_accessor.hpp b/src/storage/v3/vertex_accessor.hpp index 682a04e39..d708f723e 100644 --- a/src/storage/v3/vertex_accessor.hpp +++ b/src/storage/v3/vertex_accessor.hpp @@ -118,6 +118,9 @@ class VertexAccessor final { } bool operator!=(const VertexAccessor &other) const noexcept { return !(*this == other); } + // Dummy function + size_t CypherId() const; + private: /// Add a label and return `true` if insertion took place. /// `false` is returned if the label already existed. diff --git a/tests/unit/storage_v3_edge.cpp b/tests/unit/storage_v3_edge.cpp index 21c4214ce..5c0759e98 100644 --- a/tests/unit/storage_v3_edge.cpp +++ b/tests/unit/storage_v3_edge.cpp @@ -98,8 +98,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); @@ -117,8 +117,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); @@ -131,8 +131,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); @@ -188,8 +188,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); @@ -200,8 +200,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); @@ -212,8 +212,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); @@ -224,8 +224,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); @@ -309,8 +309,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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); +// ASSERT_EQ(edge.From(), *vertex_from); +// ASSERT_EQ(edge.To(), *vertex_to); // // Check edges without filters // ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); @@ -327,8 +327,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); @@ -340,8 +340,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -386,8 +386,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_from->OutEdges(View::NEW); @@ -397,8 +397,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_to->InEdges(View::OLD); @@ -408,8 +408,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_to->InEdges(View::NEW); @@ -419,8 +419,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -476,8 +476,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // ASSERT_TRUE(res.HasValue()); // auto edge = res.GetValue(); // ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), *vertex); -// ASSERT_EQ(edge.ToVertex(), *vertex); +// ASSERT_EQ(edge.From(), *vertex); +// ASSERT_EQ(edge.To(), *vertex); // // Check edges without filters // ASSERT_EQ(vertex->InEdges(View::OLD)->size(), 0); @@ -490,8 +490,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // ASSERT_EQ(vertex->OutEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex->OutDegree(View::OLD), 0); @@ -503,8 +503,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // auto other_et = acc.NameToEdgeType("other"); @@ -539,8 +539,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // { // auto ret = vertex->InEdges(View::NEW); @@ -550,8 +550,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // { // auto ret = vertex->OutEdges(View::OLD); @@ -561,8 +561,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // { // auto ret = vertex->OutEdges(View::NEW); @@ -572,8 +572,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // auto other_et = acc.NameToEdgeType("other"); @@ -626,8 +626,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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); +// ASSERT_EQ(edge.From(), *vertex_from); +// ASSERT_EQ(edge.To(), *vertex_to); // // Check edges without filters // ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); @@ -644,8 +644,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); @@ -657,8 +657,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -723,8 +723,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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); +// ASSERT_EQ(edge.From(), *vertex_from); +// ASSERT_EQ(edge.To(), *vertex_to); // // Check edges without filters // ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); @@ -741,8 +741,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); @@ -754,8 +754,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -800,8 +800,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_from->OutEdges(View::NEW); @@ -811,8 +811,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_to->InEdges(View::OLD); @@ -822,8 +822,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_to->InEdges(View::NEW); @@ -833,8 +833,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -901,8 +901,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); @@ -920,8 +920,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); @@ -934,8 +934,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); @@ -1013,8 +1013,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); @@ -1032,8 +1032,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); @@ -1046,8 +1046,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); @@ -1105,8 +1105,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); @@ -1117,8 +1117,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); @@ -1129,8 +1129,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); @@ -1141,8 +1141,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); @@ -1199,8 +1199,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { // ASSERT_TRUE(res.HasValue()); // auto edge = res.GetValue(); // ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), *vertex); -// ASSERT_EQ(edge.ToVertex(), *vertex); +// ASSERT_EQ(edge.From(), *vertex); +// ASSERT_EQ(edge.To(), *vertex); // // Check edges without filters // ASSERT_EQ(vertex->InEdges(View::OLD)->size(), 0); @@ -1213,8 +1213,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // ASSERT_EQ(vertex->OutEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex->OutDegree(View::OLD), 0); @@ -1226,8 +1226,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { // 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); +// ASSERT_EQ(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // auto other_et = acc.NameToEdgeType("other"); @@ -1276,8 +1276,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { // ASSERT_TRUE(res.HasValue()); // auto edge = res.GetValue(); // ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), *vertex); -// ASSERT_EQ(edge.ToVertex(), *vertex); +// ASSERT_EQ(edge.From(), *vertex); +// ASSERT_EQ(edge.To(), *vertex); // // Check edges without filters // ASSERT_EQ(vertex->InEdges(View::OLD)->size(), 0); @@ -1290,8 +1290,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // ASSERT_EQ(vertex->OutEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex->OutDegree(View::OLD), 0); @@ -1303,8 +1303,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { // 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); +// ASSERT_EQ(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // auto other_et = acc.NameToEdgeType("other"); @@ -1339,8 +1339,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // { // auto ret = vertex->InEdges(View::NEW); @@ -1350,8 +1350,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // { // auto ret = vertex->OutEdges(View::OLD); @@ -1361,8 +1361,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // { // auto ret = vertex->OutEdges(View::NEW); @@ -1372,8 +1372,8 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) { // 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); +// ASSERT_EQ(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // auto other_et = acc.NameToEdgeType("other"); @@ -1433,8 +1433,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); @@ -1452,8 +1452,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 @@ -1491,8 +1491,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); @@ -1503,8 +1503,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 @@ -1531,7 +1531,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()); @@ -1549,8 +1549,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); @@ -1620,8 +1620,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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); +// ASSERT_EQ(edge.From(), *vertex_from); +// ASSERT_EQ(edge.To(), *vertex_to); // // Check edges without filters // ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); @@ -1638,8 +1638,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); @@ -1651,8 +1651,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -1697,8 +1697,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_from->OutEdges(View::NEW); @@ -1708,8 +1708,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_to->InEdges(View::OLD); @@ -1719,8 +1719,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_to->InEdges(View::NEW); @@ -1730,8 +1730,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -1790,8 +1790,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_from->OutEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 0); @@ -1803,8 +1803,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); @@ -1883,8 +1883,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // ASSERT_TRUE(res.HasValue()); // auto edge = res.GetValue(); // ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), *vertex); -// ASSERT_EQ(edge.ToVertex(), *vertex); +// ASSERT_EQ(edge.From(), *vertex); +// ASSERT_EQ(edge.To(), *vertex); // // Check edges without filters // ASSERT_EQ(vertex->InEdges(View::OLD)->size(), 0); @@ -1897,8 +1897,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // ASSERT_EQ(vertex->OutEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex->OutDegree(View::OLD), 0); @@ -1910,8 +1910,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // auto other_et = acc.NameToEdgeType("other"); @@ -1946,8 +1946,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // { // auto ret = vertex->InEdges(View::NEW); @@ -1957,8 +1957,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // { // auto ret = vertex->OutEdges(View::OLD); @@ -1968,8 +1968,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // { // auto ret = vertex->OutEdges(View::NEW); @@ -1979,8 +1979,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // auto other_et = acc.NameToEdgeType("other"); @@ -2029,8 +2029,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // ASSERT_EQ(vertex->InEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex->InDegree(View::NEW), 0); @@ -2042,8 +2042,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // ASSERT_EQ(vertex->OutEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex->OutDegree(View::NEW), 0); @@ -2113,8 +2113,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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); +// ASSERT_EQ(edge.From(), *vertex_from); +// ASSERT_EQ(edge.To(), *vertex_to); // // Check edges without filters // ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); @@ -2131,8 +2131,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->InEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex_to->InDegree(View::OLD), 0); @@ -2144,8 +2144,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -2190,8 +2190,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_from->OutEdges(View::NEW); @@ -2201,8 +2201,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_to->InEdges(View::OLD); @@ -2212,8 +2212,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_to->InEdges(View::NEW); @@ -2223,8 +2223,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -2283,8 +2283,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_from->OutEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 0); @@ -2296,8 +2296,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); @@ -2344,8 +2344,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_from->OutEdges(View::NEW); @@ -2355,8 +2355,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_to->InEdges(View::OLD); @@ -2366,8 +2366,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_to->InEdges(View::NEW); @@ -2377,8 +2377,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->OutEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex_to->OutDegree(View::OLD), 0); @@ -2437,8 +2437,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_from->OutEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex_from->OutDegree(View::NEW), 0); @@ -2450,8 +2450,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); @@ -2533,8 +2533,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); @@ -2548,8 +2548,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); @@ -2586,8 +2586,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); @@ -2598,8 +2598,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); @@ -2639,7 +2639,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()); @@ -2654,8 +2654,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); @@ -2693,8 +2693,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); @@ -2705,8 +2705,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); @@ -2746,7 +2746,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()); @@ -2759,8 +2759,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); @@ -2839,8 +2839,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // ASSERT_TRUE(res.HasValue()); // auto edge = res.GetValue(); // ASSERT_EQ(edge.EdgeType(), et); -// ASSERT_EQ(edge.FromVertex(), *vertex); -// ASSERT_EQ(edge.ToVertex(), *vertex); +// ASSERT_EQ(edge.From(), *vertex); +// ASSERT_EQ(edge.To(), *vertex); // // Check edges without filters // ASSERT_EQ(vertex->InEdges(View::OLD)->size(), 0); @@ -2853,8 +2853,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // ASSERT_EQ(vertex->OutEdges(View::OLD)->size(), 0); // ASSERT_EQ(*vertex->OutDegree(View::OLD), 0); @@ -2866,8 +2866,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // 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); +// ASSERT_EQ(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // auto other_et = acc.NameToEdgeType("other"); @@ -2902,8 +2902,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // { // auto ret = vertex->InEdges(View::NEW); @@ -2913,8 +2913,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // { // auto ret = vertex->OutEdges(View::OLD); @@ -2924,8 +2924,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // { // auto ret = vertex->OutEdges(View::NEW); @@ -2935,8 +2935,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // 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); +// ASSERT_EQ(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // auto other_et = acc.NameToEdgeType("other"); @@ -2985,8 +2985,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // ASSERT_EQ(vertex->InEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex->InDegree(View::NEW), 0); @@ -2998,8 +2998,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // ASSERT_EQ(vertex->OutEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex->OutDegree(View::NEW), 0); @@ -3036,8 +3036,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // { // auto ret = vertex->InEdges(View::NEW); @@ -3047,8 +3047,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // { // auto ret = vertex->OutEdges(View::OLD); @@ -3058,8 +3058,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // { // auto ret = vertex->OutEdges(View::NEW); @@ -3069,8 +3069,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // 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); +// ASSERT_EQ(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // auto other_et = acc.NameToEdgeType("other"); @@ -3119,8 +3119,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // ASSERT_EQ(vertex->InEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex->InDegree(View::NEW), 0); @@ -3132,8 +3132,8 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) { // 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(e.From(), *vertex); +// ASSERT_EQ(e.To(), *vertex); // } // ASSERT_EQ(vertex->OutEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex->OutDegree(View::NEW), 0); @@ -3196,8 +3196,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); @@ -3211,8 +3211,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); @@ -3222,8 +3222,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); @@ -3267,8 +3267,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(), Error::DELETED_OBJECT); ASSERT_EQ(vertex_from->OutDegree(View::NEW).GetError(), Error::DELETED_OBJECT); @@ -3281,8 +3281,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); @@ -3338,29 +3338,29 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // ASSERT_TRUE(res1.HasValue()); // auto edge1 = res1.GetValue(); // ASSERT_EQ(edge1.EdgeType(), et1); -// ASSERT_EQ(edge1.FromVertex(), vertex1); -// ASSERT_EQ(edge1.ToVertex(), vertex2); +// ASSERT_EQ(edge1.From(), vertex1); +// ASSERT_EQ(edge1.To(), 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); +// ASSERT_EQ(edge2.From(), vertex2); +// ASSERT_EQ(edge2.To(), 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); +// ASSERT_EQ(edge3.From(), vertex1); +// ASSERT_EQ(edge3.To(), 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); +// ASSERT_EQ(edge4.From(), vertex2); +// ASSERT_EQ(edge4.To(), vertex2); // // Check edges // { @@ -3372,14 +3372,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex1); +// ASSERT_EQ(e.From(), vertex2); +// ASSERT_EQ(e.To(), vertex1); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex1); +// ASSERT_EQ(e.From(), vertex1); +// ASSERT_EQ(e.To(), vertex1); // } // } // { @@ -3391,14 +3391,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex2); +// ASSERT_EQ(e.From(), vertex1); +// ASSERT_EQ(e.To(), vertex2); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex1); +// ASSERT_EQ(e.From(), vertex1); +// ASSERT_EQ(e.To(), vertex1); // } // } // { @@ -3410,14 +3410,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex2); +// ASSERT_EQ(e.From(), vertex1); +// ASSERT_EQ(e.To(), vertex2); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex2); +// ASSERT_EQ(e.From(), vertex2); +// ASSERT_EQ(e.To(), vertex2); // } // } // { @@ -3429,14 +3429,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex1); +// ASSERT_EQ(e.From(), vertex2); +// ASSERT_EQ(e.To(), vertex1); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex2); +// ASSERT_EQ(e.From(), vertex2); +// ASSERT_EQ(e.To(), vertex2); // } // } @@ -3480,14 +3480,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex1); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex1); // } // } // ASSERT_EQ(vertex1->InEdges(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); @@ -3501,14 +3501,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex1); // } // } // ASSERT_EQ(vertex1->OutEdges(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); @@ -3522,14 +3522,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // } // { @@ -3540,8 +3540,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto ret = vertex2->OutEdges(View::OLD); @@ -3552,14 +3552,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex1); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // } // { @@ -3570,8 +3570,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // acc.Commit(GetNextHlc()); @@ -3596,8 +3596,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto ret = vertex2->InEdges(View::NEW); @@ -3607,8 +3607,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto ret = vertex2->OutEdges(View::OLD); @@ -3618,8 +3618,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto ret = vertex2->OutEdges(View::NEW); @@ -3629,8 +3629,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // } // } @@ -3653,8 +3653,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(edge.From(), vertex_from); +// ASSERT_EQ(edge.To(), vertex_to); // gid_from = vertex_from.Gid(); // gid_to = vertex_to.Gid(); @@ -3670,8 +3670,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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(e.From(), vertex_from); +// ASSERT_EQ(e.To(), vertex_to); // } // { // auto ret = vertex_to.InEdges(View::NEW); @@ -3681,8 +3681,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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(e.From(), vertex_from); +// ASSERT_EQ(e.To(), vertex_to); // } // ASSERT_EQ(vertex_to.OutEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex_to.OutDegree(View::NEW), 0); @@ -3727,8 +3727,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *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); @@ -3740,8 +3740,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); @@ -3774,8 +3774,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // { // auto ret = vertex_to->InEdges(View::NEW); @@ -3785,8 +3785,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->OutEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex_to->OutDegree(View::NEW), 0); @@ -3831,8 +3831,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *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); @@ -3844,8 +3844,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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(e.From(), *vertex_from); +// ASSERT_EQ(e.To(), *vertex_to); // } // ASSERT_EQ(vertex_to->InEdges(View::NEW)->size(), 0); // ASSERT_EQ(*vertex_to->InDegree(View::NEW), 0); @@ -3901,29 +3901,29 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // ASSERT_TRUE(res1.HasValue()); // auto edge1 = res1.GetValue(); // ASSERT_EQ(edge1.EdgeType(), et1); -// ASSERT_EQ(edge1.FromVertex(), vertex1); -// ASSERT_EQ(edge1.ToVertex(), vertex2); +// ASSERT_EQ(edge1.From(), vertex1); +// ASSERT_EQ(edge1.To(), 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); +// ASSERT_EQ(edge2.From(), vertex2); +// ASSERT_EQ(edge2.To(), 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); +// ASSERT_EQ(edge3.From(), vertex1); +// ASSERT_EQ(edge3.To(), 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); +// ASSERT_EQ(edge4.From(), vertex2); +// ASSERT_EQ(edge4.To(), vertex2); // // Check edges // { @@ -3935,14 +3935,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex1); +// ASSERT_EQ(e.From(), vertex2); +// ASSERT_EQ(e.To(), vertex1); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex1); +// ASSERT_EQ(e.From(), vertex1); +// ASSERT_EQ(e.To(), vertex1); // } // } // { @@ -3954,14 +3954,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex2); +// ASSERT_EQ(e.From(), vertex1); +// ASSERT_EQ(e.To(), vertex2); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex1); +// ASSERT_EQ(e.From(), vertex1); +// ASSERT_EQ(e.To(), vertex1); // } // } // { @@ -3973,14 +3973,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), vertex1); -// ASSERT_EQ(e.ToVertex(), vertex2); +// ASSERT_EQ(e.From(), vertex1); +// ASSERT_EQ(e.To(), vertex2); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex2); +// ASSERT_EQ(e.From(), vertex2); +// ASSERT_EQ(e.To(), vertex2); // } // } // { @@ -3992,14 +3992,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex1); +// ASSERT_EQ(e.From(), vertex2); +// ASSERT_EQ(e.To(), vertex1); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), vertex2); -// ASSERT_EQ(e.ToVertex(), vertex2); +// ASSERT_EQ(e.From(), vertex2); +// ASSERT_EQ(e.To(), vertex2); // } // } @@ -4043,14 +4043,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex1); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex1); // } // } // ASSERT_EQ(vertex1->InEdges(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); @@ -4064,14 +4064,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex1); // } // } // ASSERT_EQ(vertex1->OutEdges(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); @@ -4085,14 +4085,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // } // { @@ -4103,8 +4103,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto ret = vertex2->OutEdges(View::OLD); @@ -4115,14 +4115,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex1); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // } // { @@ -4133,8 +4133,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // acc.Abort(); @@ -4163,14 +4163,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex1); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex1); // } // } // { @@ -4182,14 +4182,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex1); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex1); // } // } // { @@ -4201,14 +4201,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex1); // } // } // { @@ -4220,14 +4220,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex1); // } // } // { @@ -4239,14 +4239,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // } // { @@ -4258,14 +4258,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // } // { @@ -4277,14 +4277,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex1); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // } // { @@ -4296,14 +4296,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex1); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // } @@ -4347,14 +4347,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex1); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex1); // } // } // ASSERT_EQ(vertex1->InEdges(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); @@ -4368,14 +4368,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et3); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex1); // } // } // ASSERT_EQ(vertex1->OutEdges(View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT); @@ -4389,14 +4389,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et1); -// ASSERT_EQ(e.FromVertex(), *vertex1); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex1); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // } // { @@ -4407,8 +4407,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto ret = vertex2->OutEdges(View::OLD); @@ -4419,14 +4419,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // { // auto e = edges[0]; // ASSERT_EQ(e.EdgeType(), et2); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex1); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex1); // } // { // auto e = edges[1]; // ASSERT_EQ(e.EdgeType(), et4); -// ASSERT_EQ(e.FromVertex(), *vertex2); -// ASSERT_EQ(e.ToVertex(), *vertex2); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // } // { @@ -4437,8 +4437,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // acc.Commit(GetNextHlc()); @@ -4463,8 +4463,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto ret = vertex2->InEdges(View::NEW); @@ -4474,8 +4474,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto ret = vertex2->OutEdges(View::OLD); @@ -4485,8 +4485,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // { // auto ret = vertex2->OutEdges(View::NEW); @@ -4496,8 +4496,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(e.From(), *vertex2); +// ASSERT_EQ(e.To(), *vertex2); // } // } // } @@ -4513,8 +4513,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(edge.From(), vertex); +// ASSERT_EQ(edge.To(), vertex); // auto property = acc.NameToProperty("property5"); @@ -4645,8 +4645,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(edge.From(), vertex); +// ASSERT_EQ(edge.To(), vertex); // acc.Commit(GetNextHlc()); // } @@ -4934,8 +4934,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(edge.From(), vertex); +// ASSERT_EQ(edge.To(), vertex); // acc.Commit(GetNextHlc()); // } @@ -5045,8 +5045,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(edge.From(), vertex); +// ASSERT_EQ(edge.To(), vertex); // auto old_value = edge.SetProperty(property1, memgraph::storage::PropertyValue("value")); // ASSERT_TRUE(old_value.HasValue()); @@ -5158,8 +5158,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(edge.From(), vertex); +// ASSERT_EQ(edge.To(), vertex); // acc.Commit(GetNextHlc()); // } // { @@ -5226,8 +5226,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) { // 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); +// ASSERT_EQ(edge.From(), vertex); +// ASSERT_EQ(edge.To(), vertex); // acc.Commit(GetNextHlc()); // } // { From 94bc671551281a426e5c3ea94e0c3ca3d22fdba3 Mon Sep 17 00:00:00 2001 From: Kostas Kyrimis Date: Wed, 9 Nov 2022 18:15:30 +0200 Subject: [PATCH 02/30] Add awesome memgraph functions e2e tests --- tests/e2e/distributed_queries/CMakeLists.txt | 1 + .../awesome_memgraph_functions.py | 90 +++++++++++++++++++ tests/e2e/distributed_queries/workloads.yaml | 5 ++ 3 files changed, 96 insertions(+) create mode 100644 tests/e2e/distributed_queries/awesome_memgraph_functions.py 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..469979733 --- /dev/null +++ b/tests/e2e/distributed_queries/awesome_memgraph_functions.py @@ -0,0 +1,90 @@ +# 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 time +import typing + +import mgclient +import pytest + +from common import * + + +def test_vertex_creation_and_scanall(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 From 213583f91649621dc796b54958b50ff6eb08bc62 Mon Sep 17 00:00:00 2001 From: Kostas Kyrimis Date: Mon, 14 Nov 2022 20:44:32 +0200 Subject: [PATCH 03/30] Add expression evaluator && awesome_memgraph_functions unit tests --- tests/unit/CMakeLists.txt | 4 + tests/unit/query_v2_expression_evaluator.cpp | 2192 ++++++++++++++++++ 2 files changed, 2196 insertions(+) create mode 100644 tests/unit/query_v2_expression_evaluator.cpp diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 0e5824318..2f16ab8c2 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -403,3 +403,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..6bc6e1c8b --- /dev/null +++ b/tests/unit/query_v2_expression_evaluator.cpp @@ -0,0 +1,2192 @@ +// Copyright 2022 Memgraph Ltd. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source +// License, and you may not use this file except in compliance with the Business Source License. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "coordinator/shard_map.hpp" +#include "exceptions.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 "storage/v3/storage.hpp" +#include "utils/exceptions.hpp" +#include "utils/string.hpp" + +#include "query_v2_query_common.hpp" +//#include "utils/temporal.hpp" + +using namespace memgraph::query::v2; +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 { + +class MockedShardRequestManager : public memgraph::msgs::ShardRequestManagerInterface { + public: + using VertexAccessor = memgraph::query::v2::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 Request( + memgraph::msgs::ExecutionState &state) override { + return {}; + } + + std::vector Request(memgraph::msgs::ExecutionState &state, + std::vector new_vertices) override { + return {}; + } + + std::vector Request(memgraph::msgs::ExecutionState &state, + ExpandOneRequest request) override { + return {}; + } + + std::vector Request(memgraph::msgs::ExecutionState &state, + std::vector 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()); + } + + 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 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 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 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{"edge_type"}); + + return sm; +} + +class ExpressionEvaluatorTest : public ::testing::Test { + public: + ExpressionEvaluatorTest() {} + + protected: + // memgraph::storage::Storage db; + // memgraph::storage::Storage::Accessor storage_dba{db.Access()}; + // memgraph::query::DbAccessor dba{&storage_dba}; + // + AstStorage storage; + memgraph::utils::MonotonicBufferResource mem{1024}; + EvaluationContext ctx{.memory = &mem, .timestamp = memgraph::query::v2::QueryTimestamp()}; + SymbolTable symbol_table; + + Frame frame{128}; + std::unique_ptr shard_manager = + std::make_unique(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(name, true); + auto symbol = symbol_table.CreateSymbol(name, true); + id->MapTo(symbol); + frame[symbol] = value; + return id; + } + + template + 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(storage.Create(true), storage.Create(false)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), true); + op = storage.Create(storage.Create(true), storage.Create(true)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), true); +} + +TEST_F(ExpressionEvaluatorTest, XorOperator) { + auto *op = + storage.Create(storage.Create(true), storage.Create(false)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), true); + op = storage.Create(storage.Create(true), storage.Create(true)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), false); +} + +TEST_F(ExpressionEvaluatorTest, AndOperator) { + auto *op = + storage.Create(storage.Create(true), storage.Create(true)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), true); + op = storage.Create(storage.Create(false), storage.Create(true)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), false); +} + +TEST_F(ExpressionEvaluatorTest, AndOperatorShortCircuit) { + { + auto *op = + storage.Create(storage.Create(false), storage.Create(5)); + auto value = Eval(op); + EXPECT_EQ(value.ValueBool(), false); + } + { + auto *op = + storage.Create(storage.Create(5), storage.Create(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(storage.Create(TypedValue()), + storage.Create(5)); + EXPECT_THROW(Eval(op), ExpressionRuntimeException); + } + { + auto *op = storage.Create(storage.Create(TypedValue()), + storage.Create(true)); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + auto *op = storage.Create(storage.Create(TypedValue()), + storage.Create(false)); + auto value = Eval(op); + ASSERT_TRUE(value.IsBool()); + EXPECT_EQ(value.ValueBool(), false); + } +} + +TEST_F(ExpressionEvaluatorTest, AdditionOperator) { + auto *op = storage.Create(storage.Create(2), storage.Create(3)); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), 5); +} + +TEST_F(ExpressionEvaluatorTest, SubtractionOperator) { + auto *op = + storage.Create(storage.Create(2), storage.Create(3)); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), -1); +} + +TEST_F(ExpressionEvaluatorTest, MultiplicationOperator) { + auto *op = + storage.Create(storage.Create(2), storage.Create(3)); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), 6); +} + +TEST_F(ExpressionEvaluatorTest, DivisionOperator) { + auto *op = + storage.Create(storage.Create(50), storage.Create(10)); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), 5); +} + +TEST_F(ExpressionEvaluatorTest, ModOperator) { + auto *op = storage.Create(storage.Create(65), storage.Create(10)); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), 5); +} + +TEST_F(ExpressionEvaluatorTest, EqualOperator) { + auto *op = storage.Create(storage.Create(10), storage.Create(15)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), false); + op = storage.Create(storage.Create(15), storage.Create(15)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), true); + op = storage.Create(storage.Create(20), storage.Create(15)); + auto val3 = Eval(op); + ASSERT_EQ(val3.ValueBool(), false); +} + +TEST_F(ExpressionEvaluatorTest, NotEqualOperator) { + auto *op = + storage.Create(storage.Create(10), storage.Create(15)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), true); + op = storage.Create(storage.Create(15), storage.Create(15)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), false); + op = storage.Create(storage.Create(20), storage.Create(15)); + auto val3 = Eval(op); + ASSERT_EQ(val3.ValueBool(), true); +} + +TEST_F(ExpressionEvaluatorTest, LessOperator) { + auto *op = storage.Create(storage.Create(10), storage.Create(15)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), true); + op = storage.Create(storage.Create(15), storage.Create(15)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), false); + op = storage.Create(storage.Create(20), storage.Create(15)); + auto val3 = Eval(op); + ASSERT_EQ(val3.ValueBool(), false); +} + +TEST_F(ExpressionEvaluatorTest, GreaterOperator) { + auto *op = + storage.Create(storage.Create(10), storage.Create(15)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), false); + op = storage.Create(storage.Create(15), storage.Create(15)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), false); + op = storage.Create(storage.Create(20), storage.Create(15)); + auto val3 = Eval(op); + ASSERT_EQ(val3.ValueBool(), true); +} + +TEST_F(ExpressionEvaluatorTest, LessEqualOperator) { + auto *op = + storage.Create(storage.Create(10), storage.Create(15)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), true); + op = storage.Create(storage.Create(15), storage.Create(15)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), true); + op = storage.Create(storage.Create(20), storage.Create(15)); + auto val3 = Eval(op); + ASSERT_EQ(val3.ValueBool(), false); +} + +TEST_F(ExpressionEvaluatorTest, GreaterEqualOperator) { + auto *op = + storage.Create(storage.Create(10), storage.Create(15)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), false); + op = storage.Create(storage.Create(15), storage.Create(15)); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), true); + op = storage.Create(storage.Create(20), storage.Create(15)); + auto val3 = Eval(op); + ASSERT_EQ(val3.ValueBool(), true); +} + +TEST_F(ExpressionEvaluatorTest, InListOperator) { + auto *list_literal = storage.Create(std::vector{ + storage.Create(1), storage.Create(2), storage.Create("a")}); + { + // Element exists in list. + auto *op = storage.Create(storage.Create(2), list_literal); + auto value = Eval(op); + EXPECT_EQ(value.ValueBool(), true); + } + { + // Element doesn't exist in list. + auto *op = storage.Create(storage.Create("x"), list_literal); + auto value = Eval(op); + EXPECT_EQ(value.ValueBool(), false); + } + { + auto *list_literal = storage.Create( + std::vector{storage.Create(TypedValue()), storage.Create(2), + storage.Create("a")}); + // Element doesn't exist in list with null element. + auto *op = storage.Create(storage.Create("x"), list_literal); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + // Null list. + auto *op = storage.Create(storage.Create("x"), + storage.Create(TypedValue())); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + // Null literal. + auto *op = storage.Create(storage.Create(TypedValue()), list_literal); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + // Null literal, empty list. + auto *op = storage.Create(storage.Create(TypedValue()), + storage.Create(std::vector())); + auto value = Eval(op); + EXPECT_FALSE(value.ValueBool()); + } +} + +TEST_F(ExpressionEvaluatorTest, ListIndexing) { + auto *list_literal = storage.Create( + std::vector{storage.Create(1), storage.Create(2), + storage.Create(3), storage.Create(4)}); + { + // Legal indexing. + auto *op = storage.Create(list_literal, storage.Create(2)); + auto value = Eval(op); + EXPECT_EQ(value.ValueInt(), 3); + } + { + // Out of bounds indexing. + auto *op = storage.Create(list_literal, storage.Create(4)); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + // Out of bounds indexing with negative bound. + auto *op = storage.Create(list_literal, storage.Create(-100)); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + // Legal indexing with negative index. + auto *op = storage.Create(list_literal, storage.Create(-2)); + auto value = Eval(op); + EXPECT_EQ(value.ValueInt(), 3); + } + { + // Indexing with one operator being null. + auto *op = storage.Create(storage.Create(TypedValue()), + storage.Create(-2)); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + // Indexing with incompatible type. + auto *op = storage.Create(list_literal, storage.Create("bla")); + EXPECT_THROW(Eval(op), ExpressionRuntimeException); + } +} + +TEST_F(ExpressionEvaluatorTest, MapIndexing) { + auto *map_literal = storage.Create( + std::unordered_map{{storage.GetPropertyIx("a"), storage.Create(1)}, + {storage.GetPropertyIx("b"), storage.Create(2)}, + {storage.GetPropertyIx("c"), storage.Create(3)}}); + { + // Legal indexing. + auto *op = storage.Create(map_literal, storage.Create("b")); + auto value = Eval(op); + EXPECT_EQ(value.ValueInt(), 2); + } + { + // Legal indexing, non-existing key. + auto *op = storage.Create(map_literal, storage.Create("z")); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } + { + // Wrong key type. + auto *op = storage.Create(map_literal, storage.Create(42)); + EXPECT_THROW(Eval(op), ExpressionRuntimeException); + } + { + // Indexing with Null. + auto *op = storage.Create(map_literal, storage.Create(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 Value = memgraph::msgs::Value; +using VertexId = memgraph::msgs::VertexId; +using Label = memgraph::msgs::Label; + +memgraph::query::v2::accessors::VertexAccessor CreateVertex(std::vector> props, + const memgraph::msgs::ShardRequestManagerInterface *manager, + Label label = {}) { + static size_t id = 0; + return {Vertex{VertexId{label, id++}, {label}}, std::move(props), manager}; +} + +memgraph::query::v2::accessors::EdgeAccessor CreateEdge(std::vector> props, + const memgraph::msgs::ShardRequestManagerInterface *manager) { + auto edge = Edge{.src = VertexId{{}, 0}, + .dst = VertexId{{}, 0}, + .properties = std::move(props), + .type = EdgeType{manager->NameToEdgeType("edge_type")}}; + return memgraph::query::v2::accessors::EdgeAccessor{std::move(edge), manager}; +} + +TEST_F(ExpressionEvaluatorTest, VertexAndEdgeIndexing) { + auto prop = shard_manager->NameToProperty("prop"); + auto vertex = CreateVertex({{prop, Value(static_cast(42))}}, shard_manager.get()); + auto edge = CreateEdge({{prop, Value(static_cast(43))}}, shard_manager.get()); + + auto *vertex_id = CreateIdentifierWithValue("v1", TypedValue(vertex)); + auto *edge_id = CreateIdentifierWithValue("e11", TypedValue(edge)); + { + // Legal indexing. + auto *op1 = storage.Create(vertex_id, storage.Create("prop")); + auto value1 = Eval(op1); + EXPECT_EQ(value1.ValueInt(), 42); + + auto *op2 = storage.Create(edge_id, storage.Create("prop")); + auto value2 = Eval(op2); + EXPECT_EQ(value2.ValueInt(), 43); + } + // TODO(kostasrim) Investigate + // Shall we return null on missing properties? Or shall we throw bad optional access as we do now? + + // { + // // Legal indexing, non-existing key. + // auto *op1 = storage.Create(vertex_id, storage.Create("blah")); + // auto value1 = Eval(op1); + // EXPECT_TRUE(value1.IsNull()); + // + // auto *op2 = storage.Create(edge_id, storage.Create("blah")); + // auto value2 = Eval(op2); + // EXPECT_TRUE(value2.IsNull()); + // } + // { + // // Wrong key type. + // auto *op1 = storage.Create(vertex_id, storage.Create(1)); + // EXPECT_THROW(Eval(op1), ExpressionRuntimeException); + // + // auto *op2 = storage.Create(edge_id, storage.Create(1)); + // EXPECT_THROW(Eval(op2), ExpressionRuntimeException); + // } + // { + // // Indexing with Null. + // auto *op1 = storage.Create(vertex_id, + // storage.Create(memgraph::storage::PropertyValue())); + // auto value1 = Eval(op1); + // EXPECT_TRUE(value1.IsNull()); + // + // auto *op2 = storage.Create(edge_id, + // storage.Create(memgraph::storage::PropertyValue())); + // auto value2 = Eval(op2); + // EXPECT_TRUE(value2.IsNull()); + // } +} + +TEST_F(ExpressionEvaluatorTest, ListSlicingOperator) { + auto *list_literal = storage.Create( + std::vector{storage.Create(1), storage.Create(2), + storage.Create(3), storage.Create(4)}); + + auto extract_ints = [](TypedValue list) { + std::vector 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(list_literal, storage.Create(2), + storage.Create(4)); + auto value = Eval(op); + EXPECT_THAT(extract_ints(value), ElementsAre(3, 4)); + } + { + // Legal slicing with negative bound. + auto *op = storage.Create(list_literal, storage.Create(2), + storage.Create(-1)); + auto value = Eval(op); + EXPECT_THAT(extract_ints(value), ElementsAre(3)); + } + { + // Lower bound larger than upper bound. + auto *op = storage.Create(list_literal, storage.Create(2), + storage.Create(-4)); + auto value = Eval(op); + EXPECT_THAT(extract_ints(value), ElementsAre()); + } + { + // Bounds ouf or range. + auto *op = storage.Create(list_literal, storage.Create(-100), + storage.Create(10)); + auto value = Eval(op); + EXPECT_THAT(extract_ints(value), ElementsAre(1, 2, 3, 4)); + } + { + // Lower bound undefined. + auto *op = storage.Create(list_literal, nullptr, storage.Create(3)); + auto value = Eval(op); + EXPECT_THAT(extract_ints(value), ElementsAre(1, 2, 3)); + } + { + // Upper bound undefined. + auto *op = storage.Create(list_literal, storage.Create(-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(list_literal, storage.Create(TypedValue()), + storage.Create("mirko")); + EXPECT_THROW(Eval(op), ExpressionRuntimeException); + } + { + // List of illegal type. + auto *op = storage.Create(storage.Create("a"), + storage.Create(-2), nullptr); + EXPECT_THROW(Eval(op), ExpressionRuntimeException); + } + { + // Null value list with undefined upper bound. + auto *op = storage.Create(storage.Create(TypedValue()), + storage.Create(-2), nullptr); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + ; + } + { + // Null value index. + auto *op = storage.Create(list_literal, storage.Create(-2), + storage.Create(TypedValue())); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + ; + } +} + +TEST_F(ExpressionEvaluatorTest, IfOperator) { + auto *then_expression = storage.Create(10); + auto *else_expression = storage.Create(20); + { + auto *condition_true = + storage.Create(storage.Create(2), storage.Create(2)); + auto *op = storage.Create(condition_true, then_expression, else_expression); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), 10); + } + { + auto *condition_false = + storage.Create(storage.Create(2), storage.Create(3)); + auto *op = storage.Create(condition_false, then_expression, else_expression); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), 20); + } + { + auto *condition_exception = + storage.Create(storage.Create(2), storage.Create(3)); + auto *op = storage.Create(condition_exception, then_expression, else_expression); + ASSERT_THROW(Eval(op), ExpressionRuntimeException); + } +} + +TEST_F(ExpressionEvaluatorTest, NotOperator) { + auto *op = storage.Create(storage.Create(false)); + auto value = Eval(op); + ASSERT_EQ(value.ValueBool(), true); +} + +TEST_F(ExpressionEvaluatorTest, UnaryPlusOperator) { + auto *op = storage.Create(storage.Create(5)); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), 5); +} + +TEST_F(ExpressionEvaluatorTest, UnaryMinusOperator) { + auto *op = storage.Create(storage.Create(5)); + auto value = Eval(op); + ASSERT_EQ(value.ValueInt(), -5); +} + +TEST_F(ExpressionEvaluatorTest, IsNullOperator) { + auto *op = storage.Create(storage.Create(1)); + auto val1 = Eval(op); + ASSERT_EQ(val1.ValueBool(), false); + op = storage.Create(storage.Create(TypedValue())); + auto val2 = Eval(op); + ASSERT_EQ(val2.ValueBool(), true); +} + +// TEST_F(ExpressionEvaluatorTest, LabelsTest) { +// auto v1 = dba.InsertVertex(); +// ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("ANIMAL")).HasValue()); +// ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("DOG")).HasValue()); +// ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("NICE_DOG")).HasValue()); +// dba.AdvanceCommand(); +// auto *identifier = storage.Create("n"); +// auto node_symbol = symbol_table.CreateSymbol("n", true); +// identifier->MapTo(node_symbol); +// frame[node_symbol] = TypedValue(v1); +// { +// auto *op = storage.Create( +// identifier, std::vector{storage.GetLabelIx("DOG"), storage.GetLabelIx("ANIMAL")}); +// auto value = Eval(op); +// EXPECT_EQ(value.ValueBool(), true); +// } +// { +// auto *op = storage.Create( +// identifier, +// std::vector{storage.GetLabelIx("DOG"), storage.GetLabelIx("BAD_DOG"), +// storage.GetLabelIx("ANIMAL")}); +// auto value = Eval(op); +// EXPECT_EQ(value.ValueBool(), false); +// } +// { +// frame[node_symbol] = TypedValue(); +// auto *op = storage.Create( +// identifier, +// std::vector{storage.GetLabelIx("DOG"), storage.GetLabelIx("BAD_DOG"), +// storage.GetLabelIx("ANIMAL")}); +// auto value = Eval(op); +// EXPECT_TRUE(value.IsNull()); +// } +// } +// +TEST_F(ExpressionEvaluatorTest, Aggregation) { + auto aggr = storage.Create(storage.Create(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(std::vector{storage.Create(1), + storage.Create("bla"), + storage.Create(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(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(LITERAL(TypedValue()), LITERAL("regex"))).IsNull()); + EXPECT_TRUE(Eval(storage.Create(LITERAL(3), LITERAL("regex"))).IsNull()); + EXPECT_TRUE(Eval(storage.Create(LIST(LITERAL("string")), LITERAL("regex"))).IsNull()); + EXPECT_TRUE(Eval(storage.Create(LITERAL("string"), LITERAL(TypedValue()))).IsNull()); + EXPECT_THROW(Eval(storage.Create(LITERAL("string"), LITERAL(42))), ExpressionRuntimeException); + EXPECT_THROW(Eval(storage.Create(LITERAL("string"), LIST(LITERAL("regex")))), ExpressionRuntimeException); +} + +TEST_F(ExpressionEvaluatorTest, RegexMatchInvalidRegex) { + EXPECT_THROW(Eval(storage.Create(LITERAL("text"), LITERAL("*ext"))), ExpressionRuntimeException); + EXPECT_THROW(Eval(storage.Create(LITERAL("text"), LITERAL("[ext"))), ExpressionRuntimeException); +} + +TEST_F(ExpressionEvaluatorTest, RegexMatch) { + EXPECT_FALSE(Eval(storage.Create(LITERAL("text"), LITERAL(".*ex"))).ValueBool()); + EXPECT_TRUE(Eval(storage.Create(LITERAL("text"), LITERAL(".*ext"))).ValueBool()); + EXPECT_FALSE(Eval(storage.Create(LITERAL("text"), LITERAL("[ext]"))).ValueBool()); + EXPECT_TRUE(Eval(storage.Create(LITERAL("text"), LITERAL(".+[ext]"))).ValueBool()); +} + +class ExpressionEvaluatorPropertyLookup : public ExpressionEvaluatorTest { + protected: + std::pair prop_age = + std::make_pair("age", shard_manager->NameToProperty("age")); + std::pair prop_height = + std::make_pair("height", shard_manager->NameToProperty("height")); + Identifier *identifier = storage.Create("element"); + Symbol symbol = symbol_table.CreateSymbol("element", true); + + void SetUp() { identifier->MapTo(symbol); } + + auto Value(std::pair property) { + auto *op = storage.Create(identifier, storage.GetPropertyIx(property.first)); + return Eval(op); + } +}; + +// TEST_F(ExpressionEvaluatorPropertyLookup, Vertex) { +// auto v1 = dba.InsertVertex(); +// ASSERT_TRUE(v1.SetProperty(prop_age.second, memgraph::storage::PropertyValue(10)).HasValue()); +// dba.AdvanceCommand(); +// frame[symbol] = TypedValue(v1); +// EXPECT_EQ(Value(prop_age).ValueInt(), 10); +// EXPECT_TRUE(Value(prop_height).IsNull()); +// } +// +// TEST_F(ExpressionEvaluatorPropertyLookup, Duration) { +// const memgraph::utils::Duration dur({10, 1, 30, 2, 22, 45}); +// frame[symbol] = TypedValue(dur); +// +// const std::pair day = std::make_pair("day", dba.NameToProperty("day")); +// const auto total_days = Value(day); +// EXPECT_TRUE(total_days.IsInt()); +// EXPECT_EQ(total_days.ValueInt(), 10); +// +// const std::pair hour = std::make_pair("hour", dba.NameToProperty("hour")); +// const auto total_hours = Value(hour); +// EXPECT_TRUE(total_hours.IsInt()); +// EXPECT_EQ(total_hours.ValueInt(), 1); +// +// const std::pair minute = std::make_pair("minute", dba.NameToProperty("minute")); +// const auto total_mins = Value(minute); +// EXPECT_TRUE(total_mins.IsInt()); +// +// EXPECT_EQ(total_mins.ValueInt(), 1 * 60 + 30); +// +// const std::pair sec = std::make_pair("second", dba.NameToProperty("second")); +// const auto total_secs = Value(sec); +// EXPECT_TRUE(total_secs.IsInt()); +// const auto expected_secs = total_mins.ValueInt() * 60 + 2; +// EXPECT_EQ(total_secs.ValueInt(), expected_secs); +// +// const std::pair milli = std::make_pair("millisecond", dba.NameToProperty("millisecond")); +// const auto total_milli = Value(milli); +// EXPECT_TRUE(total_milli.IsInt()); +// const auto expected_milli = total_secs.ValueInt() * 1000 + 22; +// EXPECT_EQ(total_milli.ValueInt(), expected_milli); +// +// const std::pair micro = std::make_pair("microsecond", dba.NameToProperty("microsecond")); +// const auto total_micros = Value(micro); +// EXPECT_TRUE(total_micros.IsInt()); +// const auto expected_micros = expected_milli * 1000 + 45; +// EXPECT_EQ(total_micros.ValueInt(), expected_micros); +// +// const std::pair nano = std::make_pair("nanosecond", dba.NameToProperty("nanosecond")); +// const auto total_nano = Value(nano); +// EXPECT_TRUE(total_nano.IsInt()); +// const auto expected_nano = expected_micros * 1000; +// EXPECT_EQ(total_nano.ValueInt(), expected_nano); +// } +// +// TEST_F(ExpressionEvaluatorPropertyLookup, Date) { +// const memgraph::utils::Date date({1996, 11, 22}); +// frame[symbol] = TypedValue(date); +// +// const std::pair year = std::make_pair("year", dba.NameToProperty("year")); +// const auto y = Value(year); +// EXPECT_TRUE(y.IsInt()); +// EXPECT_EQ(y.ValueInt(), 1996); +// +// const std::pair month = std::make_pair("month", dba.NameToProperty("month")); +// const auto m = Value(month); +// EXPECT_TRUE(m.IsInt()); +// EXPECT_EQ(m.ValueInt(), 11); +// +// const std::pair day = std::make_pair("day", dba.NameToProperty("day")); +// const auto d = Value(day); +// EXPECT_TRUE(d.IsInt()); +// EXPECT_EQ(d.ValueInt(), 22); +// } +// +// TEST_F(ExpressionEvaluatorPropertyLookup, LocalTime) { +// const memgraph::utils::LocalTime lt({1, 2, 3, 11, 22}); +// frame[symbol] = TypedValue(lt); +// +// const std::pair hour = std::make_pair("hour", dba.NameToProperty("hour")); +// const auto h = Value(hour); +// EXPECT_TRUE(h.IsInt()); +// EXPECT_EQ(h.ValueInt(), 1); +// +// const std::pair minute = std::make_pair("minute", dba.NameToProperty("minute")); +// const auto min = Value(minute); +// EXPECT_TRUE(min.IsInt()); +// EXPECT_EQ(min.ValueInt(), 2); +// +// const std::pair second = std::make_pair("second", dba.NameToProperty("second")); +// const auto sec = Value(second); +// EXPECT_TRUE(sec.IsInt()); +// EXPECT_EQ(sec.ValueInt(), 3); +// +// const std::pair millis = std::make_pair("millisecond", dba.NameToProperty("millisecond")); +// const auto mil = Value(millis); +// EXPECT_TRUE(mil.IsInt()); +// EXPECT_EQ(mil.ValueInt(), 11); +// +// const std::pair micros = std::make_pair("microsecond", dba.NameToProperty("microsecond")); +// const auto mic = Value(micros); +// EXPECT_TRUE(mic.IsInt()); +// EXPECT_EQ(mic.ValueInt(), 22); +// } +// +// TEST_F(ExpressionEvaluatorPropertyLookup, LocalDateTime) { +// const memgraph::utils::LocalDateTime ldt({1993, 8, 6}, {2, 3, 4, 55, 40}); +// frame[symbol] = TypedValue(ldt); +// +// const std::pair year = std::make_pair("year", dba.NameToProperty("year")); +// const auto y = Value(year); +// EXPECT_TRUE(y.IsInt()); +// EXPECT_EQ(y.ValueInt(), 1993); +// +// const std::pair month = std::make_pair("month", dba.NameToProperty("month")); +// const auto m = Value(month); +// EXPECT_TRUE(m.IsInt()); +// EXPECT_EQ(m.ValueInt(), 8); +// +// const std::pair day = std::make_pair("day", dba.NameToProperty("day")); +// const auto d = Value(day); +// EXPECT_TRUE(d.IsInt()); +// EXPECT_EQ(d.ValueInt(), 6); +// +// const std::pair hour = std::make_pair("hour", dba.NameToProperty("hour")); +// const auto h = Value(hour); +// EXPECT_TRUE(h.IsInt()); +// EXPECT_EQ(h.ValueInt(), 2); +// +// const std::pair minute = std::make_pair("minute", dba.NameToProperty("minute")); +// const auto min = Value(minute); +// EXPECT_TRUE(min.IsInt()); +// EXPECT_EQ(min.ValueInt(), 3); +// +// const std::pair second = std::make_pair("second", dba.NameToProperty("second")); +// const auto sec = Value(second); +// EXPECT_TRUE(sec.IsInt()); +// EXPECT_EQ(sec.ValueInt(), 4); +// +// const std::pair millis = std::make_pair("millisecond", dba.NameToProperty("millisecond")); +// const auto mil = Value(millis); +// EXPECT_TRUE(mil.IsInt()); +// EXPECT_EQ(mil.ValueInt(), 55); +// +// const std::pair micros = std::make_pair("microsecond", dba.NameToProperty("microsecond")); +// const auto mic = Value(micros); +// EXPECT_TRUE(mic.IsInt()); +// EXPECT_EQ(mic.ValueInt(), 40); +// } +// +// TEST_F(ExpressionEvaluatorPropertyLookup, Edge) { +// auto v1 = dba.InsertVertex(); +// auto v2 = dba.InsertVertex(); +// auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("edge_type")); +// ASSERT_TRUE(e12.HasValue()); +// ASSERT_TRUE(e12->SetProperty(prop_age.second, memgraph::storage::PropertyValue(10)).HasValue()); +// dba.AdvanceCommand(); +// frame[symbol] = TypedValue(*e12); +// EXPECT_EQ(Value(prop_age).ValueInt(), 10); +// EXPECT_TRUE(Value(prop_height).IsNull()); +// } +// +TEST_F(ExpressionEvaluatorPropertyLookup, Null) { + frame[symbol] = TypedValue(); + EXPECT_TRUE(Value(prop_age).IsNull()); +} + +TEST_F(ExpressionEvaluatorPropertyLookup, MapLiteral) { + frame[symbol] = TypedValue(std::map{{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 ExpressionsFromTypedValues(const std::vector &tvs) { + std::vector expressions; + expressions.reserve(tvs.size()); + + for (size_t i = 0; i < tvs.size(); ++i) { + auto *ident = storage.Create("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 &expressions) { + auto *op = storage.Create(function_name, expressions); + return Eval(op); + } + + template + TypedValue EvaluateFunction(const std::string &function_name, std::tuple args) { + std::vector 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 + TypedValue EvaluateFunction(const std::string &function_name, TArgs &&...args) { + return EvaluateFunctionWithExprs(function_name, + ExpressionsFromTypedValues(std::vector{TypedValue(args)...})); + } +}; + +template +static TypedValue MakeTypedValueList(TArgs &&...args) { + return TypedValue(std::vector{TypedValue(args)...}); +} + +TEST_F(FunctionTest, EndNode) { + ASSERT_THROW(EvaluateFunction("ENDNODE"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("ENDNODE", TypedValue()).IsNull()); + auto v1 = CreateVertex({}, shard_manager.get()); + auto e = CreateEdge({}, shard_manager.get()); + const auto expected = VertexId{{}, 0}; + const auto res = EvaluateFunction("ENDNODE", e).ValueVertex().Id(); + ASSERT_EQ(res, expected); + 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(5))}, {age, Value(static_cast(10))}}, + shard_manager.get()); + auto e = CreateEdge({{height, Value(static_cast(3))}, {age, Value(static_cast(15))}}, + shard_manager.get()); + + auto prop_values_to_int = [](TypedValue t) { + std::unordered_map 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{ + {"a", TypedValue(5)}, {"b", TypedValue(true)}, {"c", TypedValue("123")}}) + .ValueInt(), + 3); + ASSERT_THROW(EvaluateFunction("SIZE", 5), FunctionRuntimeException); + + // TODO(kostasrim) Add this when we enable paths on the accessors + // auto v0 = dba.InsertVertex(); + // memgraph::query::Path path(v0); + // EXPECT_EQ(EvaluateFunction("SIZE", path).ValueInt(), 0); + // auto v1 = dba.InsertVertex(); + // auto edge = dba.InsertEdge(&v0, &v1, dba.NameToEdgeType("type")); + // ASSERT_TRUE(edge.HasValue()); + // path.Expand(*edge); + // path.Expand(v1); + // EXPECT_EQ(EvaluateFunction("SIZE", path).ValueInt(), 1); +} + +TEST_F(FunctionTest, StartNode) { + ASSERT_THROW(EvaluateFunction("STARTNODE"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("STARTNODE", TypedValue()).IsNull()); + auto v1 = CreateVertex({}, shard_manager.get()); + auto e = CreateEdge({}, shard_manager.get()); + const auto expected = VertexId{{}, 0}; + const auto res = EvaluateFunction("STARTNODE", e).ValueVertex().Id(); + ASSERT_EQ(res, expected); + ASSERT_THROW(EvaluateFunction("STARTNODE", 2), FunctionRuntimeException); +} + +// TODO(kostasrim) Enable this test once we add degree to the accessors +// TEST_F(FunctionTest, Degree) { +// ASSERT_THROW(EvaluateFunction("DEGREE"), ExpressionRuntimeException); +// ASSERT_TRUE(EvaluateFunction("DEGREE", TypedValue()).IsNull()); +// auto v1 = dba.InsertVertex(); +// auto v2 = dba.InsertVertex(); +// auto v3 = dba.InsertVertex(); +// auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t")); +// ASSERT_TRUE(e12.HasValue()); +// ASSERT_TRUE(dba.InsertEdge(&v3, &v2, dba.NameToEdgeType("t")).HasValue()); +// dba.AdvanceCommand(); +// ASSERT_EQ(EvaluateFunction("DEGREE", v1).ValueInt(), 1); +// ASSERT_EQ(EvaluateFunction("DEGREE", v2).ValueInt(), 2); +// ASSERT_EQ(EvaluateFunction("DEGREE", v3).ValueInt(), 1); +// ASSERT_THROW(EvaluateFunction("DEGREE", 2), ExpressionRuntimeException); +// ASSERT_THROW(EvaluateFunction("DEGREE", *e12), ExpressionRuntimeException); +// } +// +// TODO(kostasrim) Enable this test once we add InDegree to the accessors +// TEST_F(FunctionTest, InDegree) { +// ASSERT_THROW(EvaluateFunction("INDEGREE"), ExpressionRuntimeException); +// ASSERT_TRUE(EvaluateFunction("INDEGREE", TypedValue()).IsNull()); +// auto v1 = dba.InsertVertex(); +// auto v2 = dba.InsertVertex(); +// auto v3 = dba.InsertVertex(); +// auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t")); +// ASSERT_TRUE(e12.HasValue()); +// ASSERT_TRUE(dba.InsertEdge(&v3, &v2, dba.NameToEdgeType("t")).HasValue()); +// dba.AdvanceCommand(); +// ASSERT_EQ(EvaluateFunction("INDEGREE", v1).ValueInt(), 0); +// ASSERT_EQ(EvaluateFunction("INDEGREE", v2).ValueInt(), 2); +// ASSERT_EQ(EvaluateFunction("INDEGREE", v3).ValueInt(), 0); +// ASSERT_THROW(EvaluateFunction("INDEGREE", 2), ExpressionRuntimeException); +// ASSERT_THROW(EvaluateFunction("INDEGREE", *e12), ExpressionRuntimeException); +// } +// +// TODO(kostasrim) Enable this test once we add OutDegree to the accessors +// TEST_F(FunctionTest, OutDegree) { +// ASSERT_THROW(EvaluateFunction("OUTDEGREE"), ExpressionRuntimeException); +// ASSERT_TRUE(EvaluateFunction("OUTDEGREE", TypedValue()).IsNull()); +// auto v1 = dba.InsertVertex(); +// auto v2 = dba.InsertVertex(); +// auto v3 = dba.InsertVertex(); +// auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t")); +// ASSERT_TRUE(e12.HasValue()); +// ASSERT_TRUE(dba.InsertEdge(&v3, &v2, dba.NameToEdgeType("t")).HasValue()); +// dba.AdvanceCommand(); +// ASSERT_EQ(EvaluateFunction("OUTDEGREE", v1).ValueInt(), 1); +// ASSERT_EQ(EvaluateFunction("OUTDEGREE", v2).ValueInt(), 0); +// ASSERT_EQ(EvaluateFunction("OUTDEGREE", v3).ValueInt(), 1); +// ASSERT_THROW(EvaluateFunction("OUTDEGREE", 2), ExpressionRuntimeException); +// ASSERT_THROW(EvaluateFunction("OUTDEGREE", *e12), ExpressionRuntimeException); +// } +// +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(1), TypedValue(2)})).ValueString(), + "LIST"); + ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(std::map{{"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"); + // TODO(kostasrim) Fix this when we add Path to accessors + // Path p(v1, *e, v2); + // ASSERT_EQ(EvaluateFunction("VALUETYPE", p).ValueString(), "PATH"); +} + +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(), std::move(label)); + std::vector labels; + auto _labels = EvaluateFunction("LABELS", v).ValueList(); + labels.reserve(_labels.size()); + for (auto label : _labels) { + labels.emplace_back(label.ValueString()); + } + ASSERT_THAT(labels, UnorderedElementsAre("label1")); + ASSERT_THROW(EvaluateFunction("LABELS", 2), FunctionRuntimeException); +} + +// TODO(kostasrim) Enable this once we fix accessors Path +// TEST_F(FunctionTest, NodesRelationships) { +// EXPECT_THROW(EvaluateFunction("NODES"), ExpressionRuntimeException); +// EXPECT_THROW(EvaluateFunction("RELATIONSHIPS"), ExpressionRuntimeException); +// EXPECT_TRUE(EvaluateFunction("NODES", TypedValue()).IsNull()); +// EXPECT_TRUE(EvaluateFunction("RELATIONSHIPS", TypedValue()).IsNull()); +// +// { +// auto v1 = dba.InsertVertex(); +// auto v2 = dba.InsertVertex(); +// auto v3 = dba.InsertVertex(); +// auto e1 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("Type")); +// ASSERT_TRUE(e1.HasValue()); +// auto e2 = dba.InsertEdge(&v2, &v3, dba.NameToEdgeType("Type")); +// ASSERT_TRUE(e2.HasValue()); +// memgraph::query::Path path{v1, *e1, v2, *e2, v3}; +// dba.AdvanceCommand(); +// +// auto _nodes = EvaluateFunction("NODES", path).ValueList(); +// std::vector nodes; +// for (const auto &node : _nodes) { +// nodes.push_back(node.ValueVertex()); +// } +// EXPECT_THAT(nodes, ElementsAre(v1, v2, v3)); +// +// auto _edges = EvaluateFunction("RELATIONSHIPS", path).ValueList(); +// std::vector edges; +// for (const auto &edge : _edges) { +// edges.push_back(edge.ValueEdge()); +// } +// EXPECT_THAT(edges, ElementsAre(*e1, *e2)); +// } +// +// EXPECT_THROW(EvaluateFunction("NODES", 2), ExpressionRuntimeException); +// EXPECT_THROW(EvaluateFunction("RELATIONSHIPS", 2), ExpressionRuntimeException); +// } +// +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, Keys) { + ASSERT_THROW(EvaluateFunction("KEYS"), FunctionRuntimeException); + ASSERT_TRUE(EvaluateFunction("KEYS", TypedValue()).IsNull()); + const auto height = shard_manager->NameToProperty("height"); + const auto age = shard_manager->NameToProperty("age"); + auto v1 = CreateVertex({{height, Value(static_cast(5))}, {age, Value(static_cast(10))}}, + shard_manager.get()); + auto edge = CreateEdge({{height, Value(static_cast(3))}, {age, Value(static_cast(15))}}, + shard_manager.get()); + auto prop_keys_to_string = [](TypedValue t) { + std::vector keys; + for (auto property : t.ValueList()) { + keys.emplace_back(property.ValueString()); + } + return keys; + }; + ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", v1)), UnorderedElementsAre("height", "age")); + ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", edge)), UnorderedElementsAre("height", "age")); + ASSERT_THROW(EvaluateFunction("KEYS", 2), FunctionRuntimeException); +} + +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); +} + +// TODO(kostasrim) Add this once we fix accessors CypherId() functions +// TEST_F(FunctionTest, Id) { +// auto v = CreateVertex({}, shard_manager.get()); +// auto e = CreateEdge({}, shard_manager.get()); +// EXPECT_TRUE(EvaluateFunction("ID", TypedValue()).IsNull()); +// EXPECT_EQ(EvaluateFunction("ID", v).ValueInt(), 0); +// EXPECT_EQ(EvaluateFunction("ID", e).ValueInt(), 0); +// EXPECT_THROW(EvaluateFunction("ID"), FunctionRuntimeException); +// EXPECT_THROW(EvaluateFunction("ID", 0), FunctionRuntimeException); +// EXPECT_THROW(EvaluateFunction("ID", v, e), FunctionRuntimeException); +//} + +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, ToStringDate) { +// const auto date = memgraph::utils::Date({1970, 1, 2}); +// EXPECT_EQ(EvaluateFunction("TOSTRING", date).ValueString(), "1970-01-02"); +// } +// +// TEST_F(FunctionTest, ToStringLocalTime) { +// const auto lt = memgraph::utils::LocalTime({13, 2, 40, 100, 50}); +// EXPECT_EQ(EvaluateFunction("TOSTRING", lt).ValueString(), "13:02:40.100050"); +// } +// +// TEST_F(FunctionTest, ToStringLocalDateTime) { +// const auto ldt = memgraph::utils::LocalDateTime({1970, 1, 2}, {23, 02, 59}); +// EXPECT_EQ(EvaluateFunction("TOSTRING", ldt).ValueString(), "1970-01-02T23:02:59.000000"); +// } +// +// TEST_F(FunctionTest, ToStringDuration) { +// memgraph::utils::Duration duration{{.minute = 2, .second = 2, .microsecond = 33}}; +// EXPECT_EQ(EvaluateFunction("TOSTRING", duration).ValueString(), "P0DT0H2M2.000033S"); +// } +// +TEST_F(FunctionTest, ToStringExceptions) { + EXPECT_THROW(EvaluateFunction("TOSTRING", 1, 2, 3), FunctionRuntimeException); +} +// +// TEST_F(FunctionTest, TimestampVoid) { +// ctx.timestamp = 42; +// EXPECT_EQ(EvaluateFunction("TIMESTAMP").ValueInt(), 42); +// } +// +// TEST_F(FunctionTest, TimestampDate) { +// ctx.timestamp = 42; +// EXPECT_EQ(EvaluateFunction("TIMESTAMP", memgraph::utils::Date({1970, 1, 1})).ValueInt(), 0); +// EXPECT_EQ(EvaluateFunction("TIMESTAMP", memgraph::utils::Date({1971, 1, 1})).ValueInt(), 31536000000000); +// } +// +// TEST_F(FunctionTest, TimestampLocalTime) { +// ctx.timestamp = 42; +// const memgraph::utils::LocalTime time(10000); +// EXPECT_EQ(EvaluateFunction("TIMESTAMP", time).ValueInt(), 10000); +// } +// +// TEST_F(FunctionTest, TimestampLocalDateTime) { +// ctx.timestamp = 42; +// const memgraph::utils::LocalDateTime time(20000); +// EXPECT_EQ(EvaluateFunction("TIMESTAMP", time).ValueInt(), 20000); +// } +// +// TEST_F(FunctionTest, TimestampDuration) { +// ctx.timestamp = 42; +// const memgraph::utils::Duration time(20000); +// EXPECT_EQ(EvaluateFunction("TIMESTAMP", time).ValueInt(), 20000); +// } +// +// TEST_F(FunctionTest, TimestampExceptions) { +// ctx.timestamp = 42; +// EXPECT_THROW(EvaluateFunction("TIMESTAMP", 1).ValueInt(), ExpressionRuntimeException); +// } +// +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"); +} + +// TEST_F(FunctionTest, Date) { +// const auto unix_epoch = memgraph::utils::Date({1970, 1, 1}); +// EXPECT_EQ(EvaluateFunction("DATE", "1970-01-01").ValueDate(), unix_epoch); +// const auto map_param = TypedValue( +// std::map{{"year", TypedValue(1970)}, {"month", TypedValue(1)}, {"day", +// TypedValue(1)}}); +// EXPECT_EQ(EvaluateFunction("DATE", map_param).ValueDate(), unix_epoch); +// const auto today = memgraph::utils::CurrentDate(); +// EXPECT_EQ(EvaluateFunction("DATE").ValueDate(), today); +// +// EXPECT_THROW(EvaluateFunction("DATE", "{}"), memgraph::utils::BasicException); +// EXPECT_THROW(EvaluateFunction("DATE", std::map{{"years", TypedValue(1970)}}), +// ExpressionRuntimeException); +// EXPECT_THROW(EvaluateFunction("DATE", std::map{{"mnths", TypedValue(1970)}}), +// ExpressionRuntimeException); +// EXPECT_THROW(EvaluateFunction("DATE", std::map{{"dayz", TypedValue(1970)}}), +// ExpressionRuntimeException); +// } +// +// TEST_F(FunctionTest, LocalTime) { +// const auto local_time = memgraph::utils::LocalTime({13, 3, 2, 0, 0}); +// EXPECT_EQ(EvaluateFunction("LOCALTIME", "130302").ValueLocalTime(), local_time); +// const auto one_sec_in_microseconds = 1000000; +// const auto map_param = TypedValue(std::map{{"hour", TypedValue(1)}, +// {"minute", TypedValue(2)}, +// {"second", TypedValue(3)}, +// {"millisecond", TypedValue(4)}, +// {"microsecond", TypedValue(5)}}); +// EXPECT_EQ(EvaluateFunction("LOCALTIME", map_param).ValueLocalTime(), memgraph::utils::LocalTime({1, 2, 3, 4, 5})); +// const auto today = memgraph::utils::CurrentLocalTime(); +// EXPECT_NEAR(EvaluateFunction("LOCALTIME").ValueLocalTime().MicrosecondsSinceEpoch(), +// today.MicrosecondsSinceEpoch(), +// one_sec_in_microseconds); +// +// EXPECT_THROW(EvaluateFunction("LOCALTIME", "{}"), memgraph::utils::BasicException); +// EXPECT_THROW(EvaluateFunction("LOCALTIME", TypedValue(std::map{{"hous", +// TypedValue(1970)}})), +// ExpressionRuntimeException); +// EXPECT_THROW( +// EvaluateFunction("LOCALTIME", TypedValue(std::map{{"minut", TypedValue(1970)}})), +// ExpressionRuntimeException); +// EXPECT_THROW( +// EvaluateFunction("LOCALTIME", TypedValue(std::map{{"seconds", TypedValue(1970)}})), +// ExpressionRuntimeException); +// } +// +// TEST_F(FunctionTest, LocalDateTime) { +// const auto local_date_time = memgraph::utils::LocalDateTime({1970, 1, 1}, {13, 3, 2, 0, 0}); +// EXPECT_EQ(EvaluateFunction("LOCALDATETIME", "1970-01-01T13:03:02").ValueLocalDateTime(), local_date_time); +// const auto today = memgraph::utils::CurrentLocalDateTime(); +// const auto one_sec_in_microseconds = 1000000; +// const auto map_param = TypedValue(std::map{{"year", TypedValue(1972)}, +// {"month", TypedValue(2)}, +// {"day", TypedValue(3)}, +// {"hour", TypedValue(4)}, +// {"minute", TypedValue(5)}, +// {"second", TypedValue(6)}, +// {"millisecond", TypedValue(7)}, +// {"microsecond", TypedValue(8)}}); +// +// EXPECT_EQ(EvaluateFunction("LOCALDATETIME", map_param).ValueLocalDateTime(), +// memgraph::utils::LocalDateTime({1972, 2, 3}, {4, 5, 6, 7, 8})); +// EXPECT_NEAR(EvaluateFunction("LOCALDATETIME").ValueLocalDateTime().MicrosecondsSinceEpoch(), +// today.MicrosecondsSinceEpoch(), one_sec_in_microseconds); +// EXPECT_THROW(EvaluateFunction("LOCALDATETIME", "{}"), memgraph::utils::BasicException); +// EXPECT_THROW( +// EvaluateFunction("LOCALDATETIME", TypedValue(std::map{{"hours", TypedValue(1970)}})), +// ExpressionRuntimeException); +// EXPECT_THROW( +// EvaluateFunction("LOCALDATETIME", TypedValue(std::map{{"seconds", +// TypedValue(1970)}})), ExpressionRuntimeException); +// } +// +// TEST_F(FunctionTest, Duration) { +// const auto map_param = TypedValue(std::map{{"day", TypedValue(3)}, +// {"hour", TypedValue(4)}, +// {"minute", TypedValue(5)}, +// {"second", TypedValue(6)}, +// {"millisecond", TypedValue(7)}, +// {"microsecond", TypedValue(8)}}); +// +// EXPECT_EQ(EvaluateFunction("DURATION", map_param).ValueDuration(), memgraph::utils::Duration({3, 4, 5, 6, 7, 8})); +// EXPECT_THROW(EvaluateFunction("DURATION", "{}"), memgraph::utils::BasicException); +// EXPECT_THROW(EvaluateFunction("DURATION", TypedValue(std::map{{"hours", +// TypedValue(1970)}})), +// ExpressionRuntimeException); +// EXPECT_THROW( +// EvaluateFunction("DURATION", TypedValue(std::map{{"seconds", TypedValue(1970)}})), +// ExpressionRuntimeException); +// +// const auto map_param_negative = TypedValue(std::map{{"day", TypedValue(-3)}, +// {"hour", TypedValue(-4)}, +// {"minute", TypedValue(-5)}, +// {"second", TypedValue(-6)}, +// {"millisecond", TypedValue(-7)}, +// {"microsecond", TypedValue(-8)}}); +// EXPECT_EQ(EvaluateFunction("DURATION", map_param_negative).ValueDuration(), +// memgraph::utils::Duration({-3, -4, -5, -6, -7, -8})); +// +// EXPECT_EQ(EvaluateFunction("DURATION", "P4DT4H5M6.2S").ValueDuration(), +// memgraph::utils::Duration({4, 4, 5, 6, 0, 200000})); +// EXPECT_EQ(EvaluateFunction("DURATION", "P3DT4H5M6.100S").ValueDuration(), +// memgraph::utils::Duration({3, 4, 5, 6, 0, 100000})); +// EXPECT_EQ(EvaluateFunction("DURATION", "P3DT4H5M6.100110S").ValueDuration(), +// memgraph::utils::Duration({3, 4, 5, 6, 100, 110})); +// } +} // namespace From 9e81fe791cfaf5bfbf7ab321f5f7325518f6b7dd Mon Sep 17 00:00:00 2001 From: Kostas Kyrimis Date: Mon, 14 Nov 2022 21:22:28 +0200 Subject: [PATCH 04/30] Fix clang-tidy warnings, remove commented out code and add HasLabel functions in ShardRequestManager to avoid throwing bad optional on expression evaluator --- src/expr/interpret/eval.hpp | 2 +- src/functions/awesome_memgraph_functions.hpp | 152 ++--- src/query/v2/accessors.cpp | 9 +- src/query/v2/shard_request_manager.hpp | 9 + tests/unit/query_v2_expression_evaluator.cpp | 550 +++---------------- 5 files changed, 165 insertions(+), 557 deletions(-) diff --git a/src/expr/interpret/eval.hpp b/src/expr/interpret/eval.hpp index 139e37514..8b8b5a7c7 100644 --- a/src/expr/interpret/eval.hpp +++ b/src/expr/interpret/eval.hpp @@ -417,7 +417,7 @@ class ExpressionEvaluator : public ExpressionVisitor { 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 has_label; } TypedValue Visit(LabelsTest &labels_test) override { diff --git a/src/functions/awesome_memgraph_functions.hpp b/src/functions/awesome_memgraph_functions.hpp index ca415c69b..0a2b873c2 100644 --- a/src/functions/awesome_memgraph_functions.hpp +++ b/src/functions/awesome_memgraph_functions.hpp @@ -61,12 +61,10 @@ template NameToFunction(const std::string &function_name); -namespace { -const char kStartsWith[] = "STARTSWITH"; -const char kEndsWith[] = "ENDSWITH"; -const char kContains[] = "CONTAINS"; -const char kId[] = "ID"; -} // namespace +inline const char kStartsWith[] = "STARTSWITH"; +inline const char kEndsWith[] = "ENDSWITH"; +inline const char kContains[] = "CONTAINS"; +inline const char kId[] = "ID"; } // namespace memgraph::functions @@ -484,11 +482,11 @@ TypedValueT Properties(const TypedValueT *args, int64_t nargs, const FunctionCon const auto &value = args[0]; if (value.IsNull()) { return TypedValueT(ctx.memory); - } else if (value.IsVertex()) { - return get_properties(value.ValueVertex()); - } else { - return get_properties(value.ValueEdge()); } + if (value.IsVertex()) { + return get_properties(value.ValueVertex()); + } + return get_properties(value.ValueEdge()); } template @@ -497,17 +495,19 @@ TypedValueT Size(const TypedValueT *args, int64_t nargs, const FunctionContextT const auto &value = args[0]; if (value.IsNull()) { return TypedValueT(ctx.memory); - } else if (value.IsList()) { + } + if (value.IsList()) { return TypedValueT(static_cast(value.ValueList().size()), ctx.memory); - } else if (value.IsString()) { + } + if (value.IsString()) { return TypedValueT(static_cast(value.ValueString().size()), ctx.memory); - } else if (value.IsMap()) { + } + 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(value.ValueMap().size()), ctx.memory); - } else { - return TypedValueT(static_cast(value.ValuePath().edges().size()), ctx.memory); } + return TypedValueT(static_cast(value.ValuePath().edges().size()), ctx.memory); } template @@ -527,9 +527,9 @@ TypedValueT StartNode(const TypedValueT *args, int64_t nargs, const FunctionCont } } -namespace { - -size_t UnwrapDegreeResult(storage::v3::Result maybe_degree) { +// 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::Result maybe_degree) { if (maybe_degree.HasError()) { switch (maybe_degree.GetError()) { case storage::v3::Error::DELETED_OBJECT: @@ -546,8 +546,6 @@ size_t UnwrapDegreeResult(storage::v3::Result maybe_degree) { return *maybe_degree; } -} // namespace - template TypedValueT Degree(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { FType>("degree", args, nargs); @@ -599,18 +597,19 @@ TypedValueT ToBoolean(const TypedValueT *args, int64_t nargs, const FunctionCont const auto &value = args[0]; if (value.IsNull()) { return TypedValueT(ctx.memory); - } else if (value.IsBool()) { - return TypedValueT(value.ValueBool(), ctx.memory); - } else if (value.IsInt()) { - return TypedValueT(value.ValueInt() != 0L, ctx.memory); - } else { - 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); } + 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 @@ -619,16 +618,17 @@ TypedValueT ToFloat(const TypedValueT *args, int64_t nargs, const FunctionContex const auto &value = args[0]; if (value.IsNull()) { return TypedValueT(ctx.memory); - } else if (value.IsInt()) { + } + if (value.IsInt()) { return TypedValueT(static_cast(value.ValueInt()), ctx.memory); - } else if (value.IsDouble()) { + } + if (value.IsDouble()) { return TypedValueT(value, ctx.memory); - } else { - try { - return TypedValueT(utils::ParseDouble(utils::Trim(value.ValueString())), ctx.memory); - } catch (const utils::BasicException &) { - return TypedValueT(ctx.memory); - } + } + try { + return TypedValueT(utils::ParseDouble(utils::Trim(value.ValueString())), ctx.memory); + } catch (const utils::BasicException &) { + return TypedValueT(ctx.memory); } } @@ -638,20 +638,22 @@ TypedValueT ToInteger(const TypedValueT *args, int64_t nargs, const FunctionCont const auto &value = args[0]; if (value.IsNull()) { return TypedValueT(ctx.memory); - } else if (value.IsBool()) { + } + if (value.IsBool()) { return TypedValueT(value.ValueBool() ? 1L : 0L, ctx.memory); - } else if (value.IsInt()) { + } + if (value.IsInt()) { return TypedValueT(value, ctx.memory); - } else if (value.IsDouble()) { + } + if (value.IsDouble()) { return TypedValueT(static_cast(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 TypedValueT(static_cast(utils::ParseDouble(utils::Trim(value.ValueString()))), ctx.memory); - } catch (const utils::BasicException &) { - return TypedValueT(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(utils::ParseDouble(utils::Trim(value.ValueString()))), ctx.memory); + } catch (const utils::BasicException &) { + return TypedValueT(ctx.memory); } } @@ -736,11 +738,11 @@ TypedValueT Keys(const TypedValueT *args, int64_t nargs, const FunctionContextT const auto &value = args[0]; if (value.IsNull()) { return TypedValueT(ctx.memory); - } else if (value.IsVertex()) { - return get_keys(value.ValueVertex()); - } else { - return get_keys(value.ValueEdge()); } + if (value.IsVertex()) { + return get_keys(value.ValueVertex()); + } + return get_keys(value.ValueEdge()); } template @@ -853,13 +855,14 @@ TypedValueT Abs(const TypedValueT *args, int64_t nargs, const FunctionContextT & const auto &value = args[0]; if (value.IsNull()) { return TypedValueT(ctx.memory); - } else if (value.IsInt()) { - return TypedValueT(std::abs(value.ValueInt()), ctx.memory); - } else { - return TypedValueT(std::abs(value.ValueDouble()), 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 \ TypedValueT name(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { \ @@ -867,11 +870,11 @@ TypedValueT Abs(const TypedValueT *args, int64_t nargs, const FunctionContextT & const auto &value = args[0]; \ if (value.IsNull()) { \ return TypedValueT(ctx.memory); \ - } else if (value.IsInt()) { \ - return TypedValueT(lowercased_name(value.ValueInt()), ctx.memory); \ - } else { \ - return TypedValueT(lowercased_name(value.ValueDouble()), 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) @@ -899,9 +902,8 @@ TypedValueT Atan2(const TypedValueT *args, int64_t nargs, const FunctionContextT auto to_double = [](const TypedValueT &t) -> double { if (t.IsInt()) { return t.ValueInt(); - } else { - return t.ValueDouble(); } + return t.ValueDouble(); }; double y = to_double(args[0]); double x = to_double(args[1]); @@ -915,11 +917,11 @@ TypedValueT Sign(const TypedValueT *args, int64_t nargs, const FunctionContextT const auto &value = args[0]; if (value.IsNull()) { return TypedValueT(ctx.memory); - } else if (value.IsInt()) { - return sign(value.ValueInt()); - } else { - return sign(value.ValueDouble()); } + if (value.IsInt()) { + return sign(value.ValueInt()); + } + return sign(value.ValueDouble()); } template @@ -962,7 +964,7 @@ struct StartsWithPredicate { }; template -inline auto StartsWith = StringMatchOperator, TypedValueT, FunctionContextT>; +inline const auto StartsWith = StringMatchOperator, TypedValueT, FunctionContextT>; // Check if s1 ends with s2. template @@ -975,7 +977,7 @@ struct EndsWithPredicate { }; template -inline auto EndsWith = StringMatchOperator, TypedValueT, FunctionContextT>; +inline const auto EndsWith = StringMatchOperator, TypedValueT, FunctionContextT>; // Check if s1 contains s2. template @@ -988,7 +990,7 @@ struct ContainsPredicate { }; template -inline auto Contains = StringMatchOperator, TypedValueT, FunctionContextT>; +inline const auto Contains = StringMatchOperator, TypedValueT, FunctionContextT>; template TypedValueT Assert(const TypedValueT *args, int64_t nargs, const FunctionContextT &ctx) { @@ -1026,11 +1028,11 @@ TypedValueT Id(const TypedValueT *args, int64_t nargs, const FunctionContextT &c const auto &arg = args[0]; if (arg.IsNull()) { return TypedValueT(ctx.memory); - } else if (arg.IsVertex()) { - return TypedValueT(static_cast(arg.ValueVertex().CypherId()), ctx.memory); - } else { - return TypedValueT(static_cast(arg.ValueEdge().CypherId()), ctx.memory); } + if (arg.IsVertex()) { + return TypedValueT(static_cast(arg.ValueVertex().CypherId()), ctx.memory); + } + return TypedValueT(static_cast(arg.ValueEdge().CypherId()), ctx.memory); } template diff --git a/src/query/v2/accessors.cpp b/src/query/v2/accessors.cpp index cbdce89e0..63915327f 100644 --- a/src/query/v2/accessors.cpp +++ b/src/query/v2/accessors.cpp @@ -23,11 +23,11 @@ EdgeTypeId EdgeAccessor::EdgeType() const { return edge.type.id; } const std::vector> &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()) { + if (!manager_->HasProperty(prop_name)) { return {}; } + 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; }); return it->second; } @@ -88,6 +88,9 @@ Value VertexAccessor::GetProperty(PropertyId prop_id) const { // NOLINTNEXTLINE(readability-convert-member-functions-to-static) Value VertexAccessor::GetProperty(const std::string &prop_name) const { + if (!manager_->HasProperty(prop_name)) { + return {}; + } return GetProperty(manager_->NameToProperty(prop_name)); } diff --git a/src/query/v2/shard_request_manager.hpp b/src/query/v2/shard_request_manager.hpp index a73201046..003cafaf8 100644 --- a/src/query/v2/shard_request_manager.hpp +++ b/src/query/v2/shard_request_manager.hpp @@ -129,6 +129,9 @@ class ShardRequestManagerInterface { 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 bool HasProperty(const std::string &name) const = 0; + virtual bool HasEdgeType(const std::string &name) const = 0; + virtual bool HasLabel(const std::string &name) const = 0; virtual bool IsPrimaryLabel(LabelId label) const = 0; virtual bool IsPrimaryKey(LabelId primary_label, PropertyId property) const = 0; }; @@ -353,6 +356,12 @@ class ShardRequestManager : public ShardRequestManagerInterface { return result_rows; } + bool HasProperty(const std::string &name) const override { return shards_map_.GetPropertyId(name).has_value(); } + + bool HasEdgeType(const std::string &name) const override { return shards_map_.GetEdgeTypeId(name).has_value(); } + + bool HasLabel(const std::string &name) const override { return shards_map_.GetLabelId(name).has_value(); } + private: enum class PaginatedResponseState { Pending, PartiallyFinished }; diff --git a/tests/unit/query_v2_expression_evaluator.cpp b/tests/unit/query_v2_expression_evaluator.cpp index 6bc6e1c8b..97122ff0f 100644 --- a/tests/unit/query_v2_expression_evaluator.cpp +++ b/tests/unit/query_v2_expression_evaluator.cpp @@ -30,6 +30,7 @@ #include "query/v2/frontend/ast/ast.hpp" #include "query/v2/requests.hpp" #include "query/v2/shard_request_manager.hpp" +#include "storage/v3/property_value.hpp" #include "storage/v3/storage.hpp" #include "utils/exceptions.hpp" #include "utils/string.hpp" @@ -117,6 +118,12 @@ class MockedShardRequestManager : public memgraph::msgs::ShardRequestManagerInte return edge_types_.IdToName(id.AsUint()); } + bool HasProperty(const std::string &name) const override { return shards_map_.GetPropertyId(name).has_value(); } + + bool HasEdgeType(const std::string &name) const override { return shards_map_.GetEdgeTypeId(name).has_value(); } + + bool HasLabel(const std::string &name) const override { return shards_map_.GetLabelId(name).has_value(); } + bool IsPrimaryLabel(LabelId label) const override { return true; } bool IsPrimaryKey(LabelId primary_label, PropertyId property) const override { return true; } @@ -202,10 +209,6 @@ class ExpressionEvaluatorTest : public ::testing::Test { ExpressionEvaluatorTest() {} protected: - // memgraph::storage::Storage db; - // memgraph::storage::Storage::Accessor storage_dba{db.Access()}; - // memgraph::query::DbAccessor dba{&storage_dba}; - // AstStorage storage; memgraph::utils::MonotonicBufferResource mem{1024}; EvaluationContext ctx{.memory = &mem, .timestamp = memgraph::query::v2::QueryTimestamp()}; @@ -575,36 +578,33 @@ TEST_F(ExpressionEvaluatorTest, VertexAndEdgeIndexing) { // TODO(kostasrim) Investigate // Shall we return null on missing properties? Or shall we throw bad optional access as we do now? - // { - // // Legal indexing, non-existing key. - // auto *op1 = storage.Create(vertex_id, storage.Create("blah")); - // auto value1 = Eval(op1); - // EXPECT_TRUE(value1.IsNull()); - // - // auto *op2 = storage.Create(edge_id, storage.Create("blah")); - // auto value2 = Eval(op2); - // EXPECT_TRUE(value2.IsNull()); - // } - // { - // // Wrong key type. - // auto *op1 = storage.Create(vertex_id, storage.Create(1)); - // EXPECT_THROW(Eval(op1), ExpressionRuntimeException); - // - // auto *op2 = storage.Create(edge_id, storage.Create(1)); - // EXPECT_THROW(Eval(op2), ExpressionRuntimeException); - // } - // { - // // Indexing with Null. - // auto *op1 = storage.Create(vertex_id, - // storage.Create(memgraph::storage::PropertyValue())); - // auto value1 = Eval(op1); - // EXPECT_TRUE(value1.IsNull()); - // - // auto *op2 = storage.Create(edge_id, - // storage.Create(memgraph::storage::PropertyValue())); - // auto value2 = Eval(op2); - // EXPECT_TRUE(value2.IsNull()); - // } + { + // Legal indexing, non-existing key. + auto *op1 = storage.Create(vertex_id, storage.Create("blah")); + auto value1 = Eval(op1); + EXPECT_TRUE(value1.IsNull()); + auto *op2 = storage.Create(edge_id, storage.Create("blah")); + auto value2 = Eval(op2); + EXPECT_TRUE(value2.IsNull()); + } + { + // Wrong key type. + auto *op1 = storage.Create(vertex_id, storage.Create(1)); + EXPECT_THROW(Eval(op1), ExpressionRuntimeException); + + auto *op2 = storage.Create(edge_id, storage.Create(1)); + EXPECT_THROW(Eval(op2), ExpressionRuntimeException); + } + { + // Indexing with Null. + auto *op1 = storage.Create(vertex_id, storage.Create(TypedValue{})); + auto value1 = Eval(op1); + EXPECT_TRUE(value1.IsNull()); + + auto *op2 = storage.Create(edge_id, storage.Create(TypedValue{})); + auto value2 = Eval(op2); + EXPECT_TRUE(value2.IsNull()); + } } TEST_F(ExpressionEvaluatorTest, ListSlicingOperator) { @@ -741,41 +741,31 @@ TEST_F(ExpressionEvaluatorTest, IsNullOperator) { ASSERT_EQ(val2.ValueBool(), true); } -// TEST_F(ExpressionEvaluatorTest, LabelsTest) { -// auto v1 = dba.InsertVertex(); -// ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("ANIMAL")).HasValue()); -// ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("DOG")).HasValue()); -// ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("NICE_DOG")).HasValue()); -// dba.AdvanceCommand(); -// auto *identifier = storage.Create("n"); -// auto node_symbol = symbol_table.CreateSymbol("n", true); -// identifier->MapTo(node_symbol); -// frame[node_symbol] = TypedValue(v1); -// { -// auto *op = storage.Create( -// identifier, std::vector{storage.GetLabelIx("DOG"), storage.GetLabelIx("ANIMAL")}); -// auto value = Eval(op); -// EXPECT_EQ(value.ValueBool(), true); -// } -// { -// auto *op = storage.Create( -// identifier, -// std::vector{storage.GetLabelIx("DOG"), storage.GetLabelIx("BAD_DOG"), -// storage.GetLabelIx("ANIMAL")}); -// auto value = Eval(op); -// EXPECT_EQ(value.ValueBool(), false); -// } -// { -// frame[node_symbol] = TypedValue(); -// auto *op = storage.Create( -// identifier, -// std::vector{storage.GetLabelIx("DOG"), storage.GetLabelIx("BAD_DOG"), -// storage.GetLabelIx("ANIMAL")}); -// auto value = Eval(op); -// EXPECT_TRUE(value.IsNull()); -// } -// } -// +TEST_F(ExpressionEvaluatorTest, LabelsTest) { + Label label{shard_manager->NameToLabel("label1")}; + auto v1 = CreateVertex({}, shard_manager.get(), label); + auto *identifier = storage.Create("n"); + auto node_symbol = symbol_table.CreateSymbol("n", true); + identifier->MapTo(node_symbol); + frame[node_symbol] = TypedValue(v1); + { + auto *op = storage.Create(identifier, std::vector{LabelIx{"label1", label.id.AsInt()}}); + auto value = Eval(op); + EXPECT_EQ(value.ValueBool(), true); + } + { + auto *op = storage.Create(identifier, std::vector{LabelIx{"label2", 10}}); + auto value = Eval(op); + EXPECT_EQ(value.ValueBool(), false); + } + { + auto *op = storage.Create(identifier, std::vector{LabelIx{"label2", 10}}); + frame[node_symbol] = TypedValue(); + auto value = Eval(op); + EXPECT_TRUE(value.IsNull()); + } +} + TEST_F(ExpressionEvaluatorTest, Aggregation) { auto aggr = storage.Create(storage.Create(42), nullptr, Aggregation::Op::COUNT); auto aggr_sym = symbol_table.CreateSymbol("aggr", true); @@ -794,11 +784,8 @@ TEST_F(ExpressionEvaluatorTest, ListLiteral) { 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) { @@ -1148,167 +1135,16 @@ class ExpressionEvaluatorPropertyLookup : public ExpressionEvaluatorTest { } }; +// TODO(kostasrim) These will fail because of memory resource not propagating correctly. This should be done as part of +// polishing the allocators. // TEST_F(ExpressionEvaluatorPropertyLookup, Vertex) { -// auto v1 = dba.InsertVertex(); -// ASSERT_TRUE(v1.SetProperty(prop_age.second, memgraph::storage::PropertyValue(10)).HasValue()); -// dba.AdvanceCommand(); +// auto v1 = CreateVertex({{prop_age.second, memgraph::msgs::Value(static_cast(32))}}, shard_manager.get()); // frame[symbol] = TypedValue(v1); // EXPECT_EQ(Value(prop_age).ValueInt(), 10); // EXPECT_TRUE(Value(prop_height).IsNull()); // } -// -// TEST_F(ExpressionEvaluatorPropertyLookup, Duration) { -// const memgraph::utils::Duration dur({10, 1, 30, 2, 22, 45}); -// frame[symbol] = TypedValue(dur); -// -// const std::pair day = std::make_pair("day", dba.NameToProperty("day")); -// const auto total_days = Value(day); -// EXPECT_TRUE(total_days.IsInt()); -// EXPECT_EQ(total_days.ValueInt(), 10); -// -// const std::pair hour = std::make_pair("hour", dba.NameToProperty("hour")); -// const auto total_hours = Value(hour); -// EXPECT_TRUE(total_hours.IsInt()); -// EXPECT_EQ(total_hours.ValueInt(), 1); -// -// const std::pair minute = std::make_pair("minute", dba.NameToProperty("minute")); -// const auto total_mins = Value(minute); -// EXPECT_TRUE(total_mins.IsInt()); -// -// EXPECT_EQ(total_mins.ValueInt(), 1 * 60 + 30); -// -// const std::pair sec = std::make_pair("second", dba.NameToProperty("second")); -// const auto total_secs = Value(sec); -// EXPECT_TRUE(total_secs.IsInt()); -// const auto expected_secs = total_mins.ValueInt() * 60 + 2; -// EXPECT_EQ(total_secs.ValueInt(), expected_secs); -// -// const std::pair milli = std::make_pair("millisecond", dba.NameToProperty("millisecond")); -// const auto total_milli = Value(milli); -// EXPECT_TRUE(total_milli.IsInt()); -// const auto expected_milli = total_secs.ValueInt() * 1000 + 22; -// EXPECT_EQ(total_milli.ValueInt(), expected_milli); -// -// const std::pair micro = std::make_pair("microsecond", dba.NameToProperty("microsecond")); -// const auto total_micros = Value(micro); -// EXPECT_TRUE(total_micros.IsInt()); -// const auto expected_micros = expected_milli * 1000 + 45; -// EXPECT_EQ(total_micros.ValueInt(), expected_micros); -// -// const std::pair nano = std::make_pair("nanosecond", dba.NameToProperty("nanosecond")); -// const auto total_nano = Value(nano); -// EXPECT_TRUE(total_nano.IsInt()); -// const auto expected_nano = expected_micros * 1000; -// EXPECT_EQ(total_nano.ValueInt(), expected_nano); -// } -// -// TEST_F(ExpressionEvaluatorPropertyLookup, Date) { -// const memgraph::utils::Date date({1996, 11, 22}); -// frame[symbol] = TypedValue(date); -// -// const std::pair year = std::make_pair("year", dba.NameToProperty("year")); -// const auto y = Value(year); -// EXPECT_TRUE(y.IsInt()); -// EXPECT_EQ(y.ValueInt(), 1996); -// -// const std::pair month = std::make_pair("month", dba.NameToProperty("month")); -// const auto m = Value(month); -// EXPECT_TRUE(m.IsInt()); -// EXPECT_EQ(m.ValueInt(), 11); -// -// const std::pair day = std::make_pair("day", dba.NameToProperty("day")); -// const auto d = Value(day); -// EXPECT_TRUE(d.IsInt()); -// EXPECT_EQ(d.ValueInt(), 22); -// } -// -// TEST_F(ExpressionEvaluatorPropertyLookup, LocalTime) { -// const memgraph::utils::LocalTime lt({1, 2, 3, 11, 22}); -// frame[symbol] = TypedValue(lt); -// -// const std::pair hour = std::make_pair("hour", dba.NameToProperty("hour")); -// const auto h = Value(hour); -// EXPECT_TRUE(h.IsInt()); -// EXPECT_EQ(h.ValueInt(), 1); -// -// const std::pair minute = std::make_pair("minute", dba.NameToProperty("minute")); -// const auto min = Value(minute); -// EXPECT_TRUE(min.IsInt()); -// EXPECT_EQ(min.ValueInt(), 2); -// -// const std::pair second = std::make_pair("second", dba.NameToProperty("second")); -// const auto sec = Value(second); -// EXPECT_TRUE(sec.IsInt()); -// EXPECT_EQ(sec.ValueInt(), 3); -// -// const std::pair millis = std::make_pair("millisecond", dba.NameToProperty("millisecond")); -// const auto mil = Value(millis); -// EXPECT_TRUE(mil.IsInt()); -// EXPECT_EQ(mil.ValueInt(), 11); -// -// const std::pair micros = std::make_pair("microsecond", dba.NameToProperty("microsecond")); -// const auto mic = Value(micros); -// EXPECT_TRUE(mic.IsInt()); -// EXPECT_EQ(mic.ValueInt(), 22); -// } -// -// TEST_F(ExpressionEvaluatorPropertyLookup, LocalDateTime) { -// const memgraph::utils::LocalDateTime ldt({1993, 8, 6}, {2, 3, 4, 55, 40}); -// frame[symbol] = TypedValue(ldt); -// -// const std::pair year = std::make_pair("year", dba.NameToProperty("year")); -// const auto y = Value(year); -// EXPECT_TRUE(y.IsInt()); -// EXPECT_EQ(y.ValueInt(), 1993); -// -// const std::pair month = std::make_pair("month", dba.NameToProperty("month")); -// const auto m = Value(month); -// EXPECT_TRUE(m.IsInt()); -// EXPECT_EQ(m.ValueInt(), 8); -// -// const std::pair day = std::make_pair("day", dba.NameToProperty("day")); -// const auto d = Value(day); -// EXPECT_TRUE(d.IsInt()); -// EXPECT_EQ(d.ValueInt(), 6); -// -// const std::pair hour = std::make_pair("hour", dba.NameToProperty("hour")); -// const auto h = Value(hour); -// EXPECT_TRUE(h.IsInt()); -// EXPECT_EQ(h.ValueInt(), 2); -// -// const std::pair minute = std::make_pair("minute", dba.NameToProperty("minute")); -// const auto min = Value(minute); -// EXPECT_TRUE(min.IsInt()); -// EXPECT_EQ(min.ValueInt(), 3); -// -// const std::pair second = std::make_pair("second", dba.NameToProperty("second")); -// const auto sec = Value(second); -// EXPECT_TRUE(sec.IsInt()); -// EXPECT_EQ(sec.ValueInt(), 4); -// -// const std::pair millis = std::make_pair("millisecond", dba.NameToProperty("millisecond")); -// const auto mil = Value(millis); -// EXPECT_TRUE(mil.IsInt()); -// EXPECT_EQ(mil.ValueInt(), 55); -// -// const std::pair micros = std::make_pair("microsecond", dba.NameToProperty("microsecond")); -// const auto mic = Value(micros); -// EXPECT_TRUE(mic.IsInt()); -// EXPECT_EQ(mic.ValueInt(), 40); -// } -// -// TEST_F(ExpressionEvaluatorPropertyLookup, Edge) { -// auto v1 = dba.InsertVertex(); -// auto v2 = dba.InsertVertex(); -// auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("edge_type")); -// ASSERT_TRUE(e12.HasValue()); -// ASSERT_TRUE(e12->SetProperty(prop_age.second, memgraph::storage::PropertyValue(10)).HasValue()); -// dba.AdvanceCommand(); -// frame[symbol] = TypedValue(*e12); -// EXPECT_EQ(Value(prop_age).ValueInt(), 10); -// EXPECT_TRUE(Value(prop_height).IsNull()); -// } -// +// TEST_F(ExpressionEvaluatorPropertyLookup, Edge) {} + TEST_F(ExpressionEvaluatorPropertyLookup, Null) { frame[symbol] = TypedValue(); EXPECT_TRUE(Value(prop_age).IsNull()); @@ -1455,59 +1291,10 @@ TEST_F(FunctionTest, StartNode) { } // TODO(kostasrim) Enable this test once we add degree to the accessors -// TEST_F(FunctionTest, Degree) { -// ASSERT_THROW(EvaluateFunction("DEGREE"), ExpressionRuntimeException); -// ASSERT_TRUE(EvaluateFunction("DEGREE", TypedValue()).IsNull()); -// auto v1 = dba.InsertVertex(); -// auto v2 = dba.InsertVertex(); -// auto v3 = dba.InsertVertex(); -// auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t")); -// ASSERT_TRUE(e12.HasValue()); -// ASSERT_TRUE(dba.InsertEdge(&v3, &v2, dba.NameToEdgeType("t")).HasValue()); -// dba.AdvanceCommand(); -// ASSERT_EQ(EvaluateFunction("DEGREE", v1).ValueInt(), 1); -// ASSERT_EQ(EvaluateFunction("DEGREE", v2).ValueInt(), 2); -// ASSERT_EQ(EvaluateFunction("DEGREE", v3).ValueInt(), 1); -// ASSERT_THROW(EvaluateFunction("DEGREE", 2), ExpressionRuntimeException); -// ASSERT_THROW(EvaluateFunction("DEGREE", *e12), ExpressionRuntimeException); -// } -// -// TODO(kostasrim) Enable this test once we add InDegree to the accessors -// TEST_F(FunctionTest, InDegree) { -// ASSERT_THROW(EvaluateFunction("INDEGREE"), ExpressionRuntimeException); -// ASSERT_TRUE(EvaluateFunction("INDEGREE", TypedValue()).IsNull()); -// auto v1 = dba.InsertVertex(); -// auto v2 = dba.InsertVertex(); -// auto v3 = dba.InsertVertex(); -// auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t")); -// ASSERT_TRUE(e12.HasValue()); -// ASSERT_TRUE(dba.InsertEdge(&v3, &v2, dba.NameToEdgeType("t")).HasValue()); -// dba.AdvanceCommand(); -// ASSERT_EQ(EvaluateFunction("INDEGREE", v1).ValueInt(), 0); -// ASSERT_EQ(EvaluateFunction("INDEGREE", v2).ValueInt(), 2); -// ASSERT_EQ(EvaluateFunction("INDEGREE", v3).ValueInt(), 0); -// ASSERT_THROW(EvaluateFunction("INDEGREE", 2), ExpressionRuntimeException); -// ASSERT_THROW(EvaluateFunction("INDEGREE", *e12), ExpressionRuntimeException); -// } -// -// TODO(kostasrim) Enable this test once we add OutDegree to the accessors -// TEST_F(FunctionTest, OutDegree) { -// ASSERT_THROW(EvaluateFunction("OUTDEGREE"), ExpressionRuntimeException); -// ASSERT_TRUE(EvaluateFunction("OUTDEGREE", TypedValue()).IsNull()); -// auto v1 = dba.InsertVertex(); -// auto v2 = dba.InsertVertex(); -// auto v3 = dba.InsertVertex(); -// auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t")); -// ASSERT_TRUE(e12.HasValue()); -// ASSERT_TRUE(dba.InsertEdge(&v3, &v2, dba.NameToEdgeType("t")).HasValue()); -// dba.AdvanceCommand(); -// ASSERT_EQ(EvaluateFunction("OUTDEGREE", v1).ValueInt(), 1); -// ASSERT_EQ(EvaluateFunction("OUTDEGREE", v2).ValueInt(), 0); -// ASSERT_EQ(EvaluateFunction("OUTDEGREE", v3).ValueInt(), 1); -// ASSERT_THROW(EvaluateFunction("OUTDEGREE", 2), ExpressionRuntimeException); -// ASSERT_THROW(EvaluateFunction("OUTDEGREE", *e12), ExpressionRuntimeException); -// } -// +// TEST_F(FunctionTest, Degree) {} +// TEST_F(FunctionTest, InDegree) {} +// TEST_F(FunctionTest, OutDegree) {} + TEST_F(FunctionTest, ToBoolean) { ASSERT_THROW(EvaluateFunction("TOBOOLEAN"), FunctionRuntimeException); ASSERT_TRUE(EvaluateFunction("TOBOOLEAN", TypedValue()).IsNull()); @@ -1591,42 +1378,8 @@ TEST_F(FunctionTest, Labels) { } // TODO(kostasrim) Enable this once we fix accessors Path -// TEST_F(FunctionTest, NodesRelationships) { -// EXPECT_THROW(EvaluateFunction("NODES"), ExpressionRuntimeException); -// EXPECT_THROW(EvaluateFunction("RELATIONSHIPS"), ExpressionRuntimeException); -// EXPECT_TRUE(EvaluateFunction("NODES", TypedValue()).IsNull()); -// EXPECT_TRUE(EvaluateFunction("RELATIONSHIPS", TypedValue()).IsNull()); -// -// { -// auto v1 = dba.InsertVertex(); -// auto v2 = dba.InsertVertex(); -// auto v3 = dba.InsertVertex(); -// auto e1 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("Type")); -// ASSERT_TRUE(e1.HasValue()); -// auto e2 = dba.InsertEdge(&v2, &v3, dba.NameToEdgeType("Type")); -// ASSERT_TRUE(e2.HasValue()); -// memgraph::query::Path path{v1, *e1, v2, *e2, v3}; -// dba.AdvanceCommand(); -// -// auto _nodes = EvaluateFunction("NODES", path).ValueList(); -// std::vector nodes; -// for (const auto &node : _nodes) { -// nodes.push_back(node.ValueVertex()); -// } -// EXPECT_THAT(nodes, ElementsAre(v1, v2, v3)); -// -// auto _edges = EvaluateFunction("RELATIONSHIPS", path).ValueList(); -// std::vector edges; -// for (const auto &edge : _edges) { -// edges.push_back(edge.ValueEdge()); -// } -// EXPECT_THAT(edges, ElementsAre(*e1, *e2)); -// } -// -// EXPECT_THROW(EvaluateFunction("NODES", 2), ExpressionRuntimeException); -// EXPECT_THROW(EvaluateFunction("RELATIONSHIPS", 2), ExpressionRuntimeException); -// } -// +// TEST_F(FunctionTest, NodesRelationships) {} + TEST_F(FunctionTest, Range) { EXPECT_THROW(EvaluateFunction("RANGE"), FunctionRuntimeException); EXPECT_TRUE(EvaluateFunction("RANGE", 1, 2, TypedValue()).IsNull()); @@ -1889,64 +1642,10 @@ TEST_F(FunctionTest, ToStringBool) { EXPECT_EQ(EvaluateFunction("TOSTRING", false).ValueString(), "false"); } -// TEST_F(FunctionTest, ToStringDate) { -// const auto date = memgraph::utils::Date({1970, 1, 2}); -// EXPECT_EQ(EvaluateFunction("TOSTRING", date).ValueString(), "1970-01-02"); -// } -// -// TEST_F(FunctionTest, ToStringLocalTime) { -// const auto lt = memgraph::utils::LocalTime({13, 2, 40, 100, 50}); -// EXPECT_EQ(EvaluateFunction("TOSTRING", lt).ValueString(), "13:02:40.100050"); -// } -// -// TEST_F(FunctionTest, ToStringLocalDateTime) { -// const auto ldt = memgraph::utils::LocalDateTime({1970, 1, 2}, {23, 02, 59}); -// EXPECT_EQ(EvaluateFunction("TOSTRING", ldt).ValueString(), "1970-01-02T23:02:59.000000"); -// } -// -// TEST_F(FunctionTest, ToStringDuration) { -// memgraph::utils::Duration duration{{.minute = 2, .second = 2, .microsecond = 33}}; -// EXPECT_EQ(EvaluateFunction("TOSTRING", duration).ValueString(), "P0DT0H2M2.000033S"); -// } -// TEST_F(FunctionTest, ToStringExceptions) { EXPECT_THROW(EvaluateFunction("TOSTRING", 1, 2, 3), FunctionRuntimeException); } -// -// TEST_F(FunctionTest, TimestampVoid) { -// ctx.timestamp = 42; -// EXPECT_EQ(EvaluateFunction("TIMESTAMP").ValueInt(), 42); -// } -// -// TEST_F(FunctionTest, TimestampDate) { -// ctx.timestamp = 42; -// EXPECT_EQ(EvaluateFunction("TIMESTAMP", memgraph::utils::Date({1970, 1, 1})).ValueInt(), 0); -// EXPECT_EQ(EvaluateFunction("TIMESTAMP", memgraph::utils::Date({1971, 1, 1})).ValueInt(), 31536000000000); -// } -// -// TEST_F(FunctionTest, TimestampLocalTime) { -// ctx.timestamp = 42; -// const memgraph::utils::LocalTime time(10000); -// EXPECT_EQ(EvaluateFunction("TIMESTAMP", time).ValueInt(), 10000); -// } -// -// TEST_F(FunctionTest, TimestampLocalDateTime) { -// ctx.timestamp = 42; -// const memgraph::utils::LocalDateTime time(20000); -// EXPECT_EQ(EvaluateFunction("TIMESTAMP", time).ValueInt(), 20000); -// } -// -// TEST_F(FunctionTest, TimestampDuration) { -// ctx.timestamp = 42; -// const memgraph::utils::Duration time(20000); -// EXPECT_EQ(EvaluateFunction("TIMESTAMP", time).ValueInt(), 20000); -// } -// -// TEST_F(FunctionTest, TimestampExceptions) { -// ctx.timestamp = 42; -// EXPECT_THROW(EvaluateFunction("TIMESTAMP", 1).ValueInt(), ExpressionRuntimeException); -// } -// + TEST_F(FunctionTest, Left) { EXPECT_THROW(EvaluateFunction("LEFT"), FunctionRuntimeException); @@ -2083,110 +1782,5 @@ TEST_F(FunctionTest, FromByteString) { EXPECT_EQ(EvaluateFunction("FROMBYTESTRING", std::string("\x00\x42", 2)).ValueString(), "0x0042"); } -// TEST_F(FunctionTest, Date) { -// const auto unix_epoch = memgraph::utils::Date({1970, 1, 1}); -// EXPECT_EQ(EvaluateFunction("DATE", "1970-01-01").ValueDate(), unix_epoch); -// const auto map_param = TypedValue( -// std::map{{"year", TypedValue(1970)}, {"month", TypedValue(1)}, {"day", -// TypedValue(1)}}); -// EXPECT_EQ(EvaluateFunction("DATE", map_param).ValueDate(), unix_epoch); -// const auto today = memgraph::utils::CurrentDate(); -// EXPECT_EQ(EvaluateFunction("DATE").ValueDate(), today); -// -// EXPECT_THROW(EvaluateFunction("DATE", "{}"), memgraph::utils::BasicException); -// EXPECT_THROW(EvaluateFunction("DATE", std::map{{"years", TypedValue(1970)}}), -// ExpressionRuntimeException); -// EXPECT_THROW(EvaluateFunction("DATE", std::map{{"mnths", TypedValue(1970)}}), -// ExpressionRuntimeException); -// EXPECT_THROW(EvaluateFunction("DATE", std::map{{"dayz", TypedValue(1970)}}), -// ExpressionRuntimeException); -// } -// -// TEST_F(FunctionTest, LocalTime) { -// const auto local_time = memgraph::utils::LocalTime({13, 3, 2, 0, 0}); -// EXPECT_EQ(EvaluateFunction("LOCALTIME", "130302").ValueLocalTime(), local_time); -// const auto one_sec_in_microseconds = 1000000; -// const auto map_param = TypedValue(std::map{{"hour", TypedValue(1)}, -// {"minute", TypedValue(2)}, -// {"second", TypedValue(3)}, -// {"millisecond", TypedValue(4)}, -// {"microsecond", TypedValue(5)}}); -// EXPECT_EQ(EvaluateFunction("LOCALTIME", map_param).ValueLocalTime(), memgraph::utils::LocalTime({1, 2, 3, 4, 5})); -// const auto today = memgraph::utils::CurrentLocalTime(); -// EXPECT_NEAR(EvaluateFunction("LOCALTIME").ValueLocalTime().MicrosecondsSinceEpoch(), -// today.MicrosecondsSinceEpoch(), -// one_sec_in_microseconds); -// -// EXPECT_THROW(EvaluateFunction("LOCALTIME", "{}"), memgraph::utils::BasicException); -// EXPECT_THROW(EvaluateFunction("LOCALTIME", TypedValue(std::map{{"hous", -// TypedValue(1970)}})), -// ExpressionRuntimeException); -// EXPECT_THROW( -// EvaluateFunction("LOCALTIME", TypedValue(std::map{{"minut", TypedValue(1970)}})), -// ExpressionRuntimeException); -// EXPECT_THROW( -// EvaluateFunction("LOCALTIME", TypedValue(std::map{{"seconds", TypedValue(1970)}})), -// ExpressionRuntimeException); -// } -// -// TEST_F(FunctionTest, LocalDateTime) { -// const auto local_date_time = memgraph::utils::LocalDateTime({1970, 1, 1}, {13, 3, 2, 0, 0}); -// EXPECT_EQ(EvaluateFunction("LOCALDATETIME", "1970-01-01T13:03:02").ValueLocalDateTime(), local_date_time); -// const auto today = memgraph::utils::CurrentLocalDateTime(); -// const auto one_sec_in_microseconds = 1000000; -// const auto map_param = TypedValue(std::map{{"year", TypedValue(1972)}, -// {"month", TypedValue(2)}, -// {"day", TypedValue(3)}, -// {"hour", TypedValue(4)}, -// {"minute", TypedValue(5)}, -// {"second", TypedValue(6)}, -// {"millisecond", TypedValue(7)}, -// {"microsecond", TypedValue(8)}}); -// -// EXPECT_EQ(EvaluateFunction("LOCALDATETIME", map_param).ValueLocalDateTime(), -// memgraph::utils::LocalDateTime({1972, 2, 3}, {4, 5, 6, 7, 8})); -// EXPECT_NEAR(EvaluateFunction("LOCALDATETIME").ValueLocalDateTime().MicrosecondsSinceEpoch(), -// today.MicrosecondsSinceEpoch(), one_sec_in_microseconds); -// EXPECT_THROW(EvaluateFunction("LOCALDATETIME", "{}"), memgraph::utils::BasicException); -// EXPECT_THROW( -// EvaluateFunction("LOCALDATETIME", TypedValue(std::map{{"hours", TypedValue(1970)}})), -// ExpressionRuntimeException); -// EXPECT_THROW( -// EvaluateFunction("LOCALDATETIME", TypedValue(std::map{{"seconds", -// TypedValue(1970)}})), ExpressionRuntimeException); -// } -// -// TEST_F(FunctionTest, Duration) { -// const auto map_param = TypedValue(std::map{{"day", TypedValue(3)}, -// {"hour", TypedValue(4)}, -// {"minute", TypedValue(5)}, -// {"second", TypedValue(6)}, -// {"millisecond", TypedValue(7)}, -// {"microsecond", TypedValue(8)}}); -// -// EXPECT_EQ(EvaluateFunction("DURATION", map_param).ValueDuration(), memgraph::utils::Duration({3, 4, 5, 6, 7, 8})); -// EXPECT_THROW(EvaluateFunction("DURATION", "{}"), memgraph::utils::BasicException); -// EXPECT_THROW(EvaluateFunction("DURATION", TypedValue(std::map{{"hours", -// TypedValue(1970)}})), -// ExpressionRuntimeException); -// EXPECT_THROW( -// EvaluateFunction("DURATION", TypedValue(std::map{{"seconds", TypedValue(1970)}})), -// ExpressionRuntimeException); -// -// const auto map_param_negative = TypedValue(std::map{{"day", TypedValue(-3)}, -// {"hour", TypedValue(-4)}, -// {"minute", TypedValue(-5)}, -// {"second", TypedValue(-6)}, -// {"millisecond", TypedValue(-7)}, -// {"microsecond", TypedValue(-8)}}); -// EXPECT_EQ(EvaluateFunction("DURATION", map_param_negative).ValueDuration(), -// memgraph::utils::Duration({-3, -4, -5, -6, -7, -8})); -// -// EXPECT_EQ(EvaluateFunction("DURATION", "P4DT4H5M6.2S").ValueDuration(), -// memgraph::utils::Duration({4, 4, 5, 6, 0, 200000})); -// EXPECT_EQ(EvaluateFunction("DURATION", "P3DT4H5M6.100S").ValueDuration(), -// memgraph::utils::Duration({3, 4, 5, 6, 0, 100000})); -// EXPECT_EQ(EvaluateFunction("DURATION", "P3DT4H5M6.100110S").ValueDuration(), -// memgraph::utils::Duration({3, 4, 5, 6, 100, 110})); -// } +// TODO(kostasrim) Add temporal type tests. } // namespace From 631d18465b5fc8b89fa6c872da2e57a87645944e Mon Sep 17 00:00:00 2001 From: Tyler Neely Date: Tue, 15 Nov 2022 17:52:38 +0000 Subject: [PATCH 05/30] Allow the RsmClient to store multiple in-flight requests. Update the ShardRequestManager to use the new request tokens and refactor some bug-prone aspects of it --- src/io/rsm/rsm_client.hpp | 131 +++++++++------ src/query/v2/shard_request_manager.hpp | 211 ++++++++++++------------- 2 files changed, 188 insertions(+), 154 deletions(-) diff --git a/src/io/rsm/rsm_client.hpp b/src/io/rsm/rsm_client.hpp index b60380b08..ce6781956 100644 --- a/src/io/rsm/rsm_client.hpp +++ b/src/io/rsm/rsm_client.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "io/address.hpp" @@ -36,6 +37,21 @@ using memgraph::io::rsm::WriteRequest; using memgraph::io::rsm::WriteResponse; using memgraph::utils::BasicResult; +class AsyncRequestToken { + size_t id_; + + public: + AsyncRequestToken(size_t id) : id_(id) {} + size_t GetId() const { return id_; } +}; + +template +struct AsyncRequest { + Time start_time; + RequestT request; + ResponseFuture future; +}; + template class RsmClient { @@ -47,13 +63,10 @@ class RsmClient { /// State for single async read/write operations. In the future this could become a map /// of async operations that can be accessed via an ID etc... - std::optional