Add schema.assert (#1485)
This commit is contained in:
parent
d03fafcef6
commit
1d90b60f56
@ -236,6 +236,54 @@ 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) {
|
||||
|
@ -876,6 +876,65 @@ 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.
|
||||
|
@ -21,12 +21,10 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "_mgp.hpp"
|
||||
#include "mg_exceptions.hpp"
|
||||
@ -1289,7 +1287,7 @@ class Value {
|
||||
std::string_view ValueString() const;
|
||||
std::string_view ValueString();
|
||||
/// @pre Value type needs to be Type::List.
|
||||
const List ValueList() const;
|
||||
List ValueList() const;
|
||||
List ValueList();
|
||||
/// @pre Value type needs to be Type::Map.
|
||||
const Map ValueMap() const;
|
||||
@ -3651,7 +3649,7 @@ inline std::string_view Value::ValueString() {
|
||||
return mgp::value_get_string(ptr_);
|
||||
}
|
||||
|
||||
inline const List Value::ValueList() const {
|
||||
inline List Value::ValueList() const {
|
||||
if (Type() != Type::List) {
|
||||
throw ValueException("Type of value is wrong: expected List.");
|
||||
}
|
||||
@ -4279,9 +4277,77 @@ inline void AddParamsReturnsToProc(mgp_proc *proc, std::vector<Parameter> ¶m
|
||||
}
|
||||
} // 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);
|
||||
@ -4289,7 +4355,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);
|
||||
|
@ -10,18 +10,33 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include <mgp.hpp>
|
||||
#include "utils/string.hpp"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace Schema {
|
||||
|
||||
/*NodeTypeProperties and RelTypeProperties constants*/
|
||||
constexpr std::string_view kStatusKept = "Kept";
|
||||
constexpr std::string_view kStatusCreated = "Created";
|
||||
constexpr std::string_view kStatusDropped = "Dropped";
|
||||
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);
|
||||
|
||||
@ -35,6 +50,7 @@ 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
|
||||
@ -92,21 +108,22 @@ 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().size() == 0) {
|
||||
if (node.Properties().empty()) {
|
||||
auto record = record_factory.NewRecord();
|
||||
ProcessPropertiesNode<std::string>(record, type, labels, "", "", false);
|
||||
continue;
|
||||
@ -126,16 +143,15 @@ void Schema::NodeTypeProperties(mgp_list *args, mgp_graph *memgraph_graph, mgp_r
|
||||
}
|
||||
}
|
||||
|
||||
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().size() == 0) {
|
||||
if (rel.Properties().empty()) {
|
||||
auto record = record_factory.NewRecord();
|
||||
ProcessPropertiesRel<std::string>(record, type, "", "", false);
|
||||
continue;
|
||||
@ -155,29 +171,436 @@ void Schema::RelTypeProperties(mgp_list *args, mgp_graph *memgraph_graph, mgp_re
|
||||
}
|
||||
}
|
||||
|
||||
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, 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)},
|
||||
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)},
|
||||
module, memory);
|
||||
|
||||
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)},
|
||||
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)},
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -139,6 +139,8 @@ 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 {
|
||||
|
@ -694,6 +694,8 @@ class SubgraphDbAccessor final {
|
||||
std::optional<VertexAccessor> FindVertex(storage::Gid gid, storage::View view);
|
||||
|
||||
Graph *getGraph();
|
||||
|
||||
DbAccessor *GetAccessor();
|
||||
};
|
||||
|
||||
} // namespace memgraph::query
|
||||
|
@ -147,6 +147,8 @@ void memgraph::query::CurrentDB::CleanupDBTransaction(bool abort) {
|
||||
|
||||
namespace memgraph::query {
|
||||
|
||||
constexpr std::string_view kSchemaAssert = "SCHEMA.ASSERT";
|
||||
|
||||
template <typename>
|
||||
constexpr auto kAlwaysFalse = false;
|
||||
|
||||
@ -3715,7 +3717,8 @@ 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;
|
||||
utils::Downcast<ConstraintQuery>(parsed_query.query) != nullptr ||
|
||||
upper_case_query.find(kSchemaAssert) != std::string::npos;
|
||||
SetupDatabaseTransaction(could_commit, unique);
|
||||
}
|
||||
|
||||
|
@ -2535,6 +2535,333 @@ 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;
|
||||
|
@ -1003,7 +1003,7 @@ void InMemoryStorage::InMemoryAccessor::FinalizeTransaction() {
|
||||
}
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryAccessor::CreateIndex(LabelId label) {
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Create index requires a unique access to the storage!");
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Creating label 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)) {
|
||||
@ -1017,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(), "Create index requires a unique access to the storage!");
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Creating label-property 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());
|
||||
@ -1031,7 +1031,7 @@ utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryA
|
||||
}
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryAccessor::DropIndex(LabelId label) {
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Create index requires a unique access to the storage!");
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Dropping label 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)) {
|
||||
@ -1045,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(), "Create index requires a unique access to the storage!");
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Dropping label-property 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());
|
||||
@ -1060,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(), "Create index requires a unique access to the storage!");
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Creating existence 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)) {
|
||||
@ -1078,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(), "Create index requires a unique access to the storage!");
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Dropping existence constraint 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)) {
|
||||
@ -1090,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(), "Create index requires a unique access to the storage!");
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Creating unique constraint 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());
|
||||
@ -1107,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(), "Create index requires a unique access to the storage!");
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Dropping unique constraint 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());
|
||||
|
@ -15,6 +15,410 @@ 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(
|
||||
|
@ -27,7 +27,7 @@ workloads:
|
||||
args: ["query_modules/mgps_test.py"]
|
||||
<<: *in_memory_cluster
|
||||
|
||||
- name: "Convert query module test"
|
||||
- name: "Schema test"
|
||||
pre_set_workload: "tests/e2e/x.sh"
|
||||
binary: "tests/e2e/pytest_runner.sh"
|
||||
proc: "query_modules/"
|
||||
|
Loading…
Reference in New Issue
Block a user