Move schema to storage v3 and query v2

* Move schema to storage v3

* Remove schema from v2

* Move schema to query v2

* Remove schema from query v1

* Make glue v2

* Move schema related tests to newer versions of query and storage

* Fix typo in CMake

* Fix interpreter test

* Fix clang tidy errors

* Change temp dir name
This commit is contained in:
Jure Bajic 2022-08-04 09:50:02 +02:00 committed by GitHub
parent f57f30c8cf
commit a12a1ea358
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
85 changed files with 7893 additions and 2066 deletions

View File

@ -57,8 +57,6 @@ auth::Permission PrivilegeToPermission(query::AuthQuery::Privilege privilege) {
return auth::Permission::MODULE_WRITE;
case query::AuthQuery::Privilege::WEBSOCKET:
return auth::Permission::WEBSOCKET;
case query::AuthQuery::Privilege::SCHEMA:
return auth::Permission::SCHEMA;
}
}
} // namespace memgraph::glue

64
src/glue/v2/auth.cpp Normal file
View File

@ -0,0 +1,64 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include "glue/v2/auth.hpp"
namespace memgraph::glue::v2 {
auth::Permission PrivilegeToPermission(query::v2::AuthQuery::Privilege privilege) {
switch (privilege) {
case query::v2::AuthQuery::Privilege::MATCH:
return auth::Permission::MATCH;
case query::v2::AuthQuery::Privilege::CREATE:
return auth::Permission::CREATE;
case query::v2::AuthQuery::Privilege::MERGE:
return auth::Permission::MERGE;
case query::v2::AuthQuery::Privilege::DELETE:
return auth::Permission::DELETE;
case query::v2::AuthQuery::Privilege::SET:
return auth::Permission::SET;
case query::v2::AuthQuery::Privilege::REMOVE:
return auth::Permission::REMOVE;
case query::v2::AuthQuery::Privilege::INDEX:
return auth::Permission::INDEX;
case query::v2::AuthQuery::Privilege::STATS:
return auth::Permission::STATS;
case query::v2::AuthQuery::Privilege::CONSTRAINT:
return auth::Permission::CONSTRAINT;
case query::v2::AuthQuery::Privilege::DUMP:
return auth::Permission::DUMP;
case query::v2::AuthQuery::Privilege::REPLICATION:
return auth::Permission::REPLICATION;
case query::v2::AuthQuery::Privilege::DURABILITY:
return auth::Permission::DURABILITY;
case query::v2::AuthQuery::Privilege::READ_FILE:
return auth::Permission::READ_FILE;
case query::v2::AuthQuery::Privilege::FREE_MEMORY:
return auth::Permission::FREE_MEMORY;
case query::v2::AuthQuery::Privilege::TRIGGER:
return auth::Permission::TRIGGER;
case query::v2::AuthQuery::Privilege::CONFIG:
return auth::Permission::CONFIG;
case query::v2::AuthQuery::Privilege::AUTH:
return auth::Permission::AUTH;
case query::v2::AuthQuery::Privilege::STREAM:
return auth::Permission::STREAM;
case query::v2::AuthQuery::Privilege::MODULE_READ:
return auth::Permission::MODULE_READ;
case query::v2::AuthQuery::Privilege::MODULE_WRITE:
return auth::Permission::MODULE_WRITE;
case query::v2::AuthQuery::Privilege::WEBSOCKET:
return auth::Permission::WEBSOCKET;
case query::v2::AuthQuery::Privilege::SCHEMA:
return auth::Permission::SCHEMA;
}
}
} // namespace memgraph::glue::v2

23
src/glue/v2/auth.hpp Normal file
View File

@ -0,0 +1,23 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include "auth/models.hpp"
#include "query/v2/frontend/ast/ast.hpp"
namespace memgraph::glue::v2 {
/**
* This function converts query::AuthQuery::Privilege to its corresponding
* auth::Permission.
*/
auth::Permission PrivilegeToPermission(query::v2::AuthQuery::Privilege privilege);
} // namespace memgraph::glue::v2

View File

@ -0,0 +1,275 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include "glue/v2/communication.hpp"
#include <map>
#include <string>
#include <vector>
#include "storage/v3/edge_accessor.hpp"
#include "storage/v3/storage.hpp"
#include "storage/v3/vertex_accessor.hpp"
#include "utils/temporal.hpp"
using memgraph::communication::bolt::Value;
namespace memgraph::glue::v2 {
query::v2::TypedValue ToTypedValue(const Value &value) {
switch (value.type()) {
case Value::Type::Null:
return {};
case Value::Type::Bool:
return query::v2::TypedValue(value.ValueBool());
case Value::Type::Int:
return query::v2::TypedValue(value.ValueInt());
case Value::Type::Double:
return query::v2::TypedValue(value.ValueDouble());
case Value::Type::String:
return query::v2::TypedValue(value.ValueString());
case Value::Type::List: {
std::vector<query::v2::TypedValue> list;
list.reserve(value.ValueList().size());
for (const auto &v : value.ValueList()) list.push_back(ToTypedValue(v));
return query::v2::TypedValue(std::move(list));
}
case Value::Type::Map: {
std::map<std::string, query::v2::TypedValue> map;
for (const auto &kv : value.ValueMap()) map.emplace(kv.first, ToTypedValue(kv.second));
return query::v2::TypedValue(std::move(map));
}
case Value::Type::Vertex:
case Value::Type::Edge:
case Value::Type::UnboundedEdge:
case Value::Type::Path:
throw communication::bolt::ValueException("Unsupported conversion from Value to TypedValue");
case Value::Type::Date:
return query::v2::TypedValue(value.ValueDate());
case Value::Type::LocalTime:
return query::v2::TypedValue(value.ValueLocalTime());
case Value::Type::LocalDateTime:
return query::v2::TypedValue(value.ValueLocalDateTime());
case Value::Type::Duration:
return query::v2::TypedValue(value.ValueDuration());
}
}
storage::v3::Result<communication::bolt::Vertex> ToBoltVertex(const query::v2::VertexAccessor &vertex,
const storage::v3::Storage &db, storage::v3::View view) {
return ToBoltVertex(vertex.impl_, db, view);
}
storage::v3::Result<communication::bolt::Edge> ToBoltEdge(const query::v2::EdgeAccessor &edge,
const storage::v3::Storage &db, storage::v3::View view) {
return ToBoltEdge(edge.impl_, db, view);
}
storage::v3::Result<Value> ToBoltValue(const query::v2::TypedValue &value, const storage::v3::Storage &db,
storage::v3::View view) {
switch (value.type()) {
case query::v2::TypedValue::Type::Null:
return Value();
case query::v2::TypedValue::Type::Bool:
return Value(value.ValueBool());
case query::v2::TypedValue::Type::Int:
return Value(value.ValueInt());
case query::v2::TypedValue::Type::Double:
return Value(value.ValueDouble());
case query::v2::TypedValue::Type::String:
return Value(std::string(value.ValueString()));
case query::v2::TypedValue::Type::List: {
std::vector<Value> values;
values.reserve(value.ValueList().size());
for (const auto &v : value.ValueList()) {
auto maybe_value = ToBoltValue(v, db, view);
if (maybe_value.HasError()) return maybe_value.GetError();
values.emplace_back(std::move(*maybe_value));
}
return Value(std::move(values));
}
case query::v2::TypedValue::Type::Map: {
std::map<std::string, Value> map;
for (const auto &kv : value.ValueMap()) {
auto maybe_value = ToBoltValue(kv.second, db, view);
if (maybe_value.HasError()) return maybe_value.GetError();
map.emplace(kv.first, std::move(*maybe_value));
}
return Value(std::move(map));
}
case query::v2::TypedValue::Type::Vertex: {
auto maybe_vertex = ToBoltVertex(value.ValueVertex(), db, view);
if (maybe_vertex.HasError()) return maybe_vertex.GetError();
return Value(std::move(*maybe_vertex));
}
case query::v2::TypedValue::Type::Edge: {
auto maybe_edge = ToBoltEdge(value.ValueEdge(), db, view);
if (maybe_edge.HasError()) return maybe_edge.GetError();
return Value(std::move(*maybe_edge));
}
case query::v2::TypedValue::Type::Path: {
auto maybe_path = ToBoltPath(value.ValuePath(), db, view);
if (maybe_path.HasError()) return maybe_path.GetError();
return Value(std::move(*maybe_path));
}
case query::v2::TypedValue::Type::Date:
return Value(value.ValueDate());
case query::v2::TypedValue::Type::LocalTime:
return Value(value.ValueLocalTime());
case query::v2::TypedValue::Type::LocalDateTime:
return Value(value.ValueLocalDateTime());
case query::v2::TypedValue::Type::Duration:
return Value(value.ValueDuration());
}
}
storage::v3::Result<communication::bolt::Vertex> ToBoltVertex(const storage::v3::VertexAccessor &vertex,
const storage::v3::Storage &db, storage::v3::View view) {
auto id = communication::bolt::Id::FromUint(vertex.Gid().AsUint());
auto maybe_labels = vertex.Labels(view);
if (maybe_labels.HasError()) return maybe_labels.GetError();
std::vector<std::string> labels;
labels.reserve(maybe_labels->size());
for (const auto &label : *maybe_labels) {
labels.push_back(db.LabelToName(label));
}
auto maybe_properties = vertex.Properties(view);
if (maybe_properties.HasError()) return maybe_properties.GetError();
std::map<std::string, Value> properties;
for (const auto &prop : *maybe_properties) {
properties[db.PropertyToName(prop.first)] = ToBoltValue(prop.second);
}
return communication::bolt::Vertex{id, labels, properties};
}
storage::v3::Result<communication::bolt::Edge> ToBoltEdge(const storage::v3::EdgeAccessor &edge,
const storage::v3::Storage &db, storage::v3::View view) {
auto id = communication::bolt::Id::FromUint(edge.Gid().AsUint());
auto from = communication::bolt::Id::FromUint(edge.FromVertex().Gid().AsUint());
auto to = communication::bolt::Id::FromUint(edge.ToVertex().Gid().AsUint());
const auto &type = db.EdgeTypeToName(edge.EdgeType());
auto maybe_properties = edge.Properties(view);
if (maybe_properties.HasError()) return maybe_properties.GetError();
std::map<std::string, Value> properties;
for (const auto &prop : *maybe_properties) {
properties[db.PropertyToName(prop.first)] = ToBoltValue(prop.second);
}
return communication::bolt::Edge{id, from, to, type, properties};
}
storage::v3::Result<communication::bolt::Path> ToBoltPath(const query::v2::Path &path, const storage::v3::Storage &db,
storage::v3::View view) {
std::vector<communication::bolt::Vertex> vertices;
vertices.reserve(path.vertices().size());
for (const auto &v : path.vertices()) {
auto maybe_vertex = ToBoltVertex(v, db, view);
if (maybe_vertex.HasError()) return maybe_vertex.GetError();
vertices.emplace_back(std::move(*maybe_vertex));
}
std::vector<communication::bolt::Edge> edges;
edges.reserve(path.edges().size());
for (const auto &e : path.edges()) {
auto maybe_edge = ToBoltEdge(e, db, view);
if (maybe_edge.HasError()) return maybe_edge.GetError();
edges.emplace_back(std::move(*maybe_edge));
}
return communication::bolt::Path(vertices, edges);
}
storage::v3::PropertyValue ToPropertyValue(const Value &value) {
switch (value.type()) {
case Value::Type::Null:
return {};
case Value::Type::Bool:
return storage::v3::PropertyValue(value.ValueBool());
case Value::Type::Int:
return storage::v3::PropertyValue(value.ValueInt());
case Value::Type::Double:
return storage::v3::PropertyValue(value.ValueDouble());
case Value::Type::String:
return storage::v3::PropertyValue(value.ValueString());
case Value::Type::List: {
std::vector<storage::v3::PropertyValue> vec;
vec.reserve(value.ValueList().size());
for (const auto &value : value.ValueList()) vec.emplace_back(ToPropertyValue(value));
return storage::v3::PropertyValue(std::move(vec));
}
case Value::Type::Map: {
std::map<std::string, storage::v3::PropertyValue> map;
for (const auto &kv : value.ValueMap()) map.emplace(kv.first, ToPropertyValue(kv.second));
return storage::v3::PropertyValue(std::move(map));
}
case Value::Type::Vertex:
case Value::Type::Edge:
case Value::Type::UnboundedEdge:
case Value::Type::Path:
throw communication::bolt::ValueException("Unsupported conversion from Value to PropertyValue");
case Value::Type::Date:
return storage::v3::PropertyValue(
storage::v3::TemporalData(storage::v3::TemporalType::Date, value.ValueDate().MicrosecondsSinceEpoch()));
case Value::Type::LocalTime:
return storage::v3::PropertyValue(storage::v3::TemporalData(storage::v3::TemporalType::LocalTime,
value.ValueLocalTime().MicrosecondsSinceEpoch()));
case Value::Type::LocalDateTime:
return storage::v3::PropertyValue(storage::v3::TemporalData(storage::v3::TemporalType::LocalDateTime,
value.ValueLocalDateTime().MicrosecondsSinceEpoch()));
case Value::Type::Duration:
return storage::v3::PropertyValue(
storage::v3::TemporalData(storage::v3::TemporalType::Duration, value.ValueDuration().microseconds));
}
}
Value ToBoltValue(const storage::v3::PropertyValue &value) {
switch (value.type()) {
case storage::v3::PropertyValue::Type::Null:
return {};
case storage::v3::PropertyValue::Type::Bool:
return {value.ValueBool()};
case storage::v3::PropertyValue::Type::Int:
return {value.ValueInt()};
break;
case storage::v3::PropertyValue::Type::Double:
return {value.ValueDouble()};
case storage::v3::PropertyValue::Type::String:
return {value.ValueString()};
case storage::v3::PropertyValue::Type::List: {
const auto &values = value.ValueList();
std::vector<Value> vec;
vec.reserve(values.size());
for (const auto &v : values) {
vec.push_back(ToBoltValue(v));
}
return {std::move(vec)};
}
case storage::v3::PropertyValue::Type::Map: {
const auto &map = value.ValueMap();
std::map<std::string, Value> dv_map;
for (const auto &kv : map) {
dv_map.emplace(kv.first, ToBoltValue(kv.second));
}
return {std::move(dv_map)};
}
case storage::v3::PropertyValue::Type::TemporalData:
const auto &type = value.ValueTemporalData();
switch (type.type) {
case storage::v3::TemporalType::Date:
return {utils::Date(type.microseconds)};
case storage::v3::TemporalType::LocalTime:
return {utils::LocalTime(type.microseconds)};
case storage::v3::TemporalType::LocalDateTime:
return {utils::LocalDateTime(type.microseconds)};
case storage::v3::TemporalType::Duration:
return {utils::Duration(type.microseconds)};
}
}
}
} // namespace memgraph::glue::v2

View File

@ -0,0 +1,68 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
/// @file Conversion functions between Value and other memgraph types.
#pragma once
#include "communication/bolt/v1/value.hpp"
#include "query/v2/typed_value.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/result.hpp"
#include "storage/v3/view.hpp"
namespace memgraph::storage::v3 {
class EdgeAccessor;
class Storage;
class VertexAccessor;
} // namespace memgraph::storage::v3
namespace memgraph::glue::v2 {
/// @param storage::v3::VertexAccessor for converting to
/// communication::bolt::Vertex.
/// @param storage::v3::Storage for getting label and property names.
/// @param storage::v3::View for deciding which vertex attributes are visible.
///
/// @throw std::bad_alloc
storage::v3::Result<communication::bolt::Vertex> ToBoltVertex(const storage::v3::VertexAccessor &vertex,
const storage::v3::Storage &db, storage::v3::View view);
/// @param storage::v3::EdgeAccessor for converting to communication::bolt::Edge.
/// @param storage::v3::Storage for getting edge type and property names.
/// @param storage::v3::View for deciding which edge attributes are visible.
///
/// @throw std::bad_alloc
storage::v3::Result<communication::bolt::Edge> ToBoltEdge(const storage::v3::EdgeAccessor &edge,
const storage::v3::Storage &db, storage::v3::View view);
/// @param query::v2::Path for converting to communication::bolt::Path.
/// @param storage::v3::Storage for ToBoltVertex and ToBoltEdge.
/// @param storage::v3::View for ToBoltVertex and ToBoltEdge.
///
/// @throw std::bad_alloc
storage::v3::Result<communication::bolt::Path> ToBoltPath(const query::v2::Path &path, const storage::v3::Storage &db,
storage::v3::View view);
/// @param query::v2::TypedValue for converting to communication::bolt::Value.
/// @param storage::v3::Storage for ToBoltVertex and ToBoltEdge.
/// @param storage::v3::View for ToBoltVertex and ToBoltEdge.
///
/// @throw std::bad_alloc
storage::v3::Result<communication::bolt::Value> ToBoltValue(const query::v2::TypedValue &value,
const storage::v3::Storage &db, storage::v3::View view);
query::v2::TypedValue ToTypedValue(const communication::bolt::Value &value);
communication::bolt::Value ToBoltValue(const storage::v3::PropertyValue &value);
storage::v3::PropertyValue ToPropertyValue(const communication::bolt::Value &value);
} // namespace memgraph::glue::v2

View File

@ -16,7 +16,6 @@
#include <cstdint>
#include <string>
#include <string_view>
#include <type_traits>
#include "query/db_accessor.hpp"
#include "query/exceptions.hpp"
@ -25,12 +24,8 @@
#include "query/typed_value.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/result.hpp"
#include "storage/v2/schema_validator.hpp"
#include "storage/v2/view.hpp"
#include "utils/exceptions.hpp"
#include "utils/logging.hpp"
#include "utils/variant_helpers.hpp"
namespace memgraph::query {
@ -86,79 +81,27 @@ concept AccessorWithSetProperty = requires(T accessor, const storage::PropertyId
{ accessor.SetProperty(key, new_value) } -> std::same_as<storage::Result<storage::PropertyValue>>;
};
inline void HandleSchemaViolation(const storage::SchemaViolation &schema_violation, const DbAccessor &dba) {
switch (schema_violation.status) {
case storage::SchemaViolation::ValidationStatus::VERTEX_HAS_NO_PRIMARY_PROPERTY: {
throw SchemaViolationException(
fmt::format("Primary key {} not defined on label :{}",
storage::SchemaTypeToString(schema_violation.violated_schema_property->type),
dba.LabelToName(schema_violation.label)));
}
case storage::SchemaViolation::ValidationStatus::NO_SCHEMA_DEFINED_FOR_LABEL: {
throw SchemaViolationException(
fmt::format("Label :{} is not a primary label", dba.LabelToName(schema_violation.label)));
}
case storage::SchemaViolation::ValidationStatus::VERTEX_PROPERTY_WRONG_TYPE: {
throw SchemaViolationException(
fmt::format("Wrong type of property {} in schema :{}, should be of type {}",
*schema_violation.violated_property_value, dba.LabelToName(schema_violation.label),
storage::SchemaTypeToString(schema_violation.violated_schema_property->type)));
}
case storage::SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_KEY: {
throw SchemaViolationException(fmt::format("Updating of primary key {} on schema :{} not supported",
*schema_violation.violated_property_value,
dba.LabelToName(schema_violation.label)));
}
case storage::SchemaViolation::ValidationStatus::VERTEX_MODIFY_PRIMARY_LABEL: {
throw SchemaViolationException(fmt::format("Cannot add or remove label :{} since it is a primary label",
dba.LabelToName(schema_violation.label)));
}
case storage::SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY: {
throw SchemaViolationException(
fmt::format("Cannot create vertex with secondary label :{}", dba.LabelToName(schema_violation.label)));
}
}
}
inline void HandleErrorOnPropertyUpdate(const storage::Error error) {
switch (error) {
case storage::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to set properties on a deleted object.");
case storage::Error::PROPERTIES_DISABLED:
throw QueryRuntimeException("Can't set property because properties on edges are disabled.");
case storage::Error::VERTEX_HAS_EDGES:
case storage::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when setting a property.");
}
}
/// Set a property `value` mapped with given `key` on a `record`.
///
/// @throw QueryRuntimeException if value cannot be set as a property value
template <AccessorWithSetProperty T>
storage::PropertyValue PropsSetChecked(T *record, const DbAccessor &dba, const storage::PropertyId &key,
const TypedValue &value) {
storage::PropertyValue PropsSetChecked(T *record, const storage::PropertyId &key, const TypedValue &value) {
try {
if constexpr (std::is_same_v<T, VertexAccessor>) {
const auto maybe_old_value = record->SetPropertyAndValidate(key, storage::PropertyValue(value));
if (maybe_old_value.HasError()) {
std::visit(utils::Overloaded{[](const storage::Error error) { HandleErrorOnPropertyUpdate(error); },
[&dba](const storage::SchemaViolation &schema_violation) {
HandleSchemaViolation(schema_violation, dba);
}},
maybe_old_value.GetError());
auto maybe_old_value = record->SetProperty(key, storage::PropertyValue(value));
if (maybe_old_value.HasError()) {
switch (maybe_old_value.GetError()) {
case storage::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to set properties on a deleted object.");
case storage::Error::PROPERTIES_DISABLED:
throw QueryRuntimeException("Can't set property because properties on edges are disabled.");
case storage::Error::VERTEX_HAS_EDGES:
case storage::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when setting a property.");
}
return std::move(*maybe_old_value);
} else {
// No validation on edge properties
const auto maybe_old_value = record->SetProperty(key, storage::PropertyValue(value));
if (maybe_old_value.HasError()) {
HandleErrorOnPropertyUpdate(maybe_old_value.GetError());
}
return std::move(*maybe_old_value);
}
return std::move(*maybe_old_value);
} catch (const TypedValueException &) {
throw QueryRuntimeException("'{}' cannot be used as a property value.", value.type());
}

View File

@ -12,7 +12,6 @@
#pragma once
#include <optional>
#include <vector>
#include <cppitertools/filter.hpp>
#include <cppitertools/imap.hpp>
@ -24,7 +23,7 @@
///////////////////////////////////////////////////////////
// Our communication layer and query engine don't mix
// very well on Centos because OpenSSL version available
// very well on Centos because OpenSSL version avaialable
// on Centos 7 include libkrb5 which has brilliant macros
// called TRUE and FALSE. For more detailed explanation go
// to memgraph.cpp.
@ -35,8 +34,6 @@
// simply undefine those macros as we're sure that libkrb5
// won't and can't be used anywhere in the query engine.
#include "storage/v2/storage.hpp"
#include "utils/logging.hpp"
#include "utils/result.hpp"
#undef FALSE
#undef TRUE
@ -105,18 +102,10 @@ class VertexAccessor final {
auto Labels(storage::View view) const { return impl_.Labels(view); }
auto PrimaryLabel(storage::View view) const { return impl_.PrimaryLabel(view); }
storage::Result<bool> AddLabel(storage::LabelId label) { return impl_.AddLabel(label); }
storage::ResultSchema<bool> AddLabelAndValidate(storage::LabelId label) { return impl_.AddLabelAndValidate(label); }
storage::Result<bool> RemoveLabel(storage::LabelId label) { return impl_.RemoveLabel(label); }
storage::ResultSchema<bool> RemoveLabelAndValidate(storage::LabelId label) {
return impl_.RemoveLabelAndValidate(label);
}
storage::Result<bool> HasLabel(storage::View view, storage::LabelId label) const {
return impl_.HasLabel(label, view);
}
@ -131,13 +120,8 @@ class VertexAccessor final {
return impl_.SetProperty(key, value);
}
storage::ResultSchema<storage::PropertyValue> SetPropertyAndValidate(storage::PropertyId key,
const storage::PropertyValue &value) {
return impl_.SetPropertyAndValidate(key, value);
}
storage::ResultSchema<storage::PropertyValue> RemovePropertyAndValidate(storage::PropertyId key) {
return SetPropertyAndValidate(key, storage::PropertyValue{});
storage::Result<storage::PropertyValue> RemoveProperty(storage::PropertyId key) {
return SetProperty(key, storage::PropertyValue());
}
storage::Result<std::map<storage::PropertyId, storage::PropertyValue>> ClearProperties() {
@ -263,18 +247,7 @@ class DbAccessor final {
return VerticesIterable(accessor_->Vertices(label, property, lower, upper, view));
}
// TODO Remove when query modules have been fixed
[[deprecated]] VertexAccessor InsertVertex() { return VertexAccessor(accessor_->CreateVertex()); }
storage::ResultSchema<VertexAccessor> InsertVertexAndValidate(
const storage::LabelId primary_label, const std::vector<storage::LabelId> &labels,
const std::vector<std::pair<storage::PropertyId, storage::PropertyValue>> &properties) {
auto maybe_vertex_acc = accessor_->CreateVertexAndValidate(primary_label, labels, properties);
if (maybe_vertex_acc.HasError()) {
return {std::move(maybe_vertex_acc.GetError())};
}
return VertexAccessor{maybe_vertex_acc.GetValue()};
}
VertexAccessor InsertVertex() { return VertexAccessor(accessor_->CreateVertex()); }
storage::Result<EdgeAccessor> InsertEdge(VertexAccessor *from, VertexAccessor *to,
const storage::EdgeTypeId &edge_type) {
@ -332,7 +305,7 @@ class DbAccessor final {
return std::optional<VertexAccessor>{};
}
return {std::make_optional<VertexAccessor>(*value)};
return std::make_optional<VertexAccessor>(*value);
}
storage::PropertyId NameToProperty(const std::string_view name) { return accessor_->NameToProperty(name); }
@ -381,10 +354,6 @@ class DbAccessor final {
storage::IndicesInfo ListAllIndices() const { return accessor_->ListAllIndices(); }
storage::ConstraintsInfo ListAllConstraints() const { return accessor_->ListAllConstraints(); }
const storage::SchemaValidator &GetSchemaValidator() const { return accessor_->GetSchemaValidator(); }
storage::SchemasInfo ListAllSchemas() const { return accessor_->ListAllSchemas(); }
};
} // namespace memgraph::query

View File

@ -224,12 +224,4 @@ class VersionInfoInMulticommandTxException : public QueryException {
: QueryException("Version info query not allowed in multicommand transactions.") {}
};
/**
* An exception for an illegal operation that violates schema
*/
class SchemaViolationException : public QueryRuntimeException {
public:
using QueryRuntimeException::QueryRuntimeException;
};
} // namespace memgraph::query

View File

@ -17,7 +17,6 @@
#include <variant>
#include <vector>
#include "common/types.hpp"
#include "query/frontend/ast/ast_visitor.hpp"
#include "query/frontend/semantic/symbol.hpp"
#include "query/interpret/awesome_memgraph_functions.hpp"
@ -134,15 +133,6 @@ cpp<#
}
cpp<#))
(defun clone-schema-property-vector (source dest)
#>cpp
${dest}.reserve(${source}.size());
for (const auto &[property_ix, property_type]: ${source}) {
${dest}.emplace_back(storage->GetPropertyIx(property_ix.name), property_type);
}
cpp<#)
;; The following index structs serve as a decoupling point of AST from
;; concrete database types. All the names are collected in AstStorage, and can
;; be indexed through these instances. This means that we can create a vector
@ -2263,7 +2253,7 @@ cpp<#
(lcp:define-enum privilege
(create delete match merge set remove index stats auth constraint
dump replication durability read_file free_memory trigger config stream module_read module_write
websocket schema)
websocket)
(:serialize))
#>cpp
AuthQuery() = default;
@ -2305,7 +2295,7 @@ const std::vector<AuthQuery::Privilege> kPrivilegesAll = {
AuthQuery::Privilege::FREE_MEMORY, AuthQuery::Privilege::TRIGGER,
AuthQuery::Privilege::CONFIG, AuthQuery::Privilege::STREAM,
AuthQuery::Privilege::MODULE_READ, AuthQuery::Privilege::MODULE_WRITE,
AuthQuery::Privilege::WEBSOCKET, AuthQuery::Privilege::SCHEMA};
AuthQuery::Privilege::WEBSOCKET};
cpp<#
(lcp:define-class info-query (query)
@ -2675,38 +2665,5 @@ cpp<#
(:serialize (:slk))
(:clone))
(lcp:define-class schema-query (query)
((action "Action" :scope :public)
(label "LabelIx" :scope :public
:slk-load (lambda (member)
#>cpp
slk::Load(&self->${member}, reader, storage);
cpp<#)
:clone (lambda (source dest)
#>cpp
${dest} = storage->GetLabelIx(${source}.name);
cpp<#))
(schema_type_map "std::vector<std::pair<PropertyIx, common::SchemaType>>"
:slk-save #'slk-save-property-map
:slk-load #'slk-load-property-map
:clone #'clone-schema-property-vector
:scope :public))
(:public
(lcp:define-enum action
(create-schema drop-schema show-schema show-schemas)
(:serialize))
#>cpp
SchemaQuery() = default;
DEFVISITABLE(QueryVisitor<void>);
cpp<#)
(:private
#>cpp
friend class AstStorage;
cpp<#)
(:serialize (:slk))
(:clone))
(lcp:pop-namespace) ;; namespace query
(lcp:pop-namespace) ;; namespace memgraph

View File

@ -94,7 +94,6 @@ class StreamQuery;
class SettingQuery;
class VersionQuery;
class Foreach;
class SchemaQuery;
using TreeCompositeVisitor = utils::CompositeVisitor<
SingleQuery, CypherUnion, NamedExpression, OrOperator, XorOperator, AndOperator, NotOperator, AdditionOperator,
@ -126,9 +125,9 @@ class ExpressionVisitor
None, ParameterLookup, Identifier, PrimitiveLiteral, RegexMatch> {};
template <class TResult>
class QueryVisitor : public utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery, IndexQuery, AuthQuery,
InfoQuery, ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery,
FreeMemoryQuery, TriggerQuery, IsolationLevelQuery, CreateSnapshotQuery,
StreamQuery, SettingQuery, VersionQuery, SchemaQuery> {};
class QueryVisitor
: public utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery, IndexQuery, AuthQuery, InfoQuery,
ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery, FreeMemoryQuery, TriggerQuery,
IsolationLevelQuery, CreateSnapshotQuery, StreamQuery, SettingQuery, VersionQuery> {};
} // namespace memgraph::query

View File

@ -17,7 +17,6 @@
#include <cstring>
#include <iterator>
#include <limits>
#include <ranges>
#include <string>
#include <tuple>
#include <type_traits>
@ -28,7 +27,6 @@
#include <boost/preprocessor/cat.hpp>
#include "common/types.hpp"
#include "query/exceptions.hpp"
#include "query/frontend/ast/ast.hpp"
#include "query/frontend/ast/ast_visitor.hpp"
@ -1349,7 +1347,6 @@ antlrcpp::Any CypherMainVisitor::visitPrivilege(MemgraphCypher::PrivilegeContext
if (ctx->MODULE_READ()) return AuthQuery::Privilege::MODULE_READ;
if (ctx->MODULE_WRITE()) return AuthQuery::Privilege::MODULE_WRITE;
if (ctx->WEBSOCKET()) return AuthQuery::Privilege::WEBSOCKET;
if (ctx->SCHEMA()) return AuthQuery::Privilege::SCHEMA;
LOG_FATAL("Should not get here - unknown privilege!");
}
@ -2356,93 +2353,6 @@ antlrcpp::Any CypherMainVisitor::visitForeach(MemgraphCypher::ForeachContext *ct
return for_each;
}
antlrcpp::Any CypherMainVisitor::visitSchemaQuery(MemgraphCypher::SchemaQueryContext *ctx) {
MG_ASSERT(ctx->children.size() == 1, "SchemaQuery should have exactly one child!");
auto *schema_query = ctx->children[0]->accept(this).as<SchemaQuery *>();
query_ = schema_query;
return schema_query;
}
antlrcpp::Any CypherMainVisitor::visitShowSchema(MemgraphCypher::ShowSchemaContext *ctx) {
auto *schema_query = storage_->Create<SchemaQuery>();
schema_query->action_ = SchemaQuery::Action::SHOW_SCHEMA;
schema_query->label_ = AddLabel(ctx->labelName()->accept(this));
query_ = schema_query;
return schema_query;
}
antlrcpp::Any CypherMainVisitor::visitShowSchemas(MemgraphCypher::ShowSchemasContext * /*ctx*/) {
auto *schema_query = storage_->Create<SchemaQuery>();
schema_query->action_ = SchemaQuery::Action::SHOW_SCHEMAS;
query_ = schema_query;
return schema_query;
}
antlrcpp::Any CypherMainVisitor::visitPropertyType(MemgraphCypher::PropertyTypeContext *ctx) {
MG_ASSERT(ctx->symbolicName());
const auto property_type = utils::ToLowerCase(ctx->symbolicName()->accept(this).as<std::string>());
if (property_type == "bool") {
return common::SchemaType::BOOL;
}
if (property_type == "string") {
return common::SchemaType::STRING;
}
if (property_type == "integer") {
return common::SchemaType::INT;
}
if (property_type == "date") {
return common::SchemaType::DATE;
}
if (property_type == "duration") {
return common::SchemaType::DURATION;
}
if (property_type == "localdatetime") {
return common::SchemaType::LOCALDATETIME;
}
if (property_type == "localtime") {
return common::SchemaType::LOCALTIME;
}
throw SyntaxException("Property type must be one of the supported types!");
}
/**
* @return Schema*
*/
antlrcpp::Any CypherMainVisitor::visitSchemaPropertyMap(MemgraphCypher::SchemaPropertyMapContext *ctx) {
std::vector<std::pair<PropertyIx, common::SchemaType>> schema_property_map;
for (auto *property_key_pair : ctx->propertyKeyTypePair()) {
PropertyIx key = property_key_pair->propertyKeyName()->accept(this);
common::SchemaType type = property_key_pair->propertyType()->accept(this);
if (std::ranges::find_if(schema_property_map, [&key](const auto &elem) { return elem.first == key; }) !=
schema_property_map.end()) {
throw SemanticException("Same property name can't appear twice in a schema map.");
}
schema_property_map.emplace_back(key, type);
}
return schema_property_map;
}
antlrcpp::Any CypherMainVisitor::visitCreateSchema(MemgraphCypher::CreateSchemaContext *ctx) {
auto *schema_query = storage_->Create<SchemaQuery>();
schema_query->action_ = SchemaQuery::Action::CREATE_SCHEMA;
schema_query->label_ = AddLabel(ctx->labelName()->accept(this));
schema_query->schema_type_map_ =
ctx->schemaPropertyMap()->accept(this).as<std::vector<std::pair<PropertyIx, common::SchemaType>>>();
query_ = schema_query;
return schema_query;
}
/**
* @return Schema*
*/
antlrcpp::Any CypherMainVisitor::visitDropSchema(MemgraphCypher::DropSchemaContext *ctx) {
auto *schema_query = storage_->Create<SchemaQuery>();
schema_query->action_ = SchemaQuery::Action::DROP_SCHEMA;
schema_query->label_ = AddLabel(ctx->labelName()->accept(this));
query_ = schema_query;
return schema_query;
}
LabelIx CypherMainVisitor::AddLabel(const std::string &name) { return storage_->GetLabelIx(name); }
PropertyIx CypherMainVisitor::AddProperty(const std::string &name) { return storage_->GetPropertyIx(name); }

View File

@ -849,41 +849,6 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
*/
antlrcpp::Any visitForeach(MemgraphCypher::ForeachContext *ctx) override;
/**
* @return Schema*
*/
antlrcpp::Any visitPropertyType(MemgraphCypher::PropertyTypeContext *ctx) override;
/**
* @return Schema*
*/
antlrcpp::Any visitSchemaPropertyMap(MemgraphCypher::SchemaPropertyMapContext *ctx) override;
/**
* @return Schema*
*/
antlrcpp::Any visitSchemaQuery(MemgraphCypher::SchemaQueryContext *ctx) override;
/**
* @return Schema*
*/
antlrcpp::Any visitShowSchema(MemgraphCypher::ShowSchemaContext *ctx) override;
/**
* @return Schema*
*/
antlrcpp::Any visitShowSchemas(MemgraphCypher::ShowSchemasContext *ctx) override;
/**
* @return Schema*
*/
antlrcpp::Any visitCreateSchema(MemgraphCypher::CreateSchemaContext *ctx) override;
/**
* @return Schema*
*/
antlrcpp::Any visitDropSchema(MemgraphCypher::DropSchemaContext *ctx) override;
public:
Query *query() { return query_; }
const static std::string kAnonPrefix;

View File

@ -46,10 +46,10 @@ memgraphCypherKeyword : cypherKeyword
| DROP
| DUMP
| EXECUTE
| FREE
| FROM
| FOR
| FOREACH
| FREE
| FROM
| GLOBAL
| GRANT
| HEADER
@ -76,8 +76,6 @@ memgraphCypherKeyword : cypherKeyword
| ROLE
| ROLES
| QUOTE
| SCHEMA
| SCHEMAS
| SESSION
| SETTING
| SETTINGS
@ -124,7 +122,6 @@ query : cypherQuery
| streamQuery
| settingQuery
| versionQuery
| schemaQuery
;
authQuery : createRole
@ -195,12 +192,6 @@ settingQuery : setSetting
| showSettings
;
schemaQuery : showSchema
| showSchemas
| createSchema
| dropSchema
;
loadCsv : LOAD CSV FROM csvFile ( WITH | NO ) HEADER
( IGNORE BAD ) ?
( DELIMITER delimiter ) ?
@ -263,7 +254,6 @@ privilege : CREATE
| MODULE_READ
| MODULE_WRITE
| WEBSOCKET
| SCHEMA
;
privilegeList : privilege ( ',' privilege )* ;
@ -383,17 +373,3 @@ showSetting : SHOW DATABASE SETTING settingName ;
showSettings : SHOW DATABASE SETTINGS ;
versionQuery : SHOW VERSION ;
showSchema : SHOW SCHEMA ON ':' labelName ;
showSchemas : SHOW SCHEMAS ;
propertyType : symbolicName ;
propertyKeyTypePair : propertyKeyName propertyType ;
schemaPropertyMap : '(' propertyKeyTypePair ( ',' propertyKeyTypePair )* ')' ;
createSchema : CREATE SCHEMA ON ':' labelName schemaPropertyMap ;
dropSchema : DROP SCHEMA ON ':' labelName ;

View File

@ -89,8 +89,6 @@ REVOKE : R E V O K E ;
ROLE : R O L E ;
ROLES : R O L E S ;
QUOTE : Q U O T E ;
SCHEMA : S C H E M A ;
SCHEMAS : S C H E M A S ;
SERVICE_URL : S E R V I C E UNDERSCORE U R L ;
SESSION : S E S S I O N ;
SETTING : S E T T I N G ;

View File

@ -80,8 +80,6 @@ class PrivilegeExtractor : public QueryVisitor<void>, public HierarchicalTreeVis
void Visit(VersionQuery & /*version_query*/) override { AddPrivilege(AuthQuery::Privilege::STATS); }
void Visit(SchemaQuery & /*schema_query*/) override { AddPrivilege(AuthQuery::Privilege::SCHEMA); }
bool PreVisit(Create & /*unused*/) override {
AddPrivilege(AuthQuery::Privilege::CREATE);
return false;

View File

@ -205,8 +205,7 @@ const trie::Trie kKeywords = {"union",
"service_url",
"version",
"websocket",
"foreach",
"schema"};
"foreach"};
// Unicode codepoints that are allowed at the start of the unescaped name.
const std::bitset<kBitsetSize> kUnescapedNameAllowedStarts(

View File

@ -45,7 +45,6 @@
#include "query/typed_value.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/replication/enums.hpp"
#include "storage/v2/schemas.hpp"
#include "utils/algorithm.hpp"
#include "utils/csv_parsing.hpp"
#include "utils/event_counter.hpp"
@ -873,102 +872,6 @@ Callback HandleSettingQuery(SettingQuery *setting_query, const Parameters &param
}
}
Callback HandleSchemaQuery(SchemaQuery *schema_query, InterpreterContext *interpreter_context,
std::vector<Notification> *notifications) {
Callback callback;
switch (schema_query->action_) {
case SchemaQuery::Action::SHOW_SCHEMAS: {
callback.header = {"label", "primary_key"};
callback.fn = [interpreter_context]() {
auto *db = interpreter_context->db;
auto schemas_info = db->ListAllSchemas();
std::vector<std::vector<TypedValue>> results;
results.reserve(schemas_info.schemas.size());
for (const auto &[label_id, schema_types] : schemas_info.schemas) {
std::vector<TypedValue> schema_info_row;
schema_info_row.reserve(3);
schema_info_row.emplace_back(db->LabelToName(label_id));
std::vector<std::string> primary_key_properties;
primary_key_properties.reserve(schema_types.size());
std::transform(schema_types.begin(), schema_types.end(), std::back_inserter(primary_key_properties),
[&db](const auto &schema_type) {
return db->PropertyToName(schema_type.property_id) +
"::" + storage::SchemaTypeToString(schema_type.type);
});
schema_info_row.emplace_back(utils::Join(primary_key_properties, ", "));
results.push_back(std::move(schema_info_row));
}
return results;
};
return callback;
}
case SchemaQuery::Action::SHOW_SCHEMA: {
callback.header = {"property_name", "property_type"};
callback.fn = [interpreter_context, primary_label = schema_query->label_]() {
auto *db = interpreter_context->db;
const auto label = db->NameToLabel(primary_label.name);
const auto schema = db->GetSchema(label);
std::vector<std::vector<TypedValue>> results;
if (schema) {
for (const auto &schema_property : schema->second) {
std::vector<TypedValue> schema_info_row;
schema_info_row.reserve(2);
schema_info_row.emplace_back(db->PropertyToName(schema_property.property_id));
schema_info_row.emplace_back(storage::SchemaTypeToString(schema_property.type));
results.push_back(std::move(schema_info_row));
}
return results;
}
throw QueryException(fmt::format("Schema on label :{} not found!", primary_label.name));
};
return callback;
}
case SchemaQuery::Action::CREATE_SCHEMA: {
auto schema_type_map = schema_query->schema_type_map_;
if (schema_query->schema_type_map_.empty()) {
throw SyntaxException("One or more types have to be defined in schema definition.");
}
callback.fn = [interpreter_context, primary_label = schema_query->label_,
schema_type_map = std::move(schema_type_map)]() {
auto *db = interpreter_context->db;
const auto label = db->NameToLabel(primary_label.name);
std::vector<storage::SchemaProperty> schemas_types;
schemas_types.reserve(schema_type_map.size());
for (const auto &schema_type : schema_type_map) {
auto property_id = db->NameToProperty(schema_type.first.name);
schemas_types.push_back({property_id, schema_type.second});
}
if (!db->CreateSchema(label, schemas_types)) {
throw QueryException(fmt::format("Schema on label :{} already exists!", primary_label.name));
}
return std::vector<std::vector<TypedValue>>{};
};
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::CREATE_SCHEMA,
fmt::format("Create schema on label :{}", schema_query->label_.name));
return callback;
}
case SchemaQuery::Action::DROP_SCHEMA: {
callback.fn = [interpreter_context, primary_label = schema_query->label_]() {
auto *db = interpreter_context->db;
const auto label = db->NameToLabel(primary_label.name);
if (!db->DropSchema(label)) {
throw QueryException(fmt::format("Schema on label :{} does not exist!", primary_label.name));
}
return std::vector<std::vector<TypedValue>>{};
};
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::DROP_SCHEMA,
fmt::format("Dropped schema on label :{}", schema_query->label_.name));
return callback;
}
}
return callback;
}
// Struct for lazy pulling from a vector
struct PullPlanVector {
explicit PullPlanVector(std::vector<std::vector<TypedValue>> values) : values_(std::move(values)) {}
@ -2163,32 +2066,6 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
RWType::NONE};
}
PreparedQuery PrepareSchemaQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
InterpreterContext *interpreter_context, std::vector<Notification> *notifications) {
if (in_explicit_transaction) {
throw ConstraintInMulticommandTxException();
}
auto *schema_query = utils::Downcast<SchemaQuery>(parsed_query.query);
MG_ASSERT(schema_query);
auto callback = HandleSchemaQuery(schema_query, interpreter_context, notifications);
return PreparedQuery{std::move(callback.header), std::move(parsed_query.required_privileges),
[handler = std::move(callback.fn), action = QueryHandlerResult::NOTHING,
pull_plan = std::shared_ptr<PullPlanVector>(nullptr)](
AnyStream *stream, std::optional<int> n) mutable -> std::optional<QueryHandlerResult> {
if (!pull_plan) {
auto results = handler();
pull_plan = std::make_shared<PullPlanVector>(std::move(results));
}
if (pull_plan->Pull(stream, n)) {
return action;
}
return std::nullopt;
},
RWType::NONE};
}
void Interpreter::BeginTransaction() {
const auto prepared_query = PrepareTransactionQuery("BEGIN");
prepared_query.query_handler(nullptr, {});
@ -2322,9 +2199,6 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
prepared_query = PrepareSettingQuery(std::move(parsed_query), in_explicit_transaction_, &*execution_db_accessor_);
} else if (utils::Downcast<VersionQuery>(parsed_query.query)) {
prepared_query = PrepareVersionQuery(std::move(parsed_query), in_explicit_transaction_);
} else if (utils::Downcast<SchemaQuery>(parsed_query.query)) {
prepared_query = PrepareSchemaQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_,
&query_execution->notifications);
} else {
LOG_FATAL("Should not get here -- unknown query type!");
}

View File

@ -38,8 +38,6 @@ constexpr std::string_view GetCodeString(const NotificationCode code) {
return "CreateIndex"sv;
case NotificationCode::CREATE_STREAM:
return "CreateStream"sv;
case NotificationCode::CREATE_SCHEMA:
return "CreateSchema"sv;
case NotificationCode::CHECK_STREAM:
return "CheckStream"sv;
case NotificationCode::CREATE_TRIGGER:
@ -50,8 +48,6 @@ constexpr std::string_view GetCodeString(const NotificationCode code) {
return "DropReplica"sv;
case NotificationCode::DROP_INDEX:
return "DropIndex"sv;
case NotificationCode::DROP_SCHEMA:
return "DropSchema"sv;
case NotificationCode::DROP_STREAM:
return "DropStream"sv;
case NotificationCode::DROP_TRIGGER:
@ -72,10 +68,6 @@ constexpr std::string_view GetCodeString(const NotificationCode code) {
return "ReplicaPortWarning"sv;
case NotificationCode::SET_REPLICA:
return "SetReplica"sv;
case NotificationCode::SHOW_SCHEMA:
return "ShowSchema"sv;
case NotificationCode::SHOW_SCHEMAS:
return "ShowSchemas"sv;
case NotificationCode::START_STREAM:
return "StartStream"sv;
case NotificationCode::START_ALL_STREAMS:

View File

@ -26,14 +26,12 @@ enum class SeverityLevel : uint8_t { INFO, WARNING };
enum class NotificationCode : uint8_t {
CREATE_CONSTRAINT,
CREATE_INDEX,
CREATE_SCHEMA,
CHECK_STREAM,
CREATE_STREAM,
CREATE_TRIGGER,
DROP_CONSTRAINT,
DROP_INDEX,
DROP_REPLICA,
DROP_SCHEMA,
DROP_STREAM,
DROP_TRIGGER,
EXISTANT_INDEX,
@ -44,8 +42,6 @@ enum class NotificationCode : uint8_t {
REPLICA_PORT_WARNING,
REGISTER_REPLICA,
SET_REPLICA,
SHOW_SCHEMA,
SHOW_SCHEMAS,
START_STREAM,
START_ALL_STREAMS,
STOP_STREAM,

View File

@ -37,12 +37,7 @@
#include "query/procedure/cypher_types.hpp"
#include "query/procedure/mg_procedure_impl.hpp"
#include "query/procedure/module.hpp"
#include "query/typed_value.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/result.hpp"
#include "storage/v2/schema_validator.hpp"
#include "storage/v2/schemas.hpp"
#include "utils/algorithm.hpp"
#include "utils/csv_parsing.hpp"
#include "utils/event_counter.hpp"
@ -57,7 +52,6 @@
#include "utils/readable_size.hpp"
#include "utils/string.hpp"
#include "utils/temporal.hpp"
#include "utils/variant_helpers.hpp"
// macro for the default implementation of LogicalOperator::Accept
// that accepts the visitor and visits it's input_ operator
@ -180,56 +174,45 @@ CreateNode::CreateNode(const std::shared_ptr<LogicalOperator> &input, const Node
// Creates a vertex on this GraphDb. Returns a reference to vertex placed on the
// frame.
VertexAccessor &CreateLocalVertexAtomically(const NodeCreationInfo &node_info, Frame *frame,
ExecutionContext &context) {
VertexAccessor &CreateLocalVertex(const NodeCreationInfo &node_info, Frame *frame, ExecutionContext &context) {
auto &dba = *context.db_accessor;
auto new_node = dba.InsertVertex();
context.execution_stats[ExecutionStats::Key::CREATED_NODES] += 1;
for (auto label : node_info.labels) {
auto maybe_error = new_node.AddLabel(label);
if (maybe_error.HasError()) {
switch (maybe_error.GetError()) {
case storage::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to set a label on a deleted node.");
case storage::Error::VERTEX_HAS_EDGES:
case storage::Error::PROPERTIES_DISABLED:
case storage::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when setting a label.");
}
}
context.execution_stats[ExecutionStats::Key::CREATED_LABELS] += 1;
}
// Evaluator should use the latest accessors, as modified in this query, when
// setting properties on new nodes.
ExpressionEvaluator evaluator(frame, context.symbol_table, context.evaluation_context, context.db_accessor,
storage::View::NEW);
std::vector<std::pair<storage::PropertyId, storage::PropertyValue>> properties;
// TODO: PropsSetChecked allocates a PropertyValue, make it use context.memory
// when we update PropertyValue with custom allocator.
if (const auto *node_info_properties = std::get_if<PropertiesMapList>(&node_info.properties)) {
properties.reserve(node_info_properties->size());
for (const auto &[key, value_expression] : *node_info_properties) {
properties.emplace_back(key, storage::PropertyValue(value_expression->Accept(evaluator)));
PropsSetChecked(&new_node, key, value_expression->Accept(evaluator));
}
} else {
auto property_map = evaluator.Visit(*std::get<ParameterLookup *>(node_info.properties)).ValueMap();
properties.reserve(property_map.size());
for (const auto &[key, value] : property_map) {
auto property_map = evaluator.Visit(*std::get<ParameterLookup *>(node_info.properties));
for (const auto &[key, value] : property_map.ValueMap()) {
auto property_id = dba.NameToProperty(key);
properties.emplace_back(property_id, value);
PropsSetChecked(&new_node, property_id, value);
}
}
// TODO Remove later on since that will be enforced from grammar side
MG_ASSERT(!node_info.labels.empty(), "There must be at least one label!");
const auto primary_label = node_info.labels[0];
std::vector<storage::LabelId> secondary_labels(node_info.labels.begin() + 1, node_info.labels.end());
auto maybe_new_node = dba.InsertVertexAndValidate(primary_label, secondary_labels, properties);
if (maybe_new_node.HasError()) {
std::visit(utils::Overloaded{[&dba](const storage::SchemaViolation &schema_violation) {
HandleSchemaViolation(schema_violation, dba);
},
[](const storage::Error error) {
switch (error) {
case storage::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to set a label on a deleted node.");
case storage::Error::VERTEX_HAS_EDGES:
case storage::Error::PROPERTIES_DISABLED:
case storage::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when setting a label.");
}
}},
maybe_new_node.GetError());
}
context.execution_stats[ExecutionStats::Key::CREATED_NODES] += 1;
(*frame)[node_info.symbol] = *maybe_new_node;
(*frame)[node_info.symbol] = new_node;
return (*frame)[node_info.symbol].ValueVertex();
}
@ -254,7 +237,7 @@ bool CreateNode::CreateNodeCursor::Pull(Frame &frame, ExecutionContext &context)
SCOPED_PROFILE_OP("CreateNode");
if (input_cursor_->Pull(frame, context)) {
auto created_vertex = CreateLocalVertexAtomically(self_.node_info_, &frame, context);
auto created_vertex = CreateLocalVertex(self_.node_info_, &frame, context);
if (context.trigger_context_collector) {
context.trigger_context_collector->RegisterCreatedObject(created_vertex);
}
@ -303,13 +286,13 @@ EdgeAccessor CreateEdge(const EdgeCreationInfo &edge_info, DbAccessor *dba, Vert
auto &edge = *maybe_edge;
if (const auto *properties = std::get_if<PropertiesMapList>(&edge_info.properties)) {
for (const auto &[key, value_expression] : *properties) {
PropsSetChecked(&edge, *dba, key, value_expression->Accept(*evaluator));
PropsSetChecked(&edge, key, value_expression->Accept(*evaluator));
}
} else {
auto property_map = evaluator->Visit(*std::get<ParameterLookup *>(edge_info.properties));
for (const auto &[key, value] : property_map.ValueMap()) {
auto property_id = dba->NameToProperty(key);
PropsSetChecked(&edge, *dba, property_id, value);
PropsSetChecked(&edge, property_id, value);
}
}
@ -386,7 +369,7 @@ VertexAccessor &CreateExpand::CreateExpandCursor::OtherVertex(Frame &frame, Exec
ExpectType(self_.node_info_.symbol, dest_node_value, TypedValue::Type::Vertex);
return dest_node_value.ValueVertex();
}
auto &created_vertex = CreateLocalVertexAtomically(self_.node_info_, &frame, context);
auto &created_vertex = CreateLocalVertex(self_.node_info_, &frame, context);
if (context.trigger_context_collector) {
context.trigger_context_collector->RegisterCreatedObject(created_vertex);
}
@ -2063,7 +2046,7 @@ bool SetProperty::SetPropertyCursor::Pull(Frame &frame, ExecutionContext &contex
switch (lhs.type()) {
case TypedValue::Type::Vertex: {
auto old_value = PropsSetChecked(&lhs.ValueVertex(), *context.db_accessor, self_.property_, rhs);
auto old_value = PropsSetChecked(&lhs.ValueVertex(), self_.property_, rhs);
context.execution_stats[ExecutionStats::Key::UPDATED_PROPERTIES] += 1;
if (context.trigger_context_collector) {
// rhs cannot be moved because it was created with the allocator that is only valid during current pull
@ -2073,7 +2056,7 @@ bool SetProperty::SetPropertyCursor::Pull(Frame &frame, ExecutionContext &contex
break;
}
case TypedValue::Type::Edge: {
auto old_value = PropsSetChecked(&lhs.ValueEdge(), *context.db_accessor, self_.property_, rhs);
auto old_value = PropsSetChecked(&lhs.ValueEdge(), self_.property_, rhs);
context.execution_stats[ExecutionStats::Key::UPDATED_PROPERTIES] += 1;
if (context.trigger_context_collector) {
// rhs cannot be moved because it was created with the allocator that is only valid during current pull
@ -2227,7 +2210,7 @@ void SetPropertiesOnRecord(TRecordAccessor *record, const TypedValue &rhs, SetPr
case TypedValue::Type::Map: {
for (const auto &kv : rhs.ValueMap()) {
auto key = context->db_accessor->NameToProperty(kv.first);
auto old_value = PropsSetChecked(record, *context->db_accessor, key, kv.second);
auto old_value = PropsSetChecked(record, key, kv.second);
if (should_register_change) {
register_set_property(std::move(old_value), key, kv.second);
}
@ -2311,31 +2294,22 @@ bool SetLabels::SetLabelsCursor::Pull(Frame &frame, ExecutionContext &context) {
// Skip setting labels on Null (can occur in optional match).
if (vertex_value.IsNull()) return true;
ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
auto &dba = *context.db_accessor;
auto &vertex = vertex_value.ValueVertex();
for (const auto label : self_.labels_) {
auto maybe_value = vertex.AddLabelAndValidate(label);
for (auto label : self_.labels_) {
auto maybe_value = vertex.AddLabel(label);
if (maybe_value.HasError()) {
std::visit(utils::Overloaded{[](const storage::Error error) {
switch (error) {
case storage::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to set a label on a deleted node.");
case storage::Error::VERTEX_HAS_EDGES:
case storage::Error::PROPERTIES_DISABLED:
case storage::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when setting a label.");
}
},
[&dba](const storage::SchemaViolation schema_violation) {
HandleSchemaViolation(schema_violation, dba);
}},
maybe_value.GetError());
switch (maybe_value.GetError()) {
case storage::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to set a label on a deleted node.");
case storage::Error::VERTEX_HAS_EDGES:
case storage::Error::PROPERTIES_DISABLED:
case storage::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when setting a label.");
}
}
context.execution_stats[ExecutionStats::Key::CREATED_LABELS]++;
if (context.trigger_context_collector && *maybe_value) {
context.trigger_context_collector->RegisterSetVertexLabel(vertex, label);
}
@ -2378,11 +2352,26 @@ bool RemoveProperty::RemovePropertyCursor::Pull(Frame &frame, ExecutionContext &
TypedValue lhs = self_.lhs_->expression_->Accept(evaluator);
auto remove_prop = [property = self_.property_, &context](auto *record) {
auto old_value = PropsSetChecked(record, *context.db_accessor, property, TypedValue{});
auto maybe_old_value = record->RemoveProperty(property);
if (maybe_old_value.HasError()) {
switch (maybe_old_value.GetError()) {
case storage::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to remove a property on a deleted graph element.");
case storage::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::Error::PROPERTIES_DISABLED:
throw QueryRuntimeException(
"Can't remove property because properties on edges are "
"disabled.");
case storage::Error::VERTEX_HAS_EDGES:
case storage::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when removing property.");
}
}
if (context.trigger_context_collector) {
context.trigger_context_collector->RegisterRemovedObjectProperty(*record, property,
TypedValue(std::move(old_value)));
TypedValue(std::move(*maybe_old_value)));
}
};
@ -2436,25 +2425,18 @@ bool RemoveLabels::RemoveLabelsCursor::Pull(Frame &frame, ExecutionContext &cont
ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
auto &vertex = vertex_value.ValueVertex();
for (auto label : self_.labels_) {
auto maybe_value = vertex.RemoveLabelAndValidate(label);
auto maybe_value = vertex.RemoveLabel(label);
if (maybe_value.HasError()) {
std::visit(
utils::Overloaded{[](const storage::Error error) {
switch (error) {
case storage::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to remove labels from a deleted node.");
case storage::Error::VERTEX_HAS_EDGES:
case storage::Error::PROPERTIES_DISABLED:
case storage::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when removing labels from a node.");
}
},
[&context](const storage::SchemaViolation &schema_violation) {
HandleSchemaViolation(schema_violation, *context.db_accessor);
}},
maybe_value.GetError());
switch (maybe_value.GetError()) {
case storage::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to remove labels from a deleted node.");
case storage::Error::VERTEX_HAS_EDGES:
case storage::Error::PROPERTIES_DISABLED:
case storage::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when removing labels from a node.");
}
}
context.execution_stats[ExecutionStats::Key::DELETED_LABELS] += 1;

View File

@ -16,6 +16,7 @@
#include <cstdint>
#include <string>
#include <string_view>
#include <type_traits>
#include "query/v2/db_accessor.hpp"
#include "query/v2/exceptions.hpp"
@ -24,8 +25,12 @@
#include "query/v2/typed_value.hpp"
#include "storage/v3/id_types.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/result.hpp"
#include "storage/v3/schema_validator.hpp"
#include "storage/v3/view.hpp"
#include "utils/exceptions.hpp"
#include "utils/logging.hpp"
#include "utils/variant_helpers.hpp"
namespace memgraph::query::v2 {
@ -81,27 +86,79 @@ concept AccessorWithSetProperty = requires(T accessor, const storage::v3::Proper
{ accessor.SetProperty(key, new_value) } -> std::same_as<storage::v3::Result<storage::v3::PropertyValue>>;
};
inline void HandleSchemaViolation(const storage::v3::SchemaViolation &schema_violation, const DbAccessor &dba) {
switch (schema_violation.status) {
case storage::v3::SchemaViolation::ValidationStatus::VERTEX_HAS_NO_PRIMARY_PROPERTY: {
throw SchemaViolationException(
fmt::format("Primary key {} not defined on label :{}",
storage::v3::SchemaTypeToString(schema_violation.violated_schema_property->type),
dba.LabelToName(schema_violation.label)));
}
case storage::v3::SchemaViolation::ValidationStatus::NO_SCHEMA_DEFINED_FOR_LABEL: {
throw SchemaViolationException(
fmt::format("Label :{} is not a primary label", dba.LabelToName(schema_violation.label)));
}
case storage::v3::SchemaViolation::ValidationStatus::VERTEX_PROPERTY_WRONG_TYPE: {
throw SchemaViolationException(
fmt::format("Wrong type of property {} in schema :{}, should be of type {}",
*schema_violation.violated_property_value, dba.LabelToName(schema_violation.label),
storage::v3::SchemaTypeToString(schema_violation.violated_schema_property->type)));
}
case storage::v3::SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_KEY: {
throw SchemaViolationException(fmt::format("Updating of primary key {} on schema :{} not supported",
*schema_violation.violated_property_value,
dba.LabelToName(schema_violation.label)));
}
case storage::v3::SchemaViolation::ValidationStatus::VERTEX_MODIFY_PRIMARY_LABEL: {
throw SchemaViolationException(fmt::format("Cannot add or remove label :{} since it is a primary label",
dba.LabelToName(schema_violation.label)));
}
case storage::v3::SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY: {
throw SchemaViolationException(
fmt::format("Cannot create vertex with secondary label :{}", dba.LabelToName(schema_violation.label)));
}
}
}
inline void HandleErrorOnPropertyUpdate(const storage::v3::Error error) {
switch (error) {
case storage::v3::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::v3::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to set properties on a deleted object.");
case storage::v3::Error::PROPERTIES_DISABLED:
throw QueryRuntimeException("Can't set property because properties on edges are disabled.");
case storage::v3::Error::VERTEX_HAS_EDGES:
case storage::v3::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when setting a property.");
}
}
/// Set a property `value` mapped with given `key` on a `record`.
///
/// @throw QueryRuntimeException if value cannot be set as a property value
template <AccessorWithSetProperty T>
storage::v3::PropertyValue PropsSetChecked(T *record, const storage::v3::PropertyId &key, const TypedValue &value) {
storage::v3::PropertyValue PropsSetChecked(T *record, const DbAccessor &dba, const storage::v3::PropertyId &key,
const TypedValue &value) {
try {
auto maybe_old_value = record->SetProperty(key, storage::v3::PropertyValue(value));
if (maybe_old_value.HasError()) {
switch (maybe_old_value.GetError()) {
case storage::v3::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::v3::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to set properties on a deleted object.");
case storage::v3::Error::PROPERTIES_DISABLED:
throw QueryRuntimeException("Can't set property because properties on edges are disabled.");
case storage::v3::Error::VERTEX_HAS_EDGES:
case storage::v3::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when setting a property.");
if constexpr (std::is_same_v<T, VertexAccessor>) {
const auto maybe_old_value = record->SetPropertyAndValidate(key, storage::v3::PropertyValue(value));
if (maybe_old_value.HasError()) {
std::visit(utils::Overloaded{[](const storage::v3::Error error) { HandleErrorOnPropertyUpdate(error); },
[&dba](const storage::v3::SchemaViolation &schema_violation) {
HandleSchemaViolation(schema_violation, dba);
}},
maybe_old_value.GetError());
}
return std::move(*maybe_old_value);
} else {
// No validation on edge properties
const auto maybe_old_value = record->SetProperty(key, storage::v3::PropertyValue(value));
if (maybe_old_value.HasError()) {
HandleErrorOnPropertyUpdate(maybe_old_value.GetError());
}
return std::move(*maybe_old_value);
}
return std::move(*maybe_old_value);
} catch (const TypedValueException &) {
throw QueryRuntimeException("'{}' cannot be used as a property value.", value.type());
}

View File

@ -12,6 +12,7 @@
#pragma once
#include <optional>
#include <vector>
#include <cppitertools/filter.hpp>
#include <cppitertools/imap.hpp>
@ -23,7 +24,7 @@
///////////////////////////////////////////////////////////
// Our communication layer and query engine don't mix
// very well on Centos because OpenSSL version avaialable
// very well on Centos because OpenSSL version available
// on Centos 7 include libkrb5 which has brilliant macros
// called TRUE and FALSE. For more detailed explanation go
// to memgraph.cpp.
@ -34,6 +35,8 @@
// simply undefine those macros as we're sure that libkrb5
// won't and can't be used anywhere in the query engine.
#include "storage/v3/storage.hpp"
#include "utils/logging.hpp"
#include "utils/result.hpp"
#undef FALSE
#undef TRUE
@ -51,7 +54,6 @@ class EdgeAccessor final {
public:
storage::v3::EdgeAccessor impl_;
public:
explicit EdgeAccessor(storage::v3::EdgeAccessor impl) : impl_(std::move(impl)) {}
bool IsVisible(storage::v3::View view) const { return impl_.IsVisible(view); }
@ -99,17 +101,26 @@ class VertexAccessor final {
static EdgeAccessor MakeEdgeAccessor(const storage::v3::EdgeAccessor impl) { return EdgeAccessor(impl); }
public:
explicit VertexAccessor(storage::v3::VertexAccessor impl) : impl_(impl) {}
bool IsVisible(storage::v3::View view) const { return impl_.IsVisible(view); }
auto Labels(storage::v3::View view) const { return impl_.Labels(view); }
auto PrimaryLabel(storage::v3::View view) const { return impl_.PrimaryLabel(view); }
storage::v3::Result<bool> AddLabel(storage::v3::LabelId label) { return impl_.AddLabel(label); }
storage::v3::ResultSchema<bool> AddLabelAndValidate(storage::v3::LabelId label) {
return impl_.AddLabelAndValidate(label);
}
storage::v3::Result<bool> RemoveLabel(storage::v3::LabelId label) { return impl_.RemoveLabel(label); }
storage::v3::ResultSchema<bool> RemoveLabelAndValidate(storage::v3::LabelId label) {
return impl_.RemoveLabelAndValidate(label);
}
storage::v3::Result<bool> HasLabel(storage::v3::View view, storage::v3::LabelId label) const {
return impl_.HasLabel(label, view);
}
@ -126,8 +137,13 @@ class VertexAccessor final {
return impl_.SetProperty(key, value);
}
storage::v3::Result<storage::v3::PropertyValue> RemoveProperty(storage::v3::PropertyId key) {
return SetProperty(key, storage::v3::PropertyValue());
storage::v3::ResultSchema<storage::v3::PropertyValue> SetPropertyAndValidate(
storage::v3::PropertyId key, const storage::v3::PropertyValue &value) {
return impl_.SetPropertyAndValidate(key, value);
}
storage::v3::ResultSchema<storage::v3::PropertyValue> RemovePropertyAndValidate(storage::v3::PropertyId key) {
return SetPropertyAndValidate(key, storage::v3::PropertyValue{});
}
storage::v3::Result<std::map<storage::v3::PropertyId, storage::v3::PropertyValue>> ClearProperties() {
@ -254,7 +270,18 @@ class DbAccessor final {
return VerticesIterable(accessor_->Vertices(label, property, lower, upper, view));
}
VertexAccessor InsertVertex() { return VertexAccessor(accessor_->CreateVertex()); }
// TODO Remove when query modules have been fixed
[[deprecated]] VertexAccessor InsertVertex() { return VertexAccessor(accessor_->CreateVertex()); }
storage::v3::ResultSchema<VertexAccessor> InsertVertexAndValidate(
const storage::v3::LabelId primary_label, const std::vector<storage::v3::LabelId> &labels,
const std::vector<std::pair<storage::v3::PropertyId, storage::v3::PropertyValue>> &properties) {
auto maybe_vertex_acc = accessor_->CreateVertexAndValidate(primary_label, labels, properties);
if (maybe_vertex_acc.HasError()) {
return {std::move(maybe_vertex_acc.GetError())};
}
return VertexAccessor{maybe_vertex_acc.GetValue()};
}
storage::v3::Result<EdgeAccessor> InsertEdge(VertexAccessor *from, VertexAccessor *to,
const storage::v3::EdgeTypeId &edge_type) {
@ -312,7 +339,7 @@ class DbAccessor final {
return std::optional<VertexAccessor>{};
}
return std::make_optional<VertexAccessor>(*value);
return {std::make_optional<VertexAccessor>(*value)};
}
storage::v3::PropertyId NameToProperty(const std::string_view name) { return accessor_->NameToProperty(name); }
@ -361,6 +388,10 @@ class DbAccessor final {
storage::v3::IndicesInfo ListAllIndices() const { return accessor_->ListAllIndices(); }
storage::v3::ConstraintsInfo ListAllConstraints() const { return accessor_->ListAllConstraints(); }
const storage::v3::SchemaValidator &GetSchemaValidator() const { return accessor_->GetSchemaValidator(); }
storage::v3::SchemasInfo ListAllSchemas() const { return accessor_->ListAllSchemas(); }
};
} // namespace memgraph::query::v2

View File

@ -224,4 +224,12 @@ class VersionInfoInMulticommandTxException : public QueryException {
: QueryException("Version info query not allowed in multicommand transactions.") {}
};
/**
* An exception for an illegal operation that violates schema
*/
class SchemaViolationException : public QueryRuntimeException {
public:
using QueryRuntimeException::QueryRuntimeException;
};
} // namespace memgraph::query::v2

View File

@ -134,6 +134,15 @@ cpp<#
}
cpp<#))
(defun clone-schema-property-vector (source dest)
#>cpp
${dest}.reserve(${source}.size());
for (const auto &[property_ix, property_type]: ${source}) {
${dest}.emplace_back(storage->GetPropertyIx(property_ix.name), property_type);
}
cpp<#)
;; The following index structs serve as a decoupling point of AST from
;; concrete database types. All the names are collected in AstStorage, and can
;; be indexed through these instances. This means that we can create a vector
@ -2256,7 +2265,7 @@ cpp<#
(lcp:define-enum privilege
(create delete match merge set remove index stats auth constraint
dump replication durability read_file free_memory trigger config stream module_read module_write
websocket)
websocket schema)
(:serialize))
#>cpp
AuthQuery() = default;
@ -2298,7 +2307,7 @@ const std::vector<AuthQuery::Privilege> kPrivilegesAll = {
AuthQuery::Privilege::FREE_MEMORY, AuthQuery::Privilege::TRIGGER,
AuthQuery::Privilege::CONFIG, AuthQuery::Privilege::STREAM,
AuthQuery::Privilege::MODULE_READ, AuthQuery::Privilege::MODULE_WRITE,
AuthQuery::Privilege::WEBSOCKET};
AuthQuery::Privilege::WEBSOCKET, AuthQuery::Privilege::SCHEMA};
cpp<#
(lcp:define-class info-query (query)
@ -2671,6 +2680,39 @@ cpp<#
(:serialize (:slk))
(:clone))
(lcp:define-class schema-query (query)
((action "Action" :scope :public)
(label "LabelIx" :scope :public
:slk-load (lambda (member)
#>cpp
slk::Load(&self->${member}, reader, storage);
cpp<#)
:clone (lambda (source dest)
#>cpp
${dest} = storage->GetLabelIx(${source}.name);
cpp<#))
(schema_type_map "std::vector<std::pair<PropertyIx, common::SchemaType>>"
:slk-save #'slk-save-property-map
:slk-load #'slk-load-property-map
:clone #'clone-schema-property-vector
:scope :public))
(:public
(lcp:define-enum action
(create-schema drop-schema show-schema show-schemas)
(:serialize))
#>cpp
SchemaQuery() = default;
DEFVISITABLE(QueryVisitor<void>);
cpp<#)
(:private
#>cpp
friend class AstStorage;
cpp<#)
(:serialize (:slk))
(:clone))
(lcp:pop-namespace) ;; namespace v2
(lcp:pop-namespace) ;; namespace query
(lcp:pop-namespace) ;; namespace memgraph

View File

@ -94,6 +94,7 @@ class StreamQuery;
class SettingQuery;
class VersionQuery;
class Foreach;
class SchemaQuery;
using TreeCompositeVisitor = utils::CompositeVisitor<
SingleQuery, CypherUnion, NamedExpression, OrOperator, XorOperator, AndOperator, NotOperator, AdditionOperator,
@ -125,9 +126,9 @@ class ExpressionVisitor
None, ParameterLookup, Identifier, PrimitiveLiteral, RegexMatch> {};
template <class TResult>
class QueryVisitor
: public utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery, IndexQuery, AuthQuery, InfoQuery,
ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery, FreeMemoryQuery, TriggerQuery,
IsolationLevelQuery, CreateSnapshotQuery, StreamQuery, SettingQuery, VersionQuery> {};
class QueryVisitor : public utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery, IndexQuery, AuthQuery,
InfoQuery, ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery,
FreeMemoryQuery, TriggerQuery, IsolationLevelQuery, CreateSnapshotQuery,
StreamQuery, SettingQuery, VersionQuery, SchemaQuery> {};
} // namespace memgraph::query::v2

View File

@ -17,6 +17,7 @@
#include <cstring>
#include <iterator>
#include <limits>
#include <ranges>
#include <string>
#include <tuple>
#include <type_traits>
@ -27,6 +28,7 @@
#include <boost/preprocessor/cat.hpp>
#include "common/types.hpp"
#include "query/v2/exceptions.hpp"
#include "query/v2/frontend/ast/ast.hpp"
#include "query/v2/frontend/ast/ast_visitor.hpp"
@ -275,18 +277,7 @@ antlrcpp::Any CypherMainVisitor::visitRegisterReplica(MemgraphCypher::RegisterRe
replication_query->replica_name_ = std::any_cast<std::string>(ctx->replicaName()->symbolicName()->accept(this));
if (ctx->SYNC()) {
replication_query->sync_mode_ = memgraph::query::v2::ReplicationQuery::SyncMode::SYNC;
if (ctx->WITH() && ctx->TIMEOUT()) {
if (ctx->timeout->numberLiteral()) {
// we accept both double and integer literals
replication_query->timeout_ = std::any_cast<Expression *>(ctx->timeout->accept(this));
} else {
throw SemanticException("Timeout should be a integer or double literal!");
}
}
} else if (ctx->ASYNC()) {
if (ctx->WITH() && ctx->TIMEOUT()) {
throw SyntaxException("Timeout can be set only for the SYNC replication mode!");
}
replication_query->sync_mode_ = memgraph::query::v2::ReplicationQuery::SyncMode::ASYNC;
}
@ -1358,6 +1349,7 @@ antlrcpp::Any CypherMainVisitor::visitPrivilege(MemgraphCypher::PrivilegeContext
if (ctx->MODULE_READ()) return AuthQuery::Privilege::MODULE_READ;
if (ctx->MODULE_WRITE()) return AuthQuery::Privilege::MODULE_WRITE;
if (ctx->WEBSOCKET()) return AuthQuery::Privilege::WEBSOCKET;
if (ctx->SCHEMA()) return AuthQuery::Privilege::SCHEMA;
LOG_FATAL("Should not get here - unknown privilege!");
}
@ -2364,6 +2356,93 @@ antlrcpp::Any CypherMainVisitor::visitForeach(MemgraphCypher::ForeachContext *ct
return for_each;
}
antlrcpp::Any CypherMainVisitor::visitSchemaQuery(MemgraphCypher::SchemaQueryContext *ctx) {
MG_ASSERT(ctx->children.size() == 1, "SchemaQuery should have exactly one child!");
auto *schema_query = std::any_cast<SchemaQuery *>(ctx->children[0]->accept(this));
query_ = schema_query;
return schema_query;
}
antlrcpp::Any CypherMainVisitor::visitShowSchema(MemgraphCypher::ShowSchemaContext *ctx) {
auto *schema_query = storage_->Create<SchemaQuery>();
schema_query->action_ = SchemaQuery::Action::SHOW_SCHEMA;
schema_query->label_ = AddLabel(std::any_cast<std::string>(ctx->labelName()->accept(this)));
query_ = schema_query;
return schema_query;
}
antlrcpp::Any CypherMainVisitor::visitShowSchemas(MemgraphCypher::ShowSchemasContext * /*ctx*/) {
auto *schema_query = storage_->Create<SchemaQuery>();
schema_query->action_ = SchemaQuery::Action::SHOW_SCHEMAS;
query_ = schema_query;
return schema_query;
}
antlrcpp::Any CypherMainVisitor::visitPropertyType(MemgraphCypher::PropertyTypeContext *ctx) {
MG_ASSERT(ctx->symbolicName());
const auto property_type = utils::ToLowerCase(std::any_cast<std::string>(ctx->symbolicName()->accept(this)));
if (property_type == "bool") {
return common::SchemaType::BOOL;
}
if (property_type == "string") {
return common::SchemaType::STRING;
}
if (property_type == "integer") {
return common::SchemaType::INT;
}
if (property_type == "date") {
return common::SchemaType::DATE;
}
if (property_type == "duration") {
return common::SchemaType::DURATION;
}
if (property_type == "localdatetime") {
return common::SchemaType::LOCALDATETIME;
}
if (property_type == "localtime") {
return common::SchemaType::LOCALTIME;
}
throw SyntaxException("Property type must be one of the supported types!");
}
/**
* @return Schema*
*/
antlrcpp::Any CypherMainVisitor::visitSchemaPropertyMap(MemgraphCypher::SchemaPropertyMapContext *ctx) {
std::vector<std::pair<PropertyIx, common::SchemaType>> schema_property_map;
for (auto *property_key_pair : ctx->propertyKeyTypePair()) {
auto key = std::any_cast<PropertyIx>(property_key_pair->propertyKeyName()->accept(this));
auto type = std::any_cast<common::SchemaType>(property_key_pair->propertyType()->accept(this));
if (std::ranges::find_if(schema_property_map, [&key](const auto &elem) { return elem.first == key; }) !=
schema_property_map.end()) {
throw SemanticException("Same property name can't appear twice in a schema map.");
}
schema_property_map.emplace_back(key, type);
}
return schema_property_map;
}
antlrcpp::Any CypherMainVisitor::visitCreateSchema(MemgraphCypher::CreateSchemaContext *ctx) {
auto *schema_query = storage_->Create<SchemaQuery>();
schema_query->action_ = SchemaQuery::Action::CREATE_SCHEMA;
schema_query->label_ = AddLabel(std::any_cast<std::string>(ctx->labelName()->accept(this)));
schema_query->schema_type_map_ =
std::any_cast<std::vector<std::pair<PropertyIx, common::SchemaType>>>(ctx->schemaPropertyMap()->accept(this));
query_ = schema_query;
return schema_query;
}
/**
* @return Schema*
*/
antlrcpp::Any CypherMainVisitor::visitDropSchema(MemgraphCypher::DropSchemaContext *ctx) {
auto *schema_query = storage_->Create<SchemaQuery>();
schema_query->action_ = SchemaQuery::Action::DROP_SCHEMA;
schema_query->label_ = AddLabel(std::any_cast<std::string>(ctx->labelName()->accept(this)));
query_ = schema_query;
return schema_query;
}
LabelIx CypherMainVisitor::AddLabel(const std::string &name) { return storage_->GetLabelIx(name); }
PropertyIx CypherMainVisitor::AddProperty(const std::string &name) { return storage_->GetPropertyIx(name); }

View File

@ -849,6 +849,41 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
*/
antlrcpp::Any visitForeach(MemgraphCypher::ForeachContext *ctx) override;
/**
* @return Schema*
*/
antlrcpp::Any visitPropertyType(MemgraphCypher::PropertyTypeContext *ctx) override;
/**
* @return Schema*
*/
antlrcpp::Any visitSchemaPropertyMap(MemgraphCypher::SchemaPropertyMapContext *ctx) override;
/**
* @return Schema*
*/
antlrcpp::Any visitSchemaQuery(MemgraphCypher::SchemaQueryContext *ctx) override;
/**
* @return Schema*
*/
antlrcpp::Any visitShowSchema(MemgraphCypher::ShowSchemaContext *ctx) override;
/**
* @return Schema*
*/
antlrcpp::Any visitShowSchemas(MemgraphCypher::ShowSchemasContext *ctx) override;
/**
* @return Schema*
*/
antlrcpp::Any visitCreateSchema(MemgraphCypher::CreateSchemaContext *ctx) override;
/**
* @return Schema*
*/
antlrcpp::Any visitDropSchema(MemgraphCypher::DropSchemaContext *ctx) override;
public:
Query *query() { return query_; }
const static std::string kAnonPrefix;

View File

@ -46,10 +46,10 @@ memgraphCypherKeyword : cypherKeyword
| DROP
| DUMP
| EXECUTE
| FOR
| FOREACH
| FREE
| FROM
| FOR
| FOREACH
| GLOBAL
| GRANT
| HEADER
@ -76,6 +76,8 @@ memgraphCypherKeyword : cypherKeyword
| ROLE
| ROLES
| QUOTE
| SCHEMA
| SCHEMAS
| SESSION
| SETTING
| SETTINGS
@ -122,6 +124,7 @@ query : cypherQuery
| streamQuery
| settingQuery
| versionQuery
| schemaQuery
;
authQuery : createRole
@ -192,6 +195,12 @@ settingQuery : setSetting
| showSettings
;
schemaQuery : showSchema
| showSchemas
| createSchema
| dropSchema
;
loadCsv : LOAD CSV FROM csvFile ( WITH | NO ) HEADER
( IGNORE BAD ) ?
( DELIMITER delimiter ) ?
@ -254,6 +263,7 @@ privilege : CREATE
| MODULE_READ
| MODULE_WRITE
| WEBSOCKET
| SCHEMA
;
privilegeList : privilege ( ',' privilege )* ;
@ -276,7 +286,6 @@ replicaName : symbolicName ;
socketAddress : literal ;
registerReplica : REGISTER REPLICA replicaName ( SYNC | ASYNC )
( WITH TIMEOUT timeout=literal ) ?
TO socketAddress ;
dropReplica : DROP REPLICA replicaName ;
@ -374,3 +383,17 @@ showSetting : SHOW DATABASE SETTING settingName ;
showSettings : SHOW DATABASE SETTINGS ;
versionQuery : SHOW VERSION ;
showSchema : SHOW SCHEMA ON ':' labelName ;
showSchemas : SHOW SCHEMAS ;
propertyType : symbolicName ;
propertyKeyTypePair : propertyKeyName propertyType ;
schemaPropertyMap : '(' propertyKeyTypePair ( ',' propertyKeyTypePair )* ')' ;
createSchema : CREATE SCHEMA ON ':' labelName schemaPropertyMap ;
dropSchema : DROP SCHEMA ON ':' labelName ;

View File

@ -89,6 +89,8 @@ REVOKE : R E V O K E ;
ROLE : R O L E ;
ROLES : R O L E S ;
QUOTE : Q U O T E ;
SCHEMA : S C H E M A ;
SCHEMAS : S C H E M A S ;
SERVICE_URL : S E R V I C E UNDERSCORE U R L ;
SESSION : S E S S I O N ;
SETTING : S E T T I N G ;

View File

@ -80,6 +80,8 @@ class PrivilegeExtractor : public QueryVisitor<void>, public HierarchicalTreeVis
void Visit(VersionQuery & /*version_query*/) override { AddPrivilege(AuthQuery::Privilege::STATS); }
void Visit(SchemaQuery & /*schema_query*/) override { AddPrivilege(AuthQuery::Privilege::SCHEMA); }
bool PreVisit(Create & /*unused*/) override {
AddPrivilege(AuthQuery::Privilege::CREATE);
return false;

View File

@ -204,8 +204,9 @@ const trie::Trie kKeywords = {"union",
"pulsar",
"service_url",
"version",
"websocket"
"foreach"};
"websocket",
"foreach",
"schema"};
// Unicode codepoints that are allowed at the start of the unescaped name.
const std::bitset<kBitsetSize> kUnescapedNameAllowedStarts(

View File

@ -877,6 +877,102 @@ Callback HandleSettingQuery(SettingQuery *setting_query, const Parameters &param
}
}
Callback HandleSchemaQuery(SchemaQuery *schema_query, InterpreterContext *interpreter_context,
std::vector<Notification> *notifications) {
Callback callback;
switch (schema_query->action_) {
case SchemaQuery::Action::SHOW_SCHEMAS: {
callback.header = {"label", "primary_key"};
callback.fn = [interpreter_context]() {
auto *db = interpreter_context->db;
auto schemas_info = db->ListAllSchemas();
std::vector<std::vector<TypedValue>> results;
results.reserve(schemas_info.schemas.size());
for (const auto &[label_id, schema_types] : schemas_info.schemas) {
std::vector<TypedValue> schema_info_row;
schema_info_row.reserve(3);
schema_info_row.emplace_back(db->LabelToName(label_id));
std::vector<std::string> primary_key_properties;
primary_key_properties.reserve(schema_types.size());
std::transform(schema_types.begin(), schema_types.end(), std::back_inserter(primary_key_properties),
[&db](const auto &schema_type) {
return db->PropertyToName(schema_type.property_id) +
"::" + storage::v3::SchemaTypeToString(schema_type.type);
});
schema_info_row.emplace_back(utils::Join(primary_key_properties, ", "));
results.push_back(std::move(schema_info_row));
}
return results;
};
return callback;
}
case SchemaQuery::Action::SHOW_SCHEMA: {
callback.header = {"property_name", "property_type"};
callback.fn = [interpreter_context, primary_label = schema_query->label_]() {
auto *db = interpreter_context->db;
const auto label = db->NameToLabel(primary_label.name);
const auto *schema = db->GetSchema(label);
std::vector<std::vector<TypedValue>> results;
if (schema) {
for (const auto &schema_property : schema->second) {
std::vector<TypedValue> schema_info_row;
schema_info_row.reserve(2);
schema_info_row.emplace_back(db->PropertyToName(schema_property.property_id));
schema_info_row.emplace_back(storage::v3::SchemaTypeToString(schema_property.type));
results.push_back(std::move(schema_info_row));
}
return results;
}
throw QueryException(fmt::format("Schema on label :{} not found!", primary_label.name));
};
return callback;
}
case SchemaQuery::Action::CREATE_SCHEMA: {
auto schema_type_map = schema_query->schema_type_map_;
if (schema_query->schema_type_map_.empty()) {
throw SyntaxException("One or more types have to be defined in schema definition.");
}
callback.fn = [interpreter_context, primary_label = schema_query->label_,
schema_type_map = std::move(schema_type_map)]() {
auto *db = interpreter_context->db;
const auto label = db->NameToLabel(primary_label.name);
std::vector<storage::v3::SchemaProperty> schemas_types;
schemas_types.reserve(schema_type_map.size());
for (const auto &schema_type : schema_type_map) {
auto property_id = db->NameToProperty(schema_type.first.name);
schemas_types.push_back({property_id, schema_type.second});
}
if (!db->CreateSchema(label, schemas_types)) {
throw QueryException(fmt::format("Schema on label :{} already exists!", primary_label.name));
}
return std::vector<std::vector<TypedValue>>{};
};
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::CREATE_SCHEMA,
fmt::format("Create schema on label :{}", schema_query->label_.name));
return callback;
}
case SchemaQuery::Action::DROP_SCHEMA: {
callback.fn = [interpreter_context, primary_label = schema_query->label_]() {
auto *db = interpreter_context->db;
const auto label = db->NameToLabel(primary_label.name);
if (!db->DropSchema(label)) {
throw QueryException(fmt::format("Schema on label :{} does not exist!", primary_label.name));
}
return std::vector<std::vector<TypedValue>>{};
};
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::DROP_SCHEMA,
fmt::format("Dropped schema on label :{}", schema_query->label_.name));
return callback;
}
}
return callback;
}
// Struct for lazy pulling from a vector
struct PullPlanVector {
explicit PullPlanVector(std::vector<std::vector<TypedValue>> values) : values_(std::move(values)) {}
@ -2072,6 +2168,32 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
RWType::NONE};
}
PreparedQuery PrepareSchemaQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
InterpreterContext *interpreter_context, std::vector<Notification> *notifications) {
if (in_explicit_transaction) {
throw ConstraintInMulticommandTxException();
}
auto *schema_query = utils::Downcast<SchemaQuery>(parsed_query.query);
MG_ASSERT(schema_query);
auto callback = HandleSchemaQuery(schema_query, interpreter_context, notifications);
return PreparedQuery{std::move(callback.header), std::move(parsed_query.required_privileges),
[handler = std::move(callback.fn), action = QueryHandlerResult::NOTHING,
pull_plan = std::shared_ptr<PullPlanVector>(nullptr)](
AnyStream *stream, std::optional<int> n) mutable -> std::optional<QueryHandlerResult> {
if (!pull_plan) {
auto results = handler();
pull_plan = std::make_shared<PullPlanVector>(std::move(results));
}
if (pull_plan->Pull(stream, n)) {
return action;
}
return std::nullopt;
},
RWType::NONE};
}
void Interpreter::BeginTransaction() {
const auto prepared_query = PrepareTransactionQuery("BEGIN");
prepared_query.query_handler(nullptr, {});
@ -2205,6 +2327,9 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
prepared_query = PrepareSettingQuery(std::move(parsed_query), in_explicit_transaction_, &*execution_db_accessor_);
} else if (utils::Downcast<VersionQuery>(parsed_query.query)) {
prepared_query = PrepareVersionQuery(std::move(parsed_query), in_explicit_transaction_);
} else if (utils::Downcast<SchemaQuery>(parsed_query.query)) {
prepared_query = PrepareSchemaQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_,
&query_execution->notifications);
} else {
LOG_FATAL("Should not get here -- unknown query type!");
}

View File

@ -38,6 +38,8 @@ constexpr std::string_view GetCodeString(const NotificationCode code) {
return "CreateIndex"sv;
case NotificationCode::CREATE_STREAM:
return "CreateStream"sv;
case NotificationCode::CREATE_SCHEMA:
return "CreateSchema"sv;
case NotificationCode::CHECK_STREAM:
return "CheckStream"sv;
case NotificationCode::CREATE_TRIGGER:
@ -48,6 +50,8 @@ constexpr std::string_view GetCodeString(const NotificationCode code) {
return "DropReplica"sv;
case NotificationCode::DROP_INDEX:
return "DropIndex"sv;
case NotificationCode::DROP_SCHEMA:
return "DropSchema"sv;
case NotificationCode::DROP_STREAM:
return "DropStream"sv;
case NotificationCode::DROP_TRIGGER:
@ -68,6 +72,10 @@ constexpr std::string_view GetCodeString(const NotificationCode code) {
return "ReplicaPortWarning"sv;
case NotificationCode::SET_REPLICA:
return "SetReplica"sv;
case NotificationCode::SHOW_SCHEMA:
return "ShowSchema"sv;
case NotificationCode::SHOW_SCHEMAS:
return "ShowSchemas"sv;
case NotificationCode::START_STREAM:
return "StartStream"sv;
case NotificationCode::START_ALL_STREAMS:

View File

@ -26,12 +26,14 @@ enum class SeverityLevel : uint8_t { INFO, WARNING };
enum class NotificationCode : uint8_t {
CREATE_CONSTRAINT,
CREATE_INDEX,
CREATE_SCHEMA,
CHECK_STREAM,
CREATE_STREAM,
CREATE_TRIGGER,
DROP_CONSTRAINT,
DROP_INDEX,
DROP_REPLICA,
DROP_SCHEMA,
DROP_STREAM,
DROP_TRIGGER,
EXISTANT_INDEX,
@ -42,6 +44,8 @@ enum class NotificationCode : uint8_t {
REPLICA_PORT_WARNING,
REGISTER_REPLICA,
SET_REPLICA,
SHOW_SCHEMA,
SHOW_SCHEMAS,
START_STREAM,
START_ALL_STREAMS,
STOP_STREAM,

View File

@ -52,6 +52,7 @@
#include "utils/readable_size.hpp"
#include "utils/string.hpp"
#include "utils/temporal.hpp"
#include "utils/variant_helpers.hpp"
// macro for the default implementation of LogicalOperator::Accept
// that accepts the visitor and visits it's input_ operator
@ -174,45 +175,56 @@ CreateNode::CreateNode(const std::shared_ptr<LogicalOperator> &input, const Node
// Creates a vertex on this GraphDb. Returns a reference to vertex placed on the
// frame.
VertexAccessor &CreateLocalVertex(const NodeCreationInfo &node_info, Frame *frame, ExecutionContext &context) {
VertexAccessor &CreateLocalVertexAtomically(const NodeCreationInfo &node_info, Frame *frame,
ExecutionContext &context) {
auto &dba = *context.db_accessor;
auto new_node = dba.InsertVertex();
context.execution_stats[ExecutionStats::Key::CREATED_NODES] += 1;
for (auto label : node_info.labels) {
auto maybe_error = new_node.AddLabel(label);
if (maybe_error.HasError()) {
switch (maybe_error.GetError()) {
case storage::v3::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::v3::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to set a label on a deleted node.");
case storage::v3::Error::VERTEX_HAS_EDGES:
case storage::v3::Error::PROPERTIES_DISABLED:
case storage::v3::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when setting a label.");
}
}
context.execution_stats[ExecutionStats::Key::CREATED_LABELS] += 1;
}
// Evaluator should use the latest accessors, as modified in this query, when
// setting properties on new nodes.
ExpressionEvaluator evaluator(frame, context.symbol_table, context.evaluation_context, context.db_accessor,
storage::v3::View::NEW);
// TODO: PropsSetChecked allocates a PropertyValue, make it use context.memory
// when we update PropertyValue with custom allocator.
std::vector<std::pair<storage::v3::PropertyId, storage::v3::PropertyValue>> properties;
if (const auto *node_info_properties = std::get_if<PropertiesMapList>(&node_info.properties)) {
properties.reserve(node_info_properties->size());
for (const auto &[key, value_expression] : *node_info_properties) {
PropsSetChecked(&new_node, key, value_expression->Accept(evaluator));
properties.emplace_back(key, storage::v3::PropertyValue(value_expression->Accept(evaluator)));
}
} else {
auto property_map = evaluator.Visit(*std::get<ParameterLookup *>(node_info.properties));
for (const auto &[key, value] : property_map.ValueMap()) {
auto property_map = evaluator.Visit(*std::get<ParameterLookup *>(node_info.properties)).ValueMap();
properties.reserve(property_map.size());
for (const auto &[key, value] : property_map) {
auto property_id = dba.NameToProperty(key);
PropsSetChecked(&new_node, property_id, value);
properties.emplace_back(property_id, value);
}
}
// TODO Remove later on since that will be enforced from grammar side
MG_ASSERT(!node_info.labels.empty(), "There must be at least one label!");
const auto primary_label = node_info.labels[0];
std::vector<storage::v3::LabelId> secondary_labels(node_info.labels.begin() + 1, node_info.labels.end());
auto maybe_new_node = dba.InsertVertexAndValidate(primary_label, secondary_labels, properties);
if (maybe_new_node.HasError()) {
std::visit(utils::Overloaded{[&dba](const storage::v3::SchemaViolation &schema_violation) {
HandleSchemaViolation(schema_violation, dba);
},
[](const storage::v3::Error error) {
switch (error) {
case storage::v3::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::v3::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to set a label on a deleted node.");
case storage::v3::Error::VERTEX_HAS_EDGES:
case storage::v3::Error::PROPERTIES_DISABLED:
case storage::v3::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when setting a label.");
}
}},
maybe_new_node.GetError());
}
(*frame)[node_info.symbol] = new_node;
context.execution_stats[ExecutionStats::Key::CREATED_NODES] += 1;
(*frame)[node_info.symbol] = *maybe_new_node;
return (*frame)[node_info.symbol].ValueVertex();
}
@ -237,7 +249,7 @@ bool CreateNode::CreateNodeCursor::Pull(Frame &frame, ExecutionContext &context)
SCOPED_PROFILE_OP("CreateNode");
if (input_cursor_->Pull(frame, context)) {
auto created_vertex = CreateLocalVertex(self_.node_info_, &frame, context);
auto created_vertex = CreateLocalVertexAtomically(self_.node_info_, &frame, context);
if (context.trigger_context_collector) {
context.trigger_context_collector->RegisterCreatedObject(created_vertex);
}
@ -286,13 +298,13 @@ EdgeAccessor CreateEdge(const EdgeCreationInfo &edge_info, DbAccessor *dba, Vert
auto &edge = *maybe_edge;
if (const auto *properties = std::get_if<PropertiesMapList>(&edge_info.properties)) {
for (const auto &[key, value_expression] : *properties) {
PropsSetChecked(&edge, key, value_expression->Accept(*evaluator));
PropsSetChecked(&edge, *dba, key, value_expression->Accept(*evaluator));
}
} else {
auto property_map = evaluator->Visit(*std::get<ParameterLookup *>(edge_info.properties));
for (const auto &[key, value] : property_map.ValueMap()) {
auto property_id = dba->NameToProperty(key);
PropsSetChecked(&edge, property_id, value);
PropsSetChecked(&edge, *dba, property_id, value);
}
}
@ -368,13 +380,12 @@ VertexAccessor &CreateExpand::CreateExpandCursor::OtherVertex(Frame &frame, Exec
TypedValue &dest_node_value = frame[self_.node_info_.symbol];
ExpectType(self_.node_info_.symbol, dest_node_value, TypedValue::Type::Vertex);
return dest_node_value.ValueVertex();
} else {
auto &created_vertex = CreateLocalVertex(self_.node_info_, &frame, context);
if (context.trigger_context_collector) {
context.trigger_context_collector->RegisterCreatedObject(created_vertex);
}
return created_vertex;
}
auto &created_vertex = CreateLocalVertexAtomically(self_.node_info_, &frame, context);
if (context.trigger_context_collector) {
context.trigger_context_collector->RegisterCreatedObject(created_vertex);
}
return created_vertex;
}
template <class TVerticesFun>
@ -2050,7 +2061,7 @@ bool SetProperty::SetPropertyCursor::Pull(Frame &frame, ExecutionContext &contex
switch (lhs.type()) {
case TypedValue::Type::Vertex: {
auto old_value = PropsSetChecked(&lhs.ValueVertex(), self_.property_, rhs);
auto old_value = PropsSetChecked(&lhs.ValueVertex(), *context.db_accessor, self_.property_, rhs);
context.execution_stats[ExecutionStats::Key::UPDATED_PROPERTIES] += 1;
if (context.trigger_context_collector) {
// rhs cannot be moved because it was created with the allocator that is only valid during current pull
@ -2060,7 +2071,7 @@ bool SetProperty::SetPropertyCursor::Pull(Frame &frame, ExecutionContext &contex
break;
}
case TypedValue::Type::Edge: {
auto old_value = PropsSetChecked(&lhs.ValueEdge(), self_.property_, rhs);
auto old_value = PropsSetChecked(&lhs.ValueEdge(), *context.db_accessor, self_.property_, rhs);
context.execution_stats[ExecutionStats::Key::UPDATED_PROPERTIES] += 1;
if (context.trigger_context_collector) {
// rhs cannot be moved because it was created with the allocator that is only valid during current pull
@ -2216,7 +2227,7 @@ void SetPropertiesOnRecord(TRecordAccessor *record, const TypedValue &rhs, SetPr
case TypedValue::Type::Map: {
for (const auto &kv : rhs.ValueMap()) {
auto key = context->db_accessor->NameToProperty(kv.first);
auto old_value = PropsSetChecked(record, key, kv.second);
auto old_value = PropsSetChecked(record, *context->db_accessor, key, kv.second);
if (should_register_change) {
register_set_property(std::move(old_value), key, kv.second);
}
@ -2300,22 +2311,31 @@ bool SetLabels::SetLabelsCursor::Pull(Frame &frame, ExecutionContext &context) {
// Skip setting labels on Null (can occur in optional match).
if (vertex_value.IsNull()) return true;
ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
auto &dba = *context.db_accessor;
auto &vertex = vertex_value.ValueVertex();
for (auto label : self_.labels_) {
auto maybe_value = vertex.AddLabel(label);
for (const auto label : self_.labels_) {
auto maybe_value = vertex.AddLabelAndValidate(label);
if (maybe_value.HasError()) {
switch (maybe_value.GetError()) {
case storage::v3::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::v3::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to set a label on a deleted node.");
case storage::v3::Error::VERTEX_HAS_EDGES:
case storage::v3::Error::PROPERTIES_DISABLED:
case storage::v3::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when setting a label.");
}
std::visit(utils::Overloaded{[](const storage::v3::Error error) {
switch (error) {
case storage::v3::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::v3::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to set a label on a deleted node.");
case storage::v3::Error::VERTEX_HAS_EDGES:
case storage::v3::Error::PROPERTIES_DISABLED:
case storage::v3::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when setting a label.");
}
},
[&dba](const storage::v3::SchemaViolation schema_violation) {
HandleSchemaViolation(schema_violation, dba);
}},
maybe_value.GetError());
}
context.execution_stats[ExecutionStats::Key::CREATED_LABELS]++;
if (context.trigger_context_collector && *maybe_value) {
context.trigger_context_collector->RegisterSetVertexLabel(vertex, label);
}
@ -2358,26 +2378,11 @@ bool RemoveProperty::RemovePropertyCursor::Pull(Frame &frame, ExecutionContext &
TypedValue lhs = self_.lhs_->expression_->Accept(evaluator);
auto remove_prop = [property = self_.property_, &context](auto *record) {
auto maybe_old_value = record->RemoveProperty(property);
if (maybe_old_value.HasError()) {
switch (maybe_old_value.GetError()) {
case storage::v3::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to remove a property on a deleted graph element.");
case storage::v3::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::v3::Error::PROPERTIES_DISABLED:
throw QueryRuntimeException(
"Can't remove property because properties on edges are "
"disabled.");
case storage::v3::Error::VERTEX_HAS_EDGES:
case storage::v3::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when removing property.");
}
}
auto old_value = PropsSetChecked(record, *context.db_accessor, property, TypedValue{});
if (context.trigger_context_collector) {
context.trigger_context_collector->RegisterRemovedObjectProperty(*record, property,
TypedValue(std::move(*maybe_old_value)));
TypedValue(std::move(old_value)));
}
};
@ -2431,18 +2436,25 @@ bool RemoveLabels::RemoveLabelsCursor::Pull(Frame &frame, ExecutionContext &cont
ExpectType(self_.input_symbol_, vertex_value, TypedValue::Type::Vertex);
auto &vertex = vertex_value.ValueVertex();
for (auto label : self_.labels_) {
auto maybe_value = vertex.RemoveLabel(label);
auto maybe_value = vertex.RemoveLabelAndValidate(label);
if (maybe_value.HasError()) {
switch (maybe_value.GetError()) {
case storage::v3::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::v3::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to remove labels from a deleted node.");
case storage::v3::Error::VERTEX_HAS_EDGES:
case storage::v3::Error::PROPERTIES_DISABLED:
case storage::v3::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when removing labels from a node.");
}
std::visit(
utils::Overloaded{[](const storage::v3::Error error) {
switch (error) {
case storage::v3::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::v3::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to remove labels from a deleted node.");
case storage::v3::Error::VERTEX_HAS_EDGES:
case storage::v3::Error::PROPERTIES_DISABLED:
case storage::v3::Error::NONEXISTENT_OBJECT:
throw QueryRuntimeException("Unexpected error when removing labels from a node.");
}
},
[&context](const storage::v3::SchemaViolation &schema_violation) {
HandleSchemaViolation(schema_violation, *context.db_accessor);
}},
maybe_value.GetError());
}
context.execution_stats[ExecutionStats::Key::DELETED_LABELS] += 1;

View File

@ -10,8 +10,6 @@ set(storage_v2_src_files
indices.cpp
property_store.cpp
vertex_accessor.cpp
schemas.cpp
schema_validator.cpp
storage.cpp)
##### Replication #####

View File

@ -16,7 +16,6 @@
#include <map>
#include "storage/v2/mvcc.hpp"
#include "storage/v2/vertex.hpp"
#include "utils/logging.hpp"
namespace memgraph::storage {
@ -60,7 +59,7 @@ bool LastCommittedVersionHasLabelProperty(const Vertex &vertex, LabelId label, c
std::lock_guard<utils::SpinLock> guard(vertex.lock);
delta = vertex.delta;
deleted = vertex.deleted;
has_label = VertexHasLabel(vertex, label);
has_label = utils::Contains(vertex.labels, label);
size_t i = 0;
for (const auto &property : properties) {
@ -143,7 +142,7 @@ bool AnyVersionHasLabelProperty(const Vertex &vertex, LabelId label, const std::
Delta *delta;
{
std::lock_guard<utils::SpinLock> guard(vertex.lock);
has_label = VertexHasLabel(vertex, label);
has_label = utils::Contains(vertex.labels, label);
deleted = vertex.deleted;
delta = vertex.delta;
@ -268,7 +267,7 @@ bool UniqueConstraints::Entry::operator==(const std::vector<PropertyValue> &rhs)
void UniqueConstraints::UpdateBeforeCommit(const Vertex *vertex, const Transaction &tx) {
for (auto &[label_props, storage] : constraints_) {
if (!VertexHasLabel(*vertex, label_props.first)) {
if (!utils::Contains(vertex->labels, label_props.first)) {
continue;
}
auto values = ExtractPropertyValues(*vertex, label_props.second);
@ -302,7 +301,7 @@ utils::BasicResult<ConstraintViolation, UniqueConstraints::CreationStatus> Uniqu
auto acc = constraint->second.access();
for (const Vertex &vertex : vertices) {
if (vertex.deleted || !VertexHasLabel(vertex, label)) {
if (vertex.deleted || !utils::Contains(vertex.labels, label)) {
continue;
}
auto values = ExtractPropertyValues(vertex, properties);
@ -353,7 +352,7 @@ std::optional<ConstraintViolation> UniqueConstraints::Validate(const Vertex &ver
for (const auto &[label_props, storage] : constraints_) {
const auto &label = label_props.first;
const auto &properties = label_props.second;
if (!VertexHasLabel(vertex, label)) {
if (!utils::Contains(vertex.labels, label)) {
continue;
}

View File

@ -158,7 +158,7 @@ inline utils::BasicResult<ConstraintViolation, bool> CreateExistenceConstraint(
return false;
}
for (const auto &vertex : vertices) {
if (!vertex.deleted && VertexHasLabel(vertex, label) && !vertex.properties.HasProperty(property)) {
if (!vertex.deleted && utils::Contains(vertex.labels, label) && !vertex.properties.HasProperty(property)) {
return ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label, std::set<PropertyId>{property}};
}
}
@ -184,7 +184,7 @@ inline bool DropExistenceConstraint(Constraints *constraints, LabelId label, Pro
[[nodiscard]] inline std::optional<ConstraintViolation> ValidateExistenceConstraints(const Vertex &vertex,
const Constraints &constraints) {
for (const auto &[label, property] : constraints.existence_constraints) {
if (!vertex.deleted && VertexHasLabel(vertex, label) && !vertex.properties.HasProperty(property)) {
if (!vertex.deleted && utils::Contains(vertex.labels, label) && !vertex.properties.HasProperty(property)) {
return ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label, std::set<PropertyId>{property}};
}
}

View File

@ -628,9 +628,8 @@ RecoveredSnapshot LoadSnapshot(const std::filesystem::path &path, utils::SkipLis
void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snapshot_directory,
const std::filesystem::path &wal_directory, uint64_t snapshot_retention_count,
utils::SkipList<Vertex> *vertices, utils::SkipList<Edge> *edges, NameIdMapper *name_id_mapper,
Indices *indices, Constraints *constraints, Config::Items items,
const SchemaValidator &schema_validator, const std::string &uuid, const std::string_view epoch_id,
const std::deque<std::pair<std::string, uint64_t>> &epoch_history,
Indices *indices, Constraints *constraints, Config::Items items, const std::string &uuid,
const std::string_view epoch_id, const std::deque<std::pair<std::string, uint64_t>> &epoch_history,
utils::FileRetainer *file_retainer) {
// Ensure that the storage directory exists.
utils::EnsureDirOrDie(snapshot_directory);
@ -714,9 +713,8 @@ void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snaps
// type and invalid from/to pointers because we don't know them here,
// but that isn't an issue because we won't use that part of the API
// here.
// TODO(jbajic) Fix snapshot with new schema rules
auto ea = EdgeAccessor{edge_ref, EdgeTypeId::FromUint(0UL), nullptr, nullptr, transaction, indices, constraints,
items, schema_validator};
auto ea =
EdgeAccessor{edge_ref, EdgeTypeId::FromUint(0UL), nullptr, nullptr, transaction, indices, constraints, items};
// Get edge data.
auto maybe_props = ea.Properties(View::OLD);
@ -744,7 +742,7 @@ void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snaps
auto acc = vertices->access();
for (auto &vertex : acc) {
// The visibility check is implemented for vertices so we use it here.
auto va = VertexAccessor::Create(&vertex, transaction, indices, constraints, items, schema_validator, View::OLD);
auto va = VertexAccessor::Create(&vertex, transaction, indices, constraints, items, View::OLD);
if (!va) continue;
// Get vertex data.

View File

@ -21,7 +21,6 @@
#include "storage/v2/edge.hpp"
#include "storage/v2/indices.hpp"
#include "storage/v2/name_id_mapper.hpp"
#include "storage/v2/schema_validator.hpp"
#include "storage/v2/transaction.hpp"
#include "storage/v2/vertex.hpp"
#include "utils/file_locker.hpp"
@ -69,9 +68,8 @@ RecoveredSnapshot LoadSnapshot(const std::filesystem::path &path, utils::SkipLis
void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snapshot_directory,
const std::filesystem::path &wal_directory, uint64_t snapshot_retention_count,
utils::SkipList<Vertex> *vertices, utils::SkipList<Edge> *edges, NameIdMapper *name_id_mapper,
Indices *indices, Constraints *constraints, Config::Items items,
const SchemaValidator &schema_validator, const std::string &uuid, std::string_view epoch_id,
const std::deque<std::pair<std::string, uint64_t>> &epoch_history,
Indices *indices, Constraints *constraints, Config::Items items, const std::string &uuid,
std::string_view epoch_id, const std::deque<std::pair<std::string, uint64_t>> &epoch_history,
utils::FileRetainer *file_retainer);
} // namespace memgraph::storage::durability

View File

@ -15,7 +15,6 @@
#include "storage/v2/mvcc.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/schema_validator.hpp"
#include "storage/v2/vertex_accessor.hpp"
#include "utils/memory_tracker.hpp"
@ -55,11 +54,11 @@ bool EdgeAccessor::IsVisible(const View view) const {
}
VertexAccessor EdgeAccessor::FromVertex() const {
return VertexAccessor{from_vertex_, transaction_, indices_, constraints_, config_, *schema_validator_};
return VertexAccessor{from_vertex_, transaction_, indices_, constraints_, config_};
}
VertexAccessor EdgeAccessor::ToVertex() const {
return VertexAccessor{to_vertex_, transaction_, indices_, constraints_, config_, *schema_validator_};
return VertexAccessor{to_vertex_, transaction_, indices_, constraints_, config_};
}
Result<storage::PropertyValue> EdgeAccessor::SetProperty(PropertyId property, const PropertyValue &value) {

View File

@ -18,7 +18,6 @@
#include "storage/v2/config.hpp"
#include "storage/v2/result.hpp"
#include "storage/v2/schema_validator.hpp"
#include "storage/v2/transaction.hpp"
#include "storage/v2/view.hpp"
@ -35,8 +34,7 @@ class EdgeAccessor final {
public:
EdgeAccessor(EdgeRef edge, EdgeTypeId edge_type, Vertex *from_vertex, Vertex *to_vertex, Transaction *transaction,
Indices *indices, Constraints *constraints, Config::Items config,
const SchemaValidator &schema_validator, bool for_deleted = false)
Indices *indices, Constraints *constraints, Config::Items config, bool for_deleted = false)
: edge_(edge),
edge_type_(edge_type),
from_vertex_(from_vertex),
@ -45,7 +43,6 @@ class EdgeAccessor final {
indices_(indices),
constraints_(constraints),
config_(config),
schema_validator_{&schema_validator},
for_deleted_(for_deleted) {}
/// @return true if the object is visible from the current transaction
@ -95,7 +92,6 @@ class EdgeAccessor final {
Indices *indices_;
Constraints *constraints_;
Config::Items config_;
const SchemaValidator *schema_validator_;
// if the accessor was created for a deleted edge.
// Accessor behaves differently for some methods based on this

View File

@ -14,7 +14,6 @@
#include "storage/v2/mvcc.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/schema_validator.hpp"
#include "utils/bound.hpp"
#include "utils/logging.hpp"
#include "utils/memory_tracker.hpp"
@ -328,7 +327,7 @@ void LabelIndex::RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp) {
LabelIndex::Iterable::Iterator::Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator)
: self_(self),
index_iterator_(index_iterator),
current_vertex_accessor_(nullptr, nullptr, nullptr, nullptr, self_->config_, *self_->schema_validator_),
current_vertex_accessor_(nullptr, nullptr, nullptr, nullptr, self_->config_),
current_vertex_(nullptr) {
AdvanceUntilValid();
}
@ -346,8 +345,8 @@ void LabelIndex::Iterable::Iterator::AdvanceUntilValid() {
}
if (CurrentVersionHasLabel(*index_iterator_->vertex, self_->label_, self_->transaction_, self_->view_)) {
current_vertex_ = index_iterator_->vertex;
current_vertex_accessor_ = VertexAccessor{current_vertex_, self_->transaction_, self_->indices_,
self_->constraints_, self_->config_, *self_->schema_validator_};
current_vertex_accessor_ =
VertexAccessor{current_vertex_, self_->transaction_, self_->indices_, self_->constraints_, self_->config_};
break;
}
}
@ -355,15 +354,14 @@ void LabelIndex::Iterable::Iterator::AdvanceUntilValid() {
LabelIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label, View view,
Transaction *transaction, Indices *indices, Constraints *constraints,
Config::Items config, const SchemaValidator &schema_validator)
Config::Items config)
: index_accessor_(std::move(index_accessor)),
label_(label),
view_(view),
transaction_(transaction),
indices_(indices),
constraints_(constraints),
config_(config),
schema_validator_(&schema_validator) {}
config_(config) {}
void LabelIndex::RunGC() {
for (auto &index_entry : index_) {
@ -480,7 +478,7 @@ void LabelPropertyIndex::RemoveObsoleteEntries(uint64_t oldest_active_start_time
LabelPropertyIndex::Iterable::Iterator::Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator)
: self_(self),
index_iterator_(index_iterator),
current_vertex_accessor_(nullptr, nullptr, nullptr, nullptr, self_->config_, *self_->schema_validator_),
current_vertex_accessor_(nullptr, nullptr, nullptr, nullptr, self_->config_),
current_vertex_(nullptr) {
AdvanceUntilValid();
}
@ -519,8 +517,8 @@ void LabelPropertyIndex::Iterable::Iterator::AdvanceUntilValid() {
if (CurrentVersionHasLabelProperty(*index_iterator_->vertex, self_->label_, self_->property_,
index_iterator_->value, self_->transaction_, self_->view_)) {
current_vertex_ = index_iterator_->vertex;
current_vertex_accessor_ = VertexAccessor(current_vertex_, self_->transaction_, self_->indices_,
self_->constraints_, self_->config_, *self_->schema_validator_);
current_vertex_accessor_ =
VertexAccessor(current_vertex_, self_->transaction_, self_->indices_, self_->constraints_, self_->config_);
break;
}
}
@ -543,7 +541,7 @@ LabelPropertyIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_ac
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view,
Transaction *transaction, Indices *indices, Constraints *constraints,
Config::Items config, const SchemaValidator &schema_validator)
Config::Items config)
: index_accessor_(std::move(index_accessor)),
label_(label),
property_(property),
@ -553,8 +551,7 @@ LabelPropertyIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_ac
transaction_(transaction),
indices_(indices),
constraints_(constraints),
config_(config),
schema_validator_(&schema_validator) {
config_(config) {
// We have to fix the bounds that the user provided to us. If the user
// provided only one bound we should make sure that only values of that type
// are returned by the iterator. We ensure this by supplying either an

View File

@ -17,7 +17,6 @@
#include "storage/v2/config.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/schema_validator.hpp"
#include "storage/v2/transaction.hpp"
#include "storage/v2/vertex_accessor.hpp"
#include "utils/bound.hpp"
@ -52,8 +51,8 @@ class LabelIndex {
};
public:
LabelIndex(Indices *indices, Constraints *constraints, Config::Items config, const SchemaValidator &schema_validator)
: indices_(indices), constraints_(constraints), config_(config), schema_validator_{&schema_validator} {}
LabelIndex(Indices *indices, Constraints *constraints, Config::Items config)
: indices_(indices), constraints_(constraints), config_(config) {}
/// @throw std::bad_alloc
void UpdateOnAddLabel(LabelId label, Vertex *vertex, const Transaction &tx);
@ -73,7 +72,7 @@ class LabelIndex {
class Iterable {
public:
Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label, View view, Transaction *transaction,
Indices *indices, Constraints *constraints, Config::Items config, const SchemaValidator &schema_validator);
Indices *indices, Constraints *constraints, Config::Items config);
class Iterator {
public:
@ -106,14 +105,13 @@ class LabelIndex {
Indices *indices_;
Constraints *constraints_;
Config::Items config_;
const SchemaValidator *schema_validator_;
};
/// Returns an self with vertices visible from the given transaction.
Iterable Vertices(LabelId label, View view, Transaction *transaction) {
auto it = index_.find(label);
MG_ASSERT(it != index_.end(), "Index for label {} doesn't exist", label.AsUint());
return {it->second.access(), label, view, transaction, indices_, constraints_, config_, *schema_validator_};
return {it->second.access(), label, view, transaction, indices_, constraints_, config_};
}
int64_t ApproximateVertexCount(LabelId label) {
@ -131,7 +129,6 @@ class LabelIndex {
Indices *indices_;
Constraints *constraints_;
Config::Items config_;
const SchemaValidator *schema_validator_;
};
class LabelPropertyIndex {
@ -149,9 +146,8 @@ class LabelPropertyIndex {
};
public:
LabelPropertyIndex(Indices *indices, Constraints *constraints, Config::Items config,
const SchemaValidator &schema_validator)
: indices_(indices), constraints_(constraints), config_(config), schema_validator_{&schema_validator} {}
LabelPropertyIndex(Indices *indices, Constraints *constraints, Config::Items config)
: indices_(indices), constraints_(constraints), config_(config) {}
/// @throw std::bad_alloc
void UpdateOnAddLabel(LabelId label, Vertex *vertex, const Transaction &tx);
@ -175,7 +171,7 @@ class LabelPropertyIndex {
Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label, PropertyId property,
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Transaction *transaction,
Indices *indices, Constraints *constraints, Config::Items config, const SchemaValidator &schema_validator);
Indices *indices, Constraints *constraints, Config::Items config);
class Iterator {
public:
@ -212,17 +208,16 @@ class LabelPropertyIndex {
Indices *indices_;
Constraints *constraints_;
Config::Items config_;
const SchemaValidator *schema_validator_;
};
Iterable Vertices(LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Transaction *transaction,
const SchemaValidator &schema_validator_) {
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view,
Transaction *transaction) {
auto it = index_.find({label, property});
MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(),
property.AsUint());
return {it->second.access(), label, property, lower_bound, upper_bound, view,
transaction, indices_, constraints_, config_, schema_validator_};
return {it->second.access(), label, property, lower_bound, upper_bound, view,
transaction, indices_, constraints_, config_};
}
int64_t ApproximateVertexCount(LabelId label, PropertyId property) const {
@ -251,13 +246,11 @@ class LabelPropertyIndex {
Indices *indices_;
Constraints *constraints_;
Config::Items config_;
const SchemaValidator *schema_validator_;
};
struct Indices {
Indices(Constraints *constraints, Config::Items config, const SchemaValidator &schema_validator)
: label_index(this, constraints, config, schema_validator),
label_property_index(this, constraints, config, schema_validator) {}
Indices(Constraints *constraints, Config::Items config)
: label_index(this, constraints, config), label_property_index(this, constraints, config) {}
// Disable copy and move because members hold pointer to `this`.
Indices(const Indices &) = delete;

View File

@ -166,10 +166,9 @@ void Storage::ReplicationServer::SnapshotHandler(slk::Reader *req_reader, slk::B
storage_->edges_.clear();
storage_->constraints_ = Constraints();
storage_->indices_.label_index =
LabelIndex(&storage_->indices_, &storage_->constraints_, storage_->config_.items, storage_->schema_validator_);
storage_->indices_.label_property_index = LabelPropertyIndex(&storage_->indices_, &storage_->constraints_,
storage_->config_.items, storage_->schema_validator_);
storage_->indices_.label_index = LabelIndex(&storage_->indices_, &storage_->constraints_, storage_->config_.items);
storage_->indices_.label_property_index =
LabelPropertyIndex(&storage_->indices_, &storage_->constraints_, storage_->config_.items);
try {
spdlog::debug("Loading snapshot");
auto recovered_snapshot = durability::LoadSnapshot(*maybe_snapshot_path, &storage_->vertices_, &storage_->edges_,
@ -474,8 +473,7 @@ uint64_t Storage::ReplicationServer::ReadAndApplyDelta(durability::BaseDecoder *
&transaction->transaction_,
&storage_->indices_,
&storage_->constraints_,
storage_->config_.items,
storage_->schema_validator_};
storage_->config_.items};
auto ret = ea.SetProperty(transaction->NameToProperty(delta.vertex_edge_set_property.property),
delta.vertex_edge_set_property.value);

View File

@ -14,7 +14,6 @@
#include <atomic>
#include <memory>
#include <mutex>
#include <optional>
#include <variant>
#include <gflags/gflags.h>
@ -27,22 +26,17 @@
#include "storage/v2/durability/snapshot.hpp"
#include "storage/v2/durability/wal.hpp"
#include "storage/v2/edge_accessor.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/indices.hpp"
#include "storage/v2/mvcc.hpp"
#include "storage/v2/replication/config.hpp"
#include "storage/v2/replication/enums.hpp"
#include "storage/v2/replication/replication_persistence_helper.hpp"
#include "storage/v2/schema_validator.hpp"
#include "storage/v2/schemas.hpp"
#include "storage/v2/transaction.hpp"
#include "storage/v2/vertex_accessor.hpp"
#include "utils/exceptions.hpp"
#include "utils/file.hpp"
#include "utils/logging.hpp"
#include "utils/memory_tracker.hpp"
#include "utils/message.hpp"
#include "utils/result.hpp"
#include "utils/rw_lock.hpp"
#include "utils/spin_lock.hpp"
#include "utils/stat.hpp"
@ -76,9 +70,9 @@ std::string RegisterReplicaErrorToString(Storage::RegisterReplicaError error) {
auto AdvanceToVisibleVertex(utils::SkipList<Vertex>::Iterator it, utils::SkipList<Vertex>::Iterator end,
std::optional<VertexAccessor> *vertex, Transaction *tx, View view, Indices *indices,
Constraints *constraints, Config::Items config, const SchemaValidator &schema_validator) {
Constraints *constraints, Config::Items config) {
while (it != end) {
*vertex = VertexAccessor::Create(&*it, tx, indices, constraints, config, schema_validator, view);
*vertex = VertexAccessor::Create(&*it, tx, indices, constraints, config, view);
if (!*vertex) {
++it;
continue;
@ -91,14 +85,14 @@ auto AdvanceToVisibleVertex(utils::SkipList<Vertex>::Iterator it, utils::SkipLis
AllVerticesIterable::Iterator::Iterator(AllVerticesIterable *self, utils::SkipList<Vertex>::Iterator it)
: self_(self),
it_(AdvanceToVisibleVertex(it, self->vertices_accessor_.end(), &self->vertex_, self->transaction_, self->view_,
self->indices_, self_->constraints_, self->config_, *self->schema_validator_)) {}
self->indices_, self_->constraints_, self->config_)) {}
VertexAccessor AllVerticesIterable::Iterator::operator*() const { return *self_->vertex_; }
AllVerticesIterable::Iterator &AllVerticesIterable::Iterator::operator++() {
++it_;
it_ = AdvanceToVisibleVertex(it_, self_->vertices_accessor_.end(), &self_->vertex_, self_->transaction_, self_->view_,
self_->indices_, self_->constraints_, self_->config_, *self_->schema_validator_);
self_->indices_, self_->constraints_, self_->config_);
return *this;
}
@ -319,8 +313,7 @@ bool VerticesIterable::Iterator::operator==(const Iterator &other) const {
}
Storage::Storage(Config config)
: schema_validator_(schemas_),
indices_(&constraints_, config.items, schema_validator_),
: indices_(&constraints_, config.items),
isolation_level_(config.transaction.isolation_level),
config_(config),
snapshot_directory_(config_.durability.storage_directory / durability::kSnapshotDirectory),
@ -492,8 +485,7 @@ Storage::Accessor::~Accessor() {
FinalizeTransaction();
}
// TODO Remove when import csv is fixed
[[deprecated]] VertexAccessor Storage::Accessor::CreateVertex() {
VertexAccessor Storage::Accessor::CreateVertex() {
OOMExceptionEnabler oom_exception;
auto gid = storage_->vertex_id_.fetch_add(1, std::memory_order_acq_rel);
auto acc = storage_->vertices_.access();
@ -501,71 +493,34 @@ Storage::Accessor::~Accessor() {
auto [it, inserted] = acc.insert(Vertex{storage::Gid::FromUint(gid), delta});
MG_ASSERT(inserted, "The vertex must be inserted here!");
MG_ASSERT(it != acc.end(), "Invalid Vertex accessor!");
delta->prev.Set(&*it);
return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_, storage_->schema_validator_};
return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_};
}
// TODO Remove when replication is fixed
VertexAccessor Storage::Accessor::CreateVertex(storage::Gid gid) {
OOMExceptionEnabler oom_exception;
// NOTE: When we update the next `vertex_id_` here we perform a RMW
// (read-modify-write) operation that ISN'T atomic! But, that isn't an issue
// because this function is only called from the replication delta applier
// that runs single-threaded and while this instance is set-up to apply
// that runs single-threadedly and while this instance is set-up to apply
// threads (it is the replica), it is guaranteed that no other writes are
// possible.
storage_->vertex_id_.store(std::max(storage_->vertex_id_.load(std::memory_order_acquire), gid.AsUint() + 1),
std::memory_order_release);
auto acc = storage_->vertices_.access();
auto *delta = CreateDeleteObjectDelta(&transaction_);
auto [it, inserted] = acc.insert(Vertex{gid});
auto [it, inserted] = acc.insert(Vertex{gid, delta});
MG_ASSERT(inserted, "The vertex must be inserted here!");
MG_ASSERT(it != acc.end(), "Invalid Vertex accessor!");
delta->prev.Set(&*it);
return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_, storage_->schema_validator_};
}
ResultSchema<VertexAccessor> Storage::Accessor::CreateVertexAndValidate(
storage::LabelId primary_label, const std::vector<storage::LabelId> &labels,
const std::vector<std::pair<storage::PropertyId, storage::PropertyValue>> &properties) {
auto maybe_schema_violation = GetSchemaValidator().ValidateVertexCreate(primary_label, labels, properties);
if (maybe_schema_violation) {
return {std::move(*maybe_schema_violation)};
}
OOMExceptionEnabler oom_exception;
auto gid = storage_->vertex_id_.fetch_add(1, std::memory_order_acq_rel);
auto acc = storage_->vertices_.access();
auto *delta = CreateDeleteObjectDelta(&transaction_);
auto [it, inserted] = acc.insert(Vertex{storage::Gid::FromUint(gid), delta, primary_label});
MG_ASSERT(inserted, "The vertex must be inserted here!");
MG_ASSERT(it != acc.end(), "Invalid Vertex accessor!");
delta->prev.Set(&*it);
auto va = VertexAccessor{
&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_, storage_->schema_validator_};
for (const auto label : labels) {
const auto maybe_error = va.AddLabel(label);
if (maybe_error.HasError()) {
return {maybe_error.GetError()};
}
}
// Set properties
for (auto [property_id, property_value] : properties) {
const auto maybe_error = va.SetProperty(property_id, property_value);
if (maybe_error.HasError()) {
return {maybe_error.GetError()};
}
}
return va;
return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_};
}
std::optional<VertexAccessor> Storage::Accessor::FindVertex(Gid gid, View view) {
auto acc = storage_->vertices_.access();
auto it = acc.find(gid);
if (it == acc.end()) return std::nullopt;
return VertexAccessor::Create(&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_,
storage_->schema_validator_, view);
return VertexAccessor::Create(&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_, view);
}
Result<std::optional<VertexAccessor>> Storage::Accessor::DeleteVertex(VertexAccessor *vertex) {
@ -588,7 +543,7 @@ Result<std::optional<VertexAccessor>> Storage::Accessor::DeleteVertex(VertexAcce
vertex_ptr->deleted = true;
return std::make_optional<VertexAccessor>(vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_,
config_, storage_->schema_validator_, true);
config_, true);
}
Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Storage::Accessor::DetachDeleteVertex(
@ -618,7 +573,7 @@ Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Stor
for (const auto &item : in_edges) {
auto [edge_type, from_vertex, edge] = item;
EdgeAccessor e(edge, edge_type, from_vertex, vertex_ptr, &transaction_, &storage_->indices_,
&storage_->constraints_, config_, storage_->schema_validator_);
&storage_->constraints_, config_);
auto ret = DeleteEdge(&e);
if (ret.HasError()) {
MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!");
@ -632,7 +587,7 @@ Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Stor
for (const auto &item : out_edges) {
auto [edge_type, to_vertex, edge] = item;
EdgeAccessor e(edge, edge_type, vertex_ptr, to_vertex, &transaction_, &storage_->indices_, &storage_->constraints_,
config_, storage_->schema_validator_);
config_);
auto ret = DeleteEdge(&e);
if (ret.HasError()) {
MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!");
@ -658,8 +613,7 @@ Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Stor
vertex_ptr->deleted = true;
return std::make_optional<ReturnType>(
VertexAccessor{vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_, config_,
storage_->schema_validator_, true},
VertexAccessor{vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_, config_, true},
std::move(deleted_edges));
}
@ -719,7 +673,7 @@ Result<EdgeAccessor> Storage::Accessor::CreateEdge(VertexAccessor *from, VertexA
storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &storage_->indices_,
&storage_->constraints_, config_, storage_->schema_validator_);
&storage_->constraints_, config_);
}
Result<EdgeAccessor> Storage::Accessor::CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type,
@ -787,7 +741,7 @@ Result<EdgeAccessor> Storage::Accessor::CreateEdge(VertexAccessor *from, VertexA
storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &storage_->indices_,
&storage_->constraints_, config_, storage_->schema_validator_);
&storage_->constraints_, config_);
}
Result<std::optional<EdgeAccessor>> Storage::Accessor::DeleteEdge(EdgeAccessor *edge) {
@ -871,8 +825,7 @@ Result<std::optional<EdgeAccessor>> Storage::Accessor::DeleteEdge(EdgeAccessor *
storage_->edge_count_.fetch_add(-1, std::memory_order_acq_rel);
return std::make_optional<EdgeAccessor>(edge_ref, edge_type, from_vertex, to_vertex, &transaction_,
&storage_->indices_, &storage_->constraints_, config_,
storage_->schema_validator_, true);
&storage_->indices_, &storage_->constraints_, config_, true);
}
const std::string &Storage::Accessor::LabelToName(LabelId label) const { return storage_->LabelToName(label); }
@ -916,11 +869,11 @@ utils::BasicResult<ConstraintViolation, void> Storage::Accessor::Commit(
auto validation_result = ValidateExistenceConstraints(*prev.vertex, storage_->constraints_);
if (validation_result) {
Abort();
return {*validation_result};
return *validation_result;
}
}
// Result of validating the vertex against unique constraints. It has to be
// Result of validating the vertex against unqiue constraints. It has to be
// declared outside of the critical section scope because its value is
// tested for Abort call which has to be done out of the scope.
std::optional<ConstraintViolation> unique_constraint_violation;
@ -1001,7 +954,7 @@ utils::BasicResult<ConstraintViolation, void> Storage::Accessor::Commit(
if (unique_constraint_violation) {
Abort();
return {*unique_constraint_violation};
return *unique_constraint_violation;
}
}
is_transaction_active_ = false;
@ -1302,29 +1255,11 @@ UniqueConstraints::DeletionStatus Storage::DropUniqueConstraint(
return UniqueConstraints::DeletionStatus::SUCCESS;
}
const SchemaValidator &Storage::Accessor::GetSchemaValidator() const { return storage_->schema_validator_; }
ConstraintsInfo Storage::ListAllConstraints() const {
std::shared_lock<utils::RWLock> storage_guard_(main_lock_);
return {ListExistenceConstraints(constraints_), constraints_.unique_constraints.ListConstraints()};
}
SchemasInfo Storage::ListAllSchemas() const {
std::shared_lock<utils::RWLock> storage_guard_(main_lock_);
return {schemas_.ListSchemas()};
}
const Schemas::Schema *Storage::GetSchema(const LabelId primary_label) const {
std::shared_lock<utils::RWLock> storage_guard_(main_lock_);
return schemas_.GetSchema(primary_label);
}
bool Storage::CreateSchema(const LabelId primary_label, const std::vector<SchemaProperty> &schemas_types) {
return schemas_.CreateSchema(primary_label, schemas_types);
}
bool Storage::DropSchema(const LabelId primary_label) { return schemas_.DropSchema(primary_label); }
StorageInfo Storage::GetInfo() const {
auto vertex_count = vertices_.size();
auto edge_count = edge_count_.load(std::memory_order_acquire);
@ -1341,22 +1276,21 @@ VerticesIterable Storage::Accessor::Vertices(LabelId label, View view) {
}
VerticesIterable Storage::Accessor::Vertices(LabelId label, PropertyId property, View view) {
return VerticesIterable(storage_->indices_.label_property_index.Vertices(
label, property, std::nullopt, std::nullopt, view, &transaction_, storage_->schema_validator_));
return VerticesIterable(storage_->indices_.label_property_index.Vertices(label, property, std::nullopt, std::nullopt,
view, &transaction_));
}
VerticesIterable Storage::Accessor::Vertices(LabelId label, PropertyId property, const PropertyValue &value,
View view) {
return VerticesIterable(storage_->indices_.label_property_index.Vertices(
label, property, utils::MakeBoundInclusive(value), utils::MakeBoundInclusive(value), view, &transaction_,
storage_->schema_validator_));
label, property, utils::MakeBoundInclusive(value), utils::MakeBoundInclusive(value), view, &transaction_));
}
VerticesIterable Storage::Accessor::Vertices(LabelId label, PropertyId property,
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) {
return VerticesIterable(storage_->indices_.label_property_index.Vertices(
label, property, lower_bound, upper_bound, view, &transaction_, storage_->schema_validator_));
return VerticesIterable(
storage_->indices_.label_property_index.Vertices(label, property, lower_bound, upper_bound, view, &transaction_));
}
Transaction Storage::CreateTransaction(IsolationLevel isolation_level) {
@ -1884,8 +1818,8 @@ utils::BasicResult<Storage::CreateSnapshotError> Storage::CreateSnapshot() {
// Create snapshot.
durability::CreateSnapshot(&transaction, snapshot_directory_, wal_directory_,
config_.durability.snapshot_retention_count, &vertices_, &edges_, &name_id_mapper_,
&indices_, &constraints_, config_.items, schema_validator_, uuid_, epoch_id_,
epoch_history_, &file_retainer_);
&indices_, &constraints_, config_.items, uuid_, epoch_id_, epoch_history_,
&file_retainer_);
// Finalize snapshot transaction.
commit_log_->MarkFinished(transaction.start_timestamp);

View File

@ -16,7 +16,6 @@
#include <optional>
#include <shared_mutex>
#include <variant>
#include <vector>
#include "io/network/endpoint.hpp"
#include "kvstore/kvstore.hpp"
@ -27,19 +26,14 @@
#include "storage/v2/durability/wal.hpp"
#include "storage/v2/edge.hpp"
#include "storage/v2/edge_accessor.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/indices.hpp"
#include "storage/v2/isolation_level.hpp"
#include "storage/v2/mvcc.hpp"
#include "storage/v2/name_id_mapper.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/result.hpp"
#include "storage/v2/schema_validator.hpp"
#include "storage/v2/schemas.hpp"
#include "storage/v2/transaction.hpp"
#include "storage/v2/vertex.hpp"
#include "storage/v2/vertex_accessor.hpp"
#include "utils/exceptions.hpp"
#include "utils/file_locker.hpp"
#include "utils/on_scope_exit.hpp"
#include "utils/rw_lock.hpp"
@ -73,7 +67,6 @@ class AllVerticesIterable final {
Indices *indices_;
Constraints *constraints_;
Config::Items config_;
const SchemaValidator *schema_validator_;
std::optional<VertexAccessor> vertex_;
public:
@ -94,15 +87,13 @@ class AllVerticesIterable final {
};
AllVerticesIterable(utils::SkipList<Vertex>::Accessor vertices_accessor, Transaction *transaction, View view,
Indices *indices, Constraints *constraints, Config::Items config,
SchemaValidator *schema_validator)
Indices *indices, Constraints *constraints, Config::Items config)
: vertices_accessor_(std::move(vertices_accessor)),
transaction_(transaction),
view_(view),
indices_(indices),
constraints_(constraints),
config_(config),
schema_validator_(schema_validator) {}
config_(config) {}
Iterator begin() { return Iterator(this, vertices_accessor_.begin()); }
Iterator end() { return Iterator(this, vertices_accessor_.end()); }
@ -183,11 +174,6 @@ struct ConstraintsInfo {
std::vector<std::pair<LabelId, std::set<PropertyId>>> unique;
};
/// Structure used to return information about existing schemas in the storage
struct SchemasInfo {
Schemas::SchemasList schemas;
};
/// Structure used to return information about the storage.
struct StorageInfo {
uint64_t vertex_count;
@ -224,21 +210,15 @@ class Storage final {
~Accessor();
VertexAccessor CreateVertex();
VertexAccessor CreateVertex(storage::Gid gid);
/// @throw std::bad_alloc
ResultSchema<VertexAccessor> CreateVertexAndValidate(
storage::LabelId primary_label, const std::vector<storage::LabelId> &labels,
const std::vector<std::pair<storage::PropertyId, storage::PropertyValue>> &properties);
VertexAccessor CreateVertex();
std::optional<VertexAccessor> FindVertex(Gid gid, View view);
VerticesIterable Vertices(View view) {
return VerticesIterable(AllVerticesIterable(storage_->vertices_.access(), &transaction_, view,
&storage_->indices_, &storage_->constraints_, storage_->config_.items,
&storage_->schema_validator_));
&storage_->indices_, &storage_->constraints_,
storage_->config_.items));
}
VerticesIterable Vertices(LabelId label, View view);
@ -327,10 +307,6 @@ class Storage final {
storage_->constraints_.unique_constraints.ListConstraints()};
}
const SchemaValidator &GetSchemaValidator() const;
SchemasInfo ListAllSchemas() const { return {storage_->schemas_.ListSchemas()}; }
void AdvanceCommand();
/// Commit returns `ConstraintViolation` if the changes made by this
@ -346,7 +322,7 @@ class Storage final {
private:
/// @throw std::bad_alloc
VertexAccessor CreateVertex(storage::Gid gid, storage::LabelId primary_label);
VertexAccessor CreateVertex(storage::Gid gid);
/// @throw std::bad_alloc
Result<EdgeAccessor> CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type, storage::Gid gid);
@ -389,7 +365,7 @@ class Storage final {
IndicesInfo ListAllIndices() const;
/// Creates an existence constraint. Returns true if the constraint was
/// successfully added, false if it already exists and a `ConstraintViolation`
/// successfuly added, false if it already exists and a `ConstraintViolation`
/// if there is an existing vertex violating the constraint.
///
/// @throw std::bad_alloc
@ -427,14 +403,6 @@ class Storage final {
ConstraintsInfo ListAllConstraints() const;
SchemasInfo ListAllSchemas() const;
const Schemas::Schema *GetSchema(LabelId primary_label) const;
bool CreateSchema(LabelId primary_label, const std::vector<SchemaProperty> &schemas_types);
bool DropSchema(LabelId primary_label);
StorageInfo GetInfo() const;
bool LockPath();
@ -536,10 +504,8 @@ class Storage final {
NameIdMapper name_id_mapper_;
SchemaValidator schema_validator_;
Constraints constraints_;
Indices indices_;
Schemas schemas_;
// Transaction engine
utils::SpinLock engine_lock_;

View File

@ -19,39 +19,18 @@
#include "storage/v2/edge_ref.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/property_store.hpp"
#include "utils/algorithm.hpp"
#include "utils/spin_lock.hpp"
namespace memgraph::storage {
struct Vertex {
Vertex(Gid gid, Delta *delta, LabelId primary_label)
: gid(gid), primary_label{primary_label}, deleted(false), delta(delta) {
MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT,
"Vertex must be created with an initial DELETE_OBJECT delta!");
}
// TODO remove this when import replication is solved
Vertex(Gid gid, LabelId primary_label) : gid(gid), primary_label{primary_label}, deleted(false) {
MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT,
"Vertex must be created with an initial DELETE_OBJECT delta!");
}
// TODO remove this when import csv is solved
[[deprecated]] Vertex(Gid gid, Delta *delta) : gid(gid), deleted(false), delta(delta) {
MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT,
"Vertex must be created with an initial DELETE_OBJECT delta!");
}
// TODO remove this when import replication is solved
[[deprecated]] explicit Vertex(Gid gid) : gid(gid), deleted(false) {
Vertex(Gid gid, Delta *delta) : gid(gid), deleted(false), delta(delta) {
MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT,
"Vertex must be created with an initial DELETE_OBJECT delta!");
}
Gid gid;
LabelId primary_label;
std::vector<LabelId> labels;
PropertyStore properties;
@ -73,8 +52,4 @@ inline bool operator<(const Vertex &first, const Vertex &second) { return first.
inline bool operator==(const Vertex &first, const Gid &second) { return first.gid == second; }
inline bool operator<(const Vertex &first, const Gid &second) { return first.gid < second; }
inline bool VertexHasLabel(const Vertex &vertex, const LabelId label) {
return vertex.primary_label == label || utils::Contains(vertex.labels, label);
}
} // namespace memgraph::storage

View File

@ -18,8 +18,6 @@
#include "storage/v2/indices.hpp"
#include "storage/v2/mvcc.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/schema_validator.hpp"
#include "storage/v2/vertex.hpp"
#include "utils/logging.hpp"
#include "utils/memory_tracker.hpp"
@ -63,13 +61,12 @@ std::pair<bool, bool> IsVisible(Vertex *vertex, Transaction *transaction, View v
} // namespace detail
std::optional<VertexAccessor> VertexAccessor::Create(Vertex *vertex, Transaction *transaction, Indices *indices,
Constraints *constraints, Config::Items config,
const SchemaValidator &schema_validator, View view) {
Constraints *constraints, Config::Items config, View view) {
if (const auto [exists, deleted] = detail::IsVisible(vertex, transaction, view); !exists || deleted) {
return std::nullopt;
}
return VertexAccessor{vertex, transaction, indices, constraints, config, schema_validator};
return VertexAccessor{vertex, transaction, indices, constraints, config};
}
bool VertexAccessor::IsVisible(View view) const {
@ -96,28 +93,6 @@ Result<bool> VertexAccessor::AddLabel(LabelId label) {
return true;
}
storage::ResultSchema<bool> VertexAccessor::AddLabelAndValidate(LabelId label) {
if (const auto maybe_violation_error = vertex_validator_.ValidateAddLabel(label); maybe_violation_error) {
return {*maybe_violation_error};
}
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
if (!PrepareForWrite(transaction_, vertex_)) return {Error::SERIALIZATION_ERROR};
if (vertex_->deleted) return {Error::DELETED_OBJECT};
if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end()) return false;
CreateAndLinkDelta(transaction_, vertex_, Delta::RemoveLabelTag(), label);
vertex_->labels.push_back(label);
UpdateOnAddLabel(indices_, label, vertex_, *transaction_);
return true;
}
Result<bool> VertexAccessor::RemoveLabel(LabelId label) {
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
@ -135,26 +110,6 @@ Result<bool> VertexAccessor::RemoveLabel(LabelId label) {
return true;
}
ResultSchema<bool> VertexAccessor::RemoveLabelAndValidate(LabelId label) {
if (const auto maybe_violation_error = vertex_validator_.ValidateRemoveLabel(label); maybe_violation_error) {
return {*maybe_violation_error};
}
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
if (!PrepareForWrite(transaction_, vertex_)) return {Error::SERIALIZATION_ERROR};
if (vertex_->deleted) return {Error::DELETED_OBJECT};
auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label);
if (it == vertex_->labels.end()) return false;
CreateAndLinkDelta(transaction_, vertex_, Delta::AddLabelTag(), label);
std::swap(*it, *vertex_->labels.rbegin());
vertex_->labels.pop_back();
return true;
}
Result<bool> VertexAccessor::HasLabel(LabelId label, View view) const {
bool exists = true;
bool deleted = false;
@ -163,7 +118,7 @@ Result<bool> VertexAccessor::HasLabel(LabelId label, View view) const {
{
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
deleted = vertex_->deleted;
has_label = VertexHasLabel(*vertex_, label);
has_label = std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end();
delta = vertex_->delta;
}
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted, &has_label, label](const Delta &delta) {
@ -203,40 +158,6 @@ Result<bool> VertexAccessor::HasLabel(LabelId label, View view) const {
return has_label;
}
Result<LabelId> VertexAccessor::PrimaryLabel(const View view) const {
bool exists = true;
bool deleted = false;
Delta *delta = nullptr;
{
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
deleted = vertex_->deleted;
delta = vertex_->delta;
}
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted](const Delta &delta) {
switch (delta.action) {
case Delta::Action::DELETE_OBJECT: {
exists = false;
break;
}
case Delta::Action::RECREATE_OBJECT: {
deleted = false;
break;
}
case Delta::Action::ADD_LABEL:
case Delta::Action::REMOVE_LABEL:
case Delta::Action::SET_PROPERTY:
case Delta::Action::ADD_IN_EDGE:
case Delta::Action::ADD_OUT_EDGE:
case Delta::Action::REMOVE_IN_EDGE:
case Delta::Action::REMOVE_OUT_EDGE:
break;
}
});
if (!exists) return Error::NONEXISTENT_OBJECT;
if (!for_deleted_ && deleted) return Error::DELETED_OBJECT;
return vertex_->primary_label;
}
Result<std::vector<LabelId>> VertexAccessor::Labels(View view) const {
bool exists = true;
bool deleted = false;
@ -309,36 +230,6 @@ Result<PropertyValue> VertexAccessor::SetProperty(PropertyId property, const Pro
return std::move(current_value);
}
ResultSchema<PropertyValue> VertexAccessor::SetPropertyAndValidate(PropertyId property, const PropertyValue &value) {
if (auto maybe_violation_error = vertex_validator_.ValidatePropertyUpdate(property); maybe_violation_error) {
return {*maybe_violation_error};
}
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
if (!PrepareForWrite(transaction_, vertex_)) {
return {Error::SERIALIZATION_ERROR};
}
if (vertex_->deleted) {
return {Error::DELETED_OBJECT};
}
auto current_value = vertex_->properties.GetProperty(property);
// We could skip setting the value if the previous one is the same to the new
// one. This would save some memory as a delta would not be created as well as
// avoid copying the value. The reason we are not doing that is because the
// current code always follows the logical pattern of "create a delta" and
// "modify in-place". Additionally, the created delta will make other
// transactions get a SERIALIZATION_ERROR.
CreateAndLinkDelta(transaction_, vertex_, Delta::SetPropertyTag(), property, current_value);
vertex_->properties.SetProperty(property, value);
UpdateOnSetProperty(indices_, property, value, vertex_, *transaction_);
return std::move(current_value);
}
Result<std::map<PropertyId, PropertyValue>> VertexAccessor::ClearProperties() {
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
@ -523,8 +414,7 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const std::
ret.reserve(in_edges.size());
for (const auto &item : in_edges) {
const auto &[edge_type, from_vertex, edge] = item;
ret.emplace_back(edge, edge_type, from_vertex, vertex_, transaction_, indices_, constraints_, config_,
*vertex_validator_.schema_validator);
ret.emplace_back(edge, edge_type, from_vertex, vertex_, transaction_, indices_, constraints_, config_);
}
return std::move(ret);
}
@ -604,8 +494,7 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const std:
ret.reserve(out_edges.size());
for (const auto &item : out_edges) {
const auto &[edge_type, to_vertex, edge] = item;
ret.emplace_back(edge, edge_type, vertex_, to_vertex, transaction_, indices_, constraints_, config_,
*vertex_validator_.schema_validator);
ret.emplace_back(edge, edge_type, vertex_, to_vertex, transaction_, indices_, constraints_, config_);
}
return std::move(ret);
}
@ -686,21 +575,4 @@ Result<size_t> VertexAccessor::OutDegree(View view) const {
return degree;
}
VertexAccessor::VertexValidator::VertexValidator(const SchemaValidator &schema_validator, const Vertex *vertex)
: schema_validator{&schema_validator}, vertex_{vertex} {}
[[nodiscard]] std::optional<SchemaViolation> VertexAccessor::VertexValidator::ValidatePropertyUpdate(
PropertyId property_id) const {
MG_ASSERT(vertex_ != nullptr, "Cannot validate vertex which is nullptr");
return schema_validator->ValidatePropertyUpdate(vertex_->primary_label, property_id);
};
[[nodiscard]] std::optional<SchemaViolation> VertexAccessor::VertexValidator::ValidateAddLabel(LabelId label) const {
return schema_validator->ValidateLabelUpdate(label);
}
[[nodiscard]] std::optional<SchemaViolation> VertexAccessor::VertexValidator::ValidateRemoveLabel(LabelId label) const {
return schema_validator->ValidateLabelUpdate(label);
}
} // namespace memgraph::storage

View File

@ -13,8 +13,6 @@
#include <optional>
#include "storage/v2/id_types.hpp"
#include "storage/v2/schema_validator.hpp"
#include "storage/v2/vertex.hpp"
#include "storage/v2/config.hpp"
@ -31,39 +29,20 @@ struct Constraints;
class VertexAccessor final {
private:
struct VertexValidator {
// TODO(jbajic) Beware since vertex is pointer it will be accessed even as nullptr
explicit VertexValidator(const SchemaValidator &schema_validator, const Vertex *vertex);
[[nodiscard]] std::optional<SchemaViolation> ValidatePropertyUpdate(PropertyId property_id) const;
[[nodiscard]] std::optional<SchemaViolation> ValidateAddLabel(LabelId label) const;
[[nodiscard]] std::optional<SchemaViolation> ValidateRemoveLabel(LabelId label) const;
const SchemaValidator *schema_validator;
private:
const Vertex *vertex_;
};
friend class Storage;
public:
// Be careful when using VertexAccessor since it can be instantiated with
// nullptr values
VertexAccessor(Vertex *vertex, Transaction *transaction, Indices *indices, Constraints *constraints,
Config::Items config, const SchemaValidator &schema_validator, bool for_deleted = false)
Config::Items config, bool for_deleted = false)
: vertex_(vertex),
transaction_(transaction),
indices_(indices),
constraints_(constraints),
config_(config),
vertex_validator_{schema_validator, vertex},
for_deleted_(for_deleted) {}
static std::optional<VertexAccessor> Create(Vertex *vertex, Transaction *transaction, Indices *indices,
Constraints *constraints, Config::Items config,
const SchemaValidator &schema_validator, View view);
Constraints *constraints, Config::Items config, View view);
/// @return true if the object is visible from the current transaction
bool IsVisible(View view) const;
@ -73,23 +52,11 @@ class VertexAccessor final {
/// @throw std::bad_alloc
Result<bool> AddLabel(LabelId label);
/// Add a label and return `true` if insertion took place.
/// `false` is returned if the label already existed, or SchemaViolation
/// if adding the label has violated one of the schema constraints.
/// @throw std::bad_alloc
storage::ResultSchema<bool> AddLabelAndValidate(LabelId label);
/// Remove a label and return `true` if deletion took place.
/// `false` is returned if the vertex did not have a label already.
/// @throw std::bad_alloc
Result<bool> RemoveLabel(LabelId label);
/// Remove a label and return `true` if deletion took place.
/// `false` is returned if the vertex did not have a label already. or SchemaViolation
/// if adding the label has violated one of the schema constraints.
/// @throw std::bad_alloc
ResultSchema<bool> RemoveLabelAndValidate(LabelId label);
Result<bool> HasLabel(LabelId label, View view) const;
/// @throw std::bad_alloc
@ -97,16 +64,10 @@ class VertexAccessor final {
/// std::vector::max_size().
Result<std::vector<LabelId>> Labels(View view) const;
Result<LabelId> PrimaryLabel(View view) const;
/// Set a property value and return the old value.
/// @throw std::bad_alloc
Result<PropertyValue> SetProperty(PropertyId property, const PropertyValue &value);
/// Set a property value and return the old value or error.
/// @throw std::bad_alloc
ResultSchema<PropertyValue> SetPropertyAndValidate(PropertyId property, const PropertyValue &value);
/// Remove all properties and return the values of the removed properties.
/// @throw std::bad_alloc
Result<std::map<PropertyId, PropertyValue>> ClearProperties();
@ -135,8 +96,6 @@ class VertexAccessor final {
Gid Gid() const noexcept { return vertex_->gid; }
const SchemaValidator *GetSchemaValidator() const;
bool operator==(const VertexAccessor &other) const noexcept {
return vertex_ == other.vertex_ && transaction_ == other.transaction_;
}
@ -148,7 +107,6 @@ class VertexAccessor final {
Indices *indices_;
Constraints *constraints_;
Config::Items config_;
VertexValidator vertex_validator_;
// if the accessor was created for a deleted vertex.
// Accessor behaves differently for some methods based on this

View File

@ -10,6 +10,8 @@ set(storage_v3_src_files
indices.cpp
property_store.cpp
vertex_accessor.cpp
schemas.cpp
schema_validator.cpp
storage.cpp)
# #### Replication #####

View File

@ -16,6 +16,7 @@
#include <map>
#include "storage/v3/mvcc.hpp"
#include "storage/v3/vertex.hpp"
#include "utils/logging.hpp"
namespace memgraph::storage::v3 {
@ -59,7 +60,7 @@ bool LastCommittedVersionHasLabelProperty(const Vertex &vertex, LabelId label, c
std::lock_guard<utils::SpinLock> guard(vertex.lock);
delta = vertex.delta;
deleted = vertex.deleted;
has_label = utils::Contains(vertex.labels, label);
has_label = VertexHasLabel(vertex, label);
size_t i = 0;
for (const auto &property : properties) {
@ -142,7 +143,7 @@ bool AnyVersionHasLabelProperty(const Vertex &vertex, LabelId label, const std::
Delta *delta{nullptr};
{
std::lock_guard<utils::SpinLock> guard(vertex.lock);
has_label = utils::Contains(vertex.labels, label);
has_label = VertexHasLabel(vertex, label);
deleted = vertex.deleted;
delta = vertex.delta;
@ -267,7 +268,7 @@ bool UniqueConstraints::Entry::operator==(const std::vector<PropertyValue> &rhs)
void UniqueConstraints::UpdateBeforeCommit(const Vertex *vertex, const Transaction &tx) {
for (auto &[label_props, storage] : constraints_) {
if (!utils::Contains(vertex->labels, label_props.first)) {
if (!VertexHasLabel(*vertex, label_props.first)) {
continue;
}
auto values = ExtractPropertyValues(*vertex, label_props.second);
@ -301,7 +302,7 @@ utils::BasicResult<ConstraintViolation, UniqueConstraints::CreationStatus> Uniqu
auto acc = constraint->second.access();
for (const Vertex &vertex : vertices) {
if (vertex.deleted || !utils::Contains(vertex.labels, label)) {
if (vertex.deleted || !VertexHasLabel(vertex, label)) {
continue;
}
auto values = ExtractPropertyValues(vertex, properties);
@ -352,7 +353,7 @@ std::optional<ConstraintViolation> UniqueConstraints::Validate(const Vertex &ver
for (const auto &[label_props, storage] : constraints_) {
const auto &label = label_props.first;
const auto &properties = label_props.second;
if (!utils::Contains(vertex.labels, label)) {
if (!VertexHasLabel(vertex, label)) {
continue;
}

View File

@ -158,7 +158,7 @@ inline utils::BasicResult<ConstraintViolation, bool> CreateExistenceConstraint(
return false;
}
for (const auto &vertex : vertices) {
if (!vertex.deleted && utils::Contains(vertex.labels, label) && !vertex.properties.HasProperty(property)) {
if (!vertex.deleted && VertexHasLabel(vertex, label) && !vertex.properties.HasProperty(property)) {
return ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label, std::set<PropertyId>{property}};
}
}
@ -184,7 +184,7 @@ inline bool DropExistenceConstraint(Constraints *constraints, LabelId label, Pro
[[nodiscard]] inline std::optional<ConstraintViolation> ValidateExistenceConstraints(const Vertex &vertex,
const Constraints &constraints) {
for (const auto &[label, property] : constraints.existence_constraints) {
if (!vertex.deleted && utils::Contains(vertex.labels, label) && !vertex.properties.HasProperty(property)) {
if (!vertex.deleted && VertexHasLabel(vertex, label) && !vertex.properties.HasProperty(property)) {
return ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label, std::set<PropertyId>{property}};
}
}

View File

@ -628,8 +628,9 @@ RecoveredSnapshot LoadSnapshot(const std::filesystem::path &path, utils::SkipLis
void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snapshot_directory,
const std::filesystem::path &wal_directory, uint64_t snapshot_retention_count,
utils::SkipList<Vertex> *vertices, utils::SkipList<Edge> *edges, NameIdMapper *name_id_mapper,
Indices *indices, Constraints *constraints, Config::Items items, const std::string &uuid,
const std::string_view epoch_id, const std::deque<std::pair<std::string, uint64_t>> &epoch_history,
Indices *indices, Constraints *constraints, Config::Items items,
const SchemaValidator &schema_validator, const std::string &uuid, const std::string_view epoch_id,
const std::deque<std::pair<std::string, uint64_t>> &epoch_history,
utils::FileRetainer *file_retainer) {
// Ensure that the storage directory exists.
utils::EnsureDirOrDie(snapshot_directory);
@ -713,8 +714,9 @@ void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snaps
// type and invalid from/to pointers because we don't know them here,
// but that isn't an issue because we won't use that part of the API
// here.
auto ea =
EdgeAccessor{edge_ref, EdgeTypeId::FromUint(0UL), nullptr, nullptr, transaction, indices, constraints, items};
// TODO(jbajic) Fix snapshot with new schema rules
auto ea = EdgeAccessor{edge_ref, EdgeTypeId::FromUint(0UL), nullptr, nullptr, transaction, indices, constraints,
items, schema_validator};
// Get edge data.
auto maybe_props = ea.Properties(View::OLD);
@ -742,7 +744,7 @@ void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snaps
auto acc = vertices->access();
for (auto &vertex : acc) {
// The visibility check is implemented for vertices so we use it here.
auto va = VertexAccessor::Create(&vertex, transaction, indices, constraints, items, View::OLD);
auto va = VertexAccessor::Create(&vertex, transaction, indices, constraints, items, schema_validator, View::OLD);
if (!va) continue;
// Get vertex data.

View File

@ -21,6 +21,7 @@
#include "storage/v3/edge.hpp"
#include "storage/v3/indices.hpp"
#include "storage/v3/name_id_mapper.hpp"
#include "storage/v3/schema_validator.hpp"
#include "storage/v3/transaction.hpp"
#include "storage/v3/vertex.hpp"
#include "utils/file_locker.hpp"
@ -68,8 +69,9 @@ RecoveredSnapshot LoadSnapshot(const std::filesystem::path &path, utils::SkipLis
void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snapshot_directory,
const std::filesystem::path &wal_directory, uint64_t snapshot_retention_count,
utils::SkipList<Vertex> *vertices, utils::SkipList<Edge> *edges, NameIdMapper *name_id_mapper,
Indices *indices, Constraints *constraints, Config::Items items, const std::string &uuid,
std::string_view epoch_id, const std::deque<std::pair<std::string, uint64_t>> &epoch_history,
Indices *indices, Constraints *constraints, Config::Items items,
const SchemaValidator &schema_validator, const std::string &uuid, std::string_view epoch_id,
const std::deque<std::pair<std::string, uint64_t>> &epoch_history,
utils::FileRetainer *file_retainer);
} // namespace memgraph::storage::v3::durability

View File

@ -15,6 +15,7 @@
#include "storage/v3/mvcc.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/schema_validator.hpp"
#include "storage/v3/vertex_accessor.hpp"
#include "utils/memory_tracker.hpp"
@ -54,11 +55,11 @@ bool EdgeAccessor::IsVisible(const View view) const {
}
VertexAccessor EdgeAccessor::FromVertex() const {
return VertexAccessor{from_vertex_, transaction_, indices_, constraints_, config_};
return {from_vertex_, transaction_, indices_, constraints_, config_, *schema_validator_};
}
VertexAccessor EdgeAccessor::ToVertex() const {
return VertexAccessor{to_vertex_, transaction_, indices_, constraints_, config_};
return {to_vertex_, transaction_, indices_, constraints_, config_, *schema_validator_};
}
Result<PropertyValue> EdgeAccessor::SetProperty(PropertyId property, const PropertyValue &value) {

View File

@ -18,6 +18,7 @@
#include "storage/v3/config.hpp"
#include "storage/v3/result.hpp"
#include "storage/v3/schema_validator.hpp"
#include "storage/v3/transaction.hpp"
#include "storage/v3/view.hpp"
@ -34,7 +35,8 @@ class EdgeAccessor final {
public:
EdgeAccessor(EdgeRef edge, EdgeTypeId edge_type, Vertex *from_vertex, Vertex *to_vertex, Transaction *transaction,
Indices *indices, Constraints *constraints, Config::Items config, bool for_deleted = false)
Indices *indices, Constraints *constraints, Config::Items config,
const SchemaValidator &schema_validator, bool for_deleted = false)
: edge_(edge),
edge_type_(edge_type),
from_vertex_(from_vertex),
@ -43,6 +45,7 @@ class EdgeAccessor final {
indices_(indices),
constraints_(constraints),
config_(config),
schema_validator_{&schema_validator},
for_deleted_(for_deleted) {}
/// @return true if the object is visible from the current transaction
@ -91,6 +94,7 @@ class EdgeAccessor final {
Indices *indices_;
Constraints *constraints_;
Config::Items config_;
const SchemaValidator *schema_validator_;
// if the accessor was created for a deleted edge.
// Accessor behaves differently for some methods based on this

View File

@ -10,10 +10,12 @@
// licenses/APL.txt.
#include "indices.hpp"
#include <limits>
#include "storage/v3/mvcc.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/schema_validator.hpp"
#include "utils/bound.hpp"
#include "utils/logging.hpp"
#include "utils/memory_tracker.hpp"
@ -327,7 +329,7 @@ void LabelIndex::RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp) {
LabelIndex::Iterable::Iterator::Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator)
: self_(self),
index_iterator_(index_iterator),
current_vertex_accessor_(nullptr, nullptr, nullptr, nullptr, self_->config_),
current_vertex_accessor_(nullptr, nullptr, nullptr, nullptr, self_->config_, *self_->schema_validator_),
current_vertex_(nullptr) {
AdvanceUntilValid();
}
@ -345,8 +347,8 @@ void LabelIndex::Iterable::Iterator::AdvanceUntilValid() {
}
if (CurrentVersionHasLabel(*index_iterator_->vertex, self_->label_, self_->transaction_, self_->view_)) {
current_vertex_ = index_iterator_->vertex;
current_vertex_accessor_ =
VertexAccessor{current_vertex_, self_->transaction_, self_->indices_, self_->constraints_, self_->config_};
current_vertex_accessor_ = VertexAccessor{current_vertex_, self_->transaction_, self_->indices_,
self_->constraints_, self_->config_, *self_->schema_validator_};
break;
}
}
@ -354,14 +356,15 @@ void LabelIndex::Iterable::Iterator::AdvanceUntilValid() {
LabelIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label, View view,
Transaction *transaction, Indices *indices, Constraints *constraints,
Config::Items config)
Config::Items config, const SchemaValidator &schema_validator)
: index_accessor_(std::move(index_accessor)),
label_(label),
view_(view),
transaction_(transaction),
indices_(indices),
constraints_(constraints),
config_(config) {}
config_(config),
schema_validator_(&schema_validator) {}
void LabelIndex::RunGC() {
for (auto &index_entry : index_) {
@ -478,7 +481,7 @@ void LabelPropertyIndex::RemoveObsoleteEntries(uint64_t oldest_active_start_time
LabelPropertyIndex::Iterable::Iterator::Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator)
: self_(self),
index_iterator_(index_iterator),
current_vertex_accessor_(nullptr, nullptr, nullptr, nullptr, self_->config_),
current_vertex_accessor_(nullptr, nullptr, nullptr, nullptr, self_->config_, *self_->schema_validator_),
current_vertex_(nullptr) {
AdvanceUntilValid();
}
@ -517,8 +520,8 @@ void LabelPropertyIndex::Iterable::Iterator::AdvanceUntilValid() {
if (CurrentVersionHasLabelProperty(*index_iterator_->vertex, self_->label_, self_->property_,
index_iterator_->value, self_->transaction_, self_->view_)) {
current_vertex_ = index_iterator_->vertex;
current_vertex_accessor_ =
VertexAccessor(current_vertex_, self_->transaction_, self_->indices_, self_->constraints_, self_->config_);
current_vertex_accessor_ = VertexAccessor(current_vertex_, self_->transaction_, self_->indices_,
self_->constraints_, self_->config_, *self_->schema_validator_);
break;
}
}
@ -541,7 +544,7 @@ LabelPropertyIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_ac
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view,
Transaction *transaction, Indices *indices, Constraints *constraints,
Config::Items config)
Config::Items config, const SchemaValidator &schema_validator)
: index_accessor_(std::move(index_accessor)),
label_(label),
property_(property),
@ -551,7 +554,8 @@ LabelPropertyIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_ac
transaction_(transaction),
indices_(indices),
constraints_(constraints),
config_(config) {
config_(config),
schema_validator_(&schema_validator) {
// We have to fix the bounds that the user provided to us. If the user
// provided only one bound we should make sure that only values of that type
// are returned by the iterator. We ensure this by supplying either an

View File

@ -11,12 +11,14 @@
#pragma once
#include <cstdint>
#include <optional>
#include <tuple>
#include <utility>
#include "storage/v3/config.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/schema_validator.hpp"
#include "storage/v3/transaction.hpp"
#include "storage/v3/vertex_accessor.hpp"
#include "utils/bound.hpp"
@ -51,8 +53,8 @@ class LabelIndex {
};
public:
LabelIndex(Indices *indices, Constraints *constraints, Config::Items config)
: indices_(indices), constraints_(constraints), config_(config) {}
LabelIndex(Indices *indices, Constraints *constraints, Config::Items config, const SchemaValidator &schema_validator)
: indices_(indices), constraints_(constraints), config_(config), schema_validator_{&schema_validator} {}
/// @throw std::bad_alloc
void UpdateOnAddLabel(LabelId label, Vertex *vertex, const Transaction &tx);
@ -72,7 +74,7 @@ class LabelIndex {
class Iterable {
public:
Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label, View view, Transaction *transaction,
Indices *indices, Constraints *constraints, Config::Items config);
Indices *indices, Constraints *constraints, Config::Items config, const SchemaValidator &schema_validator);
class Iterator {
public:
@ -105,13 +107,14 @@ class LabelIndex {
Indices *indices_;
Constraints *constraints_;
Config::Items config_;
const SchemaValidator *schema_validator_;
};
/// Returns an self with vertices visible from the given transaction.
Iterable Vertices(LabelId label, View view, Transaction *transaction) {
auto it = index_.find(label);
MG_ASSERT(it != index_.end(), "Index for label {} doesn't exist", label.AsUint());
return {it->second.access(), label, view, transaction, indices_, constraints_, config_};
return {it->second.access(), label, view, transaction, indices_, constraints_, config_, *schema_validator_};
}
int64_t ApproximateVertexCount(LabelId label) {
@ -129,6 +132,7 @@ class LabelIndex {
Indices *indices_;
Constraints *constraints_;
Config::Items config_;
const SchemaValidator *schema_validator_;
};
class LabelPropertyIndex {
@ -146,8 +150,9 @@ class LabelPropertyIndex {
};
public:
LabelPropertyIndex(Indices *indices, Constraints *constraints, Config::Items config)
: indices_(indices), constraints_(constraints), config_(config) {}
LabelPropertyIndex(Indices *indices, Constraints *constraints, Config::Items config,
const SchemaValidator &schema_validator)
: indices_(indices), constraints_(constraints), config_(config), schema_validator_{&schema_validator} {}
/// @throw std::bad_alloc
void UpdateOnAddLabel(LabelId label, Vertex *vertex, const Transaction &tx);
@ -171,7 +176,7 @@ class LabelPropertyIndex {
Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label, PropertyId property,
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Transaction *transaction,
Indices *indices, Constraints *constraints, Config::Items config);
Indices *indices, Constraints *constraints, Config::Items config, const SchemaValidator &schema_validator);
class Iterator {
public:
@ -208,16 +213,17 @@ class LabelPropertyIndex {
Indices *indices_;
Constraints *constraints_;
Config::Items config_;
const SchemaValidator *schema_validator_;
};
Iterable Vertices(LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view,
Transaction *transaction) {
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Transaction *transaction,
const SchemaValidator &schema_validator_) {
auto it = index_.find({label, property});
MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(),
property.AsUint());
return {it->second.access(), label, property, lower_bound, upper_bound, view,
transaction, indices_, constraints_, config_};
return {it->second.access(), label, property, lower_bound, upper_bound, view,
transaction, indices_, constraints_, config_, schema_validator_};
}
int64_t ApproximateVertexCount(LabelId label, PropertyId property) const {
@ -246,11 +252,13 @@ class LabelPropertyIndex {
Indices *indices_;
Constraints *constraints_;
Config::Items config_;
const SchemaValidator *schema_validator_;
};
struct Indices {
Indices(Constraints *constraints, Config::Items config)
: label_index(this, constraints, config), label_property_index(this, constraints, config) {}
Indices(Constraints *constraints, Config::Items config, const SchemaValidator &schema_validator)
: label_index(this, constraints, config, schema_validator),
label_property_index(this, constraints, config, schema_validator) {}
// Disable copy and move because members hold pointer to `this`.
Indices(const Indices &) = delete;

View File

@ -10,6 +10,7 @@
// licenses/APL.txt.
#include "storage/v3/replication/replication_server.hpp"
#include <atomic>
#include <filesystem>
@ -162,9 +163,10 @@ void Storage::ReplicationServer::SnapshotHandler(slk::Reader *req_reader, slk::B
storage_->edges_.clear();
storage_->constraints_ = Constraints();
storage_->indices_.label_index = LabelIndex(&storage_->indices_, &storage_->constraints_, storage_->config_.items);
storage_->indices_.label_property_index =
LabelPropertyIndex(&storage_->indices_, &storage_->constraints_, storage_->config_.items);
storage_->indices_.label_index =
LabelIndex(&storage_->indices_, &storage_->constraints_, storage_->config_.items, storage_->schema_validator_);
storage_->indices_.label_property_index = LabelPropertyIndex(&storage_->indices_, &storage_->constraints_,
storage_->config_.items, storage_->schema_validator_);
try {
spdlog::debug("Loading snapshot");
auto recovered_snapshot = durability::LoadSnapshot(*maybe_snapshot_path, &storage_->vertices_, &storage_->edges_,
@ -461,7 +463,8 @@ uint64_t Storage::ReplicationServer::ReadAndApplyDelta(durability::BaseDecoder *
&transaction->transaction_,
&storage_->indices_,
&storage_->constraints_,
storage_->config_.items};
storage_->config_.items,
storage_->schema_validator_};
auto ret = ea.SetProperty(transaction->NameToProperty(delta.vertex_edge_set_property.property),
delta.vertex_edge_set_property.value);

View File

@ -9,15 +9,15 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include "storage/v2/schema_validator.hpp"
#include "storage/v3/schema_validator.hpp"
#include <bits/ranges_algo.h>
#include <cstddef>
#include <ranges>
#include "storage/v2/schemas.hpp"
#include "storage/v3/schemas.hpp"
namespace memgraph::storage {
namespace memgraph::storage::v3 {
bool operator==(const SchemaViolation &lhs, const SchemaViolation &rhs) {
return lhs.status == rhs.status && lhs.label == rhs.label &&
@ -103,4 +103,4 @@ SchemaValidator::SchemaValidator(Schemas &schemas) : schemas_{schemas} {}
return std::nullopt;
}
} // namespace memgraph::storage
} // namespace memgraph::storage::v3

View File

@ -14,12 +14,12 @@
#include <optional>
#include <variant>
#include "storage/v2/id_types.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/result.hpp"
#include "storage/v2/schemas.hpp"
#include "storage/v3/id_types.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/result.hpp"
#include "storage/v3/schemas.hpp"
namespace memgraph::storage {
namespace memgraph::storage::v3 {
struct SchemaViolation {
enum class ValidationStatus : uint8_t {
@ -60,10 +60,10 @@ class SchemaValidator {
[[nodiscard]] std::optional<SchemaViolation> ValidateLabelUpdate(LabelId label) const;
private:
storage::Schemas &schemas_;
Schemas &schemas_;
};
template <typename TValue>
using ResultSchema = utils::BasicResult<std::variant<SchemaViolation, Error>, TValue>;
} // namespace memgraph::storage
} // namespace memgraph::storage::v3

View File

@ -9,14 +9,14 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include "storage/v2/schemas.hpp"
#include "storage/v3/schemas.hpp"
#include <unordered_map>
#include <vector>
#include "storage/v2/property_value.hpp"
#include "storage/v3/property_value.hpp"
namespace memgraph::storage {
namespace memgraph::storage::v3 {
bool operator==(const SchemaProperty &lhs, const SchemaProperty &rhs) {
return lhs.property_id == rhs.property_id && lhs.type == rhs.type;
@ -109,4 +109,4 @@ std::string SchemaTypeToString(const common::SchemaType type) {
}
}
} // namespace memgraph::storage
} // namespace memgraph::storage::v3

View File

@ -18,12 +18,12 @@
#include <vector>
#include "common/types.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/temporal.hpp"
#include "storage/v3/id_types.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/temporal.hpp"
#include "utils/result.hpp"
namespace memgraph::storage {
namespace memgraph::storage::v3 {
struct SchemaProperty {
PropertyId property_id;
@ -67,4 +67,4 @@ std::optional<common::SchemaType> PropertyTypeToSchemaType(const PropertyValue &
std::string SchemaTypeToString(common::SchemaType type);
} // namespace memgraph::storage
} // namespace memgraph::storage::v3

View File

@ -15,9 +15,11 @@
#include <atomic>
#include <memory>
#include <mutex>
#include <optional>
#include <variant>
#include <gflags/gflags.h>
#include <spdlog/spdlog.h>
#include "io/network/endpoint.hpp"
#include "storage/v3/constraints.hpp"
@ -27,6 +29,7 @@
#include "storage/v3/durability/snapshot.hpp"
#include "storage/v3/durability/wal.hpp"
#include "storage/v3/edge_accessor.hpp"
#include "storage/v3/id_types.hpp"
#include "storage/v3/indices.hpp"
#include "storage/v3/mvcc.hpp"
#include "storage/v3/replication/config.hpp"
@ -35,10 +38,12 @@
#include "storage/v3/replication/rpc.hpp"
#include "storage/v3/transaction.hpp"
#include "storage/v3/vertex_accessor.hpp"
#include "utils/exceptions.hpp"
#include "utils/file.hpp"
#include "utils/logging.hpp"
#include "utils/memory_tracker.hpp"
#include "utils/message.hpp"
#include "utils/result.hpp"
#include "utils/rw_lock.hpp"
#include "utils/spin_lock.hpp"
#include "utils/stat.hpp"
@ -54,9 +59,9 @@ inline constexpr uint16_t kEpochHistoryRetention = 1000;
auto AdvanceToVisibleVertex(utils::SkipList<Vertex>::Iterator it, utils::SkipList<Vertex>::Iterator end,
std::optional<VertexAccessor> *vertex, Transaction *tx, View view, Indices *indices,
Constraints *constraints, Config::Items config) {
Constraints *constraints, Config::Items config, const SchemaValidator &schema_validator) {
while (it != end) {
*vertex = VertexAccessor::Create(&*it, tx, indices, constraints, config, view);
*vertex = VertexAccessor::Create(&*it, tx, indices, constraints, config, schema_validator, view);
if (!*vertex) {
++it;
continue;
@ -69,14 +74,14 @@ auto AdvanceToVisibleVertex(utils::SkipList<Vertex>::Iterator it, utils::SkipLis
AllVerticesIterable::Iterator::Iterator(AllVerticesIterable *self, utils::SkipList<Vertex>::Iterator it)
: self_(self),
it_(AdvanceToVisibleVertex(it, self->vertices_accessor_.end(), &self->vertex_, self->transaction_, self->view_,
self->indices_, self_->constraints_, self->config_)) {}
self->indices_, self_->constraints_, self->config_, *self->schema_validator_)) {}
VertexAccessor AllVerticesIterable::Iterator::operator*() const { return *self_->vertex_; }
AllVerticesIterable::Iterator &AllVerticesIterable::Iterator::operator++() {
++it_;
it_ = AdvanceToVisibleVertex(it_, self_->vertices_accessor_.end(), &self_->vertex_, self_->transaction_, self_->view_,
self_->indices_, self_->constraints_, self_->config_);
self_->indices_, self_->constraints_, self_->config_, *self_->schema_validator_);
return *this;
}
@ -300,7 +305,8 @@ bool VerticesIterable::Iterator::operator==(const Iterator &other) const {
}
Storage::Storage(Config config)
: indices_(&constraints_, config.items),
: schema_validator_(schemas_),
indices_(&constraints_, config.items, schema_validator_),
isolation_level_(config.transaction.isolation_level),
config_(config),
snapshot_directory_(config_.durability.storage_directory / durability::kSnapshotDirectory),
@ -462,7 +468,8 @@ Storage::Accessor::~Accessor() {
FinalizeTransaction();
}
VertexAccessor Storage::Accessor::CreateVertex() {
// TODO Remove when import csv is fixed
[[deprecated]] VertexAccessor Storage::Accessor::CreateVertex() {
OOMExceptionEnabler oom_exception;
auto gid = storage_->vertex_id_.fetch_add(1, std::memory_order_acq_rel);
auto acc = storage_->vertices_.access();
@ -470,10 +477,12 @@ VertexAccessor Storage::Accessor::CreateVertex() {
auto [it, inserted] = acc.insert(Vertex{Gid::FromUint(gid), delta});
MG_ASSERT(inserted, "The vertex must be inserted here!");
MG_ASSERT(it != acc.end(), "Invalid Vertex accessor!");
delta->prev.Set(&*it);
return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_};
return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_, storage_->schema_validator_};
}
// TODO Remove when replication is fixed
VertexAccessor Storage::Accessor::CreateVertex(Gid gid) {
OOMExceptionEnabler oom_exception;
// NOTE: When we update the next `vertex_id_` here we perform a RMW
@ -486,18 +495,53 @@ VertexAccessor Storage::Accessor::CreateVertex(Gid gid) {
std::memory_order_release);
auto acc = storage_->vertices_.access();
auto *delta = CreateDeleteObjectDelta(&transaction_);
auto [it, inserted] = acc.insert(Vertex{gid, delta});
auto [it, inserted] = acc.insert(Vertex{gid});
MG_ASSERT(inserted, "The vertex must be inserted here!");
MG_ASSERT(it != acc.end(), "Invalid Vertex accessor!");
delta->prev.Set(&*it);
return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_};
return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_, storage_->schema_validator_};
}
ResultSchema<VertexAccessor> Storage::Accessor::CreateVertexAndValidate(
LabelId primary_label, const std::vector<LabelId> &labels,
const std::vector<std::pair<PropertyId, PropertyValue>> &properties) {
auto maybe_schema_violation = GetSchemaValidator().ValidateVertexCreate(primary_label, labels, properties);
if (maybe_schema_violation) {
return {std::move(*maybe_schema_violation)};
}
OOMExceptionEnabler oom_exception;
auto gid = storage_->vertex_id_.fetch_add(1, std::memory_order_acq_rel);
auto acc = storage_->vertices_.access();
auto *delta = CreateDeleteObjectDelta(&transaction_);
auto [it, inserted] = acc.insert(Vertex{Gid::FromUint(gid), delta, primary_label});
MG_ASSERT(inserted, "The vertex must be inserted here!");
MG_ASSERT(it != acc.end(), "Invalid Vertex accessor!");
delta->prev.Set(&*it);
auto va = VertexAccessor{
&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_, storage_->schema_validator_};
for (const auto label : labels) {
const auto maybe_error = va.AddLabel(label);
if (maybe_error.HasError()) {
return {maybe_error.GetError()};
}
}
// Set properties
for (auto [property_id, property_value] : properties) {
const auto maybe_error = va.SetProperty(property_id, property_value);
if (maybe_error.HasError()) {
return {maybe_error.GetError()};
}
}
return va;
}
std::optional<VertexAccessor> Storage::Accessor::FindVertex(Gid gid, View view) {
auto acc = storage_->vertices_.access();
auto it = acc.find(gid);
if (it == acc.end()) return std::nullopt;
return VertexAccessor::Create(&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_, view);
return VertexAccessor::Create(&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_,
storage_->schema_validator_, view);
}
Result<std::optional<VertexAccessor>> Storage::Accessor::DeleteVertex(VertexAccessor *vertex) {
@ -520,7 +564,7 @@ Result<std::optional<VertexAccessor>> Storage::Accessor::DeleteVertex(VertexAcce
vertex_ptr->deleted = true;
return std::make_optional<VertexAccessor>(vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_,
config_, true);
config_, storage_->schema_validator_, true);
}
Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Storage::Accessor::DetachDeleteVertex(
@ -550,7 +594,7 @@ Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Stor
for (const auto &item : in_edges) {
auto [edge_type, from_vertex, edge] = item;
EdgeAccessor e(edge, edge_type, from_vertex, vertex_ptr, &transaction_, &storage_->indices_,
&storage_->constraints_, config_);
&storage_->constraints_, config_, storage_->schema_validator_);
auto ret = DeleteEdge(&e);
if (ret.HasError()) {
MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!");
@ -564,7 +608,7 @@ Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Stor
for (const auto &item : out_edges) {
auto [edge_type, to_vertex, edge] = item;
EdgeAccessor e(edge, edge_type, vertex_ptr, to_vertex, &transaction_, &storage_->indices_, &storage_->constraints_,
config_);
config_, storage_->schema_validator_);
auto ret = DeleteEdge(&e);
if (ret.HasError()) {
MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!");
@ -590,7 +634,8 @@ Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Stor
vertex_ptr->deleted = true;
return std::make_optional<ReturnType>(
VertexAccessor{vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_, config_, true},
VertexAccessor{vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_, config_,
storage_->schema_validator_, true},
std::move(deleted_edges));
}
@ -650,7 +695,7 @@ Result<EdgeAccessor> Storage::Accessor::CreateEdge(VertexAccessor *from, VertexA
storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &storage_->indices_,
&storage_->constraints_, config_);
&storage_->constraints_, config_, storage_->schema_validator_);
}
Result<EdgeAccessor> Storage::Accessor::CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type,
@ -718,7 +763,7 @@ Result<EdgeAccessor> Storage::Accessor::CreateEdge(VertexAccessor *from, VertexA
storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &storage_->indices_,
&storage_->constraints_, config_);
&storage_->constraints_, config_, storage_->schema_validator_);
}
Result<std::optional<EdgeAccessor>> Storage::Accessor::DeleteEdge(EdgeAccessor *edge) {
@ -802,7 +847,8 @@ Result<std::optional<EdgeAccessor>> Storage::Accessor::DeleteEdge(EdgeAccessor *
storage_->edge_count_.fetch_add(-1, std::memory_order_acq_rel);
return std::make_optional<EdgeAccessor>(edge_ref, edge_type, from_vertex, to_vertex, &transaction_,
&storage_->indices_, &storage_->constraints_, config_, true);
&storage_->indices_, &storage_->constraints_, config_,
storage_->schema_validator_, true);
}
const std::string &Storage::Accessor::LabelToName(LabelId label) const { return storage_->LabelToName(label); }
@ -815,11 +861,11 @@ const std::string &Storage::Accessor::EdgeTypeToName(EdgeTypeId edge_type) const
return storage_->EdgeTypeToName(edge_type);
}
LabelId Storage::Accessor::NameToLabel(const std::string_view &name) { return storage_->NameToLabel(name); }
LabelId Storage::Accessor::NameToLabel(const std::string_view name) { return storage_->NameToLabel(name); }
PropertyId Storage::Accessor::NameToProperty(const std::string_view &name) { return storage_->NameToProperty(name); }
PropertyId Storage::Accessor::NameToProperty(const std::string_view name) { return storage_->NameToProperty(name); }
EdgeTypeId Storage::Accessor::NameToEdgeType(const std::string_view &name) { return storage_->NameToEdgeType(name); }
EdgeTypeId Storage::Accessor::NameToEdgeType(const std::string_view name) { return storage_->NameToEdgeType(name); }
void Storage::Accessor::AdvanceCommand() { ++transaction_.command_id; }
@ -846,11 +892,11 @@ utils::BasicResult<ConstraintViolation, void> Storage::Accessor::Commit(
auto validation_result = ValidateExistenceConstraints(*prev.vertex, storage_->constraints_);
if (validation_result) {
Abort();
return *validation_result;
return {*validation_result};
}
}
// Result of validating the vertex against unqiue constraints. It has to be
// Result of validating the vertex against unique constraints. It has to be
// declared outside of the critical section scope because its value is
// tested for Abort call which has to be done out of the scope.
std::optional<ConstraintViolation> unique_constraint_violation;
@ -931,7 +977,7 @@ utils::BasicResult<ConstraintViolation, void> Storage::Accessor::Commit(
if (unique_constraint_violation) {
Abort();
return *unique_constraint_violation;
return {*unique_constraint_violation};
}
}
is_transaction_active_ = false;
@ -1124,13 +1170,13 @@ const std::string &Storage::EdgeTypeToName(EdgeTypeId edge_type) const {
return name_id_mapper_.IdToName(edge_type.AsUint());
}
LabelId Storage::NameToLabel(const std::string_view &name) { return LabelId::FromUint(name_id_mapper_.NameToId(name)); }
LabelId Storage::NameToLabel(const std::string_view name) { return LabelId::FromUint(name_id_mapper_.NameToId(name)); }
PropertyId Storage::NameToProperty(const std::string_view &name) {
PropertyId Storage::NameToProperty(const std::string_view name) {
return PropertyId::FromUint(name_id_mapper_.NameToId(name));
}
EdgeTypeId Storage::NameToEdgeType(const std::string_view &name) {
EdgeTypeId Storage::NameToEdgeType(const std::string_view name) {
return EdgeTypeId::FromUint(name_id_mapper_.NameToId(name));
}
@ -1232,11 +1278,29 @@ UniqueConstraints::DeletionStatus Storage::DropUniqueConstraint(
return UniqueConstraints::DeletionStatus::SUCCESS;
}
const SchemaValidator &Storage::Accessor::GetSchemaValidator() const { return storage_->schema_validator_; }
ConstraintsInfo Storage::ListAllConstraints() const {
std::shared_lock<utils::RWLock> storage_guard_(main_lock_);
return {ListExistenceConstraints(constraints_), constraints_.unique_constraints.ListConstraints()};
}
SchemasInfo Storage::ListAllSchemas() const {
std::shared_lock<utils::RWLock> storage_guard_(main_lock_);
return {schemas_.ListSchemas()};
}
const Schemas::Schema *Storage::GetSchema(const LabelId primary_label) const {
std::shared_lock<utils::RWLock> storage_guard_(main_lock_);
return schemas_.GetSchema(primary_label);
}
bool Storage::CreateSchema(const LabelId primary_label, const std::vector<SchemaProperty> &schemas_types) {
return schemas_.CreateSchema(primary_label, schemas_types);
}
bool Storage::DropSchema(const LabelId primary_label) { return schemas_.DropSchema(primary_label); }
StorageInfo Storage::GetInfo() const {
auto vertex_count = vertices_.size();
auto edge_count = edge_count_.load(std::memory_order_acquire);
@ -1253,21 +1317,22 @@ VerticesIterable Storage::Accessor::Vertices(LabelId label, View view) {
}
VerticesIterable Storage::Accessor::Vertices(LabelId label, PropertyId property, View view) {
return VerticesIterable(storage_->indices_.label_property_index.Vertices(label, property, std::nullopt, std::nullopt,
view, &transaction_));
return VerticesIterable(storage_->indices_.label_property_index.Vertices(
label, property, std::nullopt, std::nullopt, view, &transaction_, storage_->schema_validator_));
}
VerticesIterable Storage::Accessor::Vertices(LabelId label, PropertyId property, const PropertyValue &value,
View view) {
return VerticesIterable(storage_->indices_.label_property_index.Vertices(
label, property, utils::MakeBoundInclusive(value), utils::MakeBoundInclusive(value), view, &transaction_));
label, property, utils::MakeBoundInclusive(value), utils::MakeBoundInclusive(value), view, &transaction_,
storage_->schema_validator_));
}
VerticesIterable Storage::Accessor::Vertices(LabelId label, PropertyId property,
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) {
return VerticesIterable(
storage_->indices_.label_property_index.Vertices(label, property, lower_bound, upper_bound, view, &transaction_));
return VerticesIterable(storage_->indices_.label_property_index.Vertices(
label, property, lower_bound, upper_bound, view, &transaction_, storage_->schema_validator_));
}
Transaction Storage::CreateTransaction(IsolationLevel isolation_level) {
@ -1795,8 +1860,8 @@ utils::BasicResult<Storage::CreateSnapshotError> Storage::CreateSnapshot() {
// Create snapshot.
durability::CreateSnapshot(&transaction, snapshot_directory_, wal_directory_,
config_.durability.snapshot_retention_count, &vertices_, &edges_, &name_id_mapper_,
&indices_, &constraints_, config_.items, uuid_, epoch_id_, epoch_history_,
&file_retainer_);
&indices_, &constraints_, config_.items, schema_validator_, uuid_, epoch_id_,
epoch_history_, &file_retainer_);
// Finalize snapshot transaction.
commit_log_->MarkFinished(transaction.start_timestamp);

View File

@ -16,8 +16,10 @@
#include <optional>
#include <shared_mutex>
#include <variant>
#include <vector>
#include "io/network/endpoint.hpp"
#include "kvstore/kvstore.hpp"
#include "storage/v3/commit_log.hpp"
#include "storage/v3/config.hpp"
#include "storage/v3/constraints.hpp"
@ -25,14 +27,19 @@
#include "storage/v3/durability/wal.hpp"
#include "storage/v3/edge.hpp"
#include "storage/v3/edge_accessor.hpp"
#include "storage/v3/id_types.hpp"
#include "storage/v3/indices.hpp"
#include "storage/v3/isolation_level.hpp"
#include "storage/v3/mvcc.hpp"
#include "storage/v3/name_id_mapper.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/result.hpp"
#include "storage/v3/schema_validator.hpp"
#include "storage/v3/schemas.hpp"
#include "storage/v3/transaction.hpp"
#include "storage/v3/vertex.hpp"
#include "storage/v3/vertex_accessor.hpp"
#include "utils/exceptions.hpp"
#include "utils/file_locker.hpp"
#include "utils/on_scope_exit.hpp"
#include "utils/rw_lock.hpp"
@ -66,6 +73,7 @@ class AllVerticesIterable final {
Indices *indices_;
Constraints *constraints_;
Config::Items config_;
const SchemaValidator *schema_validator_;
std::optional<VertexAccessor> vertex_;
public:
@ -86,13 +94,15 @@ class AllVerticesIterable final {
};
AllVerticesIterable(utils::SkipList<Vertex>::Accessor vertices_accessor, Transaction *transaction, View view,
Indices *indices, Constraints *constraints, Config::Items config)
Indices *indices, Constraints *constraints, Config::Items config,
SchemaValidator *schema_validator)
: vertices_accessor_(std::move(vertices_accessor)),
transaction_(transaction),
view_(view),
indices_(indices),
constraints_(constraints),
config_(config) {}
config_(config),
schema_validator_(schema_validator) {}
Iterator begin() { return {this, vertices_accessor_.begin()}; }
Iterator end() { return {this, vertices_accessor_.end()}; }
@ -173,6 +183,11 @@ struct ConstraintsInfo {
std::vector<std::pair<LabelId, std::set<PropertyId>>> unique;
};
/// Structure used to return information about existing schemas in the storage
struct SchemasInfo {
Schemas::SchemasList schemas;
};
/// Structure used to return information about the storage.
struct StorageInfo {
uint64_t vertex_count;
@ -190,10 +205,6 @@ class Storage final {
/// @throw std::bad_alloc
explicit Storage(Config config = Config());
Storage(const Storage &) = delete;
Storage(Storage &&) = delete;
Storage &operator=(const Storage &) = delete;
Storage &operator=(Storage &&) = delete;
~Storage();
class Accessor final {
@ -213,15 +224,21 @@ class Storage final {
~Accessor();
/// @throw std::bad_alloc
VertexAccessor CreateVertex();
VertexAccessor CreateVertex(Gid gid);
/// @throw std::bad_alloc
ResultSchema<VertexAccessor> CreateVertexAndValidate(
LabelId primary_label, const std::vector<LabelId> &labels,
const std::vector<std::pair<PropertyId, PropertyValue>> &properties);
std::optional<VertexAccessor> FindVertex(Gid gid, View view);
VerticesIterable Vertices(View view) {
return VerticesIterable(AllVerticesIterable(storage_->vertices_.access(), &transaction_, view,
&storage_->indices_, &storage_->constraints_,
storage_->config_.items));
&storage_->indices_, &storage_->constraints_, storage_->config_.items,
&storage_->schema_validator_));
}
VerticesIterable Vertices(LabelId label, View view);
@ -287,13 +304,13 @@ class Storage final {
const std::string &EdgeTypeToName(EdgeTypeId edge_type) const;
/// @throw std::bad_alloc if unable to insert a new mapping
LabelId NameToLabel(const std::string_view &name);
LabelId NameToLabel(std::string_view name);
/// @throw std::bad_alloc if unable to insert a new mapping
PropertyId NameToProperty(const std::string_view &name);
PropertyId NameToProperty(std::string_view name);
/// @throw std::bad_alloc if unable to insert a new mapping
EdgeTypeId NameToEdgeType(const std::string_view &name);
EdgeTypeId NameToEdgeType(std::string_view name);
bool LabelIndexExists(LabelId label) const { return storage_->indices_.label_index.IndexExists(label); }
@ -310,6 +327,10 @@ class Storage final {
storage_->constraints_.unique_constraints.ListConstraints()};
}
const SchemaValidator &GetSchemaValidator() const;
SchemasInfo ListAllSchemas() const { return {storage_->schemas_.ListSchemas()}; }
void AdvanceCommand();
/// Commit returns `ConstraintViolation` if the changes made by this
@ -325,7 +346,7 @@ class Storage final {
private:
/// @throw std::bad_alloc
VertexAccessor CreateVertex(Gid gid);
VertexAccessor CreateVertex(Gid gid, LabelId primary_label);
/// @throw std::bad_alloc
Result<EdgeAccessor> CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type, Gid gid);
@ -347,13 +368,13 @@ class Storage final {
const std::string &EdgeTypeToName(EdgeTypeId edge_type) const;
/// @throw std::bad_alloc if unable to insert a new mapping
LabelId NameToLabel(const std::string_view &name);
LabelId NameToLabel(std::string_view name);
/// @throw std::bad_alloc if unable to insert a new mapping
PropertyId NameToProperty(const std::string_view &name);
PropertyId NameToProperty(std::string_view name);
/// @throw std::bad_alloc if unable to insert a new mapping
EdgeTypeId NameToEdgeType(const std::string_view &name);
EdgeTypeId NameToEdgeType(std::string_view name);
/// @throw std::bad_alloc
bool CreateIndex(LabelId label, std::optional<uint64_t> desired_commit_timestamp = {});
@ -368,7 +389,7 @@ class Storage final {
IndicesInfo ListAllIndices() const;
/// Creates an existence constraint. Returns true if the constraint was
/// successfuly added, false if it already exists and a `ConstraintViolation`
/// successfully added, false if it already exists and a `ConstraintViolation`
/// if there is an existing vertex violating the constraint.
///
/// @throw std::bad_alloc
@ -406,6 +427,14 @@ class Storage final {
ConstraintsInfo ListAllConstraints() const;
SchemasInfo ListAllSchemas() const;
const Schemas::Schema *GetSchema(LabelId primary_label) const;
bool CreateSchema(LabelId primary_label, const std::vector<SchemaProperty> &schemas_types);
bool DropSchema(LabelId primary_label);
StorageInfo GetInfo() const;
bool LockPath();
@ -415,7 +444,12 @@ class Storage final {
bool SetMainReplicationRole();
enum class RegisterReplicaError : uint8_t { NAME_EXISTS, END_POINT_EXISTS, CONNECTION_FAILED };
enum class RegisterReplicaError : uint8_t {
NAME_EXISTS,
END_POINT_EXISTS,
CONNECTION_FAILED,
COULD_NOT_BE_PERSISTED
};
/// @pre The instance should have a MAIN role
/// @pre Timeout can only be set for SYNC replication
@ -493,8 +527,10 @@ class Storage final {
NameIdMapper name_id_mapper_;
SchemaValidator schema_validator_;
Constraints constraints_;
Indices indices_;
Schemas schemas_;
// Transaction engine
utils::SpinLock engine_lock_;

View File

@ -19,18 +19,39 @@
#include "storage/v3/edge_ref.hpp"
#include "storage/v3/id_types.hpp"
#include "storage/v3/property_store.hpp"
#include "utils/algorithm.hpp"
#include "utils/spin_lock.hpp"
namespace memgraph::storage::v3 {
struct Vertex {
Vertex(Gid gid, Delta *delta, LabelId primary_label)
: gid(gid), primary_label{primary_label}, deleted(false), delta(delta) {
MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT,
"Vertex must be created with an initial DELETE_OBJECT delta!");
}
// TODO remove this when import replication is solved
Vertex(Gid gid, LabelId primary_label) : gid(gid), primary_label{primary_label}, deleted(false) {
MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT,
"Vertex must be created with an initial DELETE_OBJECT delta!");
}
// TODO remove this when import csv is solved
Vertex(Gid gid, Delta *delta) : gid(gid), deleted(false), delta(delta) {
MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT,
"Vertex must be created with an initial DELETE_OBJECT delta!");
}
// TODO remove this when import replication is solved
explicit Vertex(Gid gid) : gid(gid), deleted(false) {
MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT,
"Vertex must be created with an initial DELETE_OBJECT delta!");
}
Gid gid;
LabelId primary_label;
std::vector<LabelId> labels;
PropertyStore properties;
@ -52,4 +73,8 @@ inline bool operator<(const Vertex &first, const Vertex &second) { return first.
inline bool operator==(const Vertex &first, const Gid &second) { return first.gid == second; }
inline bool operator<(const Vertex &first, const Gid &second) { return first.gid < second; }
inline bool VertexHasLabel(const Vertex &vertex, const LabelId label) {
return vertex.primary_label == label || utils::Contains(vertex.labels, label);
}
} // namespace memgraph::storage::v3

View File

@ -18,6 +18,8 @@
#include "storage/v3/indices.hpp"
#include "storage/v3/mvcc.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/schema_validator.hpp"
#include "storage/v3/vertex.hpp"
#include "utils/logging.hpp"
#include "utils/memory_tracker.hpp"
@ -61,12 +63,13 @@ std::pair<bool, bool> IsVisible(Vertex *vertex, Transaction *transaction, View v
} // namespace detail
std::optional<VertexAccessor> VertexAccessor::Create(Vertex *vertex, Transaction *transaction, Indices *indices,
Constraints *constraints, Config::Items config, View view) {
Constraints *constraints, Config::Items config,
const SchemaValidator &schema_validator, View view) {
if (const auto [exists, deleted] = detail::IsVisible(vertex, transaction, view); !exists || deleted) {
return std::nullopt;
}
return VertexAccessor{vertex, transaction, indices, constraints, config};
return VertexAccessor{vertex, transaction, indices, constraints, config, schema_validator};
}
bool VertexAccessor::IsVisible(View view) const {
@ -93,6 +96,28 @@ Result<bool> VertexAccessor::AddLabel(LabelId label) {
return true;
}
ResultSchema<bool> VertexAccessor::AddLabelAndValidate(LabelId label) {
if (const auto maybe_violation_error = vertex_validator_.ValidateAddLabel(label); maybe_violation_error) {
return {*maybe_violation_error};
}
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
if (!PrepareForWrite(transaction_, vertex_)) return {Error::SERIALIZATION_ERROR};
if (vertex_->deleted) return {Error::DELETED_OBJECT};
if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end()) return false;
CreateAndLinkDelta(transaction_, vertex_, Delta::RemoveLabelTag(), label);
vertex_->labels.push_back(label);
UpdateOnAddLabel(indices_, label, vertex_, *transaction_);
return true;
}
Result<bool> VertexAccessor::RemoveLabel(LabelId label) {
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
@ -110,6 +135,26 @@ Result<bool> VertexAccessor::RemoveLabel(LabelId label) {
return true;
}
ResultSchema<bool> VertexAccessor::RemoveLabelAndValidate(LabelId label) {
if (const auto maybe_violation_error = vertex_validator_.ValidateRemoveLabel(label); maybe_violation_error) {
return {*maybe_violation_error};
}
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
if (!PrepareForWrite(transaction_, vertex_)) return {Error::SERIALIZATION_ERROR};
if (vertex_->deleted) return {Error::DELETED_OBJECT};
auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label);
if (it == vertex_->labels.end()) return false;
CreateAndLinkDelta(transaction_, vertex_, Delta::AddLabelTag(), label);
std::swap(*it, *vertex_->labels.rbegin());
vertex_->labels.pop_back();
return true;
}
Result<bool> VertexAccessor::HasLabel(LabelId label, View view) const {
bool exists = true;
bool deleted = false;
@ -118,7 +163,7 @@ Result<bool> VertexAccessor::HasLabel(LabelId label, View view) const {
{
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
deleted = vertex_->deleted;
has_label = std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end();
has_label = VertexHasLabel(*vertex_, label);
delta = vertex_->delta;
}
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted, &has_label, label](const Delta &delta) {
@ -158,6 +203,40 @@ Result<bool> VertexAccessor::HasLabel(LabelId label, View view) const {
return has_label;
}
Result<LabelId> VertexAccessor::PrimaryLabel(const View view) const {
bool exists = true;
bool deleted = false;
Delta *delta = nullptr;
{
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
deleted = vertex_->deleted;
delta = vertex_->delta;
}
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted](const Delta &delta) {
switch (delta.action) {
case Delta::Action::DELETE_OBJECT: {
exists = false;
break;
}
case Delta::Action::RECREATE_OBJECT: {
deleted = false;
break;
}
case Delta::Action::ADD_LABEL:
case Delta::Action::REMOVE_LABEL:
case Delta::Action::SET_PROPERTY:
case Delta::Action::ADD_IN_EDGE:
case Delta::Action::ADD_OUT_EDGE:
case Delta::Action::REMOVE_IN_EDGE:
case Delta::Action::REMOVE_OUT_EDGE:
break;
}
});
if (!exists) return Error::NONEXISTENT_OBJECT;
if (!for_deleted_ && deleted) return Error::DELETED_OBJECT;
return vertex_->primary_label;
}
Result<std::vector<LabelId>> VertexAccessor::Labels(View view) const {
bool exists = true;
bool deleted = false;
@ -230,6 +309,36 @@ Result<PropertyValue> VertexAccessor::SetProperty(PropertyId property, const Pro
return std::move(current_value);
}
ResultSchema<PropertyValue> VertexAccessor::SetPropertyAndValidate(PropertyId property, const PropertyValue &value) {
if (auto maybe_violation_error = vertex_validator_.ValidatePropertyUpdate(property); maybe_violation_error) {
return {*maybe_violation_error};
}
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
if (!PrepareForWrite(transaction_, vertex_)) {
return {Error::SERIALIZATION_ERROR};
}
if (vertex_->deleted) {
return {Error::DELETED_OBJECT};
}
auto current_value = vertex_->properties.GetProperty(property);
// We could skip setting the value if the previous one is the same to the new
// one. This would save some memory as a delta would not be created as well as
// avoid copying the value. The reason we are not doing that is because the
// current code always follows the logical pattern of "create a delta" and
// "modify in-place". Additionally, the created delta will make other
// transactions get a SERIALIZATION_ERROR.
CreateAndLinkDelta(transaction_, vertex_, Delta::SetPropertyTag(), property, current_value);
vertex_->properties.SetProperty(property, value);
UpdateOnSetProperty(indices_, property, value, vertex_, *transaction_);
return std::move(current_value);
}
Result<std::map<PropertyId, PropertyValue>> VertexAccessor::ClearProperties() {
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
@ -414,7 +523,8 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const std::
ret.reserve(in_edges.size());
for (const auto &item : in_edges) {
const auto &[edge_type, from_vertex, edge] = item;
ret.emplace_back(edge, edge_type, from_vertex, vertex_, transaction_, indices_, constraints_, config_);
ret.emplace_back(edge, edge_type, from_vertex, vertex_, transaction_, indices_, constraints_, config_,
*vertex_validator_.schema_validator);
}
return std::move(ret);
}
@ -494,7 +604,8 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const std:
ret.reserve(out_edges.size());
for (const auto &item : out_edges) {
const auto &[edge_type, to_vertex, edge] = item;
ret.emplace_back(edge, edge_type, vertex_, to_vertex, transaction_, indices_, constraints_, config_);
ret.emplace_back(edge, edge_type, vertex_, to_vertex, transaction_, indices_, constraints_, config_,
*vertex_validator_.schema_validator);
}
return std::move(ret);
}
@ -575,4 +686,21 @@ Result<size_t> VertexAccessor::OutDegree(View view) const {
return degree;
}
VertexAccessor::VertexValidator::VertexValidator(const SchemaValidator &schema_validator, const Vertex *vertex)
: schema_validator{&schema_validator}, vertex_{vertex} {}
[[nodiscard]] std::optional<SchemaViolation> VertexAccessor::VertexValidator::ValidatePropertyUpdate(
PropertyId property_id) const {
MG_ASSERT(vertex_ != nullptr, "Cannot validate vertex which is nullptr");
return schema_validator->ValidatePropertyUpdate(vertex_->primary_label, property_id);
};
[[nodiscard]] std::optional<SchemaViolation> VertexAccessor::VertexValidator::ValidateAddLabel(LabelId label) const {
return schema_validator->ValidateLabelUpdate(label);
}
[[nodiscard]] std::optional<SchemaViolation> VertexAccessor::VertexValidator::ValidateRemoveLabel(LabelId label) const {
return schema_validator->ValidateLabelUpdate(label);
}
} // namespace memgraph::storage::v3

View File

@ -13,6 +13,8 @@
#include <optional>
#include "storage/v3/id_types.hpp"
#include "storage/v3/schema_validator.hpp"
#include "storage/v3/vertex.hpp"
#include "storage/v3/config.hpp"
@ -29,20 +31,39 @@ struct Constraints;
class VertexAccessor final {
private:
struct VertexValidator {
// TODO(jbajic) Beware since vertex is pointer it will be accessed even as nullptr
explicit VertexValidator(const SchemaValidator &schema_validator, const Vertex *vertex);
[[nodiscard]] std::optional<SchemaViolation> ValidatePropertyUpdate(PropertyId property_id) const;
[[nodiscard]] std::optional<SchemaViolation> ValidateAddLabel(LabelId label) const;
[[nodiscard]] std::optional<SchemaViolation> ValidateRemoveLabel(LabelId label) const;
const SchemaValidator *schema_validator;
private:
const Vertex *vertex_;
};
friend class Storage;
public:
// Be careful when using VertexAccessor since it can be instantiated with
// nullptr values
VertexAccessor(Vertex *vertex, Transaction *transaction, Indices *indices, Constraints *constraints,
Config::Items config, bool for_deleted = false)
Config::Items config, const SchemaValidator &schema_validator, bool for_deleted = false)
: vertex_(vertex),
transaction_(transaction),
indices_(indices),
constraints_(constraints),
config_(config),
vertex_validator_{schema_validator, vertex},
for_deleted_(for_deleted) {}
static std::optional<VertexAccessor> Create(Vertex *vertex, Transaction *transaction, Indices *indices,
Constraints *constraints, Config::Items config, View view);
Constraints *constraints, Config::Items config,
const SchemaValidator &schema_validator, View view);
/// @return true if the object is visible from the current transaction
bool IsVisible(View view) const;
@ -52,11 +73,23 @@ class VertexAccessor final {
/// @throw std::bad_alloc
Result<bool> AddLabel(LabelId label);
/// Add a label and return `true` if insertion took place.
/// `false` is returned if the label already existed, or SchemaViolation
/// if adding the label has violated one of the schema constraints.
/// @throw std::bad_alloc
ResultSchema<bool> AddLabelAndValidate(LabelId label);
/// Remove a label and return `true` if deletion took place.
/// `false` is returned if the vertex did not have a label already.
/// @throw std::bad_alloc
Result<bool> RemoveLabel(LabelId label);
/// Remove a label and return `true` if deletion took place.
/// `false` is returned if the vertex did not have a label already. or SchemaViolation
/// if adding the label has violated one of the schema constraints.
/// @throw std::bad_alloc
ResultSchema<bool> RemoveLabelAndValidate(LabelId label);
Result<bool> HasLabel(LabelId label, View view) const;
/// @throw std::bad_alloc
@ -64,10 +97,16 @@ class VertexAccessor final {
/// std::vector::max_size().
Result<std::vector<LabelId>> Labels(View view) const;
Result<LabelId> PrimaryLabel(View view) const;
/// Set a property value and return the old value.
/// @throw std::bad_alloc
Result<PropertyValue> SetProperty(PropertyId property, const PropertyValue &value);
/// Set a property value and return the old value or error.
/// @throw std::bad_alloc
ResultSchema<PropertyValue> SetPropertyAndValidate(PropertyId property, const PropertyValue &value);
/// Remove all properties and return the values of the removed properties.
/// @throw std::bad_alloc
Result<std::map<PropertyId, PropertyValue>> ClearProperties();
@ -96,6 +135,8 @@ class VertexAccessor final {
Gid Gid() const noexcept { return vertex_->gid; }
const SchemaValidator *GetSchemaValidator() const;
bool operator==(const VertexAccessor &other) const noexcept {
return vertex_ == other.vertex_ && transaction_ == other.transaction_;
}
@ -107,6 +148,7 @@ class VertexAccessor final {
Indices *indices_;
Constraints *constraints_;
Config::Items config_;
VertexValidator vertex_validator_;
// if the accessor was created for a deleted vertex.
// Accessor behaves differently for some methods based on this

View File

@ -75,8 +75,9 @@ target_link_libraries(${test_prefix}bfs_single_node mg-query)
add_unit_test(cypher_main_visitor.cpp)
target_link_libraries(${test_prefix}cypher_main_visitor mg-query)
# add_unit_test(interpreter.cpp ${CMAKE_SOURCE_DIR}/src/glue/communication.cpp)
# target_link_libraries(${test_prefix}interpreter mg-communication mg-query)
add_unit_test(interpreter.cpp ${CMAKE_SOURCE_DIR}/src/glue/communication.cpp)
target_link_libraries(${test_prefix}interpreter mg-communication mg-query)
add_unit_test(plan_pretty_print.cpp)
target_link_libraries(${test_prefix}plan_pretty_print mg-query)
@ -92,25 +93,28 @@ target_link_libraries(${test_prefix}query_expression_evaluator mg-query)
add_unit_test(query_plan.cpp)
target_link_libraries(${test_prefix}query_plan mg-query)
# add_unit_test(query_plan_accumulate_aggregate.cpp)
# target_link_libraries(${test_prefix}query_plan_accumulate_aggregate mg-query)
add_unit_test(query_plan_accumulate_aggregate.cpp)
target_link_libraries(${test_prefix}query_plan_accumulate_aggregate mg-query)
# add_unit_test(query_plan_bag_semantics.cpp)
# target_link_libraries(${test_prefix}query_plan_bag_semantics mg-query)
add_unit_test(query_plan_bag_semantics.cpp)
target_link_libraries(${test_prefix}query_plan_bag_semantics mg-query)
# add_unit_test(query_plan_create_set_remove_delete.cpp)
# target_link_libraries(${test_prefix}query_plan_create_set_remove_delete mg-query)
# add_unit_test(query_plan_edge_cases.cpp ${CMAKE_SOURCE_DIR}/src/glue/communication.cpp)
# target_link_libraries(${test_prefix}query_plan_edge_cases mg-communication mg-query)
# add_unit_test(query_plan_match_filter_return.cpp)
# target_link_libraries(${test_prefix}query_plan_match_filter_return mg-query)
add_unit_test(query_plan_create_set_remove_delete.cpp)
target_link_libraries(${test_prefix}query_plan_create_set_remove_delete mg-query)
add_unit_test(query_plan_edge_cases.cpp ${CMAKE_SOURCE_DIR}/src/glue/communication.cpp)
target_link_libraries(${test_prefix}query_plan_edge_cases mg-communication mg-query)
add_unit_test(query_plan_match_filter_return.cpp)
target_link_libraries(${test_prefix}query_plan_match_filter_return mg-query)
add_unit_test(query_plan_read_write_typecheck.cpp
${CMAKE_SOURCE_DIR}/src/query/plan/read_write_type_checker.cpp)
target_link_libraries(${test_prefix}query_plan_read_write_typecheck mg-query)
# add_unit_test(query_plan_v2_create_set_remove_delete.cpp)
# target_link_libraries(${test_prefix}query_plan_v2_create_set_remove_delete mg-query)
add_unit_test(query_plan_v2_create_set_remove_delete.cpp)
target_link_libraries(${test_prefix}query_plan_v2_create_set_remove_delete mg-query)
add_unit_test(query_pretty_print.cpp)
target_link_libraries(${test_prefix}query_pretty_print mg-query)
@ -278,66 +282,76 @@ target_link_libraries(${test_prefix}commit_log_v2 gflags mg-utils mg-storage-v2)
add_unit_test(property_value_v2.cpp)
target_link_libraries(${test_prefix}property_value_v2 mg-storage-v2 mg-utils)
# add_unit_test(storage_v2.cpp)
# target_link_libraries(${test_prefix}storage_v2 mg-storage-v2 storage_test_utils)
add_unit_test(storage_v2.cpp)
target_link_libraries(${test_prefix}storage_v2 mg-storage-v2 storage_test_utils)
add_unit_test(storage_v2_constraints.cpp)
target_link_libraries(${test_prefix}storage_v2_constraints mg-storage-v2)
add_unit_test(storage_v2_decoder_encoder.cpp)
target_link_libraries(${test_prefix}storage_v2_decoder_encoder mg-storage-v2)
# add_unit_test(storage_v2_durability.cpp)
# target_link_libraries(${test_prefix}storage_v2_durability mg-storage-v2)
add_unit_test(storage_v2_durability.cpp)
target_link_libraries(${test_prefix}storage_v2_durability mg-storage-v2)
add_unit_test(storage_v2_edge.cpp)
target_link_libraries(${test_prefix}storage_v2_edge mg-storage-v2)
# add_unit_test(storage_v2_edge.cpp)
# target_link_libraries(${test_prefix}storage_v2_edge mg-storage-v2)
add_unit_test(storage_v2_gc.cpp)
target_link_libraries(${test_prefix}storage_v2_gc mg-storage-v2)
# add_unit_test(storage_v2_indices.cpp)
# target_link_libraries(${test_prefix}storage_v2_indices mg-storage-v2 mg-utils)
add_unit_test(storage_v2_indices.cpp)
target_link_libraries(${test_prefix}storage_v2_indices mg-storage-v2 mg-utils)
add_unit_test(storage_v2_name_id_mapper.cpp)
target_link_libraries(${test_prefix}storage_v2_name_id_mapper mg-storage-v2)
add_unit_test(storage_v2_property_store.cpp)
target_link_libraries(${test_prefix}storage_v2_property_store mg-storage-v2 fmt)
# add_unit_test(storage_v2_wal_file.cpp)
# target_link_libraries(${test_prefix}storage_v2_wal_file mg-storage-v2 fmt)
add_unit_test(storage_v2_wal_file.cpp)
target_link_libraries(${test_prefix}storage_v2_wal_file mg-storage-v2 fmt)
add_unit_test(storage_v2_replication.cpp)
target_link_libraries(${test_prefix}storage_v2_replication mg-storage-v2 fmt)
# add_unit_test(storage_v2_replication.cpp)
# target_link_libraries(${test_prefix}storage_v2_replication mg-storage-v2 fmt)
add_unit_test(storage_v2_isolation_level.cpp)
target_link_libraries(${test_prefix}storage_v2_isolation_level mg-storage-v2)
# Test mg-storage-v3
add_unit_test(storage_v3.cpp)
target_link_libraries(${test_prefix}storage_v3 mg-storage-v3)
add_unit_test(storage_v3_schema.cpp)
target_link_libraries(${test_prefix}storage_v3_schema mg-storage-v2)
target_link_libraries(${test_prefix}storage_v3_schema mg-storage-v3)
add_unit_test(interpreter_v2.cpp ${CMAKE_SOURCE_DIR}/src/glue/communication.cpp)
target_link_libraries(${test_prefix}interpreter_v2 mg-storage-v2 mg-query mg-communication)
# Test mg-query-v3
add_unit_test(query_v2_interpreter.cpp ${CMAKE_SOURCE_DIR}/src/glue/v2/communication.cpp)
target_link_libraries(${test_prefix}query_v2_interpreter mg-storage-v3 mg-query-v2 mg-communication)
add_unit_test(query_v2_query_plan_accumulate_aggregate.cpp)
target_link_libraries(${test_prefix}query_v2_query_plan_accumulate_aggregate mg-query)
target_link_libraries(${test_prefix}query_v2_query_plan_accumulate_aggregate mg-query-v2)
add_unit_test(query_v2_query_plan_create_set_remove_delete.cpp)
target_link_libraries(${test_prefix}query_v2_query_plan_create_set_remove_delete mg-query)
target_link_libraries(${test_prefix}query_v2_query_plan_create_set_remove_delete mg-query-v2)
add_unit_test(query_v2_query_plan_bag_semantics.cpp)
target_link_libraries(${test_prefix}query_v2_query_plan_bag_semantics mg-query)
target_link_libraries(${test_prefix}query_v2_query_plan_bag_semantics mg-query-v2)
add_unit_test(query_v2_query_plan_edge_cases.cpp ${CMAKE_SOURCE_DIR}/src/glue/communication.cpp)
target_link_libraries(${test_prefix}query_v2_query_plan_edge_cases mg-communication mg-query)
add_unit_test(query_v2_query_plan_edge_cases.cpp ${CMAKE_SOURCE_DIR}/src/glue/v2/communication.cpp)
target_link_libraries(${test_prefix}query_v2_query_plan_edge_cases mg-communication mg-query-v2)
add_unit_test(query_v2_query_plan_v2_create_set_remove_delete.cpp)
target_link_libraries(${test_prefix}query_v2_query_plan_v2_create_set_remove_delete mg-query)
target_link_libraries(${test_prefix}query_v2_query_plan_v2_create_set_remove_delete mg-query-v2)
add_unit_test(query_v2_query_plan_match_filter_return.cpp)
target_link_libraries(${test_prefix}query_v2_query_plan_match_filter_return mg-query)
target_link_libraries(${test_prefix}query_v2_query_plan_match_filter_return mg-query-v2)
add_unit_test(query_v2_cypher_main_visitor.cpp)
target_link_libraries(${test_prefix}query_v2_cypher_main_visitor mg-query-v2)
add_unit_test(query_v2_query_required_privileges.cpp)
target_link_libraries(${test_prefix}query_v2_query_required_privileges mg-query-v2)
add_unit_test(replication_persistence_helper.cpp)
target_link_libraries(${test_prefix}replication_persistence_helper mg-storage-v2)

View File

@ -32,7 +32,6 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "common/types.hpp"
#include "query/exceptions.hpp"
#include "query/frontend/ast/ast.hpp"
#include "query/frontend/ast/cypher_main_visitor.hpp"
@ -2214,8 +2213,6 @@ TEST_P(CypherMainVisitorTest, GrantPrivilege) {
{AuthQuery::Privilege::MODULE_READ});
check_auth_query(&ast_generator, "GRANT MODULE_WRITE TO user", AuthQuery::Action::GRANT_PRIVILEGE, "", "", "user", {},
{AuthQuery::Privilege::MODULE_WRITE});
check_auth_query(&ast_generator, "GRANT SCHEMA TO user", AuthQuery::Action::GRANT_PRIVILEGE, "", "", "user", {},
{AuthQuery::Privilege::SCHEMA});
}
TEST_P(CypherMainVisitorTest, DenyPrivilege) {
@ -2256,8 +2253,6 @@ TEST_P(CypherMainVisitorTest, DenyPrivilege) {
{AuthQuery::Privilege::MODULE_READ});
check_auth_query(&ast_generator, "DENY MODULE_WRITE TO user", AuthQuery::Action::DENY_PRIVILEGE, "", "", "user", {},
{AuthQuery::Privilege::MODULE_WRITE});
check_auth_query(&ast_generator, "DENY SCHEMA TO user", AuthQuery::Action::DENY_PRIVILEGE, "", "", "user", {},
{AuthQuery::Privilege::SCHEMA});
}
TEST_P(CypherMainVisitorTest, RevokePrivilege) {
@ -2300,8 +2295,6 @@ TEST_P(CypherMainVisitorTest, RevokePrivilege) {
{}, {AuthQuery::Privilege::MODULE_READ});
check_auth_query(&ast_generator, "REVOKE MODULE_WRITE FROM user", AuthQuery::Action::REVOKE_PRIVILEGE, "", "", "user",
{}, {AuthQuery::Privilege::MODULE_WRITE});
check_auth_query(&ast_generator, "REVOKE SCHEMA FROM user", AuthQuery::Action::REVOKE_PRIVILEGE, "", "", "user", {},
{AuthQuery::Privilege::SCHEMA});
}
TEST_P(CypherMainVisitorTest, ShowPrivileges) {
@ -4216,110 +4209,3 @@ TEST_P(CypherMainVisitorTest, Foreach) {
ASSERT_TRUE(dynamic_cast<RemoveProperty *>(*++clauses.begin()));
}
}
TEST_P(CypherMainVisitorTest, TestShowSchemas) {
auto &ast_generator = *GetParam();
auto *query = dynamic_cast<SchemaQuery *>(ast_generator.ParseQuery("SHOW SCHEMAS"));
ASSERT_TRUE(query);
EXPECT_EQ(query->action_, SchemaQuery::Action::SHOW_SCHEMAS);
}
TEST_P(CypherMainVisitorTest, TestShowSchema) {
auto &ast_generator = *GetParam();
EXPECT_THROW(ast_generator.ParseQuery("SHOW SCHEMA ON label"), SyntaxException);
EXPECT_THROW(ast_generator.ParseQuery("SHOW SCHEMA :label"), SyntaxException);
EXPECT_THROW(ast_generator.ParseQuery("SHOW SCHEMA label"), SyntaxException);
auto *query = dynamic_cast<SchemaQuery *>(ast_generator.ParseQuery("SHOW SCHEMA ON :label"));
ASSERT_TRUE(query);
EXPECT_EQ(query->action_, SchemaQuery::Action::SHOW_SCHEMA);
EXPECT_EQ(query->label_, ast_generator.Label("label"));
}
void AssertSchemaPropertyMap(auto &schema_property_map,
std::vector<std::pair<std::string, memgraph::common::SchemaType>> properties_type,
auto &ast_generator) {
EXPECT_EQ(schema_property_map.size(), properties_type.size());
for (size_t i{0}; i < schema_property_map.size(); ++i) {
// Assert PropertyId
EXPECT_EQ(schema_property_map[i].first, ast_generator.Prop(properties_type[i].first));
// Assert Property Type
EXPECT_EQ(schema_property_map[i].second, properties_type[i].second);
}
}
TEST_P(CypherMainVisitorTest, TestCreateSchema) {
{
auto &ast_generator = *GetParam();
EXPECT_THROW(ast_generator.ParseQuery("CREATE SCHEMA ON :label"), SyntaxException);
EXPECT_THROW(ast_generator.ParseQuery("CREATE SCHEMA ON :label()"), SyntaxException);
EXPECT_THROW(ast_generator.ParseQuery("CREATE SCHEMA ON :label(123 INTEGER)"), SyntaxException);
EXPECT_THROW(ast_generator.ParseQuery("CREATE SCHEMA ON :label(name TYPE)"), SyntaxException);
EXPECT_THROW(ast_generator.ParseQuery("CREATE SCHEMA ON :label(name, age)"), SyntaxException);
EXPECT_THROW(ast_generator.ParseQuery("CREATE SCHEMA ON :label(name, DURATION)"), SyntaxException);
EXPECT_THROW(ast_generator.ParseQuery("CREATE SCHEMA ON label(name INTEGER)"), SyntaxException);
EXPECT_THROW(ast_generator.ParseQuery("CREATE SCHEMA ON :label(name INTEGER, name INTEGER)"), SemanticException);
EXPECT_THROW(ast_generator.ParseQuery("CREATE SCHEMA ON :label(name INTEGER, name STRING)"), SemanticException);
}
{
auto &ast_generator = *GetParam();
auto *query = dynamic_cast<SchemaQuery *>(ast_generator.ParseQuery("CREATE SCHEMA ON :label1(name STRING)"));
ASSERT_TRUE(query);
EXPECT_EQ(query->action_, SchemaQuery::Action::CREATE_SCHEMA);
EXPECT_EQ(query->label_, ast_generator.Label("label1"));
AssertSchemaPropertyMap(query->schema_type_map_, {{"name", memgraph::common::SchemaType::STRING}}, ast_generator);
}
{
auto &ast_generator = *GetParam();
auto *query = dynamic_cast<SchemaQuery *>(ast_generator.ParseQuery("CREATE SCHEMA ON :label2(name string)"));
ASSERT_TRUE(query);
EXPECT_EQ(query->action_, SchemaQuery::Action::CREATE_SCHEMA);
EXPECT_EQ(query->label_, ast_generator.Label("label2"));
AssertSchemaPropertyMap(query->schema_type_map_, {{"name", memgraph::common::SchemaType::STRING}}, ast_generator);
}
{
auto &ast_generator = *GetParam();
auto *query = dynamic_cast<SchemaQuery *>(
ast_generator.ParseQuery("CREATE SCHEMA ON :label3(first_name STRING, last_name STRING)"));
ASSERT_TRUE(query);
EXPECT_EQ(query->action_, SchemaQuery::Action::CREATE_SCHEMA);
EXPECT_EQ(query->label_, ast_generator.Label("label3"));
AssertSchemaPropertyMap(
query->schema_type_map_,
{{"first_name", memgraph::common::SchemaType::STRING}, {"last_name", memgraph::common::SchemaType::STRING}},
ast_generator);
}
{
auto &ast_generator = *GetParam();
auto *query = dynamic_cast<SchemaQuery *>(
ast_generator.ParseQuery("CREATE SCHEMA ON :label4(name STRING, age INTEGER, dur DURATION, birthday1 "
"LOCALDATETIME, birthday2 DATE, some_time LOCALTIME, speaks_truth BOOL)"));
ASSERT_TRUE(query);
EXPECT_EQ(query->action_, SchemaQuery::Action::CREATE_SCHEMA);
EXPECT_EQ(query->label_, ast_generator.Label("label4"));
AssertSchemaPropertyMap(query->schema_type_map_,
{
{"name", memgraph::common::SchemaType::STRING},
{"age", memgraph::common::SchemaType::INT},
{"dur", memgraph::common::SchemaType::DURATION},
{"birthday1", memgraph::common::SchemaType::LOCALDATETIME},
{"birthday2", memgraph::common::SchemaType::DATE},
{"some_time", memgraph::common::SchemaType::LOCALTIME},
{"speaks_truth", memgraph::common::SchemaType::BOOL},
},
ast_generator);
}
}
TEST_P(CypherMainVisitorTest, TestDropSchema) {
auto &ast_generator = *GetParam();
EXPECT_THROW(ast_generator.ParseQuery("DROP SCHEMA"), SyntaxException);
EXPECT_THROW(ast_generator.ParseQuery("DROP SCHEMA ON label"), SyntaxException);
EXPECT_THROW(ast_generator.ParseQuery("DROP SCHEMA :label"), SyntaxException);
EXPECT_THROW(ast_generator.ParseQuery("DROP SCHEMA ON :label()"), SyntaxException);
auto *query = dynamic_cast<SchemaQuery *>(ast_generator.ParseQuery("DROP SCHEMA ON :label"));
ASSERT_TRUE(query);
EXPECT_EQ(query->action_, SchemaQuery::Action::DROP_SCHEMA);
EXPECT_EQ(query->label_, ast_generator.Label("label"));
}

View File

@ -192,11 +192,6 @@ TEST_F(TestPrivilegeExtractor, ShowVersion) {
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::STATS));
}
TEST_F(TestPrivilegeExtractor, SchemaQuery) {
auto *query = storage.Create<SchemaQuery>();
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::SCHEMA));
}
TEST_F(TestPrivilegeExtractor, CallProcedureQuery) {
{
auto *query = QUERY(SINGLE_QUERY(CALL_PROCEDURE("mg.get_module_files")));

File diff suppressed because it is too large Load Diff

View File

@ -15,20 +15,21 @@
#include <filesystem>
#include <unordered_set>
#include "communication/bolt/v1/value.hpp"
#include "communication/result_stream_faker.hpp"
#include "glue/communication.hpp"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "query/auth_checker.hpp"
#include "query/config.hpp"
#include "query/exceptions.hpp"
#include "query/interpreter.hpp"
#include "query/stream.hpp"
#include "query/typed_value.hpp"
#include "query_common.hpp"
#include "storage/v2/isolation_level.hpp"
#include "storage/v2/property_value.hpp"
#include "communication/bolt/v1/value.hpp"
#include "glue/v2/communication.hpp"
#include "query/v2/auth_checker.hpp"
#include "query/v2/config.hpp"
#include "query/v2/exceptions.hpp"
#include "query/v2/interpreter.hpp"
#include "query/v2/stream.hpp"
#include "query/v2/typed_value.hpp"
#include "query_v2_query_common.hpp"
#include "result_stream_faker.hpp"
#include "storage/v3/isolation_level.hpp"
#include "storage/v3/property_value.hpp"
#include "utils/csv_parsing.hpp"
#include "utils/logging.hpp"
@ -48,13 +49,14 @@ auto StringToUnorderedSet(const std::string &element) {
};
struct InterpreterFaker {
InterpreterFaker(memgraph::storage::Storage *db, const memgraph::query::InterpreterConfig config,
InterpreterFaker(memgraph::storage::v3::Storage *db, const memgraph::query::v2::InterpreterConfig config,
const std::filesystem::path &data_directory)
: interpreter_context(db, config, data_directory), interpreter(&interpreter_context) {
interpreter_context.auth_checker = &auth_checker;
}
auto Prepare(const std::string &query, const std::map<std::string, memgraph::storage::PropertyValue> &params = {}) {
auto Prepare(const std::string &query,
const std::map<std::string, memgraph::storage::v3::PropertyValue> &params = {}) {
ResultStreamFaker stream(interpreter_context.db);
const auto [header, _, qid] = interpreter.Prepare(query, params, nullptr);
@ -72,7 +74,8 @@ struct InterpreterFaker {
*
* Return the query stream.
*/
auto Interpret(const std::string &query, const std::map<std::string, memgraph::storage::PropertyValue> &params = {}) {
auto Interpret(const std::string &query,
const std::map<std::string, memgraph::storage::v3::PropertyValue> &params = {}) {
auto prepare_result = Prepare(query, params);
auto &stream = prepare_result.first;
@ -82,9 +85,9 @@ struct InterpreterFaker {
return std::move(stream);
}
memgraph::query::AllowEverythingAuthChecker auth_checker;
memgraph::query::InterpreterContext interpreter_context;
memgraph::query::Interpreter interpreter;
memgraph::query::v2::AllowEverythingAuthChecker auth_checker;
memgraph::query::v2::InterpreterContext interpreter_context;
memgraph::query::v2::Interpreter interpreter;
};
} // namespace
@ -94,12 +97,13 @@ struct InterpreterFaker {
class InterpreterTest : public ::testing::Test {
protected:
memgraph::storage::Storage db_;
std::filesystem::path data_directory{std::filesystem::temp_directory_path() / "MG_tests_unit_interpreter"};
memgraph::storage::v3::Storage db_;
std::filesystem::path data_directory{std::filesystem::temp_directory_path() / "MG_tests_unit_query_v2_interpreter"};
InterpreterFaker default_interpreter{&db_, {}, data_directory};
auto Prepare(const std::string &query, const std::map<std::string, memgraph::storage::PropertyValue> &params = {}) {
auto Prepare(const std::string &query,
const std::map<std::string, memgraph::storage::v3::PropertyValue> &params = {}) {
return default_interpreter.Prepare(query, params);
}
@ -107,7 +111,8 @@ class InterpreterTest : public ::testing::Test {
default_interpreter.Pull(stream, n, qid);
}
auto Interpret(const std::string &query, const std::map<std::string, memgraph::storage::PropertyValue> &params = {}) {
auto Interpret(const std::string &query,
const std::map<std::string, memgraph::storage::v3::PropertyValue> &params = {}) {
return default_interpreter.Interpret(query, params);
}
};
@ -197,8 +202,8 @@ TEST_F(InterpreterTest, AstCache) {
// Run query with same ast multiple times with different parameters.
TEST_F(InterpreterTest, Parameters) {
{
auto stream = Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::PropertyValue(10)},
{"a b", memgraph::storage::PropertyValue(15)}});
auto stream = Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::v3::PropertyValue(10)},
{"a b", memgraph::storage::v3::PropertyValue(15)}});
ASSERT_EQ(stream.GetHeader().size(), 1U);
EXPECT_EQ(stream.GetHeader()[0], "$2 + $`a b`");
ASSERT_EQ(stream.GetResults().size(), 1U);
@ -207,9 +212,9 @@ TEST_F(InterpreterTest, Parameters) {
}
{
// Not needed parameter.
auto stream = Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::PropertyValue(10)},
{"a b", memgraph::storage::PropertyValue(15)},
{"c", memgraph::storage::PropertyValue(10)}});
auto stream = Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::v3::PropertyValue(10)},
{"a b", memgraph::storage::v3::PropertyValue(15)},
{"c", memgraph::storage::v3::PropertyValue(10)}});
ASSERT_EQ(stream.GetHeader().size(), 1U);
EXPECT_EQ(stream.GetHeader()[0], "$2 + $`a b`");
ASSERT_EQ(stream.GetResults().size(), 1U);
@ -218,28 +223,29 @@ TEST_F(InterpreterTest, Parameters) {
}
{
// Cached ast, different parameters.
auto stream = Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::PropertyValue("da")},
{"a b", memgraph::storage::PropertyValue("ne")}});
auto stream = Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::v3::PropertyValue("da")},
{"a b", memgraph::storage::v3::PropertyValue("ne")}});
ASSERT_EQ(stream.GetResults().size(), 1U);
ASSERT_EQ(stream.GetResults()[0].size(), 1U);
ASSERT_EQ(stream.GetResults()[0][0].ValueString(), "dane");
}
{
// Non-primitive literal.
auto stream =
Interpret("RETURN $2", {{"2", memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
memgraph::storage::PropertyValue(5), memgraph::storage::PropertyValue(2),
memgraph::storage::PropertyValue(3)})}});
auto stream = Interpret(
"RETURN $2", {{"2", memgraph::storage::v3::PropertyValue(std::vector<memgraph::storage::v3::PropertyValue>{
memgraph::storage::v3::PropertyValue(5), memgraph::storage::v3::PropertyValue(2),
memgraph::storage::v3::PropertyValue(3)})}});
ASSERT_EQ(stream.GetResults().size(), 1U);
ASSERT_EQ(stream.GetResults()[0].size(), 1U);
auto result = memgraph::query::test_common::ToIntList(memgraph::glue::ToTypedValue(stream.GetResults()[0][0]));
auto result =
memgraph::query::v2::test_common::ToIntList(memgraph::glue::v2::ToTypedValue(stream.GetResults()[0][0]));
ASSERT_THAT(result, testing::ElementsAre(5, 2, 3));
}
{
// Cached ast, unprovided parameter.
ASSERT_THROW(Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::PropertyValue("da")},
{"ab", memgraph::storage::PropertyValue("ne")}}),
memgraph::query::UnprovidedParameterError);
ASSERT_THROW(Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::v3::PropertyValue("da")},
{"ab", memgraph::storage::v3::PropertyValue("ne")}}),
memgraph::query::v2::UnprovidedParameterError);
}
}
@ -247,12 +253,12 @@ TEST_F(InterpreterTest, Parameters) {
TEST_F(InterpreterTest, ParametersAsPropertyMap) {
{
EXPECT_NO_THROW(Interpret("CREATE SCHEMA ON :label(name STRING, age INTEGER)"));
std::map<std::string, memgraph::storage::PropertyValue> property_map{};
property_map["name"] = memgraph::storage::PropertyValue("name1");
property_map["age"] = memgraph::storage::PropertyValue(25);
std::map<std::string, memgraph::storage::v3::PropertyValue> property_map{};
property_map["name"] = memgraph::storage::v3::PropertyValue("name1");
property_map["age"] = memgraph::storage::v3::PropertyValue(25);
auto stream =
Interpret("CREATE (n:label $prop) RETURN n", {
{"prop", memgraph::storage::PropertyValue(property_map)},
{"prop", memgraph::storage::v3::PropertyValue(property_map)},
});
ASSERT_EQ(stream.GetHeader().size(), 1U);
ASSERT_EQ(stream.GetHeader()[0], "n");
@ -264,13 +270,13 @@ TEST_F(InterpreterTest, ParametersAsPropertyMap) {
}
{
EXPECT_NO_THROW(Interpret("CREATE SCHEMA ON :Person(name STRING, age INTEGER)"));
std::map<std::string, memgraph::storage::PropertyValue> property_map{};
property_map["name"] = memgraph::storage::PropertyValue("name1");
property_map["age"] = memgraph::storage::PropertyValue(25);
std::map<std::string, memgraph::storage::v3::PropertyValue> property_map{};
property_map["name"] = memgraph::storage::v3::PropertyValue("name1");
property_map["age"] = memgraph::storage::v3::PropertyValue(25);
EXPECT_NO_THROW(Interpret("CREATE (:Person {name: 'test', age: 30})"));
auto stream = Interpret("MATCH (m:Person) CREATE (n:Person $prop) RETURN n",
{
{"prop", memgraph::storage::PropertyValue(property_map)},
{"prop", memgraph::storage::v3::PropertyValue(property_map)},
});
ASSERT_EQ(stream.GetHeader().size(), 1U);
ASSERT_EQ(stream.GetHeader()[0], "n");
@ -282,12 +288,12 @@ TEST_F(InterpreterTest, ParametersAsPropertyMap) {
}
{
EXPECT_NO_THROW(Interpret("CREATE SCHEMA ON :L1(name STRING)"));
std::map<std::string, memgraph::storage::PropertyValue> property_map{};
property_map["name"] = memgraph::storage::PropertyValue("name1");
property_map["weight"] = memgraph::storage::PropertyValue(121);
std::map<std::string, memgraph::storage::v3::PropertyValue> property_map{};
property_map["name"] = memgraph::storage::v3::PropertyValue("name1");
property_map["weight"] = memgraph::storage::v3::PropertyValue(121);
auto stream = Interpret("CREATE (:L1 {name: 'name1'})-[r:TO $prop]->(:L1 {name: 'name2'}) RETURN r",
{
{"prop", memgraph::storage::PropertyValue(property_map)},
{"prop", memgraph::storage::v3::PropertyValue(property_map)},
});
ASSERT_EQ(stream.GetHeader().size(), 1U);
ASSERT_EQ(stream.GetHeader()[0], "r");
@ -298,25 +304,25 @@ TEST_F(InterpreterTest, ParametersAsPropertyMap) {
EXPECT_EQ(result.properties["weight"].ValueInt(), 121);
}
{
std::map<std::string, memgraph::storage::PropertyValue> property_map{};
property_map["name"] = memgraph::storage::PropertyValue("name1");
property_map["age"] = memgraph::storage::PropertyValue(15);
std::map<std::string, memgraph::storage::v3::PropertyValue> property_map{};
property_map["name"] = memgraph::storage::v3::PropertyValue("name1");
property_map["age"] = memgraph::storage::v3::PropertyValue(15);
ASSERT_THROW(Interpret("MATCH (n $prop) RETURN n",
{
{"prop", memgraph::storage::PropertyValue(property_map)},
{"prop", memgraph::storage::v3::PropertyValue(property_map)},
}),
memgraph::query::SemanticException);
memgraph::query::v2::SemanticException);
}
{
EXPECT_NO_THROW(Interpret("CREATE SCHEMA ON :L2(name STRING, age INTEGER)"));
std::map<std::string, memgraph::storage::PropertyValue> property_map{};
property_map["name"] = memgraph::storage::PropertyValue("name1");
property_map["age"] = memgraph::storage::PropertyValue(15);
std::map<std::string, memgraph::storage::v3::PropertyValue> property_map{};
property_map["name"] = memgraph::storage::v3::PropertyValue("name1");
property_map["age"] = memgraph::storage::v3::PropertyValue(15);
ASSERT_THROW(Interpret("MERGE (n:L2 $prop) RETURN n",
{
{"prop", memgraph::storage::PropertyValue(property_map)},
{"prop", memgraph::storage::v3::PropertyValue(property_map)},
}),
memgraph::query::SemanticException);
memgraph::query::v2::SemanticException);
}
}
@ -331,26 +337,26 @@ TEST_F(InterpreterTest, Bfs) {
const auto kReachable = "reachable";
const auto kId = "id";
std::vector<std::vector<memgraph::query::VertexAccessor>> levels(kNumLevels);
std::vector<std::vector<memgraph::query::v2::VertexAccessor>> levels(kNumLevels);
int id = 0;
// Set up.
{
auto storage_dba = db_.Access();
memgraph::query::DbAccessor dba(&storage_dba);
memgraph::query::v2::DbAccessor dba(&storage_dba);
auto add_node = [&](int level, bool reachable) {
auto node = dba.InsertVertex();
MG_ASSERT(node.SetProperty(dba.NameToProperty(kId), memgraph::storage::PropertyValue(id++)).HasValue());
MG_ASSERT(node.SetProperty(dba.NameToProperty(kId), memgraph::storage::v3::PropertyValue(id++)).HasValue());
MG_ASSERT(
node.SetProperty(dba.NameToProperty(kReachable), memgraph::storage::PropertyValue(reachable)).HasValue());
node.SetProperty(dba.NameToProperty(kReachable), memgraph::storage::v3::PropertyValue(reachable)).HasValue());
levels[level].push_back(node);
return node;
};
auto add_edge = [&](auto &v1, auto &v2, bool reachable) {
auto edge = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("edge"));
MG_ASSERT(
edge->SetProperty(dba.NameToProperty(kReachable), memgraph::storage::PropertyValue(reachable)).HasValue());
MG_ASSERT(edge->SetProperty(dba.NameToProperty(kReachable), memgraph::storage::v3::PropertyValue(reachable))
.HasValue());
};
// Add source node.
@ -497,45 +503,45 @@ TEST_F(InterpreterTest, ShortestPath) {
TEST_F(InterpreterTest, CreateLabelIndexInMulticommandTransaction) {
Interpret("BEGIN");
ASSERT_THROW(Interpret("CREATE INDEX ON :X"), memgraph::query::IndexInMulticommandTxException);
ASSERT_THROW(Interpret("CREATE INDEX ON :X"), memgraph::query::v2::IndexInMulticommandTxException);
Interpret("ROLLBACK");
}
TEST_F(InterpreterTest, CreateLabelPropertyIndexInMulticommandTransaction) {
Interpret("BEGIN");
ASSERT_THROW(Interpret("CREATE INDEX ON :X(y)"), memgraph::query::IndexInMulticommandTxException);
ASSERT_THROW(Interpret("CREATE INDEX ON :X(y)"), memgraph::query::v2::IndexInMulticommandTxException);
Interpret("ROLLBACK");
}
TEST_F(InterpreterTest, CreateExistenceConstraintInMulticommandTransaction) {
Interpret("BEGIN");
ASSERT_THROW(Interpret("CREATE CONSTRAINT ON (n:A) ASSERT EXISTS (n.a)"),
memgraph::query::ConstraintInMulticommandTxException);
memgraph::query::v2::ConstraintInMulticommandTxException);
Interpret("ROLLBACK");
}
TEST_F(InterpreterTest, CreateUniqueConstraintInMulticommandTransaction) {
Interpret("BEGIN");
ASSERT_THROW(Interpret("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.b IS UNIQUE"),
memgraph::query::ConstraintInMulticommandTxException);
memgraph::query::v2::ConstraintInMulticommandTxException);
Interpret("ROLLBACK");
}
TEST_F(InterpreterTest, ShowIndexInfoInMulticommandTransaction) {
Interpret("BEGIN");
ASSERT_THROW(Interpret("SHOW INDEX INFO"), memgraph::query::InfoInMulticommandTxException);
ASSERT_THROW(Interpret("SHOW INDEX INFO"), memgraph::query::v2::InfoInMulticommandTxException);
Interpret("ROLLBACK");
}
TEST_F(InterpreterTest, ShowConstraintInfoInMulticommandTransaction) {
Interpret("BEGIN");
ASSERT_THROW(Interpret("SHOW CONSTRAINT INFO"), memgraph::query::InfoInMulticommandTxException);
ASSERT_THROW(Interpret("SHOW CONSTRAINT INFO"), memgraph::query::v2::InfoInMulticommandTxException);
Interpret("ROLLBACK");
}
TEST_F(InterpreterTest, ShowStorageInfoInMulticommandTransaction) {
Interpret("BEGIN");
ASSERT_THROW(Interpret("SHOW STORAGE INFO"), memgraph::query::InfoInMulticommandTxException);
ASSERT_THROW(Interpret("SHOW STORAGE INFO"), memgraph::query::v2::InfoInMulticommandTxException);
Interpret("ROLLBACK");
}
@ -546,20 +552,21 @@ TEST_F(InterpreterTest, ExistenceConstraintTest) {
Interpret("CREATE CONSTRAINT ON (n:A) ASSERT EXISTS (n.b);");
Interpret("CREATE (:A{a: 3, b:1})");
Interpret("CREATE (:A{a: 3, b:2})");
ASSERT_THROW(Interpret("CREATE (:A {a: 12})"), memgraph::query::QueryException);
ASSERT_THROW(Interpret("CREATE (:A {a: 12})"), memgraph::query::v2::QueryException);
Interpret("MATCH (n:A{a:3, b: 2}) SET n.b=5");
Interpret("CREATE (:A{a:2, b: 3})");
Interpret("MATCH (n:A{a:3, b: 1}) DETACH DELETE n");
Interpret("CREATE (n:A{a:2, b: 3})");
ASSERT_THROW(Interpret("CREATE CONSTRAINT ON (n:A) ASSERT EXISTS (n.c);"), memgraph::query::QueryRuntimeException);
ASSERT_THROW(Interpret("CREATE CONSTRAINT ON (n:A) ASSERT EXISTS (n.c);"),
memgraph::query::v2::QueryRuntimeException);
}
TEST_F(InterpreterTest, UniqueConstraintTest) {
ASSERT_NO_THROW(Interpret("CREATE SCHEMA ON :A(a INTEGER);"));
// Empty property list should result with syntax exception.
ASSERT_THROW(Interpret("CREATE CONSTRAINT ON (n:A) ASSERT IS UNIQUE;"), memgraph::query::SyntaxException);
ASSERT_THROW(Interpret("DROP CONSTRAINT ON (n:A) ASSERT IS UNIQUE;"), memgraph::query::SyntaxException);
ASSERT_THROW(Interpret("CREATE CONSTRAINT ON (n:A) ASSERT IS UNIQUE;"), memgraph::query::v2::SyntaxException);
ASSERT_THROW(Interpret("DROP CONSTRAINT ON (n:A) ASSERT IS UNIQUE;"), memgraph::query::v2::SyntaxException);
// Too large list of properties should also result with syntax exception.
{
@ -572,26 +579,27 @@ TEST_F(InterpreterTest, UniqueConstraintTest) {
stream << " IS UNIQUE;";
std::string create_query = "CREATE CONSTRAINT" + stream.str();
std::string drop_query = "DROP CONSTRAINT" + stream.str();
ASSERT_THROW(Interpret(create_query), memgraph::query::SyntaxException);
ASSERT_THROW(Interpret(drop_query), memgraph::query::SyntaxException);
ASSERT_THROW(Interpret(create_query), memgraph::query::v2::SyntaxException);
ASSERT_THROW(Interpret(drop_query), memgraph::query::v2::SyntaxException);
}
// Providing property list with duplicates results with syntax exception.
ASSERT_THROW(Interpret("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.b, n.a IS UNIQUE;"),
memgraph::query::SyntaxException);
ASSERT_THROW(Interpret("DROP CONSTRAINT ON (n:A) ASSERT n.a, n.b, n.a IS UNIQUE;"), memgraph::query::SyntaxException);
memgraph::query::v2::SyntaxException);
ASSERT_THROW(Interpret("DROP CONSTRAINT ON (n:A) ASSERT n.a, n.b, n.a IS UNIQUE;"),
memgraph::query::v2::SyntaxException);
// Commit of vertex should fail if a constraint is violated.
Interpret("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.b IS UNIQUE;");
Interpret("CREATE (:A{a:1, b:2})");
Interpret("CREATE (:A{a:1, b:3})");
ASSERT_THROW(Interpret("CREATE (:A{a:1, b:2})"), memgraph::query::QueryException);
ASSERT_THROW(Interpret("CREATE (:A{a:1, b:2})"), memgraph::query::v2::QueryException);
// Attempt to create a constraint should fail if it's violated.
Interpret("CREATE (:A{a:1, c:2})");
Interpret("CREATE (:A{a:1, c:2})");
ASSERT_THROW(Interpret("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.c IS UNIQUE;"),
memgraph::query::QueryRuntimeException);
memgraph::query::v2::QueryRuntimeException);
Interpret("MATCH (n:A{a:2, b:2}) SET n.a=1");
Interpret("CREATE (:A{a:2})");
@ -716,7 +724,7 @@ TEST_F(InterpreterTest, ExplainQueryWithParams) {
EXPECT_EQ(interpreter_context.plan_cache.size(), 0U);
EXPECT_EQ(interpreter_context.ast_cache.size(), 0U);
auto stream =
Interpret("EXPLAIN MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::PropertyValue(42)}});
Interpret("EXPLAIN MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::v3::PropertyValue(42)}});
ASSERT_EQ(stream.GetHeader().size(), 1U);
EXPECT_EQ(stream.GetHeader().front(), "QUERY PLAN");
std::vector<std::string> expected_rows{" * Produce {n}", " * Filter", " * ScanAll (n)", " * Once"};
@ -731,7 +739,7 @@ TEST_F(InterpreterTest, ExplainQueryWithParams) {
EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
// We should have AST cache for EXPLAIN ... and for inner MATCH ...
EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
Interpret("MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::PropertyValue("something else")}});
Interpret("MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::v3::PropertyValue("something else")}});
EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
}
@ -801,7 +809,7 @@ TEST_F(InterpreterTest, ProfileQueryMultiplePulls) {
TEST_F(InterpreterTest, ProfileQueryInMulticommandTransaction) {
Interpret("BEGIN");
ASSERT_THROW(Interpret("PROFILE MATCH (n) RETURN *;"), memgraph::query::ProfileInMulticommandTxException);
ASSERT_THROW(Interpret("PROFILE MATCH (n) RETURN *;"), memgraph::query::v2::ProfileInMulticommandTxException);
Interpret("ROLLBACK");
}
@ -811,7 +819,7 @@ TEST_F(InterpreterTest, ProfileQueryWithParams) {
EXPECT_EQ(interpreter_context.plan_cache.size(), 0U);
EXPECT_EQ(interpreter_context.ast_cache.size(), 0U);
auto stream =
Interpret("PROFILE MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::PropertyValue(42)}});
Interpret("PROFILE MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::v3::PropertyValue(42)}});
std::vector<std::string> expected_header{"OPERATOR", "ACTUAL HITS", "RELATIVE TIME", "ABSOLUTE TIME"};
EXPECT_EQ(stream.GetHeader(), expected_header);
std::vector<std::string> expected_rows{"* Produce", "* Filter", "* ScanAll", "* Once"};
@ -826,7 +834,7 @@ TEST_F(InterpreterTest, ProfileQueryWithParams) {
EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
// We should have AST cache for PROFILE ... and for inner MATCH ...
EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
Interpret("MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::PropertyValue("something else")}});
Interpret("MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::v3::PropertyValue("something else")}});
EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
}
@ -860,10 +868,10 @@ TEST_F(InterpreterTest, ProfileQueryWithLiterals) {
TEST_F(InterpreterTest, Transactions) {
auto &interpreter = default_interpreter.interpreter;
{
ASSERT_THROW(interpreter.CommitTransaction(), memgraph::query::ExplicitTransactionUsageException);
ASSERT_THROW(interpreter.RollbackTransaction(), memgraph::query::ExplicitTransactionUsageException);
ASSERT_THROW(interpreter.CommitTransaction(), memgraph::query::v2::ExplicitTransactionUsageException);
ASSERT_THROW(interpreter.RollbackTransaction(), memgraph::query::v2::ExplicitTransactionUsageException);
interpreter.BeginTransaction();
ASSERT_THROW(interpreter.BeginTransaction(), memgraph::query::ExplicitTransactionUsageException);
ASSERT_THROW(interpreter.BeginTransaction(), memgraph::query::v2::ExplicitTransactionUsageException);
auto [stream, qid] = Prepare("RETURN 2");
ASSERT_EQ(stream.GetHeader().size(), 1U);
EXPECT_EQ(stream.GetHeader()[0], "2");
@ -894,7 +902,7 @@ TEST_F(InterpreterTest, Qid) {
interpreter.BeginTransaction();
auto [stream, qid] = Prepare("RETURN 2");
ASSERT_TRUE(qid);
ASSERT_THROW(Pull(&stream, {}, *qid + 1), memgraph::query::InvalidArgumentsException);
ASSERT_THROW(Pull(&stream, {}, *qid + 1), memgraph::query::v2::InvalidArgumentsException);
interpreter.RollbackTransaction();
}
{
@ -1496,40 +1504,41 @@ TEST_F(InterpreterTest, LoadCsvClauseNotification) {
TEST_F(InterpreterTest, CreateSchemaMulticommandTransaction) {
Interpret("BEGIN");
ASSERT_THROW(Interpret("CREATE SCHEMA ON :label(name STRING, age INTEGER)"),
memgraph::query::ConstraintInMulticommandTxException);
memgraph::query::v2::ConstraintInMulticommandTxException);
Interpret("ROLLBACK");
}
TEST_F(InterpreterTest, ShowSchemasMulticommandTransaction) {
Interpret("BEGIN");
ASSERT_THROW(Interpret("SHOW SCHEMAS"), memgraph::query::ConstraintInMulticommandTxException);
ASSERT_THROW(Interpret("SHOW SCHEMAS"), memgraph::query::v2::ConstraintInMulticommandTxException);
Interpret("ROLLBACK");
}
TEST_F(InterpreterTest, ShowSchemaMulticommandTransaction) {
Interpret("BEGIN");
ASSERT_THROW(Interpret("SHOW SCHEMA ON :label"), memgraph::query::ConstraintInMulticommandTxException);
ASSERT_THROW(Interpret("SHOW SCHEMA ON :label"), memgraph::query::v2::ConstraintInMulticommandTxException);
Interpret("ROLLBACK");
}
TEST_F(InterpreterTest, DropSchemaMulticommandTransaction) {
Interpret("BEGIN");
ASSERT_THROW(Interpret("DROP SCHEMA ON :label"), memgraph::query::ConstraintInMulticommandTxException);
ASSERT_THROW(Interpret("DROP SCHEMA ON :label"), memgraph::query::v2::ConstraintInMulticommandTxException);
Interpret("ROLLBACK");
}
TEST_F(InterpreterTest, SchemaTestCreateAndShow) {
// Empty schema type map should result with syntax exception.
ASSERT_THROW(Interpret("CREATE SCHEMA ON :label();"), memgraph::query::SyntaxException);
ASSERT_THROW(Interpret("CREATE SCHEMA ON :label();"), memgraph::query::v2::SyntaxException);
// Duplicate properties are should also cause an exception
ASSERT_THROW(Interpret("CREATE SCHEMA ON :label(name STRING, name STRING);"), memgraph::query::SemanticException);
ASSERT_THROW(Interpret("CREATE SCHEMA ON :label(name STRING, name INTEGER);"), memgraph::query::SemanticException);
ASSERT_THROW(Interpret("CREATE SCHEMA ON :label(name STRING, name STRING);"), memgraph::query::v2::SemanticException);
ASSERT_THROW(Interpret("CREATE SCHEMA ON :label(name STRING, name INTEGER);"),
memgraph::query::v2::SemanticException);
{
// Cannot create same schema twice
Interpret("CREATE SCHEMA ON :label(name STRING, age INTEGER)");
ASSERT_THROW(Interpret("CREATE SCHEMA ON :label(name STRING);"), memgraph::query::QueryException);
ASSERT_THROW(Interpret("CREATE SCHEMA ON :label(name STRING);"), memgraph::query::v2::QueryException);
}
// Show schema
{
@ -1588,9 +1597,9 @@ TEST_F(InterpreterTest, SchemaTestCreateAndShow) {
TEST_F(InterpreterTest, SchemaTestCreateDropAndShow) {
Interpret("CREATE SCHEMA ON :label(name STRING, age INTEGER)");
// Wrong syntax for dropping schema.
ASSERT_THROW(Interpret("DROP SCHEMA ON :label();"), memgraph::query::SyntaxException);
ASSERT_THROW(Interpret("DROP SCHEMA ON :label();"), memgraph::query::v2::SyntaxException);
// Cannot drop non existant schema.
ASSERT_THROW(Interpret("DROP SCHEMA ON :label1;"), memgraph::query::QueryException);
ASSERT_THROW(Interpret("DROP SCHEMA ON :label1;"), memgraph::query::v2::QueryException);
// Create Schema and Drop
auto get_number_of_schemas = [this]() {

View File

@ -0,0 +1,594 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
/// @file
/// This file provides macros for easier construction of openCypher query AST.
/// The usage of macros is very similar to how one would write openCypher. For
/// example:
///
/// AstStorage storage; // Macros rely on storage being in scope.
/// // PROPERTY_LOOKUP and PROPERTY_PAIR macros
/// // rely on a DbAccessor *reference* named dba.
/// database::GraphDb db;
/// auto dba_ptr = db.Access();
/// auto &dba = *dba_ptr;
///
/// QUERY(MATCH(PATTERN(NODE("n"), EDGE("e"), NODE("m"))),
/// WHERE(LESS(PROPERTY_LOOKUP("e", edge_prop), LITERAL(3))),
/// RETURN(SUM(PROPERTY_LOOKUP("m", prop)), AS("sum"),
/// ORDER_BY(IDENT("sum")),
/// SKIP(ADD(LITERAL(1), LITERAL(2)))));
///
/// Each of the macros is accompanied by a function. The functions use overload
/// resolution and template magic to provide a type safe way of constructing
/// queries. Although the functions can be used by themselves, it is more
/// convenient to use the macros.
#pragma once
#include <map>
#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "query/v2/frontend/ast/ast.hpp"
#include "query/v2/frontend/ast/pretty_print.hpp"
#include "storage/v3/id_types.hpp"
#include "utils/string.hpp"
namespace memgraph::query::v2 {
namespace test_common {
auto ToIntList(const TypedValue &t) {
std::vector<int64_t> list;
for (auto x : t.ValueList()) {
list.push_back(x.ValueInt());
}
return list;
};
auto ToIntMap(const TypedValue &t) {
std::map<std::string, int64_t> map;
for (const auto &kv : t.ValueMap()) map.emplace(kv.first, kv.second.ValueInt());
return map;
};
std::string ToString(Expression *expr) {
std::ostringstream ss;
PrintExpression(expr, &ss);
return ss.str();
}
std::string ToString(NamedExpression *expr) {
std::ostringstream ss;
PrintExpression(expr, &ss);
return ss.str();
}
// Custom types for ORDER BY, SKIP, LIMIT, ON MATCH and ON CREATE expressions,
// so that they can be used to resolve function calls.
struct OrderBy {
std::vector<SortItem> expressions;
};
struct Skip {
Expression *expression = nullptr;
};
struct Limit {
Expression *expression = nullptr;
};
struct OnMatch {
std::vector<Clause *> set;
};
struct OnCreate {
std::vector<Clause *> set;
};
// Helper functions for filling the OrderBy with expressions.
auto FillOrderBy(OrderBy &order_by, Expression *expression, Ordering ordering = Ordering::ASC) {
order_by.expressions.push_back({ordering, expression});
}
template <class... T>
auto FillOrderBy(OrderBy &order_by, Expression *expression, Ordering ordering, T... rest) {
FillOrderBy(order_by, expression, ordering);
FillOrderBy(order_by, rest...);
}
template <class... T>
auto FillOrderBy(OrderBy &order_by, Expression *expression, T... rest) {
FillOrderBy(order_by, expression);
FillOrderBy(order_by, rest...);
}
/// Create OrderBy expressions.
///
/// The supported combination of arguments is: (Expression, [Ordering])+
/// Since the Ordering is optional, by default it is ascending.
template <class... T>
auto GetOrderBy(T... exprs) {
OrderBy order_by;
FillOrderBy(order_by, exprs...);
return order_by;
}
/// Create PropertyLookup with given name and property.
///
/// Name is used to create the Identifier which is used for property lookup.
template <class TDbAccessor>
auto GetPropertyLookup(AstStorage &storage, TDbAccessor &dba, const std::string &name,
memgraph::storage::v3::PropertyId property) {
return storage.Create<PropertyLookup>(storage.Create<Identifier>(name),
storage.GetPropertyIx(dba.PropertyToName(property)));
}
template <class TDbAccessor>
auto GetPropertyLookup(AstStorage &storage, TDbAccessor &dba, Expression *expr,
memgraph::storage::v3::PropertyId property) {
return storage.Create<PropertyLookup>(expr, storage.GetPropertyIx(dba.PropertyToName(property)));
}
template <class TDbAccessor>
auto GetPropertyLookup(AstStorage &storage, TDbAccessor &dba, Expression *expr, const std::string &property) {
return storage.Create<PropertyLookup>(expr, storage.GetPropertyIx(property));
}
template <class TDbAccessor>
auto GetPropertyLookup(AstStorage &storage, TDbAccessor &, const std::string &name,
const std::pair<std::string, memgraph::storage::v3::PropertyId> &prop_pair) {
return storage.Create<PropertyLookup>(storage.Create<Identifier>(name), storage.GetPropertyIx(prop_pair.first));
}
template <class TDbAccessor>
auto GetPropertyLookup(AstStorage &storage, TDbAccessor &, Expression *expr,
const std::pair<std::string, memgraph::storage::v3::PropertyId> &prop_pair) {
return storage.Create<PropertyLookup>(expr, storage.GetPropertyIx(prop_pair.first));
}
/// Create an EdgeAtom with given name, direction and edge_type.
///
/// Name is used to create the Identifier which is assigned to the edge.
auto GetEdge(AstStorage &storage, const std::string &name, EdgeAtom::Direction dir = EdgeAtom::Direction::BOTH,
const std::vector<std::string> &edge_types = {}) {
std::vector<EdgeTypeIx> types;
types.reserve(edge_types.size());
for (const auto &type : edge_types) {
types.push_back(storage.GetEdgeTypeIx(type));
}
return storage.Create<EdgeAtom>(storage.Create<Identifier>(name), EdgeAtom::Type::SINGLE, dir, types);
}
/// Create a variable length expansion EdgeAtom with given name, direction and
/// edge_type.
///
/// Name is used to create the Identifier which is assigned to the edge.
auto GetEdgeVariable(AstStorage &storage, const std::string &name, EdgeAtom::Type type = EdgeAtom::Type::DEPTH_FIRST,
EdgeAtom::Direction dir = EdgeAtom::Direction::BOTH,
const std::vector<std::string> &edge_types = {}, Identifier *flambda_inner_edge = nullptr,
Identifier *flambda_inner_node = nullptr, Identifier *wlambda_inner_edge = nullptr,
Identifier *wlambda_inner_node = nullptr, Expression *wlambda_expression = nullptr,
Identifier *total_weight = nullptr) {
std::vector<EdgeTypeIx> types;
types.reserve(edge_types.size());
for (const auto &type : edge_types) {
types.push_back(storage.GetEdgeTypeIx(type));
}
auto r_val = storage.Create<EdgeAtom>(storage.Create<Identifier>(name), type, dir, types);
r_val->filter_lambda_.inner_edge =
flambda_inner_edge ? flambda_inner_edge : storage.Create<Identifier>(memgraph::utils::RandomString(20));
r_val->filter_lambda_.inner_node =
flambda_inner_node ? flambda_inner_node : storage.Create<Identifier>(memgraph::utils::RandomString(20));
if (type == EdgeAtom::Type::WEIGHTED_SHORTEST_PATH) {
r_val->weight_lambda_.inner_edge =
wlambda_inner_edge ? wlambda_inner_edge : storage.Create<Identifier>(memgraph::utils::RandomString(20));
r_val->weight_lambda_.inner_node =
wlambda_inner_node ? wlambda_inner_node : storage.Create<Identifier>(memgraph::utils::RandomString(20));
r_val->weight_lambda_.expression =
wlambda_expression ? wlambda_expression : storage.Create<memgraph::query::v2::PrimitiveLiteral>(1);
r_val->total_weight_ = total_weight;
}
return r_val;
}
/// Create a NodeAtom with given name and label.
///
/// Name is used to create the Identifier which is assigned to the node.
auto GetNode(AstStorage &storage, const std::string &name, std::optional<std::string> label = std::nullopt) {
auto node = storage.Create<NodeAtom>(storage.Create<Identifier>(name));
if (label) node->labels_.emplace_back(storage.GetLabelIx(*label));
return node;
}
/// Create a Pattern with given atoms.
auto GetPattern(AstStorage &storage, std::vector<PatternAtom *> atoms) {
auto pattern = storage.Create<Pattern>();
pattern->identifier_ = storage.Create<Identifier>(memgraph::utils::RandomString(20), false);
pattern->atoms_.insert(pattern->atoms_.begin(), atoms.begin(), atoms.end());
return pattern;
}
/// Create a Pattern with given name and atoms.
auto GetPattern(AstStorage &storage, const std::string &name, std::vector<PatternAtom *> atoms) {
auto pattern = storage.Create<Pattern>();
pattern->identifier_ = storage.Create<Identifier>(name, true);
pattern->atoms_.insert(pattern->atoms_.begin(), atoms.begin(), atoms.end());
return pattern;
}
/// This function fills an AST node which with given patterns.
///
/// The function is most commonly used to create Match and Create clauses.
template <class TWithPatterns>
auto GetWithPatterns(TWithPatterns *with_patterns, std::vector<Pattern *> patterns) {
with_patterns->patterns_.insert(with_patterns->patterns_.begin(), patterns.begin(), patterns.end());
return with_patterns;
}
/// Create a query with given clauses.
auto GetSingleQuery(SingleQuery *single_query, Clause *clause) {
single_query->clauses_.emplace_back(clause);
return single_query;
}
auto GetSingleQuery(SingleQuery *single_query, Match *match, Where *where) {
match->where_ = where;
single_query->clauses_.emplace_back(match);
return single_query;
}
auto GetSingleQuery(SingleQuery *single_query, With *with, Where *where) {
with->where_ = where;
single_query->clauses_.emplace_back(with);
return single_query;
}
template <class... T>
auto GetSingleQuery(SingleQuery *single_query, Match *match, Where *where, T *...clauses) {
match->where_ = where;
single_query->clauses_.emplace_back(match);
return GetSingleQuery(single_query, clauses...);
}
template <class... T>
auto GetSingleQuery(SingleQuery *single_query, With *with, Where *where, T *...clauses) {
with->where_ = where;
single_query->clauses_.emplace_back(with);
return GetSingleQuery(single_query, clauses...);
}
template <class... T>
auto GetSingleQuery(SingleQuery *single_query, Clause *clause, T *...clauses) {
single_query->clauses_.emplace_back(clause);
return GetSingleQuery(single_query, clauses...);
}
auto GetCypherUnion(CypherUnion *cypher_union, SingleQuery *single_query) {
cypher_union->single_query_ = single_query;
return cypher_union;
}
auto GetQuery(AstStorage &storage, SingleQuery *single_query) {
auto *query = storage.Create<CypherQuery>();
query->single_query_ = single_query;
return query;
}
template <class... T>
auto GetQuery(AstStorage &storage, SingleQuery *single_query, T *...cypher_unions) {
auto *query = storage.Create<CypherQuery>();
query->single_query_ = single_query;
query->cypher_unions_ = std::vector<CypherUnion *>{cypher_unions...};
return query;
}
// Helper functions for constructing RETURN and WITH clauses.
void FillReturnBody(AstStorage &, ReturnBody &body, NamedExpression *named_expr) {
body.named_expressions.emplace_back(named_expr);
}
void FillReturnBody(AstStorage &storage, ReturnBody &body, const std::string &name) {
if (name == "*") {
body.all_identifiers = true;
} else {
auto *ident = storage.Create<memgraph::query::v2::Identifier>(name);
auto *named_expr = storage.Create<memgraph::query::v2::NamedExpression>(name, ident);
body.named_expressions.emplace_back(named_expr);
}
}
void FillReturnBody(AstStorage &, ReturnBody &body, Limit limit) { body.limit = limit.expression; }
void FillReturnBody(AstStorage &, ReturnBody &body, Skip skip, Limit limit = Limit{}) {
body.skip = skip.expression;
body.limit = limit.expression;
}
void FillReturnBody(AstStorage &, ReturnBody &body, OrderBy order_by, Limit limit = Limit{}) {
body.order_by = order_by.expressions;
body.limit = limit.expression;
}
void FillReturnBody(AstStorage &, ReturnBody &body, OrderBy order_by, Skip skip, Limit limit = Limit{}) {
body.order_by = order_by.expressions;
body.skip = skip.expression;
body.limit = limit.expression;
}
void FillReturnBody(AstStorage &, ReturnBody &body, Expression *expr, NamedExpression *named_expr) {
// This overload supports `RETURN(expr, AS(name))` construct, since
// NamedExpression does not inherit Expression.
named_expr->expression_ = expr;
body.named_expressions.emplace_back(named_expr);
}
void FillReturnBody(AstStorage &storage, ReturnBody &body, const std::string &name, NamedExpression *named_expr) {
named_expr->expression_ = storage.Create<memgraph::query::v2::Identifier>(name);
body.named_expressions.emplace_back(named_expr);
}
template <class... T>
void FillReturnBody(AstStorage &storage, ReturnBody &body, Expression *expr, NamedExpression *named_expr, T... rest) {
named_expr->expression_ = expr;
body.named_expressions.emplace_back(named_expr);
FillReturnBody(storage, body, rest...);
}
template <class... T>
void FillReturnBody(AstStorage &storage, ReturnBody &body, NamedExpression *named_expr, T... rest) {
body.named_expressions.emplace_back(named_expr);
FillReturnBody(storage, body, rest...);
}
template <class... T>
void FillReturnBody(AstStorage &storage, ReturnBody &body, const std::string &name, NamedExpression *named_expr,
T... rest) {
named_expr->expression_ = storage.Create<memgraph::query::v2::Identifier>(name);
body.named_expressions.emplace_back(named_expr);
FillReturnBody(storage, body, rest...);
}
template <class... T>
void FillReturnBody(AstStorage &storage, ReturnBody &body, const std::string &name, T... rest) {
auto *ident = storage.Create<memgraph::query::v2::Identifier>(name);
auto *named_expr = storage.Create<memgraph::query::v2::NamedExpression>(name, ident);
body.named_expressions.emplace_back(named_expr);
FillReturnBody(storage, body, rest...);
}
/// Create the return clause with given expressions.
///
/// The supported expression combination of arguments is:
///
/// (String | NamedExpression | (Expression NamedExpression))+
/// [OrderBy] [Skip] [Limit]
///
/// When the pair (Expression NamedExpression) is given, the Expression will be
/// moved inside the NamedExpression. This is done, so that the constructs like
/// RETURN(expr, AS("name"), ...) are supported. Taking a String is a shorthand
/// for RETURN(IDENT(string), AS(string), ....).
///
/// @sa GetWith
template <class... T>
auto GetReturn(AstStorage &storage, bool distinct, T... exprs) {
auto ret = storage.Create<Return>();
ret->body_.distinct = distinct;
FillReturnBody(storage, ret->body_, exprs...);
return ret;
}
/// Create the with clause with given expressions.
///
/// The supported expression combination is the same as for @c GetReturn.
///
/// @sa GetReturn
template <class... T>
auto GetWith(AstStorage &storage, bool distinct, T... exprs) {
auto with = storage.Create<With>();
with->body_.distinct = distinct;
FillReturnBody(storage, with->body_, exprs...);
return with;
}
/// Create the UNWIND clause with given named expression.
auto GetUnwind(AstStorage &storage, NamedExpression *named_expr) {
return storage.Create<memgraph::query::v2::Unwind>(named_expr);
}
auto GetUnwind(AstStorage &storage, Expression *expr, NamedExpression *as) {
as->expression_ = expr;
return GetUnwind(storage, as);
}
/// Create the delete clause with given named expressions.
auto GetDelete(AstStorage &storage, std::vector<Expression *> exprs, bool detach = false) {
auto del = storage.Create<Delete>();
del->expressions_.insert(del->expressions_.begin(), exprs.begin(), exprs.end());
del->detach_ = detach;
return del;
}
/// Create a set property clause for given property lookup and the right hand
/// side expression.
auto GetSet(AstStorage &storage, PropertyLookup *prop_lookup, Expression *expr) {
return storage.Create<SetProperty>(prop_lookup, expr);
}
/// Create a set properties clause for given identifier name and the right hand
/// side expression.
auto GetSet(AstStorage &storage, const std::string &name, Expression *expr, bool update = false) {
return storage.Create<SetProperties>(storage.Create<Identifier>(name), expr, update);
}
/// Create a set labels clause for given identifier name and labels.
auto GetSet(AstStorage &storage, const std::string &name, std::vector<std::string> label_names) {
std::vector<LabelIx> labels;
labels.reserve(label_names.size());
for (const auto &label : label_names) {
labels.push_back(storage.GetLabelIx(label));
}
return storage.Create<SetLabels>(storage.Create<Identifier>(name), labels);
}
/// Create a remove property clause for given property lookup
auto GetRemove(AstStorage &storage, PropertyLookup *prop_lookup) { return storage.Create<RemoveProperty>(prop_lookup); }
/// Create a remove labels clause for given identifier name and labels.
auto GetRemove(AstStorage &storage, const std::string &name, std::vector<std::string> label_names) {
std::vector<LabelIx> labels;
labels.reserve(label_names.size());
for (const auto &label : label_names) {
labels.push_back(storage.GetLabelIx(label));
}
return storage.Create<RemoveLabels>(storage.Create<Identifier>(name), labels);
}
/// Create a Merge clause for given Pattern with optional OnMatch and OnCreate
/// parts.
auto GetMerge(AstStorage &storage, Pattern *pattern, OnCreate on_create = OnCreate{}) {
auto *merge = storage.Create<memgraph::query::v2::Merge>();
merge->pattern_ = pattern;
merge->on_create_ = on_create.set;
return merge;
}
auto GetMerge(AstStorage &storage, Pattern *pattern, OnMatch on_match, OnCreate on_create = OnCreate{}) {
auto *merge = storage.Create<memgraph::query::v2::Merge>();
merge->pattern_ = pattern;
merge->on_match_ = on_match.set;
merge->on_create_ = on_create.set;
return merge;
}
auto GetCallProcedure(AstStorage &storage, std::string procedure_name,
std::vector<memgraph::query::v2::Expression *> arguments = {}) {
auto *call_procedure = storage.Create<memgraph::query::v2::CallProcedure>();
call_procedure->procedure_name_ = std::move(procedure_name);
call_procedure->arguments_ = std::move(arguments);
return call_procedure;
}
/// Create the FOREACH clause with given named expression.
auto GetForeach(AstStorage &storage, NamedExpression *named_expr, const std::vector<query::v2::Clause *> &clauses) {
return storage.Create<query::v2::Foreach>(named_expr, clauses);
}
} // namespace test_common
} // namespace memgraph::query::v2
/// All the following macros implicitly pass `storage` variable to functions.
/// You need to have `AstStorage storage;` somewhere in scope to use them.
/// Refer to function documentation to see what the macro does.
///
/// Example usage:
///
/// // Create MATCH (n) -[r]- (m) RETURN m AS new_name
/// AstStorage storage;
/// auto query = QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))),
/// RETURN(NEXPR("new_name"), IDENT("m")));
#define NODE(...) memgraph::query::v2::test_common::GetNode(storage, __VA_ARGS__)
#define EDGE(...) memgraph::query::v2::test_common::GetEdge(storage, __VA_ARGS__)
#define EDGE_VARIABLE(...) memgraph::query::v2::test_common::GetEdgeVariable(storage, __VA_ARGS__)
#define PATTERN(...) memgraph::query::v2::test_common::GetPattern(storage, {__VA_ARGS__})
#define NAMED_PATTERN(name, ...) memgraph::query::v2::test_common::GetPattern(storage, name, {__VA_ARGS__})
#define OPTIONAL_MATCH(...) \
memgraph::query::v2::test_common::GetWithPatterns(storage.Create<memgraph::query::v2::Match>(true), {__VA_ARGS__})
#define MATCH(...) \
memgraph::query::v2::test_common::GetWithPatterns(storage.Create<memgraph::query::v2::Match>(), {__VA_ARGS__})
#define WHERE(expr) storage.Create<memgraph::query::v2::Where>((expr))
#define CREATE(...) \
memgraph::query::v2::test_common::GetWithPatterns(storage.Create<memgraph::query::v2::Create>(), {__VA_ARGS__})
#define IDENT(...) storage.Create<memgraph::query::v2::Identifier>(__VA_ARGS__)
#define LITERAL(val) storage.Create<memgraph::query::v2::PrimitiveLiteral>((val))
#define LIST(...) \
storage.Create<memgraph::query::v2::ListLiteral>(std::vector<memgraph::query::v2::Expression *>{__VA_ARGS__})
#define MAP(...) \
storage.Create<memgraph::query::v2::MapLiteral>( \
std::unordered_map<memgraph::query::v2::PropertyIx, memgraph::query::v2::Expression *>{__VA_ARGS__})
#define PROPERTY_PAIR(property_name) std::make_pair(property_name, dba.NameToProperty(property_name))
#define PROPERTY_LOOKUP(...) memgraph::query::v2::test_common::GetPropertyLookup(storage, dba, __VA_ARGS__)
#define PARAMETER_LOOKUP(token_position) storage.Create<memgraph::query::v2::ParameterLookup>((token_position))
#define NEXPR(name, expr) storage.Create<memgraph::query::v2::NamedExpression>((name), (expr))
// AS is alternative to NEXPR which does not initialize NamedExpression with
// Expression. It should be used with RETURN or WITH. For example:
// RETURN(IDENT("n"), AS("n")) vs. RETURN(NEXPR("n", IDENT("n"))).
#define AS(name) storage.Create<memgraph::query::v2::NamedExpression>((name))
#define RETURN(...) memgraph::query::v2::test_common::GetReturn(storage, false, __VA_ARGS__)
#define WITH(...) memgraph::query::v2::test_common::GetWith(storage, false, __VA_ARGS__)
#define RETURN_DISTINCT(...) memgraph::query::v2::test_common::GetReturn(storage, true, __VA_ARGS__)
#define WITH_DISTINCT(...) memgraph::query::v2::test_common::GetWith(storage, true, __VA_ARGS__)
#define UNWIND(...) memgraph::query::v2::test_common::GetUnwind(storage, __VA_ARGS__)
#define ORDER_BY(...) memgraph::query::v2::test_common::GetOrderBy(__VA_ARGS__)
#define SKIP(expr) \
memgraph::query::v2::test_common::Skip { (expr) }
#define LIMIT(expr) \
memgraph::query::v2::test_common::Limit { (expr) }
#define DELETE(...) memgraph::query::v2::test_common::GetDelete(storage, {__VA_ARGS__})
#define DETACH_DELETE(...) memgraph::query::v2::test_common::GetDelete(storage, {__VA_ARGS__}, true)
#define SET(...) memgraph::query::v2::test_common::GetSet(storage, __VA_ARGS__)
#define REMOVE(...) memgraph::query::v2::test_common::GetRemove(storage, __VA_ARGS__)
#define MERGE(...) memgraph::query::v2::test_common::GetMerge(storage, __VA_ARGS__)
#define ON_MATCH(...) \
memgraph::query::v2::test_common::OnMatch { \
std::vector<memgraph::query::v2::Clause *> { __VA_ARGS__ } \
}
#define ON_CREATE(...) \
memgraph::query::v2::test_common::OnCreate { \
std::vector<memgraph::query::v2::Clause *> { __VA_ARGS__ } \
}
#define CREATE_INDEX_ON(label, property) \
storage.Create<memgraph::query::v2::IndexQuery>(memgraph::query::v2::IndexQuery::Action::CREATE, (label), \
std::vector<memgraph::query::v2::PropertyIx>{(property)})
#define QUERY(...) memgraph::query::v2::test_common::GetQuery(storage, __VA_ARGS__)
#define SINGLE_QUERY(...) memgraph::query::v2::test_common::GetSingleQuery(storage.Create<SingleQuery>(), __VA_ARGS__)
#define UNION(...) memgraph::query::v2::test_common::GetCypherUnion(storage.Create<CypherUnion>(true), __VA_ARGS__)
#define UNION_ALL(...) memgraph::query::v2::test_common::GetCypherUnion(storage.Create<CypherUnion>(false), __VA_ARGS__)
#define FOREACH(...) memgraph::query::v2::test_common::GetForeach(storage, __VA_ARGS__)
// Various operators
#define NOT(expr) storage.Create<memgraph::query::v2::NotOperator>((expr))
#define UPLUS(expr) storage.Create<memgraph::query::v2::UnaryPlusOperator>((expr))
#define UMINUS(expr) storage.Create<memgraph::query::v2::UnaryMinusOperator>((expr))
#define IS_NULL(expr) storage.Create<memgraph::query::v2::IsNullOperator>((expr))
#define ADD(expr1, expr2) storage.Create<memgraph::query::v2::AdditionOperator>((expr1), (expr2))
#define LESS(expr1, expr2) storage.Create<memgraph::query::v2::LessOperator>((expr1), (expr2))
#define LESS_EQ(expr1, expr2) storage.Create<memgraph::query::v2::LessEqualOperator>((expr1), (expr2))
#define GREATER(expr1, expr2) storage.Create<memgraph::query::v2::GreaterOperator>((expr1), (expr2))
#define GREATER_EQ(expr1, expr2) storage.Create<memgraph::query::v2::GreaterEqualOperator>((expr1), (expr2))
#define SUM(expr) \
storage.Create<memgraph::query::v2::Aggregation>((expr), nullptr, memgraph::query::v2::Aggregation::Op::SUM)
#define COUNT(expr) \
storage.Create<memgraph::query::v2::Aggregation>((expr), nullptr, memgraph::query::v2::Aggregation::Op::COUNT)
#define AVG(expr) \
storage.Create<memgraph::query::v2::Aggregation>((expr), nullptr, memgraph::query::v2::Aggregation::Op::AVG)
#define COLLECT_LIST(expr) \
storage.Create<memgraph::query::v2::Aggregation>((expr), nullptr, memgraph::query::v2::Aggregation::Op::COLLECT_LIST)
#define EQ(expr1, expr2) storage.Create<memgraph::query::v2::EqualOperator>((expr1), (expr2))
#define NEQ(expr1, expr2) storage.Create<memgraph::query::v2::NotEqualOperator>((expr1), (expr2))
#define AND(expr1, expr2) storage.Create<memgraph::query::v2::AndOperator>((expr1), (expr2))
#define OR(expr1, expr2) storage.Create<memgraph::query::v2::OrOperator>((expr1), (expr2))
#define IN_LIST(expr1, expr2) storage.Create<memgraph::query::v2::InListOperator>((expr1), (expr2))
#define IF(cond, then, else) storage.Create<memgraph::query::v2::IfOperator>((cond), (then), (else))
// Function call
#define FN(function_name, ...) \
storage.Create<memgraph::query::v2::Function>(memgraph::utils::ToUpperCase(function_name), \
std::vector<memgraph::query::v2::Expression *>{__VA_ARGS__})
// List slicing
#define SLICE(list, lower_bound, upper_bound) \
storage.Create<memgraph::query::v2::ListSlicingOperator>(list, lower_bound, upper_bound)
// all(variable IN list WHERE predicate)
#define ALL(variable, list, where) \
storage.Create<memgraph::query::v2::All>(storage.Create<memgraph::query::v2::Identifier>(variable), list, where)
#define SINGLE(variable, list, where) \
storage.Create<memgraph::query::v2::Single>(storage.Create<memgraph::query::v2::Identifier>(variable), list, where)
#define ANY(variable, list, where) \
storage.Create<memgraph::query::v2::Any>(storage.Create<memgraph::query::v2::Identifier>(variable), list, where)
#define NONE(variable, list, where) \
storage.Create<memgraph::query::v2::None>(storage.Create<memgraph::query::v2::Identifier>(variable), list, where)
#define REDUCE(accumulator, initializer, variable, list, expr) \
storage.Create<memgraph::query::v2::Reduce>(storage.Create<memgraph::query::v2::Identifier>(accumulator), \
initializer, storage.Create<memgraph::query::v2::Identifier>(variable), \
list, expr)
#define COALESCE(...) \
storage.Create<memgraph::query::v2::Coalesce>(std::vector<memgraph::query::v2::Expression *>{__VA_ARGS__})
#define EXTRACT(variable, list, expr) \
storage.Create<memgraph::query::v2::Extract>(storage.Create<memgraph::query::v2::Identifier>(variable), list, expr)
#define AUTH_QUERY(action, user, role, user_or_role, password, privileges) \
storage.Create<memgraph::query::v2::AuthQuery>((action), (user), (role), (user_or_role), password, (privileges))
#define DROP_USER(usernames) storage.Create<memgraph::query::v2::DropUser>((usernames))
#define CALL_PROCEDURE(...) memgraph::query::v2::test_common::GetCallProcedure(storage, __VA_ARGS__)

View File

@ -18,16 +18,17 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "query/context.hpp"
#include "query/exceptions.hpp"
#include "query/plan/operator.hpp"
#include "query_plan_common.hpp"
#include "storage/v2/property_value.hpp"
#include "query/v2/context.hpp"
#include "query/v2/exceptions.hpp"
#include "query/v2/plan/operator.hpp"
#include "query_v2_query_plan_common.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/schemas.hpp"
using namespace memgraph::query;
using namespace memgraph::query::plan;
using memgraph::query::test_common::ToIntList;
using memgraph::query::test_common::ToIntMap;
using namespace memgraph::query::v2;
using namespace memgraph::query::v2::plan;
using test_common::ToIntList;
using test_common::ToIntMap;
using testing::UnorderedElementsAre;
namespace memgraph::query::v2::tests {
@ -35,12 +36,12 @@ namespace memgraph::query::v2::tests {
class QueryPlanAccumulateAggregateTest : public ::testing::Test {
protected:
void SetUp() override {
ASSERT_TRUE(db.CreateSchema(label, {storage::SchemaProperty{property, common::SchemaType::INT}}));
ASSERT_TRUE(db.CreateSchema(label, {storage::v3::SchemaProperty{property, common::SchemaType::INT}}));
}
storage::Storage db;
const storage::LabelId label{db.NameToLabel("label")};
const storage::PropertyId property{db.NameToProperty("property")};
storage::v3::Storage db;
const storage::v3::LabelId label{db.NameToLabel("label")};
const storage::v3::PropertyId property{db.NameToProperty("property")};
};
TEST_F(QueryPlanAccumulateAggregateTest, Accumulate) {
@ -55,10 +56,10 @@ TEST_F(QueryPlanAccumulateAggregateTest, Accumulate) {
DbAccessor dba(&storage_dba);
auto prop = dba.NameToProperty("x");
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}});
ASSERT_TRUE(v1.SetProperty(prop, storage::PropertyValue(0)).HasValue());
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}});
ASSERT_TRUE(v2.SetProperty(prop, storage::PropertyValue(0)).HasValue());
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}});
ASSERT_TRUE(v1.SetProperty(prop, storage::v3::PropertyValue(0)).HasValue());
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}});
ASSERT_TRUE(v2.SetProperty(prop, storage::v3::PropertyValue(0)).HasValue());
ASSERT_TRUE(dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("T")).HasValue());
dba.AdvanceCommand();
@ -67,7 +68,7 @@ TEST_F(QueryPlanAccumulateAggregateTest, Accumulate) {
auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::BOTH, {}, "m", false,
storage::View::OLD);
storage::v3::View::OLD);
auto one = LITERAL(1);
auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop);
@ -109,7 +110,7 @@ TEST_F(QueryPlanAccumulateAggregateTest, AccumulateAdvance) {
NodeCreationInfo node;
node.symbol = symbol_table.CreateSymbol("n", true);
node.labels = {label};
std::get<std::vector<std::pair<storage::PropertyId, Expression *>>>(node.properties)
std::get<std::vector<std::pair<storage::v3::PropertyId, Expression *>>>(node.properties)
.emplace_back(property, LITERAL(1));
auto create = std::make_shared<CreateNode>(nullptr, node);
auto accumulate = std::make_shared<Accumulate>(create, std::vector<Symbol>{node.symbol}, advance);
@ -157,14 +158,14 @@ std::shared_ptr<Produce> MakeAggregationProduce(std::shared_ptr<LogicalOperator>
class QueryPlanAggregateOps : public ::testing::Test {
protected:
void SetUp() override {
ASSERT_TRUE(db.CreateSchema(label, {storage::SchemaProperty{property, common::SchemaType::INT}}));
ASSERT_TRUE(db.CreateSchema(label, {storage::v3::SchemaProperty{property, common::SchemaType::INT}}));
}
storage::Storage db;
storage::Storage::Accessor storage_dba{db.Access()};
storage::v3::Storage db;
storage::v3::Storage::Accessor storage_dba{db.Access()};
DbAccessor dba{&storage_dba};
storage::LabelId label = db.NameToLabel("label");
storage::PropertyId property = db.NameToProperty("property");
storage::PropertyId prop = db.NameToProperty("prop");
storage::v3::LabelId label = db.NameToLabel("label");
storage::v3::PropertyId property = db.NameToProperty("property");
storage::v3::PropertyId prop = db.NameToProperty("prop");
AstStorage storage;
SymbolTable symbol_table;
@ -173,18 +174,18 @@ class QueryPlanAggregateOps : public ::testing::Test {
// setup is several nodes most of which have an int property set
// we will take the sum, avg, min, max and count
// we won't group by anything
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}})
->SetProperty(prop, storage::PropertyValue(5))
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}})
->SetProperty(prop, storage::v3::PropertyValue(5))
.HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}})
->SetProperty(prop, storage::PropertyValue(7))
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}})
->SetProperty(prop, storage::v3::PropertyValue(7))
.HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(3)}})
->SetProperty(prop, storage::PropertyValue(12))
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(3)}})
->SetProperty(prop, storage::v3::PropertyValue(12))
.HasValue());
// a missing property (null) gets ignored by all aggregations except
// COUNT(*)
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(4)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(4)}}).HasValue());
dba.AdvanceCommand();
}
@ -305,9 +306,9 @@ TEST_F(QueryPlanAccumulateAggregateTest, AggregateGroupByValues) {
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
// a vector of storage::PropertyValue to be set as property values on vertices
// a vector of storage::v3::PropertyValue to be set as property values on vertices
// most of them should result in a distinct group (commented where not)
std::vector<storage::PropertyValue> group_by_vals;
std::vector<storage::v3::PropertyValue> group_by_vals;
group_by_vals.emplace_back(4);
group_by_vals.emplace_back(7);
group_by_vals.emplace_back(7.3);
@ -317,20 +318,22 @@ TEST_F(QueryPlanAccumulateAggregateTest, AggregateGroupByValues) {
group_by_vals.emplace_back("1");
group_by_vals.emplace_back(true);
group_by_vals.emplace_back(false);
group_by_vals.emplace_back(std::vector<storage::PropertyValue>{storage::PropertyValue(1)});
group_by_vals.emplace_back(std::vector<storage::PropertyValue>{storage::PropertyValue(1), storage::PropertyValue(2)});
group_by_vals.emplace_back(std::vector<storage::PropertyValue>{storage::PropertyValue(2), storage::PropertyValue(1)});
group_by_vals.emplace_back(storage::PropertyValue());
group_by_vals.emplace_back(std::vector<storage::v3::PropertyValue>{storage::v3::PropertyValue(1)});
group_by_vals.emplace_back(
std::vector<storage::v3::PropertyValue>{storage::v3::PropertyValue(1), storage::v3::PropertyValue(2)});
group_by_vals.emplace_back(
std::vector<storage::v3::PropertyValue>{storage::v3::PropertyValue(2), storage::v3::PropertyValue(1)});
group_by_vals.emplace_back(storage::v3::PropertyValue());
// should NOT result in another group because 7.0 == 7
group_by_vals.emplace_back(7.0);
// should NOT result in another group
group_by_vals.emplace_back(
std::vector<storage::PropertyValue>{storage::PropertyValue(1), storage::PropertyValue(2.0)});
std::vector<storage::v3::PropertyValue>{storage::v3::PropertyValue(1), storage::v3::PropertyValue(2.0)});
// generate a lot of vertices and set props on them
auto prop = dba.NameToProperty("prop");
for (int i = 0; i < 1000; ++i)
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}})
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}})
->SetProperty(prop, group_by_vals[i % group_by_vals.size()])
.HasValue());
dba.AdvanceCommand();
@ -371,10 +374,10 @@ TEST_F(QueryPlanAccumulateAggregateTest, AggregateMultipleGroupBy) {
auto prop2 = dba.NameToProperty("prop2");
auto prop3 = dba.NameToProperty("prop3");
for (int i = 0; i < 2 * 3 * 5; ++i) {
auto v = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(i)}});
ASSERT_TRUE(v.SetProperty(prop1, storage::PropertyValue(static_cast<bool>(i % 2))).HasValue());
ASSERT_TRUE(v.SetProperty(prop2, storage::PropertyValue(i % 3)).HasValue());
ASSERT_TRUE(v.SetProperty(prop3, storage::PropertyValue("value" + std::to_string(i % 5))).HasValue());
auto v = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(i)}});
ASSERT_TRUE(v.SetProperty(prop1, storage::v3::PropertyValue(static_cast<bool>(i % 2))).HasValue());
ASSERT_TRUE(v.SetProperty(prop2, storage::v3::PropertyValue(i % 3)).HasValue());
ASSERT_TRUE(v.SetProperty(prop3, storage::v3::PropertyValue("value" + std::to_string(i % 5))).HasValue());
}
dba.AdvanceCommand();
@ -396,7 +399,7 @@ TEST_F(QueryPlanAccumulateAggregateTest, AggregateMultipleGroupBy) {
}
TEST(QueryPlan, AggregateNoInput) {
storage::Storage db;
storage::v3::Storage db;
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
AstStorage storage;
@ -448,24 +451,24 @@ TEST_F(QueryPlanAccumulateAggregateTest, AggregateCountEdgeCases) {
EXPECT_EQ(0, count());
// one vertex, no property set
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}}).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(0, count());
// one vertex, property set
for (auto va : dba.Vertices(storage::View::OLD))
ASSERT_TRUE(va.SetProperty(prop, storage::PropertyValue(42)).HasValue());
for (auto va : dba.Vertices(storage::v3::View::OLD))
ASSERT_TRUE(va.SetProperty(prop, storage::v3::PropertyValue(42)).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(1, count());
// two vertices, one with property set
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}}).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(1, count());
// two vertices, both with property set
for (auto va : dba.Vertices(storage::View::OLD))
ASSERT_TRUE(va.SetProperty(prop, storage::PropertyValue(42)).HasValue());
for (auto va : dba.Vertices(storage::v3::View::OLD))
ASSERT_TRUE(va.SetProperty(prop, storage::v3::PropertyValue(42)).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(2, count());
}
@ -477,11 +480,11 @@ TEST_F(QueryPlanAccumulateAggregateTest, AggregateFirstValueTypes) {
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}});
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}});
auto prop_string = dba.NameToProperty("string");
ASSERT_TRUE(v1.SetProperty(prop_string, storage::PropertyValue("johhny")).HasValue());
ASSERT_TRUE(v1.SetProperty(prop_string, storage::v3::PropertyValue("johhny")).HasValue());
auto prop_int = dba.NameToProperty("int");
ASSERT_TRUE(v1.SetProperty(prop_int, storage::PropertyValue(12)).HasValue());
ASSERT_TRUE(v1.SetProperty(prop_int, storage::v3::PropertyValue(12)).HasValue());
dba.AdvanceCommand();
AstStorage storage;
@ -531,18 +534,18 @@ TEST_F(QueryPlanAccumulateAggregateTest, AggregateTypes) {
DbAccessor dba(&storage_dba);
auto p1 = dba.NameToProperty("p1"); // has only string props
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}})
->SetProperty(p1, storage::PropertyValue("string"))
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}})
->SetProperty(p1, storage::v3::PropertyValue("string"))
.HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}})
->SetProperty(p1, storage::PropertyValue("str2"))
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}})
->SetProperty(p1, storage::v3::PropertyValue("str2"))
.HasValue());
auto p2 = dba.NameToProperty("p2"); // combines int and bool
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}})
->SetProperty(p2, storage::PropertyValue(42))
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}})
->SetProperty(p2, storage::v3::PropertyValue(42))
.HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}})
->SetProperty(p2, storage::PropertyValue(true))
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}})
->SetProperty(p2, storage::v3::PropertyValue(true))
.HasValue());
dba.AdvanceCommand();
@ -589,18 +592,18 @@ TEST_F(QueryPlanAccumulateAggregateTest, AggregateTypes) {
}
TEST(QueryPlan, Unwind) {
storage::Storage db;
storage::v3::Storage db;
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
AstStorage storage;
SymbolTable symbol_table;
// UNWIND [ [1, true, "x"], [], ["bla"] ] AS x UNWIND x as y RETURN x, y
auto input_expr = storage.Create<PrimitiveLiteral>(std::vector<storage::PropertyValue>{
storage::PropertyValue(std::vector<storage::PropertyValue>{
storage::PropertyValue(1), storage::PropertyValue(true), storage::PropertyValue("x")}),
storage::PropertyValue(std::vector<storage::PropertyValue>{}),
storage::PropertyValue(std::vector<storage::PropertyValue>{storage::PropertyValue("bla")})});
auto input_expr = storage.Create<PrimitiveLiteral>(std::vector<storage::v3::PropertyValue>{
storage::v3::PropertyValue(std::vector<storage::v3::PropertyValue>{
storage::v3::PropertyValue(1), storage::v3::PropertyValue(true), storage::v3::PropertyValue("x")}),
storage::v3::PropertyValue(std::vector<storage::v3::PropertyValue>{}),
storage::v3::PropertyValue(std::vector<storage::v3::PropertyValue>{storage::v3::PropertyValue("bla")})});
auto x = symbol_table.CreateSymbol("x", true);
auto unwind_0 = std::make_shared<plan::Unwind>(nullptr, input_expr, x);

View File

@ -17,28 +17,28 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "query/context.hpp"
#include "query/exceptions.hpp"
#include "query/frontend/ast/ast.hpp"
#include "query/plan/operator.hpp"
#include "query/v2/context.hpp"
#include "query/v2/exceptions.hpp"
#include "query/v2/frontend/ast/ast.hpp"
#include "query/v2/plan/operator.hpp"
#include "query_plan_common.hpp"
#include "storage/v2/property_value.hpp"
#include "query_v2_query_plan_common.hpp"
#include "storage/v3/property_value.hpp"
using namespace memgraph::query;
using namespace memgraph::query::plan;
using namespace memgraph::query::v2;
using namespace memgraph::query::v2::plan;
namespace memgraph::query::tests {
namespace memgraph::query::v2::tests {
class QueryPlanBagSemanticsTest : public testing::Test {
protected:
void SetUp() override {
ASSERT_TRUE(db.CreateSchema(label, {storage::SchemaProperty{property, common::SchemaType::INT}}));
ASSERT_TRUE(db.CreateSchema(label, {storage::v3::SchemaProperty{property, common::SchemaType::INT}}));
}
storage::Storage db;
const storage::LabelId label{db.NameToLabel("label")};
const storage::PropertyId property{db.NameToProperty("property")};
storage::v3::Storage db;
const storage::v3::LabelId label{db.NameToLabel("label")};
const storage::v3::PropertyId property{db.NameToProperty("property")};
};
TEST_F(QueryPlanBagSemanticsTest, Skip) {
@ -54,20 +54,20 @@ TEST_F(QueryPlanBagSemanticsTest, Skip) {
auto context = MakeContext(storage, symbol_table, &dba);
EXPECT_EQ(0, PullAll(*skip, &context));
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}}).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(0, PullAll(*skip, &context));
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}}).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(0, PullAll(*skip, &context));
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(3)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(3)}}).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(1, PullAll(*skip, &context));
for (int i = 0; i < 10; ++i) {
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(i + 3)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(i + 3)}}).HasValue());
}
dba.AdvanceCommand();
EXPECT_EQ(11, PullAll(*skip, &context));
@ -86,20 +86,20 @@ TEST_F(QueryPlanBagSemanticsTest, Limit) {
auto context = MakeContext(storage, symbol_table, &dba);
EXPECT_EQ(0, PullAll(*skip, &context));
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}}).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(1, PullAll(*skip, &context));
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}}).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(2, PullAll(*skip, &context));
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(3)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(3)}}).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(2, PullAll(*skip, &context));
for (int i = 0; i < 10; ++i) {
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(i + 3)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(i + 3)}}).HasValue());
}
dba.AdvanceCommand();
EXPECT_EQ(2, PullAll(*skip, &context));
@ -111,8 +111,8 @@ TEST_F(QueryPlanBagSemanticsTest, CreateLimit) {
// in the end we need to have 3 vertices in the db
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}}).HasValue());
dba.AdvanceCommand();
AstStorage storage;
@ -122,14 +122,15 @@ TEST_F(QueryPlanBagSemanticsTest, CreateLimit) {
NodeCreationInfo m;
m.symbol = symbol_table.CreateSymbol("m", true);
m.labels = {label};
std::get<std::vector<std::pair<storage::PropertyId, Expression *>>>(m.properties).emplace_back(property, LITERAL(3));
std::get<std::vector<std::pair<storage::v3::PropertyId, Expression *>>>(m.properties)
.emplace_back(property, LITERAL(3));
auto c = std::make_shared<CreateNode>(n.op_, m);
auto skip = std::make_shared<plan::Limit>(c, LITERAL(1));
auto context = MakeContext(storage, symbol_table, &dba);
EXPECT_EQ(1, PullAll(*skip, &context));
dba.AdvanceCommand();
EXPECT_EQ(3, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(3, CountIterable(dba.Vertices(storage::v3::View::OLD)));
}
TEST_F(QueryPlanBagSemanticsTest, OrderBy) {
@ -141,33 +142,34 @@ TEST_F(QueryPlanBagSemanticsTest, OrderBy) {
// contains a series of tests
// each test defines the ordering a vector of values in the desired order
auto Null = storage::PropertyValue();
std::vector<std::pair<Ordering, std::vector<storage::PropertyValue>>> orderable{
auto Null = storage::v3::PropertyValue();
std::vector<std::pair<Ordering, std::vector<storage::v3::PropertyValue>>> orderable{
{Ordering::ASC,
{storage::PropertyValue(0), storage::PropertyValue(0), storage::PropertyValue(0.5), storage::PropertyValue(1),
storage::PropertyValue(2), storage::PropertyValue(12.6), storage::PropertyValue(42), Null, Null}},
{storage::v3::PropertyValue(0), storage::v3::PropertyValue(0), storage::v3::PropertyValue(0.5),
storage::v3::PropertyValue(1), storage::v3::PropertyValue(2), storage::v3::PropertyValue(12.6),
storage::v3::PropertyValue(42), Null, Null}},
{Ordering::ASC,
{storage::PropertyValue(false), storage::PropertyValue(false), storage::PropertyValue(true),
storage::PropertyValue(true), Null, Null}},
{storage::v3::PropertyValue(false), storage::v3::PropertyValue(false), storage::v3::PropertyValue(true),
storage::v3::PropertyValue(true), Null, Null}},
{Ordering::ASC,
{storage::PropertyValue("A"), storage::PropertyValue("B"), storage::PropertyValue("a"),
storage::PropertyValue("a"), storage::PropertyValue("aa"), storage::PropertyValue("ab"),
storage::PropertyValue("aba"), Null, Null}},
{storage::v3::PropertyValue("A"), storage::v3::PropertyValue("B"), storage::v3::PropertyValue("a"),
storage::v3::PropertyValue("a"), storage::v3::PropertyValue("aa"), storage::v3::PropertyValue("ab"),
storage::v3::PropertyValue("aba"), Null, Null}},
{Ordering::DESC,
{Null, Null, storage::PropertyValue(33), storage::PropertyValue(33), storage::PropertyValue(32.5),
storage::PropertyValue(32), storage::PropertyValue(2.2), storage::PropertyValue(2.1),
storage::PropertyValue(0)}},
{Ordering::DESC, {Null, storage::PropertyValue(true), storage::PropertyValue(false)}},
{Ordering::DESC, {Null, storage::PropertyValue("zorro"), storage::PropertyValue("borro")}}};
{Null, Null, storage::v3::PropertyValue(33), storage::v3::PropertyValue(33), storage::v3::PropertyValue(32.5),
storage::v3::PropertyValue(32), storage::v3::PropertyValue(2.2), storage::v3::PropertyValue(2.1),
storage::v3::PropertyValue(0)}},
{Ordering::DESC, {Null, storage::v3::PropertyValue(true), storage::v3::PropertyValue(false)}},
{Ordering::DESC, {Null, storage::v3::PropertyValue("zorro"), storage::v3::PropertyValue("borro")}}};
for (const auto &order_value_pair : orderable) {
std::vector<TypedValue> values;
values.reserve(order_value_pair.second.size());
for (const auto &v : order_value_pair.second) values.emplace_back(v);
// empty database
for (auto vertex : dba.Vertices(storage::View::OLD)) ASSERT_TRUE(dba.DetachRemoveVertex(&vertex).HasValue());
for (auto vertex : dba.Vertices(storage::v3::View::OLD)) ASSERT_TRUE(dba.DetachRemoveVertex(&vertex).HasValue());
dba.AdvanceCommand();
ASSERT_EQ(0, CountIterable(dba.Vertices(storage::View::OLD)));
ASSERT_EQ(0, CountIterable(dba.Vertices(storage::v3::View::OLD)));
// take some effort to shuffle the values
// because we are testing that something not ordered gets ordered
@ -183,8 +185,8 @@ TEST_F(QueryPlanBagSemanticsTest, OrderBy) {
// create the vertices
for (const auto &value : shuffled) {
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}})
->SetProperty(prop, storage::PropertyValue(value))
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}})
->SetProperty(prop, storage::v3::PropertyValue(value))
.HasValue());
}
dba.AdvanceCommand();
@ -221,9 +223,9 @@ TEST_F(QueryPlanBagSemanticsTest, OrderByMultiple) {
for (int i = 0; i < N * N; ++i) prop_values.emplace_back(i % N, i / N);
std::random_shuffle(prop_values.begin(), prop_values.end());
for (const auto &pair : prop_values) {
auto v = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}});
ASSERT_TRUE(v.SetProperty(p1, storage::PropertyValue(pair.first)).HasValue());
ASSERT_TRUE(v.SetProperty(p2, storage::PropertyValue(pair.second)).HasValue());
auto v = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}});
ASSERT_TRUE(v.SetProperty(p1, storage::v3::PropertyValue(pair.first)).HasValue());
ASSERT_TRUE(v.SetProperty(p2, storage::v3::PropertyValue(pair.second)).HasValue());
}
dba.AdvanceCommand();
@ -265,37 +267,37 @@ TEST_F(QueryPlanBagSemanticsTest, OrderByExceptions) {
// a vector of pairs of typed values that should result
// in an exception when trying to order on them
std::vector<std::pair<storage::PropertyValue, storage::PropertyValue>> exception_pairs{
{storage::PropertyValue(42), storage::PropertyValue(true)},
{storage::PropertyValue(42), storage::PropertyValue("bla")},
{storage::PropertyValue(42),
storage::PropertyValue(std::vector<storage::PropertyValue>{storage::PropertyValue(42)})},
{storage::PropertyValue(true), storage::PropertyValue("bla")},
{storage::PropertyValue(true),
storage::PropertyValue(std::vector<storage::PropertyValue>{storage::PropertyValue(true)})},
{storage::PropertyValue("bla"),
storage::PropertyValue(std::vector<storage::PropertyValue>{storage::PropertyValue("bla")})},
std::vector<std::pair<storage::v3::PropertyValue, storage::v3::PropertyValue>> exception_pairs{
{storage::v3::PropertyValue(42), storage::v3::PropertyValue(true)},
{storage::v3::PropertyValue(42), storage::v3::PropertyValue("bla")},
{storage::v3::PropertyValue(42),
storage::v3::PropertyValue(std::vector<storage::v3::PropertyValue>{storage::v3::PropertyValue(42)})},
{storage::v3::PropertyValue(true), storage::v3::PropertyValue("bla")},
{storage::v3::PropertyValue(true),
storage::v3::PropertyValue(std::vector<storage::v3::PropertyValue>{storage::v3::PropertyValue(true)})},
{storage::v3::PropertyValue("bla"),
storage::v3::PropertyValue(std::vector<storage::v3::PropertyValue>{storage::v3::PropertyValue("bla")})},
// illegal comparisons of same-type values
{storage::PropertyValue(std::vector<storage::PropertyValue>{storage::PropertyValue(42)}),
storage::PropertyValue(std::vector<storage::PropertyValue>{storage::PropertyValue(42)})}};
{storage::v3::PropertyValue(std::vector<storage::v3::PropertyValue>{storage::v3::PropertyValue(42)}),
storage::v3::PropertyValue(std::vector<storage::v3::PropertyValue>{storage::v3::PropertyValue(42)})}};
for (const auto &pair : exception_pairs) {
// empty database
for (auto vertex : dba.Vertices(storage::View::OLD)) ASSERT_TRUE(dba.DetachRemoveVertex(&vertex).HasValue());
for (auto vertex : dba.Vertices(storage::v3::View::OLD)) ASSERT_TRUE(dba.DetachRemoveVertex(&vertex).HasValue());
dba.AdvanceCommand();
ASSERT_EQ(0, CountIterable(dba.Vertices(storage::View::OLD)));
ASSERT_EQ(0, CountIterable(dba.Vertices(storage::v3::View::OLD)));
// make two vertices, and set values
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}})
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}})
->SetProperty(prop, pair.first)
.HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}})
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}})
->SetProperty(prop, pair.second)
.HasValue());
dba.AdvanceCommand();
ASSERT_EQ(2, CountIterable(dba.Vertices(storage::View::OLD)));
for (const auto &va : dba.Vertices(storage::View::OLD))
ASSERT_NE(va.GetProperty(storage::View::OLD, prop).GetValue().type(), storage::PropertyValue::Type::Null);
ASSERT_EQ(2, CountIterable(dba.Vertices(storage::v3::View::OLD)));
for (const auto &va : dba.Vertices(storage::v3::View::OLD))
ASSERT_NE(va.GetProperty(storage::v3::View::OLD, prop).GetValue().type(), storage::v3::PropertyValue::Type::Null);
// order by and expect an exception
auto n = MakeScanAll(storage, symbol_table, "n");
@ -306,4 +308,4 @@ TEST_F(QueryPlanBagSemanticsTest, OrderByExceptions) {
EXPECT_THROW(PullAll(*order_by, &context), QueryRuntimeException);
}
}
} // namespace memgraph::query::tests
} // namespace memgraph::query::v2::tests

View File

@ -0,0 +1,225 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#include <iterator>
#include <memory>
#include <vector>
#include "query/v2/common.hpp"
#include "query/v2/context.hpp"
#include "query/v2/db_accessor.hpp"
#include "query/v2/frontend/semantic/symbol_table.hpp"
#include "query/v2/interpret/frame.hpp"
#include "query/v2/plan/operator.hpp"
#include "storage/v3/storage.hpp"
#include "utils/logging.hpp"
#include "query_v2_query_common.hpp"
using namespace memgraph::query::v2;
using namespace memgraph::query::v2::plan;
using Bound = ScanAllByLabelPropertyRange::Bound;
ExecutionContext MakeContext(const AstStorage &storage, const SymbolTable &symbol_table,
memgraph::query::v2::DbAccessor *dba) {
ExecutionContext context{dba};
context.symbol_table = symbol_table;
context.evaluation_context.properties = NamesToProperties(storage.properties_, dba);
context.evaluation_context.labels = NamesToLabels(storage.labels_, dba);
return context;
}
/** Helper function that collects all the results from the given Produce. */
std::vector<std::vector<TypedValue>> CollectProduce(const Produce &produce, ExecutionContext *context) {
Frame frame(context->symbol_table.max_position());
// top level node in the operator tree is a produce (return)
// so stream out results
// collect the symbols from the return clause
std::vector<Symbol> symbols;
for (auto named_expression : produce.named_expressions_)
symbols.emplace_back(context->symbol_table.at(*named_expression));
// stream out results
auto cursor = produce.MakeCursor(memgraph::utils::NewDeleteResource());
std::vector<std::vector<TypedValue>> results;
while (cursor->Pull(frame, *context)) {
std::vector<TypedValue> values;
for (auto &symbol : symbols) values.emplace_back(frame[symbol]);
results.emplace_back(values);
}
return results;
}
int PullAll(const LogicalOperator &logical_op, ExecutionContext *context) {
Frame frame(context->symbol_table.max_position());
auto cursor = logical_op.MakeCursor(memgraph::utils::NewDeleteResource());
int count = 0;
while (cursor->Pull(frame, *context)) count++;
return count;
}
template <typename... TNamedExpressions>
auto MakeProduce(std::shared_ptr<LogicalOperator> input, TNamedExpressions... named_expressions) {
return std::make_shared<Produce>(input, std::vector<NamedExpression *>{named_expressions...});
}
struct ScanAllTuple {
NodeAtom *node_;
std::shared_ptr<LogicalOperator> op_;
Symbol sym_;
};
/**
* Creates and returns a tuple of stuff for a scan-all starting
* from the node with the given name.
*
* Returns ScanAllTuple(node_atom, scan_all_logical_op, symbol).
*/
ScanAllTuple MakeScanAll(AstStorage &storage, SymbolTable &symbol_table, const std::string &identifier,
std::shared_ptr<LogicalOperator> input = {nullptr},
memgraph::storage::v3::View view = memgraph::storage::v3::View::OLD) {
auto node = NODE(identifier);
auto symbol = symbol_table.CreateSymbol(identifier, true);
node->identifier_->MapTo(symbol);
auto logical_op = std::make_shared<ScanAll>(input, symbol, view);
return ScanAllTuple{node, logical_op, symbol};
}
ScanAllTuple MakeScanAllNew(AstStorage &storage, SymbolTable &symbol_table, const std::string &identifier,
std::shared_ptr<LogicalOperator> input = {nullptr},
memgraph::storage::v3::View view = memgraph::storage::v3::View::OLD) {
auto *node = NODE(identifier, "label");
auto symbol = symbol_table.CreateSymbol(identifier, true);
node->identifier_->MapTo(symbol);
auto logical_op = std::make_shared<ScanAll>(input, symbol, view);
return ScanAllTuple{node, logical_op, symbol};
}
/**
* Creates and returns a tuple of stuff for a scan-all starting
* from the node with the given name and label.
*
* Returns ScanAllTuple(node_atom, scan_all_logical_op, symbol).
*/
ScanAllTuple MakeScanAllByLabel(AstStorage &storage, SymbolTable &symbol_table, const std::string &identifier,
memgraph::storage::v3::LabelId label,
std::shared_ptr<LogicalOperator> input = {nullptr},
memgraph::storage::v3::View view = memgraph::storage::v3::View::OLD) {
auto node = NODE(identifier);
auto symbol = symbol_table.CreateSymbol(identifier, true);
node->identifier_->MapTo(symbol);
auto logical_op = std::make_shared<ScanAllByLabel>(input, symbol, label, view);
return ScanAllTuple{node, logical_op, symbol};
}
/**
* Creates and returns a tuple of stuff for a scan-all starting from the node
* with the given name and label whose property values are in range.
*
* Returns ScanAllTuple(node_atom, scan_all_logical_op, symbol).
*/
ScanAllTuple MakeScanAllByLabelPropertyRange(AstStorage &storage, SymbolTable &symbol_table, std::string identifier,
memgraph::storage::v3::LabelId label,
memgraph::storage::v3::PropertyId property,
const std::string &property_name, std::optional<Bound> lower_bound,
std::optional<Bound> upper_bound,
std::shared_ptr<LogicalOperator> input = {nullptr},
memgraph::storage::v3::View view = memgraph::storage::v3::View::OLD) {
auto node = NODE(identifier);
auto symbol = symbol_table.CreateSymbol(identifier, true);
node->identifier_->MapTo(symbol);
auto logical_op = std::make_shared<ScanAllByLabelPropertyRange>(input, symbol, label, property, property_name,
lower_bound, upper_bound, view);
return ScanAllTuple{node, logical_op, symbol};
}
/**
* Creates and returns a tuple of stuff for a scan-all starting from the node
* with the given name and label whose property value is equal to given value.
*
* Returns ScanAllTuple(node_atom, scan_all_logical_op, symbol).
*/
ScanAllTuple MakeScanAllByLabelPropertyValue(AstStorage &storage, SymbolTable &symbol_table, std::string identifier,
memgraph::storage::v3::LabelId label,
memgraph::storage::v3::PropertyId property,
const std::string &property_name, Expression *value,
std::shared_ptr<LogicalOperator> input = {nullptr},
memgraph::storage::v3::View view = memgraph::storage::v3::View::OLD) {
auto node = NODE(identifier);
auto symbol = symbol_table.CreateSymbol(identifier, true);
node->identifier_->MapTo(symbol);
auto logical_op =
std::make_shared<ScanAllByLabelPropertyValue>(input, symbol, label, property, property_name, value, view);
return ScanAllTuple{node, logical_op, symbol};
}
struct ExpandTuple {
EdgeAtom *edge_;
Symbol edge_sym_;
NodeAtom *node_;
Symbol node_sym_;
std::shared_ptr<LogicalOperator> op_;
};
ExpandTuple MakeExpand(AstStorage &storage, SymbolTable &symbol_table, std::shared_ptr<LogicalOperator> input,
Symbol input_symbol, const std::string &edge_identifier, EdgeAtom::Direction direction,
const std::vector<memgraph::storage::v3::EdgeTypeId> &edge_types,
const std::string &node_identifier, bool existing_node, memgraph::storage::v3::View view) {
auto edge = EDGE(edge_identifier, direction);
auto edge_sym = symbol_table.CreateSymbol(edge_identifier, true);
edge->identifier_->MapTo(edge_sym);
auto node = NODE(node_identifier);
auto node_sym = symbol_table.CreateSymbol(node_identifier, true);
node->identifier_->MapTo(node_sym);
auto op =
std::make_shared<Expand>(input, input_symbol, node_sym, edge_sym, direction, edge_types, existing_node, view);
return ExpandTuple{edge, edge_sym, node, node_sym, op};
}
struct UnwindTuple {
Symbol sym_;
std::shared_ptr<LogicalOperator> op_;
};
UnwindTuple MakeUnwind(SymbolTable &symbol_table, const std::string &symbol_name,
std::shared_ptr<LogicalOperator> input, Expression *input_expression) {
auto sym = symbol_table.CreateSymbol(symbol_name, true);
auto op = std::make_shared<memgraph::query::v2::plan::Unwind>(input, input_expression, sym);
return UnwindTuple{sym, op};
}
template <typename TIterable>
auto CountIterable(TIterable &&iterable) {
uint64_t count = 0;
for (auto it = iterable.begin(); it != iterable.end(); ++it) {
++count;
}
return count;
}
inline uint64_t CountEdges(memgraph::query::v2::DbAccessor *dba, memgraph::storage::v3::View view) {
uint64_t count = 0;
for (auto vertex : dba->Vertices(view)) {
auto maybe_edges = vertex.OutEdges(view);
MG_ASSERT(maybe_edges.HasValue());
count += CountIterable(*maybe_edges);
}
return count;
}

View File

@ -18,34 +18,34 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "query/context.hpp"
#include "query/db_accessor.hpp"
#include "query/exceptions.hpp"
#include "query/interpret/frame.hpp"
#include "query/plan/operator.hpp"
#include "query/v2/context.hpp"
#include "query/v2/db_accessor.hpp"
#include "query/v2/exceptions.hpp"
#include "query/v2/interpret/frame.hpp"
#include "query/v2/plan/operator.hpp"
#include "query_plan_common.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/schemas.hpp"
#include "storage/v2/storage.hpp"
#include "storage/v2/vertex.hpp"
#include "storage/v2/view.hpp"
#include "query_v2_query_plan_common.hpp"
#include "storage/v3/id_types.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/schemas.hpp"
#include "storage/v3/storage.hpp"
#include "storage/v3/vertex.hpp"
#include "storage/v3/view.hpp"
using namespace memgraph::query;
using namespace memgraph::query::plan;
using namespace memgraph::query::v2;
using namespace memgraph::query::v2::plan;
namespace memgraph::query::tests {
class QueryPlanCRUDTest : public testing::Test {
protected:
void SetUp() override {
ASSERT_TRUE(db.CreateSchema(label, {storage::SchemaProperty{property, common::SchemaType::INT}}));
ASSERT_TRUE(db.CreateSchema(label, {storage::v3::SchemaProperty{property, common::SchemaType::INT}}));
}
storage::Storage db;
const storage::LabelId label{db.NameToLabel("label")};
const storage::PropertyId property{db.NameToProperty("property")};
storage::v3::Storage db;
const storage::v3::LabelId label{db.NameToLabel("label")};
const storage::v3::PropertyId property{db.NameToProperty("property")};
};
TEST_F(QueryPlanCRUDTest, CreateNodeWithAttributes) {
@ -58,7 +58,7 @@ TEST_F(QueryPlanCRUDTest, CreateNodeWithAttributes) {
NodeCreationInfo node;
node.symbol = symbol_table.CreateSymbol("n", true);
node.labels.emplace_back(label);
std::get<std::vector<std::pair<storage::PropertyId, Expression *>>>(node.properties)
std::get<std::vector<std::pair<storage::v3::PropertyId, Expression *>>>(node.properties)
.emplace_back(property, LITERAL(42));
auto create = std::make_shared<CreateNode>(nullptr, node);
@ -68,18 +68,18 @@ TEST_F(QueryPlanCRUDTest, CreateNodeWithAttributes) {
// count the number of vertices
int vertex_count = 0;
for (auto vertex : dba.Vertices(storage::View::OLD)) {
for (auto vertex : dba.Vertices(storage::v3::View::OLD)) {
vertex_count++;
auto maybe_labels = vertex.Labels(storage::View::OLD);
auto maybe_labels = vertex.Labels(storage::v3::View::OLD);
ASSERT_TRUE(maybe_labels.HasValue());
const auto &labels = *maybe_labels;
EXPECT_EQ(labels.size(), 0);
auto maybe_properties = vertex.Properties(storage::View::OLD);
auto maybe_properties = vertex.Properties(storage::v3::View::OLD);
ASSERT_TRUE(maybe_properties.HasValue());
const auto &properties = *maybe_properties;
EXPECT_EQ(properties.size(), 1);
auto maybe_prop = vertex.GetProperty(storage::View::OLD, property);
auto maybe_prop = vertex.GetProperty(storage::v3::View::OLD, property);
ASSERT_TRUE(maybe_prop.HasValue());
auto prop_eq = TypedValue(*maybe_prop) == TypedValue(42);
ASSERT_EQ(prop_eq.type(), TypedValue::Type::Bool);
@ -90,13 +90,13 @@ TEST_F(QueryPlanCRUDTest, CreateNodeWithAttributes) {
TEST(QueryPlan, CreateReturn) {
// test CREATE (n:Person {age: 42}) RETURN n, n.age
storage::Storage db;
storage::v3::Storage db;
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
storage::LabelId label = dba.NameToLabel("Person");
storage::v3::LabelId label = dba.NameToLabel("Person");
auto property = PROPERTY_PAIR("property");
db.CreateSchema(label, {storage::SchemaProperty{property.second, common::SchemaType::INT}});
db.CreateSchema(label, {storage::v3::SchemaProperty{property.second, common::SchemaType::INT}});
AstStorage storage;
SymbolTable symbol_table;
@ -104,7 +104,7 @@ TEST(QueryPlan, CreateReturn) {
NodeCreationInfo node;
node.symbol = symbol_table.CreateSymbol("n", true);
node.labels.emplace_back(label);
std::get<std::vector<std::pair<storage::PropertyId, Expression *>>>(node.properties)
std::get<std::vector<std::pair<storage::v3::PropertyId, Expression *>>>(node.properties)
.emplace_back(property.second, LITERAL(42));
auto create = std::make_shared<CreateNode>(nullptr, node);
@ -119,47 +119,47 @@ TEST(QueryPlan, CreateReturn) {
EXPECT_EQ(1, results.size());
EXPECT_EQ(2, results[0].size());
EXPECT_EQ(TypedValue::Type::Vertex, results[0][0].type());
auto maybe_labels = results[0][0].ValueVertex().Labels(storage::View::NEW);
auto maybe_labels = results[0][0].ValueVertex().Labels(storage::v3::View::NEW);
EXPECT_EQ(maybe_labels->size(), 0);
EXPECT_EQ(TypedValue::Type::Int, results[0][1].type());
EXPECT_EQ(42, results[0][1].ValueInt());
dba.AdvanceCommand();
EXPECT_EQ(1, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(1, CountIterable(dba.Vertices(storage::v3::View::OLD)));
}
TEST(QueryPlan, CreateExpand) {
storage::Storage db;
storage::v3::Storage db;
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
storage::LabelId label_node_1 = dba.NameToLabel("Node1");
storage::LabelId label_node_2 = dba.NameToLabel("Node2");
storage::v3::LabelId label_node_1 = dba.NameToLabel("Node1");
storage::v3::LabelId label_node_2 = dba.NameToLabel("Node2");
auto property = PROPERTY_PAIR("property");
storage::EdgeTypeId edge_type = dba.NameToEdgeType("edge_type");
db.CreateSchema(label_node_1, {storage::SchemaProperty{property.second, common::SchemaType::INT}});
db.CreateSchema(label_node_2, {storage::SchemaProperty{property.second, common::SchemaType::INT}});
storage::v3::EdgeTypeId edge_type = dba.NameToEdgeType("edge_type");
db.CreateSchema(label_node_1, {storage::v3::SchemaProperty{property.second, common::SchemaType::INT}});
db.CreateSchema(label_node_2, {storage::v3::SchemaProperty{property.second, common::SchemaType::INT}});
SymbolTable symbol_table;
AstStorage storage;
auto test_create_path = [&](bool cycle, int expected_nodes_created, int expected_edges_created) {
int before_v = CountIterable(dba.Vertices(storage::View::OLD));
int before_e = CountEdges(&dba, storage::View::OLD);
int before_v = CountIterable(dba.Vertices(storage::v3::View::OLD));
int before_e = CountEdges(&dba, storage::v3::View::OLD);
// data for the first node
NodeCreationInfo n;
n.symbol = symbol_table.CreateSymbol("n", true);
n.labels.emplace_back(label_node_1);
std::get<std::vector<std::pair<storage::PropertyId, Expression *>>>(n.properties)
std::get<std::vector<std::pair<storage::v3::PropertyId, Expression *>>>(n.properties)
.emplace_back(property.second, LITERAL(1));
// data for the second node
NodeCreationInfo m;
m.symbol = cycle ? n.symbol : symbol_table.CreateSymbol("m", true);
m.labels.emplace_back(label_node_2);
std::get<std::vector<std::pair<storage::PropertyId, Expression *>>>(m.properties)
std::get<std::vector<std::pair<storage::v3::PropertyId, Expression *>>>(m.properties)
.emplace_back(property.second, LITERAL(2));
EdgeCreationInfo r;
@ -173,37 +173,37 @@ TEST(QueryPlan, CreateExpand) {
PullAll(*create_expand, &context);
dba.AdvanceCommand();
EXPECT_EQ(CountIterable(dba.Vertices(storage::View::OLD)) - before_v, expected_nodes_created);
EXPECT_EQ(CountEdges(&dba, storage::View::OLD) - before_e, expected_edges_created);
EXPECT_EQ(CountIterable(dba.Vertices(storage::v3::View::OLD)) - before_v, expected_nodes_created);
EXPECT_EQ(CountEdges(&dba, storage::v3::View::OLD) - before_e, expected_edges_created);
};
test_create_path(false, 2, 1);
test_create_path(true, 1, 1);
for (auto vertex : dba.Vertices(storage::View::OLD)) {
auto maybe_labels = vertex.Labels(storage::View::OLD);
for (auto vertex : dba.Vertices(storage::v3::View::OLD)) {
auto maybe_labels = vertex.Labels(storage::v3::View::OLD);
MG_ASSERT(maybe_labels.HasValue());
const auto &labels = *maybe_labels;
EXPECT_EQ(labels.size(), 0);
auto maybe_primary_label = vertex.PrimaryLabel(storage::View::OLD);
auto maybe_primary_label = vertex.PrimaryLabel(storage::v3::View::OLD);
ASSERT_TRUE(maybe_primary_label.HasValue());
if (*maybe_primary_label == label_node_1) {
// node created by first op
EXPECT_EQ(vertex.GetProperty(storage::View::OLD, property.second)->ValueInt(), 1);
EXPECT_EQ(vertex.GetProperty(storage::v3::View::OLD, property.second)->ValueInt(), 1);
} else if (*maybe_primary_label == label_node_2) {
// node create by expansion
EXPECT_EQ(vertex.GetProperty(storage::View::OLD, property.second)->ValueInt(), 2);
EXPECT_EQ(vertex.GetProperty(storage::v3::View::OLD, property.second)->ValueInt(), 2);
} else {
// should not happen
FAIL();
}
for (auto vertex : dba.Vertices(storage::View::OLD)) {
auto maybe_edges = vertex.OutEdges(storage::View::OLD);
for (auto vertex : dba.Vertices(storage::v3::View::OLD)) {
auto maybe_edges = vertex.OutEdges(storage::v3::View::OLD);
MG_ASSERT(maybe_edges.HasValue());
for (auto edge : *maybe_edges) {
EXPECT_EQ(edge.EdgeType(), edge_type);
EXPECT_EQ(edge.GetProperty(storage::View::OLD, property.second)->ValueInt(), 3);
EXPECT_EQ(edge.GetProperty(storage::v3::View::OLD, property.second)->ValueInt(), 3);
}
}
}
@ -213,9 +213,9 @@ TEST_F(QueryPlanCRUDTest, MatchCreateNode) {
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(3)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(3)}}).HasValue());
dba.AdvanceCommand();
SymbolTable symbol_table;
@ -227,38 +227,39 @@ TEST_F(QueryPlanCRUDTest, MatchCreateNode) {
NodeCreationInfo m;
m.symbol = symbol_table.CreateSymbol("m", true);
m.labels = {label};
std::get<std::vector<std::pair<storage::PropertyId, Expression *>>>(m.properties).emplace_back(property, LITERAL(1));
std::get<std::vector<std::pair<storage::v3::PropertyId, Expression *>>>(m.properties)
.emplace_back(property, LITERAL(1));
// creation op
auto create_node = std::make_shared<CreateNode>(n_scan_all.op_, m);
EXPECT_EQ(CountIterable(dba.Vertices(storage::View::OLD)), 3);
EXPECT_EQ(CountIterable(dba.Vertices(storage::v3::View::OLD)), 3);
auto context = MakeContext(storage, symbol_table, &dba);
PullAll(*create_node, &context);
dba.AdvanceCommand();
EXPECT_EQ(CountIterable(dba.Vertices(storage::View::OLD)), 6);
EXPECT_EQ(CountIterable(dba.Vertices(storage::v3::View::OLD)), 6);
}
TEST_F(QueryPlanCRUDTest, MatchCreateExpand) {
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(3)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(3)}}).HasValue());
dba.AdvanceCommand();
// storage::LabelId label_node_1 = dba.NameToLabel("Node1");
// storage::LabelId label_node_2 = dba.NameToLabel("Node2");
// storage::PropertyId property = dba.NameToLabel("prop");
storage::EdgeTypeId edge_type = dba.NameToEdgeType("edge_type");
// storage::v3::LabelId label_node_1 = dba.NameToLabel("Node1");
// storage::v3::LabelId label_node_2 = dba.NameToLabel("Node2");
// storage::v3::PropertyId property = dba.NameToLabel("prop");
storage::v3::EdgeTypeId edge_type = dba.NameToEdgeType("edge_type");
SymbolTable symbol_table;
AstStorage storage;
auto test_create_path = [&](bool cycle, int expected_nodes_created, int expected_edges_created) {
int before_v = CountIterable(dba.Vertices(storage::View::OLD));
int before_e = CountEdges(&dba, storage::View::OLD);
int before_v = CountIterable(dba.Vertices(storage::v3::View::OLD));
int before_e = CountEdges(&dba, storage::v3::View::OLD);
// data for the first node
auto n_scan_all = MakeScanAll(storage, symbol_table, "n");
@ -267,7 +268,7 @@ TEST_F(QueryPlanCRUDTest, MatchCreateExpand) {
NodeCreationInfo m;
m.symbol = cycle ? n_scan_all.sym_ : symbol_table.CreateSymbol("m", true);
m.labels = {label};
std::get<std::vector<std::pair<storage::PropertyId, Expression *>>>(m.properties)
std::get<std::vector<std::pair<storage::v3::PropertyId, Expression *>>>(m.properties)
.emplace_back(property, LITERAL(1));
EdgeCreationInfo r;
@ -280,8 +281,8 @@ TEST_F(QueryPlanCRUDTest, MatchCreateExpand) {
PullAll(*create_expand, &context);
dba.AdvanceCommand();
EXPECT_EQ(CountIterable(dba.Vertices(storage::View::OLD)) - before_v, expected_nodes_created);
EXPECT_EQ(CountEdges(&dba, storage::View::OLD) - before_e, expected_edges_created);
EXPECT_EQ(CountIterable(dba.Vertices(storage::v3::View::OLD)) - before_v, expected_nodes_created);
EXPECT_EQ(CountEdges(&dba, storage::v3::View::OLD) - before_e, expected_edges_created);
};
test_create_path(false, 3, 3);
@ -295,15 +296,15 @@ TEST_F(QueryPlanCRUDTest, Delete) {
// make a fully-connected (one-direction, no cycles) with 4 nodes
std::vector<VertexAccessor> vertices;
for (int i = 0; i < 4; ++i) {
vertices.push_back(*dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(i)}}));
vertices.push_back(*dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(i)}}));
}
auto type = dba.NameToEdgeType("type");
for (int j = 0; j < 4; ++j)
for (int k = j + 1; k < 4; ++k) ASSERT_TRUE(dba.InsertEdge(&vertices[j], &vertices[k], type).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(4, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(6, CountEdges(&dba, storage::View::OLD));
EXPECT_EQ(4, CountIterable(dba.Vertices(storage::v3::View::OLD)));
EXPECT_EQ(6, CountEdges(&dba, storage::v3::View::OLD));
AstStorage storage;
SymbolTable symbol_table;
@ -316,8 +317,8 @@ TEST_F(QueryPlanCRUDTest, Delete) {
auto context = MakeContext(storage, symbol_table, &dba);
EXPECT_THROW(PullAll(*delete_op, &context), QueryRuntimeException);
dba.AdvanceCommand();
EXPECT_EQ(4, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(6, CountEdges(&dba, storage::View::OLD));
EXPECT_EQ(4, CountIterable(dba.Vertices(storage::v3::View::OLD)));
EXPECT_EQ(6, CountEdges(&dba, storage::v3::View::OLD));
}
// detach delete a single vertex
@ -329,22 +330,22 @@ TEST_F(QueryPlanCRUDTest, Delete) {
auto context = MakeContext(storage, symbol_table, &dba);
delete_op->MakeCursor(utils::NewDeleteResource())->Pull(frame, context);
dba.AdvanceCommand();
EXPECT_EQ(3, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(3, CountEdges(&dba, storage::View::OLD));
EXPECT_EQ(3, CountIterable(dba.Vertices(storage::v3::View::OLD)));
EXPECT_EQ(3, CountEdges(&dba, storage::v3::View::OLD));
}
// delete all remaining edges
{
auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
storage::View::NEW);
storage::v3::View::NEW);
auto r_get = storage.Create<Identifier>("r")->MapTo(r_m.edge_sym_);
auto delete_op = std::make_shared<plan::Delete>(r_m.op_, std::vector<Expression *>{r_get}, false);
auto context = MakeContext(storage, symbol_table, &dba);
PullAll(*delete_op, &context);
dba.AdvanceCommand();
EXPECT_EQ(3, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(0, CountEdges(&dba, storage::View::OLD));
EXPECT_EQ(3, CountIterable(dba.Vertices(storage::v3::View::OLD)));
EXPECT_EQ(0, CountEdges(&dba, storage::v3::View::OLD));
}
// delete all remaining vertices
@ -355,8 +356,8 @@ TEST_F(QueryPlanCRUDTest, Delete) {
auto context = MakeContext(storage, symbol_table, &dba);
PullAll(*delete_op, &context);
dba.AdvanceCommand();
EXPECT_EQ(0, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(0, CountEdges(&dba, storage::View::OLD));
EXPECT_EQ(0, CountIterable(dba.Vertices(storage::v3::View::OLD)));
EXPECT_EQ(0, CountEdges(&dba, storage::v3::View::OLD));
}
}
@ -376,19 +377,19 @@ TEST_F(QueryPlanCRUDTest, DeleteTwiceDeleteBlockingEdge) {
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}});
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}});
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}});
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}});
ASSERT_TRUE(dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("T")).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(2, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(1, CountEdges(&dba, storage::View::OLD));
EXPECT_EQ(2, CountIterable(dba.Vertices(storage::v3::View::OLD)));
EXPECT_EQ(1, CountEdges(&dba, storage::v3::View::OLD));
AstStorage storage;
SymbolTable symbol_table;
auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::BOTH, {}, "m", false,
storage::View::OLD);
storage::v3::View::OLD);
// getter expressions for deletion
auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
@ -399,8 +400,8 @@ TEST_F(QueryPlanCRUDTest, DeleteTwiceDeleteBlockingEdge) {
auto context = MakeContext(storage, symbol_table, &dba);
EXPECT_EQ(2, PullAll(*delete_op, &context));
dba.AdvanceCommand();
EXPECT_EQ(0, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(0, CountEdges(&dba, storage::View::OLD));
EXPECT_EQ(0, CountIterable(dba.Vertices(storage::v3::View::OLD)));
EXPECT_EQ(0, CountEdges(&dba, storage::v3::View::OLD));
};
test_delete(true);
@ -413,14 +414,14 @@ TEST_F(QueryPlanCRUDTest, DeleteReturn) {
// make a fully-connected (one-direction, no cycles) with 4 nodes
for (int i = 0; i < 4; ++i) {
const auto property_value = storage::PropertyValue(i);
const auto property_value = storage::v3::PropertyValue(i);
auto va = *dba.InsertVertexAndValidate(label, {}, {{property, property_value}});
EXPECT_EQ(*va.GetProperty(storage::View::NEW, property), property_value);
EXPECT_EQ(*va.GetProperty(storage::v3::View::NEW, property), property_value);
}
dba.AdvanceCommand();
EXPECT_EQ(4, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(0, CountEdges(&dba, storage::View::OLD));
EXPECT_EQ(4, CountIterable(dba.Vertices(storage::v3::View::OLD)));
EXPECT_EQ(0, CountEdges(&dba, storage::v3::View::OLD));
AstStorage storage;
SymbolTable symbol_table;
@ -440,7 +441,7 @@ TEST_F(QueryPlanCRUDTest, DeleteReturn) {
TEST(QueryPlan, DeleteNull) {
// test (simplified) WITH Null as x delete x
storage::Storage db;
storage::v3::Storage db;
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
AstStorage storage;
@ -469,7 +470,7 @@ TEST_F(QueryPlanCRUDTest, DeleteAdvance) {
{
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}}).HasValue());
dba.AdvanceCommand();
auto produce = MakeProduce(advance, NEXPR("res", LITERAL(42))->MapTo(res_sym));
auto context = MakeContext(storage, symbol_table, &dba);
@ -478,7 +479,7 @@ TEST_F(QueryPlanCRUDTest, DeleteAdvance) {
{
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}}).HasValue());
dba.AdvanceCommand();
auto n_prop = PROPERTY_LOOKUP(n_get, dba.NameToProperty("prop"));
auto produce = MakeProduce(advance, NEXPR("res", n_prop)->MapTo(res_sym));
@ -495,10 +496,10 @@ TEST_F(QueryPlanCRUDTest, SetProperty) {
// the origin vertex in each par and both edges
// have a property set
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}});
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}});
auto v3 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(3)}});
auto v4 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(4)}});
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}});
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}});
auto v3 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(3)}});
auto v4 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(4)}});
auto edge_type = dba.NameToEdgeType("edge_type");
ASSERT_TRUE(dba.InsertEdge(&v1, &v3, edge_type).HasValue());
ASSERT_TRUE(dba.InsertEdge(&v2, &v4, edge_type).HasValue());
@ -510,7 +511,7 @@ TEST_F(QueryPlanCRUDTest, SetProperty) {
// scan (n)-[r]->(m)
auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
storage::View::OLD);
storage::v3::View::OLD);
// set prop1 to 42 on n and r
auto prop1 = dba.NameToProperty("prop1");
@ -525,18 +526,18 @@ TEST_F(QueryPlanCRUDTest, SetProperty) {
EXPECT_EQ(2, PullAll(*set_r_p, &context));
dba.AdvanceCommand();
EXPECT_EQ(CountEdges(&dba, storage::View::OLD), 2);
for (auto vertex : dba.Vertices(storage::View::OLD)) {
auto maybe_edges = vertex.OutEdges(storage::View::OLD);
EXPECT_EQ(CountEdges(&dba, storage::v3::View::OLD), 2);
for (auto vertex : dba.Vertices(storage::v3::View::OLD)) {
auto maybe_edges = vertex.OutEdges(storage::v3::View::OLD);
ASSERT_TRUE(maybe_edges.HasValue());
for (auto edge : *maybe_edges) {
ASSERT_EQ(edge.GetProperty(storage::View::OLD, prop1)->type(), storage::PropertyValue::Type::Int);
EXPECT_EQ(edge.GetProperty(storage::View::OLD, prop1)->ValueInt(), 42);
ASSERT_EQ(edge.GetProperty(storage::v3::View::OLD, prop1)->type(), storage::v3::PropertyValue::Type::Int);
EXPECT_EQ(edge.GetProperty(storage::v3::View::OLD, prop1)->ValueInt(), 42);
auto from = edge.From();
auto to = edge.To();
ASSERT_EQ(from.GetProperty(storage::View::OLD, prop1)->type(), storage::PropertyValue::Type::Int);
EXPECT_EQ(from.GetProperty(storage::View::OLD, prop1)->ValueInt(), 42);
ASSERT_EQ(to.GetProperty(storage::View::OLD, prop1)->type(), storage::PropertyValue::Type::Null);
ASSERT_EQ(from.GetProperty(storage::v3::View::OLD, prop1)->type(), storage::v3::PropertyValue::Type::Int);
EXPECT_EQ(from.GetProperty(storage::v3::View::OLD, prop1)->ValueInt(), 42);
ASSERT_EQ(to.GetProperty(storage::v3::View::OLD, prop1)->type(), storage::v3::PropertyValue::Type::Null);
}
}
}
@ -550,14 +551,14 @@ TEST_F(QueryPlanCRUDTest, SetProperties) {
auto prop_a = dba.NameToProperty("a");
auto prop_b = dba.NameToProperty("b");
auto prop_c = dba.NameToProperty("c");
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}});
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}});
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}});
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}});
dba.AdvanceCommand();
auto e = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("R"));
ASSERT_TRUE(v1.SetPropertyAndValidate(prop_a, storage::PropertyValue(0)).HasValue());
ASSERT_TRUE(e->SetProperty(prop_b, storage::PropertyValue(1)).HasValue());
ASSERT_TRUE(v2.SetPropertyAndValidate(prop_c, storage::PropertyValue(2)).HasValue());
ASSERT_TRUE(v1.SetPropertyAndValidate(prop_a, storage::v3::PropertyValue(0)).HasValue());
ASSERT_TRUE(e->SetProperty(prop_b, storage::v3::PropertyValue(1)).HasValue());
ASSERT_TRUE(v2.SetPropertyAndValidate(prop_c, storage::v3::PropertyValue(2)).HasValue());
dba.AdvanceCommand();
AstStorage storage;
@ -566,7 +567,7 @@ TEST_F(QueryPlanCRUDTest, SetProperties) {
// scan (n)-[r]->(m)
auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
storage::View::OLD);
storage::v3::View::OLD);
auto op = update ? plan::SetProperties::Op::UPDATE : plan::SetProperties::Op::REPLACE;
@ -579,32 +580,32 @@ TEST_F(QueryPlanCRUDTest, SetProperties) {
EXPECT_EQ(1, PullAll(*set_m_to_r, &context));
dba.AdvanceCommand();
EXPECT_EQ(CountEdges(&dba, storage::View::OLD), 1);
for (auto vertex : dba.Vertices(storage::View::OLD)) {
auto maybe_edges = vertex.OutEdges(storage::View::OLD);
EXPECT_EQ(CountEdges(&dba, storage::v3::View::OLD), 1);
for (auto vertex : dba.Vertices(storage::v3::View::OLD)) {
auto maybe_edges = vertex.OutEdges(storage::v3::View::OLD);
ASSERT_TRUE(maybe_edges.HasValue());
for (auto edge : *maybe_edges) {
auto from = edge.From();
EXPECT_EQ(from.Properties(storage::View::OLD)->size(), update ? 3 : 1);
EXPECT_EQ(from.Properties(storage::v3::View::OLD)->size(), update ? 3 : 1);
if (update) {
ASSERT_EQ(from.GetProperty(storage::View::OLD, prop_a)->type(), storage::PropertyValue::Type::Int);
EXPECT_EQ(from.GetProperty(storage::View::OLD, prop_a)->ValueInt(), 0);
ASSERT_EQ(from.GetProperty(storage::v3::View::OLD, prop_a)->type(), storage::v3::PropertyValue::Type::Int);
EXPECT_EQ(from.GetProperty(storage::v3::View::OLD, prop_a)->ValueInt(), 0);
}
ASSERT_EQ(from.GetProperty(storage::View::OLD, prop_b)->type(), storage::PropertyValue::Type::Int);
EXPECT_EQ(from.GetProperty(storage::View::OLD, prop_b)->ValueInt(), 1);
ASSERT_EQ(from.GetProperty(storage::v3::View::OLD, prop_b)->type(), storage::v3::PropertyValue::Type::Int);
EXPECT_EQ(from.GetProperty(storage::v3::View::OLD, prop_b)->ValueInt(), 1);
EXPECT_EQ(edge.Properties(storage::View::OLD)->size(), update ? 3 : 2);
EXPECT_EQ(edge.Properties(storage::v3::View::OLD)->size(), update ? 3 : 2);
if (update) {
ASSERT_EQ(edge.GetProperty(storage::View::OLD, prop_b)->type(), storage::PropertyValue::Type::Int);
EXPECT_EQ(edge.GetProperty(storage::View::OLD, prop_b)->ValueInt(), 1);
ASSERT_EQ(edge.GetProperty(storage::v3::View::OLD, prop_b)->type(), storage::v3::PropertyValue::Type::Int);
EXPECT_EQ(edge.GetProperty(storage::v3::View::OLD, prop_b)->ValueInt(), 1);
}
ASSERT_EQ(edge.GetProperty(storage::View::OLD, prop_c)->type(), storage::PropertyValue::Type::Int);
EXPECT_EQ(edge.GetProperty(storage::View::OLD, prop_c)->ValueInt(), 2);
ASSERT_EQ(edge.GetProperty(storage::v3::View::OLD, prop_c)->type(), storage::v3::PropertyValue::Type::Int);
EXPECT_EQ(edge.GetProperty(storage::v3::View::OLD, prop_c)->ValueInt(), 2);
auto to = edge.To();
EXPECT_EQ(to.Properties(storage::View::OLD)->size(), 2);
ASSERT_EQ(to.GetProperty(storage::View::OLD, prop_c)->type(), storage::PropertyValue::Type::Int);
EXPECT_EQ(to.GetProperty(storage::View::OLD, prop_c)->ValueInt(), 2);
EXPECT_EQ(to.Properties(storage::v3::View::OLD)->size(), 2);
ASSERT_EQ(to.GetProperty(storage::v3::View::OLD, prop_c)->type(), storage::v3::PropertyValue::Type::Int);
EXPECT_EQ(to.GetProperty(storage::v3::View::OLD, prop_c)->ValueInt(), 2);
}
}
};
@ -617,8 +618,8 @@ TEST_F(QueryPlanCRUDTest, SetSecondaryLabels) {
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}});
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}});
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}});
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}});
auto label1 = dba.NameToLabel("label1");
auto label2 = dba.NameToLabel("label2");
@ -631,14 +632,14 @@ TEST_F(QueryPlanCRUDTest, SetSecondaryLabels) {
SymbolTable symbol_table;
auto n = MakeScanAll(storage, symbol_table, "n");
auto label_set = std::make_shared<plan::SetLabels>(n.op_, n.sym_, std::vector<storage::LabelId>{label2, label3});
auto label_set = std::make_shared<plan::SetLabels>(n.op_, n.sym_, std::vector<storage::v3::LabelId>{label2, label3});
auto context = MakeContext(storage, symbol_table, &dba);
EXPECT_EQ(2, PullAll(*label_set, &context));
for (auto vertex : dba.Vertices(storage::View::OLD)) {
EXPECT_EQ(3, vertex.Labels(storage::View::NEW)->size());
EXPECT_TRUE(*vertex.HasLabel(storage::View::NEW, label2));
EXPECT_TRUE(*vertex.HasLabel(storage::View::NEW, label3));
for (auto vertex : dba.Vertices(storage::v3::View::OLD)) {
EXPECT_EQ(3, vertex.Labels(storage::v3::View::NEW)->size());
EXPECT_TRUE(*vertex.HasLabel(storage::v3::View::NEW, label2));
EXPECT_TRUE(*vertex.HasLabel(storage::v3::View::NEW, label3));
}
}
@ -650,23 +651,23 @@ TEST_F(QueryPlanCRUDTest, RemoveProperty) {
// the origin vertex in each par and both edges
// have a property set
auto prop1 = dba.NameToProperty("prop1");
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}});
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}});
auto v3 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(3)}});
auto v4 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(4)}});
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}});
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}});
auto v3 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(3)}});
auto v4 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(4)}});
auto edge_type = dba.NameToEdgeType("edge_type");
{
auto e = dba.InsertEdge(&v1, &v3, edge_type);
ASSERT_TRUE(e.HasValue());
ASSERT_TRUE(e->SetProperty(prop1, storage::PropertyValue(42)).HasValue());
ASSERT_TRUE(e->SetProperty(prop1, storage::v3::PropertyValue(42)).HasValue());
}
ASSERT_TRUE(dba.InsertEdge(&v2, &v4, edge_type).HasValue());
ASSERT_TRUE(v2.SetProperty(prop1, storage::PropertyValue(42)).HasValue());
ASSERT_TRUE(v3.SetProperty(prop1, storage::PropertyValue(42)).HasValue());
ASSERT_TRUE(v4.SetProperty(prop1, storage::PropertyValue(42)).HasValue());
ASSERT_TRUE(v2.SetProperty(prop1, storage::v3::PropertyValue(42)).HasValue());
ASSERT_TRUE(v3.SetProperty(prop1, storage::v3::PropertyValue(42)).HasValue());
ASSERT_TRUE(v4.SetProperty(prop1, storage::v3::PropertyValue(42)).HasValue());
auto prop2 = dba.NameToProperty("prop2");
ASSERT_TRUE(v1.SetProperty(prop2, storage::PropertyValue(0)).HasValue());
ASSERT_TRUE(v2.SetProperty(prop2, storage::PropertyValue(0)).HasValue());
ASSERT_TRUE(v1.SetProperty(prop2, storage::v3::PropertyValue(0)).HasValue());
ASSERT_TRUE(v2.SetProperty(prop2, storage::v3::PropertyValue(0)).HasValue());
dba.AdvanceCommand();
AstStorage storage;
@ -675,7 +676,7 @@ TEST_F(QueryPlanCRUDTest, RemoveProperty) {
// scan (n)-[r]->(m)
auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
storage::View::OLD);
storage::v3::View::OLD);
auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop1);
auto set_n_p = std::make_shared<plan::RemoveProperty>(r_m.op_, prop1, n_p);
@ -686,17 +687,17 @@ TEST_F(QueryPlanCRUDTest, RemoveProperty) {
EXPECT_EQ(2, PullAll(*set_r_p, &context));
dba.AdvanceCommand();
EXPECT_EQ(CountEdges(&dba, storage::View::OLD), 2);
for (auto vertex : dba.Vertices(storage::View::OLD)) {
auto maybe_edges = vertex.OutEdges(storage::View::OLD);
EXPECT_EQ(CountEdges(&dba, storage::v3::View::OLD), 2);
for (auto vertex : dba.Vertices(storage::v3::View::OLD)) {
auto maybe_edges = vertex.OutEdges(storage::v3::View::OLD);
ASSERT_TRUE(maybe_edges.HasValue());
for (auto edge : *maybe_edges) {
EXPECT_EQ(edge.GetProperty(storage::View::OLD, prop1)->type(), storage::PropertyValue::Type::Null);
EXPECT_EQ(edge.GetProperty(storage::v3::View::OLD, prop1)->type(), storage::v3::PropertyValue::Type::Null);
auto from = edge.From();
auto to = edge.To();
EXPECT_EQ(from.GetProperty(storage::View::OLD, prop1)->type(), storage::PropertyValue::Type::Null);
EXPECT_EQ(from.GetProperty(storage::View::OLD, prop2)->type(), storage::PropertyValue::Type::Int);
EXPECT_EQ(to.GetProperty(storage::View::OLD, prop1)->type(), storage::PropertyValue::Type::Int);
EXPECT_EQ(from.GetProperty(storage::v3::View::OLD, prop1)->type(), storage::v3::PropertyValue::Type::Null);
EXPECT_EQ(from.GetProperty(storage::v3::View::OLD, prop2)->type(), storage::v3::PropertyValue::Type::Int);
EXPECT_EQ(to.GetProperty(storage::v3::View::OLD, prop1)->type(), storage::v3::PropertyValue::Type::Int);
}
}
}
@ -708,11 +709,11 @@ TEST_F(QueryPlanCRUDTest, RemoveLabels) {
auto label1 = dba.NameToLabel("label1");
auto label2 = dba.NameToLabel("label2");
auto label3 = dba.NameToLabel("label3");
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}});
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}});
ASSERT_TRUE(v1.AddLabel(label1).HasValue());
ASSERT_TRUE(v1.AddLabel(label2).HasValue());
ASSERT_TRUE(v1.AddLabel(label3).HasValue());
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}});
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}});
ASSERT_TRUE(v2.AddLabel(label1).HasValue());
ASSERT_TRUE(v2.AddLabel(label3).HasValue());
dba.AdvanceCommand();
@ -722,14 +723,14 @@ TEST_F(QueryPlanCRUDTest, RemoveLabels) {
auto n = MakeScanAll(storage, symbol_table, "n");
auto label_remove =
std::make_shared<plan::RemoveLabels>(n.op_, n.sym_, std::vector<storage::LabelId>{label1, label2});
std::make_shared<plan::RemoveLabels>(n.op_, n.sym_, std::vector<storage::v3::LabelId>{label1, label2});
auto context = MakeContext(storage, symbol_table, &dba);
EXPECT_EQ(2, PullAll(*label_remove, &context));
for (auto vertex : dba.Vertices(storage::View::OLD)) {
EXPECT_EQ(1, vertex.Labels(storage::View::NEW)->size());
EXPECT_FALSE(*vertex.HasLabel(storage::View::NEW, label1));
EXPECT_FALSE(*vertex.HasLabel(storage::View::NEW, label2));
for (auto vertex : dba.Vertices(storage::v3::View::OLD)) {
EXPECT_EQ(1, vertex.Labels(storage::v3::View::NEW)->size());
EXPECT_FALSE(*vertex.HasLabel(storage::v3::View::NEW, label1));
EXPECT_FALSE(*vertex.HasLabel(storage::v3::View::NEW, label2));
}
}
@ -738,11 +739,11 @@ TEST_F(QueryPlanCRUDTest, NodeFilterSet) {
DbAccessor dba(&storage_dba);
// Create a graph such that (v1 {prop: 42}) is connected to v2 and v3.
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}});
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}});
auto prop = PROPERTY_PAIR("prop");
ASSERT_TRUE(v1.SetProperty(prop.second, storage::PropertyValue(42)).HasValue());
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}});
auto v3 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(3)}});
ASSERT_TRUE(v1.SetProperty(prop.second, storage::v3::PropertyValue(42)).HasValue());
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}});
auto v3 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(3)}});
auto edge_type = dba.NameToEdgeType("Edge");
ASSERT_TRUE(dba.InsertEdge(&v1, &v2, edge_type).HasValue());
ASSERT_TRUE(dba.InsertEdge(&v1, &v3, edge_type).HasValue());
@ -756,7 +757,7 @@ TEST_F(QueryPlanCRUDTest, NodeFilterSet) {
auto scan_all = MakeScanAll(storage, symbol_table, "n");
std::get<0>(scan_all.node_->properties_)[storage.GetPropertyIx(prop.first)] = LITERAL(42);
auto expand = MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::BOTH, {}, "m",
false, storage::View::OLD);
false, storage::v3::View::OLD);
auto *filter_expr =
EQ(storage.Create<PropertyLookup>(scan_all.node_->identifier_, storage.GetPropertyIx(prop.first)), LITERAL(42));
auto node_filter = std::make_shared<Filter>(expand.op_, filter_expr);
@ -767,7 +768,7 @@ TEST_F(QueryPlanCRUDTest, NodeFilterSet) {
auto context = MakeContext(storage, symbol_table, &dba);
EXPECT_EQ(2, PullAll(*set, &context));
dba.AdvanceCommand();
auto prop_eq = TypedValue(*v1.GetProperty(storage::View::OLD, prop.second)) == TypedValue(42 + 2);
auto prop_eq = TypedValue(*v1.GetProperty(storage::v3::View::OLD, prop.second)) == TypedValue(42 + 2);
ASSERT_EQ(prop_eq.type(), TypedValue::Type::Bool);
EXPECT_TRUE(prop_eq.ValueBool());
}
@ -777,11 +778,11 @@ TEST_F(QueryPlanCRUDTest, FilterRemove) {
DbAccessor dba(&storage_dba);
// Create a graph such that (v1 {prop: 42}) is connected to v2 and v3.
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}});
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}});
auto prop = PROPERTY_PAIR("prop");
ASSERT_TRUE(v1.SetProperty(prop.second, storage::PropertyValue(42)).HasValue());
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}});
auto v3 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(3)}});
ASSERT_TRUE(v1.SetProperty(prop.second, storage::v3::PropertyValue(42)).HasValue());
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}});
auto v3 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(3)}});
auto edge_type = dba.NameToEdgeType("Edge");
ASSERT_TRUE(dba.InsertEdge(&v1, &v2, edge_type).HasValue());
ASSERT_TRUE(dba.InsertEdge(&v1, &v3, edge_type).HasValue());
@ -794,7 +795,7 @@ TEST_F(QueryPlanCRUDTest, FilterRemove) {
auto scan_all = MakeScanAll(storage, symbol_table, "n");
std::get<0>(scan_all.node_->properties_)[storage.GetPropertyIx(prop.first)] = LITERAL(42);
auto expand = MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::BOTH, {}, "m",
false, storage::View::OLD);
false, storage::v3::View::OLD);
auto filter_prop = PROPERTY_LOOKUP(IDENT("n")->MapTo(scan_all.sym_), prop);
auto filter = std::make_shared<Filter>(expand.op_, LESS(filter_prop, LITERAL(43)));
// REMOVE n.prop
@ -803,14 +804,14 @@ TEST_F(QueryPlanCRUDTest, FilterRemove) {
auto context = MakeContext(storage, symbol_table, &dba);
EXPECT_EQ(2, PullAll(*rem, &context));
dba.AdvanceCommand();
EXPECT_EQ(v1.GetProperty(storage::View::OLD, prop.second)->type(), storage::PropertyValue::Type::Null);
EXPECT_EQ(v1.GetProperty(storage::v3::View::OLD, prop.second)->type(), storage::v3::PropertyValue::Type::Null);
}
TEST_F(QueryPlanCRUDTest, SetRemove) {
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
auto v = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}});
auto v = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}});
auto label1 = dba.NameToLabel("label1");
auto label2 = dba.NameToLabel("label2");
dba.AdvanceCommand();
@ -821,13 +822,14 @@ TEST_F(QueryPlanCRUDTest, SetRemove) {
// MATCH (n) SET n :label1 :label2 REMOVE n :label1 :label2
auto scan_all = MakeScanAll(storage, symbol_table, "n");
auto set =
std::make_shared<plan::SetLabels>(scan_all.op_, scan_all.sym_, std::vector<storage::LabelId>{label1, label2});
auto rem = std::make_shared<plan::RemoveLabels>(set, scan_all.sym_, std::vector<storage::LabelId>{label1, label2});
std::make_shared<plan::SetLabels>(scan_all.op_, scan_all.sym_, std::vector<storage::v3::LabelId>{label1, label2});
auto rem =
std::make_shared<plan::RemoveLabels>(set, scan_all.sym_, std::vector<storage::v3::LabelId>{label1, label2});
auto context = MakeContext(storage, symbol_table, &dba);
EXPECT_EQ(1, PullAll(*rem, &context));
dba.AdvanceCommand();
EXPECT_FALSE(*v.HasLabel(storage::View::OLD, label1));
EXPECT_FALSE(*v.HasLabel(storage::View::OLD, label2));
EXPECT_FALSE(*v.HasLabel(storage::v3::View::OLD, label1));
EXPECT_FALSE(*v.HasLabel(storage::v3::View::OLD, label2));
}
TEST_F(QueryPlanCRUDTest, Merge) {
@ -840,11 +842,11 @@ TEST_F(QueryPlanCRUDTest, Merge) {
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}});
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(2)}});
auto v1 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}});
auto v2 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(2)}});
ASSERT_TRUE(dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("Type")).HasValue());
auto v3 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(3)}});
auto v3 = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(3)}});
dba.AdvanceCommand();
AstStorage storage;
@ -855,7 +857,7 @@ TEST_F(QueryPlanCRUDTest, Merge) {
// merge_match branch
auto r_m = MakeExpand(storage, symbol_table, std::make_shared<Once>(), n.sym_, "r", EdgeAtom::Direction::BOTH, {},
"m", false, storage::View::OLD);
"m", false, storage::v3::View::OLD);
auto m_p = PROPERTY_LOOKUP(IDENT("m")->MapTo(r_m.node_sym_), prop);
auto m_set = std::make_shared<plan::SetProperty>(r_m.op_, prop.second, m_p, LITERAL(1));
@ -868,12 +870,12 @@ TEST_F(QueryPlanCRUDTest, Merge) {
ASSERT_EQ(3, PullAll(*merge, &context));
dba.AdvanceCommand();
ASSERT_EQ(v1.GetProperty(storage::View::OLD, prop.second)->type(), storage::PropertyValue::Type::Int);
ASSERT_EQ(v1.GetProperty(storage::View::OLD, prop.second)->ValueInt(), 1);
ASSERT_EQ(v2.GetProperty(storage::View::OLD, prop.second)->type(), storage::PropertyValue::Type::Int);
ASSERT_EQ(v2.GetProperty(storage::View::OLD, prop.second)->ValueInt(), 1);
ASSERT_EQ(v3.GetProperty(storage::View::OLD, prop.second)->type(), storage::PropertyValue::Type::Int);
ASSERT_EQ(v3.GetProperty(storage::View::OLD, prop.second)->ValueInt(), 2);
ASSERT_EQ(v1.GetProperty(storage::v3::View::OLD, prop.second)->type(), storage::v3::PropertyValue::Type::Int);
ASSERT_EQ(v1.GetProperty(storage::v3::View::OLD, prop.second)->ValueInt(), 1);
ASSERT_EQ(v2.GetProperty(storage::v3::View::OLD, prop.second)->type(), storage::v3::PropertyValue::Type::Int);
ASSERT_EQ(v2.GetProperty(storage::v3::View::OLD, prop.second)->ValueInt(), 1);
ASSERT_EQ(v3.GetProperty(storage::v3::View::OLD, prop.second)->type(), storage::v3::PropertyValue::Type::Int);
ASSERT_EQ(v3.GetProperty(storage::v3::View::OLD, prop.second)->ValueInt(), 2);
}
TEST_F(QueryPlanCRUDTest, MergeNoInput) {
@ -887,21 +889,21 @@ TEST_F(QueryPlanCRUDTest, MergeNoInput) {
NodeCreationInfo node;
node.symbol = symbol_table.CreateSymbol("n", true);
node.labels = {label};
std::get<std::vector<std::pair<storage::PropertyId, Expression *>>>(node.properties)
std::get<std::vector<std::pair<storage::v3::PropertyId, Expression *>>>(node.properties)
.emplace_back(property, LITERAL(1));
auto create = std::make_shared<CreateNode>(nullptr, node);
auto merge = std::make_shared<plan::Merge>(nullptr, create, create);
EXPECT_EQ(0, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(0, CountIterable(dba.Vertices(storage::v3::View::OLD)));
auto context = MakeContext(storage, symbol_table, &dba);
EXPECT_EQ(1, PullAll(*merge, &context));
dba.AdvanceCommand();
EXPECT_EQ(1, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(1, CountIterable(dba.Vertices(storage::v3::View::OLD)));
}
TEST(QueryPlan, SetPropertyOnNull) {
// SET (Null).prop = 42
storage::Storage db;
storage::v3::Storage db;
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
AstStorage storage;
@ -918,7 +920,7 @@ TEST(QueryPlan, SetPropertyOnNull) {
TEST(QueryPlan, SetPropertiesOnNull) {
// OPTIONAL MATCH (n) SET n = n
storage::Storage db;
storage::v3::Storage db;
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
AstStorage storage;
@ -927,14 +929,14 @@ TEST(QueryPlan, SetPropertiesOnNull) {
auto n_ident = IDENT("n")->MapTo(n.sym_);
auto optional = std::make_shared<plan::Optional>(nullptr, n.op_, std::vector<Symbol>{n.sym_});
auto set_op = std::make_shared<plan::SetProperties>(optional, n.sym_, n_ident, plan::SetProperties::Op::REPLACE);
EXPECT_EQ(0, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(0, CountIterable(dba.Vertices(storage::v3::View::OLD)));
auto context = MakeContext(storage, symbol_table, &dba);
EXPECT_EQ(1, PullAll(*set_op, &context));
}
TEST(QueryPlan, SetLabelsOnNull) {
// OPTIONAL MATCH (n) SET n :label
storage::Storage db;
storage::v3::Storage db;
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
auto label = dba.NameToLabel("label");
@ -942,15 +944,15 @@ TEST(QueryPlan, SetLabelsOnNull) {
SymbolTable symbol_table;
auto n = MakeScanAll(storage, symbol_table, "n");
auto optional = std::make_shared<plan::Optional>(nullptr, n.op_, std::vector<Symbol>{n.sym_});
auto set_op = std::make_shared<plan::SetLabels>(optional, n.sym_, std::vector<storage::LabelId>{label});
EXPECT_EQ(0, CountIterable(dba.Vertices(storage::View::OLD)));
auto set_op = std::make_shared<plan::SetLabels>(optional, n.sym_, std::vector<storage::v3::LabelId>{label});
EXPECT_EQ(0, CountIterable(dba.Vertices(storage::v3::View::OLD)));
auto context = MakeContext(storage, symbol_table, &dba);
EXPECT_EQ(1, PullAll(*set_op, &context));
}
TEST(QueryPlan, RemovePropertyOnNull) {
// REMOVE (Null).prop
storage::Storage db;
storage::v3::Storage db;
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
AstStorage storage;
@ -966,7 +968,7 @@ TEST(QueryPlan, RemovePropertyOnNull) {
TEST(QueryPlan, RemoveLabelsOnNull) {
// OPTIONAL MATCH (n) REMOVE n :label
storage::Storage db;
storage::v3::Storage db;
auto storage_dba = db.Access();
DbAccessor dba(&storage_dba);
auto label = dba.NameToLabel("label");
@ -974,8 +976,8 @@ TEST(QueryPlan, RemoveLabelsOnNull) {
SymbolTable symbol_table;
auto n = MakeScanAll(storage, symbol_table, "n");
auto optional = std::make_shared<plan::Optional>(nullptr, n.op_, std::vector<Symbol>{n.sym_});
auto remove_op = std::make_shared<plan::RemoveLabels>(optional, n.sym_, std::vector<storage::LabelId>{label});
EXPECT_EQ(0, CountIterable(dba.Vertices(storage::View::OLD)));
auto remove_op = std::make_shared<plan::RemoveLabels>(optional, n.sym_, std::vector<storage::v3::LabelId>{label});
EXPECT_EQ(0, CountIterable(dba.Vertices(storage::v3::View::OLD)));
auto context = MakeContext(storage, symbol_table, &dba);
EXPECT_EQ(1, PullAll(*remove_op, &context));
}
@ -985,9 +987,9 @@ TEST_F(QueryPlanCRUDTest, DeleteSetProperty) {
DbAccessor dba(&storage_dba);
// Add a single vertex.
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}}).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(1, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(1, CountIterable(dba.Vertices(storage::v3::View::OLD)));
AstStorage storage;
SymbolTable symbol_table;
// MATCH (n) DELETE n SET n.prop = 42
@ -1007,9 +1009,9 @@ TEST_F(QueryPlanCRUDTest, DeleteSetPropertiesFromMap) {
DbAccessor dba(&storage_dba);
// Add a single vertex.
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}}).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(1, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(1, CountIterable(dba.Vertices(storage::v3::View::OLD)));
AstStorage storage;
SymbolTable symbol_table;
// MATCH (n) DELETE n SET n = {prop: 42}
@ -1033,11 +1035,11 @@ TEST_F(QueryPlanCRUDTest, DeleteSetPropertiesFrom) {
// Add a single vertex.
{
auto v = *dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}});
ASSERT_TRUE(v.SetProperty(dba.NameToProperty("prop"), storage::PropertyValue(1)).HasValue());
auto v = *dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}});
ASSERT_TRUE(v.SetProperty(dba.NameToProperty("prop"), storage::v3::PropertyValue(1)).HasValue());
}
dba.AdvanceCommand();
EXPECT_EQ(1, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(1, CountIterable(dba.Vertices(storage::v3::View::OLD)));
AstStorage storage;
SymbolTable symbol_table;
// MATCH (n) DELETE n SET n = n
@ -1057,16 +1059,16 @@ TEST_F(QueryPlanCRUDTest, DeleteRemoveLabels) {
DbAccessor dba(&storage_dba);
// Add a single vertex.
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}}).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(1, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(1, CountIterable(dba.Vertices(storage::v3::View::OLD)));
AstStorage storage;
SymbolTable symbol_table;
// MATCH (n) DELETE n REMOVE n :label
auto n = MakeScanAll(storage, symbol_table, "n");
auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
std::vector<storage::LabelId> labels{dba.NameToLabel("label1")};
std::vector<storage::v3::LabelId> labels{dba.NameToLabel("label1")};
auto rem_op = std::make_shared<plan::RemoveLabels>(delete_op, n.sym_, labels);
auto context = MakeContext(storage, symbol_table, &dba);
EXPECT_THROW(PullAll(*rem_op, &context), QueryRuntimeException);
@ -1077,9 +1079,9 @@ TEST_F(QueryPlanCRUDTest, DeleteRemoveProperty) {
DbAccessor dba(&storage_dba);
// Add a single vertex.
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::PropertyValue(1)}}).HasValue());
ASSERT_TRUE(dba.InsertVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(1)}}).HasValue());
dba.AdvanceCommand();
EXPECT_EQ(1, CountIterable(dba.Vertices(storage::View::OLD)));
EXPECT_EQ(1, CountIterable(dba.Vertices(storage::v3::View::OLD)));
AstStorage storage;
SymbolTable symbol_table;
// MATCH (n) DELETE n REMOVE n.prop

View File

@ -19,22 +19,23 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "communication/result_stream_faker.hpp"
#include "query/interpreter.hpp"
#include "storage/v2/storage.hpp"
#include "query/v2/interpreter.hpp"
#include "result_stream_faker.hpp"
#include "storage/v3/storage.hpp"
DECLARE_bool(query_cost_planner);
namespace memgraph::query::tests {
namespace memgraph::query::v2::tests {
class QueryExecution : public testing::Test {
protected:
storage::Storage db;
std::optional<storage::Storage> db_;
storage::v3::Storage db;
std::optional<storage::v3::Storage> db_;
std::optional<InterpreterContext> interpreter_context_;
std::optional<Interpreter> interpreter_;
std::filesystem::path data_directory{std::filesystem::temp_directory_path() / "MG_tests_unit_query_plan_edge_cases"};
std::filesystem::path data_directory{std::filesystem::temp_directory_path() /
"MG_tests_unit_query_v2_query_plan_edge_cases"};
void SetUp() {
db_.emplace();
@ -112,4 +113,4 @@ TEST_F(QueryExecution, EdgeUniquenessInOptional) {
.size(),
3);
}
} // namespace memgraph::query::tests
} // namespace memgraph::query::v2::tests

File diff suppressed because it is too large Load Diff

View File

@ -11,23 +11,23 @@
#include <gtest/gtest.h>
#include "query/frontend/semantic/symbol_table.hpp"
#include "query/plan/operator.hpp"
#include "query_plan_common.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/storage.hpp"
#include "query/v2/frontend/semantic/symbol_table.hpp"
#include "query/v2/plan/operator.hpp"
#include "query_v2_query_plan_common.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/storage.hpp"
namespace memgraph::query::tests {
namespace memgraph::query::v2::tests {
class QueryPlanCRUDTest : public testing::Test {
protected:
void SetUp() override {
ASSERT_TRUE(db.CreateSchema(label, {storage::SchemaProperty{property, common::SchemaType::INT}}));
ASSERT_TRUE(db.CreateSchema(label, {storage::v3::SchemaProperty{property, common::SchemaType::INT}}));
}
storage::Storage db;
const storage::LabelId label{db.NameToLabel("label")};
const storage::PropertyId property{db.NameToProperty("property")};
storage::v3::Storage db;
const storage::v3::LabelId label{db.NameToLabel("label")};
const storage::v3::PropertyId property{db.NameToProperty("property")};
};
TEST_F(QueryPlanCRUDTest, CreateNodeWithAttributes) {
@ -39,7 +39,7 @@ TEST_F(QueryPlanCRUDTest, CreateNodeWithAttributes) {
plan::NodeCreationInfo node;
node.symbol = symbol_table.CreateSymbol("n", true);
node.labels.emplace_back(label);
std::get<std::vector<std::pair<storage::PropertyId, Expression *>>>(node.properties)
std::get<std::vector<std::pair<storage::v3::PropertyId, Expression *>>>(node.properties)
.emplace_back(property, ast.Create<PrimitiveLiteral>(42));
plan::CreateNode create_node(nullptr, node);
@ -53,12 +53,12 @@ TEST_F(QueryPlanCRUDTest, CreateNodeWithAttributes) {
const auto &node_value = frame[node.symbol];
EXPECT_EQ(node_value.type(), TypedValue::Type::Vertex);
const auto &v = node_value.ValueVertex();
EXPECT_TRUE(*v.HasLabel(storage::View::NEW, label));
EXPECT_EQ(v.GetProperty(storage::View::NEW, property)->ValueInt(), 42);
EXPECT_EQ(CountIterable(*v.InEdges(storage::View::NEW)), 0);
EXPECT_EQ(CountIterable(*v.OutEdges(storage::View::NEW)), 0);
EXPECT_TRUE(*v.HasLabel(storage::v3::View::NEW, label));
EXPECT_EQ(v.GetProperty(storage::v3::View::NEW, property)->ValueInt(), 42);
EXPECT_EQ(CountIterable(*v.InEdges(storage::v3::View::NEW)), 0);
EXPECT_EQ(CountIterable(*v.OutEdges(storage::v3::View::NEW)), 0);
// Invokes LOG(FATAL) instead of erroring out.
// EXPECT_TRUE(v.HasLabel(label, storage::View::OLD).IsError());
// EXPECT_TRUE(v.HasLabel(label, storage::v3::View::OLD).IsError());
}
EXPECT_EQ(count, 1);
}
@ -70,7 +70,7 @@ TEST_F(QueryPlanCRUDTest, ScanAllEmpty) {
DbAccessor execution_dba(&dba);
auto node_symbol = symbol_table.CreateSymbol("n", true);
{
plan::ScanAll scan_all(nullptr, node_symbol, storage::View::OLD);
plan::ScanAll scan_all(nullptr, node_symbol, storage::v3::View::OLD);
auto context = MakeContext(ast, symbol_table, &execution_dba);
Frame frame(context.symbol_table.max_position());
auto cursor = scan_all.MakeCursor(utils::NewDeleteResource());
@ -79,7 +79,7 @@ TEST_F(QueryPlanCRUDTest, ScanAllEmpty) {
EXPECT_EQ(count, 0);
}
{
plan::ScanAll scan_all(nullptr, node_symbol, storage::View::NEW);
plan::ScanAll scan_all(nullptr, node_symbol, storage::v3::View::NEW);
auto context = MakeContext(ast, symbol_table, &execution_dba);
Frame frame(context.symbol_table.max_position());
auto cursor = scan_all.MakeCursor(utils::NewDeleteResource());
@ -93,8 +93,8 @@ TEST_F(QueryPlanCRUDTest, ScanAll) {
{
auto dba = db.Access();
for (int i = 0; i < 42; ++i) {
auto v = *dba.CreateVertexAndValidate(label, {}, {{property, storage::PropertyValue(i)}});
ASSERT_TRUE(v.SetProperty(property, storage::PropertyValue(i)).HasValue());
auto v = *dba.CreateVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(i)}});
ASSERT_TRUE(v.SetProperty(property, storage::v3::PropertyValue(i)).HasValue());
}
EXPECT_FALSE(dba.Commit().HasError());
}
@ -119,13 +119,13 @@ TEST_F(QueryPlanCRUDTest, ScanAllByLabel) {
auto dba = db.Access();
// Add some unlabeled vertices
for (int i = 0; i < 12; ++i) {
auto v = *dba.CreateVertexAndValidate(label, {}, {{property, storage::PropertyValue(i)}});
ASSERT_TRUE(v.SetProperty(property, storage::PropertyValue(i)).HasValue());
auto v = *dba.CreateVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(i)}});
ASSERT_TRUE(v.SetProperty(property, storage::v3::PropertyValue(i)).HasValue());
}
// Add labeled vertices
for (int i = 0; i < 42; ++i) {
auto v = *dba.CreateVertexAndValidate(label, {}, {{property, storage::PropertyValue(i)}});
ASSERT_TRUE(v.SetProperty(property, storage::PropertyValue(i)).HasValue());
auto v = *dba.CreateVertexAndValidate(label, {}, {{property, storage::v3::PropertyValue(i)}});
ASSERT_TRUE(v.SetProperty(property, storage::v3::PropertyValue(i)).HasValue());
ASSERT_TRUE(v.AddLabel(label2).HasValue());
}
EXPECT_FALSE(dba.Commit().HasError());
@ -143,4 +143,4 @@ TEST_F(QueryPlanCRUDTest, ScanAllByLabel) {
while (cursor->Pull(frame, context)) ++count;
EXPECT_EQ(count, 42);
}
} // namespace memgraph::query::tests
} // namespace memgraph::query::v2::tests

View File

@ -0,0 +1,222 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "query/v2/frontend/ast/ast.hpp"
#include "query/v2/frontend/ast/ast_visitor.hpp"
#include "query/v2/frontend/semantic/required_privileges.hpp"
#include "storage/v3/id_types.hpp"
#include "query_v2_query_common.hpp"
using namespace memgraph::query::v2;
class FakeDbAccessor {};
const std::string EDGE_TYPE = "0";
const std::string LABEL_0 = "label0";
const std::string LABEL_1 = "label1";
const std::string PROP_0 = "prop0";
using ::testing::UnorderedElementsAre;
class TestPrivilegeExtractor : public ::testing::Test {
protected:
AstStorage storage;
FakeDbAccessor dba;
};
TEST_F(TestPrivilegeExtractor, CreateNode) {
auto *query = QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n")))));
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::CREATE));
}
TEST_F(TestPrivilegeExtractor, MatchNodeDelete) {
auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), DELETE(IDENT("n"))));
EXPECT_THAT(GetRequiredPrivileges(query),
UnorderedElementsAre(AuthQuery::Privilege::MATCH, AuthQuery::Privilege::DELETE));
}
TEST_F(TestPrivilegeExtractor, MatchNodeReturn) {
auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), RETURN("n")));
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::MATCH));
}
TEST_F(TestPrivilegeExtractor, MatchCreateExpand) {
auto *query =
QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))),
CREATE(PATTERN(NODE("n"), EDGE("r", EdgeAtom::Direction::OUT, {EDGE_TYPE}), NODE("m")))));
EXPECT_THAT(GetRequiredPrivileges(query),
UnorderedElementsAre(AuthQuery::Privilege::MATCH, AuthQuery::Privilege::CREATE));
}
TEST_F(TestPrivilegeExtractor, MatchNodeSetLabels) {
auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), SET("n", {LABEL_0, LABEL_1})));
EXPECT_THAT(GetRequiredPrivileges(query),
UnorderedElementsAre(AuthQuery::Privilege::MATCH, AuthQuery::Privilege::SET));
}
TEST_F(TestPrivilegeExtractor, MatchNodeSetProperty) {
auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))),
SET(PROPERTY_LOOKUP(storage.Create<Identifier>("n"), PROP_0), LITERAL(42))));
EXPECT_THAT(GetRequiredPrivileges(query),
UnorderedElementsAre(AuthQuery::Privilege::MATCH, AuthQuery::Privilege::SET));
}
TEST_F(TestPrivilegeExtractor, MatchNodeSetProperties) {
auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), SET("n", LIST())));
EXPECT_THAT(GetRequiredPrivileges(query),
UnorderedElementsAre(AuthQuery::Privilege::MATCH, AuthQuery::Privilege::SET));
}
TEST_F(TestPrivilegeExtractor, MatchNodeRemoveLabels) {
auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), REMOVE("n", {LABEL_0, LABEL_1})));
EXPECT_THAT(GetRequiredPrivileges(query),
UnorderedElementsAre(AuthQuery::Privilege::MATCH, AuthQuery::Privilege::REMOVE));
}
TEST_F(TestPrivilegeExtractor, MatchNodeRemoveProperty) {
auto *query =
QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), REMOVE(PROPERTY_LOOKUP(storage.Create<Identifier>("n"), PROP_0))));
EXPECT_THAT(GetRequiredPrivileges(query),
UnorderedElementsAre(AuthQuery::Privilege::MATCH, AuthQuery::Privilege::REMOVE));
}
TEST_F(TestPrivilegeExtractor, CreateIndex) {
auto *query = CREATE_INDEX_ON(storage.GetLabelIx(LABEL_0), storage.GetPropertyIx(PROP_0));
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::INDEX));
}
TEST_F(TestPrivilegeExtractor, AuthQuery) {
auto *query =
AUTH_QUERY(AuthQuery::Action::CREATE_ROLE, "", "role", "", nullptr, std::vector<AuthQuery::Privilege>{});
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::AUTH));
}
TEST_F(TestPrivilegeExtractor, ShowIndexInfo) {
auto *query = storage.Create<InfoQuery>();
query->info_type_ = InfoQuery::InfoType::INDEX;
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::INDEX));
}
TEST_F(TestPrivilegeExtractor, ShowStatsInfo) {
auto *query = storage.Create<InfoQuery>();
query->info_type_ = InfoQuery::InfoType::STORAGE;
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::STATS));
}
TEST_F(TestPrivilegeExtractor, ShowConstraintInfo) {
auto *query = storage.Create<InfoQuery>();
query->info_type_ = InfoQuery::InfoType::CONSTRAINT;
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::CONSTRAINT));
}
TEST_F(TestPrivilegeExtractor, CreateConstraint) {
auto *query = storage.Create<ConstraintQuery>();
query->action_type_ = ConstraintQuery::ActionType::CREATE;
query->constraint_.label = storage.GetLabelIx("label");
query->constraint_.properties.push_back(storage.GetPropertyIx("prop0"));
query->constraint_.properties.push_back(storage.GetPropertyIx("prop1"));
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::CONSTRAINT));
}
TEST_F(TestPrivilegeExtractor, DropConstraint) {
auto *query = storage.Create<ConstraintQuery>();
query->action_type_ = ConstraintQuery::ActionType::DROP;
query->constraint_.label = storage.GetLabelIx("label");
query->constraint_.properties.push_back(storage.GetPropertyIx("prop0"));
query->constraint_.properties.push_back(storage.GetPropertyIx("prop1"));
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::CONSTRAINT));
}
// NOLINTNEXTLINE(hicpp-special-member-functions)
TEST_F(TestPrivilegeExtractor, DumpDatabase) {
auto *query = storage.Create<DumpQuery>();
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::DUMP));
}
TEST_F(TestPrivilegeExtractor, ReadFile) {
auto load_csv = storage.Create<LoadCsv>();
load_csv->row_var_ = IDENT("row");
auto *query = QUERY(SINGLE_QUERY(load_csv));
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::READ_FILE));
}
TEST_F(TestPrivilegeExtractor, LockPathQuery) {
auto *query = storage.Create<LockPathQuery>();
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::DURABILITY));
}
TEST_F(TestPrivilegeExtractor, FreeMemoryQuery) {
auto *query = storage.Create<FreeMemoryQuery>();
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::FREE_MEMORY));
}
TEST_F(TestPrivilegeExtractor, TriggerQuery) {
auto *query = storage.Create<TriggerQuery>();
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::TRIGGER));
}
TEST_F(TestPrivilegeExtractor, SetIsolationLevelQuery) {
auto *query = storage.Create<IsolationLevelQuery>();
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::CONFIG));
}
TEST_F(TestPrivilegeExtractor, CreateSnapshotQuery) {
auto *query = storage.Create<CreateSnapshotQuery>();
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::DURABILITY));
}
TEST_F(TestPrivilegeExtractor, StreamQuery) {
auto *query = storage.Create<StreamQuery>();
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::STREAM));
}
TEST_F(TestPrivilegeExtractor, SettingQuery) {
auto *query = storage.Create<SettingQuery>();
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::CONFIG));
}
TEST_F(TestPrivilegeExtractor, ShowVersion) {
auto *query = storage.Create<VersionQuery>();
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::STATS));
}
TEST_F(TestPrivilegeExtractor, SchemaQuery) {
auto *query = storage.Create<SchemaQuery>();
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::SCHEMA));
}
TEST_F(TestPrivilegeExtractor, CallProcedureQuery) {
{
auto *query = QUERY(SINGLE_QUERY(CALL_PROCEDURE("mg.get_module_files")));
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::MODULE_READ));
}
{
auto *query = QUERY(SINGLE_QUERY(CALL_PROCEDURE("mg.create_module_file", {LITERAL("some_name.py")})));
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::MODULE_WRITE));
}
{
auto *query = QUERY(
SINGLE_QUERY(CALL_PROCEDURE("mg.update_module_file", {LITERAL("some_name.py"), LITERAL("some content")})));
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::MODULE_WRITE));
}
{
auto *query = QUERY(SINGLE_QUERY(CALL_PROCEDURE("mg.get_module_file", {LITERAL("some_name.py")})));
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::MODULE_READ));
}
{
auto *query = QUERY(SINGLE_QUERY(CALL_PROCEDURE("mg.delete_module_file", {LITERAL("some_name.py")})));
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::MODULE_WRITE));
}
}

View File

@ -0,0 +1,132 @@
// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#include <map>
#include "glue/v2/communication.hpp"
#include "query/v2/typed_value.hpp"
#include "storage/v3/storage.hpp"
#include "utils/algorithm.hpp"
/**
* A mocker for the data output record stream.
* This implementation checks that messages are
* sent to it in an acceptable order, and tracks
* the content of those messages.
*/
class ResultStreamFaker {
public:
explicit ResultStreamFaker(memgraph::storage::v3::Storage *store) : store_(store) {}
ResultStreamFaker(const ResultStreamFaker &) = delete;
ResultStreamFaker &operator=(const ResultStreamFaker &) = delete;
ResultStreamFaker(ResultStreamFaker &&) = default;
ResultStreamFaker &operator=(ResultStreamFaker &&) = default;
void Header(const std::vector<std::string> &fields) { header_ = fields; }
void Result(const std::vector<memgraph::communication::bolt::Value> &values) { results_.push_back(values); }
void Result(const std::vector<memgraph::query::v2::TypedValue> &values) {
std::vector<memgraph::communication::bolt::Value> bvalues;
bvalues.reserve(values.size());
for (const auto &value : values) {
auto maybe_value = memgraph::glue::v2::ToBoltValue(value, *store_, memgraph::storage::v3::View::NEW);
MG_ASSERT(maybe_value.HasValue());
bvalues.push_back(std::move(*maybe_value));
}
results_.push_back(std::move(bvalues));
}
void Summary(const std::map<std::string, memgraph::communication::bolt::Value> &summary) { summary_ = summary; }
void Summary(const std::map<std::string, memgraph::query::v2::TypedValue> &summary) {
std::map<std::string, memgraph::communication::bolt::Value> bsummary;
for (const auto &item : summary) {
auto maybe_value = memgraph::glue::v2::ToBoltValue(item.second, *store_, memgraph::storage::v3::View::NEW);
MG_ASSERT(maybe_value.HasValue());
bsummary.insert({item.first, std::move(*maybe_value)});
}
summary_ = std::move(bsummary);
}
const auto &GetHeader() const { return header_; }
const auto &GetResults() const { return results_; }
const auto &GetSummary() const { return summary_; }
friend std::ostream &operator<<(std::ostream &os, const ResultStreamFaker &results) {
auto decoded_value_to_string = [](const auto &value) {
std::stringstream ss;
ss << value;
return ss.str();
};
const std::vector<std::string> &header = results.GetHeader();
std::vector<int> column_widths(header.size());
std::transform(header.begin(), header.end(), column_widths.begin(), [](const auto &s) { return s.size(); });
// convert all the results into strings, and track max column width
auto &results_data = results.GetResults();
std::vector<std::vector<std::string>> result_strings(results_data.size(),
std::vector<std::string>(column_widths.size()));
for (int row_ind = 0; row_ind < static_cast<int>(results_data.size()); ++row_ind) {
for (int col_ind = 0; col_ind < static_cast<int>(column_widths.size()); ++col_ind) {
std::string string_val = decoded_value_to_string(results_data[row_ind][col_ind]);
column_widths[col_ind] = std::max(column_widths[col_ind], (int)string_val.size());
result_strings[row_ind][col_ind] = string_val;
}
}
// output a results table
// first define some helper functions
auto emit_horizontal_line = [&]() {
os << "+";
for (auto col_width : column_widths) os << std::string((unsigned long)col_width + 2, '-') << "+";
os << std::endl;
};
auto emit_result_vec = [&](const std::vector<std::string> result_vec) {
os << "| ";
for (int col_ind = 0; col_ind < static_cast<int>(column_widths.size()); ++col_ind) {
const std::string &res = result_vec[col_ind];
os << res << std::string(column_widths[col_ind] - res.size(), ' ');
os << " | ";
}
os << std::endl;
};
// final output of results
emit_horizontal_line();
emit_result_vec(results.GetHeader());
emit_horizontal_line();
for (const auto &result_vec : result_strings) emit_result_vec(result_vec);
emit_horizontal_line();
os << "Found " << results_data.size() << " matching results" << std::endl;
// output the summary
os << "Query summary: {";
memgraph::utils::PrintIterable(os, results.GetSummary(), ", ",
[&](auto &stream, const auto &kv) { stream << kv.first << ": " << kv.second; });
os << "}" << std::endl;
return os;
}
private:
memgraph::storage::v3::Storage *store_;
// the data that the record stream can accept
std::vector<std::string> header_;
std::vector<std::vector<memgraph::communication::bolt::Value>> results_;
std::map<std::string, memgraph::communication::bolt::Value> summary_;
};

View File

@ -19,23 +19,23 @@
#include <vector>
#include "common/types.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/schema_validator.hpp"
#include "storage/v2/schemas.hpp"
#include "storage/v2/storage.hpp"
#include "storage/v2/temporal.hpp"
#include "storage/v3/id_types.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/schema_validator.hpp"
#include "storage/v3/schemas.hpp"
#include "storage/v3/storage.hpp"
#include "storage/v3/temporal.hpp"
using testing::Pair;
using testing::UnorderedElementsAre;
using SchemaType = memgraph::common::SchemaType;
namespace memgraph::storage::tests {
namespace memgraph::storage::v3::tests {
class SchemaTest : public testing::Test {
private:
memgraph::storage::NameIdMapper label_mapper_;
memgraph::storage::NameIdMapper property_mapper_;
NameIdMapper label_mapper_;
NameIdMapper property_mapper_;
protected:
LabelId NameToLabel(const std::string &name) { return LabelId::FromUint(label_mapper_.NameToId(name)); }
@ -161,8 +161,8 @@ class SchemaValidatorTest : public testing::Test {
PropertyId NameToProperty(const std::string &name) { return PropertyId::FromUint(property_mapper_.NameToId(name)); }
private:
memgraph::storage::NameIdMapper label_mapper_;
memgraph::storage::NameIdMapper property_mapper_;
NameIdMapper label_mapper_;
NameIdMapper property_mapper_;
protected:
Schemas schemas;
@ -291,4 +291,4 @@ TEST_F(SchemaValidatorTest, TestSchemaValidatePropertyUpdateLabel) {
}
EXPECT_EQ(schema_validator.ValidateLabelUpdate(NameToLabel("test")), std::nullopt);
}
} // namespace memgraph::storage::tests
} // namespace memgraph::storage::v3::tests