query/procedure: Implement mgp_value C API

Reviewers: mferencevic, ipaljak, llugovic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2498
This commit is contained in:
Teon Banek 2019-10-19 10:03:22 +02:00
parent 3750fbc093
commit 4c6eb0746e
2 changed files with 778 additions and 3 deletions

View File

@ -1,11 +1,12 @@
#include "query/procedure/mg_procedure_impl.hpp"
#include <algorithm>
#include <cstddef>
#include <cstring>
#include <algorithm>
#include <type_traits>
#include <glog/logging.h>
#include "utils/math.hpp"
void *mgp_alloc(mgp_memory *memory, size_t size_in_bytes) {
@ -64,3 +65,500 @@ void mgp_free(mgp_memory *memory, void *const p) {
void *const original_ptr = data - bytes_for_header;
memory->impl->Deallocate(original_ptr, alloc_size, alloc_align);
}
namespace {
// May throw whatever the constructor of U throws. `std::bad_alloc` is handled
// by returning nullptr.
template <class U, class... TArgs>
U *new_mgp_object(utils::MemoryResource *memory, TArgs &&... args) {
utils::Allocator<U> allocator(memory);
try {
return allocator.template new_object<U>(std::forward<TArgs>(args)...);
} catch (const std::bad_alloc &) {
return nullptr;
}
}
template <class U, class... TArgs>
U *new_mgp_object(mgp_memory *memory, TArgs &&... args) {
return new_mgp_object<U, TArgs...>(memory->impl,
std::forward<TArgs>(args)...);
}
// Assume that deallocation and object destruction never throws. If it does,
// we are in big trouble.
template <class T>
void delete_mgp_object(T *ptr) noexcept {
if (!ptr) return;
utils::Allocator<T> allocator(ptr->GetMemoryResource());
allocator.delete_object(ptr);
}
mgp_value_type FromTypedValueType(query::TypedValue::Type type) {
switch (type) {
case query::TypedValue::Type::Null:
return MGP_VALUE_TYPE_NULL;
case query::TypedValue::Type::Bool:
return MGP_VALUE_TYPE_BOOL;
case query::TypedValue::Type::Int:
return MGP_VALUE_TYPE_INT;
case query::TypedValue::Type::Double:
return MGP_VALUE_TYPE_DOUBLE;
case query::TypedValue::Type::String:
return MGP_VALUE_TYPE_STRING;
case query::TypedValue::Type::List:
return MGP_VALUE_TYPE_LIST;
case query::TypedValue::Type::Map:
return MGP_VALUE_TYPE_MAP;
case query::TypedValue::Type::Vertex:
return MGP_VALUE_TYPE_VERTEX;
case query::TypedValue::Type::Edge:
return MGP_VALUE_TYPE_EDGE;
case query::TypedValue::Type::Path:
return MGP_VALUE_TYPE_PATH;
}
}
} // namespace
mgp_value::mgp_value(utils::MemoryResource *m) noexcept
: type(MGP_VALUE_TYPE_NULL), memory(m) {}
mgp_value::mgp_value(bool val, utils::MemoryResource *m) noexcept
: type(MGP_VALUE_TYPE_BOOL), memory(m), bool_v(val) {}
mgp_value::mgp_value(int64_t val, utils::MemoryResource *m) noexcept
: type(MGP_VALUE_TYPE_INT), memory(m), int_v(val) {}
mgp_value::mgp_value(double val, utils::MemoryResource *m) noexcept
: type(MGP_VALUE_TYPE_DOUBLE), memory(m), double_v(val) {}
mgp_value::mgp_value(const char *val, utils::MemoryResource *m)
: type(MGP_VALUE_TYPE_STRING), memory(m), string_v(val, m) {}
mgp_value::mgp_value(mgp_list *val, utils::MemoryResource *m) noexcept
: type(MGP_VALUE_TYPE_LIST), memory(m), list_v(val) {
CHECK(val->GetMemoryResource() == m)
<< "Unable to take ownership of a pointer with different allocator.";
}
mgp_value::mgp_value(mgp_map *val, utils::MemoryResource *m) noexcept
: type(MGP_VALUE_TYPE_MAP), memory(m), map_v(val) {
CHECK(val->GetMemoryResource() == m)
<< "Unable to take ownership of a pointer with different allocator.";
}
mgp_value::mgp_value(mgp_vertex *val, utils::MemoryResource *m) noexcept
: type(MGP_VALUE_TYPE_VERTEX), memory(m), vertex_v(val) {
CHECK(val->GetMemoryResource() == m)
<< "Unable to take ownership of a pointer with different allocator.";
}
mgp_value::mgp_value(mgp_edge *val, utils::MemoryResource *m) noexcept
: type(MGP_VALUE_TYPE_EDGE), memory(m), edge_v(val) {
CHECK(val->GetMemoryResource() == m)
<< "Unable to take ownership of a pointer with different allocator.";
}
mgp_value::mgp_value(mgp_path *val, utils::MemoryResource *m) noexcept
: type(MGP_VALUE_TYPE_PATH), memory(m), path_v(val) {
CHECK(val->GetMemoryResource() == m)
<< "Unable to take ownership of a pointer with different allocator.";
}
mgp_value::mgp_value(const query::TypedValue &tv, const mgp_graph *graph,
utils::MemoryResource *m)
: type(FromTypedValueType(tv.type())), memory(m) {
switch (type) {
case MGP_VALUE_TYPE_NULL:
break;
case MGP_VALUE_TYPE_BOOL:
bool_v = tv.ValueBool();
break;
case MGP_VALUE_TYPE_INT:
int_v = tv.ValueInt();
break;
case MGP_VALUE_TYPE_DOUBLE:
double_v = tv.ValueDouble();
break;
case MGP_VALUE_TYPE_STRING:
new (&string_v) utils::pmr::string(tv.ValueString(), m);
break;
case MGP_VALUE_TYPE_LIST: {
// Fill the stack allocated container and then construct the actual member
// value. This handles the case when filling the container throws
// something and our destructor doesn't get called so member value isn't
// released.
utils::pmr::vector<mgp_value> elems(m);
elems.reserve(tv.ValueList().size());
for (const auto &elem : tv.ValueList()) {
elems.emplace_back(elem, graph);
}
utils::Allocator<mgp_list> allocator(m);
list_v = allocator.new_object<mgp_list>(std::move(elems));
break;
}
case MGP_VALUE_TYPE_MAP: {
// Fill the stack allocated container and then construct the actual member
// value. This handles the case when filling the container throws
// something and our destructor doesn't get called so member value isn't
// released.
utils::pmr::map<utils::pmr::string, mgp_value> items(m);
for (const auto &item : tv.ValueMap()) {
items.emplace(item.first, mgp_value(item.second, graph, m));
}
utils::Allocator<mgp_map> allocator(m);
map_v = allocator.new_object<mgp_map>(std::move(items));
break;
}
case MGP_VALUE_TYPE_VERTEX: {
utils::Allocator<mgp_vertex> allocator(m);
vertex_v = allocator.new_object<mgp_vertex>(tv.ValueVertex(), graph);
break;
}
case MGP_VALUE_TYPE_EDGE: {
utils::Allocator<mgp_edge> allocator(m);
edge_v = allocator.new_object<mgp_edge>(tv.ValueEdge(), graph);
break;
}
case MGP_VALUE_TYPE_PATH: {
// Fill the stack allocated container and then construct the actual member
// value. This handles the case when filling the container throws
// something and our destructor doesn't get called so member value isn't
// released.
mgp_path tmp_path(m);
tmp_path.vertices.reserve(tv.ValuePath().vertices().size());
for (const auto &v : tv.ValuePath().vertices()) {
tmp_path.vertices.emplace_back(v, graph);
}
tmp_path.edges.reserve(tv.ValuePath().edges().size());
for (const auto &e : tv.ValuePath().edges()) {
tmp_path.edges.emplace_back(e, graph);
}
utils::Allocator<mgp_path> allocator(m);
path_v = allocator.new_object<mgp_path>(std::move(tmp_path));
break;
}
}
}
mgp_value::mgp_value(const PropertyValue &pv, utils::MemoryResource *m)
: memory(m) {
switch (pv.type()) {
case PropertyValue::Type::Null:
type = MGP_VALUE_TYPE_NULL;
break;
case PropertyValue::Type::Bool:
type = MGP_VALUE_TYPE_BOOL;
bool_v = pv.ValueBool();
break;
case PropertyValue::Type::Int:
type = MGP_VALUE_TYPE_INT;
int_v = pv.ValueInt();
break;
case PropertyValue::Type::Double:
type = MGP_VALUE_TYPE_DOUBLE;
double_v = pv.ValueDouble();
break;
case PropertyValue::Type::String:
type = MGP_VALUE_TYPE_STRING;
new (&string_v) utils::pmr::string(pv.ValueString(), m);
break;
case PropertyValue::Type::List: {
// Fill the stack allocated container and then construct the actual member
// value. This handles the case when filling the container throws
// something and our destructor doesn't get called so member value isn't
// released.
type = MGP_VALUE_TYPE_LIST;
utils::pmr::vector<mgp_value> elems(m);
elems.reserve(pv.ValueList().size());
for (const auto &elem : pv.ValueList()) {
elems.emplace_back(elem);
}
utils::Allocator<mgp_list> allocator(m);
list_v = allocator.new_object<mgp_list>(std::move(elems));
break;
}
case PropertyValue::Type::Map: {
// Fill the stack allocated container and then construct the actual member
// value. This handles the case when filling the container throws
// something and our destructor doesn't get called so member value isn't
// released.
type = MGP_VALUE_TYPE_MAP;
utils::pmr::map<utils::pmr::string, mgp_value> items(m);
for (const auto &item : pv.ValueMap()) {
items.emplace(item.first, item.second);
}
utils::Allocator<mgp_map> allocator(m);
map_v = allocator.new_object<mgp_map>(std::move(items));
break;
}
}
}
mgp_value::mgp_value(const mgp_value &other, utils::MemoryResource *m)
: type(other.type), memory(m) {
switch (other.type) {
case MGP_VALUE_TYPE_NULL:
break;
case MGP_VALUE_TYPE_BOOL:
bool_v = other.bool_v;
break;
case MGP_VALUE_TYPE_INT:
int_v = other.int_v;
break;
case MGP_VALUE_TYPE_DOUBLE:
double_v = other.double_v;
break;
case MGP_VALUE_TYPE_STRING:
new (&string_v) utils::pmr::string(other.string_v, m);
break;
case MGP_VALUE_TYPE_LIST: {
utils::Allocator<mgp_list> allocator(m);
list_v = allocator.new_object<mgp_list>(*other.list_v);
break;
}
case MGP_VALUE_TYPE_MAP: {
utils::Allocator<mgp_map> allocator(m);
map_v = allocator.new_object<mgp_map>(*other.map_v);
break;
}
case MGP_VALUE_TYPE_VERTEX: {
utils::Allocator<mgp_vertex> allocator(m);
vertex_v = allocator.new_object<mgp_vertex>(*other.vertex_v);
break;
}
case MGP_VALUE_TYPE_EDGE: {
utils::Allocator<mgp_edge> allocator(m);
edge_v = allocator.new_object<mgp_edge>(*other.edge_v);
break;
}
case MGP_VALUE_TYPE_PATH: {
utils::Allocator<mgp_path> allocator(m);
path_v = allocator.new_object<mgp_path>(*other.path_v);
break;
}
}
}
namespace {
void DeleteValueMember(mgp_value *value) noexcept {
CHECK(value);
utils::Allocator<mgp_value> allocator(value->GetMemoryResource());
switch (mgp_value_get_type(value)) {
case MGP_VALUE_TYPE_NULL:
case MGP_VALUE_TYPE_BOOL:
case MGP_VALUE_TYPE_INT:
case MGP_VALUE_TYPE_DOUBLE:
return;
case MGP_VALUE_TYPE_STRING:
using TString = utils::pmr::string;
value->string_v.~TString();
return;
case MGP_VALUE_TYPE_LIST:
allocator.delete_object(value->list_v);
return;
case MGP_VALUE_TYPE_MAP:
allocator.delete_object(value->map_v);
return;
case MGP_VALUE_TYPE_VERTEX:
allocator.delete_object(value->vertex_v);
return;
case MGP_VALUE_TYPE_EDGE:
allocator.delete_object(value->edge_v);
return;
case MGP_VALUE_TYPE_PATH:
allocator.delete_object(value->path_v);
return;
}
}
} // namespace
mgp_value::mgp_value(mgp_value &&other, utils::MemoryResource *m)
: type(other.type), memory(m) {
switch (other.type) {
case MGP_VALUE_TYPE_NULL:
break;
case MGP_VALUE_TYPE_BOOL:
bool_v = other.bool_v;
break;
case MGP_VALUE_TYPE_INT:
int_v = other.int_v;
break;
case MGP_VALUE_TYPE_DOUBLE:
double_v = other.double_v;
break;
case MGP_VALUE_TYPE_STRING:
new (&string_v) utils::pmr::string(std::move(other.string_v), m);
break;
case MGP_VALUE_TYPE_LIST:
static_assert(std::is_pointer_v<decltype(list_v)>,
"Expected to move list_v by copying pointers.");
if (*other.GetMemoryResource() == *m) {
list_v = other.list_v;
} else {
utils::Allocator<mgp_list> allocator(m);
list_v = allocator.new_object<mgp_list>(std::move(*other.list_v));
}
break;
case MGP_VALUE_TYPE_MAP:
static_assert(std::is_pointer_v<decltype(map_v)>,
"Expected to move map_v by copying pointers.");
if (*other.GetMemoryResource() == *m) {
map_v = other.map_v;
} else {
utils::Allocator<mgp_map> allocator(m);
map_v = allocator.new_object<mgp_map>(std::move(*other.map_v));
}
break;
case MGP_VALUE_TYPE_VERTEX:
static_assert(std::is_pointer_v<decltype(vertex_v)>,
"Expected to move vertex_v by copying pointers.");
if (*other.GetMemoryResource() == *m) {
vertex_v = other.vertex_v;
} else {
utils::Allocator<mgp_vertex> allocator(m);
vertex_v = allocator.new_object<mgp_vertex>(std::move(*other.vertex_v));
}
break;
case MGP_VALUE_TYPE_EDGE:
static_assert(std::is_pointer_v<decltype(edge_v)>,
"Expected to move edge_v by copying pointers.");
if (*other.GetMemoryResource() == *m) {
edge_v = other.edge_v;
} else {
utils::Allocator<mgp_edge> allocator(m);
edge_v = allocator.new_object<mgp_edge>(std::move(*other.edge_v));
}
break;
case MGP_VALUE_TYPE_PATH:
static_assert(std::is_pointer_v<decltype(path_v)>,
"Expected to move path_v by copying pointers.");
if (*other.GetMemoryResource() == *m) {
path_v = other.path_v;
} else {
utils::Allocator<mgp_path> allocator(m);
path_v = allocator.new_object<mgp_path>(std::move(*other.path_v));
}
break;
}
DeleteValueMember(&other);
other.type = MGP_VALUE_TYPE_NULL;
}
mgp_value::~mgp_value() noexcept { DeleteValueMember(this); }
void mgp_value_destroy(mgp_value *val) { delete_mgp_object(val); }
mgp_value *mgp_value_make_null(mgp_memory *memory) {
return new_mgp_object<mgp_value>(memory);
}
mgp_value *mgp_value_make_bool(int val, mgp_memory *memory) {
return new_mgp_object<mgp_value>(memory, val != 0);
}
mgp_value *mgp_value_make_int(int64_t val, mgp_memory *memory) {
return new_mgp_object<mgp_value>(memory, val);
}
mgp_value *mgp_value_make_double(double val, mgp_memory *memory) {
return new_mgp_object<mgp_value>(memory, val);
}
mgp_value *mgp_value_make_string(const char *val, mgp_memory *memory) {
try {
// This may throw something from std::string constructor, it could be
// std::length_error, but it's not really well defined, so catch all.
return new_mgp_object<mgp_value>(memory, val);
} catch (...) {
return nullptr;
}
}
mgp_value *mgp_value_make_list(mgp_list *val) {
return new_mgp_object<mgp_value>(val->GetMemoryResource(), val);
}
mgp_value *mgp_value_make_map(mgp_map *val) {
return new_mgp_object<mgp_value>(val->GetMemoryResource(), val);
}
mgp_value *mgp_value_make_vertex(mgp_vertex *val) {
return new_mgp_object<mgp_value>(val->GetMemoryResource(), val);
}
mgp_value *mgp_value_make_edge(mgp_edge *val) {
return new_mgp_object<mgp_value>(val->GetMemoryResource(), val);
}
mgp_value *mgp_value_make_path(mgp_path *val) {
return new_mgp_object<mgp_value>(val->GetMemoryResource(), val);
}
mgp_value_type mgp_value_get_type(const mgp_value *val) { return val->type; }
int mgp_value_is_null(const mgp_value *val) {
return mgp_value_get_type(val) == MGP_VALUE_TYPE_NULL;
}
int mgp_value_is_bool(const mgp_value *val) {
return mgp_value_get_type(val) == MGP_VALUE_TYPE_BOOL;
}
int mgp_value_is_int(const mgp_value *val) {
return mgp_value_get_type(val) == MGP_VALUE_TYPE_INT;
}
int mgp_value_is_double(const mgp_value *val) {
return mgp_value_get_type(val) == MGP_VALUE_TYPE_DOUBLE;
}
int mgp_value_is_string(const mgp_value *val) {
return mgp_value_get_type(val) == MGP_VALUE_TYPE_STRING;
}
int mgp_value_is_list(const mgp_value *val) {
return mgp_value_get_type(val) == MGP_VALUE_TYPE_LIST;
}
int mgp_value_is_map(const mgp_value *val) {
return mgp_value_get_type(val) == MGP_VALUE_TYPE_MAP;
}
int mgp_value_is_vertex(const mgp_value *val) {
return mgp_value_get_type(val) == MGP_VALUE_TYPE_VERTEX;
}
int mgp_value_is_edge(const mgp_value *val) {
return mgp_value_get_type(val) == MGP_VALUE_TYPE_EDGE;
}
int mgp_value_is_path(const mgp_value *val) {
return mgp_value_get_type(val) == MGP_VALUE_TYPE_PATH;
}
int mgp_value_get_bool(const mgp_value *val) { return val->bool_v ? 1 : 0; }
int64_t mgp_value_get_int(const mgp_value *val) { return val->int_v; }
double mgp_value_get_double(const mgp_value *val) { return val->double_v; }
const char *mgp_value_get_string(const mgp_value *val) {
return val->string_v.c_str();
}
const mgp_list *mgp_value_get_list(const mgp_value *val) { return val->list_v; }
const mgp_map *mgp_value_get_map(const mgp_value *val) { return val->map_v; }
const mgp_vertex *mgp_value_get_vertex(const mgp_value *val) {
return val->vertex_v;
}
const mgp_edge *mgp_value_get_edge(const mgp_value *val) { return val->edge_v; }
const mgp_path *mgp_value_get_path(const mgp_value *val) { return val->path_v; }

View File

@ -4,8 +4,13 @@
#pragma once
#include "mg_procedure.h"
#include "query/db_accessor.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"
/// Wraps memory resource used in custom procedures.
///
@ -17,3 +22,275 @@
struct mgp_memory {
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 = utils::Allocator<mgp_value>;
// Construct MGP_VALUE_TYPE_NULL.
explicit mgp_value(utils::MemoryResource *) noexcept;
mgp_value(bool, utils::MemoryResource *) noexcept;
mgp_value(int64_t, utils::MemoryResource *) noexcept;
mgp_value(double, utils::MemoryResource *) noexcept;
/// @throw std::bad_alloc
mgp_value(const char *, utils::MemoryResource *);
/// Take ownership of the mgp_list, MemoryResource must match.
mgp_value(mgp_list *, utils::MemoryResource *) noexcept;
/// Take ownership of the mgp_map, MemoryResource must match.
mgp_value(mgp_map *, utils::MemoryResource *) noexcept;
/// Take ownership of the mgp_vertex, MemoryResource must match.
mgp_value(mgp_vertex *, utils::MemoryResource *) noexcept;
/// Take ownership of the mgp_edge, MemoryResource must match.
mgp_value(mgp_edge *, utils::MemoryResource *) noexcept;
/// Take ownership of the mgp_path, MemoryResource must match.
mgp_value(mgp_path *, utils::MemoryResource *) noexcept;
/// Construct by copying query::TypedValue using utils::MemoryResource.
/// mgp_graph is needed to construct mgp_vertex and mgp_edge.
/// @throw std::bad_alloc
mgp_value(const query::TypedValue &, const mgp_graph *,
utils::MemoryResource *);
/// Construct by copying PropertyValue using utils::MemoryResource.
/// @throw std::bad_alloc
mgp_value(const PropertyValue &, utils::MemoryResource *);
/// Copy construction without utils::MemoryResource is not allowed.
mgp_value(const mgp_value &) = delete;
/// Copy construct using given utils::MemoryResource.
/// @throw std::bad_alloc
mgp_value(const mgp_value &, utils::MemoryResource *);
/// Move construct using given utils::MemoryResource.
/// @throw std::bad_alloc if MemoryResource is different, so we cannot move.
mgp_value(mgp_value &&, utils::MemoryResource *);
/// Move construct, 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;
utils::MemoryResource *GetMemoryResource() const noexcept { return memory; }
mgp_value_type type;
utils::MemoryResource *memory;
union {
bool bool_v;
int64_t int_v;
double double_v;
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;
};
};
struct mgp_list {
/// Allocator type so that STL containers are aware that we need one.
using allocator_type = utils::Allocator<mgp_list>;
explicit mgp_list(utils::MemoryResource *memory) : elems(memory) {}
mgp_list(utils::pmr::vector<mgp_value> &&elems, utils::MemoryResource *memory)
: elems(std::move(elems), memory) {}
mgp_list(const mgp_list &other, utils::MemoryResource *memory)
: elems(other.elems, memory) {}
mgp_list(mgp_list &&other, utils::MemoryResource *memory)
: elems(std::move(other.elems), memory) {}
mgp_list(mgp_list &&other) noexcept : elems(std::move(other.elems)) {}
/// Copy construction without 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;
utils::MemoryResource *GetMemoryResource() const noexcept {
return elems.get_allocator().GetMemoryResource();
}
// C++17 vector can work with incomplete type.
utils::pmr::vector<mgp_value> elems;
};
struct mgp_map {
/// Allocator type so that STL containers are aware that we need one.
using allocator_type = utils::Allocator<mgp_map>;
explicit mgp_map(utils::MemoryResource *memory) : items(memory) {}
mgp_map(utils::pmr::map<utils::pmr::string, mgp_value> &&items,
utils::MemoryResource *memory)
: items(std::move(items), memory) {}
mgp_map(const mgp_map &other, utils::MemoryResource *memory)
: items(other.items, memory) {}
mgp_map(mgp_map &&other, utils::MemoryResource *memory)
: items(std::move(other.items), memory) {}
mgp_map(mgp_map &&other) noexcept : items(std::move(other.items)) {}
/// Copy construction without 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;
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.
utils::pmr::map<utils::pmr::string, mgp_value> items;
};
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 = utils::Allocator<mgp_vertex>;
// Hopefully VertexAccessor copy constructor remains noexcept, so that we can
// have everything noexcept here.
static_assert(std::is_nothrow_copy_constructible_v<query::VertexAccessor>);
mgp_vertex(query::VertexAccessor v, const mgp_graph *graph,
utils::MemoryResource *memory) noexcept
: memory(memory), impl(v), graph(graph) {}
mgp_vertex(const mgp_vertex &other, utils::MemoryResource *memory) noexcept
: memory(memory), impl(other.impl), graph(other.graph) {}
mgp_vertex(mgp_vertex &&other, utils::MemoryResource *memory) noexcept
: memory(memory), impl(other.impl), graph(other.graph) {}
mgp_vertex(mgp_vertex &&other) noexcept
: memory(other.memory), impl(other.impl), graph(other.graph) {}
/// Copy construction without utils::MemoryResource is not allowed.
mgp_vertex(const mgp_vertex &) = delete;
mgp_vertex &operator=(const mgp_vertex &) = delete;
mgp_vertex &operator=(mgp_vertex &&) = delete;
~mgp_vertex() = default;
utils::MemoryResource *GetMemoryResource() const noexcept { return memory; }
utils::MemoryResource *memory;
query::VertexAccessor impl;
const mgp_graph *graph;
};
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 = utils::Allocator<mgp_edge>;
// Hopefully EdgeAccessor copy constructor remains noexcept, so that we can
// have everything noexcept here.
static_assert(std::is_nothrow_copy_constructible_v<query::EdgeAccessor>);
mgp_edge(const query::EdgeAccessor &impl, const mgp_graph *graph,
utils::MemoryResource *memory) noexcept
: memory(memory),
impl(impl),
from(impl.From(), graph, memory),
to(impl.To(), graph, memory) {}
mgp_edge(const mgp_edge &other, utils::MemoryResource *memory) noexcept
: memory(memory),
impl(other.impl),
from(other.from, memory),
to(other.to, memory) {}
mgp_edge(mgp_edge &&other, 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 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;
utils::MemoryResource *GetMemoryResource() const noexcept { return memory; }
utils::MemoryResource *memory;
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 = utils::Allocator<mgp_path>;
explicit mgp_path(utils::MemoryResource *memory)
: vertices(memory), edges(memory) {}
mgp_path(const mgp_path &other, utils::MemoryResource *memory)
: vertices(other.vertices, memory), edges(other.edges, memory) {}
mgp_path(mgp_path &&other, 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 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;
utils::MemoryResource *GetMemoryResource() const noexcept {
return vertices.get_allocator().GetMemoryResource();
}
utils::pmr::vector<mgp_vertex> vertices;
utils::pmr::vector<mgp_edge> edges;
};
struct mgp_graph {
query::DbAccessor *impl;
storage::View view;
};