Add write procedures

This commit is contained in:
Benjamin Antal 2021-09-09 16:10:19 +02:00 committed by Antonio Andelic
parent d58a1cbb58
commit 482798295e
23 changed files with 1811 additions and 777 deletions

View File

@ -29,9 +29,12 @@ enum MGP_NODISCARD mgp_error {
MGP_ERROR_INSUFFICIENT_BUFFER,
MGP_ERROR_OUT_OF_RANGE,
MGP_ERROR_LOGIC_ERROR,
MGP_ERROR_NON_EXISTENT_OBJECT,
MGP_ERROR_DELETED_OBJECT,
MGP_ERROR_INVALID_ARGUMENT,
MGP_ERROR_KEY_ALREADY_EXISTS,
MGP_ERROR_IMMUTABLE_OBJECT,
MGP_ERROR_VALUE_CONVERSION,
MGP_ERROR_SERIALIZATION_ERROR,
};
///@}
@ -208,93 +211,93 @@ enum mgp_error mgp_value_make_path(struct mgp_path *val, struct mgp_value **resu
/// Get the type of the value contained in mgp_value.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_get_type(const struct mgp_value *val, enum mgp_value_type *result);
enum mgp_error mgp_value_get_type(struct mgp_value *val, enum mgp_value_type *result);
/// Result is non-zero if the given mgp_value represents `null`.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_is_null(const struct mgp_value *val, int *result);
enum mgp_error mgp_value_is_null(struct mgp_value *val, int *result);
/// Result is non-zero if the given mgp_value stores a boolean.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_is_bool(const struct mgp_value *val, int *result);
enum mgp_error mgp_value_is_bool(struct mgp_value *val, int *result);
/// Result is non-zero if the given mgp_value stores an integer.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_is_int(const struct mgp_value *val, int *result);
enum mgp_error mgp_value_is_int(struct mgp_value *val, int *result);
/// Result is non-zero if the given mgp_value stores a double floating-point.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_is_double(const struct mgp_value *val, int *result);
enum mgp_error mgp_value_is_double(struct mgp_value *val, int *result);
/// Result is non-zero if the given mgp_value stores a character string.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_is_string(const struct mgp_value *val, int *result);
enum mgp_error mgp_value_is_string(struct mgp_value *val, int *result);
/// Result is non-zero if the given mgp_value stores a list of values.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_is_list(const struct mgp_value *val, int *result);
enum mgp_error mgp_value_is_list(struct mgp_value *val, int *result);
/// Result is non-zero if the given mgp_value stores a map of values.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_is_map(const struct mgp_value *val, int *result);
enum mgp_error mgp_value_is_map(struct mgp_value *val, int *result);
/// Result is non-zero if the given mgp_value stores a vertex.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_is_vertex(const struct mgp_value *val, int *result);
enum mgp_error mgp_value_is_vertex(struct mgp_value *val, int *result);
/// Result is non-zero if the given mgp_value stores an edge.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_is_edge(const struct mgp_value *val, int *result);
enum mgp_error mgp_value_is_edge(struct mgp_value *val, int *result);
/// Result is non-zero if the given mgp_value stores a path.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_is_path(const struct mgp_value *val, int *result);
enum mgp_error mgp_value_is_path(struct mgp_value *val, int *result);
/// Get the contained boolean value.
/// Non-zero values represent `true`, while zero represents `false`.
/// Result is undefined if mgp_value does not contain the expected type.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_get_bool(const struct mgp_value *val, int *result);
enum mgp_error mgp_value_get_bool(struct mgp_value *val, int *result);
/// Get the contained integer.
/// Result is undefined if mgp_value does not contain the expected type.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_get_int(const struct mgp_value *val, int64_t *result);
enum mgp_error mgp_value_get_int(struct mgp_value *val, int64_t *result);
/// Get the contained double floating-point.
/// Result is undefined if mgp_value does not contain the expected type.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_get_double(const struct mgp_value *val, double *result);
enum mgp_error mgp_value_get_double(struct mgp_value *val, double *result);
/// Get the contained character string.
/// Result is undefined if mgp_value does not contain the expected type.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_get_string(const struct mgp_value *val, const char **result);
enum mgp_error mgp_value_get_string(struct mgp_value *val, const char **result);
/// Get the contained list of values.
/// Result is undefined if mgp_value does not contain the expected type.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_get_list(const struct mgp_value *val, const struct mgp_list **result);
enum mgp_error mgp_value_get_list(struct mgp_value *val, struct mgp_list **result);
/// Return the contained map of values.
/// Get the contained map of values.
/// Result is undefined if mgp_value does not contain the expected type.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_get_map(const struct mgp_value *val, const struct mgp_map **result);
enum mgp_error mgp_value_get_map(struct mgp_value *val, struct mgp_map **result);
/// Get the contained vertex.
/// Result is undefined if mgp_value does not contain the expected type.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_get_vertex(const struct mgp_value *val, const struct mgp_vertex **result);
enum mgp_error mgp_value_get_vertex(struct mgp_value *val, struct mgp_vertex **result);
/// Get the contained edge.
/// Result is undefined if mgp_value does not contain the expected type.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_get_edge(const struct mgp_value *val, const struct mgp_edge **result);
enum mgp_error mgp_value_get_edge(struct mgp_value *val, struct mgp_edge **result);
/// Get the contained path.
/// Result is undefined if mgp_value does not contain the expected type.
/// Current implementation always returns without errors.
enum mgp_error mgp_value_get_path(const struct mgp_value *val, const struct mgp_path **result);
enum mgp_error mgp_value_get_path(struct mgp_value *val, struct mgp_path **result);
/// Create an empty list with given capacity.
/// You need to free the created instance with mgp_list_destroy.
@ -313,7 +316,7 @@ void mgp_list_destroy(struct mgp_list *list);
/// original value.
/// Return MGP_ERROR_INSUFFICIENT_BUFFER if there's no capacity.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_value.
enum mgp_error mgp_list_append(struct mgp_list *list, const struct mgp_value *val);
enum mgp_error mgp_list_append(struct mgp_list *list, struct mgp_value *val);
/// Append a copy of mgp_value to mgp_list increasing capacity if needed.
/// The list copies the given value and therefore does not take ownership of the
@ -323,20 +326,20 @@ enum mgp_error mgp_list_append(struct mgp_list *list, const struct mgp_value *va
/// memory and any references to them will be invalid.
/// Return MGP_ERROR_INSUFFICIENT_BUFFER if there's no capacity.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_value.
enum mgp_error mgp_list_append_extend(struct mgp_list *list, const struct mgp_value *val);
enum mgp_error mgp_list_append_extend(struct mgp_list *list, struct mgp_value *val);
/// Get the number of elements stored in mgp_list.
/// Current implementation always returns without errors.
enum mgp_error mgp_list_size(const struct mgp_list *list, size_t *result);
enum mgp_error mgp_list_size(struct mgp_list *list, size_t *result);
/// Get the total number of elements for which there's already allocated
/// memory in mgp_list.
/// Current implementation always returns without errors.
enum mgp_error mgp_list_capacity(const struct mgp_list *list, size_t *result);
enum mgp_error mgp_list_capacity(struct mgp_list *list, size_t *result);
/// Get the element in mgp_list at given position.
/// MGP_ERROR_OUT_OF_RANGE is returned if the index is not within mgp_list_size.
enum mgp_error mgp_list_at(const struct mgp_list *list, size_t index, const struct mgp_value **result);
enum mgp_error mgp_list_at(struct mgp_list *list, size_t index, struct mgp_value **result);
/// Create an empty map of character strings to mgp_value instances.
/// You need to free the created instance with mgp_map_destroy.
@ -353,24 +356,24 @@ void mgp_map_destroy(struct mgp_map *map);
/// you still need to free their memory explicitly.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate for insertion.
/// Return MGP_ERROR_KEY_ALREADY_EXISTS if a previous mapping already exists.
enum mgp_error mgp_map_insert(struct mgp_map *map, const char *key, const struct mgp_value *value);
enum mgp_error mgp_map_insert(struct mgp_map *map, const char *key, struct mgp_value *value);
/// Get the number of items stored in mgp_map.
/// Current implementation always returns without errors.
enum mgp_error mgp_map_size(const struct mgp_map *map, size_t *result);
enum mgp_error mgp_map_size(struct mgp_map *map, size_t *result);
/// Get the mapped mgp_value to the given character string.
/// Result is NULL if no mapping exists.
enum mgp_error mgp_map_at(const struct mgp_map *map, const char *key, const struct mgp_value **result);
enum mgp_error mgp_map_at(struct mgp_map *map, const char *key, struct mgp_value **result);
/// An item in the mgp_map.
struct mgp_map_item;
/// Get the key of the mapped item.
enum mgp_error mgp_map_item_key(const struct mgp_map_item *item, const char **result);
enum mgp_error mgp_map_item_key(struct mgp_map_item *item, const char **result);
/// Get the value of the mapped item.
enum mgp_error mgp_map_item_value(const struct mgp_map_item *item, const struct mgp_value **result);
enum mgp_error mgp_map_item_value(struct mgp_map_item *item, struct mgp_value **result);
/// An iterator over the items in mgp_map.
struct mgp_map_items_iterator;
@ -379,7 +382,7 @@ struct mgp_map_items_iterator;
/// The resulting mgp_map_items_iterator needs to be deallocated with
/// mgp_map_items_iterator_destroy.
/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_map_items_iterator.
enum mgp_error mgp_map_iter_items(const struct mgp_map *map, struct mgp_memory *memory,
enum mgp_error mgp_map_iter_items(struct mgp_map *map, struct mgp_memory *memory,
struct mgp_map_items_iterator **result);
/// Deallocate memory used by mgp_map_items_iterator.
@ -393,24 +396,23 @@ void mgp_map_items_iterator_destroy(struct mgp_map_items_iterator *it);
/// as the value before, and use them after invoking
/// mgp_map_items_iterator_next.
/// Result is NULL if the end of the iteration has been reached.
enum mgp_error mgp_map_items_iterator_get(const struct mgp_map_items_iterator *it, const struct mgp_map_item **result);
enum mgp_error mgp_map_items_iterator_get(struct mgp_map_items_iterator *it, struct mgp_map_item **result);
/// Advance the iterator to the next item stored in map and return it.
/// The previous pointer obtained through mgp_map_items_iterator_get will
/// be invalidated, but the pointers to key and value will remain valid.
/// Result is NULL if the end of the iteration has been reached.
enum mgp_error mgp_map_items_iterator_next(struct mgp_map_items_iterator *it, const struct mgp_map_item **result);
enum mgp_error mgp_map_items_iterator_next(struct mgp_map_items_iterator *it, struct mgp_map_item **result);
/// Create a path with the copy of the given starting vertex.
/// You need to free the created instance with mgp_path_destroy.
/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_path.
enum mgp_error mgp_path_make_with_start(const struct mgp_vertex *vertex, struct mgp_memory *memory,
struct mgp_path **result);
enum mgp_error mgp_path_make_with_start(struct mgp_vertex *vertex, struct mgp_memory *memory, struct mgp_path **result);
/// Copy a mgp_path.
/// Returned pointer must be freed with mgp_path_destroy.
/// MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate a mgp_path.
enum mgp_error mgp_path_copy(const struct mgp_path *path, struct mgp_memory *memory, struct mgp_path **result);
enum mgp_error mgp_path_copy(struct mgp_path *path, struct mgp_memory *memory, struct mgp_path **result);
/// Free the memory used by the given mgp_path and contained vertices and edges.
void mgp_path_destroy(struct mgp_path *path);
@ -423,24 +425,24 @@ void mgp_path_destroy(struct mgp_path *path);
/// edge, as continued from the current last vertex.
/// Return MGP_ERROR_LOGIC_ERROR if the current last vertex in the path is not part of the given edge.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for path extension.
enum mgp_error mgp_path_expand(struct mgp_path *path, const struct mgp_edge *edge);
enum mgp_error mgp_path_expand(struct mgp_path *path, struct mgp_edge *edge);
/// Get the number of edges in a mgp_path.
/// Current implementation always returns without errors.
enum mgp_error mgp_path_size(const struct mgp_path *path, size_t *result);
enum mgp_error mgp_path_size(struct mgp_path *path, size_t *result);
/// Get the vertex from a path at given index.
/// The valid index range is [0, mgp_path_size].
/// MGP_ERROR_OUT_OF_RANGE is returned if index is out of range.
enum mgp_error mgp_path_vertex_at(const struct mgp_path *path, size_t index, const struct mgp_vertex **result);
enum mgp_error mgp_path_vertex_at(struct mgp_path *path, size_t index, struct mgp_vertex **result);
/// Get the edge from a path at given index.
/// The valid index range is [0, mgp_path_size - 1].
/// MGP_ERROR_OUT_OF_RANGE is returned if index is out of range.
enum mgp_error mgp_path_edge_at(const struct mgp_path *path, size_t index, const struct mgp_edge **result);
enum mgp_error mgp_path_edge_at(struct mgp_path *path, size_t index, struct mgp_edge **result);
/// Result is non-zero if given paths are equal, otherwise 0.
enum mgp_error mgp_path_equal(const struct mgp_path *p1, const struct mgp_path *p2, int *result);
enum mgp_error mgp_path_equal(struct mgp_path *p1, struct mgp_path *p2, int *result);
///@}
@ -468,7 +470,7 @@ enum mgp_error mgp_result_new_record(struct mgp_result *res, struct mgp_result_r
/// Return MGP_ERROR_OUT_OF_RANGE if there is no field named `field_name`.
/// Return MGP_ERROR_LOGIC_ERROR `val` does not satisfy the type of the field name `field_name`.
enum mgp_error mgp_result_record_insert(struct mgp_result_record *record, const char *field_name,
const struct mgp_value *val);
struct mgp_value *val);
///@}
/// @name Graph Constructs
@ -497,21 +499,21 @@ struct mgp_property {
/// Name (key) of a property as a NULL terminated string.
const char *name;
/// Value of the referenced property.
const struct mgp_value *value;
struct mgp_value *value;
};
/// Get the current property pointed to by the iterator.
/// When the mgp_properties_iterator_next is invoked, the previous
/// mgp_property is invalidated and its value must not be used.
/// Result is NULL if the end of the iteration has been reached.
enum mgp_error mgp_properties_iterator_get(const struct mgp_properties_iterator *it,
const struct mgp_property **result);
enum mgp_error mgp_properties_iterator_get(struct mgp_properties_iterator *it, struct mgp_property **result);
/// Advance the iterator to the next property and return it.
/// The previous mgp_property obtained through mgp_properties_iterator_get
/// will be invalidated, and you must not use its value.
/// Result is NULL if the end of the iteration has been reached.
enum mgp_error mgp_properties_iterator_next(struct mgp_properties_iterator *it, const struct mgp_property **result);
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_property.
enum mgp_error mgp_properties_iterator_next(struct mgp_properties_iterator *it, struct mgp_property **result);
/// Iterator over edges of a vertex.
struct mgp_edges_iterator;
@ -527,70 +529,116 @@ struct mgp_vertex_id {
/// Get the ID of given vertex.
/// The ID is only valid for a single query execution, you should never store it
/// globally in a query module.
enum mgp_error mgp_vertex_get_id(const struct mgp_vertex *v, struct mgp_vertex_id *result);
enum mgp_error mgp_vertex_get_id(struct mgp_vertex *v, struct mgp_vertex_id *result);
/// Result is non-zero if the vertex can be modified.
/// The mutability of the vertex is the same as the graph which it is part of. If a vertex is immutable, then edges
/// cannot be added or removed, properties and labels cannot be set or removed and all of the returned edges will be
/// immutable also.
/// Current implementation always returns without errors.
enum mgp_error mgp_vertex_underlying_graph_is_mutable(struct mgp_vertex *v, int *result);
/// Set the value of a property on a vertex.
/// When the value is `null`, then the property is removed from the vertex.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for storing the property.
/// Return MGP_ERROR_IMMUTABLE_OBJECT if `v` is immutable.
/// Return MGP_ERROR_DELETED_OBJECT if `v` has been removed.
/// Return MGP_ERROR_SERIALIZATION_ERROR if `v` has been modified by another transaction.
/// Return MGP_ERROR_VALUE_CONVERSION if `property_value` is vertex, edge or path.
enum mgp_error mgp_vertex_set_property(struct mgp_vertex *v, const char *property_name,
struct mgp_value *property_value);
/// Add the label to the vertex.
/// If the vertex already has the label, this function does nothing.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for storing the label.
/// Return MGP_ERROR_IMMUTABLE_OBJECT if `v` is immutable.
/// Return MGP_ERROR_DELETED_OBJECT if `v` has been removed.
/// Return MGP_ERROR_SERIALIZATION_ERROR if `v` has been modified by another transaction.
enum mgp_error mgp_vertex_add_label(struct mgp_vertex *v, struct mgp_label label);
/// Remove the label from the vertex.
/// If the vertex doesn't have the label, this function does nothing.
/// Return MGP_ERROR_IMMUTABLE_OBJECT if `v` is immutable.
/// Return MGP_ERROR_DELETED_OBJECT if `v` has been removed.
/// Return MGP_ERROR_SERIALIZATION_ERROR if `v` has been modified by another transaction.
enum mgp_error mgp_vertex_remove_label(struct mgp_vertex *v, struct mgp_label label);
/// Copy a mgp_vertex.
/// Resulting pointer must be freed with mgp_vertex_destroy.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_vertex.
enum mgp_error mgp_vertex_copy(const struct mgp_vertex *v, struct mgp_memory *memory, struct mgp_vertex **result);
enum mgp_error mgp_vertex_copy(struct mgp_vertex *v, struct mgp_memory *memory, struct mgp_vertex **result);
/// Free the memory used by a mgp_vertex.
void mgp_vertex_destroy(struct mgp_vertex *v);
/// Result is non-zero if given vertices are equal, otherwise 0.
enum mgp_error mgp_vertex_equal(const struct mgp_vertex *v1, const struct mgp_vertex *v2, int *result);
enum mgp_error mgp_vertex_equal(struct mgp_vertex *v1, struct mgp_vertex *v2, int *result);
/// Get the number of labels a given vertex has.
enum mgp_error mgp_vertex_labels_count(const struct mgp_vertex *v, size_t *result);
/// Return MGP_ERROR_DELETED_OBJECT if `v` has been removed.
enum mgp_error mgp_vertex_labels_count(struct mgp_vertex *v, size_t *result);
/// Get mgp_label in mgp_vertex at given index.
/// Return MGP_ERROR_OUT_OF_RANGE if the index is out of range.
enum mgp_error mgp_vertex_label_at(const struct mgp_vertex *v, size_t index, struct mgp_label *result);
/// Return MGP_ERROR_DELETED_OBJECT if `v` has been removed.
enum mgp_error mgp_vertex_label_at(struct mgp_vertex *v, size_t index, struct mgp_label *result);
/// Result is non-zero if the given vertex has the given label.
enum mgp_error mgp_vertex_has_label(const struct mgp_vertex *v, struct mgp_label label, int *result);
/// Return MGP_ERROR_DELETED_OBJECT if `v` has been removed.
enum mgp_error mgp_vertex_has_label(struct mgp_vertex *v, struct mgp_label label, int *result);
/// Result is non-zero if the given vertex has a label with given name.
enum mgp_error mgp_vertex_has_label_named(const struct mgp_vertex *v, const char *label_name, int *result);
/// Return MGP_ERROR_DELETED_OBJECT if `v` has been removed.
enum mgp_error mgp_vertex_has_label_named(struct mgp_vertex *v, const char *label_name, int *result);
/// Get a copy of a vertex property mapped to a given name.
/// Resulting value must be freed with mgp_value_destroy.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_value.
enum mgp_error mgp_vertex_get_property(const struct mgp_vertex *v, const char *property_name, struct mgp_memory *memory,
/// Return MGP_ERROR_DELETED_OBJECT if `v` has been removed.
enum mgp_error mgp_vertex_get_property(struct mgp_vertex *v, const char *property_name, struct mgp_memory *memory,
struct mgp_value **result);
/// Start iterating over properties stored in the given vertex.
/// The resulting mgp_properties_iterator needs to be deallocated with
/// mgp_properties_iterator_destroy.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_properties_iterator.
enum mgp_error mgp_vertex_iter_properties(const struct mgp_vertex *v, struct mgp_memory *memory,
/// Return MGP_ERROR_DELETED_OBJECT if `v` has been removed.
enum mgp_error mgp_vertex_iter_properties(struct mgp_vertex *v, struct mgp_memory *memory,
struct mgp_properties_iterator **result);
/// Start iterating over inbound edges of the given vertex.
/// The resulting mgp_edges_iterator needs to be deallocated with
/// mgp_edges_iterator_destroy.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_edges_iterator.
enum mgp_error mgp_vertex_iter_in_edges(const struct mgp_vertex *v, struct mgp_memory *memory,
/// Return MGP_ERROR_DELETED_OBJECT if `v` has been removed.
enum mgp_error mgp_vertex_iter_in_edges(struct mgp_vertex *v, struct mgp_memory *memory,
struct mgp_edges_iterator **result);
/// Start iterating over outbound edges of the given vertex.
/// The returned mgp_edges_iterator needs to be deallocated with
/// The resulting mgp_edges_iterator needs to be deallocated with
/// mgp_edges_iterator_destroy.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_edges_iterator.
enum mgp_error mgp_vertex_iter_out_edges(const struct mgp_vertex *v, struct mgp_memory *memory,
/// Return MGP_ERROR_DELETED_OBJECT if `v` has been removed.
enum mgp_error mgp_vertex_iter_out_edges(struct mgp_vertex *v, struct mgp_memory *memory,
struct mgp_edges_iterator **result);
/// Result is non-zero if the edges returned by this iterator can be modified.
/// The mutability of the mgp_edges_iterator is the same as the graph which it belongs to.
/// Current implementation always returns without errors.
enum mgp_error mgp_edges_iterator_underlying_graph_is_mutable(struct mgp_edges_iterator *it, int *result);
/// Get the current edge pointed to by the iterator.
/// When the mgp_edges_iterator_next is invoked, the previous
/// mgp_edge is invalidated and its value must not be used.
/// Result is NULL if the end of the iteration has been reached.
enum mgp_error mgp_edges_iterator_get(const struct mgp_edges_iterator *it, const struct mgp_edge **result);
enum mgp_error mgp_edges_iterator_get(struct mgp_edges_iterator *it, struct mgp_edge **result);
/// Advance the iterator to the next edge and return it.
/// The previous mgp_edge obtained through mgp_edges_iterator_get
/// will be invalidated, and you must not use its value.
/// Result is NULL if the end of the iteration has been reached.
enum mgp_error mgp_edges_iterator_next(struct mgp_edges_iterator *it, const struct mgp_edge **result);
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_edge.
enum mgp_error mgp_edges_iterator_next(struct mgp_edges_iterator *it, struct mgp_edge **result);
/// ID of an edge; valid during a single query execution.
struct mgp_edge_id {
@ -600,75 +648,133 @@ struct mgp_edge_id {
/// Get the ID of given edge.
/// The ID is only valid for a single query execution, you should never store it
/// globally in a query module.
enum mgp_error mgp_edge_get_id(const struct mgp_edge *e, struct mgp_edge_id *result);
enum mgp_error mgp_edge_get_id(struct mgp_edge *e, struct mgp_edge_id *result);
/// Result is non-zero if the edge can be modified.
/// The mutability of the edge is the same as the graph which it is part of. If an edge is immutable, properties cannot
/// be set or removed and all of the returned vertices will be immutable also.
/// Current implementation always returns without errors.
enum mgp_error mgp_edge_underlying_graph_is_mutable(struct mgp_edge *e, int *result);
/// Copy a mgp_edge.
/// Resulting pointer must be freed with mgp_edge_destroy.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_edge.
enum mgp_error mgp_edge_copy(const struct mgp_edge *e, struct mgp_memory *memory, struct mgp_edge **result);
enum mgp_error mgp_edge_copy(struct mgp_edge *e, struct mgp_memory *memory, struct mgp_edge **result);
/// Free the memory used by a mgp_edge.
void mgp_edge_destroy(struct mgp_edge *e);
/// Result is non-zero if given edges are equal, otherwise 0.
enum mgp_error mgp_edge_equal(const struct mgp_edge *e1, const struct mgp_edge *e2, int *result);
enum mgp_error mgp_edge_equal(struct mgp_edge *e1, struct mgp_edge *e2, int *result);
/// Get the type of the given edge.
enum mgp_error mgp_edge_get_type(const struct mgp_edge *e, struct mgp_edge_type *result);
enum mgp_error mgp_edge_get_type(struct mgp_edge *e, struct mgp_edge_type *result);
/// Get the source vertex of the given edge.
/// Resulting vertex is valid until the edge is valid and it must not be used afterwards.
/// Current implementation always returns without errors.
enum mgp_error mgp_edge_get_from(const struct mgp_edge *e, const struct mgp_vertex **result);
enum mgp_error mgp_edge_get_from(struct mgp_edge *e, struct mgp_vertex **result);
/// Get the destination vertex of the given edge.
/// Resulting vertex is valid until the edge is valid and it must not be used afterwards.
/// Current implementation always returns without errors.
enum mgp_error mgp_edge_get_to(const struct mgp_edge *e, const struct mgp_vertex **result);
enum mgp_error mgp_edge_get_to(struct mgp_edge *e, struct mgp_vertex **result);
/// Get a copy of a edge property mapped to a given name.
/// Resulting value must be freed with mgp_value_destroy.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_value.
enum mgp_error mgp_edge_get_property(const struct mgp_edge *e, const char *property_name, struct mgp_memory *memory,
/// Return MGP_ERROR_DELETED_OBJECT if `e` has been removed.
enum mgp_error mgp_edge_get_property(struct mgp_edge *e, const char *property_name, struct mgp_memory *memory,
struct mgp_value **result);
/// Set the value of a property on an edge.
/// When the value is `null`, then the property is removed from the edge.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for storing the property.
/// Return MGP_ERROR_IMMUTABLE_OBJECT if `e` is immutable.
/// Return MGP_ERROR_DELETED_OBJECT if `e` has been removed.
/// Return MGP_ERROR_LOGIC_ERROR if properties on edges are disabled.
/// Return MGP_ERROR_SERIALIZATION_ERROR if `e` has been modified by another transaction.
/// Return MGP_ERROR_VALUE_CONVERSION if `property_value` is vertex, edge or path.
enum mgp_error mgp_edge_set_property(struct mgp_edge *e, const char *property_name, struct mgp_value *property_value);
/// Start iterating over properties stored in the given edge.
/// Resulting mgp_properties_iterator needs to be deallocated with
/// mgp_properties_iterator_destroy.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_properties_iterator.
enum mgp_error mgp_edge_iter_properties(const struct mgp_edge *e, struct mgp_memory *memory,
/// Return MGP_ERROR_DELETED_OBJECT if `e` has been removed.
enum mgp_error mgp_edge_iter_properties(struct mgp_edge *e, struct mgp_memory *memory,
struct mgp_properties_iterator **result);
/// State of the graph database.
struct mgp_graph;
/// Return the vertex corresponding to given ID.
/// Get the vertex corresponding to given ID, or NULL if no such vertex exists.
/// Resulting vertex must be freed using mgp_vertex_destroy.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the vertex or if ID is not valid.
enum mgp_error mgp_graph_get_vertex_by_id(const struct mgp_graph *g, struct mgp_vertex_id id, struct mgp_memory *memory,
enum mgp_error mgp_graph_get_vertex_by_id(struct mgp_graph *g, struct mgp_vertex_id id, struct mgp_memory *memory,
struct mgp_vertex **result);
/// Result is non-zero if the graph can be modified.
/// If a graph is immutable, then vertices cannot be added or removed, and all of the returned vertices will be
/// immutable also.
/// Current implementation always returns without errors.
enum mgp_error mgp_graph_is_mutable(struct mgp_graph *graph, int *result);
/// Add a new vertex to the graph.
/// Return MGP_ERROR_IMMUTABLE_OBJECT if `graph` is immutable.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_vertex.
enum mgp_error mgp_graph_create_vertex(struct mgp_graph *graph, struct mgp_memory *memory, struct mgp_vertex **result);
/// Remove a vertex from the graph.
/// Return MGP_ERROR_IMMUTABLE_OBJECT if `graph` is immutable.
/// Return MGP_ERROR_LOGIC_ERROR if `vertex` has edges.
/// Return MGP_ERROR_SERIALIZATION_ERROR if `vertex` has been modified by another transaction.
enum mgp_error mgp_graph_remove_vertex(struct mgp_graph *graph, struct mgp_vertex *vertex);
/// Add a new directed edge between the two vertices with the specified label.
/// NULL is returned if the the edge creation fails for any reason.
/// Return MGP_ERROR_IMMUTABLE_OBJECT if `graph` is immutable.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_edge.
/// Return MGP_ERROR_DELETED_OBJECT if `from` or `to` has been removed.
/// Return MGP_ERROR_SERIALIZATION_ERROR if `from` or `to` has been modified by another transaction.
enum mgp_error mgp_graph_create_edge(struct mgp_graph *graph, struct mgp_vertex *from, struct mgp_vertex *to,
struct mgp_edge_type type, struct mgp_memory *memory, struct mgp_edge **result);
/// Remove an edge from the graph.
/// Return MGP_ERROR_IMMUTABLE_OBJECT if `graph` is immutable.
/// Return MGP_ERROR_LOGIC_ERROR if `vertex` has edges.
/// Return MGP_ERROR_SERIALIZATION_ERROR if `edge`, its source or destination vertex has been modified by another
/// transaction.
enum mgp_error mgp_graph_remove_edge(struct mgp_graph *graph, struct mgp_edge *edge);
/// Iterator over vertices.
struct mgp_vertices_iterator;
/// Free the memory used by a mgp_vertices_iterator.
void mgp_vertices_iterator_destroy(struct mgp_vertices_iterator *it);
/// Start iterating over vertices of the given graph.
/// Resulting mgp_vertices_iterator needs to be deallocated with mgp_vertices_iterator_destroy.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_vertices_iterator.
enum mgp_error mgp_graph_iter_vertices(const struct mgp_graph *g, struct mgp_memory *memory,
enum mgp_error mgp_graph_iter_vertices(struct mgp_graph *g, struct mgp_memory *memory,
struct mgp_vertices_iterator **result);
/// Result is non-zero if the vertices returned by this iterator can be modified.
/// The mutability of the mgp_vertices_iterator is the same as the graph which it belongs to.
/// Current implementation always returns without errors.
enum mgp_error mgp_vertices_iterator_underlying_graph_is_mutable(struct mgp_vertices_iterator *it, int *result);
/// Get the current vertex pointed to by the iterator.
/// When the mgp_vertices_iterator_next is invoked, the previous
/// mgp_vertex is invalidated and its value must not be used.
/// Result is NULL if the end of the iteration has been reached.
enum mgp_error mgp_vertices_iterator_get(const struct mgp_vertices_iterator *it, const struct mgp_vertex **result);
enum mgp_error mgp_vertices_iterator_get(struct mgp_vertices_iterator *it, struct mgp_vertex **result);
/// Advance the iterator to the next vertex and return it.
/// The previous mgp_vertex obtained through mgp_vertices_iterator_get
/// will be invalidated, and you must not use its value.
/// Result is NULL if the end of the iteration has been reached.
enum mgp_error mgp_vertices_iterator_next(struct mgp_vertices_iterator *it, const struct mgp_vertex **result);
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate a mgp_vertex.
enum mgp_error mgp_vertices_iterator_next(struct mgp_vertices_iterator *it, struct mgp_vertex **result);
///@}
/// @name Type System
@ -687,29 +793,29 @@ struct mgp_type;
///
/// The ANY type is the parent type of all types.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
enum mgp_error mgp_type_any(const struct mgp_type **result);
enum mgp_error mgp_type_any(struct mgp_type **result);
/// Get the type representing boolean values.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
enum mgp_error mgp_type_bool(const struct mgp_type **result);
enum mgp_error mgp_type_bool(struct mgp_type **result);
/// Get the type representing character string values.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
enum mgp_error mgp_type_string(const struct mgp_type **result);
enum mgp_error mgp_type_string(struct mgp_type **result);
/// Get the type representing integer values.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
enum mgp_error mgp_type_int(const struct mgp_type **result);
enum mgp_error mgp_type_int(struct mgp_type **result);
/// Get the type representing floating-point values.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
enum mgp_error mgp_type_float(const struct mgp_type **result);
enum mgp_error mgp_type_float(struct mgp_type **result);
/// Get the type representing any number value.
///
/// This is the parent type for numeric types, i.e. INTEGER and FLOAT.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
enum mgp_error mgp_type_number(const struct mgp_type **result);
enum mgp_error mgp_type_number(struct mgp_type **result);
/// Get the type representing map values.
///
@ -721,35 +827,35 @@ enum mgp_error mgp_type_number(const struct mgp_type **result);
/// @sa mgp_type_node
/// @sa mgp_type_relationship
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
enum mgp_error mgp_type_map(const struct mgp_type **result);
enum mgp_error mgp_type_map(struct mgp_type **result);
/// Get the type representing graph node values.
///
/// Since a node contains a map of properties, the node itself is also of MAP
/// type.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
enum mgp_error mgp_type_node(const struct mgp_type **result);
enum mgp_error mgp_type_node(struct mgp_type **result);
/// Get the type representing graph relationship values.
///
/// Since a relationship contains a map of properties, the relationship itself
/// is also of MAP type.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
enum mgp_error mgp_type_relationship(const struct mgp_type **result);
enum mgp_error mgp_type_relationship(struct mgp_type **result);
/// Get the type representing a graph path (walk) from one node to another.
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
enum mgp_error mgp_type_path(const struct mgp_type **result);
enum mgp_error mgp_type_path(struct mgp_type **result);
/// Build a type representing a list of values of given `element_type`.
///
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
enum mgp_error mgp_type_list(const struct mgp_type *element_type, const struct mgp_type **result);
enum mgp_error mgp_type_list(struct mgp_type *element_type, struct mgp_type **result);
/// Build a type representing either a `null` value or a value of given `type`.
///
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate the new type.
enum mgp_error mgp_type_nullable(const struct mgp_type *type, const struct mgp_type **result);
enum mgp_error mgp_type_nullable(struct mgp_type *type, struct mgp_type **result);
///@}
/// @name Query Module & Procedures
@ -772,15 +878,14 @@ struct mgp_module;
/// Describes a procedure of a query module.
struct mgp_proc;
/// Entry-point for a query module procedure, invoked through openCypher.
/// Entry-point for a query module read procedure, invoked through openCypher.
///
/// Passed in arguments will not live longer than the callback's execution.
/// Therefore, you must not store them globally or use the passed in mgp_memory
/// to allocate global resources.
typedef void (*mgp_proc_cb)(const struct mgp_list *, const struct mgp_graph *, struct mgp_result *,
struct mgp_memory *);
typedef void (*mgp_proc_cb)(struct mgp_list *, struct mgp_graph *, struct mgp_result *, struct mgp_memory *);
/// Register a read-only procedure with a module.
/// Register a read-only procedure to a module.
///
/// The `name` must be a sequence of digits, underscores, lowercase and
/// uppercase Latin letters. The name must begin with a non-digit character.
@ -793,6 +898,17 @@ typedef void (*mgp_proc_cb)(const struct mgp_list *, const struct mgp_graph *, s
enum mgp_error mgp_module_add_read_procedure(struct mgp_module *module, const char *name, mgp_proc_cb cb,
struct mgp_proc **result);
/// Register a read-only procedure to a module.
///
/// The `name` must be a valid identifier, following the same rules as the
/// procedure`name` in mgp_module_add_read_procedure.
///
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for mgp_proc.
/// Return MGP_ERROR_INVALID_ARGUMENT if `name` is not a valid procedure name.
/// RETURN MGP_ERROR_LOGIC_ERROR if a procedure with the same name was already registered.
enum mgp_error mgp_module_add_write_procedure(struct mgp_module *module, const char *name, mgp_proc_cb cb,
struct mgp_proc **result);
/// Add a required argument to a procedure.
///
/// The order of adding arguments will correspond to the order the procedure
@ -807,7 +923,7 @@ enum mgp_error mgp_module_add_read_procedure(struct mgp_module *module, const ch
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for an argument.
/// Return MGP_ERROR_INVALID_ARGUMENT if `name` is not a valid argument name.
/// RETURN MGP_ERROR_LOGIC_ERROR if the procedure already has any optional argument.
enum mgp_error mgp_proc_add_arg(struct mgp_proc *proc, const char *name, const struct mgp_type *type);
enum mgp_error mgp_proc_add_arg(struct mgp_proc *proc, const char *name, struct mgp_type *type);
/// Add an optional argument with a default value to a procedure.
///
@ -831,8 +947,8 @@ enum mgp_error mgp_proc_add_arg(struct mgp_proc *proc, const char *name, const s
/// Return MGP_ERROR_INVALID_ARGUMENT if `name` is not a valid argument name.
/// RETURN MGP_ERROR_OUT_OF_RANGE if `default_value` is a graph element (vertex, edge or path).
/// RETURN MGP_ERROR_LOGIC_ERROR if `default_value` does not satisfy `type`.
enum mgp_error mgp_proc_add_opt_arg(struct mgp_proc *proc, const char *name, const struct mgp_type *type,
const struct mgp_value *default_value);
enum mgp_error mgp_proc_add_opt_arg(struct mgp_proc *proc, const char *name, struct mgp_type *type,
struct mgp_value *default_value);
/// Add a result field to a procedure.
///
@ -845,7 +961,7 @@ enum mgp_error mgp_proc_add_opt_arg(struct mgp_proc *proc, const char *name, con
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for an argument.
/// Return MGP_ERROR_INVALID_ARGUMENT if `name` is not a valid result name.
/// RETURN MGP_ERROR_LOGIC_ERROR if a result field with the same name was already added.
enum mgp_error mgp_proc_add_result(struct mgp_proc *proc, const char *name, const struct mgp_type *type);
enum mgp_error mgp_proc_add_result(struct mgp_proc *proc, const char *name, struct mgp_type *type);
/// Add a result field to a procedure and mark it as deprecated.
///
@ -855,7 +971,7 @@ enum mgp_error mgp_proc_add_result(struct mgp_proc *proc, const char *name, cons
/// Return MGP_ERROR_UNABLE_TO_ALLOCATE if unable to allocate memory for an argument.
/// Return MGP_ERROR_INVALID_ARGUMENT if `name` is not a valid result name.
/// RETURN MGP_ERROR_LOGIC_ERROR if a result field with the same name was already added.
enum mgp_error mgp_proc_add_deprecated_result(struct mgp_proc *proc, const char *name, const struct mgp_type *type);
enum mgp_error mgp_proc_add_deprecated_result(struct mgp_proc *proc, const char *name, struct mgp_type *type);
///@}
/// @name Execution
@ -873,7 +989,7 @@ enum mgp_error mgp_proc_add_deprecated_result(struct mgp_proc *proc, const char
/// their code in order to determine whether they should abort or not. Note that
/// this mechanism is purely cooperative and depends on the procedure doing the
/// checking and aborting on its own.
int mgp_must_abort(const struct mgp_graph *graph);
int mgp_must_abort(struct mgp_graph *graph);
/// @}
@ -892,37 +1008,36 @@ struct mgp_messages;
/// Payload is not null terminated and not a string but rather a byte array.
/// You need to call mgp_message_payload_size() first, to read the size of
/// the payload.
enum mgp_error mgp_message_payload(const struct mgp_message *message, const char **result);
enum mgp_error mgp_message_payload(struct mgp_message *message, const char **result);
/// Return the payload size
enum mgp_error mgp_message_payload_size(const struct mgp_message *message, size_t *result);
/// Get the payload size
enum mgp_error mgp_message_payload_size(struct mgp_message *message, size_t *result);
/// Return the name of topic
enum mgp_error mgp_message_topic_name(const struct mgp_message *message, const char **result);
/// Get the name of topic
enum mgp_error mgp_message_topic_name(struct mgp_message *message, const char **result);
/// Return the key of mgp_message as a byte array
enum mgp_error mgp_message_key(const struct mgp_message *message, const char **result);
/// Get the key of mgp_message as a byte array
enum mgp_error mgp_message_key(struct mgp_message *message, const char **result);
/// Return the key size of mgp_message
enum mgp_error mgp_message_key_size(const struct mgp_message *message, size_t *result);
/// Get the key size of mgp_message
enum mgp_error mgp_message_key_size(struct mgp_message *message, size_t *result);
/// Return the timestamp of mgp_message as a byte array
enum mgp_error mgp_message_timestamp(const struct mgp_message *message, int64_t *result);
/// Get the timestamp of mgp_message as a byte array
enum mgp_error mgp_message_timestamp(struct mgp_message *message, int64_t *result);
/// Return the number of messages contained in the mgp_messages list
/// Get the number of messages contained in the mgp_messages list
/// Current implementation always returns without errors.
enum mgp_error mgp_messages_size(const struct mgp_messages *message, size_t *result);
enum mgp_error mgp_messages_size(struct mgp_messages *message, size_t *result);
/// Return the message from a messages list at given index
enum mgp_error mgp_messages_at(const struct mgp_messages *message, size_t index, const struct mgp_message **result);
/// Get the message from a messages list at given index
enum mgp_error mgp_messages_at(struct mgp_messages *message, size_t index, struct mgp_message **result);
/// Entry-point for a module transformation, invoked through a stream transformation.
///
/// Passed in arguments will not live longer than the callback's execution.
/// Therefore, you must not store them globally or use the passed in mgp_memory
/// to allocate global resources.
typedef void (*mgp_trans_cb)(const struct mgp_messages *, const struct mgp_graph *, struct mgp_result *,
struct mgp_memory *);
typedef void (*mgp_trans_cb)(struct mgp_messages *, struct mgp_graph *, struct mgp_result *, struct mgp_memory *);
/// Register a transformation with a module.
///

View File

@ -16,7 +16,7 @@
// CALL example.procedure(1, 2) YIELD args, result;
// CALL example.procedure(1) YIELD args, result;
// Naturally, you may pass in different arguments or yield less fields.
static void procedure(const struct mgp_list *args, const struct mgp_graph *graph, struct mgp_result *result,
static void procedure(struct mgp_list *args, struct mgp_graph *graph, struct mgp_result *result,
struct mgp_memory *memory) {
size_t args_size = 0;
if (mgp_list_size(args, &args_size) != MGP_ERROR_NO_ERROR) {
@ -27,7 +27,7 @@ static void procedure(const struct mgp_list *args, const struct mgp_graph *graph
goto error_something_went_wrong;
}
for (size_t i = 0; i < args_size; ++i) {
const struct mgp_value *value = NULL;
struct mgp_value *value = NULL;
if (mgp_list_at(args, i, &value) != MGP_ERROR_NO_ERROR) {
goto error_free_list;
}
@ -69,6 +69,11 @@ error_something_went_wrong:
return;
}
static void write_procedure(struct mgp_list *args, struct mgp_graph *graph, struct mgp_result *result,
struct mgp_memory *memory) {
// TODO(antaljanosbenjamin): Finish this example
}
// Each module needs to define mgp_init_module function.
// Here you can register multiple procedures your module supports.
int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) {
@ -76,11 +81,11 @@ int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) {
if (mgp_module_add_read_procedure(module, "procedure", procedure, &proc) != MGP_ERROR_NO_ERROR) {
return 1;
}
const struct mgp_type *any_type = NULL;
struct mgp_type *any_type = NULL;
if (mgp_type_any(&any_type) != MGP_ERROR_NO_ERROR) {
return 1;
}
const struct mgp_type *nullable_any_type = NULL;
struct mgp_type *nullable_any_type = NULL;
if (mgp_type_nullable(any_type, &nullable_any_type) != MGP_ERROR_NO_ERROR) {
return 1;
}
@ -97,14 +102,14 @@ int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) {
return 1;
}
mgp_value_destroy(null_value);
const struct mgp_type *string = NULL;
struct mgp_type *string = NULL;
if (mgp_type_string(&string) != MGP_ERROR_NO_ERROR) {
return 1;
}
if (mgp_proc_add_result(proc, "result", string) != MGP_ERROR_NO_ERROR) {
return 1;
}
const struct mgp_type *list_of_anything = NULL;
struct mgp_type *list_of_anything = NULL;
if (mgp_type_list(nullable_any_type, &list_of_anything) != MGP_ERROR_NO_ERROR) {
return 1;
}

View File

@ -22,6 +22,7 @@
#include "query/interpret/eval.hpp"
#include "query/path.hpp"
#include "query/plan/scoped_profile.hpp"
#include "query/procedure/cypher_types.hpp"
#include "query/procedure/mg_procedure_impl.hpp"
#include "query/procedure/module.hpp"
#include "storage/v2/property_value.hpp"

View File

@ -0,0 +1,9 @@
#pragma once
#include <functional>
#include <memory>
namespace query::procedure {
class CypherType;
using CypherTypePtr = std::unique_ptr<CypherType, std::function<void(CypherType *)>>;
} // namespace query::procedure

View File

@ -7,7 +7,8 @@
#include <memory>
#include <string_view>
#include "query/procedure/mg_procedure_helpers.hpp"
#include "query/procedure/cypher_type_ptr.hpp"
#include "query/procedure/mg_procedure_impl.hpp"
#include "query/typed_value.hpp"
#include "utils/memory.hpp"
#include "utils/pmr/string.hpp"
@ -43,15 +44,13 @@ class CypherType {
virtual const NullableType *AsNullableType() const { return nullptr; }
};
using CypherTypePtr = std::unique_ptr<CypherType, std::function<void(CypherType *)>>;
// Simple Types
class AnyType : public CypherType {
public:
std::string_view GetPresentableName() const override { return "ANY"; }
bool SatisfiesType(const mgp_value &value) const override { return !CallBool(mgp_value_is_null, &value); }
bool SatisfiesType(const mgp_value &value) const override { return value.type != MGP_VALUE_TYPE_NULL; }
bool SatisfiesType(const query::TypedValue &value) const override { return !value.IsNull(); }
};
@ -60,7 +59,7 @@ class BoolType : public CypherType {
public:
std::string_view GetPresentableName() const override { return "BOOLEAN"; }
bool SatisfiesType(const mgp_value &value) const override { return CallBool(mgp_value_is_bool, &value); }
bool SatisfiesType(const mgp_value &value) const override { return value.type == MGP_VALUE_TYPE_BOOL; }
bool SatisfiesType(const query::TypedValue &value) const override { return value.IsBool(); }
};
@ -69,7 +68,7 @@ class StringType : public CypherType {
public:
std::string_view GetPresentableName() const override { return "STRING"; }
bool SatisfiesType(const mgp_value &value) const override { return CallBool(mgp_value_is_string, &value); }
bool SatisfiesType(const mgp_value &value) const override { return value.type == MGP_VALUE_TYPE_STRING; }
bool SatisfiesType(const query::TypedValue &value) const override { return value.IsString(); }
};
@ -78,7 +77,7 @@ class IntType : public CypherType {
public:
std::string_view GetPresentableName() const override { return "INTEGER"; }
bool SatisfiesType(const mgp_value &value) const override { return CallBool(mgp_value_is_int, &value); }
bool SatisfiesType(const mgp_value &value) const override { return value.type == MGP_VALUE_TYPE_INT; }
bool SatisfiesType(const query::TypedValue &value) const override { return value.IsInt(); }
};
@ -87,7 +86,7 @@ class FloatType : public CypherType {
public:
std::string_view GetPresentableName() const override { return "FLOAT"; }
bool SatisfiesType(const mgp_value &value) const override { return CallBool(mgp_value_is_double, &value); }
bool SatisfiesType(const mgp_value &value) const override { return value.type == MGP_VALUE_TYPE_DOUBLE; }
bool SatisfiesType(const query::TypedValue &value) const override { return value.IsDouble(); }
};
@ -97,7 +96,7 @@ class NumberType : public CypherType {
std::string_view GetPresentableName() const override { return "NUMBER"; }
bool SatisfiesType(const mgp_value &value) const override {
return CallBool(mgp_value_is_int, &value) || CallBool(mgp_value_is_double, &value);
return value.type == MGP_VALUE_TYPE_INT || value.type == MGP_VALUE_TYPE_DOUBLE;
}
bool SatisfiesType(const query::TypedValue &value) const override { return value.IsInt() || value.IsDouble(); }
@ -107,7 +106,7 @@ class NodeType : public CypherType {
public:
std::string_view GetPresentableName() const override { return "NODE"; }
bool SatisfiesType(const mgp_value &value) const override { return CallBool(mgp_value_is_vertex, &value); }
bool SatisfiesType(const mgp_value &value) const override { return value.type == MGP_VALUE_TYPE_VERTEX; }
bool SatisfiesType(const query::TypedValue &value) const override { return value.IsVertex(); }
};
@ -116,7 +115,7 @@ class RelationshipType : public CypherType {
public:
std::string_view GetPresentableName() const override { return "RELATIONSHIP"; }
bool SatisfiesType(const mgp_value &value) const override { return CallBool(mgp_value_is_edge, &value); }
bool SatisfiesType(const mgp_value &value) const override { return value.type == MGP_VALUE_TYPE_EDGE; }
bool SatisfiesType(const query::TypedValue &value) const override { return value.IsEdge(); }
};
@ -125,7 +124,7 @@ class PathType : public CypherType {
public:
std::string_view GetPresentableName() const override { return "PATH"; }
bool SatisfiesType(const mgp_value &value) const override { return CallBool(mgp_value_is_path, &value); }
bool SatisfiesType(const mgp_value &value) const override { return value.type == MGP_VALUE_TYPE_PATH; }
bool SatisfiesType(const query::TypedValue &value) const override { return value.IsPath(); }
};
@ -142,8 +141,7 @@ class MapType : public CypherType {
std::string_view GetPresentableName() const override { return "MAP"; }
bool SatisfiesType(const mgp_value &value) const override {
return CallBool(mgp_value_is_map, &value) || CallBool(mgp_value_is_vertex, &value) ||
CallBool(mgp_value_is_edge, &value);
return value.type == MGP_VALUE_TYPE_MAP || value.type == MGP_VALUE_TYPE_VERTEX || value.type == MGP_VALUE_TYPE_EDGE;
}
bool SatisfiesType(const query::TypedValue &value) const override {
@ -168,13 +166,13 @@ class ListType : public CypherType {
std::string_view GetPresentableName() const override { return presentable_name_; }
bool SatisfiesType(const mgp_value &value) const override {
if (!CallBool(mgp_value_is_list, &value)) {
if (value.type != MGP_VALUE_TYPE_LIST) {
return false;
}
auto *list = Call<const mgp_list *>(mgp_value_get_list, &value);
const auto list_size = Call<size_t>(mgp_list_size, list);
auto *list = value.list_v;
const auto list_size = list->elems.size();
for (size_t i = 0; i < list_size; ++i) {
if (!element_type_->SatisfiesType(*Call<const mgp_value *>(mgp_list_at, list, i))) {
if (!element_type_->SatisfiesType(list->elems[i])) {
return false;
};
}
@ -235,7 +233,7 @@ class NullableType : public CypherType {
std::string_view GetPresentableName() const override { return presentable_name_; }
bool SatisfiesType(const mgp_value &value) const override {
return CallBool(mgp_value_is_null, &value) || type_->SatisfiesType(value);
return value.type == MGP_VALUE_TYPE_NULL || type_->SatisfiesType(value);
}
bool SatisfiesType(const query::TypedValue &value) const override {

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@
#include "integrations/kafka/consumer.hpp"
#include "query/context.hpp"
#include "query/db_accessor.hpp"
#include "query/procedure/cypher_types.hpp"
#include "query/procedure/cypher_type_ptr.hpp"
#include "query/typed_value.hpp"
#include "storage/v2/view.hpp"
#include "utils/memory.hpp"
@ -56,7 +56,7 @@ struct mgp_value {
/// 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 *);
mgp_value(const query::TypedValue &, mgp_graph *, utils::MemoryResource *);
/// Construct by copying storage::PropertyValue using utils::MemoryResource.
/// @throw std::bad_alloc
@ -165,13 +165,13 @@ struct mgp_map {
struct mgp_map_item {
const char *key;
const mgp_value *value;
mgp_value *value;
};
struct mgp_map_items_iterator {
using allocator_type = utils::Allocator<mgp_map_items_iterator>;
mgp_map_items_iterator(const mgp_map *map, utils::MemoryResource *memory)
mgp_map_items_iterator(mgp_map *map, 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();
@ -189,7 +189,7 @@ struct mgp_map_items_iterator {
utils::MemoryResource *GetMemoryResource() const { return memory; }
utils::MemoryResource *memory;
const mgp_map *map;
mgp_map *map;
decltype(map->items.begin()) current_it;
mgp_map_item current;
};
@ -204,7 +204,7 @@ struct mgp_vertex {
// 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
mgp_vertex(query::VertexAccessor v, mgp_graph *graph, utils::MemoryResource *memory) noexcept
: memory(memory), impl(v), graph(graph) {}
mgp_vertex(const mgp_vertex &other, utils::MemoryResource *memory) noexcept
@ -230,7 +230,7 @@ struct mgp_vertex {
utils::MemoryResource *memory;
query::VertexAccessor impl;
const mgp_graph *graph;
mgp_graph *graph;
};
struct mgp_edge {
@ -243,7 +243,9 @@ struct mgp_edge {
// 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
static mgp_edge *Copy(const mgp_edge &edge, mgp_memory &memory);
mgp_edge(const query::EdgeAccessor &impl, 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
@ -334,20 +336,20 @@ struct mgp_properties_iterator {
// need to be visible in method definitions.
utils::MemoryResource *memory;
const mgp_graph *graph;
mgp_graph *graph;
std::remove_reference_t<decltype(*std::declval<query::VertexAccessor>().Properties(graph->view))> pvs;
decltype(pvs.begin()) current_it;
std::optional<std::pair<utils::pmr::string, mgp_value>> current;
mgp_property property{nullptr, nullptr};
// Construct with no properties.
explicit mgp_properties_iterator(const mgp_graph *graph, utils::MemoryResource *memory)
explicit mgp_properties_iterator(mgp_graph *graph, 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(const mgp_graph *graph, decltype(pvs) pvs, utils::MemoryResource *memory)
mgp_properties_iterator(mgp_graph *graph, decltype(pvs) pvs, utils::MemoryResource *memory)
: memory(memory), graph(graph), pvs(std::move(pvs)), current_it(this->pvs.begin()) {
if (current_it != this->pvs.end()) {
current.emplace(utils::pmr::string(graph->impl->PropertyToName(current_it->first), memory),
@ -408,7 +410,7 @@ struct mgp_vertices_iterator {
using allocator_type = utils::Allocator<mgp_vertices_iterator>;
/// @throw anything VerticesIterable may throw
mgp_vertices_iterator(const mgp_graph *graph, utils::MemoryResource *memory)
mgp_vertices_iterator(mgp_graph *graph, utils::MemoryResource *memory)
: memory(memory), graph(graph), vertices(graph->impl->Vertices(graph->view)), current_it(vertices.begin()) {
if (current_it != vertices.end()) {
current_v.emplace(*current_it, graph, memory);
@ -418,7 +420,7 @@ struct mgp_vertices_iterator {
utils::MemoryResource *GetMemoryResource() const { return memory; }
utils::MemoryResource *memory;
const mgp_graph *graph;
mgp_graph *graph;
decltype(graph->impl->Vertices(graph->view)) vertices;
decltype(vertices.begin()) current_it;
std::optional<mgp_vertex> current_v;
@ -433,14 +435,24 @@ struct mgp_proc {
/// @throw std::bad_alloc
/// @throw std::length_error
mgp_proc(const char *name, mgp_proc_cb cb, utils::MemoryResource *memory)
: name(name, memory), cb(cb), args(memory), opt_args(memory), results(memory) {}
mgp_proc(const char *name, mgp_proc_cb cb, utils::MemoryResource *memory, bool is_write_procedure)
: name(name, memory),
cb(cb),
args(memory),
opt_args(memory),
results(memory),
is_write_procedure(is_write_procedure) {}
/// @throw std::bad_alloc
/// @throw std::length_error
mgp_proc(const char *name, std::function<void(const mgp_list *, const mgp_graph *, mgp_result *, mgp_memory *)> cb,
utils::MemoryResource *memory)
: name(name, memory), cb(cb), args(memory), opt_args(memory), results(memory) {}
mgp_proc(const char *name, std::function<void(mgp_list *, mgp_graph *, mgp_result *, mgp_memory *)> cb,
utils::MemoryResource *memory, bool is_write_procedure)
: name(name, memory),
cb(cb),
args(memory),
opt_args(memory),
results(memory),
is_write_procedure(is_write_procedure) {}
/// @throw std::bad_alloc
/// @throw std::length_error
@ -449,14 +461,16 @@ struct mgp_proc {
cb(other.cb),
args(other.args, memory),
opt_args(other.opt_args, memory),
results(other.results, memory) {}
results(other.results, memory),
is_write_procedure(other.is_write_procedure) {}
mgp_proc(mgp_proc &&other, 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) {}
results(std::move(other.results), memory),
is_write_procedure(other.is_write_procedure) {}
mgp_proc(const mgp_proc &other) = default;
mgp_proc(mgp_proc &&other) = default;
@ -469,13 +483,14 @@ struct mgp_proc {
/// Name of the procedure.
utils::pmr::string name;
/// Entry-point for the procedure.
std::function<void(const mgp_list *, const mgp_graph *, mgp_result *, mgp_memory *)> cb;
std::function<void(mgp_list *, mgp_graph *, mgp_result *, mgp_memory *)> cb;
/// Required, positional arguments as a (name, type) pair.
utils::pmr::vector<std::pair<utils::pmr::string, const query::procedure::CypherType *>> args;
/// Optional positional arguments as a (name, type, default_value) tuple.
utils::pmr::vector<std::tuple<utils::pmr::string, const query::procedure::CypherType *, query::TypedValue>> opt_args;
/// Fields this procedure returns, as a (name -> (type, is_deprecated)) map.
utils::pmr::map<utils::pmr::string, std::pair<const query::procedure::CypherType *, bool>> results;
bool is_write_procedure{false};
};
struct mgp_trans {
@ -488,8 +503,7 @@ struct mgp_trans {
/// @throw std::bad_alloc
/// @throw std::length_error
mgp_trans(const char *name,
std::function<void(const mgp_messages *, const mgp_graph *, mgp_result *, mgp_memory *)> cb,
mgp_trans(const char *name, std::function<void(mgp_messages *, mgp_graph *, mgp_result *, mgp_memory *)> cb,
utils::MemoryResource *memory)
: name(name, memory), cb(cb), results(memory) {}
@ -512,7 +526,7 @@ struct mgp_trans {
/// Name of the transformation.
utils::pmr::string name;
/// Entry-point for the transformation.
std::function<void(const mgp_messages *, const mgp_graph *, mgp_result *, mgp_memory *)> cb;
std::function<void(mgp_messages *, mgp_graph *, mgp_result *, mgp_memory *)> cb;
/// Fields this transformation returns.
utils::pmr::map<utils::pmr::string, std::pair<const query::procedure::CypherType *, bool>> results;
};

View File

@ -9,6 +9,7 @@ extern "C" {
#include "fmt/format.h"
#include "py/py.hpp"
#include "query/procedure/mg_procedure_helpers.hpp"
#include "query/procedure/py_module.hpp"
#include "utils/file.hpp"
#include "utils/logging.hpp"
@ -91,16 +92,16 @@ void RegisterMgLoad(ModuleRegistry *module_registry, utils::RWLock *lock, Builti
}
lock->lock_shared();
};
auto load_all_cb = [module_registry, with_unlock_shared](const mgp_list * /*args*/, const mgp_graph * /*graph*/,
auto load_all_cb = [module_registry, with_unlock_shared](mgp_list * /*args*/, mgp_graph * /*graph*/,
mgp_result * /*result*/, mgp_memory * /*memory*/) {
with_unlock_shared([&]() { module_registry->UnloadAndLoadModulesFromDirectories(); });
};
mgp_proc load_all("load_all", load_all_cb, utils::NewDeleteResource());
mgp_proc load_all("load_all", load_all_cb, utils::NewDeleteResource(), false);
module->AddProcedure("load_all", std::move(load_all));
auto load_cb = [module_registry, with_unlock_shared](const mgp_list *args, const mgp_graph * /*graph*/,
mgp_result *result, mgp_memory * /*memory*/) {
auto load_cb = [module_registry, with_unlock_shared](mgp_list *args, mgp_graph * /*graph*/, mgp_result *result,
mgp_memory * /*memory*/) {
MG_ASSERT(Call<size_t>(mgp_list_size, args) == 1U, "Should have been type checked already");
const auto *arg = Call<const mgp_value *>(mgp_list_at, args, 0);
auto *arg = Call<mgp_value *>(mgp_list_at, args, 0);
MG_ASSERT(CallBool(mgp_value_is_string, arg), "Should have been type checked already");
bool succ = false;
with_unlock_shared([&]() {
@ -115,15 +116,15 @@ void RegisterMgLoad(ModuleRegistry *module_registry, utils::RWLock *lock, Builti
MG_ASSERT(mgp_result_set_error_msg(result, "Failed to (re)load the module.") == MGP_ERROR_NO_ERROR);
}
};
mgp_proc load("load", load_cb, utils::NewDeleteResource());
MG_ASSERT(mgp_proc_add_arg(&load, "module_name", Call<const mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
mgp_proc load("load", load_cb, utils::NewDeleteResource(), false);
MG_ASSERT(mgp_proc_add_arg(&load, "module_name", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
module->AddProcedure("load", std::move(load));
}
void RegisterMgProcedures(
// We expect modules to be sorted by name.
const std::map<std::string, std::unique_ptr<Module>, std::less<>> *all_modules, BuiltinModule *module) {
auto procedures_cb = [all_modules](const mgp_list * /*args*/, const mgp_graph * /*graph*/, mgp_result *result,
auto procedures_cb = [all_modules](mgp_list * /*args*/, mgp_graph * /*graph*/, mgp_result *result,
mgp_memory *memory) {
// Iterating over all_modules assumes that the standard mechanism of custom
// procedure invocations takes the ModuleRegistry::lock_ with READ access.
@ -178,16 +179,15 @@ void RegisterMgProcedures(
}
}
};
mgp_proc procedures("procedures", procedures_cb, utils::NewDeleteResource());
MG_ASSERT(mgp_proc_add_result(&procedures, "name", Call<const mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
MG_ASSERT(mgp_proc_add_result(&procedures, "signature", Call<const mgp_type *>(mgp_type_string)) ==
MGP_ERROR_NO_ERROR);
mgp_proc procedures("procedures", procedures_cb, utils::NewDeleteResource(), false);
MG_ASSERT(mgp_proc_add_result(&procedures, "name", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
MG_ASSERT(mgp_proc_add_result(&procedures, "signature", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
module->AddProcedure("procedures", std::move(procedures));
}
void RegisterMgTransformations(const std::map<std::string, std::unique_ptr<Module>, std::less<>> *all_modules,
BuiltinModule *module) {
auto procedures_cb = [all_modules](const mgp_list * /*unused*/, const mgp_graph * /*unused*/, mgp_result *result,
auto procedures_cb = [all_modules](mgp_list * /*unused*/, mgp_graph * /*unused*/, mgp_result *result,
mgp_memory *memory) {
for (const auto &[module_name, module] : *all_modules) {
// Return the results in sorted order by module and by transformation.
@ -225,8 +225,8 @@ void RegisterMgTransformations(const std::map<std::string, std::unique_ptr<Modul
}
}
};
mgp_proc procedures("transformations", procedures_cb, utils::NewDeleteResource());
MG_ASSERT(mgp_proc_add_result(&procedures, "name", Call<const mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
mgp_proc procedures("transformations", procedures_cb, utils::NewDeleteResource(), false);
MG_ASSERT(mgp_proc_add_result(&procedures, "name", Call<mgp_type *>(mgp_type_string)) == MGP_ERROR_NO_ERROR);
module->AddProcedure("transformations", std::move(procedures));
}

View File

@ -41,7 +41,7 @@ PyObject *DisallowPickleAndCopy(PyObject *self, PyObject *Py_UNUSED(ignored)) {
// clang-format off
struct PyGraph {
PyObject_HEAD
const mgp_graph *graph;
mgp_graph *graph;
mgp_memory *memory;
};
// clang-format on
@ -54,7 +54,7 @@ struct PyVerticesIterator {
};
// clang-format on
PyObject *MakePyVertex(const mgp_vertex &vertex, PyGraph *py_graph);
PyObject *MakePyVertex(mgp_vertex &vertex, PyGraph *py_graph);
void PyVerticesIteratorDealloc(PyVerticesIterator *self) {
MG_ASSERT(self->it);
@ -71,7 +71,7 @@ PyObject *PyVerticesIteratorGet(PyVerticesIterator *self, PyObject *Py_UNUSED(ig
MG_ASSERT(self->it);
MG_ASSERT(self->py_graph);
MG_ASSERT(self->py_graph->graph);
const auto *vertex = Call<const mgp_vertex *>(mgp_vertices_iterator_get, self->it);
auto *vertex = Call<mgp_vertex *>(mgp_vertices_iterator_get, self->it);
if (!vertex) Py_RETURN_NONE;
return MakePyVertex(*vertex, self->py_graph);
}
@ -80,7 +80,7 @@ PyObject *PyVerticesIteratorNext(PyVerticesIterator *self, PyObject *Py_UNUSED(i
MG_ASSERT(self->it);
MG_ASSERT(self->py_graph);
MG_ASSERT(self->py_graph->graph);
const auto *vertex = Call<const mgp_vertex *>(mgp_vertices_iterator_next, self->it);
auto *vertex = Call<mgp_vertex *>(mgp_vertices_iterator_next, self->it);
if (!vertex) Py_RETURN_NONE;
return MakePyVertex(*vertex, self->py_graph);
}
@ -114,7 +114,7 @@ struct PyEdgesIterator {
};
// clang-format on
PyObject *MakePyEdge(const mgp_edge &edge, PyGraph *py_graph);
PyObject *MakePyEdge(mgp_edge &edge, PyGraph *py_graph);
void PyEdgesIteratorDealloc(PyEdgesIterator *self) {
MG_ASSERT(self->it);
@ -131,7 +131,7 @@ PyObject *PyEdgesIteratorGet(PyEdgesIterator *self, PyObject *Py_UNUSED(ignored)
MG_ASSERT(self->it);
MG_ASSERT(self->py_graph);
MG_ASSERT(self->py_graph->graph);
const auto *edge = Call<const mgp_edge *>(mgp_edges_iterator_get, self->it);
auto *edge = Call<mgp_edge *>(mgp_edges_iterator_get, self->it);
if (!edge) Py_RETURN_NONE;
return MakePyEdge(*edge, self->py_graph);
}
@ -140,7 +140,7 @@ PyObject *PyEdgesIteratorNext(PyEdgesIterator *self, PyObject *Py_UNUSED(ignored
MG_ASSERT(self->it);
MG_ASSERT(self->py_graph);
MG_ASSERT(self->py_graph->graph);
const auto *edge = Call<const mgp_edge *>(mgp_edges_iterator_next, self->it);
auto *edge = Call<mgp_edge *>(mgp_edges_iterator_next, self->it);
if (!edge) Py_RETURN_NONE;
return MakePyEdge(*edge, self->py_graph);
}
@ -241,7 +241,7 @@ static PyTypeObject PyGraphType = {
};
// clang-format on
PyObject *MakePyGraph(const mgp_graph *graph, mgp_memory *memory) {
PyObject *MakePyGraph(mgp_graph *graph, mgp_memory *memory) {
MG_ASSERT(!graph || (graph && memory));
auto *py_graph = PyObject_New(PyGraph, &PyGraphType);
if (!py_graph) return nullptr;
@ -253,7 +253,7 @@ PyObject *MakePyGraph(const mgp_graph *graph, mgp_memory *memory) {
// clang-format off
struct PyCypherType {
PyObject_HEAD
const mgp_type *type;
mgp_type *type;
};
// clang-format on
@ -267,7 +267,7 @@ static PyTypeObject PyCypherTypeType = {
};
// clang-format on
PyObject *MakePyCypherType(const mgp_type *type) {
PyObject *MakePyCypherType(mgp_type *type) {
MG_ASSERT(type);
auto *py_type = PyObject_New(PyCypherType, &PyCypherTypeType);
if (!py_type) return nullptr;
@ -291,7 +291,7 @@ PyObject *PyQueryProcAddArg(PyQueryProc *self, PyObject *args) {
PyErr_SetString(PyExc_TypeError, "Expected a _mgp.Type.");
return nullptr;
}
const auto *type = reinterpret_cast<PyCypherType *>(py_type)->type;
auto *type = reinterpret_cast<PyCypherType *>(py_type)->type;
if (mgp_proc_add_arg(self->proc, name, type) != MGP_ERROR_NO_ERROR) {
PyErr_SetString(PyExc_ValueError, "Invalid call to mgp_proc_add_arg.");
return nullptr;
@ -309,7 +309,7 @@ PyObject *PyQueryProcAddOptArg(PyQueryProc *self, PyObject *args) {
PyErr_SetString(PyExc_TypeError, "Expected a _mgp.Type.");
return nullptr;
}
const auto *type = reinterpret_cast<PyCypherType *>(py_type)->type;
auto *type = reinterpret_cast<PyCypherType *>(py_type)->type;
mgp_memory memory{self->proc->opt_args.get_allocator().GetMemoryResource()};
mgp_value *value{nullptr};
try {
@ -346,7 +346,7 @@ PyObject *PyQueryProcAddResult(PyQueryProc *self, PyObject *args) {
PyErr_SetString(PyExc_TypeError, "Expected a _mgp.Type.");
return nullptr;
}
const auto *type = reinterpret_cast<PyCypherType *>(py_type)->type;
auto *type = reinterpret_cast<PyCypherType *>(py_type)->type;
if (mgp_proc_add_result(self->proc, name, type) != MGP_ERROR_NO_ERROR) {
PyErr_SetString(PyExc_ValueError, "Invalid call to mgp_proc_add_result.");
return nullptr;
@ -363,7 +363,7 @@ PyObject *PyQueryProcAddDeprecatedResult(PyQueryProc *self, PyObject *args) {
PyErr_SetString(PyExc_TypeError, "Expected a _mgp.Type.");
return nullptr;
}
const auto *type = reinterpret_cast<PyCypherType *>(py_type)->type;
auto *type = reinterpret_cast<PyCypherType *>(py_type)->type;
if (const auto err = mgp_proc_add_deprecated_result(self->proc, name, type); err != MGP_ERROR_NO_ERROR) {
PyErr_SetString(PyExc_ValueError, "Invalid call to mgp_proc_add_deprecated_result.");
return nullptr;
@ -404,13 +404,13 @@ struct PyQueryModule {
struct PyMessages {
PyObject_HEAD;
const mgp_messages *messages;
mgp_messages *messages;
mgp_memory *memory;
};
struct PyMessage {
PyObject_HEAD;
const mgp_message *message;
mgp_message *message;
const PyMessages *messages;
mgp_memory *memory;
};
@ -529,8 +529,8 @@ PyObject *PyMessagesGetMessageAt(PyMessages *self, PyObject *args) {
MG_ASSERT(self->memory);
int64_t id = 0;
if (!PyArg_ParseTuple(args, "l", &id)) return nullptr;
if (id < 0) return nullptr;
const auto *message = Call<const mgp_message *>(mgp_messages_at, self->messages, id);
if (id < 0 || id >= self->messages->messages.size()) return nullptr;
auto *message = &self->messages->messages[id];
// NOLINTNEXTLINE
auto *py_message = PyObject_New(PyMessage, &PyMessageType);
if (!py_message) {
@ -574,7 +574,7 @@ static PyTypeObject PyMessagesType = {
.tp_methods = PyMessagesMethods,
};
PyObject *MakePyMessages(const mgp_messages *msgs, mgp_memory *memory) {
PyObject *MakePyMessages(mgp_messages *msgs, mgp_memory *memory) {
MG_ASSERT(!msgs || (msgs && memory));
// NOLINTNEXTLINE
auto *py_messages = PyObject_New(PyMessages, &PyMessagesType);
@ -584,14 +584,14 @@ PyObject *MakePyMessages(const mgp_messages *msgs, mgp_memory *memory) {
return reinterpret_cast<PyObject *>(py_messages);
}
py::Object MgpListToPyTuple(const mgp_list *list, PyGraph *py_graph) {
py::Object MgpListToPyTuple(mgp_list *list, PyGraph *py_graph) {
MG_ASSERT(list);
MG_ASSERT(py_graph);
const auto len = Call<size_t>(mgp_list_size, list);
const auto len = list->elems.size();
py::Object py_tuple(PyTuple_New(len));
if (!py_tuple) return nullptr;
for (size_t i = 0; i < len; ++i) {
auto elem = MgpValueToPyObject(*Call<const mgp_value *>(mgp_list_at, list, i), py_graph);
auto elem = MgpValueToPyObject(list->elems[i], py_graph);
if (!elem) return nullptr;
// Explicitly convert `py_tuple`, which is `py::Object`, via static_cast.
// Then the macro will cast it to `PyTuple *`.
@ -600,7 +600,7 @@ py::Object MgpListToPyTuple(const mgp_list *list, PyGraph *py_graph) {
return py_tuple;
}
py::Object MgpListToPyTuple(const mgp_list *list, PyObject *py_graph) {
py::Object MgpListToPyTuple(mgp_list *list, PyObject *py_graph) {
if (Py_TYPE(py_graph) != &PyGraphType) {
PyErr_SetString(PyExc_TypeError, "Expected a _mgp.Graph.");
return nullptr;
@ -689,7 +689,7 @@ std::optional<py::ExceptionInfo> AddMultipleRecordsFromPython(mgp_result *result
return std::nullopt;
}
void CallPythonProcedure(const py::Object &py_cb, const mgp_list *args, const mgp_graph *graph, mgp_result *result,
void CallPythonProcedure(const py::Object &py_cb, mgp_list *args, mgp_graph *graph, mgp_result *result,
mgp_memory *memory) {
auto gil = py::EnsureGIL();
@ -769,8 +769,8 @@ void CallPythonProcedure(const py::Object &py_cb, const mgp_list *args, const mg
}
}
void CallPythonTransformation(const py::Object &py_cb, const mgp_messages *msgs, const mgp_graph *graph,
mgp_result *result, mgp_memory *memory) {
void CallPythonTransformation(const py::Object &py_cb, mgp_messages *msgs, mgp_graph *graph, mgp_result *result,
mgp_memory *memory) {
auto gil = py::EnsureGIL();
auto error_to_msg = [](const std::optional<py::ExceptionInfo> &exc_info) -> std::optional<std::string> {
@ -868,10 +868,10 @@ PyObject *PyQueryModuleAddReadProcedure(PyQueryModule *self, PyObject *cb) {
auto *memory = self->module->procedures.get_allocator().GetMemoryResource();
mgp_proc proc(
name,
[py_cb](const mgp_list *args, const mgp_graph *graph, mgp_result *result, mgp_memory *memory) {
[py_cb](mgp_list *args, mgp_graph *graph, mgp_result *result, mgp_memory *memory) {
CallPythonProcedure(py_cb, args, graph, result, memory);
},
memory);
memory, false);
const auto &[proc_it, did_insert] = self->module->procedures.emplace(name, std::move(proc));
if (!did_insert) {
PyErr_SetString(PyExc_ValueError, "Already registered a procedure with the same name.");
@ -900,7 +900,7 @@ PyObject *PyQueryModuleAddTransformation(PyQueryModule *self, PyObject *cb) {
auto *memory = self->module->transformations.get_allocator().GetMemoryResource();
mgp_trans trans(
name,
[py_cb](const mgp_messages *msgs, const mgp_graph *graph, mgp_result *result, mgp_memory *memory) {
[py_cb](mgp_messages *msgs, mgp_graph *graph, mgp_result *result, mgp_memory *memory) {
CallPythonTransformation(py_cb, msgs, graph, result, memory);
},
memory);
@ -946,7 +946,7 @@ PyObject *PyMgpModuleTypeNullable(PyObject *mod, PyObject *obj) {
return nullptr;
}
auto *py_type = reinterpret_cast<PyCypherType *>(obj);
return MakePyCypherType(Call<const mgp_type *>(mgp_type_nullable, py_type->type));
return MakePyCypherType(Call<mgp_type *>(mgp_type_nullable, py_type->type));
}
PyObject *PyMgpModuleTypeList(PyObject *mod, PyObject *obj) {
@ -955,47 +955,47 @@ PyObject *PyMgpModuleTypeList(PyObject *mod, PyObject *obj) {
return nullptr;
}
auto *py_type = reinterpret_cast<PyCypherType *>(obj);
return MakePyCypherType(Call<const mgp_type *>(mgp_type_list, py_type->type));
return MakePyCypherType(Call<mgp_type *>(mgp_type_list, py_type->type));
}
PyObject *PyMgpModuleTypeAny(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
return MakePyCypherType(Call<const mgp_type *>(mgp_type_any));
return MakePyCypherType(Call<mgp_type *>(mgp_type_any));
}
PyObject *PyMgpModuleTypeBool(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
return MakePyCypherType(Call<const mgp_type *>(mgp_type_bool));
return MakePyCypherType(Call<mgp_type *>(mgp_type_bool));
}
PyObject *PyMgpModuleTypeString(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
return MakePyCypherType(Call<const mgp_type *>(mgp_type_string));
return MakePyCypherType(Call<mgp_type *>(mgp_type_string));
}
PyObject *PyMgpModuleTypeInt(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
return MakePyCypherType(Call<const mgp_type *>(mgp_type_int));
return MakePyCypherType(Call<mgp_type *>(mgp_type_int));
}
PyObject *PyMgpModuleTypeFloat(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
return MakePyCypherType(Call<const mgp_type *>(mgp_type_float));
return MakePyCypherType(Call<mgp_type *>(mgp_type_float));
}
PyObject *PyMgpModuleTypeNumber(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
return MakePyCypherType(Call<const mgp_type *>(mgp_type_number));
return MakePyCypherType(Call<mgp_type *>(mgp_type_number));
}
PyObject *PyMgpModuleTypeMap(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
return MakePyCypherType(Call<const mgp_type *>(mgp_type_map));
return MakePyCypherType(Call<mgp_type *>(mgp_type_map));
}
PyObject *PyMgpModuleTypeNode(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
return MakePyCypherType(Call<const mgp_type *>(mgp_type_node));
return MakePyCypherType(Call<mgp_type *>(mgp_type_node));
}
PyObject *PyMgpModuleTypeRelationship(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
return MakePyCypherType(Call<const mgp_type *>(mgp_type_relationship));
return MakePyCypherType(Call<mgp_type *>(mgp_type_relationship));
}
PyObject *PyMgpModuleTypePath(PyObject * /*mod*/, PyObject *Py_UNUSED(ignored)) {
return MakePyCypherType(Call<const mgp_type *>(mgp_type_path));
return MakePyCypherType(Call<mgp_type *>(mgp_type_path));
}
static PyMethodDef PyMgpModuleMethods[] = {
@ -1051,7 +1051,7 @@ PyObject *PyPropertiesIteratorGet(PyPropertiesIterator *self, PyObject *Py_UNUSE
MG_ASSERT(self->it);
MG_ASSERT(self->py_graph);
MG_ASSERT(self->py_graph->graph);
const auto *property = Call<const mgp_property *>(mgp_properties_iterator_get, self->it);
auto *property = Call<mgp_property *>(mgp_properties_iterator_get, self->it);
if (!property) Py_RETURN_NONE;
py::Object py_name(PyUnicode_FromString(property->name));
if (!py_name) return nullptr;
@ -1064,7 +1064,7 @@ PyObject *PyPropertiesIteratorNext(PyPropertiesIterator *self, PyObject *Py_UNUS
MG_ASSERT(self->it);
MG_ASSERT(self->py_graph);
MG_ASSERT(self->py_graph->graph);
const auto *property = Call<const mgp_property *>(mgp_properties_iterator_next, self->it);
auto *property = Call<mgp_property *>(mgp_properties_iterator_next, self->it);
if (!property) Py_RETURN_NONE;
py::Object py_name(PyUnicode_FromString(property->name));
if (!py_name) return nullptr;
@ -1114,9 +1114,8 @@ PyObject *PyEdgeFromVertex(PyEdge *self, PyObject *Py_UNUSED(ignored)) {
MG_ASSERT(self->edge);
MG_ASSERT(self->py_graph);
MG_ASSERT(self->py_graph->graph);
const auto *vertex = Call<const mgp_vertex *>(mgp_edge_get_from, self->edge);
MG_ASSERT(vertex);
return MakePyVertex(*vertex, self->py_graph);
auto &vertex = self->edge->from;
return MakePyVertex(&vertex, self->py_graph);
}
PyObject *PyEdgeToVertex(PyEdge *self, PyObject *Py_UNUSED(ignored)) {
@ -1124,9 +1123,8 @@ PyObject *PyEdgeToVertex(PyEdge *self, PyObject *Py_UNUSED(ignored)) {
MG_ASSERT(self->edge);
MG_ASSERT(self->py_graph);
MG_ASSERT(self->py_graph->graph);
const auto *vertex = Call<const mgp_vertex *>(mgp_edge_get_to, self->edge);
MG_ASSERT(vertex);
return MakePyVertex(*vertex, self->py_graph);
auto &vertex = self->edge->to;
return MakePyVertex(vertex, self->py_graph);
}
void PyEdgeDealloc(PyEdge *self) {
@ -1232,10 +1230,11 @@ static PyTypeObject PyEdgeType = {
///
/// The created instance references an existing `_mgp.Graph` instance, which
/// marks the execution context.
PyObject *MakePyEdge(const mgp_edge &edge, PyGraph *py_graph) {
PyObject *MakePyEdge(mgp_edge &edge, PyGraph *py_graph) {
MG_ASSERT(py_graph);
MG_ASSERT(py_graph->graph && py_graph->memory);
mgp_edge *edge_copy{nullptr};
// TODO(antaljanosbenjamin)
if (const auto err = mgp_edge_copy(&edge, py_graph->memory, &edge_copy); err == MGP_ERROR_UNABLE_TO_ALLOCATE) {
PyErr_SetString(PyExc_MemoryError, "Unable to allocate mgp_edge.");
return nullptr;
@ -1492,7 +1491,7 @@ PyObject *MakePyVertex(mgp_vertex *vertex, PyGraph *py_graph) {
return reinterpret_cast<PyObject *>(py_vertex);
}
PyObject *MakePyVertex(const mgp_vertex &vertex, PyGraph *py_graph) {
PyObject *MakePyVertex(mgp_vertex &vertex, PyGraph *py_graph) {
MG_ASSERT(py_graph);
MG_ASSERT(py_graph->graph && py_graph->memory);
@ -1590,7 +1589,7 @@ PyObject *PyPathVertexAt(PyPath *self, PyObject *args) {
if (!PyArg_ParseTuple(args, "n", &i)) {
return nullptr;
}
const mgp_vertex *vertex{nullptr};
mgp_vertex *vertex{nullptr};
if (const auto err = mgp_path_vertex_at(self->path, i, &vertex); err == MGP_ERROR_OUT_OF_RANGE) {
PyErr_SetString(PyExc_IndexError, "Index is out of range.");
return nullptr;
@ -1610,7 +1609,7 @@ PyObject *PyPathEdgeAt(PyPath *self, PyObject *args) {
if (!PyArg_ParseTuple(args, "n", &i)) {
return nullptr;
}
const mgp_edge *edge{nullptr};
mgp_edge *edge{nullptr};
if (const auto err = mgp_path_edge_at(self->path, i, &edge); err == MGP_ERROR_OUT_OF_RANGE) {
PyErr_SetString(PyExc_IndexError, "Index is out of range.");
return nullptr;
@ -1661,7 +1660,7 @@ PyObject *MakePyPath(mgp_path *path, PyGraph *py_graph) {
return reinterpret_cast<PyObject *>(py_path);
}
PyObject *MakePyPath(const mgp_path &path, PyGraph *py_graph) {
PyObject *MakePyPath(mgp_path &path, PyGraph *py_graph) {
MG_ASSERT(py_graph);
MG_ASSERT(py_graph->graph && py_graph->memory);
mgp_path *path_copy{nullptr};
@ -1783,22 +1782,22 @@ py::Object MgpValueToPyObject(const mgp_value &value, PyObject *py_graph) {
}
py::Object MgpValueToPyObject(const mgp_value &value, PyGraph *py_graph) {
switch (Call<mgp_value_type>(mgp_value_get_type, &value)) {
switch (value.type) {
case MGP_VALUE_TYPE_NULL:
Py_INCREF(Py_None);
return py::Object(Py_None);
case MGP_VALUE_TYPE_BOOL:
return py::Object(PyBool_FromLong(CallBool(mgp_value_get_bool, &value)));
return py::Object(PyBool_FromLong(value.bool_v));
case MGP_VALUE_TYPE_INT:
return py::Object(PyLong_FromLongLong(Call<int64_t>(mgp_value_get_int, &value)));
return py::Object(PyLong_FromLongLong(value.int_v));
case MGP_VALUE_TYPE_DOUBLE:
return py::Object(PyFloat_FromDouble(Call<double>(mgp_value_get_double, &value)));
return py::Object(PyFloat_FromDouble(value.double_v));
case MGP_VALUE_TYPE_STRING:
return py::Object(PyUnicode_FromString(Call<const char *>(mgp_value_get_string, &value)));
return py::Object(PyUnicode_FromString(value.string_v.c_str()));
case MGP_VALUE_TYPE_LIST:
return MgpListToPyTuple(Call<const mgp_list *>(mgp_value_get_list, &value), py_graph);
return MgpListToPyTuple(value.list_v, py_graph);
case MGP_VALUE_TYPE_MAP: {
const auto *map = Call<const mgp_map *>(mgp_value_get_map, &value);
auto *map = value.map_v;
py::Object py_dict(PyDict_New());
if (!py_dict) {
return nullptr;
@ -1816,21 +1815,21 @@ py::Object MgpValueToPyObject(const mgp_value &value, PyGraph *py_graph) {
case MGP_VALUE_TYPE_VERTEX: {
py::Object py_mgp(PyImport_ImportModule("mgp"));
if (!py_mgp) return nullptr;
const auto *v = Call<const mgp_vertex *>(mgp_value_get_vertex, &value);
auto *v = value.vertex_v;
py::Object py_vertex(reinterpret_cast<PyObject *>(MakePyVertex(*v, py_graph)));
return py_mgp.CallMethod("Vertex", py_vertex);
}
case MGP_VALUE_TYPE_EDGE: {
py::Object py_mgp(PyImport_ImportModule("mgp"));
if (!py_mgp) return nullptr;
const auto *e = Call<const mgp_edge *>(mgp_value_get_edge, &value);
auto *e = value.edge_v;
py::Object py_edge(reinterpret_cast<PyObject *>(MakePyEdge(*e, py_graph)));
return py_mgp.CallMethod("Edge", py_edge);
}
case MGP_VALUE_TYPE_PATH: {
py::Object py_mgp(PyImport_ImportModule("mgp"));
if (!py_mgp) return nullptr;
const auto *p = Call<const mgp_path *>(mgp_value_get_path, &value);
auto *p = value.path_v;
py::Object py_path(reinterpret_cast<PyObject *>(MakePyPath(*p, py_graph)));
return py_mgp.CallMethod("Path", py_path);
}

View File

@ -50,7 +50,7 @@ mgp_value *PyObjectToMgpValue(PyObject *, mgp_memory *);
PyObject *PyInitMgpModule();
/// Create an instance of _mgp.Graph class.
PyObject *MakePyGraph(const mgp_graph *, mgp_memory *);
PyObject *MakePyGraph(mgp_graph *, mgp_memory *);
/// Import a module with given name in the context of mgp_module.
///

View File

@ -10,6 +10,8 @@ namespace utils {
template <class TError, class TValue = void>
class [[nodiscard]] BasicResult final {
public:
using ErrorType = TError;
using ValueType = TValue;
BasicResult(const TValue &value) : value_(value) {}
BasicResult(TValue &&value) noexcept : value_(std::move(value)) {}
BasicResult(const TError &error) : error_(error) {}
@ -96,6 +98,8 @@ class [[nodiscard]] BasicResult final {
template <class TError>
class [[nodiscard]] BasicResult<TError, void> final {
public:
using ErrorType = TError;
using ValueType = void;
BasicResult() = default;
BasicResult(const TError &error) : error_(error) {}
BasicResult(TError &&error) noexcept : error_(std::move(error)) {}

View File

@ -55,7 +55,7 @@ int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) {
const enum mgp_error error_proc_err = mgp_module_add_read_procedure(module, "error", error, &error_proc);
if (error_proc_err != MGP_ERROR_NO_ERROR) return 1;
const struct mgp_type *string_type = NULL;
struct mgp_type *string_type = NULL;
const enum mgp_error string_type_err = mgp_type_string(&string_type);
if (string_type_err != MGP_ERROR_NO_ERROR) return 1;
if (mgp_proc_add_result(error_proc, "error_result", string_type) != MGP_ERROR_NO_ERROR) return 1;

View File

@ -44,6 +44,12 @@ function(_add_unit_test test_cpp custom_main)
endfunction(_add_unit_test)
# Test utilities
add_library(storage_test_utils storage_test_utils.cpp)
target_link_libraries(storage_test_utils mg-storage-v2)
# Test integrations-kafka
add_library(kafka-mock STATIC kafka_mock.cpp)
@ -132,6 +138,10 @@ target_include_directories(${test_prefix}query_procedure_mgp_module PRIVATE ${CM
add_unit_test_with_custom_main(query_procedure_py_module.cpp)
target_link_libraries(${test_prefix}query_procedure_py_module mg-query)
target_include_directories(${test_prefix}query_procedure_py_module PRIVATE ${CMAKE_SOURCE_DIR}/include)
add_unit_test(query_procedures_mgp_graph.cpp)
target_link_libraries(${test_prefix}query_procedures_mgp_graph mg-query storage_test_utils)
target_include_directories(${test_prefix}query_procedures_mgp_graph PRIVATE ${CMAKE_SOURCE_DIR}/include)
# END query/procedure
add_unit_test(query_profile.cpp)
@ -275,7 +285,7 @@ add_unit_test(property_value_v2.cpp)
target_link_libraries(${test_prefix}property_value_v2 mg-utils)
add_unit_test(storage_v2.cpp)
target_link_libraries(${test_prefix}storage_v2 mg-storage-v2)
target_link_libraries(${test_prefix}storage_v2 mg-storage-v2 storage_test_utils)
add_unit_test(storage_v2_constraints.cpp)
target_link_libraries(${test_prefix}storage_v2_constraints mg-storage-v2)

View File

@ -133,11 +133,11 @@ class MgpApiTest : public ::testing::Test {
};
TEST_F(MgpApiTest, TestAllMgpKafkaCApi) {
const mgp_messages &messages = Messages();
mgp_messages &messages = Messages();
EXPECT_EQ(EXPECT_MGP_NO_ERROR(size_t, mgp_messages_size, &messages), expected.size());
for (int i = 0; i < expected.size(); ++i) {
const auto *message = EXPECT_MGP_NO_ERROR(const mgp_message *, mgp_messages_at, &messages, i);
auto *message = EXPECT_MGP_NO_ERROR(mgp_message *, mgp_messages_at, &messages, i);
// Test for key and key size. Key size is always 1 in this test.
EXPECT_EQ(EXPECT_MGP_NO_ERROR(size_t, mgp_message_key_size, message), 1);
EXPECT_EQ(*EXPECT_MGP_NO_ERROR(const char *, mgp_message_key, message), expected[i].key);

View File

@ -5,8 +5,7 @@
#include "test_utils.hpp"
TEST(MgpTransTest, TestMgpTransApi) {
constexpr auto no_op_cb = [](const mgp_messages *msg, const mgp_graph *graph, mgp_result *result,
mgp_memory *memory) {};
constexpr auto no_op_cb = [](mgp_messages *msg, mgp_graph *graph, mgp_result *result, mgp_memory *memory) {};
mgp_module module(utils::NewDeleteResource());
// If this is false, then mgp_module_add_transformation()
// correctly calls IsValidIdentifier(). We don't need to test

View File

@ -1,5 +1,6 @@
#include <gtest/gtest.h>
#include <functional>
#include <sstream>
#include <string_view>
@ -7,7 +8,7 @@
#include "test_utils.hpp"
static void DummyCallback(const mgp_list *, const mgp_graph *, mgp_result *, mgp_memory *) {}
static void DummyCallback(mgp_list *, mgp_graph *, mgp_result *, mgp_memory *) {}
TEST(Module, InvalidProcedureRegistration) {
mgp_module module(utils::NewDeleteResource());
@ -61,36 +62,33 @@ TEST(Module, ProcedureSignature) {
mgp_module module(utils::NewDeleteResource());
auto *proc = EXPECT_MGP_NO_ERROR(mgp_proc *, mgp_module_add_read_procedure, &module, "proc", &DummyCallback);
CheckSignature(proc, "proc() :: ()");
EXPECT_EQ(mgp_proc_add_arg(proc, "arg1", EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number)), MGP_ERROR_NO_ERROR);
EXPECT_EQ(mgp_proc_add_arg(proc, "arg1", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number)), MGP_ERROR_NO_ERROR);
CheckSignature(proc, "proc(arg1 :: NUMBER) :: ()");
EXPECT_EQ(
mgp_proc_add_opt_arg(
proc, "opt1",
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any)),
test_utils::CreateValueOwningPtr(EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory)).get()),
MGP_ERROR_NO_ERROR);
CheckSignature(proc, "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: ()");
EXPECT_EQ(mgp_proc_add_result(proc, "res1",
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list,
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int))),
EXPECT_EQ(mgp_proc_add_opt_arg(
proc, "opt1",
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any)),
test_utils::CreateValueOwningPtr(EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory)).get()),
MGP_ERROR_NO_ERROR);
CheckSignature(proc, "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: ()");
EXPECT_EQ(
mgp_proc_add_result(
proc, "res1", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_list, EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int))),
MGP_ERROR_NO_ERROR);
CheckSignature(proc, "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: (res1 :: LIST OF INTEGER)");
EXPECT_EQ(mgp_proc_add_arg(proc, "arg2", EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number)),
MGP_ERROR_LOGIC_ERROR);
EXPECT_EQ(mgp_proc_add_arg(proc, "arg2", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number)), MGP_ERROR_LOGIC_ERROR);
CheckSignature(proc, "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: (res1 :: LIST OF INTEGER)");
EXPECT_EQ(mgp_proc_add_arg(proc, "arg2", EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map)), MGP_ERROR_LOGIC_ERROR);
EXPECT_EQ(mgp_proc_add_arg(proc, "arg2", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map)), MGP_ERROR_LOGIC_ERROR);
CheckSignature(proc, "proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: (res1 :: LIST OF INTEGER)");
EXPECT_EQ(mgp_proc_add_deprecated_result(proc, "res2", EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string)),
EXPECT_EQ(mgp_proc_add_deprecated_result(proc, "res2", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string)),
MGP_ERROR_NO_ERROR);
CheckSignature(proc,
"proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: "
"(res1 :: LIST OF INTEGER, DEPRECATED res2 :: STRING)");
EXPECT_EQ(mgp_proc_add_result(proc, "res2", EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any)),
MGP_ERROR_LOGIC_ERROR);
EXPECT_EQ(mgp_proc_add_deprecated_result(proc, "res1", EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any)),
EXPECT_EQ(mgp_proc_add_result(proc, "res2", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any)), MGP_ERROR_LOGIC_ERROR);
EXPECT_EQ(mgp_proc_add_deprecated_result(proc, "res1", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any)),
MGP_ERROR_LOGIC_ERROR);
EXPECT_EQ(
mgp_proc_add_opt_arg(proc, "opt2", EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
mgp_proc_add_opt_arg(proc, "opt2", EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string),
test_utils::CreateValueOwningPtr(
EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_string, "string=\"value\"", &memory))
.get()),
@ -105,11 +103,23 @@ TEST(Module, ProcedureSignatureOnlyOptArg) {
mgp_memory memory{utils::NewDeleteResource()};
mgp_module module(utils::NewDeleteResource());
auto *proc = EXPECT_MGP_NO_ERROR(mgp_proc *, mgp_module_add_read_procedure, &module, "proc", &DummyCallback);
EXPECT_EQ(
mgp_proc_add_opt_arg(
proc, "opt1",
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any)),
test_utils::CreateValueOwningPtr(EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory)).get()),
MGP_ERROR_NO_ERROR);
EXPECT_EQ(mgp_proc_add_opt_arg(
proc, "opt1",
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any)),
test_utils::CreateValueOwningPtr(EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory)).get()),
MGP_ERROR_NO_ERROR);
CheckSignature(proc, "proc(opt1 = Null :: ANY?) :: ()");
}
TEST(Module, ReadWriteProcedures) {
mgp_module module(utils::NewDeleteResource());
auto *read_proc = EXPECT_MGP_NO_ERROR(mgp_proc *, mgp_module_add_read_procedure, &module, "read", &DummyCallback);
EXPECT_FALSE(read_proc->is_write_procedure);
auto *write_proc = EXPECT_MGP_NO_ERROR(mgp_proc *, mgp_module_add_write_procedure, &module, "write", &DummyCallback);
EXPECT_TRUE(write_proc->is_write_procedure);
mgp_proc read_proc_with_function{"dummy_name",
std::function<void(mgp_list *, mgp_graph *, mgp_result *, mgp_memory *)>{
[](mgp_list *, mgp_graph *, mgp_result *, mgp_memory *) {}},
utils::NewDeleteResource(), false};
EXPECT_FALSE(read_proc_with_function.is_write_procedure);
}

View File

@ -4,68 +4,68 @@
#include <gtest/gtest.h>
#include "query/procedure/cypher_types.hpp"
#include "query/procedure/mg_procedure_impl.hpp"
#include "test_utils.hpp"
TEST(CypherType, PresentableNameSimpleTypes) {
EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any)->impl->GetPresentableName(), "ANY");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool)->impl->GetPresentableName(), "BOOLEAN");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string)->impl->GetPresentableName(), "STRING");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int)->impl->GetPresentableName(), "INTEGER");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float)->impl->GetPresentableName(), "FLOAT");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number)->impl->GetPresentableName(), "NUMBER");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map)->impl->GetPresentableName(), "MAP");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node)->impl->GetPresentableName(), "NODE");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship)->impl->GetPresentableName(), "RELATIONSHIP");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)->impl->GetPresentableName(), "PATH");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any)->impl->GetPresentableName(), "ANY");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_bool)->impl->GetPresentableName(), "BOOLEAN");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string)->impl->GetPresentableName(), "STRING");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int)->impl->GetPresentableName(), "INTEGER");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_float)->impl->GetPresentableName(), "FLOAT");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number)->impl->GetPresentableName(), "NUMBER");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map)->impl->GetPresentableName(), "MAP");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_node)->impl->GetPresentableName(), "NODE");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_relationship)->impl->GetPresentableName(), "RELATIONSHIP");
EXPECT_EQ(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)->impl->GetPresentableName(), "PATH");
}
TEST(CypherType, PresentableNameCompositeTypes) {
const mgp_type *any_type = EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any);
mgp_type *any_type = EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any);
{
const auto *nullable_any = EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, any_type);
auto *nullable_any = EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, any_type);
EXPECT_EQ(nullable_any->impl->GetPresentableName(), "ANY?");
}
{
const auto *nullable_any =
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable,
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable,
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, any_type)));
auto *nullable_any =
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable,
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable,
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, any_type)));
EXPECT_EQ(nullable_any->impl->GetPresentableName(), "ANY?");
}
{
const auto *nullable_list = EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable,
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list, any_type));
auto *nullable_list =
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_list, any_type));
EXPECT_EQ(nullable_list->impl->GetPresentableName(), "LIST? OF ANY");
}
{
const auto *list_of_int =
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list, EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int));
auto *list_of_int = EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_list, EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int));
EXPECT_EQ(list_of_int->impl->GetPresentableName(), "LIST OF INTEGER");
}
{
const auto *list_of_nullable_path = EXPECT_MGP_NO_ERROR(
const mgp_type *, mgp_type_list,
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)));
auto *list_of_nullable_path = EXPECT_MGP_NO_ERROR(
mgp_type *, mgp_type_list,
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)));
EXPECT_EQ(list_of_nullable_path->impl->GetPresentableName(), "LIST OF PATH?");
}
{
const auto *list_of_list_of_map = EXPECT_MGP_NO_ERROR(
const mgp_type *, mgp_type_list,
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list, EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map)));
auto *list_of_list_of_map = EXPECT_MGP_NO_ERROR(
mgp_type *, mgp_type_list,
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_list, EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map)));
EXPECT_EQ(list_of_list_of_map->impl->GetPresentableName(), "LIST OF LIST OF MAP");
}
{
const auto *nullable_list_of_nullable_list_of_nullable_string = EXPECT_MGP_NO_ERROR(
const mgp_type *, mgp_type_nullable,
auto *nullable_list_of_nullable_list_of_nullable_string = EXPECT_MGP_NO_ERROR(
mgp_type *, mgp_type_nullable,
EXPECT_MGP_NO_ERROR(
const mgp_type *, mgp_type_list,
mgp_type *, mgp_type_list,
EXPECT_MGP_NO_ERROR(
const mgp_type *, mgp_type_nullable,
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list,
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable,
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string))))));
mgp_type *, mgp_type_nullable,
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_list,
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable,
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string))))));
EXPECT_EQ(nullable_list_of_nullable_list_of_nullable_string->impl->GetPresentableName(),
"LIST? OF LIST? OF STRING?");
}
@ -76,24 +76,19 @@ TEST(CypherType, NullSatisfiesType) {
{
auto *mgp_null = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory);
const query::TypedValue tv_null;
std::vector<const mgp_type *> primitive_types{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)};
for (const auto *primitive_type : primitive_types) {
for (const auto *type :
{primitive_type, EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list, primitive_type),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list,
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, primitive_type))}) {
std::vector<mgp_type *> primitive_types{
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_bool),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_float), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_relationship), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)};
for (auto *primitive_type : primitive_types) {
for (auto *type : {primitive_type, EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_list, primitive_type),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_list,
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, primitive_type))}) {
EXPECT_FALSE(type->impl->SatisfiesType(*mgp_null));
EXPECT_FALSE(type->impl->SatisfiesType(tv_null));
const auto *null_type = EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, type);
auto *null_type = EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, type);
EXPECT_TRUE(null_type->impl->SatisfiesType(*mgp_null));
EXPECT_TRUE(null_type->impl->SatisfiesType(tv_null));
}
@ -103,26 +98,25 @@ TEST(CypherType, NullSatisfiesType) {
}
static void CheckSatisfiesTypesAndNullable(const mgp_value *mgp_val, const query::TypedValue &tv,
const std::vector<const mgp_type *> &types) {
for (const auto *type : types) {
const std::vector<mgp_type *> &types) {
for (auto *type : types) {
EXPECT_TRUE(type->impl->SatisfiesType(*mgp_val)) << type->impl->GetPresentableName();
EXPECT_TRUE(type->impl->SatisfiesType(tv));
const auto *null_type = EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, type);
auto *null_type = EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, type);
EXPECT_TRUE(null_type->impl->SatisfiesType(*mgp_val)) << null_type->impl->GetPresentableName();
EXPECT_TRUE(null_type->impl->SatisfiesType(tv));
}
}
static void CheckNotSatisfiesTypesAndListAndNullable(const mgp_value *mgp_val, const query::TypedValue &tv,
const std::vector<const mgp_type *> &elem_types) {
for (const auto *elem_type : elem_types) {
for (const auto *type :
{elem_type, EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list, elem_type),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list,
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, elem_type))}) {
const std::vector<mgp_type *> &elem_types) {
for (auto *elem_type : elem_types) {
for (auto *type : {elem_type, EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_list, elem_type),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_list,
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, elem_type))}) {
EXPECT_FALSE(type->impl->SatisfiesType(*mgp_val)) << type->impl->GetPresentableName();
EXPECT_FALSE(type->impl->SatisfiesType(tv));
const auto *null_type = EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, type);
auto *null_type = EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, type);
EXPECT_FALSE(null_type->impl->SatisfiesType(*mgp_val)) << null_type->impl->GetPresentableName();
EXPECT_FALSE(null_type->impl->SatisfiesType(tv));
}
@ -135,14 +129,13 @@ TEST(CypherType, BoolSatisfiesType) {
const query::TypedValue tv_bool(true);
CheckSatisfiesTypesAndNullable(
mgp_bool, tv_bool,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_bool)});
CheckNotSatisfiesTypesAndListAndNullable(
mgp_bool, tv_bool,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_float), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_relationship), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)});
mgp_value_destroy(mgp_bool);
}
@ -152,15 +145,14 @@ TEST(CypherType, IntSatisfiesType) {
const query::TypedValue tv_int(42);
CheckSatisfiesTypesAndNullable(
mgp_int, tv_int,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number)});
CheckNotSatisfiesTypesAndListAndNullable(
mgp_int, tv_int,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_float), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_node), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)});
mgp_value_destroy(mgp_int);
}
@ -170,15 +162,14 @@ TEST(CypherType, DoubleSatisfiesType) {
const query::TypedValue tv_double(42.0);
CheckSatisfiesTypesAndNullable(
mgp_double, tv_double,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_float),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number)});
CheckNotSatisfiesTypesAndListAndNullable(
mgp_double, tv_double,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_node), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)});
mgp_value_destroy(mgp_double);
}
@ -188,14 +179,13 @@ TEST(CypherType, StringSatisfiesType) {
const query::TypedValue tv_string("text");
CheckSatisfiesTypesAndNullable(
mgp_string, tv_string,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string)});
CheckNotSatisfiesTypesAndListAndNullable(
mgp_string, tv_string,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_float), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_relationship), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)});
mgp_value_destroy(mgp_string);
}
@ -211,14 +201,13 @@ TEST(CypherType, MapSatisfiesType) {
const query::TypedValue tv_map(std::map<std::string, query::TypedValue>{{"key", query::TypedValue(42)}});
CheckSatisfiesTypesAndNullable(
mgp_map_v, tv_map,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map)});
CheckNotSatisfiesTypesAndListAndNullable(
mgp_map_v, tv_map,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_float),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_relationship), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)});
mgp_value_destroy(mgp_map_v);
}
@ -235,15 +224,14 @@ TEST(CypherType, VertexSatisfiesType) {
const query::TypedValue tv_vertex(vertex);
CheckSatisfiesTypesAndNullable(
mgp_vertex_v, tv_vertex,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map)});
CheckNotSatisfiesTypesAndListAndNullable(
mgp_vertex_v, tv_vertex,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_float),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)});
mgp_value_destroy(mgp_vertex_v);
}
@ -259,16 +247,16 @@ TEST(CypherType, EdgeSatisfiesType) {
mgp_graph graph{&dba, storage::View::NEW};
auto *mgp_edge_v = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_edge, alloc.new_object<mgp_edge>(edge, &graph));
const query::TypedValue tv_edge(edge);
CheckSatisfiesTypesAndNullable(mgp_edge_v, tv_edge,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map)});
CheckSatisfiesTypesAndNullable(
mgp_edge_v, tv_edge,
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map)});
CheckNotSatisfiesTypesAndListAndNullable(
mgp_edge_v, tv_edge,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_float),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)});
mgp_value_destroy(mgp_edge_v);
}
@ -293,24 +281,23 @@ TEST(CypherType, PathSatisfiesType) {
const query::TypedValue tv_path(query::Path(v1, edge, v2));
CheckSatisfiesTypesAndNullable(
mgp_path_v, tv_path,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)});
CheckNotSatisfiesTypesAndListAndNullable(
mgp_path_v, tv_path,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_float),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_node), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_relationship)});
mgp_value_destroy(mgp_path_v);
}
static std::vector<const mgp_type *> MakeListTypes(const std::vector<const mgp_type *> &element_types) {
std::vector<const mgp_type *> list_types;
static std::vector<mgp_type *> MakeListTypes(const std::vector<mgp_type *> &element_types) {
std::vector<mgp_type *> list_types;
list_types.reserve(2U * element_types.size());
for (const auto *type : element_types) {
list_types.push_back(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list, type));
list_types.push_back(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list,
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, type)));
for (auto *type : element_types) {
list_types.push_back(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_list, type));
list_types.push_back(
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_list, EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, type)));
}
return list_types;
}
@ -321,18 +308,14 @@ TEST(CypherType, EmptyListSatisfiesType) {
auto *mgp_list_v = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_list, list);
query::TypedValue tv_list(std::vector<query::TypedValue>{});
// Empty List satisfies all list element types
std::vector<const mgp_type *> primitive_types{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)};
std::vector<mgp_type *> primitive_types{
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_bool),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_float), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_relationship), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)};
auto all_types = MakeListTypes(primitive_types);
all_types.push_back(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any));
all_types.push_back(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any));
CheckSatisfiesTypesAndNullable(mgp_list_v, tv_list, all_types);
mgp_value_destroy(mgp_list_v);
}
@ -350,18 +333,17 @@ TEST(CypherType, ListOfIntSatisfiesType) {
test_utils::CreateValueOwningPtr(EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, i, &memory)).get()),
MGP_ERROR_NO_ERROR);
tv_list.ValueList().emplace_back(i);
auto valid_types = MakeListTypes({EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number)});
valid_types.push_back(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any));
auto valid_types =
MakeListTypes({EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number)});
valid_types.push_back(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any));
CheckSatisfiesTypesAndNullable(mgp_list_v, tv_list, valid_types);
CheckNotSatisfiesTypesAndListAndNullable(
mgp_list_v, tv_list,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_float), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_node), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)});
}
mgp_value_destroy(mgp_list_v);
}
@ -386,18 +368,17 @@ TEST(CypherType, ListOfIntAndBoolSatisfiesType) {
test_utils::CreateValueOwningPtr(EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_bool, 1, &memory)).get()),
MGP_ERROR_NO_ERROR);
tv_list.ValueList().emplace_back(true);
auto valid_types = MakeListTypes({EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any)});
valid_types.push_back(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any));
auto valid_types = MakeListTypes({EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any)});
valid_types.push_back(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any));
CheckSatisfiesTypesAndNullable(mgp_list_v, tv_list, valid_types);
// All other types will not be satisfied
CheckNotSatisfiesTypesAndListAndNullable(
mgp_list_v, tv_list,
{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number), EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)});
{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_bool), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_float),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_node), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)});
mgp_value_destroy(mgp_list_v);
}
@ -412,32 +393,28 @@ TEST(CypherType, ListOfNullSatisfiesType) {
MGP_ERROR_NO_ERROR);
tv_list.ValueList().emplace_back();
// List with Null satisfies all nullable list element types
std::vector<const mgp_type *> primitive_types{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_bool),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_string),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_int),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_float),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_number),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_map),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_relationship),
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_path)};
std::vector<const mgp_type *> valid_types{EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_any)};
std::vector<mgp_type *> primitive_types{
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_bool),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_int),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_float), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_number),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_map), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_node),
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_relationship), EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)};
std::vector<mgp_type *> valid_types{EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any)};
valid_types.reserve(1U + primitive_types.size());
for (const auto *elem_type : primitive_types) {
valid_types.push_back(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list,
EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, elem_type)));
for (auto *elem_type : primitive_types) {
valid_types.push_back(
EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_list, EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, elem_type)));
}
CheckSatisfiesTypesAndNullable(mgp_list_v, tv_list, valid_types);
std::vector<const mgp_type *> invalid_types;
std::vector<mgp_type *> invalid_types;
invalid_types.reserve(primitive_types.size());
for (const auto *elem_type : primitive_types) {
invalid_types.push_back(EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_list, elem_type));
for (auto *elem_type : primitive_types) {
invalid_types.push_back(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_list, elem_type));
}
for (const auto *type : invalid_types) {
for (auto *type : invalid_types) {
EXPECT_FALSE(type->impl->SatisfiesType(*mgp_list_v)) << type->impl->GetPresentableName();
EXPECT_FALSE(type->impl->SatisfiesType(tv_list));
const auto *null_type = EXPECT_MGP_NO_ERROR(const mgp_type *, mgp_type_nullable, type);
auto *null_type = EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, type);
EXPECT_FALSE(null_type->impl->SatisfiesType(*mgp_list_v)) << null_type->impl->GetPresentableName();
EXPECT_FALSE(null_type->impl->SatisfiesType(tv_list));
}

View File

@ -122,10 +122,10 @@ TEST(PyModule, PyVertex) {
ASSERT_TRUE(new_vertex_value);
ASSERT_NE(new_vertex_value, vertex_value); // Pointer compare.
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_vertex, new_vertex_value), 1);
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_vertex_equal,
EXPECT_MGP_NO_ERROR(const mgp_vertex *, mgp_value_get_vertex, vertex_value),
EXPECT_MGP_NO_ERROR(const mgp_vertex *, mgp_value_get_vertex, new_vertex_value)),
1);
ASSERT_EQ(
EXPECT_MGP_NO_ERROR(int, mgp_vertex_equal, EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_value_get_vertex, vertex_value),
EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_value_get_vertex, new_vertex_value)),
1);
// Clean up.
mgp_value_destroy(new_vertex_value);
mgp_value_destroy(vertex_value);
@ -157,10 +157,10 @@ TEST(PyModule, PyEdge) {
auto *edges_it = EXPECT_MGP_NO_ERROR(mgp_edges_iterator *, mgp_vertex_iter_out_edges, start_v, &memory);
ASSERT_TRUE(edges_it);
auto *edge = EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edge_copy,
EXPECT_MGP_NO_ERROR(const mgp_edge *, mgp_edges_iterator_get, edges_it), &memory);
EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edges_iterator_get, edges_it), &memory);
auto *edge_value = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_edge, edge);
ASSERT_EQ(EXPECT_MGP_NO_ERROR(const mgp_edge *, mgp_edges_iterator_next, edges_it), nullptr);
ASSERT_EQ(EXPECT_MGP_NO_ERROR(const mgp_edge *, mgp_edges_iterator_get, edges_it), nullptr);
ASSERT_EQ(EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edges_iterator_next, edges_it), nullptr);
ASSERT_EQ(EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edges_iterator_get, edges_it), nullptr);
mgp_edges_iterator_destroy(edges_it);
mgp_vertex_destroy(start_v);
// Initialize the Python graph object.
@ -177,10 +177,9 @@ TEST(PyModule, PyEdge) {
ASSERT_TRUE(new_edge_value);
ASSERT_NE(new_edge_value, edge_value); // Pointer compare.
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_edge, new_edge_value), 1);
ASSERT_EQ(
EXPECT_MGP_NO_ERROR(int, mgp_edge_equal, EXPECT_MGP_NO_ERROR(const mgp_edge *, mgp_value_get_edge, edge_value),
EXPECT_MGP_NO_ERROR(const mgp_edge *, mgp_value_get_edge, new_edge_value)),
1);
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_edge_equal, EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_value_get_edge, edge_value),
EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_value_get_edge, new_edge_value)),
1);
// Clean up.
mgp_value_destroy(new_edge_value);
mgp_value_destroy(edge_value);
@ -206,8 +205,8 @@ TEST(PyModule, PyPath) {
ASSERT_TRUE(path);
auto *edges_it = EXPECT_MGP_NO_ERROR(mgp_edges_iterator *, mgp_vertex_iter_out_edges, start_v, &memory);
ASSERT_TRUE(edges_it);
for (const auto *edge = EXPECT_MGP_NO_ERROR(const mgp_edge *, mgp_edges_iterator_get, edges_it); edge != nullptr;
edge = EXPECT_MGP_NO_ERROR(const mgp_edge *, mgp_edges_iterator_next, edges_it)) {
for (auto *edge = EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edges_iterator_get, edges_it); edge != nullptr;
edge = EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edges_iterator_next, edges_it)) {
ASSERT_EQ(mgp_path_expand(path, edge), MGP_ERROR_NO_ERROR);
}
ASSERT_EQ(EXPECT_MGP_NO_ERROR(size_t, mgp_path_size, path), 1);
@ -227,10 +226,9 @@ TEST(PyModule, PyPath) {
ASSERT_TRUE(new_path_value);
ASSERT_NE(new_path_value, path_value); // Pointer compare.
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_path, new_path_value), 1);
ASSERT_EQ(
EXPECT_MGP_NO_ERROR(int, mgp_path_equal, EXPECT_MGP_NO_ERROR(const mgp_path *, mgp_value_get_path, path_value),
EXPECT_MGP_NO_ERROR(const mgp_path *, mgp_value_get_path, new_path_value)),
1);
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_path_equal, EXPECT_MGP_NO_ERROR(mgp_path *, mgp_value_get_path, path_value),
EXPECT_MGP_NO_ERROR(mgp_path *, mgp_value_get_path, new_path_value)),
1);
mgp_value_destroy(new_path_value);
mgp_value_destroy(path_value);
ASSERT_FALSE(dba.Commit().HasError());
@ -244,52 +242,40 @@ TEST(PyModule, PyObjectToMgpValue) {
auto *value = query::procedure::PyObjectToMgpValue(py_value.Ptr(), &memory);
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_list, value), 1);
auto *list1 = EXPECT_MGP_NO_ERROR(const mgp_list *, mgp_value_get_list, value);
auto *list1 = EXPECT_MGP_NO_ERROR(mgp_list *, mgp_value_get_list, value);
EXPECT_EQ(EXPECT_MGP_NO_ERROR(size_t, mgp_list_size, list1), 5);
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_int, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 0)),
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_int, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list1, 0)), 1);
EXPECT_EQ(EXPECT_MGP_NO_ERROR(int64_t, mgp_value_get_int, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list1, 0)),
1);
EXPECT_EQ(
EXPECT_MGP_NO_ERROR(int64_t, mgp_value_get_int, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 0)),
1);
ASSERT_EQ(
EXPECT_MGP_NO_ERROR(int, mgp_value_is_double, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 1)), 1);
EXPECT_EQ(
EXPECT_MGP_NO_ERROR(double, mgp_value_get_double, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 1)),
1.0);
ASSERT_EQ(
EXPECT_MGP_NO_ERROR(int, mgp_value_is_string, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 2)), 1);
EXPECT_STREQ(EXPECT_MGP_NO_ERROR(const char *, mgp_value_get_string,
EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 2)),
"one");
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_list, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 3)),
1);
auto *list2 = EXPECT_MGP_NO_ERROR(const mgp_list *, mgp_value_get_list,
EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 3));
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_double, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list1, 1)), 1);
EXPECT_EQ(EXPECT_MGP_NO_ERROR(double, mgp_value_get_double, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list1, 1)),
1.0);
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_string, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list1, 2)), 1);
EXPECT_STREQ(
EXPECT_MGP_NO_ERROR(const char *, mgp_value_get_string, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list1, 2)),
"one");
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_list, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list1, 3)), 1);
auto *list2 =
EXPECT_MGP_NO_ERROR(mgp_list *, mgp_value_get_list, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list1, 3));
EXPECT_EQ(EXPECT_MGP_NO_ERROR(size_t, mgp_list_size, list2), 3);
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_int, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list2, 0)),
1);
EXPECT_EQ(
EXPECT_MGP_NO_ERROR(int64_t, mgp_value_get_int, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list2, 0)),
2);
ASSERT_EQ(
EXPECT_MGP_NO_ERROR(int, mgp_value_is_double, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list2, 1)), 1);
EXPECT_EQ(
EXPECT_MGP_NO_ERROR(double, mgp_value_get_double, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list2, 1)),
2.0);
ASSERT_EQ(
EXPECT_MGP_NO_ERROR(int, mgp_value_is_string, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list2, 2)), 1);
EXPECT_STREQ(EXPECT_MGP_NO_ERROR(const char *, mgp_value_get_string,
EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list2, 2)),
"two");
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_map, EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 4)),
1);
auto *map = EXPECT_MGP_NO_ERROR(const mgp_map *, mgp_value_get_map,
EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_list_at, list1, 4));
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_int, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list2, 0)), 1);
EXPECT_EQ(EXPECT_MGP_NO_ERROR(int64_t, mgp_value_get_int, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list2, 0)),
2);
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_double, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list2, 1)), 1);
EXPECT_EQ(EXPECT_MGP_NO_ERROR(double, mgp_value_get_double, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list2, 1)),
2.0);
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_string, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list2, 2)), 1);
EXPECT_STREQ(
EXPECT_MGP_NO_ERROR(const char *, mgp_value_get_string, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list2, 2)),
"two");
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_map, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list1, 4)), 1);
auto *map =
EXPECT_MGP_NO_ERROR(mgp_map *, mgp_value_get_map, EXPECT_MGP_NO_ERROR(mgp_value *, mgp_list_at, list1, 4));
EXPECT_EQ(EXPECT_MGP_NO_ERROR(size_t, mgp_map_size, map), 2);
const mgp_value *v1 = EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_map_at, map, "three");
mgp_value *v1 = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_map_at, map, "three");
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_int, v1), 1);
EXPECT_EQ(EXPECT_MGP_NO_ERROR(int64_t, mgp_value_get_int, v1), 3);
const mgp_value *v2 = EXPECT_MGP_NO_ERROR(const mgp_value *, mgp_map_at, map, "four");
mgp_value *v2 = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_map_at, map, "four");
ASSERT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_value_is_double, v2), 1);
EXPECT_EQ(EXPECT_MGP_NO_ERROR(double, mgp_value_get_double, v2), 4.0);
mgp_value_destroy(value);

View File

@ -0,0 +1,591 @@
#include <algorithm>
#include <iterator>
#include <list>
#include <memory>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "mg_procedure.h"
#include "query/db_accessor.hpp"
#include "query/procedure/mg_procedure_impl.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/storage.hpp"
#include "storage/v2/vertex_accessor.hpp"
#include "storage/v2/view.hpp"
#include "storage_test_utils.hpp"
#include "test_utils.hpp"
#include "utils/memory.hpp"
#define EXPECT_SUCCESS(...) EXPECT_EQ(__VA_ARGS__, MGP_ERROR_NO_ERROR)
namespace {
struct MgpEdgeDeleter {
void operator()(mgp_edge *e) {
if (e != nullptr) {
mgp_edge_destroy(e);
}
}
};
struct MgpEdgesIteratorDeleter {
void operator()(mgp_edges_iterator *it) {
if (it != nullptr) {
mgp_edges_iterator_destroy(it);
}
}
};
struct MgpVertexDeleter {
void operator()(mgp_vertex *v) {
if (v != nullptr) {
mgp_vertex_destroy(v);
}
}
};
struct MgpVerticesIteratorDeleter {
void operator()(mgp_vertices_iterator *it) {
if (it != nullptr) {
mgp_vertices_iterator_destroy(it);
}
}
};
struct MgpValueDeleter {
void operator()(mgp_value *v) {
if (v != nullptr) {
mgp_value_destroy(v);
}
}
};
using MgpEdgePtr = std::unique_ptr<mgp_edge, MgpEdgeDeleter>;
using MgpEdgesIteratorPtr = std::unique_ptr<mgp_edges_iterator, MgpEdgesIteratorDeleter>;
using MgpVertexPtr = std::unique_ptr<mgp_vertex, MgpVertexDeleter>;
using MgpVerticesIteratorPtr = std::unique_ptr<mgp_vertices_iterator, MgpVerticesIteratorDeleter>;
using MgpValuePtr = std::unique_ptr<mgp_value, MgpValueDeleter>;
template <typename TMaybeIterable>
size_t CountMaybeIterables(TMaybeIterable &&maybe_iterable) {
if (maybe_iterable.HasError()) {
ADD_FAILURE() << static_cast<std::underlying_type_t<typename TMaybeIterable::ErrorType>>(maybe_iterable.GetError());
return 0;
}
auto &iterable = maybe_iterable.GetValue();
return std::distance(iterable.begin(), iterable.end());
}
void CheckEdgeCountBetween(const MgpVertexPtr &from, const MgpVertexPtr &to, const size_t number_of_edges_between) {
EXPECT_EQ(CountMaybeIterables(from->impl.InEdges(storage::View::NEW)), 0);
EXPECT_EQ(CountMaybeIterables(from->impl.OutEdges(storage::View::NEW)), number_of_edges_between);
EXPECT_EQ(CountMaybeIterables(to->impl.InEdges(storage::View::NEW)), number_of_edges_between);
EXPECT_EQ(CountMaybeIterables(to->impl.OutEdges(storage::View::NEW)), 0);
}
} // namespace
struct MgpGraphTest : public ::testing::Test {
mgp_graph CreateGraph(const storage::View view = storage::View::NEW) {
// the execution context can be null as it shouldn't be used in these tests
return mgp_graph{&CreateDbAccessor(storage::IsolationLevel::SNAPSHOT_ISOLATION), view, nullptr};
}
std::array<storage::Gid, 2> CreateEdge() {
std::array<storage::Gid, 2> vertex_ids{};
auto accessor = CreateDbAccessor(storage::IsolationLevel::SNAPSHOT_ISOLATION);
for (auto i = 0; i < 2; ++i) {
vertex_ids[i] = accessor.InsertVertex().Gid();
}
auto from = accessor.FindVertex(vertex_ids[0], storage::View::NEW);
auto to = accessor.FindVertex(vertex_ids[1], storage::View::NEW);
EXPECT_TRUE(accessor.InsertEdge(&from.value(), &to.value(), accessor.NameToEdgeType("EDGE")).HasValue());
EXPECT_FALSE(accessor.Commit().HasError());
return vertex_ids;
}
void GetFirstOutEdge(mgp_graph &graph, storage::Gid vertex_id, MgpEdgePtr &edge) {
MgpVertexPtr from{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph,
mgp_vertex_id{vertex_id.AsInt()}, &memory)};
ASSERT_NE(from, nullptr);
MgpEdgesIteratorPtr it{EXPECT_MGP_NO_ERROR(mgp_edges_iterator *, mgp_vertex_iter_out_edges, from.get(), &memory)};
ASSERT_NE(it, nullptr);
auto *edge_from_it = EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edges_iterator_get, it.get());
ASSERT_NE(edge_from_it, nullptr);
// Copy is used to get a non const pointer because mgp_edges_iterator_get_mutable doesn't work with immutable graph
edge.reset(EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edge_copy, edge_from_it, &memory));
ASSERT_NE(edge, nullptr);
}
query::DbAccessor &CreateDbAccessor(const storage::IsolationLevel isolationLevel) {
accessors_.push_back(storage.Access(isolationLevel));
db_accessors_.emplace_back(&accessors_.back());
return db_accessors_.back();
}
storage::Storage storage;
mgp_memory memory{utils::NewDeleteResource()};
private:
std::list<storage::Storage::Accessor> accessors_;
std::list<query::DbAccessor> db_accessors_;
};
TEST_F(MgpGraphTest, IsMutable) {
mgp_graph immutable_graph = CreateGraph(storage::View::OLD);
EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_graph_is_mutable, &immutable_graph), 0);
mgp_graph mutable_graph = CreateGraph(storage::View::NEW);
EXPECT_NE(EXPECT_MGP_NO_ERROR(int, mgp_graph_is_mutable, &mutable_graph), 0);
}
TEST_F(MgpGraphTest, CreateVertex) {
mgp_graph graph = CreateGraph();
auto read_uncommited_accessor = storage.Access(storage::IsolationLevel::READ_UNCOMMITTED);
EXPECT_EQ(CountVertices(read_uncommited_accessor, storage::View::NEW), 0);
MgpVertexPtr vertex{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_create_vertex, &graph, &memory)};
EXPECT_NE(vertex, nullptr);
EXPECT_EQ(CountVertices(read_uncommited_accessor, storage::View::NEW), 1);
const auto vertex_id = EXPECT_MGP_NO_ERROR(mgp_vertex_id, mgp_vertex_get_id, vertex.get());
EXPECT_TRUE(
read_uncommited_accessor.FindVertex(storage::Gid::FromInt(vertex_id.as_int), storage::View::NEW).has_value());
}
TEST_F(MgpGraphTest, RemoveVertex) {
storage::Gid vertex_id{};
{
auto accessor = CreateDbAccessor(storage::IsolationLevel::SNAPSHOT_ISOLATION);
const auto vertex = accessor.InsertVertex();
vertex_id = vertex.Gid();
ASSERT_FALSE(accessor.Commit().HasError());
}
mgp_graph graph = CreateGraph();
auto read_uncommited_accessor = storage.Access(storage::IsolationLevel::READ_UNCOMMITTED);
EXPECT_EQ(CountVertices(read_uncommited_accessor, storage::View::NEW), 1);
MgpVertexPtr vertex{
EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{vertex_id.AsInt()}, &memory)};
EXPECT_NE(vertex, nullptr);
EXPECT_SUCCESS(mgp_graph_remove_vertex(&graph, vertex.get()));
EXPECT_EQ(CountVertices(read_uncommited_accessor, storage::View::NEW), 0);
}
TEST_F(MgpGraphTest, CreateRemoveWithImmutableGraph) {
storage::Gid vertex_id{};
{
auto accessor = CreateDbAccessor(storage::IsolationLevel::SNAPSHOT_ISOLATION);
const auto vertex = accessor.InsertVertex();
vertex_id = vertex.Gid();
ASSERT_FALSE(accessor.Commit().HasError());
}
auto read_uncommited_accessor = storage.Access(storage::IsolationLevel::READ_UNCOMMITTED);
EXPECT_EQ(CountVertices(read_uncommited_accessor, storage::View::NEW), 1);
mgp_graph immutable_graph = CreateGraph(storage::View::OLD);
mgp_vertex *raw_vertex{nullptr};
EXPECT_EQ(mgp_graph_create_vertex(&immutable_graph, &memory, &raw_vertex), MGP_ERROR_IMMUTABLE_OBJECT);
MgpVertexPtr created_vertex{raw_vertex};
EXPECT_EQ(created_vertex, nullptr);
EXPECT_EQ(CountVertices(read_uncommited_accessor, storage::View::NEW), 1);
MgpVertexPtr vertex_to_remove{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &immutable_graph,
mgp_vertex_id{vertex_id.AsInt()}, &memory)};
ASSERT_NE(vertex_to_remove, nullptr);
EXPECT_EQ(mgp_graph_remove_vertex(&immutable_graph, vertex_to_remove.get()), MGP_ERROR_IMMUTABLE_OBJECT);
EXPECT_EQ(CountVertices(read_uncommited_accessor, storage::View::NEW), 1);
}
TEST_F(MgpGraphTest, VerticesIterator) {
{
auto accessor = CreateDbAccessor(storage::IsolationLevel::SNAPSHOT_ISOLATION);
accessor.InsertVertex();
ASSERT_FALSE(accessor.Commit().HasError());
}
auto check_vertices_iterator = [this](const storage::View view) {
mgp_graph graph = CreateGraph(view);
MgpVerticesIteratorPtr vertices_iter{
EXPECT_MGP_NO_ERROR(mgp_vertices_iterator *, mgp_graph_iter_vertices, &graph, &memory)};
ASSERT_NE(vertices_iter, nullptr);
EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_vertices_iterator_get, vertices_iter.get());
if (view == storage::View::NEW) {
EXPECT_NE(EXPECT_MGP_NO_ERROR(int, mgp_vertices_iterator_underlying_graph_is_mutable, vertices_iter.get()), 0);
} else {
EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_vertices_iterator_underlying_graph_is_mutable, vertices_iter.get()), 0);
}
};
{
SCOPED_TRACE("View::OLD");
check_vertices_iterator(storage::View::OLD);
}
{
SCOPED_TRACE("View::NEW");
check_vertices_iterator(storage::View::NEW);
}
}
TEST_F(MgpGraphTest, VertexIsMutable) {
auto graph = CreateGraph(storage::View::NEW);
MgpVertexPtr vertex{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_create_vertex, &graph, &memory)};
ASSERT_NE(vertex.get(), nullptr);
EXPECT_NE(EXPECT_MGP_NO_ERROR(int, mgp_vertex_underlying_graph_is_mutable, vertex.get()), 0);
graph.view = storage::View::OLD;
EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_vertex_underlying_graph_is_mutable, vertex.get()), 0);
}
TEST_F(MgpGraphTest, VertexSetProperty) {
constexpr std::string_view property_to_update{"to_update"};
constexpr std::string_view property_to_set{"to_set"};
storage::Gid vertex_id{};
{
auto accessor = CreateDbAccessor(storage::IsolationLevel::SNAPSHOT_ISOLATION);
auto vertex = accessor.InsertVertex();
vertex_id = vertex.Gid();
const auto result = vertex.SetProperty(accessor.NameToProperty(property_to_update), storage::PropertyValue(42));
ASSERT_TRUE(result.HasValue());
ASSERT_FALSE(accessor.Commit().HasError());
}
auto read_uncommited_accessor = storage.Access(storage::IsolationLevel::READ_UNCOMMITTED);
EXPECT_EQ(CountVertices(read_uncommited_accessor, storage::View::NEW), 1);
mgp_graph graph = CreateGraph(storage::View::NEW);
MgpVertexPtr vertex{
EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{vertex_id.AsInt()}, &memory)};
ASSERT_NE(vertex, nullptr);
auto vertex_acc = read_uncommited_accessor.FindVertex(vertex_id, storage::View::NEW);
ASSERT_TRUE(vertex_acc.has_value());
const auto property_id_to_update = read_uncommited_accessor.NameToProperty(property_to_update);
{
SCOPED_TRACE("Update the property");
constexpr int64_t numerical_value_to_update_to{69};
MgpValuePtr value_to_update_to{
EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, numerical_value_to_update_to, &memory)};
ASSERT_NE(value_to_update_to, nullptr);
EXPECT_SUCCESS(mgp_vertex_set_property(vertex.get(), property_to_update.data(), value_to_update_to.get()));
const auto maybe_prop = vertex_acc->GetProperty(property_id_to_update, storage::View::NEW);
ASSERT_TRUE(maybe_prop.HasValue());
EXPECT_EQ(*maybe_prop, storage::PropertyValue{numerical_value_to_update_to});
}
{
SCOPED_TRACE("Remove the property");
MgpValuePtr null_value{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory)};
ASSERT_NE(null_value, nullptr);
EXPECT_SUCCESS(mgp_vertex_set_property(vertex.get(), property_to_update.data(), null_value.get()));
const auto maybe_prop = vertex_acc->GetProperty(property_id_to_update, storage::View::NEW);
ASSERT_TRUE(maybe_prop.HasValue());
EXPECT_EQ(*maybe_prop, storage::PropertyValue{});
}
{
SCOPED_TRACE("Add a property");
constexpr double numerical_value_to_set{3.5};
MgpValuePtr value_to_set{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_double, numerical_value_to_set, &memory)};
ASSERT_NE(value_to_set, nullptr);
EXPECT_SUCCESS(mgp_vertex_set_property(vertex.get(), property_to_set.data(), value_to_set.get()));
const auto maybe_prop =
vertex_acc->GetProperty(read_uncommited_accessor.NameToProperty(property_to_set), storage::View::NEW);
ASSERT_TRUE(maybe_prop.HasValue());
EXPECT_EQ(*maybe_prop, storage::PropertyValue{numerical_value_to_set});
}
}
TEST_F(MgpGraphTest, VertexAddLabel) {
constexpr std::string_view label = "test_label";
storage::Gid vertex_id{};
{
auto accessor = CreateDbAccessor(storage::IsolationLevel::SNAPSHOT_ISOLATION);
const auto vertex = accessor.InsertVertex();
vertex_id = vertex.Gid();
ASSERT_FALSE(accessor.Commit().HasError());
}
mgp_graph graph = CreateGraph(storage::View::NEW);
MgpVertexPtr vertex{
EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{vertex_id.AsInt()}, &memory)};
EXPECT_SUCCESS(mgp_vertex_add_label(vertex.get(), mgp_label{label.data()}));
auto check_label = [&]() {
EXPECT_NE(EXPECT_MGP_NO_ERROR(int, mgp_vertex_has_label_named, vertex.get(), label.data()), 0);
auto read_uncommited_accessor = storage.Access(storage::IsolationLevel::READ_UNCOMMITTED);
const auto maybe_vertex = read_uncommited_accessor.FindVertex(vertex_id, storage::View::NEW);
ASSERT_TRUE(maybe_vertex.has_value());
const auto label_ids = maybe_vertex->Labels(storage::View::NEW);
ASSERT_TRUE(label_ids.HasValue());
EXPECT_THAT(*label_ids, ::testing::ContainerEq(std::vector{read_uncommited_accessor.NameToLabel(label)}));
};
ASSERT_NO_FATAL_FAILURE(check_label());
EXPECT_SUCCESS(mgp_vertex_add_label(vertex.get(), mgp_label{label.data()}));
ASSERT_NO_FATAL_FAILURE(check_label());
}
TEST_F(MgpGraphTest, VertexRemoveLabel) {
constexpr std::string_view label = "test_label";
storage::Gid vertex_id{};
{
auto accessor = CreateDbAccessor(storage::IsolationLevel::SNAPSHOT_ISOLATION);
auto vertex = accessor.InsertVertex();
const auto result = vertex.AddLabel(accessor.NameToLabel(label));
ASSERT_TRUE(result.HasValue());
ASSERT_TRUE(*result);
vertex_id = vertex.Gid();
ASSERT_FALSE(accessor.Commit().HasError());
}
mgp_graph graph = CreateGraph(storage::View::NEW);
MgpVertexPtr vertex{
EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{vertex_id.AsInt()}, &memory)};
EXPECT_SUCCESS(mgp_vertex_remove_label(vertex.get(), mgp_label{label.data()}));
auto check_label = [&]() {
EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_vertex_has_label_named, vertex.get(), label.data()), 0);
auto read_uncommited_accessor = storage.Access(storage::IsolationLevel::READ_UNCOMMITTED);
const auto maybe_vertex = read_uncommited_accessor.FindVertex(vertex_id, storage::View::NEW);
ASSERT_TRUE(maybe_vertex.has_value());
const auto label_ids = maybe_vertex->Labels(storage::View::NEW);
ASSERT_TRUE(label_ids.HasValue());
EXPECT_EQ(label_ids->size(), 0);
};
ASSERT_NO_FATAL_FAILURE(check_label());
EXPECT_SUCCESS(mgp_vertex_remove_label(vertex.get(), mgp_label{label.data()}));
ASSERT_NO_FATAL_FAILURE(check_label());
}
TEST_F(MgpGraphTest, ModifyImmutableVertex) {
constexpr std::string_view label_to_remove{"label_to_remove"};
storage::Gid vertex_id{};
{
auto accessor = CreateDbAccessor(storage::IsolationLevel::SNAPSHOT_ISOLATION);
auto vertex = accessor.InsertVertex();
vertex_id = vertex.Gid();
ASSERT_TRUE(vertex.AddLabel(accessor.NameToLabel(label_to_remove)).HasValue());
ASSERT_FALSE(accessor.Commit().HasError());
}
auto graph = CreateGraph(storage::View::OLD);
MgpVertexPtr vertex{
EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{vertex_id.AsInt()}, &memory)};
EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_vertex_underlying_graph_is_mutable, vertex.get()), 0);
EXPECT_EQ(mgp_vertex_add_label(vertex.get(), mgp_label{"label"}), MGP_ERROR_IMMUTABLE_OBJECT);
EXPECT_EQ(mgp_vertex_remove_label(vertex.get(), mgp_label{label_to_remove.data()}), MGP_ERROR_IMMUTABLE_OBJECT);
MgpValuePtr value{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, 4, &memory)};
EXPECT_EQ(mgp_vertex_set_property(vertex.get(), "property", value.get()), MGP_ERROR_IMMUTABLE_OBJECT);
}
TEST_F(MgpGraphTest, CreateRemoveEdge) {
std::array<storage::Gid, 2> vertex_ids{};
{
auto accessor = CreateDbAccessor(storage::IsolationLevel::SNAPSHOT_ISOLATION);
for (auto i = 0; i < 2; ++i) {
vertex_ids[i] = accessor.InsertVertex().Gid();
}
ASSERT_FALSE(accessor.Commit().HasError());
}
auto graph = CreateGraph();
MgpVertexPtr from{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph,
mgp_vertex_id{vertex_ids[0].AsInt()}, &memory)};
MgpVertexPtr to{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph,
mgp_vertex_id{vertex_ids[1].AsInt()}, &memory)};
ASSERT_NE(from, nullptr);
ASSERT_NE(to, nullptr);
CheckEdgeCountBetween(from, to, 0);
MgpEdgePtr edge{EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_graph_create_edge, &graph, from.get(), to.get(),
mgp_edge_type{"EDGE"}, &memory)};
CheckEdgeCountBetween(from, to, 1);
ASSERT_NE(edge, nullptr);
EXPECT_SUCCESS(mgp_graph_remove_edge(&graph, edge.get()));
CheckEdgeCountBetween(from, to, 0);
}
TEST_F(MgpGraphTest, CreateRemoveEdgeWithImmutableGraph) {
storage::Gid from_id;
storage::Gid to_id;
{
auto accessor = CreateDbAccessor(storage::IsolationLevel::SNAPSHOT_ISOLATION);
auto from = accessor.InsertVertex();
auto to = accessor.InsertVertex();
from_id = from.Gid();
to_id = to.Gid();
ASSERT_TRUE(accessor.InsertEdge(&from, &to, accessor.NameToEdgeType("EDGE_TYPE_TO_REMOVE")).HasValue());
ASSERT_FALSE(accessor.Commit().HasError());
}
auto graph = CreateGraph(storage::View::OLD);
MgpVertexPtr from{
EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{from_id.AsInt()}, &memory)};
MgpVertexPtr to{
EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{to_id.AsInt()}, &memory)};
ASSERT_NE(from, nullptr);
ASSERT_NE(to, nullptr);
CheckEdgeCountBetween(from, to, 1);
mgp_edge *edge{nullptr};
EXPECT_EQ(
mgp_graph_create_edge(&graph, from.get(), to.get(), mgp_edge_type{"NEWLY_CREATED_EDGE_TYPE"}, &memory, &edge),
MGP_ERROR_IMMUTABLE_OBJECT);
CheckEdgeCountBetween(from, to, 1);
MgpEdgesIteratorPtr edges_it{
EXPECT_MGP_NO_ERROR(mgp_edges_iterator *, mgp_vertex_iter_out_edges, from.get(), &memory)};
auto *edge_from_it = EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edges_iterator_get, edges_it.get());
ASSERT_NE(edge_from_it, nullptr);
EXPECT_EQ(mgp_graph_remove_edge(&graph, edge_from_it), MGP_ERROR_IMMUTABLE_OBJECT);
MgpEdgePtr edge_copy_of_immutable{EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edge_copy, edge_from_it, &memory)};
EXPECT_EQ(mgp_graph_remove_edge(&graph, edge_copy_of_immutable.get()), MGP_ERROR_IMMUTABLE_OBJECT);
CheckEdgeCountBetween(from, to, 1);
}
TEST_F(MgpGraphTest, EdgeIsMutable) {
const auto vertex_ids = CreateEdge();
auto graph = CreateGraph();
MgpEdgePtr edge{};
ASSERT_NO_FATAL_FAILURE(GetFirstOutEdge(graph, vertex_ids[0], edge));
EXPECT_NE(EXPECT_MGP_NO_ERROR(int, mgp_edge_underlying_graph_is_mutable, edge.get()), 0);
graph.view = storage::View::OLD;
EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_edge_underlying_graph_is_mutable, edge.get()), 0);
}
TEST_F(MgpGraphTest, MutableFromTo) {
storage::Gid from_vertex_id{};
{
const auto vertex_ids = CreateEdge();
from_vertex_id = vertex_ids[0];
}
auto check_edges_iterator = [this, from_vertex_id](const storage::View view) {
mgp_graph graph = CreateGraph(view);
MgpEdgePtr edge{};
ASSERT_NO_FATAL_FAILURE(GetFirstOutEdge(graph, from_vertex_id, edge));
auto *from = EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_edge_get_from, edge.get());
auto *to = EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_edge_get_from, edge.get());
auto check_is_mutable = [&edge, from, to](bool is_mutable) {
EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_edge_underlying_graph_is_mutable, edge.get()) != 0, is_mutable);
EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_vertex_underlying_graph_is_mutable, from) != 0, is_mutable);
EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_vertex_underlying_graph_is_mutable, to) != 0, is_mutable);
};
if (view == storage::View::NEW) {
check_is_mutable(true);
} else {
check_is_mutable(false);
}
};
{
SCOPED_TRACE("View::OLD");
check_edges_iterator(storage::View::OLD);
}
{
SCOPED_TRACE("View::NEW");
check_edges_iterator(storage::View::NEW);
}
}
TEST_F(MgpGraphTest, EdgesIterator) {
storage::Gid from_vertex_id{};
{
const auto vertex_ids = CreateEdge();
from_vertex_id = vertex_ids[0];
}
auto check_edges_iterator = [this, from_vertex_id](const storage::View view) {
mgp_graph graph = CreateGraph(view);
MgpVertexPtr from{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph,
mgp_vertex_id{from_vertex_id.AsInt()}, &memory)};
MgpEdgesIteratorPtr iter{EXPECT_MGP_NO_ERROR(mgp_edges_iterator *, mgp_vertex_iter_out_edges, from.get(), &memory)};
auto *edge = EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edges_iterator_get, iter.get());
auto check_is_mutable = [&edge, &iter](bool is_mutable) {
EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_edges_iterator_underlying_graph_is_mutable, iter.get()) != 0, is_mutable);
EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_edge_underlying_graph_is_mutable, edge) != 0, is_mutable);
};
if (view == storage::View::NEW) {
check_is_mutable(true);
} else {
check_is_mutable(false);
}
};
{
SCOPED_TRACE("View::OLD");
check_edges_iterator(storage::View::OLD);
}
{
SCOPED_TRACE("View::NEW");
check_edges_iterator(storage::View::NEW);
}
}
TEST_F(MgpGraphTest, EdgeSetProperty) {
constexpr std::string_view property_to_update{"to_update"};
constexpr std::string_view property_to_set{"to_set"};
storage::Gid from_vertex_id{};
auto get_edge = [&from_vertex_id](storage::Storage::Accessor &accessor) -> storage::EdgeAccessor {
auto from = accessor.FindVertex(from_vertex_id, storage::View::NEW);
return from->OutEdges(storage::View::NEW).GetValue().front();
};
{
const auto vertex_ids = CreateEdge();
from_vertex_id = vertex_ids[0];
auto accessor = storage.Access(storage::IsolationLevel::SNAPSHOT_ISOLATION);
auto edge = get_edge(accessor);
const auto result = edge.SetProperty(accessor.NameToProperty(property_to_update), storage::PropertyValue(42));
ASSERT_TRUE(result.HasValue());
ASSERT_FALSE(accessor.Commit().HasError());
}
auto read_uncommited_accessor = storage.Access(storage::IsolationLevel::READ_UNCOMMITTED);
mgp_graph graph = CreateGraph(storage::View::NEW);
MgpEdgePtr edge;
ASSERT_NO_FATAL_FAILURE(GetFirstOutEdge(graph, from_vertex_id, edge));
const auto edge_acc = get_edge(read_uncommited_accessor);
const auto property_id_to_update = read_uncommited_accessor.NameToProperty(property_to_update);
{
SCOPED_TRACE("Update the property");
constexpr int64_t numerical_value_to_update_to{69};
MgpValuePtr value_to_update_to{
EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, numerical_value_to_update_to, &memory)};
ASSERT_NE(value_to_update_to, nullptr);
EXPECT_SUCCESS(mgp_edge_set_property(edge.get(), property_to_update.data(), value_to_update_to.get()));
const auto maybe_prop = edge_acc.GetProperty(property_id_to_update, storage::View::NEW);
ASSERT_TRUE(maybe_prop.HasValue());
EXPECT_EQ(*maybe_prop, storage::PropertyValue{numerical_value_to_update_to});
}
{
SCOPED_TRACE("Remove the property");
MgpValuePtr null_value{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory)};
ASSERT_NE(null_value, nullptr);
EXPECT_SUCCESS(mgp_edge_set_property(edge.get(), property_to_update.data(), null_value.get()));
const auto maybe_prop = edge_acc.GetProperty(property_id_to_update, storage::View::NEW);
ASSERT_TRUE(maybe_prop.HasValue());
EXPECT_EQ(*maybe_prop, storage::PropertyValue{});
}
{
SCOPED_TRACE("Add a property");
constexpr double numerical_value_to_set{3.5};
MgpValuePtr value_to_set{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_double, numerical_value_to_set, &memory)};
ASSERT_NE(value_to_set, nullptr);
EXPECT_SUCCESS(mgp_edge_set_property(edge.get(), property_to_set.data(), value_to_set.get()));
const auto maybe_prop =
edge_acc.GetProperty(read_uncommited_accessor.NameToProperty(property_to_set), storage::View::NEW);
ASSERT_TRUE(maybe_prop.HasValue());
EXPECT_EQ(*maybe_prop, storage::PropertyValue{numerical_value_to_set});
}
}
TEST_F(MgpGraphTest, EdgeSetPropertyWithImmutableGraph) {
storage::Gid from_vertex_id{};
{
const auto vertex_ids = CreateEdge();
from_vertex_id = vertex_ids[0];
}
auto graph = CreateGraph(storage::View::OLD);
MgpEdgePtr edge;
ASSERT_NO_FATAL_FAILURE(GetFirstOutEdge(graph, from_vertex_id, edge));
MgpValuePtr value{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, 65, &memory)};
EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_edge_underlying_graph_is_mutable, edge.get()), 0);
EXPECT_EQ(mgp_edge_set_property(edge.get(), "property", value.get()), MGP_ERROR_IMMUTABLE_OBJECT);
}

View File

@ -0,0 +1,9 @@
#include "storage_test_utils.hpp"
size_t CountVertices(storage::Storage::Accessor &storage_accessor, storage::View view) {
auto vertices = storage_accessor.Vertices(view);
size_t count = 0U;
for (auto it = vertices.begin(); it != vertices.end(); ++it, ++count)
;
return count;
}

View File

@ -0,0 +1,6 @@
#pragma once
#include "storage/v2/storage.hpp"
#include "storage/v2/view.hpp"
size_t CountVertices(storage::Storage::Accessor &storage_accessor, storage::View view);

View File

@ -6,16 +6,10 @@
#include "storage/v2/property_value.hpp"
#include "storage/v2/storage.hpp"
#include "storage/v2/vertex_accessor.hpp"
#include "storage_test_utils.hpp"
using testing::UnorderedElementsAre;
size_t CountVertices(storage::Storage::Accessor *storage_accessor, storage::View view) {
auto vertices = storage_accessor->Vertices(view);
size_t count = 0U;
for (auto it = vertices.begin(); it != vertices.end(); ++it) ++count;
return count;
}
// NOLINTNEXTLINE(hicpp-special-member-functions)
TEST(StorageV2, Commit) {
storage::Storage store;
@ -25,17 +19,17 @@ TEST(StorageV2, Commit) {
auto vertex = acc.CreateVertex();
gid = vertex.Gid();
ASSERT_FALSE(acc.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
ASSERT_TRUE(acc.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 1U);
ASSERT_FALSE(acc.Commit().HasError());
}
{
auto acc = store.Access();
ASSERT_TRUE(acc.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 1U);
ASSERT_TRUE(acc.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 1U);
acc.Abort();
}
{
@ -45,21 +39,21 @@ TEST(StorageV2, Commit) {
auto res = acc.DeleteVertex(&*vertex);
ASSERT_FALSE(res.HasError());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 0U);
acc.AdvanceCommand();
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 0U);
ASSERT_FALSE(acc.Commit().HasError());
}
{
auto acc = store.Access();
ASSERT_FALSE(acc.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
ASSERT_FALSE(acc.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 0U);
acc.Abort();
}
}
@ -73,17 +67,17 @@ TEST(StorageV2, Abort) {
auto vertex = acc.CreateVertex();
gid = vertex.Gid();
ASSERT_FALSE(acc.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
ASSERT_TRUE(acc.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 1U);
acc.Abort();
}
{
auto acc = store.Access();
ASSERT_FALSE(acc.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
ASSERT_FALSE(acc.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 0U);
acc.Abort();
}
}
@ -99,18 +93,18 @@ TEST(StorageV2, AdvanceCommandCommit) {
auto vertex1 = acc.CreateVertex();
gid1 = vertex1.Gid();
ASSERT_FALSE(acc.FindVertex(gid1, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
ASSERT_TRUE(acc.FindVertex(gid1, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 1U);
acc.AdvanceCommand();
auto vertex2 = acc.CreateVertex();
gid2 = vertex2.Gid();
ASSERT_FALSE(acc.FindVertex(gid2, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 1U);
ASSERT_TRUE(acc.FindVertex(gid2, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 2U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 2U);
ASSERT_TRUE(acc.FindVertex(gid1, storage::View::OLD).has_value());
ASSERT_TRUE(acc.FindVertex(gid1, storage::View::NEW).has_value());
@ -123,8 +117,8 @@ TEST(StorageV2, AdvanceCommandCommit) {
ASSERT_TRUE(acc.FindVertex(gid1, storage::View::NEW).has_value());
ASSERT_TRUE(acc.FindVertex(gid2, storage::View::OLD).has_value());
ASSERT_TRUE(acc.FindVertex(gid2, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 2U);
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 2U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 2U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 2U);
acc.Abort();
}
}
@ -140,18 +134,18 @@ TEST(StorageV2, AdvanceCommandAbort) {
auto vertex1 = acc.CreateVertex();
gid1 = vertex1.Gid();
ASSERT_FALSE(acc.FindVertex(gid1, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
ASSERT_TRUE(acc.FindVertex(gid1, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 1U);
acc.AdvanceCommand();
auto vertex2 = acc.CreateVertex();
gid2 = vertex2.Gid();
ASSERT_FALSE(acc.FindVertex(gid2, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 1U);
ASSERT_TRUE(acc.FindVertex(gid2, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 2U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 2U);
ASSERT_TRUE(acc.FindVertex(gid1, storage::View::OLD).has_value());
ASSERT_TRUE(acc.FindVertex(gid1, storage::View::NEW).has_value());
@ -164,8 +158,8 @@ TEST(StorageV2, AdvanceCommandAbort) {
ASSERT_FALSE(acc.FindVertex(gid1, storage::View::NEW).has_value());
ASSERT_FALSE(acc.FindVertex(gid2, storage::View::OLD).has_value());
ASSERT_FALSE(acc.FindVertex(gid2, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 0U);
acc.Abort();
}
}
@ -181,26 +175,26 @@ TEST(StorageV2, SnapshotIsolation) {
auto gid = vertex.Gid();
ASSERT_FALSE(acc2.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc1, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(&acc2, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc2, storage::View::OLD), 0U);
ASSERT_FALSE(acc2.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc1, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(&acc2, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc2, storage::View::NEW), 0U);
ASSERT_FALSE(acc1.Commit().HasError());
ASSERT_FALSE(acc2.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc2, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc2, storage::View::OLD), 0U);
ASSERT_FALSE(acc2.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc2, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc2, storage::View::NEW), 0U);
acc2.Abort();
auto acc3 = store.Access();
ASSERT_TRUE(acc3.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc3, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc3, storage::View::OLD), 1U);
ASSERT_TRUE(acc3.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc3, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc3, storage::View::NEW), 1U);
acc3.Abort();
}
@ -214,25 +208,25 @@ TEST(StorageV2, AccessorMove) {
gid = vertex.Gid();
ASSERT_FALSE(acc.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
ASSERT_TRUE(acc.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 1U);
storage::Storage::Accessor moved(std::move(acc));
ASSERT_FALSE(moved.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&moved, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(moved, storage::View::OLD), 0U);
ASSERT_TRUE(moved.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&moved, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(moved, storage::View::NEW), 1U);
ASSERT_FALSE(moved.Commit().HasError());
}
{
auto acc = store.Access();
ASSERT_TRUE(acc.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 1U);
ASSERT_TRUE(acc.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 1U);
acc.Abort();
}
}
@ -250,9 +244,9 @@ TEST(StorageV2, VertexDeleteCommit) {
auto vertex = acc2.CreateVertex();
gid = vertex.Gid();
ASSERT_FALSE(acc2.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc2, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc2, storage::View::OLD), 0U);
ASSERT_TRUE(acc2.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc2, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc2, storage::View::NEW), 1U);
ASSERT_FALSE(acc2.Commit().HasError());
}
@ -261,31 +255,31 @@ TEST(StorageV2, VertexDeleteCommit) {
// Check whether the vertex exists in transaction 1
ASSERT_FALSE(acc1.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc1, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::OLD), 0U);
ASSERT_FALSE(acc1.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc1, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::NEW), 0U);
// Check whether the vertex exists in transaction 3
ASSERT_TRUE(acc3.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc3, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc3, storage::View::OLD), 1U);
ASSERT_TRUE(acc3.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc3, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc3, storage::View::NEW), 1U);
// Delete the vertex in transaction 4
{
auto vertex = acc4.FindVertex(gid, storage::View::NEW);
ASSERT_TRUE(vertex);
EXPECT_EQ(CountVertices(&acc4, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(&acc4, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc4, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc4, storage::View::NEW), 1U);
auto res = acc4.DeleteVertex(&*vertex);
ASSERT_TRUE(res.HasValue());
EXPECT_EQ(CountVertices(&acc4, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(&acc4, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc4, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc4, storage::View::NEW), 0U);
acc4.AdvanceCommand();
EXPECT_EQ(CountVertices(&acc4, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(&acc4, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc4, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc4, storage::View::NEW), 0U);
ASSERT_FALSE(acc4.Commit().HasError());
}
@ -294,21 +288,21 @@ TEST(StorageV2, VertexDeleteCommit) {
// Check whether the vertex exists in transaction 1
ASSERT_FALSE(acc1.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc1, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::OLD), 0U);
ASSERT_FALSE(acc1.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc1, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::NEW), 0U);
// Check whether the vertex exists in transaction 3
ASSERT_TRUE(acc3.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc3, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc3, storage::View::OLD), 1U);
ASSERT_TRUE(acc3.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc3, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc3, storage::View::NEW), 1U);
// Check whether the vertex exists in transaction 5
ASSERT_FALSE(acc5.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc5, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc5, storage::View::OLD), 0U);
ASSERT_FALSE(acc5.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc5, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc5, storage::View::NEW), 0U);
}
// NOLINTNEXTLINE(hicpp-special-member-functions)
@ -324,9 +318,9 @@ TEST(StorageV2, VertexDeleteAbort) {
auto vertex = acc2.CreateVertex();
gid = vertex.Gid();
ASSERT_FALSE(acc2.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc2, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc2, storage::View::OLD), 0U);
ASSERT_TRUE(acc2.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc2, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc2, storage::View::NEW), 1U);
ASSERT_FALSE(acc2.Commit().HasError());
}
@ -335,31 +329,31 @@ TEST(StorageV2, VertexDeleteAbort) {
// Check whether the vertex exists in transaction 1
ASSERT_FALSE(acc1.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc1, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::OLD), 0U);
ASSERT_FALSE(acc1.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc1, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::NEW), 0U);
// Check whether the vertex exists in transaction 3
ASSERT_TRUE(acc3.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc3, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc3, storage::View::OLD), 1U);
ASSERT_TRUE(acc3.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc3, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc3, storage::View::NEW), 1U);
// Delete the vertex in transaction 4, but abort the transaction
{
auto vertex = acc4.FindVertex(gid, storage::View::NEW);
ASSERT_TRUE(vertex);
EXPECT_EQ(CountVertices(&acc4, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(&acc4, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc4, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc4, storage::View::NEW), 1U);
auto res = acc4.DeleteVertex(&*vertex);
ASSERT_TRUE(res.HasValue());
EXPECT_EQ(CountVertices(&acc4, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(&acc4, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc4, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc4, storage::View::NEW), 0U);
acc4.AdvanceCommand();
EXPECT_EQ(CountVertices(&acc4, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(&acc4, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc4, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc4, storage::View::NEW), 0U);
acc4.Abort();
}
@ -369,37 +363,37 @@ TEST(StorageV2, VertexDeleteAbort) {
// Check whether the vertex exists in transaction 1
ASSERT_FALSE(acc1.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc1, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::OLD), 0U);
ASSERT_FALSE(acc1.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc1, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::NEW), 0U);
// Check whether the vertex exists in transaction 3
ASSERT_TRUE(acc3.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc3, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc3, storage::View::OLD), 1U);
ASSERT_TRUE(acc3.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc3, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc3, storage::View::NEW), 1U);
// Check whether the vertex exists in transaction 5
ASSERT_TRUE(acc5.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc5, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc5, storage::View::OLD), 1U);
ASSERT_TRUE(acc5.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc5, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc5, storage::View::NEW), 1U);
// Delete the vertex in transaction 6
{
auto vertex = acc6.FindVertex(gid, storage::View::NEW);
ASSERT_TRUE(vertex);
EXPECT_EQ(CountVertices(&acc6, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(&acc6, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc6, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc6, storage::View::NEW), 1U);
auto res = acc6.DeleteVertex(&*vertex);
ASSERT_TRUE(res.HasValue());
EXPECT_EQ(CountVertices(&acc6, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(&acc6, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc6, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc6, storage::View::NEW), 0U);
acc6.AdvanceCommand();
EXPECT_EQ(CountVertices(&acc6, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(&acc6, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc6, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc6, storage::View::NEW), 0U);
ASSERT_FALSE(acc6.Commit().HasError());
}
@ -408,27 +402,27 @@ TEST(StorageV2, VertexDeleteAbort) {
// Check whether the vertex exists in transaction 1
ASSERT_FALSE(acc1.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc1, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::OLD), 0U);
ASSERT_FALSE(acc1.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc1, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::NEW), 0U);
// Check whether the vertex exists in transaction 3
ASSERT_TRUE(acc3.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc3, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc3, storage::View::OLD), 1U);
ASSERT_TRUE(acc3.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc3, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc3, storage::View::NEW), 1U);
// Check whether the vertex exists in transaction 5
ASSERT_TRUE(acc5.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc5, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc5, storage::View::OLD), 1U);
ASSERT_TRUE(acc5.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc5, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc5, storage::View::NEW), 1U);
// Check whether the vertex exists in transaction 7
ASSERT_FALSE(acc7.FindVertex(gid, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc7, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc7, storage::View::OLD), 0U);
ASSERT_FALSE(acc7.FindVertex(gid, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc7, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc7, storage::View::NEW), 0U);
// Commit all accessors
ASSERT_FALSE(acc1.Commit().HasError());
@ -457,44 +451,44 @@ TEST(StorageV2, VertexDeleteSerializationError) {
{
auto vertex = acc1.FindVertex(gid, storage::View::OLD);
ASSERT_TRUE(vertex);
EXPECT_EQ(CountVertices(&acc1, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(&acc1, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc1, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc1, storage::View::NEW), 1U);
{
auto res = acc1.DeleteVertex(&*vertex);
ASSERT_TRUE(res.HasValue());
ASSERT_TRUE(res.GetValue());
EXPECT_EQ(CountVertices(&acc1, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(&acc1, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc1, storage::View::NEW), 0U);
}
{
auto res = acc1.DeleteVertex(&*vertex);
ASSERT_TRUE(res.HasValue());
ASSERT_FALSE(res.GetValue());
EXPECT_EQ(CountVertices(&acc1, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(&acc1, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc1, storage::View::NEW), 0U);
}
acc1.AdvanceCommand();
EXPECT_EQ(CountVertices(&acc1, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(&acc1, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc1, storage::View::NEW), 0U);
}
// Delete vertex in accessor 2
{
auto vertex = acc2.FindVertex(gid, storage::View::OLD);
ASSERT_TRUE(vertex);
EXPECT_EQ(CountVertices(&acc2, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(&acc2, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc2, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc2, storage::View::NEW), 1U);
auto res = acc2.DeleteVertex(&*vertex);
ASSERT_TRUE(res.HasError());
ASSERT_EQ(res.GetError(), storage::Error::SERIALIZATION_ERROR);
EXPECT_EQ(CountVertices(&acc2, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(&acc2, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc2, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc2, storage::View::NEW), 1U);
acc2.AdvanceCommand();
EXPECT_EQ(CountVertices(&acc2, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(&acc2, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc2, storage::View::OLD), 1U);
EXPECT_EQ(CountVertices(acc2, storage::View::NEW), 1U);
}
// Finalize both accessors
@ -506,8 +500,8 @@ TEST(StorageV2, VertexDeleteSerializationError) {
auto acc = store.Access();
auto vertex = acc.FindVertex(gid, storage::View::OLD);
ASSERT_FALSE(vertex);
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 0U);
ASSERT_FALSE(acc.Commit().HasError());
}
}
@ -525,17 +519,17 @@ TEST(StorageV2, VertexDeleteSpecialCases) {
auto vertex = acc.CreateVertex();
gid1 = vertex.Gid();
ASSERT_FALSE(acc.FindVertex(gid1, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
ASSERT_TRUE(acc.FindVertex(gid1, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 1U);
auto res = acc.DeleteVertex(&vertex);
ASSERT_TRUE(res.HasValue());
ASSERT_TRUE(res.GetValue());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 0U);
acc.AdvanceCommand();
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 0U);
acc.Abort();
}
@ -545,17 +539,17 @@ TEST(StorageV2, VertexDeleteSpecialCases) {
auto vertex = acc.CreateVertex();
gid2 = vertex.Gid();
ASSERT_FALSE(acc.FindVertex(gid2, storage::View::OLD).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
ASSERT_TRUE(acc.FindVertex(gid2, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 1U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 1U);
auto res = acc.DeleteVertex(&vertex);
ASSERT_TRUE(res.HasValue());
ASSERT_TRUE(res.GetValue());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 0U);
acc.AdvanceCommand();
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 0U);
ASSERT_FALSE(acc.Commit().HasError());
}
@ -566,8 +560,8 @@ TEST(StorageV2, VertexDeleteSpecialCases) {
ASSERT_FALSE(acc.FindVertex(gid1, storage::View::NEW).has_value());
ASSERT_FALSE(acc.FindVertex(gid2, storage::View::OLD).has_value());
ASSERT_FALSE(acc.FindVertex(gid2, storage::View::NEW).has_value());
EXPECT_EQ(CountVertices(&acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(&acc, storage::View::NEW), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::OLD), 0U);
EXPECT_EQ(CountVertices(acc, storage::View::NEW), 0U);
acc.Abort();
}
}

View File

@ -18,7 +18,7 @@ TResult ExpectNoError(const char *file, int line, TFunc func, TArgs &&...args) {
static_assert(std::is_trivially_copyable_v<TFunc>);
static_assert((std::is_trivially_copyable_v<std::remove_reference_t<TArgs>> && ...));
TResult result{};
EXPECT_EQ(func(args..., &result), MGP_ERROR_NO_ERROR) << fmt::format("Source of error: {} at line {}", file, line);
EXPECT_EQ(func(args..., &result), MGP_ERROR_NO_ERROR) << fmt::format("Source of error: {}:{}", file, line);
return result;
}
} // namespace test_utils