diff --git a/include/mg_procedure.h b/include/mg_procedure.h index 93b9b865f..00bd5942d 100644 --- a/include/mg_procedure.h +++ b/include/mg_procedure.h @@ -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. /// diff --git a/query_modules/example.c b/query_modules/example.c index c64a44a5f..1a4dfb8c0 100644 --- a/query_modules/example.c +++ b/query_modules/example.c @@ -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; } diff --git a/src/query/plan/operator.cpp b/src/query/plan/operator.cpp index 492624a05..89c8847ac 100644 --- a/src/query/plan/operator.cpp +++ b/src/query/plan/operator.cpp @@ -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" diff --git a/src/query/procedure/cypher_type_ptr.hpp b/src/query/procedure/cypher_type_ptr.hpp new file mode 100644 index 000000000..010c3955a --- /dev/null +++ b/src/query/procedure/cypher_type_ptr.hpp @@ -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 \ No newline at end of file diff --git a/src/query/procedure/cypher_types.hpp b/src/query/procedure/cypher_types.hpp index 33ef5cc56..e7a41100d 100644 --- a/src/query/procedure/cypher_types.hpp +++ b/src/query/procedure/cypher_types.hpp @@ -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 { diff --git a/src/query/procedure/mg_procedure_impl.cpp b/src/query/procedure/mg_procedure_impl.cpp index efd45be1c..d09078a9f 100644 --- a/src/query/procedure/mg_procedure_impl.cpp +++ b/src/query/procedure/mg_procedure_impl.cpp @@ -5,13 +5,18 @@ #include <cstring> #include <exception> #include <memory> +#include <optional> #include <regex> #include <stdexcept> #include <type_traits> #include <utility> +#include "mg_procedure.h" #include "module.hpp" +#include "query/procedure/cypher_types.hpp" #include "query/procedure/mg_procedure_helpers.hpp" +#include "storage/v2/property_value.hpp" +#include "storage/v2/view.hpp" #include "utils/algorithm.hpp" #include "utils/concepts.hpp" #include "utils/logging.hpp" @@ -76,7 +81,7 @@ void MgpFreeImpl(utils::MemoryResource &memory, void *const p) noexcept { spdlog::error("Unexpected throw during the release of memory for query modules"); } } -struct NonexistentObjectException : public utils::BasicException { +struct DeletedObjectException : public utils::BasicException { using utils::BasicException::BasicException; }; @@ -88,6 +93,18 @@ struct InsufficientBufferException : public utils::BasicException { using utils::BasicException::BasicException; }; +struct ImmutableObjectException : public utils::BasicException { + using utils::BasicException::BasicException; +}; + +struct ValueConversionException : public utils::BasicException { + using utils::BasicException::BasicException; +}; + +struct SerializationException : public utils::BasicException { + using utils::BasicException::BasicException; +}; + template <typename TFunc, typename TReturn> concept ReturnsType = std::same_as<std::invoke_result_t<TFunc>, TReturn>; @@ -110,15 +127,24 @@ template <typename TFunc, typename... Args> static_assert(sizeof...(args) <= 1, "WrapExceptions should have only one or zero parameter!"); try { WrapExceptionsHelper(std::forward<TFunc>(func), std::forward<Args>(args)...); - } catch (const NonexistentObjectException &neoe) { - spdlog::error("Nonexistent object error during mg API call: {}", neoe.what()); - return MGP_ERROR_NON_EXISTENT_OBJECT; + } catch (const DeletedObjectException &neoe) { + spdlog::error("Deleted object error during mg API call: {}", neoe.what()); + return MGP_ERROR_DELETED_OBJECT; } catch (const KeyAlreadyExistsException &kaee) { spdlog::error("Key already exists error during mg API call: {}", kaee.what()); return MGP_ERROR_KEY_ALREADY_EXISTS; } catch (const InsufficientBufferException &ibe) { spdlog::error("Insufficient buffer error during mg API call: {}", ibe.what()); return MGP_ERROR_INSUFFICIENT_BUFFER; + } catch (const ImmutableObjectException &ioe) { + spdlog::error("Immutable object error during mg API call: {}", ioe.what()); + return MGP_ERROR_IMMUTABLE_OBJECT; + } catch (const ValueConversionException &vce) { + spdlog::error("Value converion error during mg API call: {}", vce.what()); + return MGP_ERROR_VALUE_CONVERSION; + } catch (const SerializationException &se) { + spdlog::error("Serialization error during mg API call: {}", se.what()); + return MGP_ERROR_SERIALIZATION_ERROR; } catch (const std::bad_alloc &bae) { spdlog::error("Memory allocation error during mg API call: {}", bae.what()); return MGP_ERROR_UNABLE_TO_ALLOCATE; @@ -143,6 +169,12 @@ template <typename TFunc, typename... Args> } return MGP_ERROR_NO_ERROR; } + +bool MgpGraphIsMutable(const mgp_graph &graph) noexcept { return graph.view == storage::View::NEW; } + +bool MgpVertexIsMutable(const mgp_vertex &vertex) { return MgpGraphIsMutable(*vertex.graph); } + +bool MgpEdgeIsMutable(const mgp_edge &edge) { return MgpVertexIsMutable(edge.from); } } // namespace mgp_error mgp_alloc(mgp_memory *memory, size_t size_in_bytes, void **result) { @@ -234,19 +266,19 @@ mgp_value_type FromTypedValueType(query::TypedValue::Type type) { } query::TypedValue ToTypedValue(const mgp_value &val, utils::MemoryResource *memory) { - switch (Call<mgp_value_type>(mgp_value_get_type, &val)) { + switch (val.type) { case MGP_VALUE_TYPE_NULL: return query::TypedValue(memory); case MGP_VALUE_TYPE_BOOL: - return query::TypedValue(CallBool(mgp_value_get_bool, &val), memory); + return query::TypedValue(val.bool_v, memory); case MGP_VALUE_TYPE_INT: - return query::TypedValue(Call<int64_t>(mgp_value_get_int, &val), memory); + return query::TypedValue(val.int_v, memory); case MGP_VALUE_TYPE_DOUBLE: - return query::TypedValue(Call<double>(mgp_value_get_double, &val), memory); + return query::TypedValue(val.double_v, memory); case MGP_VALUE_TYPE_STRING: - return query::TypedValue(Call<const char *>(mgp_value_get_string, &val), memory); + return query::TypedValue(val.string_v, memory); case MGP_VALUE_TYPE_LIST: { - const auto *list = Call<const mgp_list *>(mgp_value_get_list, &val); + const auto *list = val.list_v; query::TypedValue::TVector tv_list(memory); tv_list.reserve(list->elems.size()); for (const auto &elem : list->elems) { @@ -255,7 +287,7 @@ query::TypedValue ToTypedValue(const mgp_value &val, utils::MemoryResource *memo return query::TypedValue(std::move(tv_list)); } case MGP_VALUE_TYPE_MAP: { - const auto *map = Call<const mgp_map *>(mgp_value_get_map, &val); + const auto *map = val.map_v; query::TypedValue::TMap tv_map(memory); for (const auto &item : map->items) { tv_map.emplace(item.first, ToTypedValue(item.second, memory)); @@ -263,11 +295,11 @@ query::TypedValue ToTypedValue(const mgp_value &val, utils::MemoryResource *memo return query::TypedValue(std::move(tv_map)); } case MGP_VALUE_TYPE_VERTEX: - return query::TypedValue(Call<const mgp_vertex *>(mgp_value_get_vertex, &val)->impl, memory); + return query::TypedValue(val.vertex_v->impl, memory); case MGP_VALUE_TYPE_EDGE: - return query::TypedValue(Call<const mgp_edge *>(mgp_value_get_edge, &val)->impl, memory); + return query::TypedValue(val.edge_v->impl, memory); case MGP_VALUE_TYPE_PATH: { - const auto *path = Call<const mgp_path *>(mgp_value_get_path, &val); + const auto *path = val.path_v; MG_ASSERT(!path->vertices.empty()); MG_ASSERT(path->vertices.size() == path->edges.size() + 1); query::Path tv_path(path->vertices[0].impl, memory); @@ -320,7 +352,7 @@ mgp_value::mgp_value(mgp_path *val, utils::MemoryResource *m) noexcept MG_ASSERT(val->GetMemoryResource() == m, "Unable to take ownership of a pointer with different allocator."); } -mgp_value::mgp_value(const query::TypedValue &tv, const mgp_graph *graph, utils::MemoryResource *m) +mgp_value::mgp_value(const query::TypedValue &tv, mgp_graph *graph, utils::MemoryResource *m) : type(FromTypedValueType(tv.type())), memory(m) { switch (type) { case MGP_VALUE_TYPE_NULL: @@ -600,6 +632,10 @@ mgp_value::mgp_value(mgp_value &&other, utils::MemoryResource *m) : type(other.t mgp_value::~mgp_value() noexcept { DeleteValueMember(this); } +mgp_edge *mgp_edge::Copy(const mgp_edge &edge, mgp_memory &memory) { + return NewRawMgpObject<mgp_edge>(&memory, edge.impl, edge.from.graph); +} + void mgp_value_destroy(mgp_value *val) { DeleteRawMgpObject(val); } mgp_error mgp_value_make_null(mgp_memory *memory, mgp_value **result) { @@ -637,18 +673,18 @@ namespace { mgp_value_type MgpValueGetType(const mgp_value &val) noexcept { return val.type; } } // namespace -mgp_error mgp_value_get_type(const mgp_value *val, mgp_value_type *result) { +mgp_error mgp_value_get_type(mgp_value *val, mgp_value_type *result) { static_assert(noexcept(MgpValueGetType(*val))); *result = MgpValueGetType(*val); return MGP_ERROR_NO_ERROR; } // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define DEFINE_MGP_VALUE_IS(type_lowercase, type_uppercase) \ - mgp_error mgp_value_is_##type_lowercase(const mgp_value *val, int *result) { \ - static_assert(noexcept(MgpValueGetType(*val))); \ - *result = MgpValueGetType(*val) == MGP_VALUE_TYPE_##type_uppercase; \ - return MGP_ERROR_NO_ERROR; \ +#define DEFINE_MGP_VALUE_IS(type_lowercase, type_uppercase) \ + mgp_error mgp_value_is_##type_lowercase(mgp_value *val, int *result) { \ + static_assert(noexcept(MgpValueGetType(*val))); \ + *result = MgpValueGetType(*val) == MGP_VALUE_TYPE_##type_uppercase; \ + return MGP_ERROR_NO_ERROR; \ } DEFINE_MGP_VALUE_IS(null, NULL) @@ -662,29 +698,29 @@ DEFINE_MGP_VALUE_IS(vertex, VERTEX) DEFINE_MGP_VALUE_IS(edge, EDGE) DEFINE_MGP_VALUE_IS(path, PATH) -mgp_error mgp_value_get_bool(const mgp_value *val, int *result) { +mgp_error mgp_value_get_bool(mgp_value *val, int *result) { *result = val->bool_v ? 1 : 0; return MGP_ERROR_NO_ERROR; } -mgp_error mgp_value_get_int(const mgp_value *val, int64_t *result) { +mgp_error mgp_value_get_int(mgp_value *val, int64_t *result) { *result = val->int_v; return MGP_ERROR_NO_ERROR; } -mgp_error mgp_value_get_double(const mgp_value *val, double *result) { +mgp_error mgp_value_get_double(mgp_value *val, double *result) { *result = val->double_v; return MGP_ERROR_NO_ERROR; } -mgp_error mgp_value_get_string(const mgp_value *val, const char **result) { +mgp_error mgp_value_get_string(mgp_value *val, const char **result) { static_assert(noexcept(val->string_v.c_str())); *result = val->string_v.c_str(); return MGP_ERROR_NO_ERROR; } // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define DEFINE_MGP_VALUE_GET(type) \ - mgp_error mgp_value_get_##type(const mgp_value *val, const mgp_##type **result) { \ - *result = val->type##_v; \ - return MGP_ERROR_NO_ERROR; \ +#define DEFINE_MGP_VALUE_GET(type) \ + mgp_error mgp_value_get_##type(mgp_value *val, mgp_##type **result) { \ + *result = val->type##_v; \ + return MGP_ERROR_NO_ERROR; \ } DEFINE_MGP_VALUE_GET(list) @@ -709,7 +745,7 @@ namespace { void MgpListAppendExtend(mgp_list &list, const mgp_value &value) { list.elems.push_back(value); } } // namespace -mgp_error mgp_list_append(mgp_list *list, const mgp_value *val) { +mgp_error mgp_list_append(mgp_list *list, mgp_value *val) { return WrapExceptions([list, val] { if (Call<size_t>(mgp_list_size, list) >= Call<size_t>(mgp_list_capacity, list)) { throw InsufficientBufferException{ @@ -719,23 +755,23 @@ mgp_error mgp_list_append(mgp_list *list, const mgp_value *val) { }); } -mgp_error mgp_list_append_extend(mgp_list *list, const mgp_value *val) { +mgp_error mgp_list_append_extend(mgp_list *list, mgp_value *val) { return WrapExceptions([list, val] { MgpListAppendExtend(*list, *val); }); } -mgp_error mgp_list_size(const mgp_list *list, size_t *result) { +mgp_error mgp_list_size(mgp_list *list, size_t *result) { static_assert(noexcept(list->elems.size())); *result = list->elems.size(); return MGP_ERROR_NO_ERROR; } -mgp_error mgp_list_capacity(const mgp_list *list, size_t *result) { +mgp_error mgp_list_capacity(mgp_list *list, size_t *result) { static_assert(noexcept(list->elems.capacity())); *result = list->elems.capacity(); return MGP_ERROR_NO_ERROR; } -mgp_error mgp_list_at(const mgp_list *list, size_t i, const mgp_value **result) { +mgp_error mgp_list_at(mgp_list *list, size_t i, mgp_value **result) { return WrapExceptions( [list, i] { if (i >= Call<size_t>(mgp_list_size, list)) { @@ -752,7 +788,7 @@ mgp_error mgp_map_make_empty(mgp_memory *memory, mgp_map **result) { void mgp_map_destroy(mgp_map *map) { DeleteRawMgpObject(map); } -mgp_error mgp_map_insert(mgp_map *map, const char *key, const mgp_value *value) { +mgp_error mgp_map_insert(mgp_map *map, const char *key, mgp_value *value) { return WrapExceptions([&] { auto emplace_result = map->items.emplace(key, *value); if (!emplace_result.second) { @@ -761,15 +797,15 @@ mgp_error mgp_map_insert(mgp_map *map, const char *key, const mgp_value *value) }); } -mgp_error mgp_map_size(const mgp_map *map, size_t *result) { +mgp_error mgp_map_size(mgp_map *map, size_t *result) { static_assert(noexcept(map->items.size())); *result = map->items.size(); return MGP_ERROR_NO_ERROR; } -mgp_error mgp_map_at(const mgp_map *map, const char *key, const mgp_value **result) { +mgp_error mgp_map_at(mgp_map *map, const char *key, mgp_value **result) { return WrapExceptions( - [&map, &key]() -> const mgp_value * { + [&map, &key]() -> mgp_value * { auto found_it = map->items.find(key); if (found_it == map->items.end()) { return nullptr; @@ -783,7 +819,7 @@ mgp_error mgp_map_item_key(mgp_map_item *item, const char **result) { return WrapExceptions([&item] { return item->key; }, result); } -mgp_error mgp_map_item_value(const mgp_map_item *item, const mgp_value **result) { +mgp_error mgp_map_item_value(mgp_map_item *item, mgp_value **result) { return WrapExceptions([item] { return item->value; }, result); } @@ -793,9 +829,9 @@ mgp_error mgp_map_iter_items(mgp_map *map, mgp_memory *memory, mgp_map_items_ite void mgp_map_items_iterator_destroy(mgp_map_items_iterator *it) { DeleteRawMgpObject(it); } -mgp_error mgp_map_items_iterator_get(const mgp_map_items_iterator *it, const mgp_map_item **result) { +mgp_error mgp_map_items_iterator_get(mgp_map_items_iterator *it, mgp_map_item **result) { return WrapExceptions( - [it]() -> const mgp_map_item * { + [it]() -> mgp_map_item * { if (it->current_it == it->map->items.end()) { return nullptr; }; @@ -804,9 +840,9 @@ mgp_error mgp_map_items_iterator_get(const mgp_map_items_iterator *it, const mgp result); } -mgp_error mgp_map_items_iterator_next(mgp_map_items_iterator *it, const mgp_map_item **result) { +mgp_error mgp_map_items_iterator_next(mgp_map_items_iterator *it, mgp_map_item **result) { return WrapExceptions( - [it]() -> const mgp_map_item * { + [it]() -> mgp_map_item * { if (it->current_it == it->map->items.end()) { return nullptr; } @@ -820,7 +856,7 @@ mgp_error mgp_map_items_iterator_next(mgp_map_items_iterator *it, const mgp_map_ result); } -mgp_error mgp_path_make_with_start(const mgp_vertex *vertex, mgp_memory *memory, mgp_path **result) { +mgp_error mgp_path_make_with_start(mgp_vertex *vertex, mgp_memory *memory, mgp_path **result) { return WrapExceptions( [vertex, memory]() -> mgp_path * { auto path = NewMgpObject<mgp_path>(memory); @@ -833,7 +869,7 @@ mgp_error mgp_path_make_with_start(const mgp_vertex *vertex, mgp_memory *memory, result); } -mgp_error mgp_path_copy(const mgp_path *path, mgp_memory *memory, mgp_path **result) { +mgp_error mgp_path_copy(mgp_path *path, mgp_memory *memory, mgp_path **result) { return WrapExceptions( [path, memory] { MG_ASSERT(Call<size_t>(mgp_path_size, path) == path->vertices.size() - 1, "Invalid mgp_path"); @@ -844,17 +880,17 @@ mgp_error mgp_path_copy(const mgp_path *path, mgp_memory *memory, mgp_path **res void mgp_path_destroy(mgp_path *path) { DeleteRawMgpObject(path); } -mgp_error mgp_path_expand(mgp_path *path, const mgp_edge *edge) { +mgp_error mgp_path_expand(mgp_path *path, mgp_edge *edge) { return WrapExceptions([path, edge] { MG_ASSERT(Call<size_t>(mgp_path_size, path) == path->vertices.size() - 1, "Invalid mgp_path"); // Check that the both the last vertex on path and dst_vertex are endpoints of // the given edge. - const auto *src_vertex = &path->vertices.back(); - const mgp_vertex *dst_vertex = nullptr; - if (CallBool(mgp_vertex_equal, Call<const mgp_vertex *>(mgp_edge_get_to, edge), src_vertex) != 0) { - dst_vertex = Call<const mgp_vertex *>(mgp_edge_get_from, edge); - } else if (CallBool(mgp_vertex_equal, Call<const mgp_vertex *>(mgp_edge_get_from, edge), src_vertex)) { - dst_vertex = Call<const mgp_vertex *>(mgp_edge_get_to, edge); + auto *src_vertex = &path->vertices.back(); + mgp_vertex *dst_vertex{nullptr}; + if (edge->to == *src_vertex) { + dst_vertex = &edge->from; + } else if (edge->from == *src_vertex) { + dst_vertex = &edge->to; } else { // edge is not a continuation on src_vertex throw std::logic_error{"The current last vertex in the path is not part of the given edge."}; @@ -873,12 +909,12 @@ namespace { size_t MgpPathSize(const mgp_path &path) noexcept { return path.edges.size(); } } // namespace -mgp_error mgp_path_size(const mgp_path *path, size_t *result) { +mgp_error mgp_path_size(mgp_path *path, size_t *result) { *result = MgpPathSize(*path); return MGP_ERROR_NO_ERROR; } -mgp_error mgp_path_vertex_at(const mgp_path *path, size_t i, const mgp_vertex **result) { +mgp_error mgp_path_vertex_at(mgp_path *path, size_t i, mgp_vertex **result) { return WrapExceptions( [path, i] { const auto path_size = Call<size_t>(mgp_path_size, path); @@ -891,7 +927,7 @@ mgp_error mgp_path_vertex_at(const mgp_path *path, size_t i, const mgp_vertex ** result); } -mgp_error mgp_path_edge_at(const mgp_path *path, size_t i, const mgp_edge **result) { +mgp_error mgp_path_edge_at(mgp_path *path, size_t i, mgp_edge **result) { return WrapExceptions( [path, i] { const auto path_size = Call<size_t>(mgp_path_size, path); @@ -904,7 +940,7 @@ mgp_error mgp_path_edge_at(const mgp_path *path, size_t i, const mgp_edge **resu result); } -mgp_error mgp_path_equal(const struct mgp_path *p1, const struct mgp_path *p2, int *result) { +mgp_error mgp_path_equal(mgp_path *p1, mgp_path *p2, int *result) { return WrapExceptions( [p1, p2] { const auto p1_size = MgpPathSize(*p1); @@ -914,15 +950,15 @@ mgp_error mgp_path_equal(const struct mgp_path *p1, const struct mgp_path *p2, i if (p1_size != p2_size) { return 0; } - const auto *start1 = Call<const mgp_vertex *>(mgp_path_vertex_at, p1, 0); - const auto *start2 = Call<const mgp_vertex *>(mgp_path_vertex_at, p2, 0); + const auto *start1 = Call<mgp_vertex *>(mgp_path_vertex_at, p1, 0); + const auto *start2 = Call<mgp_vertex *>(mgp_path_vertex_at, p2, 0); static_assert(noexcept(start1->impl == start2->impl)); if (*start1 != *start2) { return 0; } for (size_t i = 0; i < p1_size; ++i) { - const auto *e1 = Call<const mgp_edge *>(mgp_path_edge_at, p1, i); - const auto *e2 = Call<const mgp_edge *>(mgp_path_edge_at, p2, i); + const auto *e1 = Call<mgp_edge *>(mgp_path_edge_at, p1, i); + const auto *e2 = Call<mgp_edge *>(mgp_path_edge_at, p2, i); if (*e1 != *e2) { return 0; } @@ -953,7 +989,7 @@ mgp_error mgp_result_new_record(mgp_result *res, mgp_result_record **result) { result); } -mgp_error mgp_result_record_insert(mgp_result_record *record, const char *field_name, const mgp_value *val) { +mgp_error mgp_result_record_insert(mgp_result_record *record, const char *field_name, mgp_value *val) { return WrapExceptions([=] { auto *memory = record->values.get_allocator().GetMemoryResource(); // Validate field_name & val satisfy the procedure's result signature. @@ -975,9 +1011,9 @@ mgp_error mgp_result_record_insert(mgp_result_record *record, const char *field_ void mgp_properties_iterator_destroy(mgp_properties_iterator *it) { DeleteRawMgpObject(it); } -mgp_error mgp_properties_iterator_get(const mgp_properties_iterator *it, const mgp_property **result) { +mgp_error mgp_properties_iterator_get(mgp_properties_iterator *it, mgp_property **result) { return WrapExceptions( - [it]() -> const mgp_property * { + [it]() -> mgp_property * { if (it->current) { return &it->property; }; @@ -986,7 +1022,7 @@ mgp_error mgp_properties_iterator_get(const mgp_properties_iterator *it, const m result); } -mgp_error mgp_properties_iterator_next(mgp_properties_iterator *it, const mgp_property **result) { +mgp_error mgp_properties_iterator_next(mgp_properties_iterator *it, mgp_property **result) { // Incrementing the iterator either for on-disk or in-memory // storage, so perhaps the underlying thing can throw. // Both copying TypedValue and/or string from PropertyName may fail to @@ -995,7 +1031,7 @@ mgp_error mgp_properties_iterator_next(mgp_properties_iterator *it, const mgp_pr // Hopefully iterator comparison doesn't throw, but wrap the whole thing in // try ... catch just to be sure. return WrapExceptions( - [it]() -> const mgp_property * { + [it]() -> mgp_property * { if (it->current_it == it->pvs.end()) { MG_ASSERT(!it->current, "Iteration is already done, so it->current should " @@ -1018,36 +1054,158 @@ mgp_error mgp_properties_iterator_next(mgp_properties_iterator *it, const mgp_pr result); } -mgp_error mgp_vertex_get_id(const mgp_vertex *v, mgp_vertex_id *result) { +mgp_error mgp_vertex_get_id(mgp_vertex *v, mgp_vertex_id *result) { return WrapExceptions([v] { return mgp_vertex_id{.as_int = v->impl.Gid().AsInt()}; }, result); } -mgp_error mgp_vertex_copy(const mgp_vertex *v, mgp_memory *memory, mgp_vertex **result) { +mgp_error mgp_vertex_underlying_graph_is_mutable(mgp_vertex *v, int *result) { + return mgp_graph_is_mutable(v->graph, result); +} + +namespace { +storage::PropertyValue ToPropertyValue(const mgp_value &value); + +storage::PropertyValue ToPropertyValue(const mgp_list &list) { + storage::PropertyValue result{std::vector<storage::PropertyValue>{}}; + auto &result_list = result.ValueList(); + for (const auto &value : list.elems) { + result_list.push_back(ToPropertyValue(value)); + } + return result; +} + +storage::PropertyValue ToPropertyValue(const mgp_map &map) { + storage::PropertyValue result{std::map<std::string, storage::PropertyValue>{}}; + auto &result_map = result.ValueMap(); + for (const auto &[key, value] : map.items) { + result_map.insert_or_assign(std::string{key}, ToPropertyValue(value)); + } + return result; +} + +storage::PropertyValue ToPropertyValue(const mgp_value &value) { + switch (value.type) { + case MGP_VALUE_TYPE_NULL: + return storage::PropertyValue{}; + case MGP_VALUE_TYPE_BOOL: + return storage::PropertyValue{value.bool_v}; + case MGP_VALUE_TYPE_INT: + return storage::PropertyValue{value.int_v}; + case MGP_VALUE_TYPE_DOUBLE: + return storage::PropertyValue{value.double_v}; + case MGP_VALUE_TYPE_STRING: + return storage::PropertyValue{std::string{value.string_v}}; + case MGP_VALUE_TYPE_LIST: + return ToPropertyValue(*value.list_v); + case MGP_VALUE_TYPE_MAP: + return ToPropertyValue(*value.map_v); + case MGP_VALUE_TYPE_VERTEX: + throw ValueConversionException{"A vertex is not a valid property value! "}; + case MGP_VALUE_TYPE_EDGE: + throw ValueConversionException{"An edge is not a valid property value!"}; + case MGP_VALUE_TYPE_PATH: + throw ValueConversionException{"A path is not a valid property value!"}; + } +} +} // namespace + +mgp_error mgp_vertex_set_property(struct mgp_vertex *v, const char *property_name, mgp_value *property_value) { + return WrapExceptions([=] { + if (!MgpVertexIsMutable(*v)) { + throw ImmutableObjectException{"Cannot set a property on an immutable vertex!"}; + } + const auto result = + v->impl.SetProperty(v->graph->impl->NameToProperty(property_name), ToPropertyValue(*property_value)); + + if (result.HasError()) { + switch (result.GetError()) { + case storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot set the properties of a deleted vertex!"}; + case storage::Error::NONEXISTENT_OBJECT: + LOG_FATAL("Query modules mustn't have access to nonexistent objects when setting a property of a vertex!"); + case storage::Error::PROPERTIES_DISABLED: + case storage::Error::VERTEX_HAS_EDGES: + LOG_FATAL("Unexpected error when setting a property of a vertex."); + case storage::Error::SERIALIZATION_ERROR: + throw SerializationException{"Cannot serialize setting a property of a vertex."}; + } + } + }); +} + +mgp_error mgp_vertex_add_label(struct mgp_vertex *v, mgp_label label) { + return WrapExceptions([=] { + if (!MgpVertexIsMutable(*v)) { + throw ImmutableObjectException{"Cannot add a label to an immutable vertex!"}; + } + const auto result = v->impl.AddLabel(v->graph->impl->NameToLabel(label.name)); + + if (result.HasError()) { + switch (result.GetError()) { + case storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot add a label to a deleted vertex!"}; + case storage::Error::NONEXISTENT_OBJECT: + LOG_FATAL("Query modules mustn't have access to nonexistent objects when adding a label to a vertex!"); + case storage::Error::PROPERTIES_DISABLED: + case storage::Error::VERTEX_HAS_EDGES: + LOG_FATAL("Unexpected error when adding a label to a vertex."); + case storage::Error::SERIALIZATION_ERROR: + throw SerializationException{"Cannot serialize adding a label to a vertex."}; + } + } + }); +} + +mgp_error mgp_vertex_remove_label(struct mgp_vertex *v, mgp_label label) { + return WrapExceptions([=] { + if (!MgpVertexIsMutable(*v)) { + throw ImmutableObjectException{"Cannot remove a label from an immutable vertex!"}; + } + const auto result = v->impl.RemoveLabel(v->graph->impl->NameToLabel(label.name)); + + if (result.HasError()) { + switch (result.GetError()) { + case storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot remove a label from a deleted vertex!"}; + case storage::Error::NONEXISTENT_OBJECT: + LOG_FATAL("Query modules mustn't have access to nonexistent objects when removing a label from a vertex!"); + case storage::Error::PROPERTIES_DISABLED: + case storage::Error::VERTEX_HAS_EDGES: + LOG_FATAL("Unexpected error when removing a label from a vertex."); + case storage::Error::SERIALIZATION_ERROR: + throw SerializationException{"Cannot serialize removing a label from a vertex."}; + } + } + }); +} + +mgp_error mgp_vertex_copy(mgp_vertex *v, mgp_memory *memory, mgp_vertex **result) { return WrapExceptions([v, memory] { return NewRawMgpObject<mgp_vertex>(memory, *v); }, result); } void mgp_vertex_destroy(mgp_vertex *v) { DeleteRawMgpObject(v); } -mgp_error mgp_vertex_equal(const mgp_vertex *v1, const mgp_vertex *v2, int *result) { +mgp_error mgp_vertex_equal(mgp_vertex *v1, mgp_vertex *v2, int *result) { // NOLINTNEXTLINE(clang-diagnostic-unevaluated-expression) static_assert(noexcept(*result = *v1 == *v2 ? 1 : 0)); *result = *v1 == *v2 ? 1 : 0; return MGP_ERROR_NO_ERROR; } -mgp_error mgp_vertex_labels_count(const mgp_vertex *v, size_t *result) { +mgp_error mgp_vertex_labels_count(mgp_vertex *v, size_t *result) { return WrapExceptions( [v]() -> size_t { auto maybe_labels = v->impl.Labels(v->graph->view); if (maybe_labels.HasError()) { switch (maybe_labels.GetError()) { case storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot get the labels of a deleted vertex!"}; case storage::Error::NONEXISTENT_OBJECT: - throw NonexistentObjectException{"Cannot get the labels of nonexistent vertex"}; + LOG_FATAL("Query modules mustn't have access to nonexistent objects when getting vertex labels!"); case storage::Error::PROPERTIES_DISABLED: case storage::Error::VERTEX_HAS_EDGES: case storage::Error::SERIALIZATION_ERROR: - MG_ASSERT(false, "Unexpected error when getting vertex labels."); + LOG_FATAL("Unexpected error when getting vertex labels."); } } return maybe_labels->size(); @@ -1055,7 +1213,7 @@ mgp_error mgp_vertex_labels_count(const mgp_vertex *v, size_t *result) { result); } -mgp_error mgp_vertex_label_at(const mgp_vertex *v, size_t i, mgp_label *result) { +mgp_error mgp_vertex_label_at(mgp_vertex *v, size_t i, mgp_label *result) { return WrapExceptions( [v, i]() -> const char * { // TODO: Maybe it's worth caching this in mgp_vertex. @@ -1063,12 +1221,13 @@ mgp_error mgp_vertex_label_at(const mgp_vertex *v, size_t i, mgp_label *result) if (maybe_labels.HasError()) { switch (maybe_labels.GetError()) { case storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot get a label of a deleted vertex!"}; case storage::Error::NONEXISTENT_OBJECT: - throw NonexistentObjectException{"Cannot get a label of nonexistent vertex"}; + LOG_FATAL("Query modules mustn't have access to nonexistent objects when getting a label of a vertex!"); case storage::Error::PROPERTIES_DISABLED: case storage::Error::VERTEX_HAS_EDGES: case storage::Error::SERIALIZATION_ERROR: - MG_ASSERT(false, "Unexpected error when getting a label of a vertex."); + LOG_FATAL("Unexpected error when getting a label of a vertex."); } } if (i >= maybe_labels->size()) { @@ -1084,7 +1243,7 @@ mgp_error mgp_vertex_label_at(const mgp_vertex *v, size_t i, mgp_label *result) &result->name); } -mgp_error mgp_vertex_has_label_named(const mgp_vertex *v, const char *name, int *result) { +mgp_error mgp_vertex_has_label_named(mgp_vertex *v, const char *name, int *result) { return WrapExceptions( [v, name] { storage::LabelId label; @@ -1094,24 +1253,27 @@ mgp_error mgp_vertex_has_label_named(const mgp_vertex *v, const char *name, int if (maybe_has_label.HasError()) { switch (maybe_has_label.GetError()) { case storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot check the existence of a label on a deleted vertex!"}; case storage::Error::NONEXISTENT_OBJECT: - throw NonexistentObjectException{"Cannot check the existence of a label on nonexistent vertex"}; + LOG_FATAL( + "Query modules mustn't have access to nonexistent objects when checking the existence of a label on " + "a vertex!"); case storage::Error::PROPERTIES_DISABLED: case storage::Error::VERTEX_HAS_EDGES: case storage::Error::SERIALIZATION_ERROR: - MG_ASSERT(false, "Unexpected error when checking the existence of a label on a vertex."); + LOG_FATAL("Unexpected error when checking the existence of a label on a vertex."); } } - return *maybe_has_label; + return *maybe_has_label ? 1 : 0; }, result); } -mgp_error mgp_vertex_has_label(const mgp_vertex *v, mgp_label label, int *result) { +mgp_error mgp_vertex_has_label(mgp_vertex *v, mgp_label label, int *result) { return mgp_vertex_has_label_named(v, label.name, result); } -mgp_error mgp_vertex_get_property(const mgp_vertex *v, const char *name, mgp_memory *memory, mgp_value **result) { +mgp_error mgp_vertex_get_property(mgp_vertex *v, const char *name, mgp_memory *memory, mgp_value **result) { return WrapExceptions( [v, name, memory]() -> mgp_value * { const auto &key = v->graph->impl->NameToProperty(name); @@ -1119,12 +1281,14 @@ mgp_error mgp_vertex_get_property(const mgp_vertex *v, const char *name, mgp_mem if (maybe_prop.HasError()) { switch (maybe_prop.GetError()) { case storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot get a property of a deleted vertex!"}; case storage::Error::NONEXISTENT_OBJECT: - throw NonexistentObjectException{"Cannot get a property of nonexistent vertex"}; + LOG_FATAL( + "Query modules mustn't have access to nonexistent objects when getting a property of a vertex."); case storage::Error::PROPERTIES_DISABLED: case storage::Error::VERTEX_HAS_EDGES: case storage::Error::SERIALIZATION_ERROR: - MG_ASSERT(false, "Unexpected error when getting a property of a vertex."); + LOG_FATAL("Unexpected error when getting a property of a vertex."); } } return NewRawMgpObject<mgp_value>(memory, std::move(*maybe_prop)); @@ -1132,7 +1296,7 @@ mgp_error mgp_vertex_get_property(const mgp_vertex *v, const char *name, mgp_mem result); } -mgp_error mgp_vertex_iter_properties(const mgp_vertex *v, mgp_memory *memory, mgp_properties_iterator **result) { +mgp_error mgp_vertex_iter_properties(mgp_vertex *v, mgp_memory *memory, mgp_properties_iterator **result) { // NOTE: This copies the whole properties into the iterator. // TODO: Think of a good way to avoid the copy which doesn't just rely on some // assumption that storage may return a pointer to the property store. This @@ -1143,12 +1307,14 @@ mgp_error mgp_vertex_iter_properties(const mgp_vertex *v, mgp_memory *memory, mg if (maybe_props.HasError()) { switch (maybe_props.GetError()) { case storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot get the properties of a deleted vertex!"}; case storage::Error::NONEXISTENT_OBJECT: - throw NonexistentObjectException{"Cannot get the properties of nonexistent vertex"}; + LOG_FATAL( + "Query modules mustn't have access to nonexistent objects when getting the properties of a vertex."); case storage::Error::PROPERTIES_DISABLED: case storage::Error::VERTEX_HAS_EDGES: case storage::Error::SERIALIZATION_ERROR: - MG_ASSERT(false, "Unexpected error when getting the properties of a vertex."); + LOG_FATAL("Unexpected error when getting the properties of a vertex."); } } return NewRawMgpObject<mgp_properties_iterator>(memory, v->graph, std::move(*maybe_props)); @@ -1158,7 +1324,7 @@ mgp_error mgp_vertex_iter_properties(const mgp_vertex *v, mgp_memory *memory, mg void mgp_edges_iterator_destroy(mgp_edges_iterator *it) { DeleteRawMgpObject(it); } -mgp_error mgp_vertex_iter_in_edges(const mgp_vertex *v, mgp_memory *memory, mgp_edges_iterator **result) { +mgp_error mgp_vertex_iter_in_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges_iterator **result) { return WrapExceptions( [v, memory] { auto it = NewMgpObject<mgp_edges_iterator>(memory, *v); @@ -1168,12 +1334,15 @@ mgp_error mgp_vertex_iter_in_edges(const mgp_vertex *v, mgp_memory *memory, mgp_ if (maybe_edges.HasError()) { switch (maybe_edges.GetError()) { case storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot get the inbound edges of a deleted vertex!"}; case storage::Error::NONEXISTENT_OBJECT: - throw NonexistentObjectException{"Cannot get the inbound edges of nonexistent vertex"}; + LOG_FATAL( + "Query modules mustn't have access to nonexistent objects when getting the inbound edges of a " + "vertex."); case storage::Error::PROPERTIES_DISABLED: case storage::Error::VERTEX_HAS_EDGES: case storage::Error::SERIALIZATION_ERROR: - MG_ASSERT(false, "Unexpected error when getting the inbound edges of a vertex."); + LOG_FATAL("Unexpected error when getting the inbound edges of a vertex."); } } it->in.emplace(std::move(*maybe_edges)); @@ -1187,7 +1356,7 @@ mgp_error mgp_vertex_iter_in_edges(const mgp_vertex *v, mgp_memory *memory, mgp_ result); } -mgp_error mgp_vertex_iter_out_edges(const mgp_vertex *v, mgp_memory *memory, mgp_edges_iterator **result) { +mgp_error mgp_vertex_iter_out_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges_iterator **result) { return WrapExceptions( [v, memory] { auto it = NewMgpObject<mgp_edges_iterator>(memory, *v); @@ -1197,12 +1366,15 @@ mgp_error mgp_vertex_iter_out_edges(const mgp_vertex *v, mgp_memory *memory, mgp if (maybe_edges.HasError()) { switch (maybe_edges.GetError()) { case storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot get the outbound edges of a deleted vertex!"}; case storage::Error::NONEXISTENT_OBJECT: - throw NonexistentObjectException{"Cannot get the outbound edges of nonexistent vertex"}; + LOG_FATAL( + "Query modules mustn't have access to nonexistent objects when getting the outbound edges of a " + "vertex."); case storage::Error::PROPERTIES_DISABLED: case storage::Error::VERTEX_HAS_EDGES: case storage::Error::SERIALIZATION_ERROR: - MG_ASSERT(false, "Unexpected error when getting the outbound edges of a vertex."); + LOG_FATAL("Unexpected error when getting the outbound edges of a vertex."); } } it->out.emplace(std::move(*maybe_edges)); @@ -1216,9 +1388,13 @@ mgp_error mgp_vertex_iter_out_edges(const mgp_vertex *v, mgp_memory *memory, mgp result); } -mgp_error mgp_edges_iterator_get(const mgp_edges_iterator *it, const mgp_edge **result) { +mgp_error mgp_edges_iterator_underlying_graph_is_mutable(mgp_edges_iterator *it, int *result) { + return mgp_vertex_underlying_graph_is_mutable(&it->source_vertex, result); +} + +mgp_error mgp_edges_iterator_get(mgp_edges_iterator *it, mgp_edge **result) { return WrapExceptions( - [it]() -> const mgp_edge * { + [it]() -> mgp_edge * { if (it->current_e.has_value()) { return &*it->current_e; } @@ -1227,11 +1403,11 @@ mgp_error mgp_edges_iterator_get(const mgp_edges_iterator *it, const mgp_edge ** result); } -mgp_error mgp_edges_iterator_next(mgp_edges_iterator *it, const mgp_edge **result) { +mgp_error mgp_edges_iterator_next(mgp_edges_iterator *it, mgp_edge **result) { return WrapExceptions( [it] { MG_ASSERT(it->in || it->out); - auto next = [&](auto *impl_it, const auto &end) -> const mgp_edge * { + auto next = [&](auto *impl_it, const auto &end) -> mgp_edge * { if (*impl_it == end) { MG_ASSERT(!it->current_e, "Iteration is already done, so it->current_e " @@ -1253,24 +1429,28 @@ mgp_error mgp_edges_iterator_next(mgp_edges_iterator *it, const mgp_edge **resul result); } -mgp_error mgp_edge_get_id(const mgp_edge *e, mgp_edge_id *result) { +mgp_error mgp_edge_get_id(mgp_edge *e, mgp_edge_id *result) { return WrapExceptions([e] { return mgp_edge_id{.as_int = e->impl.Gid().AsInt()}; }, result); } -mgp_error mgp_edge_copy(const mgp_edge *e, mgp_memory *memory, mgp_edge **result) { - return WrapExceptions([e, memory] { return NewRawMgpObject<mgp_edge>(memory, e->impl, e->from.graph); }, result); +mgp_error mgp_edge_underlying_graph_is_mutable(mgp_edge *e, int *result) { + return mgp_vertex_underlying_graph_is_mutable(&e->from, result); +} + +mgp_error mgp_edge_copy(mgp_edge *e, mgp_memory *memory, mgp_edge **result) { + return WrapExceptions([e, memory] { return mgp_edge::Copy(*e, *memory); }, result); } void mgp_edge_destroy(mgp_edge *e) { DeleteRawMgpObject(e); } -mgp_error mgp_edge_equal(const struct mgp_edge *e1, const struct mgp_edge *e2, int *result) { +mgp_error mgp_edge_equal(mgp_edge *e1, mgp_edge *e2, int *result) { // NOLINTNEXTLINE(clang-diagnostic-unevaluated-expression) static_assert(noexcept(*result = *e1 == *e2 ? 1 : 0)); *result = *e1 == *e2 ? 1 : 0; return MGP_ERROR_NO_ERROR; } -mgp_error mgp_edge_get_type(const mgp_edge *e, mgp_edge_type *result) { +mgp_error mgp_edge_get_type(mgp_edge *e, mgp_edge_type *result) { return WrapExceptions( [e] { const auto &name = e->from.graph->impl->EdgeTypeToName(e->impl.EdgeType()); @@ -1282,17 +1462,17 @@ mgp_error mgp_edge_get_type(const mgp_edge *e, mgp_edge_type *result) { &result->name); } -mgp_error mgp_edge_get_from(const mgp_edge *e, const mgp_vertex **result) { +mgp_error mgp_edge_get_from(mgp_edge *e, mgp_vertex **result) { *result = &e->from; return MGP_ERROR_NO_ERROR; } -mgp_error mgp_edge_get_to(const mgp_edge *e, const mgp_vertex **result) { +mgp_error mgp_edge_get_to(mgp_edge *e, mgp_vertex **result) { *result = &e->to; return MGP_ERROR_NO_ERROR; } -mgp_error mgp_edge_get_property(const mgp_edge *e, const char *name, mgp_memory *memory, mgp_value **result) { +mgp_error mgp_edge_get_property(mgp_edge *e, const char *name, mgp_memory *memory, mgp_value **result) { return WrapExceptions( [e, name, memory] { const auto &key = e->from.graph->impl->NameToProperty(name); @@ -1301,12 +1481,13 @@ mgp_error mgp_edge_get_property(const mgp_edge *e, const char *name, mgp_memory if (maybe_prop.HasError()) { switch (maybe_prop.GetError()) { case storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot get a property of a deleted edge!"}; case storage::Error::NONEXISTENT_OBJECT: - throw NonexistentObjectException{"Cannot get a property of nonexistent edge"}; + LOG_FATAL("Query modules mustn't have access to nonexistent objects when getting a property of an edge."); case storage::Error::PROPERTIES_DISABLED: case storage::Error::VERTEX_HAS_EDGES: case storage::Error::SERIALIZATION_ERROR: - MG_ASSERT(false, "Unexpected error when getting a property of an edge."); + LOG_FATAL("Unexpected error when getting a property of an edge."); } } return NewRawMgpObject<mgp_value>(memory, std::move(*maybe_prop)); @@ -1314,7 +1495,32 @@ mgp_error mgp_edge_get_property(const mgp_edge *e, const char *name, mgp_memory result); } -mgp_error mgp_edge_iter_properties(const mgp_edge *e, mgp_memory *memory, mgp_properties_iterator **result) { +mgp_error mgp_edge_set_property(struct mgp_edge *e, const char *property_name, mgp_value *property_value) { + return WrapExceptions([=] { + if (!MgpEdgeIsMutable(*e)) { + throw ImmutableObjectException{"Cannot set a property on an immutable edge!"}; + } + const auto result = + e->impl.SetProperty(e->from.graph->impl->NameToProperty(property_name), ToPropertyValue(*property_value)); + + if (result.HasError()) { + switch (result.GetError()) { + case storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot set the properties of a deleted edge!"}; + case storage::Error::NONEXISTENT_OBJECT: + LOG_FATAL("Query modules mustn't have access to nonexistent objects when setting a property of an edge!"); + case storage::Error::PROPERTIES_DISABLED: + throw std::logic_error{"Cannot set the properties of edges, because properties on edges are disabled!"}; + case storage::Error::VERTEX_HAS_EDGES: + LOG_FATAL("Unexpected error when setting a property of an edge."); + case storage::Error::SERIALIZATION_ERROR: + throw SerializationException{"Cannot serialize setting a property of an edge."}; + } + } + }); +} + +mgp_error mgp_edge_iter_properties(mgp_edge *e, mgp_memory *memory, mgp_properties_iterator **result) { // NOTE: This copies the whole properties into iterator. // TODO: Think of a good way to avoid the copy which doesn't just rely on some // assumption that storage may return a pointer to the property store. This @@ -1326,12 +1532,14 @@ mgp_error mgp_edge_iter_properties(const mgp_edge *e, mgp_memory *memory, mgp_pr if (maybe_props.HasError()) { switch (maybe_props.GetError()) { case storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot get the properties of a deleted edge!"}; case storage::Error::NONEXISTENT_OBJECT: - throw NonexistentObjectException{"Cannot get the properties of nonexistent edge"}; + LOG_FATAL( + "Query modules mustn't have access to nonexistent objects when getting the properties of an edge."); case storage::Error::PROPERTIES_DISABLED: case storage::Error::VERTEX_HAS_EDGES: case storage::Error::SERIALIZATION_ERROR: - MG_ASSERT(false, "Unexpected error when getting the properties of an edge."); + LOG_FATAL("Unexpected error when getting the properties of an edge."); } } return NewRawMgpObject<mgp_properties_iterator>(memory, e->from.graph, std::move(*maybe_props)); @@ -1339,8 +1547,7 @@ mgp_error mgp_edge_iter_properties(const mgp_edge *e, mgp_memory *memory, mgp_pr result); } -mgp_error mgp_graph_get_vertex_by_id(const mgp_graph *graph, mgp_vertex_id id, mgp_memory *memory, - mgp_vertex **result) { +mgp_error mgp_graph_get_vertex_by_id(mgp_graph *graph, mgp_vertex_id id, mgp_memory *memory, mgp_vertex **result) { return WrapExceptions( [graph, id, memory]() -> mgp_vertex * { auto maybe_vertex = graph->impl->FindVertex(storage::Gid::FromInt(id.as_int), graph->view); @@ -1352,15 +1559,109 @@ mgp_error mgp_graph_get_vertex_by_id(const mgp_graph *graph, mgp_vertex_id id, m result); } +mgp_error mgp_graph_is_mutable(mgp_graph *graph, int *result) { + *result = MgpGraphIsMutable(*graph) ? 1 : 0; + return MGP_ERROR_NO_ERROR; +}; + +mgp_error mgp_graph_create_vertex(struct mgp_graph *graph, mgp_memory *memory, mgp_vertex **result) { + return WrapExceptions( + [=] { + if (!MgpGraphIsMutable(*graph)) { + throw ImmutableObjectException{"Cannot create a vertex in an immutable graph!"}; + } + + auto vertex = graph->impl->InsertVertex(); + return NewRawMgpObject<mgp_vertex>(memory, vertex, graph); + }, + result); +} + +mgp_error mgp_graph_remove_vertex(struct mgp_graph *graph, mgp_vertex *vertex) { + return WrapExceptions([=] { + if (!MgpGraphIsMutable(*graph)) { + throw ImmutableObjectException{"Cannot remove a vertex from an immutable graph!"}; + } + const auto result = graph->impl->RemoveVertex(&vertex->impl); + + if (result.HasError()) { + switch (result.GetError()) { + case storage::Error::NONEXISTENT_OBJECT: + LOG_FATAL("Query modules mustn't have access to nonexistent objects when removing a vertex!"); + case storage::Error::DELETED_OBJECT: + case storage::Error::PROPERTIES_DISABLED: + LOG_FATAL("Unexpected error when removing a vertex."); + case storage::Error::VERTEX_HAS_EDGES: + throw std::logic_error{"Cannot remove a vertex that has edges!"}; + case storage::Error::SERIALIZATION_ERROR: + throw SerializationException{"Cannot serialize removing a vertex."}; + } + } + }); +} + +mgp_error mgp_graph_create_edge(mgp_graph *graph, mgp_vertex *from, mgp_vertex *to, mgp_edge_type type, + mgp_memory *memory, mgp_edge **result) { + return WrapExceptions( + [=] { + if (!MgpGraphIsMutable(*graph)) { + throw ImmutableObjectException{"Cannot create an edge in an immutable graph!"}; + } + + auto edge = graph->impl->InsertEdge(&from->impl, &to->impl, from->graph->impl->NameToEdgeType(type.name)); + if (edge.HasError()) { + switch (edge.GetError()) { + case storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot add an edge to a deleted vertex!"}; + case storage::Error::NONEXISTENT_OBJECT: + LOG_FATAL("Query modules mustn't have access to nonexistent objects when creating an edge!"); + case storage::Error::PROPERTIES_DISABLED: + case storage::Error::VERTEX_HAS_EDGES: + LOG_FATAL("Unexpected error when creating an edge."); + case storage::Error::SERIALIZATION_ERROR: + throw SerializationException{"Cannot serialize creating an edge."}; + } + } + return NewRawMgpObject<mgp_edge>(memory, edge.GetValue(), from->graph); + }, + result); +} + +mgp_error mgp_graph_remove_edge(struct mgp_graph *graph, mgp_edge *edge) { + return WrapExceptions([=] { + if (!MgpGraphIsMutable(*graph)) { + throw ImmutableObjectException{"Cannot remove an edge from an immutable graph!"}; + } + const auto result = graph->impl->RemoveEdge(&edge->impl); + + if (result.HasError()) { + switch (result.GetError()) { + case storage::Error::NONEXISTENT_OBJECT: + LOG_FATAL("Query modules mustn't have access to nonexistent objects when removing an edge!"); + case storage::Error::DELETED_OBJECT: + case storage::Error::PROPERTIES_DISABLED: + case storage::Error::VERTEX_HAS_EDGES: + LOG_FATAL("Unexpected error when removing an edge."); + case storage::Error::SERIALIZATION_ERROR: + throw SerializationException{"Cannot serialize removing an edge."}; + } + } + }); +} + void mgp_vertices_iterator_destroy(mgp_vertices_iterator *it) { DeleteRawMgpObject(it); } -mgp_error mgp_graph_iter_vertices(const mgp_graph *graph, mgp_memory *memory, mgp_vertices_iterator **result) { +mgp_error mgp_graph_iter_vertices(mgp_graph *graph, mgp_memory *memory, mgp_vertices_iterator **result) { return WrapExceptions([graph, memory] { return NewRawMgpObject<mgp_vertices_iterator>(memory, graph); }, result); } -mgp_error mgp_vertices_iterator_get(const mgp_vertices_iterator *it, const mgp_vertex **result) { +mgp_error mgp_vertices_iterator_underlying_graph_is_mutable(mgp_vertices_iterator *it, int *result) { + return mgp_graph_is_mutable(it->graph, result); +} + +mgp_error mgp_vertices_iterator_get(mgp_vertices_iterator *it, mgp_vertex **result) { return WrapExceptions( - [it]() -> const mgp_vertex * { + [it]() -> mgp_vertex * { if (it->current_v.has_value()) { return &*it->current_v; } @@ -1369,9 +1670,9 @@ mgp_error mgp_vertices_iterator_get(const mgp_vertices_iterator *it, const mgp_v result); } -mgp_error mgp_vertices_iterator_next(mgp_vertices_iterator *it, const mgp_vertex **result) { +mgp_error mgp_vertices_iterator_next(mgp_vertices_iterator *it, mgp_vertex **result) { return WrapExceptions( - [it]() -> const mgp_vertex * { + [it]() -> mgp_vertex * { if (it->current_it == it->vertices.end()) { MG_ASSERT(!it->current_v, "Iteration is already done, so it->current_v " @@ -1401,7 +1702,7 @@ void NoOpCypherTypeDeleter(CypherType * /*type*/) {} // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define DEFINE_MGP_TYPE_GETTER(cypher_type_name, mgp_type_name) \ - mgp_error mgp_type_##mgp_type_name(const mgp_type **result) { \ + mgp_error mgp_type_##mgp_type_name(mgp_type **result) { \ return WrapExceptions( \ [] { \ static cypher_type_name##Type impl; \ @@ -1422,11 +1723,11 @@ DEFINE_MGP_TYPE_GETTER(Node, node); DEFINE_MGP_TYPE_GETTER(Relationship, relationship); DEFINE_MGP_TYPE_GETTER(Path, path); -mgp_error mgp_type_list(const mgp_type *type, const mgp_type **result) { +mgp_error mgp_type_list(mgp_type *type, mgp_type **result) { return WrapExceptions( [type] { // Maps `type` to corresponding instance of ListType. - static utils::pmr::map<const mgp_type *, mgp_type> gListTypes(utils::NewDeleteResource()); + static utils::pmr::map<mgp_type *, mgp_type> gListTypes(utils::NewDeleteResource()); static utils::SpinLock lock; std::lock_guard<utils::SpinLock> guard(lock); auto found_it = gListTypes.find(type); @@ -1444,11 +1745,11 @@ mgp_error mgp_type_list(const mgp_type *type, const mgp_type **result) { result); } -mgp_error mgp_type_nullable(const mgp_type *type, const mgp_type **result) { +mgp_error mgp_type_nullable(mgp_type *type, mgp_type **result) { return WrapExceptions( [type] { // Maps `type` to corresponding instance of NullableType. - static utils::pmr::map<const mgp_type *, mgp_type> gNullableTypes(utils::NewDeleteResource()); + static utils::pmr::map<mgp_type *, mgp_type> gNullableTypes(utils::NewDeleteResource()); static utils::SpinLock lock; std::lock_guard<utils::SpinLock> guard(lock); auto found_it = gNullableTypes.find(type); @@ -1462,24 +1763,30 @@ mgp_error mgp_type_nullable(const mgp_type *type, const mgp_type **result) { result); } -mgp_error mgp_module_add_read_procedure(mgp_module *module, const char *name, mgp_proc_cb cb, mgp_proc **result) { - return WrapExceptions( - [module, name, cb]() -> mgp_proc * { - if (!IsValidIdentifierName(name)) { - throw std::invalid_argument{fmt::format("Invalid procedure name: {}", name)}; - } - if (module->procedures.find(name) != module->procedures.end()) { - throw std::logic_error{fmt::format("Procedure already exists with name '{}'", name)}; - }; +namespace { +mgp_proc *mgp_module_add_procedure(mgp_module *module, const char *name, mgp_proc_cb cb, bool is_write_procedure) { + if (!IsValidIdentifierName(name)) { + throw std::invalid_argument{fmt::format("Invalid procedure name: {}", name)}; + } + if (module->procedures.find(name) != module->procedures.end()) { + throw std::logic_error{fmt::format("Procedure already exists with name '{}'", name)}; + }; - auto *memory = module->procedures.get_allocator().GetMemoryResource(); - // May throw std::bad_alloc, std::length_error - return &module->procedures.emplace(name, mgp_proc(name, cb, memory)).first->second; - }, - result); + auto *memory = module->procedures.get_allocator().GetMemoryResource(); + // May throw std::bad_alloc, std::length_error + return &module->procedures.emplace(name, mgp_proc(name, cb, memory, is_write_procedure)).first->second; +} +} // namespace + +mgp_error mgp_module_add_read_procedure(mgp_module *module, const char *name, mgp_proc_cb cb, mgp_proc **result) { + return WrapExceptions([=] { return mgp_module_add_procedure(module, name, cb, false); }, result); } -mgp_error mgp_proc_add_arg(mgp_proc *proc, const char *name, const mgp_type *type) { +mgp_error mgp_module_add_write_procedure(mgp_module *module, const char *name, mgp_proc_cb cb, mgp_proc **result) { + return WrapExceptions([=] { return mgp_module_add_procedure(module, name, cb, true); }, result); +} + +mgp_error mgp_proc_add_arg(mgp_proc *proc, const char *name, mgp_type *type) { return WrapExceptions([=] { if (!IsValidIdentifierName(name)) { throw std::invalid_argument{fmt::format("Invalid argument name for procedure '{}': {}", proc->name, name)}; @@ -1492,7 +1799,7 @@ mgp_error mgp_proc_add_arg(mgp_proc *proc, const char *name, const mgp_type *typ }); } -mgp_error mgp_proc_add_opt_arg(mgp_proc *proc, const char *name, const mgp_type *type, const mgp_value *default_value) { +mgp_error mgp_proc_add_opt_arg(mgp_proc *proc, const char *name, mgp_type *type, mgp_value *default_value) { return WrapExceptions([=] { if (!IsValidIdentifierName(name)) { throw std::invalid_argument{fmt::format("Invalid argument name for procedure '{}': {}", proc->name, name)}; @@ -1531,7 +1838,7 @@ template <typename T> concept ModuleProperties = utils::SameAsAnyOf<T, mgp_proc, mgp_trans>; template <ModuleProperties T> -mgp_error AddResultToProp(T *prop, const char *name, const mgp_type *type, bool is_deprecated) noexcept { +mgp_error AddResultToProp(T *prop, const char *name, mgp_type *type, bool is_deprecated) noexcept { return WrapExceptions([=] { if (!IsValidIdentifierName(name)) { throw std::invalid_argument{fmt::format("Invalid result name for procedure '{}': {}", prop->name, name)}; @@ -1546,24 +1853,24 @@ mgp_error AddResultToProp(T *prop, const char *name, const mgp_type *type, bool } // namespace -mgp_error mgp_proc_add_result(mgp_proc *proc, const char *name, const mgp_type *type) { +mgp_error mgp_proc_add_result(mgp_proc *proc, const char *name, mgp_type *type) { return AddResultToProp(proc, name, type, false); } mgp_error MgpTransAddFixedResult(mgp_trans *trans) noexcept { - if (const auto err = AddResultToProp(trans, "query", Call<const mgp_type *>(mgp_type_string), false); + if (const auto err = AddResultToProp(trans, "query", Call<mgp_type *>(mgp_type_string), false); err != MGP_ERROR_NO_ERROR) { return err; } - return AddResultToProp(trans, "parameters", - Call<const mgp_type *>(mgp_type_nullable, Call<const mgp_type *>(mgp_type_map)), false); + return AddResultToProp(trans, "parameters", Call<mgp_type *>(mgp_type_nullable, Call<mgp_type *>(mgp_type_map)), + false); } -mgp_error mgp_proc_add_deprecated_result(mgp_proc *proc, const char *name, const mgp_type *type) { +mgp_error mgp_proc_add_deprecated_result(mgp_proc *proc, const char *name, mgp_type *type) { return AddResultToProp(proc, name, type, true); } -int mgp_must_abort(const mgp_graph *graph) { +int mgp_must_abort(mgp_graph *graph) { MG_ASSERT(graph->ctx); static_assert(noexcept(query::MustAbort(*graph->ctx))); return query::MustAbort(*graph->ctx) ? 1 : 0; @@ -1638,37 +1945,37 @@ bool IsValidIdentifierName(const char *name) { } // namespace query::procedure -mgp_error mgp_message_payload(const mgp_message *message, const char **result) { +mgp_error mgp_message_payload(mgp_message *message, const char **result) { return WrapExceptions([message] { return message->msg->Payload().data(); }, result); } -mgp_error mgp_message_payload_size(const mgp_message *message, size_t *result) { +mgp_error mgp_message_payload_size(mgp_message *message, size_t *result) { return WrapExceptions([message] { return message->msg->Payload().size(); }, result); } -mgp_error mgp_message_topic_name(const mgp_message *message, const char **result) { +mgp_error mgp_message_topic_name(mgp_message *message, const char **result) { return WrapExceptions([message] { return message->msg->TopicName().data(); }, result); } -mgp_error mgp_message_key(const mgp_message *message, const char **result) { +mgp_error mgp_message_key(mgp_message *message, const char **result) { return WrapExceptions([message] { return message->msg->Key().data(); }, result); } -mgp_error mgp_message_key_size(const struct mgp_message *message, size_t *result) { +mgp_error mgp_message_key_size(mgp_message *message, size_t *result) { return WrapExceptions([message] { return message->msg->Key().size(); }, result); } -mgp_error mgp_message_timestamp(const mgp_message *message, int64_t *result) { +mgp_error mgp_message_timestamp(mgp_message *message, int64_t *result) { return WrapExceptions([message] { return message->msg->Timestamp(); }, result); } -mgp_error mgp_messages_size(const mgp_messages *messages, size_t *result) { +mgp_error mgp_messages_size(mgp_messages *messages, size_t *result) { static_assert(noexcept(messages->messages.size())); *result = messages->messages.size(); return MGP_ERROR_NO_ERROR; } -mgp_error mgp_messages_at(const mgp_messages *messages, size_t index, const mgp_message **result) { +mgp_error mgp_messages_at(mgp_messages *messages, size_t index, mgp_message **result) { return WrapExceptions( [messages, index] { if (index >= Call<size_t>(mgp_messages_size, messages)) { diff --git a/src/query/procedure/mg_procedure_impl.hpp b/src/query/procedure/mg_procedure_impl.hpp index 8ea1f7fbd..1dc436ef1 100644 --- a/src/query/procedure/mg_procedure_impl.hpp +++ b/src/query/procedure/mg_procedure_impl.hpp @@ -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; }; diff --git a/src/query/procedure/module.cpp b/src/query/procedure/module.cpp index d692157ae..a9777ceee 100644 --- a/src/query/procedure/module.cpp +++ b/src/query/procedure/module.cpp @@ -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)); } diff --git a/src/query/procedure/py_module.cpp b/src/query/procedure/py_module.cpp index 0562e0083..9d71f5130 100644 --- a/src/query/procedure/py_module.cpp +++ b/src/query/procedure/py_module.cpp @@ -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); } diff --git a/src/query/procedure/py_module.hpp b/src/query/procedure/py_module.hpp index 29ed38e20..8d32c95ab 100644 --- a/src/query/procedure/py_module.hpp +++ b/src/query/procedure/py_module.hpp @@ -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. /// diff --git a/src/utils/result.hpp b/src/utils/result.hpp index d93bd91d0..1c008cd50 100644 --- a/src/utils/result.hpp +++ b/src/utils/result.hpp @@ -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)) {} diff --git a/tests/e2e/memory/procedures/global_memory_limit_proc.c b/tests/e2e/memory/procedures/global_memory_limit_proc.c index 95f7318f3..f54202ead 100644 --- a/tests/e2e/memory/procedures/global_memory_limit_proc.c +++ b/tests/e2e/memory/procedures/global_memory_limit_proc.c @@ -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; diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 610b43ee2..94bebd8d3 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -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) diff --git a/tests/unit/mgp_kafka_c_api.cpp b/tests/unit/mgp_kafka_c_api.cpp index 8e1396075..ee4e0467c 100644 --- a/tests/unit/mgp_kafka_c_api.cpp +++ b/tests/unit/mgp_kafka_c_api.cpp @@ -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); diff --git a/tests/unit/mgp_trans_c_api.cpp b/tests/unit/mgp_trans_c_api.cpp index 150dfcc4e..95085b2e7 100644 --- a/tests/unit/mgp_trans_c_api.cpp +++ b/tests/unit/mgp_trans_c_api.cpp @@ -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 diff --git a/tests/unit/query_procedure_mgp_module.cpp b/tests/unit/query_procedure_mgp_module.cpp index f01596bf4..312241a8f 100644 --- a/tests/unit/query_procedure_mgp_module.cpp +++ b/tests/unit/query_procedure_mgp_module.cpp @@ -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); +} diff --git a/tests/unit/query_procedure_mgp_type.cpp b/tests/unit/query_procedure_mgp_type.cpp index 3c1a46120..493b169d4 100644 --- a/tests/unit/query_procedure_mgp_type.cpp +++ b/tests/unit/query_procedure_mgp_type.cpp @@ -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)); } diff --git a/tests/unit/query_procedure_py_module.cpp b/tests/unit/query_procedure_py_module.cpp index 9602c2972..aee93adb8 100644 --- a/tests/unit/query_procedure_py_module.cpp +++ b/tests/unit/query_procedure_py_module.cpp @@ -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); diff --git a/tests/unit/query_procedures_mgp_graph.cpp b/tests/unit/query_procedures_mgp_graph.cpp new file mode 100644 index 000000000..0462a76bb --- /dev/null +++ b/tests/unit/query_procedures_mgp_graph.cpp @@ -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); +} diff --git a/tests/unit/storage_test_utils.cpp b/tests/unit/storage_test_utils.cpp new file mode 100644 index 000000000..4f7f656f5 --- /dev/null +++ b/tests/unit/storage_test_utils.cpp @@ -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; +} \ No newline at end of file diff --git a/tests/unit/storage_test_utils.hpp b/tests/unit/storage_test_utils.hpp new file mode 100644 index 000000000..90aee2afa --- /dev/null +++ b/tests/unit/storage_test_utils.hpp @@ -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); \ No newline at end of file diff --git a/tests/unit/storage_v2.cpp b/tests/unit/storage_v2.cpp index db9082bd7..ba6af920f 100644 --- a/tests/unit/storage_v2.cpp +++ b/tests/unit/storage_v2.cpp @@ -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(); } } diff --git a/tests/unit/test_utils.hpp b/tests/unit/test_utils.hpp index 36591d97e..671ca2aec 100644 --- a/tests/unit/test_utils.hpp +++ b/tests/unit/test_utils.hpp @@ -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