Add unique constraints capability of serving as indices
This commit is contained in:
@@ -623,6 +623,14 @@ class DbAccessor final {
|
||||
const std::set<storage::PropertyId> &properties) {
|
||||
return accessor_->DropUniqueConstraint(label, properties);
|
||||
}
|
||||
|
||||
bool UniqueConstraintExists(storage::LabelId label, storage::PropertyId prop) const {
|
||||
return accessor_->UniqueConstraintExists(label, prop);
|
||||
}
|
||||
|
||||
bool IndexedScanExists(storage::LabelId label, storage::PropertyId prop) const {
|
||||
return LabelPropertyIndexExists(label, prop) || UniqueConstraintExists(label, prop);
|
||||
}
|
||||
};
|
||||
|
||||
class SubgraphDbAccessor final {
|
||||
|
||||
@@ -43,7 +43,8 @@ class PostProcessor final {
|
||||
|
||||
template <class TPlanningContext>
|
||||
std::unique_ptr<LogicalOperator> Rewrite(std::unique_ptr<LogicalOperator> plan, TPlanningContext *context) {
|
||||
return RewriteWithIndexLookup(std::move(plan), context->symbol_table, context->ast_storage, context->db);
|
||||
return RewriteWithIndexLookup(std::move(plan), context->symbol_table, context->ast_storage, context->db,
|
||||
parameters_);
|
||||
}
|
||||
|
||||
template <class TVertexCounts>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include "query/parameters.hpp"
|
||||
#include "query/plan/operator.hpp"
|
||||
#include "query/plan/preprocess.hpp"
|
||||
|
||||
@@ -41,8 +42,8 @@ Expression *RemoveAndExpressions(Expression *expr, const std::unordered_set<Expr
|
||||
template <class TDbAccessor>
|
||||
class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
public:
|
||||
IndexLookupRewriter(SymbolTable *symbol_table, AstStorage *ast_storage, TDbAccessor *db)
|
||||
: symbol_table_(symbol_table), ast_storage_(ast_storage), db_(db) {}
|
||||
IndexLookupRewriter(SymbolTable *symbol_table, AstStorage *ast_storage, TDbAccessor *db, const Parameters ¶meters)
|
||||
: symbol_table_(symbol_table), ast_storage_(ast_storage), db_(db), parameters_(parameters) {}
|
||||
|
||||
using HierarchicalLogicalOperatorVisitor::PostVisit;
|
||||
using HierarchicalLogicalOperatorVisitor::PreVisit;
|
||||
@@ -481,6 +482,7 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
SymbolTable *symbol_table_;
|
||||
AstStorage *ast_storage_;
|
||||
TDbAccessor *db_;
|
||||
const Parameters ¶meters_;
|
||||
// Collected filters, pending for examination if they can be used for advanced
|
||||
// lookup operations (by index, node ID, ...).
|
||||
Filters filters_;
|
||||
@@ -509,7 +511,7 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
}
|
||||
|
||||
void RewriteBranch(std::shared_ptr<LogicalOperator> *branch) {
|
||||
IndexLookupRewriter<TDbAccessor> rewriter(symbol_table_, ast_storage_, db_);
|
||||
IndexLookupRewriter<TDbAccessor> rewriter(symbol_table_, ast_storage_, db_, parameters_);
|
||||
(*branch)->Accept(rewriter);
|
||||
if (rewriter.new_root_) {
|
||||
*branch = rewriter.new_root_;
|
||||
@@ -585,7 +587,8 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
continue;
|
||||
}
|
||||
const auto &property = filter.property_filter->property_;
|
||||
if (!db_->LabelPropertyIndexExists(GetLabel(label), GetProperty(property))) {
|
||||
if (!db_->LabelPropertyIndexExists(GetLabel(label), GetProperty(property)) &&
|
||||
!db_->UniqueConstraintExists(GetLabel(label), GetProperty(property))) {
|
||||
continue;
|
||||
}
|
||||
auto is_better_type = [&found](PropertyFilter::Type type) {
|
||||
@@ -597,7 +600,10 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
return type_sort_ix < found_sort_ix;
|
||||
};
|
||||
|
||||
int64_t vertex_count = db_->VerticesCount(GetLabel(label), GetProperty(property));
|
||||
std::optional<storage::PropertyValue> maybe_property_value = ConstPropertyValue(filter.property_filter->value_);
|
||||
int64_t vertex_count = maybe_property_value.has_value()
|
||||
? db_->VerticesCount(GetLabel(label), GetProperty(property), *maybe_property_value)
|
||||
: db_->VerticesCount(GetLabel(label), GetProperty(property));
|
||||
std::optional<storage::LabelPropertyIndexStats> new_stats =
|
||||
db_->GetIndexStats(GetLabel(label), GetProperty(property));
|
||||
|
||||
@@ -721,6 +727,17 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
filter_exprs_for_removal_.insert(removed_expressions.begin(), removed_expressions.end());
|
||||
return std::make_unique<ScanAllByLabel>(input, node_symbol, GetLabel(label), view);
|
||||
}
|
||||
|
||||
// If the expression is a constant property value, it is returned. Otherwise,
|
||||
// return nullopt.
|
||||
std::optional<storage::PropertyValue> ConstPropertyValue(const Expression *expression) {
|
||||
if (auto *literal = utils::Downcast<const PrimitiveLiteral>(expression)) {
|
||||
return literal->value_;
|
||||
} else if (auto *param_lookup = utils::Downcast<const ParameterLookup>(expression)) {
|
||||
return parameters_.AtTokenPosition(param_lookup->token_position_);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
@@ -728,8 +745,8 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
template <class TDbAccessor>
|
||||
std::unique_ptr<LogicalOperator> RewriteWithIndexLookup(std::unique_ptr<LogicalOperator> root_op,
|
||||
SymbolTable *symbol_table, AstStorage *ast_storage,
|
||||
TDbAccessor *db) {
|
||||
impl::IndexLookupRewriter<TDbAccessor> rewriter(symbol_table, ast_storage, db);
|
||||
TDbAccessor *db, const Parameters ¶meters) {
|
||||
impl::IndexLookupRewriter<TDbAccessor> rewriter(symbol_table, ast_storage, db, parameters);
|
||||
root_op->Accept(rewriter);
|
||||
if (rewriter.new_root_) {
|
||||
// This shouldn't happen in real use case, because IndexLookupRewriter
|
||||
|
||||
@@ -87,6 +87,10 @@ class VertexCountCache {
|
||||
return db_->GetIndexStats(label, property);
|
||||
}
|
||||
|
||||
bool UniqueConstraintExists(storage::LabelId label, storage::PropertyId prop) const {
|
||||
return db_->UniqueConstraintExists(label, prop);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef std::pair<storage::LabelId, storage::PropertyId> LabelPropertyKey;
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "storage/v2/constraints/constraint_violation.hpp"
|
||||
#include "storage/v2/transaction.hpp"
|
||||
#include "storage/v2/vertex.hpp"
|
||||
#include "storage/v2/vertex_accessor.hpp"
|
||||
#include "utils/result.hpp"
|
||||
|
||||
namespace memgraph::storage {
|
||||
@@ -58,6 +59,12 @@ class UniqueConstraints {
|
||||
|
||||
virtual std::vector<std::pair<LabelId, std::set<PropertyId>>> ListConstraints() const = 0;
|
||||
|
||||
virtual uint64_t ApproximateVertexCount(LabelId label, PropertyId property) const = 0;
|
||||
virtual uint64_t ApproximateVertexCount(LabelId label, PropertyId property, const PropertyValue &value) const = 0;
|
||||
virtual uint64_t ApproximateVertexCount(LabelId label, PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper) const = 0;
|
||||
|
||||
virtual void Clear() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -2103,6 +2103,8 @@ UniqueConstraints::DeletionStatus DiskStorage::DiskAccessor::DropUniqueConstrain
|
||||
return UniqueConstraints::DeletionStatus::SUCCESS;
|
||||
}
|
||||
|
||||
bool DiskStorage::DiskAccessor::UniqueConstraintExists(LabelId label, PropertyId property) const { return false; }
|
||||
|
||||
Transaction DiskStorage::CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode) {
|
||||
/// We acquire the transaction engine lock here because we access (and
|
||||
/// modify) the transaction engine variables (`transaction_id` and
|
||||
|
||||
@@ -228,6 +228,8 @@ class DiskStorage final : public Storage {
|
||||
UniqueConstraints::DeletionStatus DropUniqueConstraint(LabelId label,
|
||||
const std::set<PropertyId> &properties) override;
|
||||
|
||||
bool UniqueConstraintExists(LabelId label, PropertyId property) const override;
|
||||
|
||||
private:
|
||||
/// Flushes vertices and edges to the disk with the commit timestamp.
|
||||
/// At the time of calling, the commit_timestamp_ must already exist.
|
||||
|
||||
@@ -311,6 +311,17 @@ std::vector<std::pair<LabelId, std::set<PropertyId>>> DiskUniqueConstraints::Lis
|
||||
return {constraints_.begin(), constraints_.end()};
|
||||
}
|
||||
|
||||
uint64_t DiskUniqueConstraints::ApproximateVertexCount(LabelId label, PropertyId property) const { return 10; };
|
||||
uint64_t DiskUniqueConstraints::ApproximateVertexCount(LabelId label, PropertyId property,
|
||||
const PropertyValue &value) const {
|
||||
return 10;
|
||||
};
|
||||
uint64_t DiskUniqueConstraints::ApproximateVertexCount(LabelId label, PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper) const {
|
||||
return 10;
|
||||
};
|
||||
|
||||
void DiskUniqueConstraints::Clear() {
|
||||
constraints_.clear();
|
||||
|
||||
|
||||
@@ -54,6 +54,11 @@ class DiskUniqueConstraints : public UniqueConstraints {
|
||||
|
||||
std::vector<std::pair<LabelId, std::set<PropertyId>>> ListConstraints() const override;
|
||||
|
||||
uint64_t ApproximateVertexCount(LabelId label, PropertyId property) const override;
|
||||
uint64_t ApproximateVertexCount(LabelId label, PropertyId property, const PropertyValue &value) const override;
|
||||
uint64_t ApproximateVertexCount(LabelId label, PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper) const override;
|
||||
void Clear() override;
|
||||
|
||||
RocksDBStorage *GetRocksDBStorage() const;
|
||||
|
||||
@@ -1106,12 +1106,28 @@ UniqueConstraints::DeletionStatus InMemoryStorage::InMemoryAccessor::DropUniqueC
|
||||
return UniqueConstraints::DeletionStatus::SUCCESS;
|
||||
}
|
||||
|
||||
bool InMemoryStorage::InMemoryAccessor::UniqueConstraintExists(LabelId label, PropertyId property) const {
|
||||
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
|
||||
auto *mem_unique_constraints =
|
||||
static_cast<InMemoryUniqueConstraints *>(in_memory->constraints_.unique_constraints_.get());
|
||||
return mem_unique_constraints->ConstraintExists(label, {property});
|
||||
}
|
||||
|
||||
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, View view) {
|
||||
auto *mem_label_index = static_cast<InMemoryLabelIndex *>(storage_->indices_.label_index_.get());
|
||||
return VerticesIterable(mem_label_index->Vertices(label, view, storage_, &transaction_));
|
||||
}
|
||||
|
||||
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, PropertyId property, View view) {
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
|
||||
if (mem_storage->constraints_.unique_constraints_->ConstraintExists(label, {property})) {
|
||||
auto *mem_unique_constraints =
|
||||
static_cast<InMemoryUniqueConstraints *>(mem_storage->constraints_.unique_constraints_.get());
|
||||
return VerticesIterable(
|
||||
mem_unique_constraints->Vertices(label, property, std::nullopt, std::nullopt, view, storage_, &transaction_));
|
||||
}
|
||||
|
||||
auto *mem_label_property_index =
|
||||
static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
|
||||
return VerticesIterable(
|
||||
@@ -1120,6 +1136,15 @@ VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, Prop
|
||||
|
||||
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, PropertyId property,
|
||||
const PropertyValue &value, View view) {
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
if (mem_storage->constraints_.unique_constraints_->ConstraintExists(label, {property})) {
|
||||
auto *mem_unique_constraints =
|
||||
static_cast<InMemoryUniqueConstraints *>(mem_storage->constraints_.unique_constraints_.get());
|
||||
return VerticesIterable(mem_unique_constraints->Vertices(label, property, utils::MakeBoundInclusive(value),
|
||||
utils::MakeBoundInclusive(value), view, storage_,
|
||||
&transaction_));
|
||||
}
|
||||
|
||||
auto *mem_label_property_index =
|
||||
static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
|
||||
return VerticesIterable(mem_label_property_index->Vertices(label, property, utils::MakeBoundInclusive(value),
|
||||
@@ -1130,6 +1155,14 @@ VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, Prop
|
||||
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(
|
||||
LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) {
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
if (mem_storage->constraints_.unique_constraints_->ConstraintExists(label, {property})) {
|
||||
auto *mem_unique_constraints =
|
||||
static_cast<InMemoryUniqueConstraints *>(mem_storage->constraints_.unique_constraints_.get());
|
||||
return VerticesIterable(
|
||||
mem_unique_constraints->Vertices(label, property, lower_bound, upper_bound, view, storage_, &transaction_));
|
||||
}
|
||||
|
||||
auto *mem_label_property_index =
|
||||
static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
|
||||
return VerticesIterable(
|
||||
|
||||
@@ -115,16 +115,24 @@ class InMemoryStorage final : public Storage {
|
||||
/// Return approximate number of vertices with the given label and property.
|
||||
/// Note that this is always an over-estimate and never an under-estimate.
|
||||
uint64_t ApproximateVertexCount(LabelId label, PropertyId property) const override {
|
||||
return static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index_->ApproximateVertexCount(label,
|
||||
property);
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
if (mem_storage->constraints_.unique_constraints_->ConstraintExists(label, {property})) {
|
||||
return mem_storage->constraints_.unique_constraints_->ApproximateVertexCount(label, property);
|
||||
}
|
||||
|
||||
return mem_storage->indices_.label_property_index_->ApproximateVertexCount(label, property);
|
||||
}
|
||||
|
||||
/// Return approximate number of vertices with the given label and the given
|
||||
/// value for the given property. Note that this is always an over-estimate
|
||||
/// and never an under-estimate.
|
||||
uint64_t ApproximateVertexCount(LabelId label, PropertyId property, const PropertyValue &value) const override {
|
||||
return static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index_->ApproximateVertexCount(
|
||||
label, property, value);
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
if (mem_storage->constraints_.unique_constraints_->ConstraintExists(label, {property})) {
|
||||
return mem_storage->constraints_.unique_constraints_->ApproximateVertexCount(label, property, value);
|
||||
}
|
||||
|
||||
return mem_storage->indices_.label_property_index_->ApproximateVertexCount(label, property, value);
|
||||
}
|
||||
|
||||
/// Return approximate number of vertices with the given label and value for
|
||||
@@ -133,8 +141,12 @@ class InMemoryStorage final : public Storage {
|
||||
uint64_t ApproximateVertexCount(LabelId label, PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper) const override {
|
||||
return static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index_->ApproximateVertexCount(
|
||||
label, property, lower, upper);
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
if (mem_storage->constraints_.unique_constraints_->ConstraintExists(label, {property})) {
|
||||
return mem_storage->constraints_.unique_constraints_->ApproximateVertexCount(label, property, lower, upper);
|
||||
}
|
||||
|
||||
return mem_storage->indices_.label_property_index_->ApproximateVertexCount(label, property, lower, upper);
|
||||
}
|
||||
|
||||
template <typename TResult, typename TIndex, typename TIndexKey>
|
||||
@@ -191,6 +203,8 @@ class InMemoryStorage final : public Storage {
|
||||
return static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index_->IndexExists(label, property);
|
||||
}
|
||||
|
||||
bool UniqueConstraintExists(LabelId label, PropertyId property) const override;
|
||||
|
||||
IndicesInfo ListAllIndices() const override;
|
||||
|
||||
ConstraintsInfo ListAllConstraints() const override;
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "storage/v2/inmemory/unique_constraints.hpp"
|
||||
#include "storage/v2/indices/indices_utils.hpp"
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "utils/bound.hpp"
|
||||
|
||||
namespace memgraph::storage {
|
||||
|
||||
@@ -407,6 +410,30 @@ std::vector<std::pair<LabelId, std::set<PropertyId>>> InMemoryUniqueConstraints:
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t InMemoryUniqueConstraints::ApproximateVertexCount(LabelId label, PropertyId property) const {
|
||||
auto it = constraints_.find({label, {property}});
|
||||
MG_ASSERT(it != constraints_.end(), "Unique constraints for label {} and property {} doesn't exist", label.AsUint(),
|
||||
property.AsUint());
|
||||
return 1;
|
||||
};
|
||||
|
||||
uint64_t InMemoryUniqueConstraints::ApproximateVertexCount(LabelId label, PropertyId property,
|
||||
const PropertyValue &value) const {
|
||||
auto it = constraints_.find({label, {property}});
|
||||
MG_ASSERT(it != constraints_.end(), "Unique constraints for label {} and property {} doesn't exist", label.AsUint(),
|
||||
property.AsUint());
|
||||
return 1;
|
||||
};
|
||||
|
||||
uint64_t InMemoryUniqueConstraints::ApproximateVertexCount(
|
||||
LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper) const {
|
||||
auto it = constraints_.find({label, {property}});
|
||||
MG_ASSERT(it != constraints_.end(), "Unique constraints for label {} and property {} doesn't exist", label.AsUint(),
|
||||
property.AsUint());
|
||||
return 10;
|
||||
};
|
||||
|
||||
void InMemoryUniqueConstraints::RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp) {
|
||||
for (auto &[label_props, storage] : constraints_) {
|
||||
auto acc = storage.access();
|
||||
@@ -434,4 +461,200 @@ void InMemoryUniqueConstraints::Clear() {
|
||||
constraints_by_label_.clear();
|
||||
}
|
||||
|
||||
InMemoryUniqueConstraints::Iterable::Iterator::Iterator(Iterable *self,
|
||||
utils::SkipList<Entry>::Iterator constraint_iterator)
|
||||
: self_(self),
|
||||
constraint_iterator_(constraint_iterator),
|
||||
current_vertex_accessor_(nullptr, self_->storage_, nullptr),
|
||||
current_vertex_(nullptr) {
|
||||
AdvanceUntilValid();
|
||||
}
|
||||
|
||||
InMemoryUniqueConstraints::Iterable::Iterator &InMemoryUniqueConstraints::Iterable::Iterator::operator++() {
|
||||
++constraint_iterator_;
|
||||
AdvanceUntilValid();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void InMemoryUniqueConstraints::Iterable::Iterator::AdvanceUntilValid() {
|
||||
for (; constraint_iterator_ != self_->constraint_accessor_.end(); ++constraint_iterator_) {
|
||||
if (constraint_iterator_->vertex == current_vertex_) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto iterator_value = constraint_iterator_->values[0];
|
||||
|
||||
if (self_->lower_bound_) {
|
||||
if (iterator_value < self_->lower_bound_->value()) {
|
||||
continue;
|
||||
}
|
||||
if (!self_->lower_bound_->IsInclusive() && iterator_value == self_->lower_bound_->value()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (self_->upper_bound_) {
|
||||
if (self_->upper_bound_->value() < iterator_value) {
|
||||
constraint_iterator_ = self_->constraint_accessor_.end();
|
||||
break;
|
||||
}
|
||||
if (!self_->upper_bound_->IsInclusive() && iterator_value == self_->upper_bound_->value()) {
|
||||
constraint_iterator_ = self_->constraint_accessor_.end();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (CurrentVersionHasLabelProperty(*constraint_iterator_->vertex, self_->label_, self_->property_, iterator_value,
|
||||
self_->transaction_, self_->view_)) {
|
||||
current_vertex_ = const_cast<Vertex *>(constraint_iterator_->vertex);
|
||||
current_vertex_accessor_ = VertexAccessor(current_vertex_, self_->storage_, self_->transaction_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These constants represent the smallest possible value of each type that is
|
||||
// contained in a `PropertyValue`. Note that numbers (integers and doubles) are
|
||||
// treated as the same "type" in `PropertyValue`.
|
||||
const PropertyValue kSmallestBool = PropertyValue(false);
|
||||
// NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
|
||||
static_assert(-std::numeric_limits<double>::infinity() < std::numeric_limits<int64_t>::min());
|
||||
const PropertyValue kSmallestNumber = PropertyValue(-std::numeric_limits<double>::infinity());
|
||||
const PropertyValue kSmallestString = PropertyValue("");
|
||||
const PropertyValue kSmallestList = PropertyValue(std::vector<PropertyValue>());
|
||||
const PropertyValue kSmallestMap = PropertyValue(std::map<std::string, PropertyValue>());
|
||||
const PropertyValue kSmallestTemporalData =
|
||||
PropertyValue(TemporalData{static_cast<TemporalType>(0), std::numeric_limits<int64_t>::min()});
|
||||
|
||||
InMemoryUniqueConstraints::Iterable::Iterable(utils::SkipList<Entry>::Accessor constraint_accessor, LabelId label,
|
||||
PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view,
|
||||
Storage *storage, Transaction *transaction)
|
||||
: constraint_accessor_(std::move(constraint_accessor)),
|
||||
label_(label),
|
||||
property_(property),
|
||||
lower_bound_(lower_bound),
|
||||
upper_bound_(upper_bound),
|
||||
view_(view),
|
||||
storage_(storage),
|
||||
transaction_(transaction) {
|
||||
// We have to fix the bounds that the user provided to us. If the user
|
||||
// provided only one bound we should make sure that only values of that type
|
||||
// are returned by the iterator. We ensure this by supplying either an
|
||||
// inclusive lower bound of the same type, or an exclusive upper bound of the
|
||||
// following type. If neither bound is set we yield all items in the index.
|
||||
|
||||
// First we statically verify that our assumptions about the `PropertyValue`
|
||||
// type ordering holds.
|
||||
static_assert(PropertyValue::Type::Bool < PropertyValue::Type::Int);
|
||||
static_assert(PropertyValue::Type::Int < PropertyValue::Type::Double);
|
||||
static_assert(PropertyValue::Type::Double < PropertyValue::Type::String);
|
||||
static_assert(PropertyValue::Type::String < PropertyValue::Type::List);
|
||||
static_assert(PropertyValue::Type::List < PropertyValue::Type::Map);
|
||||
|
||||
// Remove any bounds that are set to `Null` because that isn't a valid value.
|
||||
if (lower_bound_ && lower_bound_->value().IsNull()) {
|
||||
lower_bound_ = std::nullopt;
|
||||
}
|
||||
if (upper_bound_ && upper_bound_->value().IsNull()) {
|
||||
upper_bound_ = std::nullopt;
|
||||
}
|
||||
|
||||
// Check whether the bounds are of comparable types if both are supplied.
|
||||
if (lower_bound_ && upper_bound_ &&
|
||||
!PropertyValue::AreComparableTypes(lower_bound_->value().type(), upper_bound_->value().type())) {
|
||||
bounds_valid_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set missing bounds.
|
||||
if (lower_bound_ && !upper_bound_) {
|
||||
// Here we need to supply an upper bound. The upper bound is set to an
|
||||
// exclusive lower bound of the following type.
|
||||
switch (lower_bound_->value().type()) {
|
||||
case PropertyValue::Type::Null:
|
||||
// This shouldn't happen because of the nullopt-ing above.
|
||||
LOG_FATAL("Invalid database state!");
|
||||
break;
|
||||
case PropertyValue::Type::Bool:
|
||||
upper_bound_ = utils::MakeBoundExclusive(kSmallestNumber);
|
||||
break;
|
||||
case PropertyValue::Type::Int:
|
||||
case PropertyValue::Type::Double:
|
||||
// Both integers and doubles are treated as the same type in
|
||||
// `PropertyValue` and they are interleaved when sorted.
|
||||
upper_bound_ = utils::MakeBoundExclusive(kSmallestString);
|
||||
break;
|
||||
case PropertyValue::Type::String:
|
||||
upper_bound_ = utils::MakeBoundExclusive(kSmallestList);
|
||||
break;
|
||||
case PropertyValue::Type::List:
|
||||
upper_bound_ = utils::MakeBoundExclusive(kSmallestMap);
|
||||
break;
|
||||
case PropertyValue::Type::Map:
|
||||
upper_bound_ = utils::MakeBoundExclusive(kSmallestTemporalData);
|
||||
break;
|
||||
case PropertyValue::Type::TemporalData:
|
||||
// This is the last type in the order so we leave the upper bound empty.
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (upper_bound_ && !lower_bound_) {
|
||||
// Here we need to supply a lower bound. The lower bound is set to an
|
||||
// inclusive lower bound of the current type.
|
||||
switch (upper_bound_->value().type()) {
|
||||
case PropertyValue::Type::Null:
|
||||
// This shouldn't happen because of the nullopt-ing above.
|
||||
LOG_FATAL("Invalid database state!");
|
||||
break;
|
||||
case PropertyValue::Type::Bool:
|
||||
lower_bound_ = utils::MakeBoundInclusive(kSmallestBool);
|
||||
break;
|
||||
case PropertyValue::Type::Int:
|
||||
case PropertyValue::Type::Double:
|
||||
// Both integers and doubles are treated as the same type in
|
||||
// `PropertyValue` and they are interleaved when sorted.
|
||||
lower_bound_ = utils::MakeBoundInclusive(kSmallestNumber);
|
||||
break;
|
||||
case PropertyValue::Type::String:
|
||||
lower_bound_ = utils::MakeBoundInclusive(kSmallestString);
|
||||
break;
|
||||
case PropertyValue::Type::List:
|
||||
lower_bound_ = utils::MakeBoundInclusive(kSmallestList);
|
||||
break;
|
||||
case PropertyValue::Type::Map:
|
||||
lower_bound_ = utils::MakeBoundInclusive(kSmallestMap);
|
||||
break;
|
||||
case PropertyValue::Type::TemporalData:
|
||||
lower_bound_ = utils::MakeBoundInclusive(kSmallestTemporalData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InMemoryUniqueConstraints::Iterable::Iterator InMemoryUniqueConstraints::Iterable::begin() {
|
||||
// If the bounds are set and don't have comparable types we don't yield any
|
||||
// items from the index.
|
||||
if (!bounds_valid_) return {this, constraint_accessor_.end()};
|
||||
auto constraint_iterator = constraint_accessor_.begin();
|
||||
if (lower_bound_) {
|
||||
constraint_iterator = constraint_accessor_.find_equal_or_greater(std::vector<PropertyValue>{lower_bound_->value()});
|
||||
}
|
||||
return {this, constraint_iterator};
|
||||
}
|
||||
|
||||
InMemoryUniqueConstraints::Iterable::Iterator InMemoryUniqueConstraints::Iterable::end() {
|
||||
return {this, constraint_accessor_.end()};
|
||||
}
|
||||
|
||||
InMemoryUniqueConstraints::Iterable InMemoryUniqueConstraints::Vertices(
|
||||
LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Storage *storage,
|
||||
Transaction *transaction) {
|
||||
auto it = constraints_.find({label, {property}});
|
||||
MG_ASSERT(it != constraints_.end(), "Constraint for label {} and property {} doesn't exist", label.AsUint(),
|
||||
property.AsUint());
|
||||
return {it->second.access(), label, property, lower_bound, upper_bound, view, storage, transaction};
|
||||
}
|
||||
|
||||
} // namespace memgraph::storage
|
||||
|
||||
@@ -89,11 +89,63 @@ class InMemoryUniqueConstraints : public UniqueConstraints {
|
||||
|
||||
std::vector<std::pair<LabelId, std::set<PropertyId>>> ListConstraints() const override;
|
||||
|
||||
class Iterable {
|
||||
public:
|
||||
Iterable(utils::SkipList<Entry>::Accessor constraint_accessor, LabelId label, PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Storage *storage,
|
||||
Transaction *transaction);
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator(Iterable *self, utils::SkipList<Entry>::Iterator constraint_iterator);
|
||||
|
||||
VertexAccessor const &operator*() const { return current_vertex_accessor_; }
|
||||
|
||||
bool operator==(const Iterator &other) const { return constraint_iterator_ == other.constraint_iterator_; }
|
||||
bool operator!=(const Iterator &other) const { return constraint_iterator_ != other.constraint_iterator_; }
|
||||
|
||||
Iterator &operator++();
|
||||
|
||||
private:
|
||||
void AdvanceUntilValid();
|
||||
|
||||
Iterable *self_;
|
||||
utils::SkipList<Entry>::Iterator constraint_iterator_;
|
||||
VertexAccessor current_vertex_accessor_;
|
||||
Vertex *current_vertex_;
|
||||
};
|
||||
|
||||
Iterator begin();
|
||||
Iterator end();
|
||||
|
||||
private:
|
||||
utils::SkipList<Entry>::Accessor constraint_accessor_;
|
||||
LabelId label_;
|
||||
PropertyId property_;
|
||||
std::optional<utils::Bound<PropertyValue>> lower_bound_;
|
||||
std::optional<utils::Bound<PropertyValue>> upper_bound_;
|
||||
bool bounds_valid_{true};
|
||||
View view_;
|
||||
Storage *storage_;
|
||||
Transaction *transaction_;
|
||||
};
|
||||
|
||||
uint64_t ApproximateVertexCount(LabelId label, PropertyId property) const override;
|
||||
uint64_t ApproximateVertexCount(LabelId label, PropertyId property, const PropertyValue &value) const override;
|
||||
uint64_t ApproximateVertexCount(LabelId label, PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper) const override;
|
||||
|
||||
/// GC method that removes outdated entries from constraints' storages.
|
||||
void RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp);
|
||||
|
||||
void Clear() override;
|
||||
|
||||
Iterable Vertices(LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Storage *storage,
|
||||
Transaction *transaction);
|
||||
|
||||
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_;
|
||||
|
||||
@@ -228,6 +228,8 @@ class Storage {
|
||||
virtual UniqueConstraints::DeletionStatus DropUniqueConstraint(LabelId label,
|
||||
const std::set<PropertyId> &properties) = 0;
|
||||
|
||||
virtual bool UniqueConstraintExists(LabelId label, PropertyId property) const = 0;
|
||||
|
||||
protected:
|
||||
Storage *storage_;
|
||||
std::shared_lock<utils::ResourceLock> storage_guard_;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "storage/v2/vertices_iterable.hpp"
|
||||
#include "storage/v2/inmemory/unique_constraints.hpp"
|
||||
|
||||
namespace memgraph::storage {
|
||||
|
||||
@@ -26,6 +27,11 @@ VerticesIterable::VerticesIterable(InMemoryLabelPropertyIndex::Iterable vertices
|
||||
new (&in_memory_vertices_by_label_property_) InMemoryLabelPropertyIndex::Iterable(std::move(vertices));
|
||||
}
|
||||
|
||||
VerticesIterable::VerticesIterable(InMemoryUniqueConstraints::Iterable vertices)
|
||||
: type_(Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY) {
|
||||
new (&in_memory_vertices_by_unique_constraint_) InMemoryUniqueConstraints::Iterable(std::move(vertices));
|
||||
}
|
||||
|
||||
VerticesIterable::VerticesIterable(VerticesIterable &&other) noexcept : type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
case Type::ALL:
|
||||
@@ -38,6 +44,10 @@ VerticesIterable::VerticesIterable(VerticesIterable &&other) noexcept : type_(ot
|
||||
new (&in_memory_vertices_by_label_property_)
|
||||
InMemoryLabelPropertyIndex::Iterable(std::move(other.in_memory_vertices_by_label_property_));
|
||||
break;
|
||||
case Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY:
|
||||
new (&in_memory_vertices_by_unique_constraint_)
|
||||
InMemoryUniqueConstraints::Iterable(std::move(other.in_memory_vertices_by_unique_constraint_));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +62,9 @@ VerticesIterable &VerticesIterable::operator=(VerticesIterable &&other) noexcept
|
||||
case Type::BY_LABEL_PROPERTY_IN_MEMORY:
|
||||
in_memory_vertices_by_label_property_.InMemoryLabelPropertyIndex::Iterable::~Iterable();
|
||||
break;
|
||||
case Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY:
|
||||
in_memory_vertices_by_unique_constraint_.InMemoryUniqueConstraints::Iterable::~Iterable();
|
||||
break;
|
||||
}
|
||||
type_ = other.type_;
|
||||
switch (other.type_) {
|
||||
@@ -65,6 +78,10 @@ VerticesIterable &VerticesIterable::operator=(VerticesIterable &&other) noexcept
|
||||
new (&in_memory_vertices_by_label_property_)
|
||||
InMemoryLabelPropertyIndex::Iterable(std::move(other.in_memory_vertices_by_label_property_));
|
||||
break;
|
||||
case Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY:
|
||||
new (&in_memory_vertices_by_unique_constraint_)
|
||||
InMemoryUniqueConstraints::Iterable(std::move(other.in_memory_vertices_by_unique_constraint_));
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -80,6 +97,9 @@ VerticesIterable::~VerticesIterable() {
|
||||
case Type::BY_LABEL_PROPERTY_IN_MEMORY:
|
||||
in_memory_vertices_by_label_property_.InMemoryLabelPropertyIndex::Iterable::~Iterable();
|
||||
break;
|
||||
case Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY:
|
||||
in_memory_vertices_by_unique_constraint_.InMemoryUniqueConstraints::Iterable::~Iterable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +111,8 @@ VerticesIterable::Iterator VerticesIterable::begin() {
|
||||
return Iterator(in_memory_vertices_by_label_.begin());
|
||||
case Type::BY_LABEL_PROPERTY_IN_MEMORY:
|
||||
return Iterator(in_memory_vertices_by_label_property_.begin());
|
||||
case Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY:
|
||||
return Iterator(in_memory_vertices_by_unique_constraint_.begin());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +124,8 @@ VerticesIterable::Iterator VerticesIterable::end() {
|
||||
return Iterator(in_memory_vertices_by_label_.end());
|
||||
case Type::BY_LABEL_PROPERTY_IN_MEMORY:
|
||||
return Iterator(in_memory_vertices_by_label_property_.end());
|
||||
case Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY:
|
||||
return Iterator(in_memory_vertices_by_unique_constraint_.end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +145,12 @@ VerticesIterable::Iterator::Iterator(InMemoryLabelPropertyIndex::Iterable::Itera
|
||||
new (&in_memory_by_label_property_it_) InMemoryLabelPropertyIndex::Iterable::Iterator(std::move(it));
|
||||
}
|
||||
|
||||
VerticesIterable::Iterator::Iterator(InMemoryUniqueConstraints::Iterable::Iterator it)
|
||||
: type_(Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY) {
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
new (&in_memory_by_unique_constraint_it_) InMemoryUniqueConstraints::Iterable::Iterator(std::move(it));
|
||||
}
|
||||
|
||||
VerticesIterable::Iterator::Iterator(const VerticesIterable::Iterator &other) : type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
case Type::ALL:
|
||||
@@ -133,6 +163,10 @@ VerticesIterable::Iterator::Iterator(const VerticesIterable::Iterator &other) :
|
||||
new (&in_memory_by_label_property_it_)
|
||||
InMemoryLabelPropertyIndex::Iterable::Iterator(other.in_memory_by_label_property_it_);
|
||||
break;
|
||||
case Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY:
|
||||
new (&in_memory_by_unique_constraint_it_)
|
||||
InMemoryUniqueConstraints::Iterable::Iterator(other.in_memory_by_unique_constraint_it_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +185,10 @@ VerticesIterable::Iterator &VerticesIterable::Iterator::operator=(const Vertices
|
||||
new (&in_memory_by_label_property_it_)
|
||||
InMemoryLabelPropertyIndex::Iterable::Iterator(other.in_memory_by_label_property_it_);
|
||||
break;
|
||||
case Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY:
|
||||
new (&in_memory_by_unique_constraint_it_)
|
||||
InMemoryUniqueConstraints::Iterable::Iterator(other.in_memory_by_unique_constraint_it_);
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -170,6 +208,11 @@ VerticesIterable::Iterator::Iterator(VerticesIterable::Iterator &&other) noexcep
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
InMemoryLabelPropertyIndex::Iterable::Iterator(std::move(other.in_memory_by_label_property_it_));
|
||||
break;
|
||||
case Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY:
|
||||
new (&in_memory_by_unique_constraint_it_)
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
InMemoryUniqueConstraints::Iterable::Iterator(std::move(other.in_memory_by_unique_constraint_it_));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +233,11 @@ VerticesIterable::Iterator &VerticesIterable::Iterator::operator=(VerticesIterab
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
InMemoryLabelPropertyIndex::Iterable::Iterator(std::move(other.in_memory_by_label_property_it_));
|
||||
break;
|
||||
case Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY:
|
||||
new (&in_memory_by_unique_constraint_it_)
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
InMemoryUniqueConstraints::Iterable::Iterator(std::move(other.in_memory_by_unique_constraint_it_));
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -207,6 +255,9 @@ void VerticesIterable::Iterator::Destroy() noexcept {
|
||||
case Type::BY_LABEL_PROPERTY_IN_MEMORY:
|
||||
in_memory_by_label_property_it_.InMemoryLabelPropertyIndex::Iterable::Iterator::~Iterator();
|
||||
break;
|
||||
case Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY:
|
||||
in_memory_by_unique_constraint_it_.InMemoryUniqueConstraints::Iterable::Iterator::~Iterator();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +269,8 @@ VertexAccessor const &VerticesIterable::Iterator::operator*() const {
|
||||
return *in_memory_by_label_it_;
|
||||
case Type::BY_LABEL_PROPERTY_IN_MEMORY:
|
||||
return *in_memory_by_label_property_it_;
|
||||
case Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY:
|
||||
return *in_memory_by_unique_constraint_it_;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,6 +285,9 @@ VerticesIterable::Iterator &VerticesIterable::Iterator::operator++() {
|
||||
case Type::BY_LABEL_PROPERTY_IN_MEMORY:
|
||||
++in_memory_by_label_property_it_;
|
||||
break;
|
||||
case Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY:
|
||||
++in_memory_by_unique_constraint_it_;
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -244,6 +300,8 @@ bool VerticesIterable::Iterator::operator==(const Iterator &other) const {
|
||||
return in_memory_by_label_it_ == other.in_memory_by_label_it_;
|
||||
case Type::BY_LABEL_PROPERTY_IN_MEMORY:
|
||||
return in_memory_by_label_property_it_ == other.in_memory_by_label_property_it_;
|
||||
case Type::BY_UNIQUE_CONSTRAINT_IN_MEMORY:
|
||||
return in_memory_by_unique_constraint_it_ == other.in_memory_by_unique_constraint_it_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,23 +14,26 @@
|
||||
#include "storage/v2/all_vertices_iterable.hpp"
|
||||
#include "storage/v2/inmemory/label_index.hpp"
|
||||
#include "storage/v2/inmemory/label_property_index.hpp"
|
||||
#include "storage/v2/inmemory/unique_constraints.hpp"
|
||||
|
||||
namespace memgraph::storage {
|
||||
|
||||
class VerticesIterable final {
|
||||
enum class Type { ALL, BY_LABEL_IN_MEMORY, BY_LABEL_PROPERTY_IN_MEMORY };
|
||||
enum class Type { ALL, BY_LABEL_IN_MEMORY, BY_LABEL_PROPERTY_IN_MEMORY, BY_UNIQUE_CONSTRAINT_IN_MEMORY };
|
||||
|
||||
Type type_;
|
||||
union {
|
||||
AllVerticesIterable all_vertices_;
|
||||
InMemoryLabelIndex::Iterable in_memory_vertices_by_label_;
|
||||
InMemoryLabelPropertyIndex::Iterable in_memory_vertices_by_label_property_;
|
||||
InMemoryUniqueConstraints::Iterable in_memory_vertices_by_unique_constraint_;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit VerticesIterable(AllVerticesIterable);
|
||||
explicit VerticesIterable(InMemoryLabelIndex::Iterable);
|
||||
explicit VerticesIterable(InMemoryLabelPropertyIndex::Iterable);
|
||||
explicit VerticesIterable(InMemoryUniqueConstraints::Iterable);
|
||||
|
||||
VerticesIterable(const VerticesIterable &) = delete;
|
||||
VerticesIterable &operator=(const VerticesIterable &) = delete;
|
||||
@@ -46,6 +49,7 @@ class VerticesIterable final {
|
||||
AllVerticesIterable::Iterator all_it_;
|
||||
InMemoryLabelIndex::Iterable::Iterator in_memory_by_label_it_;
|
||||
InMemoryLabelPropertyIndex::Iterable::Iterator in_memory_by_label_property_it_;
|
||||
InMemoryUniqueConstraints::Iterable::Iterator in_memory_by_unique_constraint_it_;
|
||||
};
|
||||
|
||||
void Destroy() noexcept;
|
||||
@@ -54,6 +58,7 @@ class VerticesIterable final {
|
||||
explicit Iterator(AllVerticesIterable::Iterator);
|
||||
explicit Iterator(InMemoryLabelIndex::Iterable::Iterator);
|
||||
explicit Iterator(InMemoryLabelPropertyIndex::Iterable::Iterator);
|
||||
explicit Iterator(InMemoryUniqueConstraints::Iterable::Iterator);
|
||||
|
||||
Iterator(const Iterator &);
|
||||
Iterator &operator=(const Iterator &);
|
||||
|
||||
@@ -213,6 +213,10 @@ class InteractiveDbAccessor {
|
||||
return label_property_index_.at(key);
|
||||
}
|
||||
|
||||
bool UniqueConstraintExists(memgraph::storage::LabelId label_id, memgraph::storage::PropertyId property_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<memgraph::storage::LabelIndexStats> GetIndexStats(const memgraph::storage::LabelId label) const {
|
||||
return dba_->GetIndexStats(label);
|
||||
}
|
||||
|
||||
@@ -487,10 +487,19 @@ class FakeDbAccessor {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t VerticesCount(memgraph::storage::LabelId label, memgraph::storage::PropertyId property,
|
||||
const memgraph::storage::PropertyValue &value) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool LabelIndexExists(memgraph::storage::LabelId label) const {
|
||||
return label_index_.find(label) != label_index_.end();
|
||||
}
|
||||
|
||||
bool UniqueConstraintExists(memgraph::storage::LabelId label, memgraph::storage::PropertyId property) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LabelPropertyIndexExists(memgraph::storage::LabelId label, memgraph::storage::PropertyId property) const {
|
||||
for (auto &index : label_property_index_) {
|
||||
if (std::get<0>(index) == label && std::get<1>(index) == property) {
|
||||
|
||||
Reference in New Issue
Block a user