// 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. /// @file /// Contains private (implementation) declarations and definitions for /// mg_procedure.h #pragma once #include "mg_procedure.h" #include #include #include "integrations/kafka/consumer.hpp" #include "integrations/pulsar/consumer.hpp" #include "query/context.hpp" #include "query/db_accessor.hpp" #include "query/frontend/ast/ast.hpp" #include "query/procedure/cypher_type_ptr.hpp" #include "query/typed_value.hpp" #include "storage/v2/view.hpp" #include "utils/memory.hpp" #include "utils/pmr/map.hpp" #include "utils/pmr/string.hpp" #include "utils/pmr/vector.hpp" #include "utils/temporal.hpp" #include "utils/variant_helpers.hpp" /// Wraps memory resource used in custom procedures. /// /// This should have been `using mgp_memory = memgraph::utils::MemoryResource`, but that's /// not valid C++ because we have a forward declare `struct mgp_memory` in /// mg_procedure.h /// TODO: Make this extendable in C API, so that custom procedure writer can add /// their own memory management wrappers. struct mgp_memory { memgraph::utils::MemoryResource *impl; }; /// Immutable container of various values that appear in openCypher. struct mgp_value { /// Allocator type so that STL containers are aware that we need one. using allocator_type = memgraph::utils::Allocator; // Construct MGP_VALUE_TYPE_NULL. explicit mgp_value(memgraph::utils::MemoryResource *) noexcept; mgp_value(bool, memgraph::utils::MemoryResource *) noexcept; mgp_value(int64_t, memgraph::utils::MemoryResource *) noexcept; mgp_value(double, memgraph::utils::MemoryResource *) noexcept; /// @throw std::bad_alloc mgp_value(const char *, memgraph::utils::MemoryResource *); /// Take ownership of the mgp_list, MemoryResource must match. mgp_value(mgp_list *, memgraph::utils::MemoryResource *) noexcept; /// Take ownership of the mgp_map, MemoryResource must match. mgp_value(mgp_map *, memgraph::utils::MemoryResource *) noexcept; /// Take ownership of the mgp_vertex, MemoryResource must match. mgp_value(mgp_vertex *, memgraph::utils::MemoryResource *) noexcept; /// Take ownership of the mgp_edge, MemoryResource must match. mgp_value(mgp_edge *, memgraph::utils::MemoryResource *) noexcept; /// Take ownership of the mgp_path, MemoryResource must match. mgp_value(mgp_path *, memgraph::utils::MemoryResource *) noexcept; mgp_value(mgp_date *, memgraph::utils::MemoryResource *) noexcept; mgp_value(mgp_local_time *, memgraph::utils::MemoryResource *) noexcept; mgp_value(mgp_local_date_time *, memgraph::utils::MemoryResource *) noexcept; mgp_value(mgp_duration *, memgraph::utils::MemoryResource *) noexcept; /// Construct by copying memgraph::query::TypedValue using memgraph::utils::MemoryResource. /// mgp_graph is needed to construct mgp_vertex and mgp_edge. /// @throw std::bad_alloc mgp_value(const memgraph::query::TypedValue &, mgp_graph *, memgraph::utils::MemoryResource *); /// Construct by copying memgraph::storage::PropertyValue using memgraph::utils::MemoryResource. /// @throw std::bad_alloc mgp_value(const memgraph::storage::PropertyValue &, memgraph::utils::MemoryResource *); /// Copy construction without memgraph::utils::MemoryResource is not allowed. mgp_value(const mgp_value &) = delete; /// Copy construct using given memgraph::utils::MemoryResource. /// @throw std::bad_alloc mgp_value(const mgp_value &, memgraph::utils::MemoryResource *); /// Move construct using given memgraph::utils::MemoryResource. /// @throw std::bad_alloc if MemoryResource is different, so we cannot move. mgp_value(mgp_value &&, memgraph::utils::MemoryResource *); /// Move construct, memgraph::utils::MemoryResource is inherited. mgp_value(mgp_value &&other) noexcept : mgp_value(other, other.memory) {} /// Copy-assignment is not allowed to preserve immutability. mgp_value &operator=(const mgp_value &) = delete; /// Move-assignment is not allowed to preserve immutability. mgp_value &operator=(mgp_value &&) = delete; ~mgp_value() noexcept; memgraph::utils::MemoryResource *GetMemoryResource() const noexcept { return memory; } mgp_value_type type; memgraph::utils::MemoryResource *memory; union { bool bool_v; int64_t int_v; double double_v; memgraph::utils::pmr::string string_v; // We use pointers so that taking ownership via C API is easier. Besides, // mgp_map cannot use incomplete mgp_value type, because that would be // undefined behaviour. mgp_list *list_v; mgp_map *map_v; mgp_vertex *vertex_v; mgp_edge *edge_v; mgp_path *path_v; mgp_date *date_v; mgp_local_time *local_time_v; mgp_local_date_time *local_date_time_v; mgp_duration *duration_v; }; }; inline memgraph::utils::DateParameters MapDateParameters(const mgp_date_parameters *parameters) { return {.year = parameters->year, .month = parameters->month, .day = parameters->day}; } struct mgp_date { /// Allocator type so that STL containers are aware that we need one. /// We don't actually need this, but it simplifies the C API, because we store /// the allocator which was used to allocate `this`. using allocator_type = memgraph::utils::Allocator; // Hopefully memgraph::utils::Date copy constructor remains noexcept, so that we can // have everything noexcept here. static_assert(std::is_nothrow_copy_constructible_v); mgp_date(const memgraph::utils::Date &date, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), date(date) {} mgp_date(const std::string_view string, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), date(memgraph::utils::ParseDateParameters(string).first) {} mgp_date(const mgp_date_parameters *parameters, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), date(MapDateParameters(parameters)) {} mgp_date(const int64_t microseconds, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), date(microseconds) {} mgp_date(const mgp_date &other, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), date(other.date) {} mgp_date(mgp_date &&other, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), date(other.date) {} mgp_date(mgp_date &&other) noexcept : memory(other.memory), date(other.date) {} /// Copy construction without memgraph::utils::MemoryResource is not allowed. mgp_date(const mgp_date &) = delete; mgp_date &operator=(const mgp_date &) = delete; mgp_date &operator=(mgp_date &&) = delete; ~mgp_date() = default; memgraph::utils::MemoryResource *GetMemoryResource() const noexcept { return memory; } memgraph::utils::MemoryResource *memory; memgraph::utils::Date date; }; inline memgraph::utils::LocalTimeParameters MapLocalTimeParameters(const mgp_local_time_parameters *parameters) { return {.hour = parameters->hour, .minute = parameters->minute, .second = parameters->second, .millisecond = parameters->millisecond, .microsecond = parameters->microsecond}; } struct mgp_local_time { /// Allocator type so that STL containers are aware that we need one. /// We don't actually need this, but it simplifies the C API, because we store /// the allocator which was used to allocate `this`. using allocator_type = memgraph::utils::Allocator; // Hopefully memgraph::utils::LocalTime copy constructor remains noexcept, so that we can // have everything noexcept here. static_assert(std::is_nothrow_copy_constructible_v); mgp_local_time(const std::string_view string, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), local_time(memgraph::utils::ParseLocalTimeParameters(string).first) {} mgp_local_time(const mgp_local_time_parameters *parameters, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), local_time(MapLocalTimeParameters(parameters)) {} mgp_local_time(const memgraph::utils::LocalTime &local_time, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), local_time(local_time) {} mgp_local_time(const int64_t microseconds, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), local_time(microseconds) {} mgp_local_time(const mgp_local_time &other, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), local_time(other.local_time) {} mgp_local_time(mgp_local_time &&other, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), local_time(other.local_time) {} mgp_local_time(mgp_local_time &&other) noexcept : memory(other.memory), local_time(other.local_time) {} /// Copy construction without memgraph::utils::MemoryResource is not allowed. mgp_local_time(const mgp_local_time &) = delete; mgp_local_time &operator=(const mgp_local_time &) = delete; mgp_local_time &operator=(mgp_local_time &&) = delete; ~mgp_local_time() = default; memgraph::utils::MemoryResource *GetMemoryResource() const noexcept { return memory; } memgraph::utils::MemoryResource *memory; memgraph::utils::LocalTime local_time; }; inline memgraph::utils::LocalDateTime CreateLocalDateTimeFromString(const std::string_view string) { const auto &[date_parameters, local_time_parameters] = memgraph::utils::ParseLocalDateTimeParameters(string); return memgraph::utils::LocalDateTime{date_parameters, local_time_parameters}; } struct mgp_local_date_time { /// Allocator type so that STL containers are aware that we need one. /// We don't actually need this, but it simplifies the C API, because we store /// the allocator which was used to allocate `this`. using allocator_type = memgraph::utils::Allocator; // Hopefully memgraph::utils::LocalDateTime copy constructor remains noexcept, so that we can // have everything noexcept here. static_assert(std::is_nothrow_copy_constructible_v); mgp_local_date_time(const memgraph::utils::LocalDateTime &local_date_time, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), local_date_time(local_date_time) {} mgp_local_date_time(const std::string_view string, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), local_date_time(CreateLocalDateTimeFromString(string)) {} mgp_local_date_time(const mgp_local_date_time_parameters *parameters, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), local_date_time(MapDateParameters(parameters->date_parameters), MapLocalTimeParameters(parameters->local_time_parameters)) {} mgp_local_date_time(const int64_t microseconds, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), local_date_time(microseconds) {} mgp_local_date_time(const mgp_local_date_time &other, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), local_date_time(other.local_date_time) {} mgp_local_date_time(mgp_local_date_time &&other, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), local_date_time(other.local_date_time) {} mgp_local_date_time(mgp_local_date_time &&other) noexcept : memory(other.memory), local_date_time(other.local_date_time) {} /// Copy construction without memgraph::utils::MemoryResource is not allowed. mgp_local_date_time(const mgp_local_date_time &) = delete; mgp_local_date_time &operator=(const mgp_local_date_time &) = delete; mgp_local_date_time &operator=(mgp_local_date_time &&) = delete; ~mgp_local_date_time() = default; memgraph::utils::MemoryResource *GetMemoryResource() const noexcept { return memory; } memgraph::utils::MemoryResource *memory; memgraph::utils::LocalDateTime local_date_time; }; inline memgraph::utils::DurationParameters MapDurationParameters(const mgp_duration_parameters *parameters) { return {.day = parameters->day, .hour = parameters->hour, .minute = parameters->minute, .second = parameters->second, .millisecond = parameters->millisecond, .microsecond = parameters->microsecond}; } struct mgp_duration { /// Allocator type so that STL containers are aware that we need one. /// We don't actually need this, but it simplifies the C API, because we store /// the allocator which was used to allocate `this`. using allocator_type = memgraph::utils::Allocator; // Hopefully memgraph::utils::Duration copy constructor remains noexcept, so that we can // have everything noexcept here. static_assert(std::is_nothrow_copy_constructible_v); mgp_duration(const std::string_view string, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), duration(memgraph::utils::ParseDurationParameters(string)) {} mgp_duration(const mgp_duration_parameters *parameters, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), duration(MapDurationParameters(parameters)) {} mgp_duration(const memgraph::utils::Duration &duration, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), duration(duration) {} mgp_duration(const int64_t microseconds, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), duration(microseconds) {} mgp_duration(const mgp_duration &other, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), duration(other.duration) {} mgp_duration(mgp_duration &&other, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), duration(other.duration) {} mgp_duration(mgp_duration &&other) noexcept : memory(other.memory), duration(other.duration) {} /// Copy construction without memgraph::utils::MemoryResource is not allowed. mgp_duration(const mgp_duration &) = delete; mgp_duration &operator=(const mgp_duration &) = delete; mgp_duration &operator=(mgp_duration &&) = delete; ~mgp_duration() = default; memgraph::utils::MemoryResource *GetMemoryResource() const noexcept { return memory; } memgraph::utils::MemoryResource *memory; memgraph::utils::Duration duration; }; struct mgp_list { /// Allocator type so that STL containers are aware that we need one. using allocator_type = memgraph::utils::Allocator; explicit mgp_list(memgraph::utils::MemoryResource *memory) : elems(memory) {} mgp_list(memgraph::utils::pmr::vector &&elems, memgraph::utils::MemoryResource *memory) : elems(std::move(elems), memory) {} mgp_list(const mgp_list &other, memgraph::utils::MemoryResource *memory) : elems(other.elems, memory) {} mgp_list(mgp_list &&other, memgraph::utils::MemoryResource *memory) : elems(std::move(other.elems), memory) {} mgp_list(mgp_list &&other) noexcept : elems(std::move(other.elems)) {} /// Copy construction without memgraph::utils::MemoryResource is not allowed. mgp_list(const mgp_list &) = delete; mgp_list &operator=(const mgp_list &) = delete; mgp_list &operator=(mgp_list &&) = delete; ~mgp_list() = default; memgraph::utils::MemoryResource *GetMemoryResource() const noexcept { return elems.get_allocator().GetMemoryResource(); } // C++17 vector can work with incomplete type. memgraph::utils::pmr::vector elems; }; struct mgp_map { /// Allocator type so that STL containers are aware that we need one. using allocator_type = memgraph::utils::Allocator; explicit mgp_map(memgraph::utils::MemoryResource *memory) : items(memory) {} mgp_map(memgraph::utils::pmr::map &&items, memgraph::utils::MemoryResource *memory) : items(std::move(items), memory) {} mgp_map(const mgp_map &other, memgraph::utils::MemoryResource *memory) : items(other.items, memory) {} mgp_map(mgp_map &&other, memgraph::utils::MemoryResource *memory) : items(std::move(other.items), memory) {} mgp_map(mgp_map &&other) noexcept : items(std::move(other.items)) {} /// Copy construction without memgraph::utils::MemoryResource is not allowed. mgp_map(const mgp_map &) = delete; mgp_map &operator=(const mgp_map &) = delete; mgp_map &operator=(mgp_map &&) = delete; ~mgp_map() = default; memgraph::utils::MemoryResource *GetMemoryResource() const noexcept { return items.get_allocator().GetMemoryResource(); } // Unfortunately using incomplete type with map is undefined, so mgp_map // needs to be defined after mgp_value. memgraph::utils::pmr::map items; }; struct mgp_map_item { const char *key; mgp_value *value; }; struct mgp_map_items_iterator { using allocator_type = memgraph::utils::Allocator; mgp_map_items_iterator(mgp_map *map, memgraph::utils::MemoryResource *memory) : memory(memory), map(map), current_it(map->items.begin()) { if (current_it != map->items.end()) { current.key = current_it->first.c_str(); current.value = ¤t_it->second; } } mgp_map_items_iterator(const mgp_map_items_iterator &) = delete; mgp_map_items_iterator(mgp_map_items_iterator &&) = delete; mgp_map_items_iterator &operator=(const mgp_map_items_iterator &) = delete; mgp_map_items_iterator &operator=(mgp_map_items_iterator &&) = delete; ~mgp_map_items_iterator() = default; memgraph::utils::MemoryResource *GetMemoryResource() const { return memory; } memgraph::utils::MemoryResource *memory; mgp_map *map; decltype(map->items.begin()) current_it; mgp_map_item current; }; struct mgp_vertex { /// Allocator type so that STL containers are aware that we need one. /// We don't actually need this, but it simplifies the C API, because we store /// the allocator which was used to allocate `this`. using allocator_type = memgraph::utils::Allocator; // Hopefully VertexAccessor copy constructor remains noexcept, so that we can // have everything noexcept here. static_assert(std::is_nothrow_copy_constructible_v); mgp_vertex(memgraph::query::VertexAccessor v, mgp_graph *graph, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), impl(v), graph(graph) {} mgp_vertex(memgraph::query::SubgraphVertexAccessor v, mgp_graph *graph, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), impl(v), graph(graph) {} mgp_vertex(const mgp_vertex &other, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), impl(other.impl), graph(other.graph) { std::visit(memgraph::utils::Overloaded{ [](memgraph::query::VertexAccessor) { std::cout << "VertexAccessor" << std::endl; }, [](memgraph::query::SubgraphVertexAccessor) { std::cout << "SubgraphVertexAccessor" << std::endl; }}, other.impl); std::visit(memgraph::utils::Overloaded{ [](memgraph::query::VertexAccessor) { std::cout << "VertexAccessor" << std::endl; }, [](memgraph::query::SubgraphVertexAccessor) { std::cout << "SubgraphVertexAccessor" << std::endl; }}, this->impl); } mgp_vertex(mgp_vertex &&other, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), impl(other.impl), graph(other.graph) { std::visit(memgraph::utils::Overloaded{ [](memgraph::query::VertexAccessor) { std::cout << "VertexAccessor" << std::endl; }, [](memgraph::query::SubgraphVertexAccessor) { std::cout << "SubgraphVertexAccessor" << std::endl; }}, other.impl); std::visit(memgraph::utils::Overloaded{ [](memgraph::query::VertexAccessor) { std::cout << "VertexAccessor" << std::endl; }, [](memgraph::query::SubgraphVertexAccessor) { std::cout << "SubgraphVertexAccessor" << std::endl; }}, this->impl); } mgp_vertex(mgp_vertex &&other) noexcept : memory(other.memory), impl(other.impl), graph(other.graph) {} memgraph::query::VertexAccessor getImpl() const { return std::visit( memgraph::utils::Overloaded{[](memgraph::query::VertexAccessor impl) { return impl; }, [](memgraph::query::SubgraphVertexAccessor impl) { return impl.impl_; }}, this->impl); } /// Copy construction without memgraph::utils::MemoryResource is not allowed. mgp_vertex(const mgp_vertex &) = delete; mgp_vertex &operator=(const mgp_vertex &) = delete; mgp_vertex &operator=(mgp_vertex &&) = delete; bool operator==(const mgp_vertex &other) const noexcept { return std::visit(memgraph::utils::Overloaded{ [&other](memgraph::query::VertexAccessor impl) { if (std::holds_alternative(other.impl)) { return false; } return impl == std::get(other.impl); }, [&other](memgraph::query::SubgraphVertexAccessor impl) -> bool { if (std::holds_alternative(other.impl)) { return false; } return impl == std::get(other.impl); }, }, this->impl); } bool operator!=(const mgp_vertex &other) const noexcept { return !(*this == other); }; ~mgp_vertex() = default; memgraph::utils::MemoryResource *GetMemoryResource() const noexcept { return memory; } memgraph::utils::MemoryResource *memory; std::variant impl; mgp_graph *graph; }; struct mgp_graph { std::variant impl; memgraph::storage::View view; // TODO: Merge `mgp_graph` and `mgp_memory` into a single `mgp_context`. The // `ctx` field is out of place here. memgraph::query::ExecutionContext *ctx; // memgraph:::query::Graph *subraph; static mgp_graph WritableGraph(memgraph::query::DbAccessor &acc, memgraph::storage::View view, memgraph::query::ExecutionContext &ctx) { return mgp_graph{&acc, view, &ctx}; } static mgp_graph NonWritableGraph(memgraph::query::DbAccessor &acc, memgraph::storage::View view) { return mgp_graph{&acc, view, nullptr}; } static mgp_graph WritableGraph(memgraph::query::SubgraphDbAccessor &acc, memgraph::storage::View view, memgraph::query::ExecutionContext &ctx) { return mgp_graph{&acc, view, &ctx}; } static mgp_graph NonWritableGraph(memgraph::query::SubgraphDbAccessor &acc, memgraph::storage::View view) { return mgp_graph{&acc, view, nullptr}; } }; struct mgp_edge { /// Allocator type so that STL containers are aware that we need one. /// We don't actually need this, but it simplifies the C API, because we store /// the allocator which was used to allocate `this`. using allocator_type = memgraph::utils::Allocator; // Hopefully EdgeAccessor copy constructor remains noexcept, so that we can // have everything noexcept here. static_assert(std::is_nothrow_copy_constructible_v); static mgp_edge *Copy(const mgp_edge &edge, mgp_memory &memory); mgp_edge(const memgraph::query::EdgeAccessor &impl, mgp_graph *graph, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), impl(impl), from(impl.From(), graph, memory), to(impl.To(), graph, memory) {} mgp_edge(const memgraph::query::EdgeAccessor &impl, const memgraph::query::VertexAccessor &from_v, const memgraph::query::VertexAccessor &to_v, mgp_graph *graph, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), impl(impl), from(from_v, graph, memory), to(to_v, graph, memory) {} mgp_edge(const memgraph::query::EdgeAccessor &impl, const memgraph::query::SubgraphVertexAccessor &from_v, const memgraph::query::SubgraphVertexAccessor &to_v, mgp_graph *graph, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), impl(impl), from(from_v, graph, memory), to(to_v, graph, memory) {} mgp_edge(const mgp_edge &other, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), impl(other.impl), from(other.from, memory), to(other.to, memory) {} mgp_edge(mgp_edge &&other, memgraph::utils::MemoryResource *memory) noexcept : memory(other.memory), impl(other.impl), from(std::move(other.from), memory), to(std::move(other.to), memory) {} mgp_edge(mgp_edge &&other) noexcept : memory(other.memory), impl(other.impl), from(std::move(other.from)), to(std::move(other.to)) {} /// Copy construction without memgraph::utils::MemoryResource is not allowed. mgp_edge(const mgp_edge &) = delete; mgp_edge &operator=(const mgp_edge &) = delete; mgp_edge &operator=(mgp_edge &&) = delete; ~mgp_edge() = default; bool operator==(const mgp_edge &other) const noexcept { return this->impl == other.impl; } bool operator!=(const mgp_edge &other) const noexcept { return !(*this == other); }; memgraph::utils::MemoryResource *GetMemoryResource() const noexcept { return memory; } memgraph::utils::MemoryResource *memory; memgraph::query::EdgeAccessor impl; mgp_vertex from; mgp_vertex to; }; struct mgp_path { /// Allocator type so that STL containers are aware that we need one. using allocator_type = memgraph::utils::Allocator; explicit mgp_path(memgraph::utils::MemoryResource *memory) : vertices(memory), edges(memory) {} mgp_path(const mgp_path &other, memgraph::utils::MemoryResource *memory) : vertices(other.vertices, memory), edges(other.edges, memory) {} mgp_path(mgp_path &&other, memgraph::utils::MemoryResource *memory) : vertices(std::move(other.vertices), memory), edges(std::move(other.edges), memory) {} mgp_path(mgp_path &&other) noexcept : vertices(std::move(other.vertices)), edges(std::move(other.edges)) {} /// Copy construction without memgraph::utils::MemoryResource is not allowed. mgp_path(const mgp_path &) = delete; mgp_path &operator=(const mgp_path &) = delete; mgp_path &operator=(mgp_path &&) = delete; ~mgp_path() = default; memgraph::utils::MemoryResource *GetMemoryResource() const noexcept { return vertices.get_allocator().GetMemoryResource(); } memgraph::utils::pmr::vector vertices; memgraph::utils::pmr::vector edges; }; struct mgp_result_record { /// Result record signature as defined for mgp_proc. const memgraph::utils::pmr::map> *signature; memgraph::utils::pmr::map values; }; struct mgp_result { explicit mgp_result( const memgraph::utils::pmr::map> *signature, memgraph::utils::MemoryResource *mem) : signature(signature), rows(mem) {} /// Result record signature as defined for mgp_proc. const memgraph::utils::pmr::map> *signature; memgraph::utils::pmr::vector rows; std::optional error_msg; }; struct mgp_func_result { mgp_func_result() {} /// Return Magic function result. If user forgets it, the error is raised std::optional value; /// Return Magic function result with potential error std::optional error_msg; }; // Prevents user to use ExecutionContext in writable callables struct mgp_func_context { memgraph::query::DbAccessor *impl; memgraph::storage::View view; }; struct mgp_properties_iterator { using allocator_type = memgraph::utils::Allocator; // Define members at the start because we use decltype a lot here, so members // need to be visible in method definitions. memgraph::utils::MemoryResource *memory; mgp_graph *graph; std::remove_reference_t().Properties(graph->view))> pvs; decltype(pvs.begin()) current_it; std::optional> current; mgp_property property{nullptr, nullptr}; // Construct with no properties. explicit mgp_properties_iterator(mgp_graph *graph, memgraph::utils::MemoryResource *memory) : memory(memory), graph(graph), current_it(pvs.begin()) {} // May throw who the #$@! knows what because PropertyValueStore doesn't // document what it throws, and it may surely throw some piece of !@#$ // exception because it's built on top of STL and other libraries. mgp_properties_iterator(mgp_graph *graph, decltype(pvs) pvs, memgraph::utils::MemoryResource *memory) : memory(memory), graph(graph), pvs(std::move(pvs)), current_it(this->pvs.begin()) { if (current_it != this->pvs.end()) { auto value = std::visit(memgraph::utils::Overloaded{ [this, memory](const memgraph::query::DbAccessor *impl) { return memgraph::utils::pmr::string(impl->PropertyToName(current_it->first), memory); }, [this, memory](const memgraph::query::SubgraphDbAccessor *impl) { return memgraph::utils::pmr::string(impl->PropertyToName(current_it->first), memory); }, }, graph->impl); current.emplace(value, mgp_value(current_it->second, memory)); property.name = current->first.c_str(); property.value = ¤t->second; } } mgp_properties_iterator(const mgp_properties_iterator &) = delete; mgp_properties_iterator(mgp_properties_iterator &&) = delete; mgp_properties_iterator &operator=(const mgp_properties_iterator &) = delete; mgp_properties_iterator &operator=(mgp_properties_iterator &&) = delete; ~mgp_properties_iterator() = default; memgraph::utils::MemoryResource *GetMemoryResource() const { return memory; } }; struct mgp_edges_iterator { using allocator_type = memgraph::utils::Allocator; // Hopefully mgp_vertex copy constructor remains noexcept, so that we can // have everything noexcept here. static_assert(std::is_nothrow_constructible_v); mgp_edges_iterator(const mgp_vertex &v, memgraph::utils::MemoryResource *memory) noexcept : memory(memory), source_vertex(v, memory) {} mgp_edges_iterator(mgp_edges_iterator &&other) noexcept : memory(other.memory), source_vertex(std::move(other.source_vertex)), in(std::move(other.in)), in_it(std::move(other.in_it)), out(std::move(other.out)), out_it(std::move(other.out_it)), current_e(std::move(other.current_e)) {} mgp_edges_iterator(const mgp_edges_iterator &) = delete; mgp_edges_iterator &operator=(const mgp_edges_iterator &) = delete; mgp_edges_iterator &operator=(mgp_edges_iterator &&) = delete; ~mgp_edges_iterator() = default; memgraph::utils::MemoryResource *GetMemoryResource() const { return memory; } memgraph::utils::MemoryResource *memory; mgp_vertex source_vertex; std::optional(source_vertex.impl).InEdges(source_vertex.graph->view))>> in; std::optionalbegin())> in_it; std::optional(source_vertex.impl).InEdges(source_vertex.graph->view))>> out; std::optionalbegin())> out_it; std::optional current_e; }; struct mgp_vertices_iterator { using allocator_type = memgraph::utils::Allocator; /// @throw anything VerticesIterable may throw mgp_vertices_iterator(mgp_graph *graph, memgraph::utils::MemoryResource *memory) : memory(memory), graph(graph), vertices(std::visit(memgraph::utils::Overloaded{[graph](auto *impl) { return impl->Vertices(graph->view); }}, graph->impl)), current_it(vertices.begin()) { if (current_it != vertices.end()) { std::visit( memgraph::utils::Overloaded{ [this, graph, memory](memgraph::query::DbAccessor *) { current_v.emplace(*current_it, graph, memory); }, [this, graph, memory](memgraph::query::SubgraphDbAccessor *impl) { current_v.emplace(memgraph::query::SubgraphVertexAccessor(*current_it, impl->getGraph()), graph, memory); }}, graph->impl); } } memgraph::utils::MemoryResource *GetMemoryResource() const { return memory; } memgraph::utils::MemoryResource *memory; mgp_graph *graph; memgraph::query::VerticesIterable vertices; decltype(vertices.begin()) current_it; std::optional current_v; }; struct mgp_type { memgraph::query::procedure::CypherTypePtr impl; }; struct ProcedureInfo { bool is_write = false; std::optional required_privilege = std::nullopt; }; struct mgp_proc { using allocator_type = memgraph::utils::Allocator; /// @throw std::bad_alloc /// @throw std::length_error mgp_proc(const char *name, mgp_proc_cb cb, memgraph::utils::MemoryResource *memory, const ProcedureInfo &info = {}) : name(name, memory), cb(cb), args(memory), opt_args(memory), results(memory), info(info) {} /// @throw std::bad_alloc /// @throw std::length_error mgp_proc(const char *name, std::function cb, memgraph::utils::MemoryResource *memory, const ProcedureInfo &info = {}) : name(name, memory), cb(cb), args(memory), opt_args(memory), results(memory), info(info) {} /// @throw std::bad_alloc /// @throw std::length_error mgp_proc(const std::string_view name, std::function cb, memgraph::utils::MemoryResource *memory, const ProcedureInfo &info = {}) : name(name, memory), cb(cb), args(memory), opt_args(memory), results(memory), info(info) {} /// @throw std::bad_alloc /// @throw std::length_error mgp_proc(const mgp_proc &other, memgraph::utils::MemoryResource *memory) : name(other.name, memory), cb(other.cb), args(other.args, memory), opt_args(other.opt_args, memory), results(other.results, memory), info(other.info) {} mgp_proc(mgp_proc &&other, memgraph::utils::MemoryResource *memory) : name(std::move(other.name), memory), cb(std::move(other.cb)), args(std::move(other.args), memory), opt_args(std::move(other.opt_args), memory), results(std::move(other.results), memory), info(other.info) {} mgp_proc(const mgp_proc &other) = default; mgp_proc(mgp_proc &&other) = default; mgp_proc &operator=(const mgp_proc &) = delete; mgp_proc &operator=(mgp_proc &&) = delete; ~mgp_proc() = default; /// Name of the procedure. memgraph::utils::pmr::string name; /// Entry-point for the procedure. std::function cb; /// Required, positional arguments as a (name, type) pair. memgraph::utils::pmr::vector> args; /// Optional positional arguments as a (name, type, default_value) tuple. memgraph::utils::pmr::vector> opt_args; /// Fields this procedure returns, as a (name -> (type, is_deprecated)) map. memgraph::utils::pmr::map> results; ProcedureInfo info; }; struct mgp_trans { using allocator_type = memgraph::utils::Allocator; /// @throw std::bad_alloc /// @throw std::length_error mgp_trans(const char *name, mgp_trans_cb cb, memgraph::utils::MemoryResource *memory) : name(name, memory), cb(cb), results(memory) {} /// @throw std::bad_alloc /// @throw std::length_error mgp_trans(const char *name, std::function cb, memgraph::utils::MemoryResource *memory) : name(name, memory), cb(cb), results(memory) {} /// @throw std::bad_alloc /// @throw std::length_error mgp_trans(const mgp_trans &other, memgraph::utils::MemoryResource *memory) : name(other.name, memory), cb(other.cb), results(other.results) {} mgp_trans(mgp_trans &&other, memgraph::utils::MemoryResource *memory) : name(std::move(other.name), memory), cb(std::move(other.cb)), results(std::move(other.results)) {} mgp_trans(const mgp_trans &other) = default; mgp_trans(mgp_trans &&other) = default; mgp_trans &operator=(const mgp_trans &) = delete; mgp_trans &operator=(mgp_trans &&) = delete; ~mgp_trans() = default; /// Name of the transformation. memgraph::utils::pmr::string name; /// Entry-point for the transformation. std::function cb; /// Fields this transformation returns. memgraph::utils::pmr::map> results; }; struct mgp_func { using allocator_type = memgraph::utils::Allocator; /// @throw std::bad_alloc /// @throw std::length_error mgp_func(const char *name, mgp_func_cb cb, memgraph::utils::MemoryResource *memory) : name(name, memory), cb(cb), args(memory), opt_args(memory) {} /// @throw std::bad_alloc /// @throw std::length_error mgp_func(const char *name, std::function cb, memgraph::utils::MemoryResource *memory) : name(name, memory), cb(cb), args(memory), opt_args(memory) {} /// @throw std::bad_alloc /// @throw std::length_error mgp_func(const mgp_func &other, memgraph::utils::MemoryResource *memory) : name(other.name, memory), cb(other.cb), args(other.args, memory), opt_args(other.opt_args, memory) {} mgp_func(mgp_func &&other, memgraph::utils::MemoryResource *memory) : name(std::move(other.name), memory), cb(std::move(other.cb)), args(std::move(other.args), memory), opt_args(std::move(other.opt_args), memory) {} mgp_func(const mgp_func &other) = default; mgp_func(mgp_func &&other) = default; mgp_func &operator=(const mgp_func &) = delete; mgp_func &operator=(mgp_func &&) = delete; ~mgp_func() = default; /// Name of the function. memgraph::utils::pmr::string name; /// Entry-point for the function. std::function cb; /// Required, positional arguments as a (name, type) pair. memgraph::utils::pmr::vector> args; /// Optional positional arguments as a (name, type, default_value) tuple. memgraph::utils::pmr::vector> opt_args; }; mgp_error MgpTransAddFixedResult(mgp_trans *trans) noexcept; struct mgp_module { using allocator_type = memgraph::utils::Allocator; explicit mgp_module(memgraph::utils::MemoryResource *memory) : procedures(memory), transformations(memory), functions(memory) {} mgp_module(const mgp_module &other, memgraph::utils::MemoryResource *memory) : procedures(other.procedures, memory), transformations(other.transformations, memory), functions(other.functions, memory) {} mgp_module(mgp_module &&other, memgraph::utils::MemoryResource *memory) : procedures(std::move(other.procedures), memory), transformations(std::move(other.transformations), memory), functions(std::move(other.functions), memory) {} mgp_module(const mgp_module &) = default; mgp_module(mgp_module &&) = default; mgp_module &operator=(const mgp_module &) = delete; mgp_module &operator=(mgp_module &&) = delete; ~mgp_module() = default; memgraph::utils::pmr::map procedures; memgraph::utils::pmr::map transformations; memgraph::utils::pmr::map functions; }; namespace memgraph::query::procedure { /// @throw std::bad_alloc /// @throw std::length_error /// @throw anything std::ostream::operator<< may throw. void PrintProcSignature(const mgp_proc &, std::ostream *); /// @throw std::bad_alloc /// @throw std::length_error /// @throw anything std::ostream::operator<< may throw. void PrintFuncSignature(const mgp_func &, std::ostream &); bool IsValidIdentifierName(const char *name); } // namespace memgraph::query::procedure struct mgp_message { explicit mgp_message(const memgraph::integrations::kafka::Message &message) : msg{&message} {} explicit mgp_message(const memgraph::integrations::pulsar::Message &message) : msg{message} {} using KafkaMessage = const memgraph::integrations::kafka::Message *; using PulsarMessage = memgraph::integrations::pulsar::Message; std::variant msg; }; struct mgp_messages { using allocator_type = memgraph::utils::Allocator; using storage_type = memgraph::utils::pmr::vector; explicit mgp_messages(storage_type &&storage) : messages(std::move(storage)) {} mgp_messages(const mgp_messages &) = delete; mgp_messages &operator=(const mgp_messages &) = delete; mgp_messages(mgp_messages &&) = delete; mgp_messages &operator=(mgp_messages &&) = delete; ~mgp_messages() = default; storage_type messages; }; memgraph::query::TypedValue ToTypedValue(const mgp_value &val, memgraph::utils::MemoryResource *memory);