Compare commits

..

1 Commits

Author SHA1 Message Date
Marko Budiselić
495251a8cd Update pull_request_template.md 2023-11-20 16:25:55 +01:00
14 changed files with 43 additions and 1409 deletions

View File

@@ -1,13 +1,9 @@
[master < Epic] PR
- [ ] Check, and update documentation if necessary
PR checklist:
- [ ] Check and update documentation if necessary
- [ ] Write E2E tests
- [ ] Compare the [benchmarking results](https://bench-graph.memgraph.com/) between the master branch and the Epic branch
- [ ] Provide the full content or a guide for the final git message
[master < Task] PR
- [ ] Check, and update documentation if necessary
- [ ] Provide the full content or a guide for the final git message
- [ ] Notify everyone downstream (platform, dx, infra, ...) if there is any breaking change and what to do about it
To keep docs changelog up to date, one more thing to do:
- [ ] Write a release note here, including added/changed clauses

View File

@@ -236,54 +236,6 @@ inline mgp_type *type_nullable(mgp_type *type) { return MgInvoke<mgp_type *>(mgp
// mgp_graph
inline bool create_label_index(mgp_graph *graph, const char *label) {
return MgInvoke<int>(mgp_create_label_index, graph, label);
}
inline bool drop_label_index(mgp_graph *graph, const char *label) {
return MgInvoke<int>(mgp_drop_label_index, graph, label);
}
inline mgp_list *list_all_label_indices(mgp_graph *graph, mgp_memory *memory) {
return MgInvoke<mgp_list *>(mgp_list_all_label_indices, graph, memory);
}
inline bool create_label_property_index(mgp_graph *graph, const char *label, const char *property) {
return MgInvoke<int>(mgp_create_label_property_index, graph, label, property);
}
inline bool drop_label_property_index(mgp_graph *graph, const char *label, const char *property) {
return MgInvoke<int>(mgp_drop_label_property_index, graph, label, property);
}
inline mgp_list *list_all_label_property_indices(mgp_graph *graph, mgp_memory *memory) {
return MgInvoke<mgp_list *>(mgp_list_all_label_property_indices, graph, memory);
}
inline bool create_existence_constraint(mgp_graph *graph, const char *label, const char *property) {
return MgInvoke<int>(mgp_create_existence_constraint, graph, label, property);
}
inline bool drop_existence_constraint(mgp_graph *graph, const char *label, const char *property) {
return MgInvoke<int>(mgp_drop_existence_constraint, graph, label, property);
}
inline mgp_list *list_all_existence_constraints(mgp_graph *graph, mgp_memory *memory) {
return MgInvoke<mgp_list *>(mgp_list_all_existence_constraints, graph, memory);
}
inline bool create_unique_constraint(mgp_graph *memgraph_graph, const char *label, mgp_value *properties) {
return MgInvoke<int>(mgp_create_unique_constraint, memgraph_graph, label, properties);
}
inline bool drop_unique_constraint(mgp_graph *memgraph_graph, const char *label, mgp_value *properties) {
return MgInvoke<int>(mgp_drop_unique_constraint, memgraph_graph, label, properties);
}
inline mgp_list *list_all_unique_constraints(mgp_graph *graph, mgp_memory *memory) {
return MgInvoke<mgp_list *>(mgp_list_all_unique_constraints, graph, memory);
}
inline bool graph_is_mutable(mgp_graph *graph) { return MgInvoke<int>(mgp_graph_is_mutable, graph); }
inline mgp_vertex *graph_create_vertex(mgp_graph *graph, mgp_memory *memory) {

View File

@@ -876,65 +876,6 @@ enum mgp_error mgp_edge_iter_properties(struct mgp_edge *e, struct mgp_memory *m
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);
/// Creates label index for given label.
/// mgp_error::MGP_ERROR_NO_ERROR is always returned.
/// if label index already exists, result will be 0, otherwise 1.
enum mgp_error mgp_create_label_index(struct mgp_graph *graph, const char *label, int *result);
/// Drop label index.
enum mgp_error mgp_drop_label_index(struct mgp_graph *graph, const char *label, int *result);
/// List all label indices.
enum mgp_error mgp_list_all_label_indices(struct mgp_graph *graph, struct mgp_memory *memory, struct mgp_list **result);
/// Creates label-property index for given label and propery.
/// mgp_error::MGP_ERROR_NO_ERROR is always returned.
/// if label property index already exists, result will be 0, otherwise 1.
enum mgp_error mgp_create_label_property_index(struct mgp_graph *graph, const char *label, const char *property,
int *result);
/// Drops label-property index for given label and propery.
/// mgp_error::MGP_ERROR_NO_ERROR is always returned.
/// if dropping label property index failed, result will be 0, otherwise 1.
enum mgp_error mgp_drop_label_property_index(struct mgp_graph *graph, const char *label, const char *property,
int *result);
/// List all label+property indices.
enum mgp_error mgp_list_all_label_property_indices(struct mgp_graph *graph, struct mgp_memory *memory,
struct mgp_list **result);
/// Creates existence constraint for given label and property.
/// mgp_error::MGP_ERROR_NO_ERROR is always returned.
/// if creating existence constraint failed, result will be 0, otherwise 1.
enum mgp_error mgp_create_existence_constraint(struct mgp_graph *graph, const char *label, const char *property,
int *result);
/// Drops existence constraint for given label and property.
/// mgp_error::MGP_ERROR_NO_ERROR is always returned.
/// if dropping existence constraint failed, result will be 0, otherwise 1.
enum mgp_error mgp_drop_existence_constraint(struct mgp_graph *graph, const char *label, const char *property,
int *result);
/// List all existence constraints.
enum mgp_error mgp_list_all_existence_constraints(struct mgp_graph *graph, struct mgp_memory *memory,
struct mgp_list **result);
/// Creates unique constraint for given label and properties.
/// mgp_error::MGP_ERROR_NO_ERROR is always returned.
/// if creating unique constraint failed, result will be 0, otherwise 1.
enum mgp_error mgp_create_unique_constraint(struct mgp_graph *graph, const char *label, struct mgp_value *properties,
int *result);
/// Drops unique constraint for given label and properties.
/// mgp_error::MGP_ERROR_NO_ERROR is always returned.
/// if dropping unique constraint failed, result will be 0, otherwise 1.
enum mgp_error mgp_drop_unique_constraint(struct mgp_graph *graph, const char *label, struct mgp_value *properties,
int *result);
/// List all unique constraints
enum mgp_error mgp_list_all_unique_constraints(struct mgp_graph *graph, struct mgp_memory *memory,
struct mgp_list **result);
/// Result is non-zero if the graph can be modified.
/// If a graph is immutable, then vertices cannot be created or deleted, and all of the returned vertices will be
/// immutable also. The same applies for edges.

View File

@@ -21,11 +21,13 @@
#include <string>
#include <string_view>
#include <thread>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
#include <functional>
#include <type_traits>
#include <utility>
#include "_mgp.hpp"
#include "mg_exceptions.hpp"
#include "mg_procedure.h"
@@ -1287,7 +1289,7 @@ class Value {
std::string_view ValueString() const;
std::string_view ValueString();
/// @pre Value type needs to be Type::List.
List ValueList() const;
const List ValueList() const;
List ValueList();
/// @pre Value type needs to be Type::Map.
const Map ValueMap() const;
@@ -3649,7 +3651,7 @@ inline std::string_view Value::ValueString() {
return mgp::value_get_string(ptr_);
}
inline List Value::ValueList() const {
inline const List Value::ValueList() const {
if (Type() != Type::List) {
throw ValueException("Type of value is wrong: expected List.");
}
@@ -4277,77 +4279,9 @@ inline void AddParamsReturnsToProc(mgp_proc *proc, std::vector<Parameter> &param
}
} // namespace detail
inline bool CreateLabelIndex(mgp_graph *memgaph_graph, const std::string_view label) {
return create_label_index(memgaph_graph, label.data());
}
inline bool DropLabelIndex(mgp_graph *memgaph_graph, const std::string_view label) {
return drop_label_index(memgaph_graph, label.data());
}
inline List ListAllLabelIndices(mgp_graph *memgraph_graph) {
auto *label_indices = mgp::MemHandlerCallback(list_all_label_indices, memgraph_graph);
if (label_indices == nullptr) {
throw ValueException("Couldn't list all label indices");
}
return List(label_indices);
}
inline bool CreateLabelPropertyIndex(mgp_graph *memgaph_graph, const std::string_view label,
const std::string_view property) {
return create_label_property_index(memgaph_graph, label.data(), property.data());
}
inline bool DropLabelPropertyIndex(mgp_graph *memgaph_graph, const std::string_view label,
const std::string_view property) {
return drop_label_property_index(memgaph_graph, label.data(), property.data());
}
inline List ListAllLabelPropertyIndices(mgp_graph *memgraph_graph) {
auto *label_property_indices = mgp::MemHandlerCallback(list_all_label_property_indices, memgraph_graph);
if (label_property_indices == nullptr) {
throw ValueException("Couldn't list all label+property indices");
}
return List(label_property_indices);
}
inline bool CreateExistenceConstraint(mgp_graph *memgraph_graph, const std::string_view label,
const std::string_view property) {
return create_existence_constraint(memgraph_graph, label.data(), property.data());
}
inline bool DropExistenceConstraint(mgp_graph *memgraph_graph, const std::string_view label,
const std::string_view property) {
return drop_existence_constraint(memgraph_graph, label.data(), property.data());
}
inline List ListAllExistenceConstraints(mgp_graph *memgraph_graph) {
auto *existence_constraints = mgp::MemHandlerCallback(list_all_existence_constraints, memgraph_graph);
if (existence_constraints == nullptr) {
throw ValueException("Couldn't list all existence_constraints");
}
return List(existence_constraints);
}
inline bool CreateUniqueConstraint(mgp_graph *memgraph_graph, const std::string_view label, mgp_value *properties) {
return create_unique_constraint(memgraph_graph, label.data(), properties);
}
inline bool DropUniqueConstraint(mgp_graph *memgraph_graph, const std::string_view label, mgp_value *properties) {
return drop_unique_constraint(memgraph_graph, label.data(), properties);
}
inline List ListAllUniqueConstraints(mgp_graph *memgraph_graph) {
auto *unique_constraints = mgp::MemHandlerCallback(list_all_unique_constraints, memgraph_graph);
if (unique_constraints == nullptr) {
throw ValueException("Couldn't list all unique_constraints");
}
return List(unique_constraints);
}
void AddProcedure(mgp_proc_cb callback, std::string_view name, ProcedureType proc_type,
std::vector<Parameter> parameters, std::vector<Return> returns, mgp_module *module,
mgp_memory * /*memory*/) {
mgp_memory *memory) {
auto *proc = (proc_type == ProcedureType::Read) ? mgp::module_add_read_procedure(module, name.data(), callback)
: mgp::module_add_write_procedure(module, name.data(), callback);
detail::AddParamsReturnsToProc(proc, parameters, returns);
@@ -4355,7 +4289,7 @@ void AddProcedure(mgp_proc_cb callback, std::string_view name, ProcedureType pro
void AddBatchProcedure(mgp_proc_cb callback, mgp_proc_initializer initializer, mgp_proc_cleanup cleanup,
std::string_view name, ProcedureType proc_type, std::vector<Parameter> parameters,
std::vector<Return> returns, mgp_module *module, mgp_memory * /*memory*/) {
std::vector<Return> returns, mgp_module *module, mgp_memory *memory) {
auto *proc = (proc_type == ProcedureType::Read)
? mgp::module_add_batch_read_procedure(module, name.data(), callback, initializer, cleanup)
: mgp::module_add_batch_write_procedure(module, name.data(), callback, initializer, cleanup);

View File

@@ -10,33 +10,18 @@
// licenses/APL.txt.
#include <mgp.hpp>
#include "utils/string.hpp"
#include <optional>
namespace Schema {
constexpr std::string_view kStatusKept = "Kept";
constexpr std::string_view kStatusCreated = "Created";
constexpr std::string_view kStatusDropped = "Dropped";
/*NodeTypeProperties and RelTypeProperties constants*/
constexpr std::string_view kReturnNodeType = "nodeType";
constexpr std::string_view kProcedureNodeType = "node_type_properties";
constexpr std::string_view kProcedureRelType = "rel_type_properties";
constexpr std::string_view kProcedureAssert = "assert";
constexpr std::string_view kReturnLabels = "nodeLabels";
constexpr std::string_view kReturnRelType = "relType";
constexpr std::string_view kReturnPropertyName = "propertyName";
constexpr std::string_view kReturnPropertyType = "propertyTypes";
constexpr std::string_view kReturnMandatory = "mandatory";
constexpr std::string_view kReturnLabel = "label";
constexpr std::string_view kReturnKey = "key";
constexpr std::string_view kReturnKeys = "keys";
constexpr std::string_view kReturnUnique = "unique";
constexpr std::string_view kReturnAction = "action";
constexpr std::string_view kParameterIndices = "indices";
constexpr std::string_view kParameterUniqueConstraints = "unique_constraints";
constexpr std::string_view kParameterExistenceConstraints = "existence_constraints";
constexpr std::string_view kParameterDropExisting = "drop_existing";
std::string TypeOf(const mgp::Type &type);
@@ -50,7 +35,6 @@ void ProcessPropertiesRel(mgp::Record &record, const std::string_view &type, con
void NodeTypeProperties(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory);
void RelTypeProperties(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory);
void Assert(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory);
} // namespace Schema
/*we have << operator for type in Cpp API, but in it we return somewhat different strings than I would like in this
@@ -108,22 +92,21 @@ void Schema::ProcessPropertiesRel(mgp::Record &record, const std::string_view &t
record.Insert(std::string(kReturnMandatory).c_str(), mandatory);
}
void Schema::NodeTypeProperties(mgp_list * /*args*/, mgp_graph *memgraph_graph, mgp_result *result,
mgp_memory *memory) {
void Schema::NodeTypeProperties(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
mgp::MemoryDispatcherGuard guard{memory};
;
const auto record_factory = mgp::RecordFactory(result);
try {
const mgp::Graph graph = mgp::Graph(memgraph_graph);
for (auto node : graph.Nodes()) {
std::string type;
std::string type = "";
mgp::List labels = mgp::List();
for (auto label : node.Labels()) {
labels.AppendExtend(mgp::Value(label));
type += ":`" + std::string(label) + "`";
}
if (node.Properties().empty()) {
if (node.Properties().size() == 0) {
auto record = record_factory.NewRecord();
ProcessPropertiesNode<std::string>(record, type, labels, "", "", false);
continue;
@@ -143,15 +126,16 @@ void Schema::NodeTypeProperties(mgp_list * /*args*/, mgp_graph *memgraph_graph,
}
}
void Schema::RelTypeProperties(mgp_list * /*args*/, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
void Schema::RelTypeProperties(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
mgp::MemoryDispatcherGuard guard{memory};
;
const auto record_factory = mgp::RecordFactory(result);
try {
const mgp::Graph graph = mgp::Graph(memgraph_graph);
for (auto rel : graph.Relationships()) {
std::string type = ":`" + std::string(rel.Type()) + "`";
if (rel.Properties().empty()) {
if (rel.Properties().size() == 0) {
auto record = record_factory.NewRecord();
ProcessPropertiesRel<std::string>(record, type, "", "", false);
continue;
@@ -171,436 +155,29 @@ void Schema::RelTypeProperties(mgp_list * /*args*/, mgp_graph *memgraph_graph, m
}
}
void InsertRecordForLabelIndex(const auto &record_factory, const std::string_view label,
const std::string_view status) {
auto record = record_factory.NewRecord();
record.Insert(std::string(Schema::kReturnLabel).c_str(), label);
record.Insert(std::string(Schema::kReturnKey).c_str(), "");
record.Insert(std::string(Schema::kReturnKeys).c_str(), mgp::List());
record.Insert(std::string(Schema::kReturnUnique).c_str(), false);
record.Insert(std::string(Schema::kReturnAction).c_str(), status);
}
void InsertRecordForUniqueConstraint(const auto &record_factory, const std::string_view label,
const mgp::List &properties, const std::string_view status) {
auto record = record_factory.NewRecord();
record.Insert(std::string(Schema::kReturnLabel).c_str(), label);
record.Insert(std::string(Schema::kReturnKey).c_str(), properties.ToString());
record.Insert(std::string(Schema::kReturnKeys).c_str(), properties);
record.Insert(std::string(Schema::kReturnUnique).c_str(), true);
record.Insert(std::string(Schema::kReturnAction).c_str(), status);
}
void InsertRecordForLabelPropertyIndexAndExistenceConstraint(const auto &record_factory, const std::string_view label,
const std::string_view property,
const std::string_view status) {
auto record = record_factory.NewRecord();
record.Insert(std::string(Schema::kReturnLabel).c_str(), label);
record.Insert(std::string(Schema::kReturnKey).c_str(), property);
record.Insert(std::string(Schema::kReturnKeys).c_str(), mgp::List({mgp::Value(property)}));
record.Insert(std::string(Schema::kReturnUnique).c_str(), false);
record.Insert(std::string(Schema::kReturnAction).c_str(), status);
}
void ProcessCreatingLabelIndex(const std::string_view label, const std::set<std::string_view> &existing_label_indices,
mgp_graph *memgraph_graph, const auto &record_factory) {
if (existing_label_indices.contains(label)) {
InsertRecordForLabelIndex(record_factory, label, Schema::kStatusKept);
} else if (mgp::CreateLabelIndex(memgraph_graph, label)) {
InsertRecordForLabelIndex(record_factory, label, Schema::kStatusCreated);
}
}
template <typename TFunc>
void ProcessCreatingLabelPropertyIndexAndExistenceConstraint(const std::string_view label,
const std::string_view property,
const std::set<std::string_view> &existing_collection,
const TFunc &func_creation, mgp_graph *memgraph_graph,
const auto &record_factory) {
const auto label_property_search_key = std::string(label) + ":" + std::string(property);
if (existing_collection.contains(label_property_search_key)) {
InsertRecordForLabelPropertyIndexAndExistenceConstraint(record_factory, label, property, Schema::kStatusKept);
} else if (func_creation(memgraph_graph, label, property)) {
InsertRecordForLabelPropertyIndexAndExistenceConstraint(record_factory, label, property, Schema::kStatusCreated);
}
}
/// We collect properties for which index was created.
using AssertedIndices = std::set<std::string, std::less<>>;
AssertedIndices CreateIndicesForLabel(const std::string_view label, const mgp::Value &properties_val,
mgp_graph *memgraph_graph, const auto &record_factory,
const std::set<std::string_view> &existing_label_indices,
const std::set<std::string_view> &existing_label_property_indices) {
AssertedIndices asserted_indices;
if (!properties_val.IsList()) {
return {};
}
if (const auto properties = properties_val.ValueList();
properties.Empty() && mgp::CreateLabelIndex(memgraph_graph, label)) {
InsertRecordForLabelIndex(record_factory, label, Schema::kStatusCreated);
asserted_indices.emplace("");
} else {
std::for_each(properties.begin(), properties.end(),
[&label, &existing_label_indices, &existing_label_property_indices, &memgraph_graph, &record_factory,
&asserted_indices](const mgp::Value &property) {
if (!property.IsString()) {
return;
}
const auto property_str = property.ValueString();
if (property_str.empty()) {
ProcessCreatingLabelIndex(label, existing_label_indices, memgraph_graph, record_factory);
asserted_indices.emplace("");
} else {
ProcessCreatingLabelPropertyIndexAndExistenceConstraint(
label, property_str, existing_label_property_indices, mgp::CreateLabelPropertyIndex,
memgraph_graph, record_factory);
asserted_indices.emplace(property_str);
}
});
}
return asserted_indices;
}
void ProcessIndices(const mgp::Map &indices_map, mgp_graph *memgraph_graph, const auto &record_factory,
bool drop_existing) {
auto mgp_existing_label_indices = mgp::ListAllLabelIndices(memgraph_graph);
auto mgp_existing_label_property_indices = mgp::ListAllLabelPropertyIndices(memgraph_graph);
std::set<std::string_view> existing_label_indices;
std::transform(mgp_existing_label_indices.begin(), mgp_existing_label_indices.end(),
std::inserter(existing_label_indices, existing_label_indices.begin()),
[](const mgp::Value &index) { return index.ValueString(); });
std::set<std::string_view> existing_label_property_indices;
std::transform(mgp_existing_label_property_indices.begin(), mgp_existing_label_property_indices.end(),
std::inserter(existing_label_property_indices, existing_label_property_indices.begin()),
[](const mgp::Value &index) { return index.ValueString(); });
std::set<std::string> asserted_label_indices;
std::set<std::string> asserted_label_property_indices;
auto merge_label_property = [](const std::string &label, const std::string &property) {
return label + ":" + property;
};
for (const auto &index : indices_map) {
const std::string_view label = index.key;
const mgp::Value &properties_val = index.value;
AssertedIndices asserted_indices_new = CreateIndicesForLabel(
label, properties_val, memgraph_graph, record_factory, existing_label_indices, existing_label_property_indices);
if (!drop_existing) {
continue;
}
std::ranges::for_each(asserted_indices_new, [&asserted_label_indices, &asserted_label_property_indices, label,
&merge_label_property](const std::string &property) {
if (property.empty()) {
asserted_label_indices.emplace(label);
} else {
asserted_label_property_indices.emplace(merge_label_property(std::string(label), property));
}
});
}
if (!drop_existing) {
return;
}
std::set<std::string_view> label_indices_to_drop;
std::ranges::set_difference(existing_label_indices, asserted_label_indices,
std::inserter(label_indices_to_drop, label_indices_to_drop.begin()));
std::ranges::for_each(label_indices_to_drop, [memgraph_graph, &record_factory](const std::string_view label) {
if (mgp::DropLabelIndex(memgraph_graph, label)) {
InsertRecordForLabelIndex(record_factory, label, Schema::kStatusDropped);
}
});
std::set<std::string_view> label_property_indices_to_drop;
std::ranges::set_difference(existing_label_property_indices, asserted_label_property_indices,
std::inserter(label_property_indices_to_drop, label_property_indices_to_drop.begin()));
auto decouple_label_property = [](const std::string_view label_property) {
const auto label_size = label_property.find(':');
const auto label = std::string(label_property.substr(0, label_size));
const auto property = std::string(label_property.substr(label_size + 1));
return std::make_pair(label, property);
};
std::ranges::for_each(label_property_indices_to_drop, [memgraph_graph, &record_factory, decouple_label_property](
const std::string_view label_property) {
const auto [label, property] = decouple_label_property(label_property);
if (mgp::DropLabelPropertyIndex(memgraph_graph, label, property)) {
InsertRecordForLabelPropertyIndexAndExistenceConstraint(record_factory, label, property, Schema::kStatusDropped);
}
});
}
using ExistenceConstraintsStorage = std::set<std::string_view>;
ExistenceConstraintsStorage CreateExistenceConstraintsForLabel(
const std::string_view label, const mgp::Value &properties_val, mgp_graph *memgraph_graph,
const auto &record_factory, const std::set<std::string_view> &existing_existence_constraints) {
ExistenceConstraintsStorage asserted_existence_constraints;
if (!properties_val.IsList()) {
return asserted_existence_constraints;
}
auto validate_property = [](const mgp::Value &property) -> bool {
return property.IsString() && !property.ValueString().empty();
};
const auto &properties = properties_val.ValueList();
std::for_each(properties.begin(), properties.end(),
[&label, &existing_existence_constraints, &asserted_existence_constraints, &memgraph_graph,
&record_factory, &validate_property](const mgp::Value &property) {
if (!validate_property(property)) {
return;
}
const std::string_view property_str = property.ValueString();
asserted_existence_constraints.emplace(property_str);
ProcessCreatingLabelPropertyIndexAndExistenceConstraint(
label, property_str, existing_existence_constraints, mgp::CreateExistenceConstraint,
memgraph_graph, record_factory);
});
return asserted_existence_constraints;
}
void ProcessExistenceConstraints(const mgp::Map &existence_constraints_map, mgp_graph *memgraph_graph,
const auto &record_factory, bool drop_existing) {
auto mgp_existing_existence_constraints = mgp::ListAllExistenceConstraints(memgraph_graph);
std::set<std::string_view> existing_existence_constraints;
std::transform(mgp_existing_existence_constraints.begin(), mgp_existing_existence_constraints.end(),
std::inserter(existing_existence_constraints, existing_existence_constraints.begin()),
[](const mgp::Value &constraint) { return constraint.ValueString(); });
auto merge_label_property = [](const std::string_view label, const std::string_view property) {
auto str = std::string(label) + ":";
str += property;
return str;
};
ExistenceConstraintsStorage asserted_existence_constraints;
for (const auto &existing_constraint : existence_constraints_map) {
const std::string_view label = existing_constraint.key;
const mgp::Value &properties_val = existing_constraint.value;
auto asserted_existence_constraints_new = CreateExistenceConstraintsForLabel(
label, properties_val, memgraph_graph, record_factory, existing_existence_constraints);
if (!drop_existing) {
continue;
}
std::ranges::for_each(asserted_existence_constraints_new, [&asserted_existence_constraints, &merge_label_property,
label](const std::string_view property) {
asserted_existence_constraints.emplace(merge_label_property(label, property));
});
}
if (!drop_existing) {
return;
}
std::set<std::string_view> existence_constraints_to_drop;
std::ranges::set_difference(existing_existence_constraints, asserted_existence_constraints,
std::inserter(existence_constraints_to_drop, existence_constraints_to_drop.begin()));
auto decouple_label_property = [](const std::string_view label_property) {
const auto label_size = label_property.find(':');
const auto label = std::string(label_property.substr(0, label_size));
const auto property = std::string(label_property.substr(label_size + 1));
return std::make_pair(label, property);
};
std::ranges::for_each(existence_constraints_to_drop, [&](const std::string_view label_property) {
const auto [label, property] = decouple_label_property(label_property);
if (mgp::DropExistenceConstraint(memgraph_graph, label, property)) {
InsertRecordForLabelPropertyIndexAndExistenceConstraint(record_factory, label, property, Schema::kStatusDropped);
}
});
}
using AssertedUniqueConstraintsStorage = std::set<std::set<std::string_view>>;
AssertedUniqueConstraintsStorage CreateUniqueConstraintsForLabel(
const std::string_view label, const mgp::Value &unique_props_nested,
const std::map<std::string_view, AssertedUniqueConstraintsStorage> &existing_unique_constraints,
mgp_graph *memgraph_graph, const auto &record_factory) {
AssertedUniqueConstraintsStorage asserted_unique_constraints;
if (!unique_props_nested.IsList()) {
return asserted_unique_constraints;
}
auto validate_unique_constraint_props = [](const mgp::Value &properties) -> bool {
if (!properties.IsList()) {
return false;
}
const auto &properties_list = properties.ValueList();
if (properties_list.Empty()) {
return false;
}
return std::all_of(properties_list.begin(), properties_list.end(), [](const mgp::Value &property) {
return property.IsString() && !property.ValueString().empty();
});
};
auto unique_constraint_exists =
[](const std::string_view label, const std::set<std::string_view> &properties,
const std::map<std::string_view, AssertedUniqueConstraintsStorage> &existing_unique_constraints) -> bool {
auto iter = existing_unique_constraints.find(label);
if (iter == existing_unique_constraints.end()) {
return false;
}
return iter->second.find(properties) != iter->second.end();
};
for (const auto unique_props_nested_list = unique_props_nested.ValueList();
const auto &properties : unique_props_nested_list) {
if (!validate_unique_constraint_props(properties)) {
continue;
}
const auto properties_list = properties.ValueList();
std::set<std::string_view> properties_coll;
std::transform(properties_list.begin(), properties_list.end(),
std::inserter(properties_coll, properties_coll.begin()),
[](const mgp::Value &property) { return property.ValueString(); });
if (unique_constraint_exists(label, properties_coll, existing_unique_constraints)) {
InsertRecordForUniqueConstraint(record_factory, label, properties_list, Schema::kStatusKept);
} else if (mgp::CreateUniqueConstraint(memgraph_graph, label, properties.ptr())) {
InsertRecordForUniqueConstraint(record_factory, label, properties_list, Schema::kStatusCreated);
}
asserted_unique_constraints.emplace(std::move(properties_coll));
}
return asserted_unique_constraints;
}
void ProcessUniqueConstraints(const mgp::Map &unique_constraints_map, mgp_graph *memgraph_graph,
const auto &record_factory, bool drop_existing) {
auto mgp_existing_unique_constraints = mgp::ListAllUniqueConstraints(memgraph_graph);
// label-unique_constraints pair
std::map<std::string_view, AssertedUniqueConstraintsStorage> existing_unique_constraints;
for (const auto &constraint : mgp_existing_unique_constraints) {
auto constraint_list = constraint.ValueList();
std::set<std::string_view> properties;
for (int i = 1; i < constraint_list.Size(); i++) {
properties.emplace(constraint_list[i].ValueString());
}
const std::string_view label = constraint_list[0].ValueString();
auto [it, inserted] = existing_unique_constraints.try_emplace(label, AssertedUniqueConstraintsStorage{properties});
if (!inserted) {
it->second.emplace(std::move(properties));
}
}
std::map<std::string_view, AssertedUniqueConstraintsStorage> asserted_unique_constraints;
for (const auto &[label, unique_props_nested] : unique_constraints_map) {
auto asserted_unique_constraints_new = CreateUniqueConstraintsForLabel(
label, unique_props_nested, existing_unique_constraints, memgraph_graph, record_factory);
if (drop_existing) {
asserted_unique_constraints.emplace(label, std::move(asserted_unique_constraints_new));
}
}
if (!drop_existing) {
return;
}
std::vector<std::pair<std::string_view, std::set<std::string_view>>> unique_constraints_to_drop;
// Check for each label for we found existing constraint in the DB whether it was asserted.
// If no unique constraint was found with label, we can drop all unique constraints for this label. (if branch)
// If some unique constraint was found with label, we can drop only those unique constraints that were not asserted.
// (else branch.)
std::ranges::for_each(existing_unique_constraints, [&asserted_unique_constraints, &unique_constraints_to_drop](
const auto &existing_label_unique_constraints) {
const auto &label = existing_label_unique_constraints.first;
const auto &existing_unique_constraints_for_label = existing_label_unique_constraints.second;
const auto &asserted_unique_constraints_for_label = asserted_unique_constraints.find(label);
if (asserted_unique_constraints_for_label == asserted_unique_constraints.end()) {
std::ranges::for_each(
std::make_move_iterator(existing_unique_constraints_for_label.begin()),
std::make_move_iterator(existing_unique_constraints_for_label.end()),
[&unique_constraints_to_drop, &label](std::set<std::string_view> existing_unique_constraint_for_label) {
unique_constraints_to_drop.emplace_back(label, std::move(existing_unique_constraint_for_label));
});
} else {
const auto &asserted_unique_constraints_for_label_coll = asserted_unique_constraints_for_label->second;
std::ranges::for_each(
std::make_move_iterator(existing_unique_constraints_for_label.begin()),
std::make_move_iterator(existing_unique_constraints_for_label.end()),
[&unique_constraints_to_drop, &label, &asserted_unique_constraints_for_label_coll](
std::set<std::string_view> existing_unique_constraint_for_label) {
if (!asserted_unique_constraints_for_label_coll.contains(existing_unique_constraint_for_label)) {
unique_constraints_to_drop.emplace_back(label, std::move(existing_unique_constraint_for_label));
}
});
}
});
std::ranges::for_each(
unique_constraints_to_drop, [memgraph_graph, &record_factory](const auto &label_unique_constraint) {
const auto &[label, unique_constraint] = label_unique_constraint;
auto unique_constraint_list = mgp::List();
std::ranges::for_each(unique_constraint, [&unique_constraint_list](const std::string_view &property) {
unique_constraint_list.AppendExtend(mgp::Value(property));
});
if (mgp::DropUniqueConstraint(memgraph_graph, label, mgp::Value(unique_constraint_list).ptr())) {
InsertRecordForUniqueConstraint(record_factory, label, unique_constraint_list, Schema::kStatusDropped);
}
});
}
void Schema::Assert(mgp_list *args, mgp_graph *memgraph_graph, mgp_result *result, mgp_memory *memory) {
mgp::MemoryDispatcherGuard guard{memory};
const auto record_factory = mgp::RecordFactory(result);
auto arguments = mgp::List(args);
auto indices_map = arguments[0].ValueMap();
auto unique_constraints_map = arguments[1].ValueMap();
auto existence_constraints_map = arguments[2].ValueMap();
auto drop_existing = arguments[3].ValueBool();
ProcessIndices(indices_map, memgraph_graph, record_factory, drop_existing);
ProcessExistenceConstraints(existence_constraints_map, memgraph_graph, record_factory, drop_existing);
ProcessUniqueConstraints(unique_constraints_map, memgraph_graph, record_factory, drop_existing);
}
extern "C" int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) {
try {
mgp::MemoryDispatcherGuard guard{memory};
;
AddProcedure(Schema::NodeTypeProperties, Schema::kProcedureNodeType, mgp::ProcedureType::Read, {},
{mgp::Return(Schema::kReturnNodeType, mgp::Type::String),
mgp::Return(Schema::kReturnLabels, {mgp::Type::List, mgp::Type::String}),
mgp::Return(Schema::kReturnPropertyName, mgp::Type::String),
mgp::Return(Schema::kReturnPropertyType, mgp::Type::Any),
mgp::Return(Schema::kReturnMandatory, mgp::Type::Bool)},
AddProcedure(Schema::NodeTypeProperties, std::string(Schema::kProcedureNodeType).c_str(), mgp::ProcedureType::Read,
{},
{mgp::Return(std::string(Schema::kReturnNodeType).c_str(), mgp::Type::String),
mgp::Return(std::string(Schema::kReturnLabels).c_str(), {mgp::Type::List, mgp::Type::String}),
mgp::Return(std::string(Schema::kReturnPropertyName).c_str(), mgp::Type::String),
mgp::Return(std::string(Schema::kReturnPropertyType).c_str(), mgp::Type::Any),
mgp::Return(std::string(Schema::kReturnMandatory).c_str(), mgp::Type::Bool)},
module, memory);
AddProcedure(Schema::RelTypeProperties, Schema::kProcedureRelType, mgp::ProcedureType::Read, {},
{mgp::Return(Schema::kReturnRelType, mgp::Type::String),
mgp::Return(Schema::kReturnPropertyName, mgp::Type::String),
mgp::Return(Schema::kReturnPropertyType, mgp::Type::Any),
mgp::Return(Schema::kReturnMandatory, mgp::Type::Bool)},
AddProcedure(Schema::RelTypeProperties, std::string(Schema::kProcedureRelType).c_str(), mgp::ProcedureType::Read,
{},
{mgp::Return(std::string(Schema::kReturnRelType).c_str(), mgp::Type::String),
mgp::Return(std::string(Schema::kReturnPropertyName).c_str(), mgp::Type::String),
mgp::Return(std::string(Schema::kReturnPropertyType).c_str(), mgp::Type::Any),
mgp::Return(std::string(Schema::kReturnMandatory).c_str(), mgp::Type::Bool)},
module, memory);
AddProcedure(
Schema::Assert, Schema::kProcedureAssert, mgp::ProcedureType::Read,
{
mgp::Parameter(Schema::kParameterIndices, {mgp::Type::Map, mgp::Type::Any}),
mgp::Parameter(Schema::kParameterUniqueConstraints, {mgp::Type::Map, mgp::Type::Any}),
mgp::Parameter(Schema::kParameterExistenceConstraints, {mgp::Type::Map, mgp::Type::Any},
mgp::Value(mgp::Map{})),
mgp::Parameter(Schema::kParameterDropExisting, mgp::Type::Bool, mgp::Value(true)),
},
{mgp::Return(Schema::kReturnLabel, mgp::Type::String), mgp::Return(Schema::kReturnKey, mgp::Type::String),
mgp::Return(Schema::kReturnKeys, {mgp::Type::List, mgp::Type::String}),
mgp::Return(Schema::kReturnUnique, mgp::Type::Bool), mgp::Return(Schema::kReturnAction, mgp::Type::String)},
module, memory);
} catch (const std::exception &e) {
std::cerr << "Error while initializing query module: " << e.what() << std::endl;
return 1;
}

View File

@@ -154,12 +154,6 @@ inline int &Get_Thread_Tracker() {
bool QueriesMemoryControl::IsThreadTracked() { return Get_Thread_Tracker() == 1; }
void QueriesMemoryControl::ResetTrackings() {
Get_Thread_Tracker() = 0;
transaction_id_to_tracker.clear();
thread_id_to_transaction_id.clear();
}
#endif
void StartTrackingCurrentThreadTransaction(uint64_t transaction_id) {
@@ -196,12 +190,6 @@ void TryStopTrackingOnTransaction(uint64_t transaction_id) {
#endif
}
void CleanTracker() {
#if USE_JEMALLOC
GetQueriesMemoryControl().ResetTrackings();
#endif
}
#if USE_JEMALLOC
bool IsTransactionTracked(uint64_t transaction_id) {
return GetQueriesMemoryControl().CheckTransactionIdTrackerExists(transaction_id);

View File

@@ -77,12 +77,6 @@ class QueriesMemoryControl {
bool IsThreadTracked();
// This method resets all the trackings.
// Method is not thread-safe so it should
// be called when there is global lock
// to not create new transactions
void ResetTrackings();
private:
struct ThreadIdToTransactionId {
std::thread::id thread_id;
@@ -133,11 +127,6 @@ void TryStartTrackingOnTransaction(uint64_t transaction_id, size_t limit);
// Does nothing if jemalloc is not enabled. Does nothing if tracker doesn't exist
void TryStopTrackingOnTransaction(uint64_t transaction_id);
// This method requires lock on global storage
// It will clean all trackers which may accidentally have left in system
// It will reset any tracking on arenas
void CleanTracker();
// Is transaction with given id tracked in memory tracker
bool IsTransactionTracked(uint64_t transaction_id);

View File

@@ -139,8 +139,6 @@ std::optional<VertexAccessor> SubgraphDbAccessor::FindVertex(storage::Gid gid, s
query::Graph *SubgraphDbAccessor::getGraph() { return graph_; }
DbAccessor *SubgraphDbAccessor::GetAccessor() { return &db_accessor_; }
VertexAccessor SubgraphVertexAccessor::GetVertexAccessor() const { return impl_; }
storage::Result<EdgeVertexAccessorResult> SubgraphVertexAccessor::OutEdges(storage::View view) const {

View File

@@ -694,8 +694,6 @@ class SubgraphDbAccessor final {
std::optional<VertexAccessor> FindVertex(storage::Gid gid, storage::View view);
Graph *getGraph();
DbAccessor *GetAccessor();
};
} // namespace memgraph::query

View File

@@ -147,8 +147,6 @@ void memgraph::query::CurrentDB::CleanupDBTransaction(bool abort) {
namespace memgraph::query {
constexpr std::string_view kSchemaAssert = "SCHEMA.ASSERT";
template <typename>
constexpr auto kAlwaysFalse = false;
@@ -3717,8 +3715,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
// TODO: ATM only a single database, will change when we have multiple database transactions
bool could_commit = utils::Downcast<CypherQuery>(parsed_query.query) != nullptr;
bool unique = utils::Downcast<IndexQuery>(parsed_query.query) != nullptr ||
utils::Downcast<ConstraintQuery>(parsed_query.query) != nullptr ||
upper_case_query.find(kSchemaAssert) != std::string::npos;
utils::Downcast<ConstraintQuery>(parsed_query.query) != nullptr;
SetupDatabaseTransaction(could_commit, unique);
}

View File

@@ -2535,333 +2535,6 @@ mgp_error mgp_graph_get_vertex_by_id(mgp_graph *graph, mgp_vertex_id id, mgp_mem
result);
}
mgp_error mgp_create_label_index(mgp_graph *graph, const char *label, int *result) {
return WrapExceptions(
[graph, label]() {
const auto label_id = std::visit([label](auto *impl) { return impl->NameToLabel(label); }, graph->impl);
const auto index_res =
std::visit(memgraph::utils::Overloaded{
[label_id](memgraph::query::DbAccessor *impl) { return impl->CreateIndex(label_id); },
[label_id](memgraph::query::SubgraphDbAccessor *impl) {
return impl->GetAccessor()->CreateIndex(label_id);
}},
graph->impl);
return index_res.HasError() ? 0 : 1;
},
result);
}
mgp_error mgp_drop_label_index(mgp_graph *graph, const char *label, int *result) {
return WrapExceptions(
[graph, label]() {
const auto label_id = std::visit([label](auto *impl) { return impl->NameToLabel(label); }, graph->impl);
const auto index_res =
std::visit(memgraph::utils::Overloaded{
[label_id](memgraph::query::DbAccessor *impl) { return impl->DropIndex(label_id); },
[label_id](memgraph::query::SubgraphDbAccessor *impl) {
return impl->GetAccessor()->DropIndex(label_id);
}},
graph->impl);
return index_res.HasError() ? 0 : 1;
},
result);
}
mgp_error mgp_list_all_label_indices(mgp_graph *graph, mgp_memory *memory, mgp_list **result) {
return WrapExceptions([graph, memory, result]() {
const auto index_res = std::visit(
memgraph::utils::Overloaded{
[](memgraph::query::DbAccessor *impl) { return impl->ListAllIndices().label; },
[](memgraph::query::SubgraphDbAccessor *impl) { return impl->GetAccessor()->ListAllIndices().label; }},
graph->impl);
if (const auto err = mgp_list_make_empty(index_res.size(), memory, result); err != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error("Listing all label indices failed due to failure of creating list");
}
for (const auto &label : index_res) {
const auto label_id_str = std::visit([label](const auto *impl) { return impl->LabelToName(label); }, graph->impl);
mgp_value *label_value = nullptr;
if (const auto err_str = mgp_value_make_string(label_id_str.c_str(), memory, &label_value);
err_str != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error("Listing all label indices failed due to failure of creating label value");
}
if (const auto err_list = mgp_list_append_extend(*result, label_value);
err_list != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error("Listing all label indices failed due to failure of appending label value");
}
mgp_value_destroy(label_value);
}
});
}
mgp_error mgp_create_label_property_index(mgp_graph *graph, const char *label, const char *property, int *result) {
return WrapExceptions(
[graph, label, property]() {
const auto label_id = std::visit([label](auto *impl) { return impl->NameToLabel(label); }, graph->impl);
const auto property_id =
std::visit([property](auto *impl) { return impl->NameToProperty(property); }, graph->impl);
const auto index_res =
std::visit(memgraph::utils::Overloaded{[label_id, property_id](memgraph::query::DbAccessor *impl) {
return impl->CreateIndex(label_id, property_id);
},
[label_id, property_id](memgraph::query::SubgraphDbAccessor *impl) {
return impl->GetAccessor()->CreateIndex(label_id, property_id);
}},
graph->impl);
return index_res.HasError() ? 0 : 1;
},
result);
}
mgp_error mgp_drop_label_property_index(mgp_graph *graph, const char *label, const char *property, int *result) {
return WrapExceptions(
[graph, label, property]() {
const auto label_id = std::visit([label](auto *impl) { return impl->NameToLabel(label); }, graph->impl);
const auto property_id =
std::visit([property](auto *impl) { return impl->NameToProperty(property); }, graph->impl);
const auto index_res =
std::visit(memgraph::utils::Overloaded{[label_id, property_id](memgraph::query::DbAccessor *impl) {
return impl->DropIndex(label_id, property_id);
},
[label_id, property_id](memgraph::query::SubgraphDbAccessor *impl) {
return impl->GetAccessor()->DropIndex(label_id, property_id);
}},
graph->impl);
return index_res.HasError() ? 0 : 1;
},
result);
}
mgp_error create_and_append_label_property_to_mgp_list(mgp_graph *graph, mgp_memory *memory, mgp_list **result,
const auto &label_property_pair) {
return WrapExceptions([graph, memory, result, &label_property_pair]() {
const auto label_id_str = std::visit(
[label_id = label_property_pair.first](const auto *impl) { return impl->LabelToName(label_id); }, graph->impl);
const auto property_id_str = std::visit(
[property_id = label_property_pair.second](const auto *impl) { return impl->PropertyToName(property_id); },
graph->impl);
// This is hack to avoid dealing with pairs
mgp_value *label_property = nullptr;
auto final_str = label_id_str + ":";
final_str += property_id_str;
if (const auto err_str = mgp_value_make_string(final_str.c_str(), memory, &label_property);
err_str != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error(
"Creating a list of label+property pairs failed due to failure of creating label+property value");
}
if (const auto err_list = mgp_list_append_extend(*result, label_property);
err_list != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error(
"Creating a list of label-property pairs due to failure of appending label+property value");
}
mgp_value_destroy(label_property);
});
}
mgp_error mgp_list_all_label_property_indices(mgp_graph *graph, mgp_memory *memory, mgp_list **result) {
return WrapExceptions([graph, memory, result]() {
const auto index_res =
std::visit(memgraph::utils::Overloaded{
[](memgraph::query::DbAccessor *impl) { return impl->ListAllIndices().label_property; },
[](memgraph::query::SubgraphDbAccessor *impl) {
return impl->GetAccessor()->ListAllIndices().label_property;
}},
graph->impl);
if (const auto err = mgp_list_make_empty(index_res.size(), memory, result); err != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error("Listing all label+property indices failed due to failure of creating list");
}
for (const auto &label_property_pair : index_res) {
if (const auto err = create_and_append_label_property_to_mgp_list(graph, memory, result, label_property_pair);
err != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error(
"Listing all label+property indices failed due to failure of appending label+property value");
}
}
});
}
mgp_error mgp_create_existence_constraint(mgp_graph *graph, const char *label, const char *property, int *result) {
return WrapExceptions(
[graph, label, property]() {
const auto label_id = std::visit([label](auto *impl) { return impl->NameToLabel(label); }, graph->impl);
const auto property_id =
std::visit([property](auto *impl) { return impl->NameToProperty(property); }, graph->impl);
const auto exist_res = std::visit(
memgraph::utils::Overloaded{[label_id, property_id](memgraph::query::DbAccessor *impl) {
return impl->CreateExistenceConstraint(label_id, property_id);
},
[label_id, property_id](memgraph::query::SubgraphDbAccessor *impl) {
return impl->GetAccessor()->CreateExistenceConstraint(label_id, property_id);
}},
graph->impl);
return exist_res.HasError() ? 0 : 1;
},
result);
}
mgp_error mgp_drop_existence_constraint(mgp_graph *graph, const char *label, const char *property, int *result) {
return WrapExceptions(
[graph, label, property]() {
const auto label_id = std::visit([label](auto *impl) { return impl->NameToLabel(label); }, graph->impl);
const auto property_id =
std::visit([property](auto *impl) { return impl->NameToProperty(property); }, graph->impl);
const auto exist_res = std::visit(
memgraph::utils::Overloaded{[label_id, property_id](memgraph::query::DbAccessor *impl) {
return impl->DropExistenceConstraint(label_id, property_id);
},
[label_id, property_id](memgraph::query::SubgraphDbAccessor *impl) {
return impl->GetAccessor()->DropExistenceConstraint(label_id, property_id);
}},
graph->impl);
return exist_res.HasError() ? 0 : 1;
},
result);
}
mgp_error mgp_list_all_existence_constraints(mgp_graph *graph, mgp_memory *memory, mgp_list **result) {
return WrapExceptions([graph, memory, result]() {
const auto constraint_res =
std::visit(memgraph::utils::Overloaded{
[](memgraph::query::DbAccessor *impl) { return impl->ListAllConstraints().existence; },
[](memgraph::query::SubgraphDbAccessor *impl) {
return impl->GetAccessor()->ListAllConstraints().existence;
}},
graph->impl);
if (const auto err = mgp_list_make_empty(constraint_res.size(), memory, result);
err != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error("Listing all existence constraints failed due to failure of creating a list");
}
for (const auto &label_property_pair : constraint_res) {
if (const auto err = create_and_append_label_property_to_mgp_list(graph, memory, result, label_property_pair);
err != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error(
"Listing all existence constraints failed due to failure of appending label+property value");
}
}
});
}
mgp_error mgp_create_unique_constraint(mgp_graph *graph, const char *label, mgp_value *properties, int *result) {
return WrapExceptions(
[graph, label, properties]() {
const auto label_id = std::visit([label](auto *impl) { return impl->NameToLabel(label); }, graph->impl);
std::set<memgraph::storage::PropertyId> property_ids;
for (const auto &elem : properties->list_v->elems) {
property_ids.insert(std::visit(
[prop_str = elem.string_v](auto *impl) { return impl->NameToProperty(prop_str); }, graph->impl));
}
const auto unique_res = std::visit(
memgraph::utils::Overloaded{[label_id, property_ids](memgraph::query::DbAccessor *impl) {
return impl->CreateUniqueConstraint(label_id, property_ids);
},
[label_id, property_ids](memgraph::query::SubgraphDbAccessor *impl) {
return impl->GetAccessor()->CreateUniqueConstraint(label_id, property_ids);
}},
graph->impl);
return unique_res.HasError() ? 0 : 1;
},
result);
}
mgp_error mgp_drop_unique_constraint(mgp_graph *graph, const char *label, mgp_value *properties, int *result) {
return WrapExceptions(
[graph, label, properties]() {
const auto label_id = std::visit([label](auto *impl) { return impl->NameToLabel(label); }, graph->impl);
std::set<memgraph::storage::PropertyId> property_ids;
for (const auto &elem : properties->list_v->elems) {
property_ids.insert(std::visit(
[prop_str = elem.string_v](auto *impl) { return impl->NameToProperty(prop_str); }, graph->impl));
}
const auto unique_res = std::visit(
memgraph::utils::Overloaded{[label_id, property_ids](memgraph::query::DbAccessor *impl) {
return impl->DropUniqueConstraint(label_id, property_ids);
},
[label_id, property_ids](memgraph::query::SubgraphDbAccessor *impl) {
return impl->GetAccessor()->DropUniqueConstraint(label_id, property_ids);
}},
graph->impl);
return unique_res == memgraph::storage::UniqueConstraints::DeletionStatus::SUCCESS ? 1 : 0;
},
result);
}
mgp_error mgp_list_all_unique_constraints(mgp_graph *graph, mgp_memory *memory, mgp_list **result) {
return WrapExceptions([graph, memory, result]() {
const auto constraints_res = std::visit(
memgraph::utils::Overloaded{
[](memgraph::query::DbAccessor *impl) { return impl->ListAllConstraints().unique; },
[](memgraph::query::SubgraphDbAccessor *impl) { return impl->GetAccessor()->ListAllConstraints().unique; }},
graph->impl);
if (const auto err = mgp_list_make_empty(constraints_res.size(), memory, result);
err != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error("Listing all unique constraints failed due to failure of creating a list");
}
for (const auto &label_properties_pair : constraints_res) {
const std::string label_id_str =
std::visit([label_id = label_properties_pair.first](const auto *impl) { return impl->LabelToName(label_id); },
graph->impl);
const std::vector<std::string> properties_str = std::visit(
[property_ids = label_properties_pair.second](const auto *impl) {
std::vector<std::string> property_ids_str;
property_ids_str.reserve(property_ids.size());
std::transform(property_ids.begin(), property_ids.end(), std::back_inserter(property_ids_str),
[impl](const auto &property_id) { return impl->PropertyToName(property_id); });
return property_ids_str;
},
graph->impl);
mgp_list *label_properties_mgp_list = nullptr;
if (const auto properties_mgp_list_err =
mgp_list_make_empty(properties_str.size() + 1, memory, &label_properties_mgp_list);
properties_mgp_list_err != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error("Listing all unique constraints failed due to failure of creating an inner list");
}
mgp_value *mgp_value_label = nullptr;
if (const auto err_label = mgp_value_make_string(label_id_str.c_str(), memory, &mgp_value_label);
err_label != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error("Listing all unique constraints failed due to failure of creating a label value");
}
if (const auto err_label_into_list = mgp_list_append_extend(label_properties_mgp_list, mgp_value_label);
err_label_into_list != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error("Listing all unique constraints failed due to failure of appending a label value");
}
mgp_value_destroy(mgp_value_label);
for (const std::string &property_str : properties_str) {
mgp_value *property_mgp_value = nullptr;
if (const auto err_str = mgp_value_make_string(property_str.c_str(), memory, &property_mgp_value);
err_str != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error("Listing all unique constraints failed due to failure of creating a property value");
}
if (const auto err_list = mgp_list_append_extend(label_properties_mgp_list, property_mgp_value);
err_list != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error("Listing all unique constraints failed due to failure of appending a property value");
}
mgp_value_destroy(property_mgp_value);
}
mgp_value value(label_properties_mgp_list, label_properties_mgp_list->GetMemoryResource());
if (const auto err_list = mgp_list_append_extend(*result, &value); err_list != mgp_error::MGP_ERROR_NO_ERROR) {
throw std::logic_error("Listing all unique constraints failed due to failure of creating label+property value");
}
mgp_value_destroy(&value);
}
});
}
mgp_error mgp_graph_is_mutable(mgp_graph *graph, int *result) {
*result = MgpGraphIsMutable(*graph) ? 1 : 0;
return mgp_error::MGP_ERROR_NO_ERROR;

View File

@@ -11,7 +11,6 @@
#include "storage/v2/inmemory/storage.hpp"
#include "dbms/constants.hpp"
#include "memory/query_memory_control.hpp"
#include "memory/global_memory_control.hpp"
#include "storage/v2/durability/durability.hpp"
#include "storage/v2/durability/snapshot.hpp"
@@ -1004,7 +1003,7 @@ void InMemoryStorage::InMemoryAccessor::FinalizeTransaction() {
}
utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryAccessor::CreateIndex(LabelId label) {
MG_ASSERT(unique_guard_.owns_lock(), "Creating label index requires a unique access to the storage!");
MG_ASSERT(unique_guard_.owns_lock(), "Create index requires a unique access to the storage!");
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
auto *mem_label_index = static_cast<InMemoryLabelIndex *>(in_memory->indices_.label_index_.get());
if (!mem_label_index->CreateIndex(label, in_memory->vertices_.access(), std::nullopt)) {
@@ -1018,7 +1017,7 @@ utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryA
utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryAccessor::CreateIndex(
LabelId label, PropertyId property) {
MG_ASSERT(unique_guard_.owns_lock(), "Creating label-property index requires a unique access to the storage!");
MG_ASSERT(unique_guard_.owns_lock(), "Create index requires a unique access to the storage!");
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
auto *mem_label_property_index =
static_cast<InMemoryLabelPropertyIndex *>(in_memory->indices_.label_property_index_.get());
@@ -1032,7 +1031,7 @@ utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryA
}
utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryAccessor::DropIndex(LabelId label) {
MG_ASSERT(unique_guard_.owns_lock(), "Dropping label index requires a unique access to the storage!");
MG_ASSERT(unique_guard_.owns_lock(), "Create index requires a unique access to the storage!");
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
auto *mem_label_index = static_cast<InMemoryLabelIndex *>(in_memory->indices_.label_index_.get());
if (!mem_label_index->DropIndex(label)) {
@@ -1046,7 +1045,7 @@ utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryA
utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryAccessor::DropIndex(
LabelId label, PropertyId property) {
MG_ASSERT(unique_guard_.owns_lock(), "Dropping label-property index requires a unique access to the storage!");
MG_ASSERT(unique_guard_.owns_lock(), "Create index requires a unique access to the storage!");
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
auto *mem_label_property_index =
static_cast<InMemoryLabelPropertyIndex *>(in_memory->indices_.label_property_index_.get());
@@ -1061,7 +1060,7 @@ utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryA
utils::BasicResult<StorageExistenceConstraintDefinitionError, void>
InMemoryStorage::InMemoryAccessor::CreateExistenceConstraint(LabelId label, PropertyId property) {
MG_ASSERT(unique_guard_.owns_lock(), "Creating existence requires a unique access to the storage!");
MG_ASSERT(unique_guard_.owns_lock(), "Create index requires a unique access to the storage!");
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
auto *existence_constraints = in_memory->constraints_.existence_constraints_.get();
if (existence_constraints->ConstraintExists(label, property)) {
@@ -1079,7 +1078,7 @@ InMemoryStorage::InMemoryAccessor::CreateExistenceConstraint(LabelId label, Prop
utils::BasicResult<StorageExistenceConstraintDroppingError, void>
InMemoryStorage::InMemoryAccessor::DropExistenceConstraint(LabelId label, PropertyId property) {
MG_ASSERT(unique_guard_.owns_lock(), "Dropping existence constraint requires a unique access to the storage!");
MG_ASSERT(unique_guard_.owns_lock(), "Create index requires a unique access to the storage!");
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
auto *existence_constraints = in_memory->constraints_.existence_constraints_.get();
if (!existence_constraints->DropConstraint(label, property)) {
@@ -1091,7 +1090,7 @@ InMemoryStorage::InMemoryAccessor::DropExistenceConstraint(LabelId label, Proper
utils::BasicResult<StorageUniqueConstraintDefinitionError, UniqueConstraints::CreationStatus>
InMemoryStorage::InMemoryAccessor::CreateUniqueConstraint(LabelId label, const std::set<PropertyId> &properties) {
MG_ASSERT(unique_guard_.owns_lock(), "Creating unique constraint requires a unique access to the storage!");
MG_ASSERT(unique_guard_.owns_lock(), "Create index requires a unique access to the storage!");
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
auto *mem_unique_constraints =
static_cast<InMemoryUniqueConstraints *>(in_memory->constraints_.unique_constraints_.get());
@@ -1108,7 +1107,7 @@ InMemoryStorage::InMemoryAccessor::CreateUniqueConstraint(LabelId label, const s
UniqueConstraints::DeletionStatus InMemoryStorage::InMemoryAccessor::DropUniqueConstraint(
LabelId label, const std::set<PropertyId> &properties) {
MG_ASSERT(unique_guard_.owns_lock(), "Dropping unique constraint requires a unique access to the storage!");
MG_ASSERT(unique_guard_.owns_lock(), "Create index requires a unique access to the storage!");
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
auto *mem_unique_constraints =
static_cast<InMemoryUniqueConstraints *>(in_memory->constraints_.unique_constraints_.get());
@@ -1244,11 +1243,7 @@ void InMemoryStorage::CollectGarbage(std::unique_lock<utils::ResourceLock> main_
if (!gc_guard.owns_lock()) {
return;
}
if constexpr (force) {
if (main_guard.owns_lock()) {
memgraph::memory::CleanTracker();
}
}
uint64_t oldest_active_start_timestamp = commit_log_->OldestActive();
// Deltas from previous GC runs or from aborts can be cleaned up here

View File

@@ -15,410 +15,6 @@ import pytest
from common import connect, execute_and_fetch_all
def test_assert_creates_label_index_empty_list():
cursor = connect().cursor()
results = list(
execute_and_fetch_all(
cursor,
"CALL libschema.assert({Person: []}, {}) YIELD * RETURN *;",
)
)
assert results == [("Created", "", [], "Person", False)]
show_index_results = list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;"))
assert show_index_results == [("label", "Person", None, 0)]
execute_and_fetch_all(cursor, "DROP INDEX ON :Person;")
def test_assert_creates_label_index_empty_string():
cursor = connect().cursor()
results = list(
execute_and_fetch_all(
cursor,
"CALL libschema.assert({Person: ['']}, {}) YIELD * RETURN *;",
)
)
assert results == [("Created", "", [], "Person", False)]
show_index_results = list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;"))
assert show_index_results == [("label", "Person", None, 0)]
execute_and_fetch_all(cursor, "DROP INDEX ON :Person;")
def test_assert_index_wrong_properties_type():
cursor = connect().cursor()
execute_and_fetch_all(
cursor,
"CALL libschema.assert({Person: ''}, {}) YIELD * RETURN *;",
)
assert list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;")) == []
def test_assert_property_is_not_a_string():
cursor = connect().cursor()
execute_and_fetch_all(
cursor,
"CALL libschema.assert({Person: ['name', 1]}, {}) YIELD * RETURN *;",
)
show_index_results = list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;"))
assert show_index_results == [("label+property", "Person", "name", 0)]
execute_and_fetch_all(cursor, "DROP INDEX ON :Person(name);")
def test_assert_creates_label_index_multiple_empty_strings():
cursor = connect().cursor()
results = list(
execute_and_fetch_all(
cursor,
"CALL libschema.assert({Person: ['', '', '', '']}, {}) YIELD * RETURN *;",
)
)
assert results == [("Created", "", [], "Person", False)]
show_index_results = list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;"))
assert show_index_results == [("label", "Person", None, 0)]
execute_and_fetch_all(cursor, "DROP INDEX ON :Person;")
def test_assert_creates_label_property_index():
cursor = connect().cursor()
results = list(
execute_and_fetch_all(
cursor,
"CALL libschema.assert({Person: ['name']}, {}) YIELD * RETURN *;",
)
)
assert results == [("Created", "name", ["name"], "Person", False)]
show_index_results = list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;"))
assert show_index_results == [("label+property", "Person", "name", 0)]
execute_and_fetch_all(cursor, "DROP INDEX ON :Person(name);")
def test_assert_creates_multiple_indices():
cursor = connect().cursor()
results = list(
execute_and_fetch_all(
cursor,
"CALL libschema.assert({Person: ['', 'id', 'name'], Ball: ['', 'size', 'size', '']}, {}) YIELD * RETURN *;",
)
)
assert len(results) == 5
assert results[0] == ("Created", "", [], "Ball", False)
assert results[1] == ("Created", "size", ["size"], "Ball", False)
assert results[2] == ("Created", "", [], "Person", False)
assert results[3] == ("Created", "id", ["id"], "Person", False)
assert results[4] == ("Created", "name", ["name"], "Person", False)
show_index_results = list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;"))
assert len(show_index_results) == 5
assert show_index_results[0] == ("label", "Ball", None, 0)
assert show_index_results[1] == ("label", "Person", None, 0)
assert show_index_results[2] == ("label+property", "Ball", "size", 0)
assert show_index_results[3] == ("label+property", "Person", "id", 0)
assert show_index_results[4] == ("label+property", "Person", "name", 0)
execute_and_fetch_all(cursor, "DROP INDEX ON :Person;")
execute_and_fetch_all(cursor, "DROP INDEX ON :Person(id);")
execute_and_fetch_all(cursor, "DROP INDEX ON :Person(name);")
execute_and_fetch_all(cursor, "DROP INDEX ON :Ball;")
execute_and_fetch_all(cursor, "DROP INDEX ON :Ball(size);")
def test_assert_creates_existence_constraints():
cursor = connect().cursor()
results = list(
execute_and_fetch_all(
cursor,
"CALL libschema.assert({}, {}, {Person: ['name', 'surname']}) YIELD * RETURN *;",
)
)
assert results == [
("Created", "name", ["name"], "Person", False),
("Created", "surname", ["surname"], "Person", False),
]
assert list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;")) == []
show_constraint_results = list(execute_and_fetch_all(cursor, "SHOW CONSTRAINT INFO;"))
assert show_constraint_results == [("exists", "Person", "name"), ("exists", "Person", "surname")]
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT EXISTS (n.name);")
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT EXISTS (n.surname);")
def test_assert_dropping_indices():
cursor = connect().cursor()
execute_and_fetch_all(cursor, "CREATE INDEX ON :Person(name);")
execute_and_fetch_all(cursor, "CREATE INDEX ON :Person(id);")
execute_and_fetch_all(cursor, "CREATE INDEX ON :Ball(size);")
execute_and_fetch_all(cursor, "CREATE INDEX ON :Ball;")
results = list(execute_and_fetch_all(cursor, "CALL libschema.assert({}, {}) YIELD * RETURN *;"))
assert len(results) == 4
assert results[0] == ("Dropped", "", [], "Ball", False)
assert results[1] == ("Dropped", "size", ["size"], "Ball", False)
assert results[2] == ("Dropped", "id", ["id"], "Person", False)
assert results[3] == ("Dropped", "name", ["name"], "Person", False)
show_index_results = list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;"))
assert show_index_results == []
def test_assert_existence_constraint_properties_not_list():
cursor = connect().cursor()
execute_and_fetch_all(cursor, "CALL libschema.assert({}, {}, {Person: 'name'}) YIELD * RETURN *;")
assert list(execute_and_fetch_all(cursor, "SHOW CONSTRAINT INFO;")) == []
def test_assert_existence_constraint_property_not_string():
cursor = connect().cursor()
execute_and_fetch_all(cursor, "CALL libschema.assert({}, {}, {Person: ['name', 1]}) YIELD * RETURN *;")
show_constraint_results = list(execute_and_fetch_all(cursor, "SHOW CONSTRAINT INFO;"))
assert show_constraint_results == [("exists", "Person", "name")]
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT EXISTS (n.name);")
def test_assert_existence_constraint_property_empty_string():
cursor = connect().cursor()
execute_and_fetch_all(cursor, "CALL libschema.assert({}, {}, {Person: ['']}) YIELD * RETURN *;")
assert list(execute_and_fetch_all(cursor, "SHOW CONSTRAINT INFO;")) == []
def test_assert_creates_indices_and_existence_constraints():
cursor = connect().cursor()
results = list(
execute_and_fetch_all(
cursor,
"CALL libschema.assert({Person: ['', 'id']}, {}, {Person: ['name', 'surname']}) YIELD * RETURN *;",
)
)
assert len(results) == 4
assert results[0] == ("Created", "", [], "Person", False)
assert results[1] == ("Created", "id", ["id"], "Person", False)
assert results[2] == ("Created", "name", ["name"], "Person", False)
assert results[3] == ("Created", "surname", ["surname"], "Person", False)
show_index_results = list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;"))
assert show_index_results == [("label", "Person", None, 0), ("label+property", "Person", "id", 0)]
show_constraint_results = list(execute_and_fetch_all(cursor, "SHOW CONSTRAINT INFO;"))
assert show_constraint_results == [("exists", "Person", "name"), ("exists", "Person", "surname")]
execute_and_fetch_all(cursor, "DROP INDEX ON :Person;")
execute_and_fetch_all(cursor, "DROP INDEX ON :Person(id);")
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT EXISTS (n.name);")
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT EXISTS (n.surname);")
def test_assert_drops_existence_constraints():
cursor = connect().cursor()
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.name);")
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.surname);")
results = list(execute_and_fetch_all(cursor, "CALL libschema.assert({}, {}, {}) YIELD * RETURN *;"))
assert len(results) == 2
assert results[0] == ("Dropped", "name", ["name"], "Person", False)
assert results[1] == ("Dropped", "surname", ["surname"], "Person", False)
show_constraint_results = list(execute_and_fetch_all(cursor, "SHOW CONSTRAINT INFO;"))
assert show_constraint_results == []
def test_assert_creates_unique_constraints():
cursor = connect().cursor()
results = list(
execute_and_fetch_all(
cursor,
"CALL libschema.assert({}, {Person: [['name', 'surname']]}) YIELD * RETURN *;",
)
)
assert results == [("Created", "[name, surname]", ["name", "surname"], "Person", True)]
assert list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;")) == []
show_constraint_results = list(execute_and_fetch_all(cursor, "SHOW CONSTRAINT INFO;"))
assert show_constraint_results == [("unique", "Person", ["name", "surname"])]
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT n.name, n.surname IS UNIQUE;")
def test_assert_creates_multiple_unique_constraints():
cursor = connect().cursor()
results = list(
execute_and_fetch_all(
cursor,
"CALL libschema.assert({}, {Person: [['name', 'surname'], ['id']]}) YIELD * RETURN *;",
)
)
assert results == [
("Created", "[name, surname]", ["name", "surname"], "Person", True),
("Created", "[id]", ["id"], "Person", True),
]
assert list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;")) == []
show_constraint_results = list(execute_and_fetch_all(cursor, "SHOW CONSTRAINT INFO;"))
assert show_constraint_results == [("unique", "Person", ["name", "surname"]), ("unique", "Person", ["id"])]
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT n.name, n.surname IS UNIQUE;")
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT n.id IS UNIQUE;")
def test_assert_creates_unique_constraints_skip_invalid():
cursor = connect().cursor()
results = list(
execute_and_fetch_all(
cursor,
"CALL libschema.assert({}, {Person: [['name', 'surname'], 'wrong_type']}) YIELD * RETURN *;",
)
)
assert results == [("Created", "[name, surname]", ["name", "surname"], "Person", True)]
assert list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;")) == []
show_constraint_results = list(execute_and_fetch_all(cursor, "SHOW CONSTRAINT INFO;"))
assert show_constraint_results == [("unique", "Person", ["name", "surname"])]
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT n.name, n.surname IS UNIQUE;")
def test_assert_creates_unique_constraints_skip_invalid_map_type():
cursor = connect().cursor()
results = list(
execute_and_fetch_all(
cursor,
"CALL libschema.assert({}, {Person: [['name', 'surname']], Ball: 'wrong_type'}) YIELD * RETURN *;",
)
)
assert results == [("Created", "[name, surname]", ["name", "surname"], "Person", True)]
assert list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;")) == []
show_constraint_results = list(execute_and_fetch_all(cursor, "SHOW CONSTRAINT INFO;"))
assert show_constraint_results == [("unique", "Person", ["name", "surname"])]
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT n.name, n.surname IS UNIQUE;")
def test_assert_creates_constraints_and_indices():
cursor = connect().cursor()
results = list(
execute_and_fetch_all(
cursor,
"CALL libschema.assert({Person: ['', 'id']}, {Person: [['name', 'surname'], ['id']]}, {Person: ['name', 'surname']}) YIELD * RETURN *;",
)
)
assert len(results) == 6
assert results[0] == ("Created", "", [], "Person", False)
assert results[1] == ("Created", "id", ["id"], "Person", False)
assert results[2] == ("Created", "name", ["name"], "Person", False)
assert results[3] == ("Created", "surname", ["surname"], "Person", False)
assert results[4] == ("Created", "[name, surname]", ["name", "surname"], "Person", True)
assert results[5] == ("Created", "[id]", ["id"], "Person", True)
show_index_results = list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;"))
assert show_index_results == [("label", "Person", None, 0), ("label+property", "Person", "id", 0)]
show_constraint_results = list(execute_and_fetch_all(cursor, "SHOW CONSTRAINT INFO;"))
assert show_constraint_results == [
("exists", "Person", "name"),
("exists", "Person", "surname"),
("unique", "Person", ["name", "surname"]),
("unique", "Person", ["id"]),
]
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT n.name, n.surname IS UNIQUE;")
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT n.id IS UNIQUE;")
execute_and_fetch_all(cursor, "DROP INDEX ON :Person;")
execute_and_fetch_all(cursor, "DROP INDEX ON :Person(id);")
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT EXISTS (n.name);")
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT EXISTS (n.surname);")
def test_assert_drops_unique_constraints():
cursor = connect().cursor()
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT n.name, n.surname IS UNIQUE;")
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT n.id IS UNIQUE;")
results = list(execute_and_fetch_all(cursor, "CALL libschema.assert({}, {}, {}) YIELD * RETURN *;"))
assert len(results) == 2
assert results[0] == ("Dropped", "[id]", ["id"], "Person", True)
assert results[1] == ("Dropped", "[name, surname]", ["name", "surname"], "Person", True)
show_constraint_results = list(execute_and_fetch_all(cursor, "SHOW CONSTRAINT INFO;"))
assert show_constraint_results == []
def test_assert_drops_indices_and_constraints():
cursor = connect().cursor()
execute_and_fetch_all(cursor, "CREATE INDEX ON :Person;")
execute_and_fetch_all(cursor, "CREATE INDEX ON :Person(id);")
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT n.name, n.surname IS UNIQUE;")
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT n.id IS UNIQUE;")
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.name);")
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.surname);")
results = list(execute_and_fetch_all(cursor, "CALL libschema.assert({}, {}, {}) YIELD * RETURN *;"))
assert len(results) == 6
assert results[0] == ("Dropped", "", [], "Person", False)
assert results[1] == ("Dropped", "id", ["id"], "Person", False)
assert results[2] == ("Dropped", "name", ["name"], "Person", False)
assert results[3] == ("Dropped", "surname", ["surname"], "Person", False)
assert results[4] == ("Dropped", "[id]", ["id"], "Person", True)
assert results[5] == ("Dropped", "[name, surname]", ["name", "surname"], "Person", True)
show_index_results = list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;"))
assert show_index_results == []
show_constraint_results = list(execute_and_fetch_all(cursor, "SHOW CONSTRAINT INFO;"))
assert show_constraint_results == []
def test_assert_does_not_drop_indices_and_constraints():
cursor = connect().cursor()
execute_and_fetch_all(cursor, "CREATE INDEX ON :Person;")
execute_and_fetch_all(cursor, "CREATE INDEX ON :Person(id);")
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT n.name, n.surname IS UNIQUE;")
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT n.id IS UNIQUE;")
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.name);")
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.surname);")
results = list(execute_and_fetch_all(cursor, "CALL libschema.assert({}, {}, {}, false) YIELD * RETURN *;"))
assert len(results) == 0
show_index_results = list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;"))
assert show_index_results == [("label", "Person", None, 0), ("label+property", "Person", "id", 0)]
show_constraint_results = list(execute_and_fetch_all(cursor, "SHOW CONSTRAINT INFO;"))
assert show_constraint_results == [
("exists", "Person", "name"),
("exists", "Person", "surname"),
("unique", "Person", ["name", "surname"]),
("unique", "Person", ["id"]),
]
execute_and_fetch_all(cursor, "DROP INDEX ON :Person;")
execute_and_fetch_all(cursor, "DROP INDEX ON :Person(id);")
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT n.name, n.surname IS UNIQUE;")
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT n.id IS UNIQUE;")
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT EXISTS (n.name);")
execute_and_fetch_all(cursor, "DROP CONSTRAINT ON (n:Person) ASSERT EXISTS (n.surname);")
def test_assert_keeps_existing_indices_and_constraints():
cursor = connect().cursor()
assert list(execute_and_fetch_all(cursor, "SHOW INDEX INFO;")) == []
assert list(execute_and_fetch_all(cursor, "SHOW CONSTRAINT INFO;")) == []
execute_and_fetch_all(cursor, "CREATE INDEX ON :Person;")
execute_and_fetch_all(cursor, "CREATE INDEX ON :Person(id);")
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT n.name, n.surname IS UNIQUE;")
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT n.id IS UNIQUE;")
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.name);")
execute_and_fetch_all(cursor, "CREATE CONSTRAINT ON (n:Person) ASSERT EXISTS (n.surname);")
results = list(
execute_and_fetch_all(
cursor,
"CALL libschema.assert({Person: ['id']}, {Person: [['name', 'surname']]}, {Person: ['name']}) YIELD * RETURN *;",
)
)
print(results)
assert len(results) == 6
assert results[0] == ("Kept", "id", ["id"], "Person", False) # label+property index on Person(id) should be kept
assert results[1] == ("Dropped", "", [], "Person", False) # label index on Person should be deleted
assert results[2] == (
"Kept",
"name",
["name"],
"Person",
False,
) # existence constraint on Person(name) should be kept
assert results[3] == (
"Dropped",
"surname",
["surname"],
"Person",
False,
) # existence constraint on surname should be deleted
assert results[4] == (
"Kept",
"[name, surname]",
["name", "surname"],
"Person",
True,
) # unique constraint on Person(name, surname) should be kept
assert results[5] == (
"Dropped",
"[id]",
["id"],
"Person",
True,
) # unique constraint on Person(id) should be deleted
def test_node_type_properties1():
cursor = connect().cursor()
execute_and_fetch_all(

View File

@@ -27,7 +27,7 @@ workloads:
args: ["query_modules/mgps_test.py"]
<<: *in_memory_cluster
- name: "Schema test"
- name: "Convert query module test"
pre_set_workload: "tests/e2e/x.sh"
binary: "tests/e2e/pytest_runner.sh"
proc: "query_modules/"