memgraph/src/storage/v2/inmemory/unique_constraints.hpp
2023-12-04 21:56:05 +01:00

138 lines
6.1 KiB
C++

// 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
// 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 <optional>
#include <span>
#include <thread>
#include <variant>
#include "storage/v2/constraints/constraint_violation.hpp"
#include "storage/v2/constraints/unique_constraints.hpp"
#include "storage/v2/durability/recovery_type.hpp"
#include "storage/v2/id_types.hpp"
#include "utils/logging.hpp"
#include "utils/rw_spin_lock.hpp"
#include "utils/synchronized.hpp"
namespace memgraph::storage {
/// Utility class to store data in a fixed size array. The array is used
/// instead of `std::vector` to avoid `std::bad_alloc` exception where not
/// necessary.
template <class T>
struct FixedCapacityArray {
size_t size;
T values[kUniqueConstraintsMaxProperties];
explicit FixedCapacityArray(size_t array_size) : size(array_size) {
MG_ASSERT(size <= kUniqueConstraintsMaxProperties, "Invalid array size!");
}
};
using PropertyIdArray = FixedCapacityArray<PropertyId>;
class InMemoryUniqueConstraints : public UniqueConstraints {
private:
struct Entry {
std::vector<PropertyValue> values;
const Vertex *vertex;
uint64_t timestamp;
bool operator<(const Entry &rhs) const;
bool operator==(const Entry &rhs) const;
bool operator<(const std::vector<PropertyValue> &rhs) const;
bool operator==(const std::vector<PropertyValue> &rhs) const;
};
static std::optional<ConstraintViolation> DoValidate(const Vertex &vertex,
utils::SkipList<Entry>::Accessor &constraint_accessor,
const LabelId &label, const std::set<PropertyId> &properties);
public:
struct MultipleThreadsConstraintValidation {
bool operator()(const utils::SkipList<Vertex>::Accessor &vertex_accessor,
utils::SkipList<Entry>::Accessor &constraint_accessor, const LabelId &label,
const std::set<PropertyId> &properties);
const durability::ParallelizedSchemaCreationInfo &parallel_exec_info;
};
struct SingleThreadConstraintValidation {
bool operator()(const utils::SkipList<Vertex>::Accessor &vertex_accessor,
utils::SkipList<Entry>::Accessor &constraint_accessor, const LabelId &label,
const std::set<PropertyId> &properties);
};
/// Indexes the given vertex for relevant labels and properties.
/// This method should be called before committing and validating vertices
/// against unique constraints.
/// @throw std::bad_alloc
void UpdateBeforeCommit(const Vertex *vertex, const Transaction &tx);
void UpdateBeforeCommit(const Vertex *vertex, std::unordered_set<LabelId> &added_labels,
std::unordered_set<PropertyId> &added_properties, const Transaction &tx);
void AbortEntries(std::span<Vertex const *const> vertices, uint64_t exact_start_timestamp);
/// Creates unique constraint on the given `label` and a list of `properties`.
/// Returns constraint violation if there are multiple vertices with the same
/// label and property values. Returns `CreationStatus::ALREADY_EXISTS` if
/// constraint already existed, `CreationStatus::EMPTY_PROPERTIES` if the
/// given list of properties is empty,
/// `CreationStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED` if the list of properties
/// exceeds the maximum allowed number of properties, and
/// `CreationStatus::SUCCESS` on success.
/// @throw std::bad_alloc
utils::BasicResult<ConstraintViolation, CreationStatus> CreateConstraint(
LabelId label, const std::set<PropertyId> &properties, const utils::SkipList<Vertex>::Accessor &vertex_accessor,
const std::optional<durability::ParallelizedSchemaCreationInfo> &par_exec_info);
/// Deletes the specified constraint. Returns `DeletionStatus::NOT_FOUND` if
/// there is not such constraint in the storage,
/// `DeletionStatus::EMPTY_PROPERTIES` if the given set of `properties` is
/// empty, `DeletionStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED` if the given set
/// of `properties` exceeds the maximum allowed number of properties, and
/// `DeletionStatus::SUCCESS` on success.
DeletionStatus DropConstraint(LabelId label, const std::set<PropertyId> &properties) override;
bool ConstraintExists(LabelId label, const std::set<PropertyId> &properties) const override;
void UpdateOnRemoveLabel(LabelId removed_label, const Vertex &vertex_before_update,
const uint64_t transaction_start_timestamp) override {}
void UpdateOnAddLabel(LabelId added_label, const Vertex &vertex_before_update,
uint64_t transaction_start_timestamp) override{};
/// Validates the given vertex against unique constraints before committing.
/// This method should be called while commit lock is active with
/// `commit_timestamp` being a potential commit timestamp of the transaction.
/// @throw std::bad_alloc
std::optional<ConstraintViolation> Validate(const Vertex &vertex, const Transaction &tx,
uint64_t commit_timestamp) const;
std::vector<std::pair<LabelId, std::set<PropertyId>>> ListConstraints() const override;
/// GC method that removes outdated entries from constraints' storages.
void RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp);
void Clear() override;
static std::variant<MultipleThreadsConstraintValidation, SingleThreadConstraintValidation> GetCreationFunction(
const std::optional<durability::ParallelizedSchemaCreationInfo> &);
private:
std::map<std::pair<LabelId, std::set<PropertyId>>, utils::SkipList<Entry>> constraints_;
std::map<LabelId, std::map<std::set<PropertyId>, utils::SkipList<Entry> *>> constraints_by_label_;
};
} // namespace memgraph::storage