Implement filtering capabilities for ScanAll ()

ScanVerticesrequest was not able to utilize filtering capabilities
before. With these modification it is now able to filter the scanned
vertices based on the filter_expressions field in the
ScanVerticesRequest message type.
This commit is contained in:
gvolfing 2022-10-19 16:09:00 +02:00 committed by GitHub
parent 6bb40a7f49
commit 85b8ce9101
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 490 additions and 167 deletions

View File

@ -21,6 +21,7 @@
#include <utility>
#include <vector>
#include "expr/typed_value_exception.hpp"
#include "utils/algorithm.hpp"
#include "utils/exceptions.hpp"
#include "utils/fnv.hpp"
@ -32,16 +33,6 @@
namespace memgraph::expr {
/**
* An exception raised by the TypedValue system. Typically when
* trying to perform operations (such as addition) on TypedValues
* of incompatible Types.
*/
class TypedValueException : public utils::BasicException {
public:
using utils::BasicException::BasicException;
};
// TODO: Neo4j does overflow checking. Should we also implement it?
/**
* Stores a query runtime value and its type.

View File

@ -0,0 +1,26 @@
// 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 "utils/exceptions.hpp"
namespace memgraph::expr {
/**
* An exception raised by the TypedValue system. Typically when
* trying to perform operations (such as addition) on TypedValues
* of incompatible Types.
*/
class TypedValueException : public utils::BasicException {
public:
using utils::BasicException::BasicException;
};
} // namespace memgraph::expr

View File

@ -202,46 +202,8 @@ Value ToBoltValue(msgs::Value value) {
return Value{std::move(map)};
}
case msgs::Value::Type::Vertex:
case msgs::Value::Type::Edge:
case msgs::Value::Type::Path: {
throw utils::BasicException("Vertex, Edge and Path are not supported!");
}
}
}
Value ToBoltValue(msgs::Value value, const coordinator::ShardMap & /*shard_map*/, storage::v3::View /*view*/) {
switch (value.type) {
case msgs::Value::Type::Null:
return {};
case msgs::Value::Type::Bool:
return {value.bool_v};
case msgs::Value::Type::Int64:
return {value.int_v};
case msgs::Value::Type::Double:
return {value.double_v};
case msgs::Value::Type::String:
return {std::string(value.string_v)};
case msgs::Value::Type::List: {
std::vector<Value> values;
values.reserve(value.list_v.size());
for (const auto &v : value.list_v) {
auto maybe_value = ToBoltValue(v);
values.emplace_back(std::move(maybe_value));
}
return Value{std::move(values)};
}
case msgs::Value::Type::Map: {
std::map<std::string, Value> map;
for (const auto &kv : value.map_v) {
auto maybe_value = ToBoltValue(kv.second);
map.emplace(kv.first, std::move(maybe_value));
}
return Value{std::move(map)};
}
case msgs::Value::Type::Vertex:
case msgs::Value::Type::Edge:
case msgs::Value::Type::Path: {
throw utils::BasicException("Vertex, Edge and Path are not supported!");
case msgs::Value::Type::Edge: {
throw utils::BasicException("Vertex and Edge not supported!");
}
}
}

View File

@ -11,5 +11,9 @@
#pragma once
#ifdef MG_AST_INCLUDE_PATH
#error You are probably trying to include files from expr from both the storage and query engines! You will have a rough time kid!
#endif
#define MG_AST_INCLUDE_PATH "query/v2/frontend/ast/ast.hpp" // NOLINT(cppcoreguidelines-macro-usage)
#define MG_INJECTED_NAMESPACE_NAME memgraph::query::v2 // NOLINT(cppcoreguidelines-macro-usage)

View File

@ -50,8 +50,6 @@ inline TypedValue ValueToTypedValue(const msgs::Value &value) {
return TypedValue(accessors::VertexAccessor(value.vertex_v, {}));
case Value::Type::Edge:
return TypedValue(accessors::EdgeAccessor(value.edge_v, {}));
case Value::Type::Path:
break;
}
throw std::runtime_error("Incorrect type in conversion");
}
@ -91,7 +89,10 @@ inline msgs::Value TypedValueToValue(const TypedValue &value) {
case TypedValue::Type::Edge:
return Value(value.ValueEdge().GetEdge());
case TypedValue::Type::Path:
default:
case TypedValue::Type::LocalTime:
case TypedValue::Type::LocalDateTime:
case TypedValue::Type::Date:
case TypedValue::Type::Duration:
break;
}
throw std::runtime_error("Incorrect type in conversion");

View File

@ -22,9 +22,9 @@
#include <variant>
#include <vector>
#include "expr/semantic/symbol.hpp"
#include "query/v2/common.hpp"
#include "query/v2/frontend/ast/ast.hpp"
#include "expr/semantic/symbol.hpp"
#include "query/v2/bindings/typed_value.hpp"
#include "query/v2/bindings/frame.hpp"
#include "query/v2/bindings/symbol_table.hpp"

View File

@ -76,16 +76,6 @@ struct Vertex {
friend bool operator==(const Vertex &lhs, const Vertex &rhs) { return lhs.id == rhs.id; }
};
struct PathPart {
Vertex dst;
Gid edge;
};
struct Path {
Vertex src;
std::vector<PathPart> parts;
};
struct Null {};
struct Value {
@ -135,13 +125,9 @@ struct Value {
case Type::Map:
std::destroy_at(&map_v);
return;
case Type::Vertex:
std::destroy_at(&vertex_v);
return;
case Type::Path:
std::destroy_at(&path_v);
return;
case Type::Edge:
std::destroy_at(&edge_v);
}
@ -175,9 +161,6 @@ struct Value {
case Type::Edge:
new (&edge_v) Edge(other.edge_v);
return;
case Type::Path:
new (&path_v) Path(other.path_v);
return;
}
}
@ -209,9 +192,6 @@ struct Value {
case Type::Edge:
new (&edge_v) Edge(std::move(other.edge_v));
break;
case Type::Path:
new (&path_v) Path(std::move(other.path_v));
break;
}
other.DestroyValue();
@ -251,9 +231,6 @@ struct Value {
case Type::Edge:
new (&edge_v) Edge(other.edge_v);
break;
case Type::Path:
new (&path_v) Path(other.path_v);
break;
}
return *this;
@ -292,9 +269,6 @@ struct Value {
case Type::Edge:
new (&edge_v) Edge(std::move(other.edge_v));
break;
case Type::Path:
new (&path_v) Path(std::move(other.path_v));
break;
}
other.DestroyValue();
@ -302,7 +276,7 @@ struct Value {
return *this;
}
enum class Type : uint8_t { Null, Bool, Int64, Double, String, List, Map, Vertex, Edge, Path };
enum class Type : uint8_t { Null, Bool, Int64, Double, String, List, Map, Vertex, Edge };
Type type{Type::Null};
union {
Null null_v;
@ -314,7 +288,6 @@ struct Value {
std::map<std::string, Value> map_v;
Vertex vertex_v;
Edge edge_v;
Path path_v;
};
friend bool operator==(const Value &lhs, const Value &rhs) {
@ -340,8 +313,6 @@ struct Value {
return lhs.vertex_v == rhs.vertex_v;
case Value::Type::Edge:
return lhs.edge_v == rhs.edge_v;
case Value::Type::Path:
return true;
}
}
};
@ -381,18 +352,22 @@ struct ScanVerticesRequest {
Hlc transaction_id;
VertexId start_id;
std::optional<std::vector<PropertyId>> props_to_return;
// expression that determines if vertex is returned or not
std::vector<std::string> filter_expressions;
// expression whose result is returned for every vertex
std::vector<std::string> vertex_expressions;
std::optional<size_t> batch_limit;
StorageView storage_view{StorageView::NEW};
std::optional<Label> label;
std::optional<std::pair<PropertyId, std::string>> property_expression_pair;
std::optional<std::vector<std::string>> filter_expressions;
};
struct ScanResultRow {
Vertex vertex;
// empty() is no properties returned
std::vector<std::pair<PropertyId, Value>> props;
std::vector<Value> evaluated_vertex_expressions;
};
struct ScanVerticesResponse {

View File

@ -11,5 +11,9 @@
#pragma once
#ifdef MG_AST_INCLUDE_PATH
#error You are probably trying to include some files of mg-expr from both the storage and query engines! You will have a rough time kid!
#endif
#define MG_AST_INCLUDE_PATH "storage/v3/bindings/ast/ast.hpp" // NOLINT(cppcoreguidelines-macro-usage)
#define MG_INJECTED_NAMESPACE_NAME memgraph::storage::v3 // NOLINT(cppcoreguidelines-macro-usage)

View File

@ -0,0 +1,19 @@
// 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 "storage/v3/bindings/bindings.hpp"
#include "expr/ast/pretty_print_ast_to_original_expression.hpp"
#include "storage/v3/bindings/typed_value.hpp"
namespace memgraph::storage::v3 {} // namespace memgraph::storage::v3

View File

@ -9,7 +9,7 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include "expr/typed_value.hpp"
#include "expr/typed_value_exception.hpp"
#include "query/v2/requests.hpp"
#include "storage/v3/property_value.hpp"
#include "utils/memory.hpp"

View File

@ -15,6 +15,7 @@
#include <cstddef>
#include <ranges>
#include "common/types.hpp"
#include "storage/v3/schemas.hpp"
namespace memgraph::storage::v3 {
@ -78,6 +79,39 @@ SchemaValidator::SchemaValidator(Schemas &schemas) : schemas_{schemas} {}
return std::nullopt;
}
[[nodiscard]] std::optional<SchemaViolation> SchemaValidator::ValidateVertexCreate(
LabelId primary_label, const std::vector<LabelId> &labels,
const std::vector<PropertyValue> &primary_properties) const {
// Schema on primary label
const auto *schema = schemas_.GetSchema(primary_label);
if (schema == nullptr) {
return SchemaViolation(SchemaViolation::ValidationStatus::NO_SCHEMA_DEFINED_FOR_LABEL, primary_label);
}
// Is there another primary label among secondary labels
for (const auto &secondary_label : labels) {
if (schemas_.GetSchema(secondary_label)) {
return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY, secondary_label);
}
}
// Quick size check
if (schema->second.size() != primary_properties.size()) {
return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PRIMARY_PROPERTIES_UNDEFINED, primary_label);
}
// Check only properties defined by schema
for (size_t i{0}; i < schema->second.size(); ++i) {
// Check schema property type
if (auto property_schema_type = PropertyTypeToSchemaType(primary_properties[i]);
property_schema_type && *property_schema_type != schema->second[i].type) {
return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PROPERTY_WRONG_TYPE, primary_label,
schema->second[i], primary_properties[i]);
}
}
return std::nullopt;
}
[[nodiscard]] std::optional<SchemaViolation> SchemaValidator::ValidatePropertyUpdate(
const LabelId primary_label, const PropertyId property_id) const {
// Verify existence of schema on primary label
@ -103,6 +137,8 @@ SchemaValidator::SchemaValidator(Schemas &schemas) : schemas_{schemas} {}
return std::nullopt;
}
const Schemas::Schema *SchemaValidator::GetSchema(LabelId label) const { return schemas_.GetSchema(label); }
VertexValidator::VertexValidator(const SchemaValidator &schema_validator, const LabelId primary_label)
: schema_validator{&schema_validator}, primary_label_{primary_label} {}

View File

@ -29,6 +29,7 @@ struct SchemaViolation {
VERTEX_UPDATE_PRIMARY_KEY,
VERTEX_UPDATE_PRIMARY_LABEL,
VERTEX_SECONDARY_LABEL_IS_PRIMARY,
VERTEX_PRIMARY_PROPERTIES_UNDEFINED,
};
SchemaViolation(ValidationStatus status, LabelId label);
@ -50,15 +51,21 @@ class SchemaValidator {
public:
explicit SchemaValidator(Schemas &schemas);
[[nodiscard]] std::optional<SchemaViolation> ValidateVertexCreate(
[[deprecated]] std::optional<SchemaViolation> ValidateVertexCreate(
LabelId primary_label, const std::vector<LabelId> &labels,
const std::vector<std::pair<PropertyId, PropertyValue>> &properties) const;
[[nodiscard]] std::optional<SchemaViolation> ValidateVertexCreate(
LabelId primary_label, const std::vector<LabelId> &labels,
const std::vector<PropertyValue> &primary_properties) const;
[[nodiscard]] std::optional<SchemaViolation> ValidatePropertyUpdate(LabelId primary_label,
PropertyId property_id) const;
[[nodiscard]] std::optional<SchemaViolation> ValidateLabelUpdate(LabelId label) const;
const Schemas::Schema *GetSchema(LabelId label) const;
private:
Schemas &schemas_;
};

View File

@ -12,6 +12,7 @@
#include "storage/v3/shard.hpp"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <memory>
@ -392,15 +393,9 @@ ResultSchema<VertexAccessor> Shard::Accessor::CreateVertexAndValidate(
const std::vector<std::pair<PropertyId, PropertyValue>> &properties) {
OOMExceptionEnabler oom_exception;
const auto schema = shard_->GetSchema(shard_->primary_label_)->second;
std::vector<std::pair<PropertyId, PropertyValue>> primary_properties_ordered;
// TODO(jbajic) Maybe react immediately and send Violation
MG_ASSERT("PrimaryKey is invalid size");
for (auto i{0}; i < schema.size(); ++i) {
primary_properties_ordered.emplace_back(schema[i].property_id, primary_properties[i]);
}
auto maybe_schema_violation =
GetSchemaValidator().ValidateVertexCreate(shard_->primary_label_, labels, primary_properties_ordered);
GetSchemaValidator().ValidateVertexCreate(shard_->primary_label_, labels, primary_properties);
if (maybe_schema_violation) {
return {std::move(*maybe_schema_violation)};
}

View File

@ -9,18 +9,31 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include <algorithm>
#include <functional>
#include <iterator>
#include <utility>
#include "parser/opencypher/parser.hpp"
#include "query/v2/requests.hpp"
#include "storage/v3/bindings/ast/ast.hpp"
#include "storage/v3/bindings/cypher_main_visitor.hpp"
#include "storage/v3/bindings/db_accessor.hpp"
#include "storage/v3/bindings/eval.hpp"
#include "storage/v3/bindings/frame.hpp"
#include "storage/v3/bindings/pretty_print_ast_to_original_expression.hpp"
#include "storage/v3/bindings/symbol_generator.hpp"
#include "storage/v3/bindings/symbol_table.hpp"
#include "storage/v3/bindings/typed_value.hpp"
#include "storage/v3/id_types.hpp"
#include "storage/v3/key_store.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/schemas.hpp"
#include "storage/v3/shard_rsm.hpp"
#include "storage/v3/storage.hpp"
#include "storage/v3/value_conversions.hpp"
#include "storage/v3/vertex_accessor.hpp"
#include "storage/v3/view.hpp"
using memgraph::msgs::Label;
using memgraph::msgs::PropertyId;
@ -30,8 +43,11 @@ using memgraph::msgs::VertexId;
using memgraph::storage::conversions::ConvertPropertyVector;
using memgraph::storage::conversions::ConvertValueVector;
using memgraph::storage::conversions::FromPropertyValueToValue;
using memgraph::storage::conversions::ToPropertyValue;
using memgraph::storage::conversions::ToValue;
using memgraph::storage::v3::View;
namespace memgraph::storage::v3 {
namespace {
std::vector<std::pair<memgraph::storage::v3::PropertyId, memgraph::storage::v3::PropertyValue>> ConvertPropertyMap(
@ -39,9 +55,10 @@ std::vector<std::pair<memgraph::storage::v3::PropertyId, memgraph::storage::v3::
std::vector<std::pair<memgraph::storage::v3::PropertyId, memgraph::storage::v3::PropertyValue>> ret;
ret.reserve(properties.size());
for (auto &[key, value] : properties) {
ret.emplace_back(std::make_pair(key, ToPropertyValue(std::move(value))));
}
std::transform(std::make_move_iterator(properties.begin()), std::make_move_iterator(properties.end()),
std::back_inserter(ret), [](std::pair<PropertyId, Value> &&property) {
return std::make_pair(property.first, ToPropertyValue(std::move(property.second)));
});
return ret;
}
@ -51,16 +68,15 @@ std::vector<std::pair<memgraph::storage::v3::PropertyId, Value>> FromMap(
std::vector<std::pair<memgraph::storage::v3::PropertyId, Value>> ret;
ret.reserve(properties.size());
for (const auto &[key, value] : properties) {
ret.emplace_back(std::make_pair(key, value));
}
std::transform(properties.begin(), properties.end(), std::back_inserter(ret),
[](const auto &property) { return std::make_pair(property.first, property.second); });
return ret;
}
std::optional<std::map<PropertyId, Value>> CollectSpecificPropertiesFromAccessor(
const memgraph::storage::v3::VertexAccessor &acc, const std::vector<memgraph::storage::v3::PropertyId> &props,
memgraph::storage::v3::View view) {
View view) {
std::map<PropertyId, Value> ret;
for (const auto &prop : props) {
@ -74,7 +90,7 @@ std::optional<std::map<PropertyId, Value>> CollectSpecificPropertiesFromAccessor
spdlog::debug("The specified property does not exist but it should");
return std::nullopt;
}
ret.emplace(std::make_pair(prop, ToValue(value)));
ret.emplace(std::make_pair(prop, FromPropertyValueToValue(value)));
}
return ret;
@ -89,9 +105,11 @@ std::optional<std::map<PropertyId, Value>> CollectAllPropertiesFromAccessor(
spdlog::debug("Encountered an error while trying to get vertex properties.");
return std::nullopt;
}
for (const auto &[prop_key, prop_val] : props.GetValue()) {
ret.emplace(prop_key, ToValue(prop_val));
}
const auto &properties = props.GetValue();
std::transform(properties.begin(), properties.end(), std::inserter(ret, ret.begin()), [](const auto &property) {
return std::make_pair(property.first, FromPropertyValueToValue(property.second));
});
auto maybe_pk = acc.PrimaryKey(view);
if (maybe_pk.HasError()) {
@ -101,31 +119,217 @@ std::optional<std::map<PropertyId, Value>> CollectAllPropertiesFromAccessor(
const auto pk = maybe_pk.GetValue();
MG_ASSERT(schema->second.size() == pk.size(), "PrimaryKey size does not match schema!");
for (size_t i{0}; i < schema->second.size(); ++i) {
ret.emplace(schema->second[i].property_id, ToValue(pk[i]));
ret.emplace(schema->second[i].property_id, FromPropertyValueToValue(pk[i]));
}
return ret;
}
Value ConstructValueVertex(const memgraph::storage::v3::VertexAccessor &acc, memgraph::storage::v3::View view) {
memgraph::msgs::Value ConstructValueVertex(const memgraph::storage::v3::VertexAccessor &acc, View view) {
// Get the vertex id
auto prim_label = acc.PrimaryLabel(view).GetValue();
Label value_label{.id = prim_label};
memgraph::msgs::Label value_label{.id = prim_label};
auto prim_key = ConvertValueVector(acc.PrimaryKey(view).GetValue());
VertexId vertex_id = std::make_pair(value_label, prim_key);
memgraph::msgs::VertexId vertex_id = std::make_pair(value_label, prim_key);
// Get the labels
auto vertex_labels = acc.Labels(view).GetValue();
std::vector<Label> value_labels;
for (const auto &label : vertex_labels) {
Label l = {.id = label};
value_labels.push_back(l);
}
std::vector<memgraph::msgs::Label> value_labels;
value_labels.reserve(vertex_labels.size());
std::transform(vertex_labels.begin(), vertex_labels.end(), std::back_inserter(value_labels),
[](const auto &label) { return msgs::Label{.id = label}; });
return Value({.id = vertex_id, .labels = value_labels});
}
Value ConstructValueEdge(const memgraph::storage::v3::EdgeAccessor &acc, View view) {
memgraph::msgs::EdgeType type = {.id = acc.EdgeType().AsUint()};
memgraph::msgs::EdgeId gid = {.gid = acc.Gid().AsUint()};
Label src_prim_label = {.id = acc.FromVertex().primary_label};
memgraph::msgs::VertexId src_vertex =
std::make_pair(src_prim_label, ConvertValueVector(acc.FromVertex().primary_key));
Label dst_prim_label = {.id = acc.ToVertex().primary_label};
memgraph::msgs::VertexId dst_vertex = std::make_pair(dst_prim_label, ConvertValueVector(acc.ToVertex().primary_key));
std::optional<std::vector<std::pair<PropertyId, Value>>> properties_opt = {};
const auto &properties = acc.Properties(view);
if (properties.HasValue()) {
const auto &props = properties.GetValue();
std::vector<std::pair<PropertyId, Value>> present_properties;
present_properties.reserve(props.size());
std::transform(props.begin(), props.end(), std::back_inserter(present_properties),
[](const auto &prop) { return std::make_pair(prop.first, FromPropertyValueToValue(prop.second)); });
properties_opt = std::move(present_properties);
}
return Value({.src = src_vertex, .dst = dst_vertex, .properties = properties_opt, .id = gid, .type = type});
}
Value FromTypedValueToValue(memgraph::storage::v3::TypedValue &&tv) {
using memgraph::storage::v3::TypedValue;
switch (tv.type()) {
case TypedValue::Type::Bool:
return Value(tv.ValueBool());
case TypedValue::Type::Double:
return Value(tv.ValueDouble());
case TypedValue::Type::Int:
return Value(tv.ValueInt());
case TypedValue::Type::List: {
std::vector<Value> list;
auto &tv_list = tv.ValueList();
list.reserve(tv_list.size());
std::transform(tv_list.begin(), tv_list.end(), std::back_inserter(list),
[](auto &elem) { return FromTypedValueToValue(std::move(elem)); });
return Value(list);
}
case TypedValue::Type::Map: {
std::map<std::string, Value> map;
for (auto &[key, val] : tv.ValueMap()) {
map.emplace(key, FromTypedValueToValue(std::move(val)));
}
return Value(map);
}
case TypedValue::Type::Null:
return Value{};
case TypedValue::Type::String:
return Value((std::string(tv.ValueString())));
case TypedValue::Type::Vertex:
return ConstructValueVertex(tv.ValueVertex(), View::OLD);
case TypedValue::Type::Edge:
return ConstructValueEdge(tv.ValueEdge(), View::OLD);
// TBD -> we need to specify temporal types, not a priority.
case TypedValue::Type::Date:
case TypedValue::Type::LocalTime:
case TypedValue::Type::LocalDateTime:
case TypedValue::Type::Duration:
case TypedValue::Type::Path: {
MG_ASSERT(false, "This conversion between TypedValue and Value is not implemented yet!");
break;
}
}
return Value{};
}
std::vector<Value> ConvertToValueVectorFromTypedValueVector(std::vector<memgraph::storage::v3::TypedValue> &&vec) {
std::vector<Value> ret;
ret.reserve(vec.size());
std::transform(vec.begin(), vec.end(), std::back_inserter(ret),
[](auto &elem) { return FromTypedValueToValue(std::move(elem)); });
return ret;
}
std::vector<PropertyId> NamesToProperties(const std::vector<std::string> &property_names, DbAccessor &dba) {
std::vector<PropertyId> properties;
properties.reserve(property_names.size());
for (const auto &name : property_names) {
properties.push_back(dba.NameToProperty(name));
}
return properties;
}
std::vector<memgraph::storage::v3::LabelId> NamesToLabels(const std::vector<std::string> &label_names,
DbAccessor &dba) {
std::vector<memgraph::storage::v3::LabelId> labels;
labels.reserve(label_names.size());
for (const auto &name : label_names) {
labels.push_back(dba.NameToLabel(name));
}
return labels;
}
template <class TExpression>
auto Eval(TExpression *expr, EvaluationContext &ctx, AstStorage &storage,
memgraph::storage::v3::ExpressionEvaluator &eval, DbAccessor &dba) {
ctx.properties = NamesToProperties(storage.properties_, dba);
ctx.labels = NamesToLabels(storage.labels_, dba);
auto value = expr->Accept(eval);
return value;
}
std::any ParseExpression(const std::string &expr, memgraph::expr::AstStorage &storage) {
memgraph::frontend::opencypher::Parser<memgraph::frontend::opencypher::ParserOpTag::EXPRESSION> parser(expr);
ParsingContext pc;
CypherMainVisitor visitor(pc, &storage);
auto *ast = parser.tree();
return visitor.visit(ast);
}
TypedValue ComputeExpression(DbAccessor &dba, const std::optional<memgraph::storage::v3::VertexAccessor> &v_acc,
const std::optional<memgraph::storage::v3::EdgeAccessor> &e_acc,
const std::string &expression, std::string_view node_name, std::string_view edge_name) {
AstStorage storage;
Frame frame{1 + 1}; // 1 for the node_identifier, 1 for the edge_identifier
SymbolTable symbol_table;
EvaluationContext ctx;
ExpressionEvaluator eval{&frame, symbol_table, ctx, &dba, View::OLD};
auto expr = ParseExpression(expression, storage);
auto node_identifier = Identifier(std::string(node_name), false);
auto edge_identifier = Identifier(std::string(edge_name), false);
std::vector<Identifier *> identifiers;
identifiers.push_back(&node_identifier);
identifiers.push_back(&edge_identifier);
expr::SymbolGenerator symbol_generator(&symbol_table, identifiers);
(std::any_cast<Expression *>(expr))->Accept(symbol_generator);
if (node_identifier.symbol_pos_ != -1) {
MG_ASSERT(std::find_if(symbol_table.table().begin(), symbol_table.table().end(),
[&node_name](const std::pair<int32_t, Symbol> &position_symbol_pair) {
return position_symbol_pair.second.name() == node_name;
}) != symbol_table.table().end());
frame[symbol_table.at(node_identifier)] = *v_acc;
}
if (edge_identifier.symbol_pos_ != -1) {
MG_ASSERT(std::find_if(symbol_table.table().begin(), symbol_table.table().end(),
[&edge_name](const std::pair<int32_t, Symbol> &position_symbol_pair) {
return position_symbol_pair.second.name() == edge_name;
}) != symbol_table.table().end());
frame[symbol_table.at(edge_identifier)] = *e_acc;
}
return Eval(std::any_cast<Expression *>(expr), ctx, storage, eval, dba);
}
bool FilterOnVertex(DbAccessor &dba, const memgraph::storage::v3::VertexAccessor &v_acc,
const std::vector<std::string> &filters, const std::string_view node_name) {
return std::ranges::all_of(filters, [&node_name, &dba, &v_acc](const auto &filter_expr) {
auto res = ComputeExpression(dba, v_acc, std::nullopt, filter_expr, node_name, "");
return res.IsBool() && res.ValueBool();
});
}
std::vector<memgraph::storage::v3::TypedValue> EvaluateVertexExpressions(
DbAccessor &dba, const memgraph::storage::v3::VertexAccessor &v_acc, const std::vector<std::string> &expressions,
std::string_view node_name) {
std::vector<memgraph::storage::v3::TypedValue> evaluated_expressions;
evaluated_expressions.reserve(expressions.size());
std::transform(expressions.begin(), expressions.end(), std::back_inserter(evaluated_expressions),
[&dba, &v_acc, &node_name](const auto &expression) {
return ComputeExpression(dba, v_acc, std::nullopt, expression, node_name, "");
});
return evaluated_expressions;
}
bool DoesEdgeTypeMatch(const memgraph::msgs::ExpandOneRequest &req, const memgraph::storage::v3::EdgeAccessor &edge) {
// TODO(gvolfing) This should be checked only once and handled accordingly.
if (req.edge_types.empty()) {
@ -143,19 +347,21 @@ struct LocalError {};
std::optional<memgraph::msgs::Vertex> FillUpSourceVertex(
const std::optional<memgraph::storage::v3::VertexAccessor> &v_acc, memgraph::msgs::ExpandOneRequest &req,
memgraph::msgs::VertexId src_vertex) {
auto secondary_labels = v_acc->Labels(memgraph::storage::v3::View::OLD);
auto secondary_labels = v_acc->Labels(View::OLD);
if (secondary_labels.HasError()) {
spdlog::debug("Encountered an error while trying to get the secondary labels of a vertex. Transaction id: {}",
req.transaction_id.logical_id);
return std::nullopt;
}
auto &sec_labels = secondary_labels.GetValue();
memgraph::msgs::Vertex source_vertex;
source_vertex.id = src_vertex;
source_vertex.labels.reserve(secondary_labels.GetValue().size());
for (auto label_id : secondary_labels.GetValue()) {
source_vertex.labels.emplace_back(memgraph::msgs::Label{.id = label_id});
}
source_vertex.labels.reserve(sec_labels.size());
std::transform(sec_labels.begin(), sec_labels.end(), std::back_inserter(source_vertex.labels),
[](auto label_id) { return memgraph::msgs::Label{.id = label_id}; });
return source_vertex;
}
@ -164,7 +370,7 @@ std::optional<std::map<PropertyId, Value>> FillUpSourceVertexProperties(
std::map<PropertyId, Value> src_vertex_properties;
if (!req.src_vertex_properties) {
auto props = v_acc->Properties(memgraph::storage::v3::View::OLD);
auto props = v_acc->Properties(View::OLD);
if (props.HasError()) {
spdlog::debug("Encountered an error while trying to access vertex properties. Transaction id: {}",
req.transaction_id.logical_id);
@ -172,16 +378,18 @@ std::optional<std::map<PropertyId, Value>> FillUpSourceVertexProperties(
}
for (auto &[key, val] : props.GetValue()) {
src_vertex_properties.insert(std::make_pair(key, ToValue(val)));
src_vertex_properties.insert(std::make_pair(key, FromPropertyValueToValue(val)));
}
} else if (req.src_vertex_properties.value().empty()) {
// NOOP
} else {
for (const auto &prop : req.src_vertex_properties.value()) {
const auto &prop_val = v_acc->GetProperty(prop, memgraph::storage::v3::View::OLD);
src_vertex_properties.insert(std::make_pair(prop, ToValue(prop_val.GetValue())));
}
auto &vertex_props = req.src_vertex_properties.value();
std::transform(vertex_props.begin(), vertex_props.end(),
std::inserter(src_vertex_properties, src_vertex_properties.begin()), [&v_acc](const auto &prop) {
const auto &prop_val = v_acc->GetProperty(prop, View::OLD);
return std::make_pair(prop, FromPropertyValueToValue(prop_val.GetValue()));
});
}
return src_vertex_properties;
@ -194,7 +402,7 @@ std::optional<std::array<std::vector<memgraph::storage::v3::EdgeAccessor>, 2>> F
switch (req.direction) {
case memgraph::msgs::EdgeDirection::OUT: {
auto out_edges_result = v_acc->OutEdges(memgraph::storage::v3::View::OLD);
auto out_edges_result = v_acc->OutEdges(View::OLD);
if (out_edges_result.HasError()) {
spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}",
req.transaction_id.logical_id);
@ -204,7 +412,7 @@ std::optional<std::array<std::vector<memgraph::storage::v3::EdgeAccessor>, 2>> F
break;
}
case memgraph::msgs::EdgeDirection::IN: {
auto in_edges_result = v_acc->InEdges(memgraph::storage::v3::View::OLD);
auto in_edges_result = v_acc->InEdges(View::OLD);
if (in_edges_result.HasError()) {
spdlog::debug(
"Encountered an error while trying to get in-going EdgeAccessors. Transaction id: {}"[req.transaction_id
@ -215,7 +423,7 @@ std::optional<std::array<std::vector<memgraph::storage::v3::EdgeAccessor>, 2>> F
break;
}
case memgraph::msgs::EdgeDirection::BOTH: {
auto in_edges_result = v_acc->InEdges(memgraph::storage::v3::View::OLD);
auto in_edges_result = v_acc->InEdges(View::OLD);
if (in_edges_result.HasError()) {
spdlog::debug("Encountered an error while trying to get in-going EdgeAccessors. Transaction id: {}",
req.transaction_id.logical_id);
@ -223,7 +431,7 @@ std::optional<std::array<std::vector<memgraph::storage::v3::EdgeAccessor>, 2>> F
}
in_edges = std::move(in_edges_result.GetValue());
auto out_edges_result = v_acc->OutEdges(memgraph::storage::v3::View::OLD);
auto out_edges_result = v_acc->OutEdges(View::OLD);
if (out_edges_result.HasError()) {
spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}",
req.transaction_id.logical_id);
@ -315,7 +523,7 @@ std::optional<memgraph::msgs::ExpandOneResultRow> GetExpandOneResult(memgraph::s
if (!req.edge_properties) {
get_edge_properties = [&req](const memgraph::storage::v3::EdgeAccessor &edge) -> EdgeProperties {
std::map<PropertyId, memgraph::msgs::Value> ret;
auto property_results = edge.Properties(memgraph::storage::v3::View::OLD);
auto property_results = edge.Properties(View::OLD);
if (property_results.HasError()) {
spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}",
req.transaction_id.logical_id);
@ -323,7 +531,7 @@ std::optional<memgraph::msgs::ExpandOneResultRow> GetExpandOneResult(memgraph::s
}
for (const auto &[prop_key, prop_val] : property_results.GetValue()) {
ret.insert(std::make_pair(prop_key, ToValue(prop_val)));
ret.insert(std::make_pair(prop_key, FromPropertyValueToValue(prop_val)));
}
return ret;
};
@ -334,14 +542,14 @@ std::optional<memgraph::msgs::ExpandOneResultRow> GetExpandOneResult(memgraph::s
ret.reserve(req.edge_properties.value().size());
for (const auto &edge_prop : req.edge_properties.value()) {
// TODO(gvolfing) maybe check for the absence of certain properties
ret.emplace_back(ToValue(edge.GetProperty(edge_prop, memgraph::storage::v3::View::OLD).GetValue()));
ret.emplace_back(FromPropertyValueToValue(edge.GetProperty(edge_prop, View::OLD).GetValue()));
}
return ret;
};
}
/// Fill up source vertex
auto v_acc = acc.FindVertex(ConvertPropertyVector(std::move(src_vertex.second)), memgraph::storage::v3::View::OLD);
auto v_acc = acc.FindVertex(ConvertPropertyVector(std::move(src_vertex.second)), View::OLD);
auto source_vertex = FillUpSourceVertex(v_acc, req, src_vertex);
if (!source_vertex) {
@ -412,11 +620,7 @@ std::optional<memgraph::msgs::ExpandOneResultRow> GetExpandOneResult(memgraph::s
.edges_with_all_properties = std::move(edges_with_all_properties),
.edges_with_specific_properties = std::move(edges_with_specific_properties)};
}
} // namespace
namespace memgraph::storage::v3 {
}; // namespace
msgs::WriteResponses ShardRsm::ApplyWrite(msgs::CreateVerticesRequest &&req) {
auto acc = shard_->Access(req.transaction_id);
@ -435,9 +639,10 @@ msgs::WriteResponses ShardRsm::ApplyWrite(msgs::CreateVerticesRequest &&req) {
// TODO(gvolfing) make sure if this conversion is actually needed.
std::vector<memgraph::storage::v3::LabelId> converted_label_ids;
converted_label_ids.reserve(new_vertex.label_ids.size());
for (const auto &label_id : new_vertex.label_ids) {
converted_label_ids.emplace_back(label_id.id);
}
std::transform(new_vertex.label_ids.begin(), new_vertex.label_ids.end(), std::back_inserter(converted_label_ids),
[](const auto &label_id) { return label_id.id; });
// TODO(jbajic) sending primary key as vector breaks validation on storage side
// cannot map id -> value
PrimaryKey transformed_pk;
@ -719,6 +924,7 @@ msgs::ReadResponses ShardRsm::HandleRead(msgs::ScanVerticesRequest &&req) {
uint64_t sample_counter = 0;
const auto start_ids = ConvertPropertyVector(std::move(req.start_id.second));
auto dba = DbAccessor{&acc};
for (auto it = vertex_iterable.begin(); it != vertex_iterable.end(); ++it) {
const auto &vertex = *it;
@ -728,6 +934,27 @@ msgs::ReadResponses ShardRsm::HandleRead(msgs::ScanVerticesRequest &&req) {
}
if (did_reach_starting_point) {
std::vector<Value> expression_results;
// TODO(gvolfing) it should be enough to check these only once.
if (vertex.Properties(View(req.storage_view)).HasError()) {
action_successful = false;
spdlog::debug("Could not retrive properties from VertexAccessor. Transaction id: {}",
req.transaction_id.logical_id);
break;
}
if (!req.filter_expressions.empty()) {
// NOTE - DbAccessor might get removed in the future.
const bool eval = FilterOnVertex(dba, vertex, req.filter_expressions, expr::identifier_node_symbol);
if (!eval) {
continue;
}
}
if (!req.vertex_expressions.empty()) {
expression_results = ConvertToValueVectorFromTypedValueVector(
EvaluateVertexExpressions(dba, vertex, req.vertex_expressions, expr::identifier_node_symbol));
}
std::optional<std::map<PropertyId, Value>> found_props;
const auto *schema = shard_->GetSchema(shard_->PrimaryLabel());
@ -746,7 +973,8 @@ msgs::ReadResponses ShardRsm::HandleRead(msgs::ScanVerticesRequest &&req) {
}
results.emplace_back(msgs::ScanResultRow{.vertex = ConstructValueVertex(vertex, view).vertex_v,
.props = FromMap(found_props.value())});
.props = FromMap(found_props.value()),
.evaluated_vertex_expressions = std::move(expression_results)});
++sample_counter;
if (req.batch_limit && sample_counter == req.batch_limit) {

View File

@ -60,13 +60,12 @@ inline memgraph::storage::v3::PropertyValue ToPropertyValue(Value value) {
// These are not PropertyValues
case Value::Type::Vertex:
case Value::Type::Edge:
case Value::Type::Path:
MG_ASSERT(false, "Not PropertyValue");
}
return ret;
}
inline Value ToValue(const memgraph::storage::v3::PropertyValue &pv) {
inline Value FromPropertyValueToValue(const memgraph::storage::v3::PropertyValue &pv) {
using memgraph::storage::v3::PropertyValue;
switch (pv.type()) {
@ -80,7 +79,7 @@ inline Value ToValue(const memgraph::storage::v3::PropertyValue &pv) {
std::vector<Value> list;
list.reserve(pv.ValueList().size());
for (const auto &elem : pv.ValueList()) {
list.emplace_back(ToValue(elem));
list.emplace_back(FromPropertyValueToValue(elem));
}
return Value(list);
@ -89,7 +88,7 @@ inline Value ToValue(const memgraph::storage::v3::PropertyValue &pv) {
std::map<std::string, Value> map;
for (const auto &[key, val] : pv.ValueMap()) {
// maybe use std::make_pair once the && issue is resolved.
map.emplace(key, ToValue(val));
map.emplace(key, FromPropertyValueToValue(val));
}
return Value(map);
@ -122,7 +121,7 @@ inline std::vector<Value> ConvertValueVector(const std::vector<memgraph::storage
ret.reserve(vec.size());
for (const auto &elem : vec) {
ret.push_back(ToValue(elem));
ret.push_back(FromPropertyValueToValue(elem));
}
return ret;

View File

@ -11,6 +11,7 @@
#include "storage/v3/vertex_accessor.hpp"
#include <cstddef>
#include <memory>
#include "storage/v3/conversions.hpp"
@ -21,6 +22,7 @@
#include "storage/v3/mvcc.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/schema_validator.hpp"
#include "storage/v3/shard.hpp"
#include "storage/v3/vertex.hpp"
#include "utils/logging.hpp"
#include "utils/memory_tracker.hpp"
@ -378,6 +380,32 @@ Result<PropertyValue> VertexAccessor::GetProperty(View view, PropertyId property
return GetProperty(property, view).GetValue();
}
PropertyValue VertexAccessor::GetPropertyValue(PropertyId property, View view) const {
PropertyValue value;
const auto primary_label = PrimaryLabel(view);
if (primary_label.HasError()) {
return value;
}
const auto *schema = vertex_validator_->schema_validator->GetSchema(*primary_label);
if (!schema) {
return value;
}
// Find PropertyId index in keystore
size_t property_index{0};
for (; property_index < schema->second.size(); ++property_index) {
if (schema->second[property_index].property_id == property) {
break;
}
}
value = vertex_->keys.GetKey(property_index);
if (value.IsNull()) {
value = vertex_->properties.GetProperty(property);
}
return value;
}
Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View view) const {
bool exists = true;
bool deleted = false;
@ -385,7 +413,7 @@ Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View view
Delta *delta = nullptr;
{
deleted = vertex_->deleted;
value = vertex_->properties.GetProperty(property);
value = GetPropertyValue(property, view);
delta = vertex_->delta;
}
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted, &value, property](const Delta &delta) {

View File

@ -133,6 +133,8 @@ class VertexAccessor final {
/// @throw std::bad_alloc
Result<PropertyValue> SetProperty(PropertyId property, const PropertyValue &value);
PropertyValue GetPropertyValue(PropertyId property, View view) const;
Result<void> CheckVertexExistence(View view) const;
Vertex *vertex_;

View File

@ -78,10 +78,10 @@ uint64_t GetUniqueInteger() {
return prop_val_val++;
}
LabelId get_primary_label() { return LabelId::FromUint(0); }
constexpr LabelId get_primary_label() { return LabelId::FromUint(1); }
SchemaProperty get_schema_property() {
return {.property_id = PropertyId::FromUint(0), .type = common::SchemaType::INT};
constexpr SchemaProperty get_schema_property() {
return {.property_id = PropertyId::FromUint(2), .type = common::SchemaType::INT};
}
msgs::PrimaryKey GetPrimaryKey(int64_t value) {
@ -92,7 +92,7 @@ msgs::PrimaryKey GetPrimaryKey(int64_t value) {
msgs::NewVertex GetNewVertex(int64_t value) {
// Specify Labels.
msgs::Label label1 = {.id = LabelId::FromUint(1)};
msgs::Label label1 = {.id = LabelId::FromUint(3)};
std::vector<msgs::Label> label_ids = {label1};
// Specify primary key.
@ -100,14 +100,14 @@ msgs::NewVertex GetNewVertex(int64_t value) {
// Specify properties
auto val1 = msgs::Value(static_cast<int64_t>(value));
auto prop1 = std::make_pair(PropertyId::FromUint(1), val1);
auto prop1 = std::make_pair(PropertyId::FromUint(4), val1);
auto val3 = msgs::Value(static_cast<int64_t>(value));
auto prop3 = std::make_pair(PropertyId::FromUint(2), val3);
auto prop3 = std::make_pair(PropertyId::FromUint(5), val3);
//(VERIFY) does the schema has to be specified with the properties or the primarykey?
auto val2 = msgs::Value(static_cast<int64_t>(value));
auto prop2 = std::make_pair(PropertyId::FromUint(0), val2);
auto prop2 = std::make_pair(PropertyId::FromUint(6), val2);
std::vector<std::pair<PropertyId, msgs::Value>> properties{prop1, prop2, prop3};
@ -185,7 +185,7 @@ bool AttemptToUpdateVertex(ShardClient &client, int64_t value) {
auto vertex_id = GetValuePrimaryKeysWithValue(value)[0];
std::vector<std::pair<PropertyId, msgs::Value>> property_updates;
auto property_update = std::make_pair(PropertyId::FromUint(2), msgs::Value(static_cast<int64_t>(10000)));
auto property_update = std::make_pair(PropertyId::FromUint(5), msgs::Value(static_cast<int64_t>(10000)));
auto vertex_prop = msgs::UpdateVertexProp{};
vertex_prop.primary_key = vertex_id;
@ -362,7 +362,7 @@ std::tuple<size_t, std::optional<msgs::VertexId>> AttemptToScanAllWithoutBatchLi
msgs::VertexId start_id) {
msgs::ScanVerticesRequest scan_req{};
scan_req.batch_limit = {};
scan_req.filter_expressions = std::nullopt;
scan_req.filter_expressions.clear();
scan_req.props_to_return = std::nullopt;
scan_req.start_id = start_id;
scan_req.storage_view = msgs::StorageView::OLD;
@ -388,7 +388,7 @@ std::tuple<size_t, std::optional<msgs::VertexId>> AttemptToScanAllWithBatchLimit
uint64_t batch_limit) {
msgs::ScanVerticesRequest scan_req{};
scan_req.batch_limit = batch_limit;
scan_req.filter_expressions = std::nullopt;
scan_req.filter_expressions.clear();
scan_req.props_to_return = std::nullopt;
scan_req.start_id = start_id;
scan_req.storage_view = msgs::StorageView::OLD;
@ -409,6 +409,41 @@ std::tuple<size_t, std::optional<msgs::VertexId>> AttemptToScanAllWithBatchLimit
}
}
std::tuple<size_t, std::optional<msgs::VertexId>> AttemptToScanAllWithExpression(ShardClient &client,
msgs::VertexId start_id,
uint64_t batch_limit,
uint64_t prop_val_to_check_against) {
std::string filter_expr1 = "MG_SYMBOL_NODE.property = " + std::to_string(prop_val_to_check_against);
std::vector<std::string> filter_expressions = {filter_expr1};
std::string regular_expr1 = "2+2";
std::vector<std::string> vertex_expressions = {regular_expr1};
msgs::ScanVerticesRequest scan_req{};
scan_req.batch_limit = batch_limit;
scan_req.filter_expressions = filter_expressions;
scan_req.vertex_expressions = vertex_expressions;
scan_req.props_to_return = std::nullopt;
scan_req.start_id = start_id;
scan_req.storage_view = msgs::StorageView::NEW;
scan_req.transaction_id.logical_id = GetTransactionId();
while (true) {
auto read_res = client.SendReadRequest(scan_req);
if (read_res.HasError()) {
continue;
}
auto write_response_result = read_res.GetValue();
auto write_response = std::get<msgs::ScanVerticesResponse>(write_response_result);
MG_ASSERT(write_response.success);
MG_ASSERT(!write_response.results.empty(), "There are no results!");
MG_ASSERT(write_response.results[0].evaluated_vertex_expressions[0].int_v == 4);
return {write_response.results.size(), write_response.next_start_id};
}
}
void AttemptToExpandOneWithWrongEdgeType(ShardClient &client, uint64_t src_vertex_val, uint64_t edge_type_id) {
// Source vertex
msgs::Label label = {.id = get_primary_label()};
@ -721,6 +756,9 @@ void TestScanAllOneGo(ShardClient &client) {
msgs::VertexId v_id = {prim_label, prim_key};
auto [result_size_2, next_id_2] = AttemptToScanAllWithExpression(client, v_id, 5, unique_prop_val_2);
MG_ASSERT(result_size_2 == 1);
auto [result_size_with_batch, next_id_with_batch] = AttemptToScanAllWithBatchLimit(client, v_id, 5);
auto [result_size_without_batch, next_id_without_batch] = AttemptToScanAllWithoutBatchLimit(client, v_id);
@ -835,14 +873,15 @@ int TestMessages() {
PropertyValue max_pk(static_cast<int64_t>(10000000));
std::vector<PropertyValue> max_prim_key = {max_pk};
std::vector<SchemaProperty> schema = {get_schema_property()};
auto shard_ptr1 = std::make_unique<Shard>(get_primary_label(), min_prim_key, max_prim_key, schema);
auto shard_ptr2 = std::make_unique<Shard>(get_primary_label(), min_prim_key, max_prim_key, schema);
auto shard_ptr3 = std::make_unique<Shard>(get_primary_label(), min_prim_key, max_prim_key, schema);
std::vector<SchemaProperty> schema_prop = {get_schema_property()};
shard_ptr1->CreateSchema(get_primary_label(), schema);
shard_ptr2->CreateSchema(get_primary_label(), schema);
shard_ptr3->CreateSchema(get_primary_label(), schema);
auto shard_ptr1 = std::make_unique<Shard>(get_primary_label(), min_prim_key, max_prim_key, schema_prop);
auto shard_ptr2 = std::make_unique<Shard>(get_primary_label(), min_prim_key, max_prim_key, schema_prop);
auto shard_ptr3 = std::make_unique<Shard>(get_primary_label(), min_prim_key, max_prim_key, schema_prop);
shard_ptr1->StoreMapping({{1, "label"}, {2, "property"}, {3, "label1"}, {4, "prop2"}, {5, "prop3"}, {6, "prop4"}});
shard_ptr2->StoreMapping({{1, "label"}, {2, "property"}, {3, "label1"}, {4, "prop2"}, {5, "prop3"}, {6, "prop4"}});
shard_ptr3->StoreMapping({{1, "label"}, {2, "property"}, {3, "label1"}, {4, "prop2"}, {5, "prop3"}, {6, "prop4"}});
std::vector<Address> address_for_1{shard_server_2_address, shard_server_3_address};
std::vector<Address> address_for_2{shard_server_1_address, shard_server_3_address};

View File

@ -192,11 +192,18 @@ TEST_F(SchemaValidatorTest, TestSchemaValidateVertexCreate) {
label1, schema_prop_string));
}
{
const auto schema_violation = schema_validator.ValidateVertexCreate(label2, {}, {});
const auto schema_violation =
schema_validator.ValidateVertexCreate(label2, {}, std::vector<std::pair<PropertyId, PropertyValue>>{});
ASSERT_NE(schema_violation, std::nullopt);
EXPECT_EQ(*schema_violation, SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_HAS_NO_PRIMARY_PROPERTY,
label2, schema_prop_string));
}
{
const auto schema_violation = schema_validator.ValidateVertexCreate(label2, {}, std::vector<PropertyValue>{});
ASSERT_NE(schema_violation, std::nullopt);
EXPECT_EQ(*schema_violation,
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PRIMARY_PROPERTIES_UNDEFINED, label2));
}
// Validate wrong secondary label
{
const auto schema_violation =