Compare commits
3 Commits
property_t
...
TMG-add-sc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd9faabf2c | ||
|
|
45c7689396 | ||
|
|
24225eee32 |
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 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
|
||||
@@ -16,4 +16,6 @@
|
||||
namespace memgraph::common {
|
||||
enum class SchemaType : uint8_t { BOOL, INT, STRING, DATE, LOCALTIME, LOCALDATETIME, DURATION };
|
||||
|
||||
enum class SchemaConfigParams : uint8_t { REPLICATION_FACTOR, SPLIT_THRESHOLD };
|
||||
|
||||
} // namespace memgraph::common
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 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
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include <boost/preprocessor/cat.hpp>
|
||||
|
||||
#include "common/types.hpp"
|
||||
#include "expr/ast.hpp"
|
||||
#include "expr/ast/ast_visitor.hpp"
|
||||
#include "expr/exceptions.hpp"
|
||||
@@ -2941,6 +2942,40 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
||||
return schema_property_map;
|
||||
}
|
||||
|
||||
inline memgraph::common::SchemaConfigParams SchemaConfigKeyToEnum(std::string_view val) {
|
||||
if (val == "replication_factor") {
|
||||
return memgraph::common::SchemaConfigParams::REPLICATION_FACTOR;
|
||||
} else if (val == "split_threshold") {
|
||||
return memgraph::common::SchemaConfigParams::SPLIT_THRESHOLD;
|
||||
}
|
||||
throw memgraph::expr::SemanticException("Schema configuration parameter not recognized!");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Schema*
|
||||
*/
|
||||
antlrcpp::Any visitSchemaConfigKeyValuePair(MemgraphCypher::SchemaConfigKeyValuePairContext *ctx) override {
|
||||
MG_ASSERT(ctx->literal().size() == 2);
|
||||
const auto key_value =
|
||||
SchemaConfigKeyToEnum(utils::ToLowerCase(std::any_cast<std::string>(ctx->literal(0)->accept(this))));
|
||||
// TODO(jbajic) Introduce variable values here
|
||||
const auto value = std::any_cast<int64_t>(ctx->literal(1)->accept(this));
|
||||
return std::pair<common::SchemaConfigParams, int64_t>{key_value, value};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Schema*
|
||||
*/
|
||||
antlrcpp::Any visitSchemaConfiguration(MemgraphCypher::SchemaConfigurationContext *ctx) override {
|
||||
std::unordered_map<common::SchemaConfigParams, int64_t> map;
|
||||
for (auto *key_value_pair : ctx->schemaConfigKeyValuePair()) {
|
||||
// If the queries are cached, then only the stripped query is parsed, so the actual keys cannot be determined
|
||||
// here. That means duplicates cannot be checked.
|
||||
map.insert(std::any_cast<std::pair<common::SchemaConfigParams, int64_t>>(key_value_pair->accept(this)));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Schema*
|
||||
*/
|
||||
@@ -2981,7 +3016,11 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
||||
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;
|
||||
if (ctx->schemaConfiguration()) {
|
||||
schema_query->schema_config_map_ = std::any_cast<std::unordered_map<common::SchemaConfigParams, int64_t>>(
|
||||
ctx->schemaConfiguration()->accept(this));
|
||||
query_ = schema_query;
|
||||
}
|
||||
return schema_query;
|
||||
}
|
||||
|
||||
|
||||
@@ -394,6 +394,10 @@ propertyKeyTypePair : propertyKeyName propertyType ;
|
||||
|
||||
schemaPropertyMap : '(' propertyKeyTypePair ( ',' propertyKeyTypePair )* ')' ;
|
||||
|
||||
createSchema : CREATE SCHEMA ON ':' labelName schemaPropertyMap ;
|
||||
schemaConfigKeyValuePair : literal '=' literal ;
|
||||
|
||||
schemaConfiguration : ( schemaConfigKeyValuePair ( ',' schemaConfigKeyValuePair )* )? ;
|
||||
|
||||
createSchema : CREATE SCHEMA ON ':' labelName schemaPropertyMap ( CONFIG schemaConfiguration ) ? ;
|
||||
|
||||
dropSchema : DROP SCHEMA ON ':' labelName ;
|
||||
|
||||
@@ -147,6 +147,14 @@ cpp<#
|
||||
}
|
||||
cpp<#)
|
||||
|
||||
(defun clone-map (source dest)
|
||||
#>cpp
|
||||
${dest}.reserve(${source}.size());
|
||||
for (const auto &[config_key, config_val]: ${source}) {
|
||||
${dest}.emplace(config_key, config_val);
|
||||
}
|
||||
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
|
||||
@@ -2698,10 +2706,15 @@ cpp<#
|
||||
#>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)
|
||||
|
||||
(schema_config_map "std::unordered_map<common::SchemaConfigParams, int64_t>"
|
||||
:clone #'clone-map
|
||||
:scope :public))
|
||||
|
||||
(:public
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "common/types.hpp"
|
||||
#include "coordinator/coordinator_client.hpp"
|
||||
#include "expr/ast/ast_visitor.hpp"
|
||||
#include "io/local_transport/local_system.hpp"
|
||||
@@ -542,7 +543,7 @@ Callback HandleSchemaQuery(SchemaQuery *schema_query, InterpreterContext *interp
|
||||
std::vector<std::vector<TypedValue>> results;
|
||||
results.reserve(schemas_info.schemas.size());
|
||||
|
||||
for (const auto &[label_id, schema_types] : schemas_info.schemas) {
|
||||
for ([[maybe_unused]] const auto &[label_id, schema_types, config] : schemas_info.schemas) {
|
||||
std::vector<TypedValue> schema_info_row;
|
||||
schema_info_row.reserve(3);
|
||||
|
||||
@@ -570,7 +571,7 @@ Callback HandleSchemaQuery(SchemaQuery *schema_query, InterpreterContext *interp
|
||||
const auto *schema = db->GetSchema(label);
|
||||
std::vector<std::vector<TypedValue>> results;
|
||||
if (schema) {
|
||||
for (const auto &schema_property : schema->second) {
|
||||
for (const auto &schema_property : schema->properties) {
|
||||
std::vector<TypedValue> schema_info_row;
|
||||
schema_info_row.reserve(2);
|
||||
schema_info_row.emplace_back(db->PropertyToName(schema_property.property_id));
|
||||
@@ -589,7 +590,8 @@ Callback HandleSchemaQuery(SchemaQuery *schema_query, InterpreterContext *interp
|
||||
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)]() {
|
||||
schema_type_map = std::move(schema_type_map),
|
||||
config_map = std::move(schema_query->schema_config_map_)]() {
|
||||
auto *db = interpreter_context->db;
|
||||
const auto label = interpreter_context->NameToLabelId(primary_label.name);
|
||||
std::vector<storage::v3::SchemaProperty> schemas_types;
|
||||
@@ -598,8 +600,30 @@ Callback HandleSchemaQuery(SchemaQuery *schema_query, InterpreterContext *interp
|
||||
auto property_id = interpreter_context->NameToPropertyId(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));
|
||||
auto schema_config = std::invoke([&config_map]() -> std::optional<storage::v3::Schema::SchemaConfiguration> {
|
||||
if (!config_map.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
storage::v3::Schema::SchemaConfiguration config;
|
||||
if (const auto replication_factor_it = config_map.find(common::SchemaConfigParams::REPLICATION_FACTOR);
|
||||
replication_factor_it != config_map.end()) {
|
||||
config.replication_factor = replication_factor_it->second;
|
||||
}
|
||||
if (const auto split_threshold_it = config_map.find(common::SchemaConfigParams::SPLIT_THRESHOLD);
|
||||
split_threshold_it != config_map.end()) {
|
||||
config.split_threshold = split_threshold_it->second;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
// TODO FIx this late
|
||||
if (schema_config) {
|
||||
if (!db->CreateSchema(label, schemas_types, *schema_config)) {
|
||||
throw QueryException(fmt::format("Schema on label :{} already exists!", primary_label.name));
|
||||
}
|
||||
} else {
|
||||
if (!db->CreateSchema(label, schemas_types)) {
|
||||
throw QueryException(fmt::format("Schema on label :{} already exists!", primary_label.name));
|
||||
}
|
||||
}
|
||||
return std::vector<std::vector<TypedValue>>{};
|
||||
};
|
||||
|
||||
@@ -146,6 +146,14 @@ cpp<#
|
||||
}
|
||||
cpp<#)
|
||||
|
||||
(defun clone-map (source dest)
|
||||
#>cpp
|
||||
${dest}.reserve(${source}.size());
|
||||
for (const auto &[config_key, config_val]: ${source}) {
|
||||
${dest}.emplace(config_key, config_val);
|
||||
}
|
||||
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
|
||||
@@ -2705,10 +2713,15 @@ cpp<#
|
||||
#>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)
|
||||
|
||||
(schema_config_map "std::unordered_map<common::SchemaConfigParams, int64_t>"
|
||||
:clone #'clone-map
|
||||
:scope :public))
|
||||
|
||||
(:public
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 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
|
||||
@@ -44,7 +44,7 @@ struct VertexIdCmpr {
|
||||
};
|
||||
|
||||
std::optional<std::map<PropertyId, Value>> PrimaryKeysFromAccessor(const VertexAccessor &acc, View view,
|
||||
const Schemas::Schema &schema) {
|
||||
const Schema &schema) {
|
||||
std::map<PropertyId, Value> ret;
|
||||
auto props = acc.Properties(view);
|
||||
auto maybe_pk = acc.PrimaryKey(view);
|
||||
@@ -53,9 +53,9 @@ std::optional<std::map<PropertyId, Value>> PrimaryKeysFromAccessor(const VertexA
|
||||
return std::nullopt;
|
||||
}
|
||||
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, FromPropertyValueToValue(std::move(pk[i])));
|
||||
MG_ASSERT(schema.properties.size() == pk.size(), "PrimaryKey size does not match schema!");
|
||||
for (size_t i{0}; i < schema.properties.size(); ++i) {
|
||||
ret.emplace(schema.properties[i].property_id, FromPropertyValueToValue(std::move(pk[i])));
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -82,8 +82,7 @@ ShardResult<std::vector<msgs::Label>> FillUpSourceVertexSecondaryLabels(const st
|
||||
|
||||
ShardResult<std::map<PropertyId, Value>> FillUpSourceVertexProperties(const std::optional<VertexAccessor> &v_acc,
|
||||
const msgs::ExpandOneRequest &req,
|
||||
storage::v3::View view,
|
||||
const Schemas::Schema &schema) {
|
||||
storage::v3::View view, const Schema &schema) {
|
||||
std::map<PropertyId, Value> src_vertex_properties;
|
||||
|
||||
if (!req.src_vertex_properties) {
|
||||
@@ -236,7 +235,7 @@ std::vector<TypedValue> EvaluateEdgeExpressions(DbAccessor &dba, const VertexAcc
|
||||
}
|
||||
|
||||
ShardResult<std::map<PropertyId, Value>> CollectAllPropertiesFromAccessor(const VertexAccessor &acc, View view,
|
||||
const Schemas::Schema &schema) {
|
||||
const Schema &schema) {
|
||||
auto ret = impl::CollectAllPropertiesImpl<VertexAccessor>(acc, view);
|
||||
if (ret.HasError()) {
|
||||
return ret.GetError();
|
||||
@@ -380,7 +379,7 @@ bool FilterOnEdge(DbAccessor &dba, const storage::v3::VertexAccessor &v_acc, con
|
||||
ShardResult<msgs::ExpandOneResultRow> GetExpandOneResult(
|
||||
Shard::Accessor &acc, msgs::VertexId src_vertex, const msgs::ExpandOneRequest &req,
|
||||
const EdgeUniquenessFunction &maybe_filter_based_on_edge_uniqueness, const EdgeFiller &edge_filler,
|
||||
const Schemas::Schema &schema) {
|
||||
const Schema &schema) {
|
||||
/// Fill up source vertex
|
||||
const auto primary_key = ConvertPropertyVector(src_vertex.second);
|
||||
auto v_acc = acc.FindVertex(primary_key, View::NEW);
|
||||
@@ -423,7 +422,7 @@ ShardResult<msgs::ExpandOneResultRow> GetExpandOneResult(
|
||||
VertexAccessor v_acc, msgs::VertexId src_vertex, const msgs::ExpandOneRequest &req,
|
||||
std::vector<EdgeAccessor> in_edge_accessors, std::vector<EdgeAccessor> out_edge_accessors,
|
||||
const EdgeUniquenessFunction &maybe_filter_based_on_edge_uniqueness, const EdgeFiller &edge_filler,
|
||||
const Schemas::Schema &schema) {
|
||||
const Schema &schema) {
|
||||
/// Fill up source vertex
|
||||
msgs::Vertex source_vertex = {.id = src_vertex};
|
||||
auto maybe_secondary_labels = FillUpSourceVertexSecondaryLabels(v_acc, req);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 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
|
||||
@@ -217,7 +217,7 @@ ShardResult<std::map<PropertyId, Value>> CollectSpecificPropertiesFromAccessor(c
|
||||
}
|
||||
|
||||
ShardResult<std::map<PropertyId, Value>> CollectAllPropertiesFromAccessor(const VertexAccessor &acc, View view,
|
||||
const Schemas::Schema &schema);
|
||||
const Schema &schema);
|
||||
namespace impl {
|
||||
template <PropertiesAccessor TAccessor>
|
||||
ShardResult<std::map<PropertyId, Value>> CollectAllPropertiesImpl(const TAccessor &acc, View view) {
|
||||
@@ -249,11 +249,11 @@ EdgeFiller InitializeEdgeFillerFunction(const msgs::ExpandOneRequest &req);
|
||||
ShardResult<msgs::ExpandOneResultRow> GetExpandOneResult(
|
||||
Shard::Accessor &acc, msgs::VertexId src_vertex, const msgs::ExpandOneRequest &req,
|
||||
const EdgeUniquenessFunction &maybe_filter_based_on_edge_uniqueness, const EdgeFiller &edge_filler,
|
||||
const Schemas::Schema &schema);
|
||||
const Schema &schema);
|
||||
|
||||
ShardResult<msgs::ExpandOneResultRow> GetExpandOneResult(
|
||||
VertexAccessor v_acc, msgs::VertexId src_vertex, const msgs::ExpandOneRequest &req,
|
||||
std::vector<EdgeAccessor> in_edge_accessors, std::vector<EdgeAccessor> out_edge_accessors,
|
||||
const EdgeUniquenessFunction &maybe_filter_based_on_edge_uniqueness, const EdgeFiller &edge_filler,
|
||||
const Schemas::Schema &schema);
|
||||
const Schema &schema);
|
||||
} // namespace memgraph::storage::v3
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 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
|
||||
@@ -44,20 +44,20 @@ ShardResult<void> SchemaValidator::ValidateVertexCreate(LabelId primary_label, c
|
||||
}
|
||||
|
||||
// Quick size check
|
||||
if (schema->second.size() != primary_properties.size()) {
|
||||
if (schema->properties.size() != primary_properties.size()) {
|
||||
return SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED,
|
||||
"Not all primary properties have been specified for :{} vertex",
|
||||
name_id_mapper_->IdToName(primary_label.AsInt()));
|
||||
}
|
||||
// Check only properties defined by schema
|
||||
for (size_t i{0}; i < schema->second.size(); ++i) {
|
||||
for (size_t i{0}; i < schema->properties.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) {
|
||||
property_schema_type && *property_schema_type != schema->properties[i].type) {
|
||||
return SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE,
|
||||
"Property {} is of wrong type, expected {}, actual {}",
|
||||
name_id_mapper_->IdToName(schema->second[i].property_id.AsInt()),
|
||||
SchemaTypeToString(schema->second[i].type), SchemaTypeToString(*property_schema_type));
|
||||
name_id_mapper_->IdToName(schema->properties[i].property_id.AsInt()),
|
||||
SchemaTypeToString(schema->properties[i].type), SchemaTypeToString(*property_schema_type));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,9 +72,9 @@ ShardResult<void> SchemaValidator::ValidatePropertyUpdate(const LabelId primary_
|
||||
|
||||
// Verify that updating property is not part of schema
|
||||
if (const auto schema_property = std::ranges::find_if(
|
||||
schema->second,
|
||||
schema->properties,
|
||||
[property_id](const auto &schema_property) { return property_id == schema_property.property_id; });
|
||||
schema_property != schema->second.end()) {
|
||||
schema_property != schema->properties.end()) {
|
||||
return SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY,
|
||||
"Cannot update primary property {} of schema on label :{}",
|
||||
name_id_mapper_->IdToName(schema_property->property_id.AsInt()),
|
||||
@@ -92,7 +92,7 @@ ShardResult<void> SchemaValidator::ValidateLabelUpdate(const LabelId label) cons
|
||||
return {};
|
||||
}
|
||||
|
||||
const Schemas::Schema *SchemaValidator::GetSchema(LabelId label) const { return schemas_->GetSchema(label); }
|
||||
const 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} {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 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
|
||||
@@ -33,7 +33,7 @@ class SchemaValidator {
|
||||
|
||||
[[nodiscard]] ShardResult<void> ValidateLabelUpdate(LabelId label) const;
|
||||
|
||||
const Schemas::Schema *GetSchema(LabelId label) const;
|
||||
const Schema *GetSchema(LabelId label) const;
|
||||
|
||||
private:
|
||||
Schemas *schemas_;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 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
|
||||
@@ -25,25 +25,26 @@ bool operator==(const SchemaProperty &lhs, const SchemaProperty &rhs) {
|
||||
}
|
||||
|
||||
Schemas::SchemasList Schemas::ListSchemas() const {
|
||||
Schemas::SchemasList ret;
|
||||
SchemasList ret;
|
||||
ret.reserve(schemas_.size());
|
||||
std::transform(schemas_.begin(), schemas_.end(), std::back_inserter(ret),
|
||||
[](const auto &schema_property_type) { return schema_property_type; });
|
||||
[](const auto &schema_property_type) { return schema_property_type.second; });
|
||||
return ret;
|
||||
}
|
||||
|
||||
const Schemas::Schema *Schemas::GetSchema(const LabelId primary_label) const {
|
||||
const Schema *Schemas::GetSchema(const LabelId primary_label) const {
|
||||
if (auto schema_map = schemas_.find(primary_label); schema_map != schemas_.end()) {
|
||||
return &*schema_map;
|
||||
return &schema_map->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Schemas::CreateSchema(const LabelId primary_label, const std::vector<SchemaProperty> &schemas_types) {
|
||||
bool Schemas::CreateSchema(const LabelId primary_label, const std::vector<SchemaProperty> &schemas_types,
|
||||
Schema::SchemaConfiguration config) {
|
||||
if (schemas_.contains(primary_label)) {
|
||||
return false;
|
||||
}
|
||||
schemas_.emplace(primary_label, schemas_types);
|
||||
schemas_.insert({primary_label, Schema{.label = primary_label, .properties = schemas_types, .config = config}});
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -51,9 +52,9 @@ bool Schemas::DropSchema(const LabelId primary_label) { return schemas_.erase(pr
|
||||
|
||||
bool Schemas::IsPropertyKey(const LabelId primary_label, const PropertyId property_id) const {
|
||||
if (const auto schema = schemas_.find(primary_label); schema != schemas_.end()) {
|
||||
return std::ranges::find_if(schema->second, [property_id](const auto &elem) {
|
||||
return std::ranges::find_if(schema->second.properties, [property_id](const auto &elem) {
|
||||
return elem.property_id == property_id;
|
||||
}) != schema->second.end();
|
||||
}) != schema->second.properties.end();
|
||||
}
|
||||
throw utils::BasicException("Schema not found!");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 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
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
@@ -43,12 +44,24 @@ struct SchemaProperty {
|
||||
}
|
||||
};
|
||||
|
||||
struct Schema {
|
||||
LabelId label;
|
||||
std::vector<SchemaProperty> properties;
|
||||
struct SchemaConfiguration {
|
||||
uint64_t replication_factor;
|
||||
uint64_t split_threshold;
|
||||
} config;
|
||||
|
||||
friend bool operator==(const Schema &lhs, const Schema &rhs) noexcept {
|
||||
return lhs.label == rhs.label && lhs.properties == rhs.properties;
|
||||
}
|
||||
};
|
||||
|
||||
/// Structure that represents a collection of schemas
|
||||
/// Schema can be mapped under only one label => primary label
|
||||
class Schemas {
|
||||
public:
|
||||
using SchemasMap = std::unordered_map<LabelId, std::vector<SchemaProperty>>;
|
||||
using Schema = SchemasMap::value_type;
|
||||
using SchemasMap = std::unordered_map<LabelId, Schema>;
|
||||
using SchemasList = std::vector<Schema>;
|
||||
|
||||
Schemas() = default;
|
||||
@@ -64,7 +77,8 @@ class Schemas {
|
||||
|
||||
// Returns true if it was successfully created or false if the schema
|
||||
// already exists
|
||||
[[nodiscard]] bool CreateSchema(LabelId primary_label, const std::vector<SchemaProperty> &schemas_types);
|
||||
[[nodiscard]] bool CreateSchema(LabelId primary_label, const std::vector<SchemaProperty> &schemas_types,
|
||||
Schema::SchemaConfiguration config = {});
|
||||
|
||||
// Returns true if it was successfully dropped or false if the schema
|
||||
// does not exist
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#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"
|
||||
@@ -403,8 +404,6 @@ ShardResult<VertexAccessor> Shard::Accessor::CreateVertexAndValidate(
|
||||
const std::vector<LabelId> &labels, const PrimaryKey &primary_properties,
|
||||
const std::vector<std::pair<PropertyId, PropertyValue>> &properties) {
|
||||
OOMExceptionEnabler oom_exception;
|
||||
const auto schema = shard_->GetSchema(shard_->primary_label_)->second;
|
||||
|
||||
auto maybe_schema_violation =
|
||||
GetSchemaValidator().ValidateVertexCreate(shard_->primary_label_, labels, primary_properties);
|
||||
if (maybe_schema_violation.HasError()) {
|
||||
@@ -907,8 +906,8 @@ bool Shard::CreateIndex(LabelId label, const std::optional<uint64_t> /*desired_c
|
||||
bool Shard::CreateIndex(LabelId label, PropertyId property,
|
||||
const std::optional<uint64_t> /*desired_commit_timestamp*/) {
|
||||
// TODO(jbajic) response should be different when index conflicts with schema
|
||||
if (label == primary_label_ && schemas_.GetSchema(primary_label_)->second.size() == 1 &&
|
||||
schemas_.GetSchema(primary_label_)->second[0].property_id == property) {
|
||||
if (label == primary_label_ && schemas_.GetSchema(primary_label_)->properties.size() == 1 &&
|
||||
schemas_.GetSchema(primary_label_)->properties[0].property_id == property) {
|
||||
// Index already exists on primary key
|
||||
return false;
|
||||
}
|
||||
@@ -934,10 +933,11 @@ const SchemaValidator &Shard::Accessor::GetSchemaValidator() const { return shar
|
||||
|
||||
SchemasInfo Shard::ListAllSchemas() const { return {schemas_.ListSchemas()}; }
|
||||
|
||||
const Schemas::Schema *Shard::GetSchema(const LabelId primary_label) const { return schemas_.GetSchema(primary_label); }
|
||||
const Schema *Shard::GetSchema(const LabelId primary_label) const { return schemas_.GetSchema(primary_label); }
|
||||
|
||||
bool Shard::CreateSchema(const LabelId primary_label, const std::vector<SchemaProperty> &schemas_types) {
|
||||
return schemas_.CreateSchema(primary_label, schemas_types);
|
||||
bool Shard::CreateSchema(const LabelId primary_label, const std::vector<SchemaProperty> &schemas_types,
|
||||
Schema::SchemaConfiguration config) {
|
||||
return schemas_.CreateSchema(primary_label, schemas_types, config);
|
||||
}
|
||||
|
||||
bool Shard::DropSchema(const LabelId primary_label) { return schemas_.DropSchema(primary_label); }
|
||||
|
||||
@@ -364,9 +364,10 @@ class Shard final {
|
||||
|
||||
SchemasInfo ListAllSchemas() const;
|
||||
|
||||
const Schemas::Schema *GetSchema(LabelId primary_label) const;
|
||||
const Schema *GetSchema(LabelId primary_label) const;
|
||||
|
||||
bool CreateSchema(LabelId primary_label, const std::vector<SchemaProperty> &schemas_types);
|
||||
bool CreateSchema(LabelId primary_label, const std::vector<SchemaProperty> &schemas_types,
|
||||
Schema::SchemaConfiguration = {});
|
||||
|
||||
bool DropSchema(LabelId primary_label);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 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
|
||||
@@ -396,8 +396,8 @@ PropertyValue VertexAccessor::GetPropertyValue(PropertyId property, View view) c
|
||||
return value;
|
||||
}
|
||||
// Find PropertyId index in keystore
|
||||
for (size_t property_index{0}; property_index < schema->second.size(); ++property_index) {
|
||||
if (schema->second[property_index].property_id == property) {
|
||||
for (size_t property_index{0}; property_index < schema->properties.size(); ++property_index) {
|
||||
if (schema->properties[property_index].property_id == property) {
|
||||
return vertex_->first[property_index];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2023 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
|
||||
@@ -60,18 +60,22 @@ TEST_F(SchemaTest, TestSchemaCreate) {
|
||||
EXPECT_TRUE(schemas.CreateSchema(label2, {schema_prop_string, schema_prop_int}));
|
||||
const auto current_schemas = schemas.ListSchemas();
|
||||
EXPECT_EQ(current_schemas.size(), 2);
|
||||
EXPECT_THAT(current_schemas,
|
||||
UnorderedElementsAre(Pair(label1, std::vector<SchemaProperty>{schema_prop_string}),
|
||||
Pair(label2, std::vector<SchemaProperty>{schema_prop_string, schema_prop_int})));
|
||||
EXPECT_THAT(
|
||||
current_schemas,
|
||||
UnorderedElementsAre(
|
||||
Schema{.label = label1, .properties = std::vector<SchemaProperty>{schema_prop_string}},
|
||||
Schema{.label = label2, .properties = std::vector<SchemaProperty>{schema_prop_string, schema_prop_int}}));
|
||||
}
|
||||
{
|
||||
// Assert after unsuccessful creation, number oif schemas remains the same
|
||||
EXPECT_FALSE(schemas.CreateSchema(label2, {schema_prop_int}));
|
||||
const auto current_schemas = schemas.ListSchemas();
|
||||
EXPECT_EQ(current_schemas.size(), 2);
|
||||
EXPECT_THAT(current_schemas,
|
||||
UnorderedElementsAre(Pair(label1, std::vector<SchemaProperty>{schema_prop_string}),
|
||||
Pair(label2, std::vector<SchemaProperty>{schema_prop_string, schema_prop_int})));
|
||||
EXPECT_THAT(
|
||||
current_schemas,
|
||||
UnorderedElementsAre(
|
||||
Schema{.label = label1, .properties = std::vector<SchemaProperty>{schema_prop_string}},
|
||||
Schema{.label = label2, .properties = std::vector<SchemaProperty>{schema_prop_string, schema_prop_int}}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,27 +93,29 @@ TEST_F(SchemaTest, TestSchemaList) {
|
||||
{
|
||||
const auto current_schemas = schemas.ListSchemas();
|
||||
EXPECT_EQ(current_schemas.size(), 2);
|
||||
EXPECT_THAT(current_schemas,
|
||||
UnorderedElementsAre(
|
||||
Pair(label1, std::vector<SchemaProperty>{schema_prop_string}),
|
||||
Pair(label2, std::vector<SchemaProperty>{{NameToProperty("prop1"), SchemaType::STRING},
|
||||
EXPECT_THAT(
|
||||
current_schemas,
|
||||
UnorderedElementsAre(
|
||||
Schema{.label = label1, .properties = std::vector<SchemaProperty>{schema_prop_string}},
|
||||
Schema{.label = label2,
|
||||
.properties = std::vector<SchemaProperty>{{NameToProperty("prop}"), SchemaType::STRING},
|
||||
{NameToProperty("prop2"), SchemaType::INT},
|
||||
{NameToProperty("prop3"), SchemaType::BOOL},
|
||||
{NameToProperty("prop4"), SchemaType::DATE},
|
||||
{NameToProperty("prop5"), SchemaType::LOCALDATETIME},
|
||||
{NameToProperty("prop6"), SchemaType::DURATION},
|
||||
{NameToProperty("prop7"), SchemaType::LOCALTIME}})));
|
||||
{NameToProperty("prop7"), SchemaType::LOCALTIME}}}));
|
||||
}
|
||||
{
|
||||
const auto *const schema1 = schemas.GetSchema(label1);
|
||||
ASSERT_NE(schema1, nullptr);
|
||||
EXPECT_EQ(*schema1, (Schemas::Schema{label1, std::vector<SchemaProperty>{schema_prop_string}}));
|
||||
EXPECT_EQ(*schema1, (Schema{.label = label1, .properties = std::vector<SchemaProperty>{schema_prop_string}}));
|
||||
}
|
||||
{
|
||||
const auto *const schema2 = schemas.GetSchema(label2);
|
||||
ASSERT_NE(schema2, nullptr);
|
||||
EXPECT_EQ(schema2->first, label2);
|
||||
EXPECT_EQ(schema2->second.size(), 7);
|
||||
EXPECT_EQ(schema2->label, label2);
|
||||
EXPECT_EQ(schema2->properties.size(), 7);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +138,8 @@ TEST_F(SchemaTest, TestSchemaDrop) {
|
||||
const auto current_schemas = schemas.ListSchemas();
|
||||
EXPECT_EQ(current_schemas.size(), 1);
|
||||
EXPECT_THAT(current_schemas,
|
||||
UnorderedElementsAre(Pair(label2, std::vector<SchemaProperty>{schema_prop_string, schema_prop_int})));
|
||||
UnorderedElementsAre(Schema{
|
||||
.label = label2, .properties = std::vector<SchemaProperty>{schema_prop_string, schema_prop_int}}));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -141,7 +148,8 @@ TEST_F(SchemaTest, TestSchemaDrop) {
|
||||
const auto current_schemas = schemas.ListSchemas();
|
||||
EXPECT_EQ(current_schemas.size(), 1);
|
||||
EXPECT_THAT(current_schemas,
|
||||
UnorderedElementsAre(Pair(label2, std::vector<SchemaProperty>{schema_prop_string, schema_prop_int})));
|
||||
UnorderedElementsAre(Schema{
|
||||
.label = label2, .properties = std::vector<SchemaProperty>{schema_prop_string, schema_prop_int}}));
|
||||
}
|
||||
|
||||
EXPECT_TRUE(schemas.DropSchema(label2));
|
||||
|
||||
Reference in New Issue
Block a user