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:
parent
3750fbc093
commit
4c6eb0746e
@ -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; }
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user