Remove existence constraint
Summary: Removing existence constraint Reviewers: ipaljak, mferencevic, vkasljevic Reviewed By: mferencevic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2043
This commit is contained in:
parent
d37460105a
commit
d70792f1ce
@ -69,7 +69,6 @@ set(mg_single_node_sources
|
|||||||
storage/common/types/property_value_store.cpp
|
storage/common/types/property_value_store.cpp
|
||||||
storage/single_node/record_accessor.cpp
|
storage/single_node/record_accessor.cpp
|
||||||
storage/single_node/vertex_accessor.cpp
|
storage/single_node/vertex_accessor.cpp
|
||||||
storage/single_node/constraints/existence_constraints.cpp
|
|
||||||
storage/single_node/constraints/record.cpp
|
storage/single_node/constraints/record.cpp
|
||||||
storage/single_node/constraints/unique_label_properties_constraint.cpp
|
storage/single_node/constraints/unique_label_properties_constraint.cpp
|
||||||
storage/single_node/constraints/unique_label_property_constraint.cpp
|
storage/single_node/constraints/unique_label_property_constraint.cpp
|
||||||
|
@ -40,8 +40,6 @@ GraphDb::GraphDb(Config config) : config_(config) {
|
|||||||
durability::RecoverWal(config_.durability_directory, this, &recovery_data,
|
durability::RecoverWal(config_.durability_directory, this, &recovery_data,
|
||||||
&recovery_transactions);
|
&recovery_transactions);
|
||||||
durability::RecoverIndexes(this, recovery_data.indexes);
|
durability::RecoverIndexes(this, recovery_data.indexes);
|
||||||
durability::RecoverExistenceConstraints(
|
|
||||||
this, recovery_data.existence_constraints);
|
|
||||||
durability::RecoverUniqueConstraints(
|
durability::RecoverUniqueConstraints(
|
||||||
this, recovery_data.unique_constraints);
|
this, recovery_data.unique_constraints);
|
||||||
}
|
}
|
||||||
|
@ -291,11 +291,6 @@ void GraphDbAccessor::UpdateOnAddLabel(storage::Label label,
|
|||||||
"Node couldn't be updated due to index constraint violation!");
|
"Node couldn't be updated due to index constraint violation!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!db_->storage().existence_constraints_.CheckOnAddLabel(vertex, label)) {
|
|
||||||
throw IndexConstraintViolationException(
|
|
||||||
"Node couldn't be updated due to existence constraint violation!");
|
|
||||||
}
|
|
||||||
|
|
||||||
db_->storage().labels_index_.Update(label, vlist_ptr, vertex);
|
db_->storage().labels_index_.Update(label, vlist_ptr, vertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,71 +320,6 @@ void GraphDbAccessor::UpdateOnRemoveProperty(
|
|||||||
const Vertex *vertex) {
|
const Vertex *vertex) {
|
||||||
db_->storage().unique_label_property_constraints_.UpdateOnRemoveProperty(
|
db_->storage().unique_label_property_constraints_.UpdateOnRemoveProperty(
|
||||||
property, accessor, transaction());
|
property, accessor, transaction());
|
||||||
|
|
||||||
if (!db_->storage().existence_constraints_.CheckOnRemoveProperty(vertex,
|
|
||||||
property)) {
|
|
||||||
throw IndexConstraintViolationException(
|
|
||||||
"Node couldn't be updated due to existence constraint violation!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphDbAccessor::BuildExistenceConstraint(
|
|
||||||
storage::Label label, const std::vector<storage::Property> &properties) {
|
|
||||||
auto dba = db_->AccessBlocking(std::make_optional(transaction().id_));
|
|
||||||
storage::constraints::ExistenceRule rule{label, properties};
|
|
||||||
|
|
||||||
for (auto v : dba.Vertices(false)) {
|
|
||||||
if (!CheckIfSatisfiesExistenceRule(v.GetOld(), rule)) {
|
|
||||||
throw IndexConstraintViolationException(
|
|
||||||
"Existence constraint couldn't be built because existing data is "
|
|
||||||
"violating it!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!db_->storage().existence_constraints_.AddConstraint(rule)) {
|
|
||||||
// Already exists
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> property_names(properties.size());
|
|
||||||
std::transform(properties.begin(), properties.end(), property_names.begin(),
|
|
||||||
[&dba](auto p) { return dba.PropertyName(p); });
|
|
||||||
|
|
||||||
dba.wal().Emplace(database::StateDelta::BuildExistenceConstraint(
|
|
||||||
dba.transaction().id_, label, dba.LabelName(label), properties,
|
|
||||||
property_names));
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphDbAccessor::DeleteExistenceConstraint(
|
|
||||||
storage::Label label, const std::vector<storage::Property> &properties) {
|
|
||||||
auto dba = db_->AccessBlocking(std::make_optional(transaction().id_));
|
|
||||||
storage::constraints::ExistenceRule rule{label, properties};
|
|
||||||
if (!db_->storage().existence_constraints_.RemoveConstraint(rule)) {
|
|
||||||
// Nothing was deleted
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> property_names(properties.size());
|
|
||||||
std::transform(properties.begin(), properties.end(), property_names.begin(),
|
|
||||||
[&dba](auto p) { return dba.PropertyName(p); });
|
|
||||||
|
|
||||||
dba.wal().Emplace(database::StateDelta::DropExistenceConstraint(
|
|
||||||
dba.transaction().id_, label, dba.LabelName(label), properties,
|
|
||||||
property_names));
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GraphDbAccessor::ExistenceConstraintExists(
|
|
||||||
storage::Label label,
|
|
||||||
const std::vector<storage::Property> &properties) const {
|
|
||||||
storage::constraints::ExistenceRule rule{label, properties};
|
|
||||||
return db_->storage().existence_constraints_.Exists(rule);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<storage::constraints::ExistenceRule>
|
|
||||||
GraphDbAccessor::ListExistenceConstraints() const {
|
|
||||||
return db_->storage().existence_constraints_.ListConstraints();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t GraphDbAccessor::VerticesCount() const {
|
int64_t GraphDbAccessor::VerticesCount() const {
|
||||||
|
@ -472,31 +472,6 @@ class GraphDbAccessor {
|
|||||||
return db_->storage().label_property_index_.Keys();
|
return db_->storage().label_property_index_.Keys();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates new existence constraint.
|
|
||||||
*/
|
|
||||||
void BuildExistenceConstraint(
|
|
||||||
storage::Label label, const std::vector<storage::Property> &properties);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes existing existence constraint.
|
|
||||||
*/
|
|
||||||
void DeleteExistenceConstraint(
|
|
||||||
storage::Label label, const std::vector<storage::Property> &properties);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether constraint exists.
|
|
||||||
*/
|
|
||||||
bool ExistenceConstraintExists(
|
|
||||||
storage::Label label,
|
|
||||||
const std::vector<storage::Property> &properties) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the list of existence constraints currently active.
|
|
||||||
*/
|
|
||||||
std::vector<storage::constraints::ExistenceRule> ListExistenceConstraints()
|
|
||||||
const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return approximate number of all vertices in the database.
|
* Return approximate number of all vertices in the database.
|
||||||
* Note that this is always an over-estimate and never an under-estimate.
|
* Note that this is always an over-estimate and never an under-estimate.
|
||||||
|
@ -150,31 +150,6 @@ bool RecoverSnapshot(const fs::path &snapshot_file, database::GraphDb *db,
|
|||||||
/*create = */ true, unique.ValueBool()});
|
/*create = */ true, unique.ValueBool()});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read a list of existence constraints
|
|
||||||
RETURN_IF_NOT(decoder.ReadValue(&dv, Value::Type::List));
|
|
||||||
auto existence_constraints = dv.ValueList();
|
|
||||||
for (auto it = existence_constraints.begin();
|
|
||||||
it != existence_constraints.end();) {
|
|
||||||
RETURN_IF_NOT(it->IsString());
|
|
||||||
auto label = it->ValueString();
|
|
||||||
++it;
|
|
||||||
RETURN_IF_NOT(it != existence_constraints.end());
|
|
||||||
RETURN_IF_NOT(it->IsInt());
|
|
||||||
auto prop_size = it->ValueInt();
|
|
||||||
++it;
|
|
||||||
std::vector<std::string> properties;
|
|
||||||
properties.reserve(prop_size);
|
|
||||||
for (size_t i = 0; i < prop_size; ++i) {
|
|
||||||
RETURN_IF_NOT(it != existence_constraints.end());
|
|
||||||
RETURN_IF_NOT(it->IsString());
|
|
||||||
properties.emplace_back(it->ValueString());
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
|
|
||||||
recovery_data->existence_constraints.emplace_back(
|
|
||||||
ExistenceConstraintRecoveryData{label, std::move(properties), true});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read a list of unique constraints
|
// Read a list of unique constraints
|
||||||
RETURN_IF_NOT(decoder.ReadValue(&dv, Value::Type::List));
|
RETURN_IF_NOT(decoder.ReadValue(&dv, Value::Type::List));
|
||||||
auto unique_constraints = dv.ValueList();
|
auto unique_constraints = dv.ValueList();
|
||||||
@ -499,48 +474,6 @@ void RecoverWal(const fs::path &durability_dir, database::GraphDb *db,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case database::StateDelta::Type::BUILD_EXISTENCE_CONSTRAINT: {
|
|
||||||
auto drop_it = std::find_if(
|
|
||||||
recovery_data->existence_constraints.begin(),
|
|
||||||
recovery_data->existence_constraints.end(),
|
|
||||||
[&delta](const ExistenceConstraintRecoveryData &data) {
|
|
||||||
return data.label == delta.label_name &&
|
|
||||||
std::is_permutation(data.properties.begin(),
|
|
||||||
data.properties.end(),
|
|
||||||
delta.property_names.begin()) &&
|
|
||||||
data.create == false;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (drop_it != recovery_data->existence_constraints.end()) {
|
|
||||||
recovery_data->existence_constraints.erase(drop_it);
|
|
||||||
} else {
|
|
||||||
recovery_data->existence_constraints.emplace_back(
|
|
||||||
ExistenceConstraintRecoveryData{delta.label_name,
|
|
||||||
delta.property_names, true});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case database::StateDelta::Type::DROP_EXISTENCE_CONSTRAINT: {
|
|
||||||
auto build_it = std::find_if(
|
|
||||||
recovery_data->existence_constraints.begin(),
|
|
||||||
recovery_data->existence_constraints.end(),
|
|
||||||
[&delta](const ExistenceConstraintRecoveryData &data) {
|
|
||||||
return data.label == delta.label_name &&
|
|
||||||
std::is_permutation(data.properties.begin(),
|
|
||||||
data.properties.end(),
|
|
||||||
delta.property_names.begin()) &&
|
|
||||||
data.create == true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (build_it != recovery_data->existence_constraints.end()) {
|
|
||||||
recovery_data->existence_constraints.erase(build_it);
|
|
||||||
} else {
|
|
||||||
recovery_data->existence_constraints.emplace_back(
|
|
||||||
ExistenceConstraintRecoveryData{delta.label_name,
|
|
||||||
delta.property_names, false});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case database::StateDelta::Type::BUILD_UNIQUE_CONSTRAINT: {
|
case database::StateDelta::Type::BUILD_UNIQUE_CONSTRAINT: {
|
||||||
auto drop_it = std::find_if(
|
auto drop_it = std::find_if(
|
||||||
recovery_data->unique_constraints.begin(),
|
recovery_data->unique_constraints.begin(),
|
||||||
@ -612,26 +545,6 @@ void RecoverIndexes(database::GraphDb *db,
|
|||||||
dba.Commit();
|
dba.Commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecoverExistenceConstraints(
|
|
||||||
database::GraphDb *db,
|
|
||||||
const std::vector<ExistenceConstraintRecoveryData> &constraints) {
|
|
||||||
auto dba = db->Access();
|
|
||||||
for (auto &constraint : constraints) {
|
|
||||||
auto label = dba.Label(constraint.label);
|
|
||||||
std::vector<storage::Property> properties;
|
|
||||||
properties.reserve(constraint.properties.size());
|
|
||||||
for (auto &prop : constraint.properties) {
|
|
||||||
properties.push_back(dba.Property(prop));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (constraint.create) {
|
|
||||||
dba.BuildExistenceConstraint(label, properties);
|
|
||||||
} else {
|
|
||||||
dba.DeleteExistenceConstraint(label, properties);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RecoverUniqueConstraints(
|
void RecoverUniqueConstraints(
|
||||||
database::GraphDb *db,
|
database::GraphDb *db,
|
||||||
const std::vector<UniqueConstraintRecoveryData> &constraints) {
|
const std::vector<UniqueConstraintRecoveryData> &constraints) {
|
||||||
|
@ -43,13 +43,6 @@ struct IndexRecoveryData {
|
|||||||
bool unique; // used only when creating an index
|
bool unique; // used only when creating an index
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExistenceConstraintRecoveryData {
|
|
||||||
std::string label;
|
|
||||||
std::vector<std::string> properties;
|
|
||||||
// distinguish between creating and dropping existence constraint
|
|
||||||
bool create;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UniqueConstraintRecoveryData {
|
struct UniqueConstraintRecoveryData {
|
||||||
std::string label;
|
std::string label;
|
||||||
std::vector<std::string> properties;
|
std::vector<std::string> properties;
|
||||||
@ -67,14 +60,12 @@ struct RecoveryData {
|
|||||||
// can be rebuilt at the end of the recovery transaction.
|
// can be rebuilt at the end of the recovery transaction.
|
||||||
std::vector<IndexRecoveryData> indexes;
|
std::vector<IndexRecoveryData> indexes;
|
||||||
|
|
||||||
std::vector<ExistenceConstraintRecoveryData> existence_constraints;
|
|
||||||
std::vector<UniqueConstraintRecoveryData> unique_constraints;
|
std::vector<UniqueConstraintRecoveryData> unique_constraints;
|
||||||
|
|
||||||
void Clear() {
|
void Clear() {
|
||||||
snapshooter_tx_id = 0;
|
snapshooter_tx_id = 0;
|
||||||
snapshooter_tx_snapshot.clear();
|
snapshooter_tx_snapshot.clear();
|
||||||
indexes.clear();
|
indexes.clear();
|
||||||
existence_constraints.clear();
|
|
||||||
unique_constraints.clear();
|
unique_constraints.clear();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -165,10 +156,6 @@ void RecoverWal(const std::filesystem::path &durability_dir,
|
|||||||
void RecoverIndexes(database::GraphDb *db,
|
void RecoverIndexes(database::GraphDb *db,
|
||||||
const std::vector<IndexRecoveryData> &indexes);
|
const std::vector<IndexRecoveryData> &indexes);
|
||||||
|
|
||||||
void RecoverExistenceConstraints(
|
|
||||||
database::GraphDb *db,
|
|
||||||
const std::vector<ExistenceConstraintRecoveryData> &constraints);
|
|
||||||
|
|
||||||
void RecoverUniqueConstraints(
|
void RecoverUniqueConstraints(
|
||||||
database::GraphDb *db,
|
database::GraphDb *db,
|
||||||
const std::vector<UniqueConstraintRecoveryData> &constraints);
|
const std::vector<UniqueConstraintRecoveryData> &constraints);
|
||||||
|
@ -17,7 +17,7 @@ namespace fs = std::filesystem;
|
|||||||
namespace durability {
|
namespace durability {
|
||||||
|
|
||||||
// Snapshot layout is described in durability/version.hpp
|
// Snapshot layout is described in durability/version.hpp
|
||||||
static_assert(durability::kVersion == 9,
|
static_assert(durability::kVersion == 10,
|
||||||
"Wrong snapshot version, please update!");
|
"Wrong snapshot version, please update!");
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -55,20 +55,6 @@ bool Encode(const fs::path &snapshot_file, database::GraphDb &db,
|
|||||||
encoder.WriteList(index_vec);
|
encoder.WriteList(index_vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write existence constraints to snapshoot
|
|
||||||
{
|
|
||||||
std::vector<communication::bolt::Value> existence_constraints;
|
|
||||||
for (const auto &rule : dba.ListExistenceConstraints()) {
|
|
||||||
existence_constraints.emplace_back(dba.LabelName(rule.label));
|
|
||||||
existence_constraints.emplace_back(
|
|
||||||
static_cast<int64_t>(rule.properties.size()));
|
|
||||||
for (auto &prop : rule.properties) {
|
|
||||||
existence_constraints.emplace_back(dba.PropertyName(prop));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
encoder.WriteList(existence_constraints);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write unique constraints to snapshoot
|
// Write unique constraints to snapshoot
|
||||||
{
|
{
|
||||||
std::vector<communication::bolt::Value> unique_constraints;
|
std::vector<communication::bolt::Value> unique_constraints;
|
||||||
|
@ -126,32 +126,6 @@ StateDelta StateDelta::DropIndex(tx::TransactionId tx_id, storage::Label label,
|
|||||||
return op;
|
return op;
|
||||||
}
|
}
|
||||||
|
|
||||||
StateDelta StateDelta::BuildExistenceConstraint(
|
|
||||||
tx::TransactionId tx_id, storage::Label label,
|
|
||||||
const std::string &label_name,
|
|
||||||
const std::vector<storage::Property> &properties,
|
|
||||||
const std::vector<std::string> &property_names) {
|
|
||||||
StateDelta op(StateDelta::Type::BUILD_EXISTENCE_CONSTRAINT, tx_id);
|
|
||||||
op.label = label;
|
|
||||||
op.label_name = label_name;
|
|
||||||
op.properties = properties;
|
|
||||||
op.property_names = property_names;
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
StateDelta StateDelta::DropExistenceConstraint(
|
|
||||||
tx::TransactionId tx_id, storage::Label label,
|
|
||||||
const std::string &label_name,
|
|
||||||
const std::vector<storage::Property> &properties,
|
|
||||||
const std::vector<std::string> &property_names) {
|
|
||||||
StateDelta op(StateDelta::Type::DROP_EXISTENCE_CONSTRAINT, tx_id);
|
|
||||||
op.label = label;
|
|
||||||
op.label_name = label_name;
|
|
||||||
op.properties = properties;
|
|
||||||
op.property_names = property_names;
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
StateDelta StateDelta::BuildUniqueConstraint(
|
StateDelta StateDelta::BuildUniqueConstraint(
|
||||||
tx::TransactionId tx_id, storage::Label label,
|
tx::TransactionId tx_id, storage::Label label,
|
||||||
const std::string &label_name,
|
const std::string &label_name,
|
||||||
@ -236,7 +210,6 @@ void StateDelta::Encode(
|
|||||||
encoder.WriteInt(property.Id());
|
encoder.WriteInt(property.Id());
|
||||||
encoder.WriteString(property_name);
|
encoder.WriteString(property_name);
|
||||||
break;
|
break;
|
||||||
case Type::BUILD_EXISTENCE_CONSTRAINT:
|
|
||||||
case Type::BUILD_UNIQUE_CONSTRAINT:
|
case Type::BUILD_UNIQUE_CONSTRAINT:
|
||||||
encoder.WriteInt(label.Id());
|
encoder.WriteInt(label.Id());
|
||||||
encoder.WriteString(label_name);
|
encoder.WriteString(label_name);
|
||||||
@ -248,7 +221,6 @@ void StateDelta::Encode(
|
|||||||
encoder.WriteString(name);
|
encoder.WriteString(name);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Type::DROP_EXISTENCE_CONSTRAINT:
|
|
||||||
case Type::DROP_UNIQUE_CONSTRAINT:
|
case Type::DROP_UNIQUE_CONSTRAINT:
|
||||||
encoder.WriteInt(label.Id());
|
encoder.WriteInt(label.Id());
|
||||||
encoder.WriteString(label_name);
|
encoder.WriteString(label_name);
|
||||||
@ -341,7 +313,6 @@ std::optional<StateDelta> StateDelta::Decode(
|
|||||||
DECODE_MEMBER_CAST(property, ValueInt, storage::Property)
|
DECODE_MEMBER_CAST(property, ValueInt, storage::Property)
|
||||||
DECODE_MEMBER(property_name, ValueString)
|
DECODE_MEMBER(property_name, ValueString)
|
||||||
break;
|
break;
|
||||||
case Type::BUILD_EXISTENCE_CONSTRAINT:
|
|
||||||
case Type::BUILD_UNIQUE_CONSTRAINT: {
|
case Type::BUILD_UNIQUE_CONSTRAINT: {
|
||||||
DECODE_MEMBER_CAST(label, ValueInt, storage::Label)
|
DECODE_MEMBER_CAST(label, ValueInt, storage::Label)
|
||||||
DECODE_MEMBER(label_name, ValueString)
|
DECODE_MEMBER(label_name, ValueString)
|
||||||
@ -358,7 +329,6 @@ std::optional<StateDelta> StateDelta::Decode(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::DROP_EXISTENCE_CONSTRAINT:
|
|
||||||
case Type::DROP_UNIQUE_CONSTRAINT: {
|
case Type::DROP_UNIQUE_CONSTRAINT: {
|
||||||
DECODE_MEMBER_CAST(label, ValueInt, storage::Label)
|
DECODE_MEMBER_CAST(label, ValueInt, storage::Label)
|
||||||
DECODE_MEMBER(label_name, ValueString)
|
DECODE_MEMBER(label_name, ValueString)
|
||||||
@ -444,24 +414,6 @@ void StateDelta::Apply(GraphDbAccessor &dba) const {
|
|||||||
LOG(FATAL) << "Index handling not handled in Apply";
|
LOG(FATAL) << "Index handling not handled in Apply";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::BUILD_EXISTENCE_CONSTRAINT: {
|
|
||||||
std::vector<storage::Property> properties;
|
|
||||||
properties.reserve(property_names.size());
|
|
||||||
for (auto &p : property_names) {
|
|
||||||
properties.push_back(dba.Property(p));
|
|
||||||
}
|
|
||||||
|
|
||||||
dba.BuildExistenceConstraint(dba.Label(label_name), properties);
|
|
||||||
} break;
|
|
||||||
case Type::DROP_EXISTENCE_CONSTRAINT: {
|
|
||||||
std::vector<storage::Property> properties;
|
|
||||||
properties.reserve(property_names.size());
|
|
||||||
for (auto &p : property_names) {
|
|
||||||
properties.push_back(dba.Property(p));
|
|
||||||
}
|
|
||||||
|
|
||||||
dba.DeleteExistenceConstraint(dba.Label(label_name), properties);
|
|
||||||
} break;
|
|
||||||
case Type::BUILD_UNIQUE_CONSTRAINT: {
|
case Type::BUILD_UNIQUE_CONSTRAINT: {
|
||||||
std::vector<storage::Property> properties;
|
std::vector<storage::Property> properties;
|
||||||
properties.reserve(property_names.size());
|
properties.reserve(property_names.size());
|
||||||
|
@ -73,8 +73,6 @@ in StateDeltas.")
|
|||||||
remove-edge ;; edge_id
|
remove-edge ;; edge_id
|
||||||
build-index ;; label, label_name, property, property_name, unique
|
build-index ;; label, label_name, property, property_name, unique
|
||||||
drop-index ;; label, label_name, property, property_name
|
drop-index ;; label, label_name, property, property_name
|
||||||
build-existence-constraint ;; label, label_name, properties, property_names
|
|
||||||
drop-existence-constraint ;; label, label_name, properties, property_names
|
|
||||||
build-unique_constraint ;; label, label_name, properties, property_names
|
build-unique_constraint ;; label, label_name, properties, property_names
|
||||||
drop-unique_constraint ;; label, label_name, properties, property_names
|
drop-unique_constraint ;; label, label_name, properties, property_names
|
||||||
)
|
)
|
||||||
@ -135,16 +133,6 @@ omitted in the comment."))
|
|||||||
const std::string &label_name,
|
const std::string &label_name,
|
||||||
storage::Property property,
|
storage::Property property,
|
||||||
const std::string &property_name);
|
const std::string &property_name);
|
||||||
static StateDelta BuildExistenceConstraint(
|
|
||||||
tx::TransactionId tx_id, storage::Label label,
|
|
||||||
const std::string &label_name,
|
|
||||||
const std::vector<storage::Property> &properties,
|
|
||||||
const std::vector<std::string> &property_names);
|
|
||||||
static StateDelta DropExistenceConstraint(
|
|
||||||
tx::TransactionId tx_id, storage::Label label,
|
|
||||||
const std::string &label_name,
|
|
||||||
const std::vector<storage::Property> &property,
|
|
||||||
const std::vector<std::string> &property_names);
|
|
||||||
static StateDelta BuildUniqueConstraint(
|
static StateDelta BuildUniqueConstraint(
|
||||||
tx::TransactionId tx_id, storage::Label label,
|
tx::TransactionId tx_id, storage::Label label,
|
||||||
const std::string &label_name,
|
const std::string &label_name,
|
||||||
|
@ -15,9 +15,9 @@ constexpr std::array<uint8_t, 4> kSnapshotMagic{{'M', 'G', 's', 'n'}};
|
|||||||
constexpr std::array<uint8_t, 4> kWalMagic{{'M', 'G', 'w', 'l'}};
|
constexpr std::array<uint8_t, 4> kWalMagic{{'M', 'G', 'w', 'l'}};
|
||||||
|
|
||||||
// The current default version of snapshot and WAL encoding / decoding.
|
// The current default version of snapshot and WAL encoding / decoding.
|
||||||
constexpr int64_t kVersion{9};
|
constexpr int64_t kVersion{10};
|
||||||
|
|
||||||
// Snapshot format (version 8):
|
// Snapshot format (version 10):
|
||||||
// 1) Magic number + snapshot version
|
// 1) Magic number + snapshot version
|
||||||
//
|
//
|
||||||
// The following two entries are required when recovering from snapshot combined
|
// The following two entries are required when recovering from snapshot combined
|
||||||
@ -27,17 +27,15 @@ constexpr int64_t kVersion{9};
|
|||||||
//
|
//
|
||||||
// 4) A list of label+property indices.
|
// 4) A list of label+property indices.
|
||||||
//
|
//
|
||||||
// 5) A list of existence constraints
|
// 5) A list of unique constraints
|
||||||
//
|
//
|
||||||
// 6) A list of unique constraints
|
// 6) Bolt encoded nodes. Each node is written in the following format:
|
||||||
//
|
|
||||||
// 7) Bolt encoded nodes. Each node is written in the following format:
|
|
||||||
// * gid, labels, properties
|
// * gid, labels, properties
|
||||||
// 8) Bolt encoded edges. Each edge is written in the following format:
|
// 7) Bolt encoded edges. Each edge is written in the following format:
|
||||||
// * gid
|
// * gid
|
||||||
// * from, to
|
// * from, to
|
||||||
// * edge_type
|
// * edge_type
|
||||||
// * properties
|
// * properties
|
||||||
//
|
//
|
||||||
// 9) Snapshot summary (number of nodes, number of edges, hash)
|
// 8) Snapshot summary (number of nodes, number of edges, hash)
|
||||||
} // namespace durability
|
} // namespace durability
|
||||||
|
@ -144,8 +144,6 @@ bool WriteAheadLog::IsStateDeltaTransactionEnd(
|
|||||||
case database::StateDelta::Type::REMOVE_EDGE:
|
case database::StateDelta::Type::REMOVE_EDGE:
|
||||||
case database::StateDelta::Type::BUILD_INDEX:
|
case database::StateDelta::Type::BUILD_INDEX:
|
||||||
case database::StateDelta::Type::DROP_INDEX:
|
case database::StateDelta::Type::DROP_INDEX:
|
||||||
case database::StateDelta::Type::BUILD_EXISTENCE_CONSTRAINT:
|
|
||||||
case database::StateDelta::Type::DROP_EXISTENCE_CONSTRAINT:
|
|
||||||
case database::StateDelta::Type::BUILD_UNIQUE_CONSTRAINT:
|
case database::StateDelta::Type::BUILD_UNIQUE_CONSTRAINT:
|
||||||
case database::StateDelta::Type::DROP_UNIQUE_CONSTRAINT:
|
case database::StateDelta::Type::DROP_UNIQUE_CONSTRAINT:
|
||||||
return false;
|
return false;
|
||||||
|
@ -654,36 +654,36 @@ Callback HandleInfoQuery(InfoQuery *info_query,
|
|||||||
|
|
||||||
Callback HandleConstraintQuery(ConstraintQuery *constraint_query,
|
Callback HandleConstraintQuery(ConstraintQuery *constraint_query,
|
||||||
database::GraphDbAccessor *db_accessor) {
|
database::GraphDbAccessor *db_accessor) {
|
||||||
Callback callback;
|
#ifdef MG_SINGLE_NODE
|
||||||
std::vector<std::string> property_names;
|
Callback callback;
|
||||||
property_names.reserve(constraint_query->constraint_.properties.size());
|
switch (constraint_query->action_type_) {
|
||||||
for (const auto &prop_ix : constraint_query->constraint_.properties) {
|
case ConstraintQuery::ActionType::CREATE: {
|
||||||
property_names.push_back(prop_ix.name);
|
switch (constraint_query->constraint_.type) {
|
||||||
}
|
case Constraint::Type::NODE_KEY:
|
||||||
std::string label_name = constraint_query->constraint_.label.name;
|
throw utils::NotYetImplemented("Node key constraints");
|
||||||
std::string type;
|
case Constraint::Type::EXISTS:
|
||||||
switch (constraint_query->constraint_.type) {
|
throw utils::NotYetImplemented("Existence constraints");
|
||||||
case Constraint::Type::EXISTS:
|
case Constraint::Type::UNIQUE:
|
||||||
type = "exists";
|
throw utils::NotYetImplemented("Unique constraints");
|
||||||
break;
|
}
|
||||||
case Constraint::Type::UNIQUE:
|
break;
|
||||||
type = "unique";
|
}
|
||||||
break;
|
case ConstraintQuery::ActionType::DROP: {
|
||||||
case Constraint::Type::NODE_KEY:
|
switch (constraint_query->constraint_.type) {
|
||||||
type = "node key";
|
case Constraint::Type::NODE_KEY:
|
||||||
break;
|
throw utils::NotYetImplemented("Node key constraints");
|
||||||
}
|
case Constraint::Type::EXISTS:
|
||||||
switch (constraint_query->action_type_) {
|
throw utils::NotYetImplemented("Existence constraints");
|
||||||
case ConstraintQuery::ActionType::CREATE:
|
case Constraint::Type::UNIQUE:
|
||||||
throw utils::NotYetImplemented("create constraint :{}({}) {}", label_name,
|
throw utils::NotYetImplemented("Unique constraints");
|
||||||
utils::Join(property_names, ", "), type);
|
}
|
||||||
break;
|
break;
|
||||||
case ConstraintQuery::ActionType::DROP:
|
}
|
||||||
throw utils::NotYetImplemented("drop constraint :{}({}) {}", label_name,
|
}
|
||||||
utils::Join(property_names, ", "), type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return callback;
|
return callback;
|
||||||
|
#else
|
||||||
|
throw utils::NotYetImplemented("Constraints");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Interpreter::Interpreter() : is_tsc_available_(utils::CheckAvailableTSC()) {}
|
Interpreter::Interpreter() : is_tsc_available_(utils::CheckAvailableTSC()) {}
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
/// @file
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "storage/common/types/property_value.hpp"
|
|
||||||
#include "storage/common/types/types.hpp"
|
|
||||||
#include "storage/single_node/vertex.hpp"
|
|
||||||
#include "transactions/type.hpp"
|
|
||||||
|
|
||||||
namespace storage::constraints {
|
|
||||||
|
|
||||||
/// Existence rule defines label -> set of properties rule. This means that
|
|
||||||
/// every vertex with that label has to have every property in the set of
|
|
||||||
/// properties. This rule doesn't care about the PropertyValues for those
|
|
||||||
/// properties.
|
|
||||||
struct ExistenceRule {
|
|
||||||
storage::Label label;
|
|
||||||
std::vector<storage::Property> properties;
|
|
||||||
|
|
||||||
bool operator==(const ExistenceRule &rule) const {
|
|
||||||
return label == rule.label &&
|
|
||||||
std::is_permutation(properties.begin(), properties.end(),
|
|
||||||
rule.properties.begin());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const ExistenceRule &rule) const { return !(*this == rule); }
|
|
||||||
};
|
|
||||||
|
|
||||||
bool CheckIfSatisfiesExistenceRule(const Vertex *vertex,
|
|
||||||
const ExistenceRule &rule);
|
|
||||||
|
|
||||||
/// ExistenceConstraints contains all active constrains. Existence constraints
|
|
||||||
/// are restriction on the vertices and are defined by ExistenceRule.
|
|
||||||
/// To create and delete constraint, the caller must ensure that there are no
|
|
||||||
/// other transactions running in parallel.
|
|
||||||
/// Additionally, for adding constraint caller must check existing vertices for
|
|
||||||
/// constraint violations before adding that constraint. You may use
|
|
||||||
/// CheckIfSatisfiesExistenceRule function for that.
|
|
||||||
/// This is needed to ensure logical correctness of transactions.
|
|
||||||
/// Once created, the client uses method CheckIfSatisfies to check that updated
|
|
||||||
/// vertex doesn't violate any of the existing constraints. If it does, that
|
|
||||||
/// update must be reverted.
|
|
||||||
class ExistenceConstraints {
|
|
||||||
public:
|
|
||||||
ExistenceConstraints() = default;
|
|
||||||
ExistenceConstraints(const ExistenceConstraints &) = delete;
|
|
||||||
ExistenceConstraints(ExistenceConstraints &&) = delete;
|
|
||||||
ExistenceConstraints &operator=(const ExistenceConstraints &) = delete;
|
|
||||||
ExistenceConstraints &operator=(ExistenceConstraints &&) = delete;
|
|
||||||
|
|
||||||
/// Adds new constraint, if the constraint already exists this method does
|
|
||||||
/// nothing. This method doesn't check if any of the existing vertices breaks
|
|
||||||
/// this constraint. Caller must do that instead. Caller must also ensure
|
|
||||||
/// that no other transaction is running in parallel.
|
|
||||||
///
|
|
||||||
/// @returns true if constraint was added, false otherwise
|
|
||||||
bool AddConstraint(const ExistenceRule &rule);
|
|
||||||
|
|
||||||
/// Removes existing constraint, if the constraint doesn't exist this method
|
|
||||||
/// does nothing. Caller must ensure that no other transaction is running in
|
|
||||||
/// parallel.
|
|
||||||
///
|
|
||||||
/// @returns true if constraint was removed, false otherwise
|
|
||||||
bool RemoveConstraint(const ExistenceRule &rule);
|
|
||||||
|
|
||||||
/// Checks whether given constraint is visible.
|
|
||||||
bool Exists(const ExistenceRule &rule) const;
|
|
||||||
|
|
||||||
/// Check if add label update satisfies all visible constraints.
|
|
||||||
/// @return true if all constraints are satisfied, false otherwise
|
|
||||||
bool CheckOnAddLabel(const Vertex *vertex, storage::Label label) const;
|
|
||||||
|
|
||||||
/// Check if remove property update satisfies all visible constraints.
|
|
||||||
/// @return true if all constraints are satisfied, false otherwise
|
|
||||||
bool CheckOnRemoveProperty(const Vertex *vertex,
|
|
||||||
storage::Property property) const;
|
|
||||||
|
|
||||||
/// Returns list of all constraints.
|
|
||||||
const std::vector<ExistenceRule> &ListConstraints() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<ExistenceRule> constraints_;
|
|
||||||
};
|
|
||||||
}; // namespace storage::constraints
|
|
@ -6,7 +6,6 @@
|
|||||||
#include "data_structures/concurrent/concurrent_map.hpp"
|
#include "data_structures/concurrent/concurrent_map.hpp"
|
||||||
#include "storage/common/kvstore/kvstore.hpp"
|
#include "storage/common/kvstore/kvstore.hpp"
|
||||||
#include "storage/common/types/types.hpp"
|
#include "storage/common/types/types.hpp"
|
||||||
#include "storage/single_node/constraints/existence_constraints.hpp"
|
|
||||||
#include "storage/single_node/constraints/unique_label_property_constraint.hpp"
|
#include "storage/single_node/constraints/unique_label_property_constraint.hpp"
|
||||||
#include "storage/single_node/edge.hpp"
|
#include "storage/single_node/edge.hpp"
|
||||||
#include "storage/single_node/indexes/key_index.hpp"
|
#include "storage/single_node/indexes/key_index.hpp"
|
||||||
@ -77,9 +76,6 @@ class Storage {
|
|||||||
KeyIndex<storage::Label, Vertex> labels_index_;
|
KeyIndex<storage::Label, Vertex> labels_index_;
|
||||||
LabelPropertyIndex label_property_index_;
|
LabelPropertyIndex label_property_index_;
|
||||||
|
|
||||||
// existence constraints
|
|
||||||
storage::constraints::ExistenceConstraints existence_constraints_;
|
|
||||||
|
|
||||||
// unique constraints
|
// unique constraints
|
||||||
storage::constraints::UniqueLabelPropertyConstraint
|
storage::constraints::UniqueLabelPropertyConstraint
|
||||||
unique_label_property_constraints_;
|
unique_label_property_constraints_;
|
||||||
|
@ -18,9 +18,6 @@ function(add_benchmark test_cpp)
|
|||||||
add_dependencies(memgraph__benchmark ${target_name})
|
add_dependencies(memgraph__benchmark ${target_name})
|
||||||
endfunction(add_benchmark)
|
endfunction(add_benchmark)
|
||||||
|
|
||||||
add_benchmark(existence_constraint.cpp)
|
|
||||||
target_link_libraries(${test_prefix}existence_constraint mg-single-node kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_benchmark(data_structures/concurrent/map_mix_concurrent.cpp)
|
add_benchmark(data_structures/concurrent/map_mix_concurrent.cpp)
|
||||||
target_link_libraries(${test_prefix}map_mix_concurrent mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}map_mix_concurrent mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
|
@ -1,133 +0,0 @@
|
|||||||
#include <benchmark/benchmark.h>
|
|
||||||
#include <benchmark/benchmark_api.h>
|
|
||||||
#include <glog/logging.h>
|
|
||||||
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
#include "database/single_node/graph_db.hpp"
|
|
||||||
#include "database/single_node/graph_db_accessor.hpp"
|
|
||||||
|
|
||||||
const constexpr size_t kNumOfConstraints = 10;
|
|
||||||
const constexpr size_t kNumOfPropsInConstraint = 5;
|
|
||||||
|
|
||||||
const constexpr size_t kNumOfProps = 100;
|
|
||||||
const constexpr size_t kNumOfLabels = 10;
|
|
||||||
|
|
||||||
const constexpr size_t kNumOfVertices = 10000;
|
|
||||||
|
|
||||||
struct TestSet {
|
|
||||||
public:
|
|
||||||
TestSet() {
|
|
||||||
constraint_labels.reserve(kNumOfConstraints);
|
|
||||||
for (size_t i = 0; i < kNumOfConstraints; ++i) {
|
|
||||||
constraint_labels.emplace_back("clabel_" + std::to_string(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
constraint_props.reserve(kNumOfConstraints * kNumOfPropsInConstraint);
|
|
||||||
for (size_t i = 0; i < kNumOfConstraints * kNumOfPropsInConstraint; ++i) {
|
|
||||||
constraint_props.emplace_back("cprop_" + std::to_string(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
labels.reserve(kNumOfLabels);
|
|
||||||
for (size_t i = 0; i < kNumOfLabels; ++i) {
|
|
||||||
labels.emplace_back("label_" + std::to_string(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
props.reserve(kNumOfProps);
|
|
||||||
for (size_t i = 0; i < kNumOfProps; ++i) {
|
|
||||||
props.emplace_back("prop_" + std::to_string(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> constraint_labels;
|
|
||||||
std::vector<std::string> constraint_props;
|
|
||||||
|
|
||||||
std::vector<std::string> labels;
|
|
||||||
std::vector<std::string> props;
|
|
||||||
|
|
||||||
long short_prop_value{0};
|
|
||||||
std::string long_prop_value{20, ' '};
|
|
||||||
};
|
|
||||||
|
|
||||||
void Run(benchmark::State &state, bool enable_constraint) {
|
|
||||||
while (state.KeepRunning()) {
|
|
||||||
database::GraphDb db;
|
|
||||||
TestSet test_set;
|
|
||||||
|
|
||||||
// CreateExistence constraints
|
|
||||||
if (enable_constraint) {
|
|
||||||
state.PauseTiming();
|
|
||||||
for (size_t i = 0; i < kNumOfConstraints; ++i) {
|
|
||||||
auto dba = db.Access();
|
|
||||||
auto label = dba.Label(test_set.constraint_labels.at(i));
|
|
||||||
std::vector<storage::Property> props;
|
|
||||||
props.reserve(kNumOfPropsInConstraint);
|
|
||||||
for (size_t j = 0; j < kNumOfPropsInConstraint; ++j) {
|
|
||||||
props.push_back(dba.Property(
|
|
||||||
test_set.constraint_props.at(kNumOfPropsInConstraint * i + j)));
|
|
||||||
}
|
|
||||||
dba.BuildExistenceConstraint(label, props);
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
state.ResumeTiming();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add all verices and also add labels and properties
|
|
||||||
std::vector<gid::Gid> vertices;
|
|
||||||
vertices.reserve(kNumOfVertices);
|
|
||||||
for (size_t k = 0; k < kNumOfVertices; ++k) {
|
|
||||||
auto dba = db.Access();
|
|
||||||
auto v = dba.InsertVertex();
|
|
||||||
vertices.push_back(v.gid());
|
|
||||||
|
|
||||||
// Labels and properties that define constraints
|
|
||||||
for (size_t i = 0; i < kNumOfConstraints; ++i) {
|
|
||||||
for (size_t j = 0; j < kNumOfPropsInConstraint; ++j) {
|
|
||||||
v.PropsSet(dba.Property(test_set.constraint_props.at(
|
|
||||||
kNumOfPropsInConstraint * i + j)),
|
|
||||||
test_set.short_prop_value);
|
|
||||||
}
|
|
||||||
auto label = dba.Label(test_set.constraint_labels.at(i));
|
|
||||||
v.add_label(label);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add other labels
|
|
||||||
for (auto label : test_set.labels) {
|
|
||||||
v.add_label(dba.Label(label));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add other properties
|
|
||||||
for (auto prop : test_set.props) {
|
|
||||||
v.PropsSet(dba.Property(prop), test_set.short_prop_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete all properties and labels
|
|
||||||
for (auto gid : vertices) {
|
|
||||||
auto dba = db.Access();
|
|
||||||
auto v = dba.FindVertex(gid, false);
|
|
||||||
std::vector<storage::Label> labels_to_del(v.labels());
|
|
||||||
for (auto &label : labels_to_del) {
|
|
||||||
v.remove_label(label);
|
|
||||||
}
|
|
||||||
|
|
||||||
v.PropsClear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete all vertices
|
|
||||||
for (auto gid : vertices) {
|
|
||||||
auto dba = db.Access();
|
|
||||||
auto v = dba.FindVertex(gid, false);
|
|
||||||
dba.RemoveVertex(v);
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BENCHMARK_CAPTURE(Run, WithoutConstraint, false)->Unit(benchmark::kMillisecond);
|
|
||||||
|
|
||||||
BENCHMARK_CAPTURE(Run, WithConstraint, true)->Unit(benchmark::kMillisecond);
|
|
||||||
|
|
||||||
BENCHMARK_MAIN();
|
|
@ -20,7 +20,7 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
// This makes sure we update the explorer when we bump the snapshot version.
|
// This makes sure we update the explorer when we bump the snapshot version.
|
||||||
// Snapshot layout is described in durability/version.hpp
|
// Snapshot layout is described in durability/version.hpp
|
||||||
static_assert(durability::kVersion == 9,
|
static_assert(durability::kVersion == 10,
|
||||||
"Wrong snapshot version, please update!");
|
"Wrong snapshot version, please update!");
|
||||||
|
|
||||||
fs::path snapshot_path(FLAGS_snapshot_file);
|
fs::path snapshot_path(FLAGS_snapshot_file);
|
||||||
@ -76,32 +76,6 @@ int main(int argc, char *argv[]) {
|
|||||||
<< property.ValueString();
|
<< property.ValueString();
|
||||||
}
|
}
|
||||||
|
|
||||||
decoder.ReadValue(&dv, Value::Type::List);
|
|
||||||
auto existence_constraint = dv.ValueList();
|
|
||||||
for (auto it = existence_constraint.begin(); it != existence_constraint.end();) {
|
|
||||||
std::string log("Adding existence constraint: ");
|
|
||||||
CHECK(it->IsString()) << "Label is not a string!";
|
|
||||||
log.append(it->ValueString());
|
|
||||||
log.append(" -> [");
|
|
||||||
++it;
|
|
||||||
CHECK(it->IsInt()) << "Number of properties is not an int!";
|
|
||||||
int64_t prop_size = it->ValueInt();
|
|
||||||
++it;
|
|
||||||
for (size_t i = 0; i < prop_size; ++i) {
|
|
||||||
CHECK(it->IsString()) << "Property is not a string!";
|
|
||||||
log.append(it->ValueString());
|
|
||||||
if (i != prop_size -1) {
|
|
||||||
log.append(", ");
|
|
||||||
} else {
|
|
||||||
log.append("]");
|
|
||||||
}
|
|
||||||
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG(INFO) << log;
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder.ReadValue(&dv, Value::Type::List);
|
decoder.ReadValue(&dv, Value::Type::List);
|
||||||
auto unique_constraint = dv.ValueList();
|
auto unique_constraint = dv.ValueList();
|
||||||
for (auto it = unique_constraint.begin(); it != unique_constraint.end();) {
|
for (auto it = unique_constraint.begin(); it != unique_constraint.end();) {
|
||||||
|
@ -45,10 +45,6 @@ std::string StateDeltaTypeToString(database::StateDelta::Type type) {
|
|||||||
return "BUILD_INDEX";
|
return "BUILD_INDEX";
|
||||||
case database::StateDelta::Type::DROP_INDEX:
|
case database::StateDelta::Type::DROP_INDEX:
|
||||||
return "DROP_INDEX";
|
return "DROP_INDEX";
|
||||||
case database::StateDelta::Type::BUILD_EXISTENCE_CONSTRAINT:
|
|
||||||
return "BUILD_EXISTENCE_CONSTRAINT";
|
|
||||||
case database::StateDelta::Type::DROP_EXISTENCE_CONSTRAINT:
|
|
||||||
return "DROP_EXISTENCE_CONSTRAINT";
|
|
||||||
case database::StateDelta::Type::BUILD_UNIQUE_CONSTRAINT:
|
case database::StateDelta::Type::BUILD_UNIQUE_CONSTRAINT:
|
||||||
return "BUILD_UNIQUE_CONSTRAINT";
|
return "BUILD_UNIQUE_CONSTRAINT";
|
||||||
case database::StateDelta::Type::DROP_UNIQUE_CONSTRAINT:
|
case database::StateDelta::Type::DROP_UNIQUE_CONSTRAINT:
|
||||||
|
@ -133,9 +133,6 @@ target_link_libraries(${test_prefix}edges_distributed mg-distributed kvstore_dum
|
|||||||
add_unit_test(edges_single_node.cpp)
|
add_unit_test(edges_single_node.cpp)
|
||||||
target_link_libraries(${test_prefix}edges_single_node mg-single-node kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}edges_single_node mg-single-node kvstore_dummy_lib)
|
||||||
|
|
||||||
add_unit_test(existence_constraints.cpp)
|
|
||||||
target_link_libraries(${test_prefix}existence_constraints mg-single-node kvstore_dummy_lib)
|
|
||||||
|
|
||||||
add_unit_test(gid.cpp)
|
add_unit_test(gid.cpp)
|
||||||
target_link_libraries(${test_prefix}gid mg-distributed kvstore_dummy_lib)
|
target_link_libraries(${test_prefix}gid mg-distributed kvstore_dummy_lib)
|
||||||
|
|
||||||
|
@ -166,36 +166,6 @@ class DbGenerator {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool CompareExistenceConstraints(const database::GraphDbAccessor &dba1,
|
|
||||||
const database::GraphDbAccessor &dba2) {
|
|
||||||
auto c1 = dba1.ListExistenceConstraints();
|
|
||||||
auto c2 = dba2.ListExistenceConstraints();
|
|
||||||
|
|
||||||
auto compare_prop = [](auto &dba1, auto &p1, auto &dba2, auto &p2) {
|
|
||||||
std::vector<std::string> p1_names;
|
|
||||||
p1_names.reserve(p1.size());
|
|
||||||
std::vector<std::string> p2_names;
|
|
||||||
p2_names.reserve(p2.size());
|
|
||||||
|
|
||||||
std::transform(p1.begin(), p1.end(), std::back_inserter(p1_names),
|
|
||||||
[&dba1](auto p) { return dba1.PropertyName(p); });
|
|
||||||
std::transform(p2.begin(), p2.end(), std::back_inserter(p2_names),
|
|
||||||
[&dba2](auto p) { return dba2.PropertyName(p); });
|
|
||||||
|
|
||||||
return p1_names.size() == p2_names.size() &&
|
|
||||||
std::is_permutation(p1_names.begin(), p1_names.end(),
|
|
||||||
p2_names.begin());
|
|
||||||
};
|
|
||||||
|
|
||||||
return c1.size() == c2.size() &&
|
|
||||||
std::is_permutation(
|
|
||||||
c1.begin(), c1.end(), c2.begin(),
|
|
||||||
[&dba1, &dba2, &compare_prop](auto &r1, auto &r2) {
|
|
||||||
return dba1.LabelName(r1.label) == dba2.LabelName(r2.label) &&
|
|
||||||
compare_prop(dba1, r1.properties, dba2, r2.properties);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CompareUniqueConstraints(const database::GraphDbAccessor &dba1,
|
bool CompareUniqueConstraints(const database::GraphDbAccessor &dba1,
|
||||||
const database::GraphDbAccessor &dba2) {
|
const database::GraphDbAccessor &dba2) {
|
||||||
auto c1 = dba1.ListUniqueLabelPropertyConstraints();
|
auto c1 = dba1.ListUniqueLabelPropertyConstraints();
|
||||||
@ -226,7 +196,6 @@ void CompareDbs(database::GraphDb &a, database::GraphDb &b) {
|
|||||||
<< "Indexes not equal [" << utils::Join(index_a, ", ") << "] != ["
|
<< "Indexes not equal [" << utils::Join(index_a, ", ") << "] != ["
|
||||||
<< utils::Join(index_b, ", ");
|
<< utils::Join(index_b, ", ");
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(CompareExistenceConstraints(dba_a, dba_b));
|
|
||||||
EXPECT_TRUE(CompareUniqueConstraints(dba_a, dba_b));
|
EXPECT_TRUE(CompareUniqueConstraints(dba_a, dba_b));
|
||||||
|
|
||||||
auto is_permutation_props = [&dba_a, &dba_b](const auto &p1_id,
|
auto is_permutation_props = [&dba_a, &dba_b](const auto &p1_id,
|
||||||
@ -532,11 +501,6 @@ TEST_F(Durability, SnapshotEncoding) {
|
|||||||
EXPECT_EQ(dv.ValueList()[1].ValueString(), "p1");
|
EXPECT_EQ(dv.ValueList()[1].ValueString(), "p1");
|
||||||
EXPECT_EQ(dv.ValueList()[2].ValueBool(), false);
|
EXPECT_EQ(dv.ValueList()[2].ValueBool(), false);
|
||||||
|
|
||||||
// Existence constraints
|
|
||||||
decoder.ReadValue(&dv);
|
|
||||||
ASSERT_TRUE(dv.IsList());
|
|
||||||
ASSERT_EQ(dv.ValueList().size(), 0);
|
|
||||||
|
|
||||||
// Unique constraints
|
// Unique constraints
|
||||||
decoder.ReadValue(&dv);
|
decoder.ReadValue(&dv);
|
||||||
ASSERT_TRUE(dv.IsList());
|
ASSERT_TRUE(dv.IsList());
|
||||||
@ -1041,81 +1005,6 @@ TEST_F(Durability, UniqueIndexRecoveryWal) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(Durability, ExistenceConstraintRecoveryWal) {
|
|
||||||
auto config = DbConfig(true, false);
|
|
||||||
database::GraphDb db{config};
|
|
||||||
{
|
|
||||||
// Fill database with some data
|
|
||||||
auto dba = db.Access();
|
|
||||||
DbGenerator gen(dba);
|
|
||||||
dba.InsertVertex();
|
|
||||||
gen.InsertVertex();
|
|
||||||
gen.InsertVertex();
|
|
||||||
auto l1 = dba.Label("l1");
|
|
||||||
std::vector<storage::Property> p1{dba.Property("p1"), dba.Property("p2")};
|
|
||||||
dba.BuildExistenceConstraint(l1, p1);
|
|
||||||
gen.InsertEdge();
|
|
||||||
auto l2 = dba.Label("l2");
|
|
||||||
std::vector<storage::Property> p2{dba.Property("p3"), dba.Property("p4")};
|
|
||||||
dba.BuildExistenceConstraint(l2, p2);
|
|
||||||
gen.InsertVertex();
|
|
||||||
gen.InsertEdge();
|
|
||||||
dba.DeleteExistenceConstraint(l1, p1);
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// Recover and compare
|
|
||||||
db.wal().Flush();
|
|
||||||
auto recovered_config = DbConfig(false, true);
|
|
||||||
database::GraphDb recovered_db{recovered_config};
|
|
||||||
CompareDbs(db, recovered_db);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(Durability, ExistenceConstraintRecoverySnapshotAndWal) {
|
|
||||||
auto config = DbConfig(true, false);
|
|
||||||
database::GraphDb db{config};
|
|
||||||
{
|
|
||||||
// Fill database with some data
|
|
||||||
auto dba = db.Access();
|
|
||||||
DbGenerator gen(dba);
|
|
||||||
dba.InsertVertex();
|
|
||||||
gen.InsertVertex();
|
|
||||||
gen.InsertVertex();
|
|
||||||
auto l1 = dba.Label("l1");
|
|
||||||
std::vector<storage::Property> p1{dba.Property("p1"), dba.Property("p2")};
|
|
||||||
dba.BuildExistenceConstraint(l1, p1);
|
|
||||||
gen.InsertEdge();
|
|
||||||
auto l2 = dba.Label("l2");
|
|
||||||
std::vector<storage::Property> p2{dba.Property("p3"), dba.Property("p4")};
|
|
||||||
dba.BuildExistenceConstraint(l2, p2);
|
|
||||||
gen.InsertVertex();
|
|
||||||
gen.InsertEdge();
|
|
||||||
dba.DeleteExistenceConstraint(l1, p1);
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
// create snapshot with build existence constraint
|
|
||||||
MakeSnapshot(db);
|
|
||||||
{
|
|
||||||
auto dba = db.Access();
|
|
||||||
DbGenerator gen(dba);
|
|
||||||
gen.InsertVertex();
|
|
||||||
gen.InsertVertex();
|
|
||||||
|
|
||||||
auto l3 = dba.Label("l3");
|
|
||||||
std::vector<storage::Property> p3{dba.Property("p5"), dba.Property("p6")};
|
|
||||||
dba.BuildExistenceConstraint(l3, p3);
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// Recover and compare
|
|
||||||
db.wal().Flush();
|
|
||||||
auto recovered_config = DbConfig(false, true);
|
|
||||||
database::GraphDb recovered_db{recovered_config};
|
|
||||||
CompareDbs(db, recovered_db);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(Durability, UniqueConstraintRecoveryWal) {
|
TEST_F(Durability, UniqueConstraintRecoveryWal) {
|
||||||
auto config = DbConfig(true, false);
|
auto config = DbConfig(true, false);
|
||||||
database::GraphDb db{config};
|
database::GraphDb db{config};
|
||||||
|
@ -1,141 +0,0 @@
|
|||||||
#include <gflags/gflags.h>
|
|
||||||
#include <glog/logging.h>
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include "database/single_node/graph_db.hpp"
|
|
||||||
#include "database/single_node/graph_db_accessor.hpp"
|
|
||||||
|
|
||||||
class ExistenceConstraintsTest : public ::testing::Test {
|
|
||||||
public:
|
|
||||||
void SetUp() override {
|
|
||||||
auto dba = db_.Access();
|
|
||||||
label_ = dba.Label("label");
|
|
||||||
property_ = dba.Property("property");
|
|
||||||
properties_ = {property_};
|
|
||||||
rule_ = {label_, properties_};
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
storage::constraints::ExistenceConstraints constraints_;
|
|
||||||
database::GraphDb db_;
|
|
||||||
storage::Label label_;
|
|
||||||
storage::Property property_;
|
|
||||||
std::vector<storage::Property> properties_;
|
|
||||||
storage::constraints::ExistenceRule rule_;
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_F(ExistenceConstraintsTest, BuildDrop) {
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
EXPECT_FALSE(dba.ExistenceConstraintExists(label_, properties_));
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
dba.BuildExistenceConstraint(label_, properties_);
|
|
||||||
EXPECT_TRUE(dba.ExistenceConstraintExists(label_, properties_));
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
dba.DeleteExistenceConstraint(label_, properties_);
|
|
||||||
EXPECT_FALSE(dba.ExistenceConstraintExists(label_, properties_));
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ExistenceConstraintsTest, BuildWithViolation) {
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
auto v = dba.InsertVertex();
|
|
||||||
v.add_label(label_);
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
EXPECT_THROW(dba.BuildExistenceConstraint(label_, properties_),
|
|
||||||
database::IndexConstraintViolationException);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ExistenceConstraintsTest, InsertFail) {
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
dba.BuildExistenceConstraint(label_, properties_);
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
auto v = dba.InsertVertex();
|
|
||||||
EXPECT_THROW(v.add_label(label_),
|
|
||||||
database::IndexConstraintViolationException);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ExistenceConstraintsTest, InsertPass) {
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
dba.BuildExistenceConstraint(label_, properties_);
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
auto v = dba.InsertVertex();
|
|
||||||
v.PropsSet(property_, PropertyValue("Something"));
|
|
||||||
v.add_label(label_);
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ExistenceConstraintsTest, RemoveFail) {
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
dba.BuildExistenceConstraint(label_, properties_);
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
gid::Gid gid;
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
auto v = dba.InsertVertex();
|
|
||||||
v.PropsSet(property_, PropertyValue("Something"));
|
|
||||||
v.add_label(label_);
|
|
||||||
gid = v.gid();
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
auto v = dba.FindVertex(gid, false);
|
|
||||||
EXPECT_THROW(v.PropsErase(property_),
|
|
||||||
database::IndexConstraintViolationException);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ExistenceConstraintsTest, RemovePass) {
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
dba.BuildExistenceConstraint(label_, properties_);
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
gid::Gid gid;
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
auto v = dba.InsertVertex();
|
|
||||||
v.PropsSet(property_, PropertyValue("Something"));
|
|
||||||
v.add_label(label_);
|
|
||||||
gid = v.gid();
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto dba = db_.Access();
|
|
||||||
auto v = dba.FindVertex(gid, false);
|
|
||||||
v.remove_label(label_);
|
|
||||||
v.PropsErase(property_);
|
|
||||||
dba.Commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
google::InitGoogleLogging(argv[0]);
|
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
|
||||||
return RUN_ALL_TESTS();
|
|
||||||
}
|
|
@ -20,7 +20,7 @@
|
|||||||
#include "utils/timer.hpp"
|
#include "utils/timer.hpp"
|
||||||
|
|
||||||
// Snapshot layout is described in durability/version.hpp
|
// Snapshot layout is described in durability/version.hpp
|
||||||
static_assert(durability::kVersion == 9,
|
static_assert(durability::kVersion == 10,
|
||||||
"Wrong snapshot version, please update!");
|
"Wrong snapshot version, please update!");
|
||||||
|
|
||||||
bool ValidateNotEmpty(const char *flagname, const std::string &value) {
|
bool ValidateNotEmpty(const char *flagname, const std::string &value) {
|
||||||
@ -424,7 +424,6 @@ void Convert(const std::vector<std::string> &nodes,
|
|||||||
encoder.WriteInt(0); // Id of transaction that is snapshooting.
|
encoder.WriteInt(0); // Id of transaction that is snapshooting.
|
||||||
encoder.WriteList({}); // Transactional snapshot.
|
encoder.WriteList({}); // Transactional snapshot.
|
||||||
encoder.WriteList({}); // Label + property indexes.
|
encoder.WriteList({}); // Label + property indexes.
|
||||||
encoder.WriteList({}); // Existence constraints
|
|
||||||
encoder.WriteList({}); // Unique constraints
|
encoder.WriteList({}); // Unique constraints
|
||||||
// PassNodes streams vertices to the encoder.
|
// PassNodes streams vertices to the encoder.
|
||||||
for (const auto &nodes_file : nodes) {
|
for (const auto &nodes_file : nodes) {
|
||||||
|
Loading…
Reference in New Issue
Block a user