Implement edge type indices (#1542)
Implement edge type indices (#1542 )
This commit is contained in:
parent
5ca98f9543
commit
619b01f3f8
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
|
@ -840,6 +840,20 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
|
||||
transaction->DeleteLabelPropertyIndexStats(storage->NameToLabel(info.label));
|
||||
break;
|
||||
}
|
||||
case WalDeltaData::Type::EDGE_INDEX_CREATE: {
|
||||
spdlog::trace(" Create edge index on :{}", delta.operation_edge_type.edge_type);
|
||||
auto *transaction = get_transaction(timestamp, kUniqueAccess);
|
||||
if (transaction->CreateIndex(storage->NameToEdgeType(delta.operation_label.label)).HasError())
|
||||
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
|
||||
break;
|
||||
}
|
||||
case WalDeltaData::Type::EDGE_INDEX_DROP: {
|
||||
spdlog::trace(" Drop edge index on :{}", delta.operation_edge_type.edge_type);
|
||||
auto *transaction = get_transaction(timestamp, kUniqueAccess);
|
||||
if (transaction->DropIndex(storage->NameToEdgeType(delta.operation_label.label)).HasError())
|
||||
throw utils::BasicException("Invalid transaction! Please raise an issue, {}:{}", __FILE__, __LINE__);
|
||||
break;
|
||||
}
|
||||
case WalDeltaData::Type::EXISTENCE_CONSTRAINT_CREATE: {
|
||||
spdlog::trace(" Create existence constraint on :{} ({})", delta.operation_label_property.label,
|
||||
delta.operation_label_property.property);
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
|
@ -371,6 +371,62 @@ class VerticesIterable final {
|
||||
}
|
||||
};
|
||||
|
||||
class EdgesIterable final {
|
||||
std::variant<storage::EdgesIterable, std::unordered_set<EdgeAccessor, std::hash<EdgeAccessor>, std::equal_to<void>,
|
||||
utils::Allocator<EdgeAccessor>> *>
|
||||
iterable_;
|
||||
|
||||
public:
|
||||
class Iterator final {
|
||||
std::variant<storage::EdgesIterable::Iterator,
|
||||
std::unordered_set<EdgeAccessor, std::hash<EdgeAccessor>, std::equal_to<void>,
|
||||
utils::Allocator<EdgeAccessor>>::iterator>
|
||||
it_;
|
||||
|
||||
public:
|
||||
explicit Iterator(storage::EdgesIterable::Iterator it) : it_(std::move(it)) {}
|
||||
explicit Iterator(std::unordered_set<EdgeAccessor, std::hash<EdgeAccessor>, std::equal_to<void>,
|
||||
utils::Allocator<EdgeAccessor>>::iterator it)
|
||||
: it_(it) {}
|
||||
|
||||
EdgeAccessor operator*() const {
|
||||
return std::visit([](auto &it_) { return EdgeAccessor(*it_); }, it_);
|
||||
}
|
||||
|
||||
Iterator &operator++() {
|
||||
std::visit([](auto &it_) { ++it_; }, it_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const Iterator &other) const { return it_ == other.it_; }
|
||||
|
||||
bool operator!=(const Iterator &other) const { return !(other == *this); }
|
||||
};
|
||||
|
||||
explicit EdgesIterable(storage::EdgesIterable iterable) : iterable_(std::move(iterable)) {}
|
||||
explicit EdgesIterable(std::unordered_set<EdgeAccessor, std::hash<EdgeAccessor>, std::equal_to<void>,
|
||||
utils::Allocator<EdgeAccessor>> *edges)
|
||||
: iterable_(edges) {}
|
||||
|
||||
Iterator begin() {
|
||||
return std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[](storage::EdgesIterable &iterable_) { return Iterator(iterable_.begin()); },
|
||||
[](std::unordered_set<EdgeAccessor, std::hash<EdgeAccessor>, std::equal_to<void>,
|
||||
utils::Allocator<EdgeAccessor>> *iterable_) { return Iterator(iterable_->begin()); }},
|
||||
iterable_);
|
||||
}
|
||||
|
||||
Iterator end() {
|
||||
return std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[](storage::EdgesIterable &iterable_) { return Iterator(iterable_.end()); },
|
||||
[](std::unordered_set<EdgeAccessor, std::hash<EdgeAccessor>, std::equal_to<void>,
|
||||
utils::Allocator<EdgeAccessor>> *iterable_) { return Iterator(iterable_->end()); }},
|
||||
iterable_);
|
||||
}
|
||||
};
|
||||
|
||||
class DbAccessor final {
|
||||
storage::Storage::Accessor *accessor_;
|
||||
|
||||
@ -416,6 +472,10 @@ class DbAccessor final {
|
||||
return VerticesIterable(accessor_->Vertices(label, property, lower, upper, view));
|
||||
}
|
||||
|
||||
EdgesIterable Edges(storage::View view, storage::EdgeTypeId edge_type) {
|
||||
return EdgesIterable(accessor_->Edges(edge_type, view));
|
||||
}
|
||||
|
||||
VertexAccessor InsertVertex() { return VertexAccessor(accessor_->CreateVertex()); }
|
||||
|
||||
storage::Result<EdgeAccessor> InsertEdge(VertexAccessor *from, VertexAccessor *to,
|
||||
@ -572,6 +632,8 @@ class DbAccessor final {
|
||||
return accessor_->LabelPropertyIndexExists(label, prop);
|
||||
}
|
||||
|
||||
bool EdgeTypeIndexExists(storage::EdgeTypeId edge_type) const { return accessor_->EdgeTypeIndexExists(edge_type); }
|
||||
|
||||
std::optional<storage::LabelIndexStats> GetIndexStats(const storage::LabelId &label) const {
|
||||
return accessor_->GetIndexStats(label);
|
||||
}
|
||||
@ -638,6 +700,10 @@ class DbAccessor final {
|
||||
return accessor_->CreateIndex(label, property);
|
||||
}
|
||||
|
||||
utils::BasicResult<storage::StorageIndexDefinitionError, void> CreateIndex(storage::EdgeTypeId edge_type) {
|
||||
return accessor_->CreateIndex(edge_type);
|
||||
}
|
||||
|
||||
utils::BasicResult<storage::StorageIndexDefinitionError, void> DropIndex(storage::LabelId label) {
|
||||
return accessor_->DropIndex(label);
|
||||
}
|
||||
@ -647,6 +713,10 @@ class DbAccessor final {
|
||||
return accessor_->DropIndex(label, property);
|
||||
}
|
||||
|
||||
utils::BasicResult<storage::StorageIndexDefinitionError, void> DropIndex(storage::EdgeTypeId edge_type) {
|
||||
return accessor_->DropIndex(edge_type);
|
||||
}
|
||||
|
||||
utils::BasicResult<storage::StorageExistenceConstraintDefinitionError, void> CreateExistenceConstraint(
|
||||
storage::LabelId label, storage::PropertyId property) {
|
||||
return accessor_->CreateExistenceConstraint(label, property);
|
||||
|
@ -242,6 +242,10 @@ void DumpLabelIndex(std::ostream *os, query::DbAccessor *dba, const storage::Lab
|
||||
*os << "CREATE INDEX ON :" << EscapeName(dba->LabelToName(label)) << ";";
|
||||
}
|
||||
|
||||
void DumpEdgeTypeIndex(std::ostream *os, query::DbAccessor *dba, const storage::EdgeTypeId edge_type) {
|
||||
*os << "CREATE EDGE INDEX ON :" << EscapeName(dba->EdgeTypeToName(edge_type)) << ";";
|
||||
}
|
||||
|
||||
void DumpLabelPropertyIndex(std::ostream *os, query::DbAccessor *dba, storage::LabelId label,
|
||||
storage::PropertyId property) {
|
||||
*os << "CREATE INDEX ON :" << EscapeName(dba->LabelToName(label)) << "(" << EscapeName(dba->PropertyToName(property))
|
||||
@ -297,7 +301,9 @@ PullPlanDump::PullPlanDump(DbAccessor *dba, dbms::DatabaseAccess db_acc)
|
||||
// Internal index cleanup
|
||||
CreateInternalIndexCleanupPullChunk(),
|
||||
// Dump all triggers
|
||||
CreateTriggersPullChunk()} {}
|
||||
CreateTriggersPullChunk(),
|
||||
// Dump all edge-type indices
|
||||
CreateEdgeTypeIndicesPullChunk()} {}
|
||||
|
||||
bool PullPlanDump::Pull(AnyStream *stream, std::optional<int> n) {
|
||||
// Iterate all functions that stream some results.
|
||||
@ -352,6 +358,33 @@ PullPlanDump::PullChunk PullPlanDump::CreateLabelIndicesPullChunk() {
|
||||
};
|
||||
}
|
||||
|
||||
PullPlanDump::PullChunk PullPlanDump::CreateEdgeTypeIndicesPullChunk() {
|
||||
// Dump all label indices
|
||||
return [this, global_index = 0U](AnyStream *stream, std::optional<int> n) mutable -> std::optional<size_t> {
|
||||
// Delay the construction of indices vectors
|
||||
if (!indices_info_) {
|
||||
indices_info_.emplace(dba_->ListAllIndices());
|
||||
}
|
||||
const auto &edge_type = indices_info_->edge_type;
|
||||
|
||||
size_t local_counter = 0;
|
||||
while (global_index < edge_type.size() && (!n || local_counter < *n)) {
|
||||
std::ostringstream os;
|
||||
DumpEdgeTypeIndex(&os, dba_, edge_type[global_index]);
|
||||
stream->Result({TypedValue(os.str())});
|
||||
|
||||
++global_index;
|
||||
++local_counter;
|
||||
}
|
||||
|
||||
if (global_index == edge_type.size()) {
|
||||
return local_counter;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
};
|
||||
}
|
||||
|
||||
PullPlanDump::PullChunk PullPlanDump::CreateLabelPropertyIndicesPullChunk() {
|
||||
return [this, global_index = 0U](AnyStream *stream, std::optional<int> n) mutable -> std::optional<size_t> {
|
||||
// Delay the construction of indices vectors
|
||||
|
@ -63,5 +63,6 @@ struct PullPlanDump {
|
||||
PullChunk CreateDropInternalIndexPullChunk();
|
||||
PullChunk CreateInternalIndexCleanupPullChunk();
|
||||
PullChunk CreateTriggersPullChunk();
|
||||
PullChunk CreateEdgeTypeIndicesPullChunk();
|
||||
};
|
||||
} // namespace memgraph::query
|
||||
|
@ -186,6 +186,9 @@ constexpr utils::TypeInfo query::ProfileQuery::kType{utils::TypeId::AST_PROFILE_
|
||||
|
||||
constexpr utils::TypeInfo query::IndexQuery::kType{utils::TypeId::AST_INDEX_QUERY, "IndexQuery", &query::Query::kType};
|
||||
|
||||
constexpr utils::TypeInfo query::EdgeIndexQuery::kType{utils::TypeId::AST_EDGE_INDEX_QUERY, "EdgeIndexQuery",
|
||||
&query::Query::kType};
|
||||
|
||||
constexpr utils::TypeInfo query::Create::kType{utils::TypeId::AST_CREATE, "Create", &query::Clause::kType};
|
||||
|
||||
constexpr utils::TypeInfo query::CallProcedure::kType{utils::TypeId::AST_CALL_PROCEDURE, "CallProcedure",
|
||||
|
@ -2224,6 +2224,34 @@ class IndexQuery : public memgraph::query::Query {
|
||||
friend class AstStorage;
|
||||
};
|
||||
|
||||
class EdgeIndexQuery : public memgraph::query::Query {
|
||||
public:
|
||||
static const utils::TypeInfo kType;
|
||||
const utils::TypeInfo &GetTypeInfo() const override { return kType; }
|
||||
|
||||
enum class Action { CREATE, DROP };
|
||||
|
||||
EdgeIndexQuery() = default;
|
||||
|
||||
DEFVISITABLE(QueryVisitor<void>);
|
||||
|
||||
memgraph::query::EdgeIndexQuery::Action action_;
|
||||
memgraph::query::EdgeTypeIx edge_type_;
|
||||
|
||||
EdgeIndexQuery *Clone(AstStorage *storage) const override {
|
||||
EdgeIndexQuery *object = storage->Create<EdgeIndexQuery>();
|
||||
object->action_ = action_;
|
||||
object->edge_type_ = storage->GetEdgeTypeIx(edge_type_.name);
|
||||
return object;
|
||||
}
|
||||
|
||||
protected:
|
||||
EdgeIndexQuery(Action action, EdgeTypeIx edge_type) : action_(action), edge_type_(edge_type) {}
|
||||
|
||||
private:
|
||||
friend class AstStorage;
|
||||
};
|
||||
|
||||
class Create : public memgraph::query::Clause {
|
||||
public:
|
||||
static const utils::TypeInfo kType;
|
||||
|
@ -82,6 +82,7 @@ class AuthQuery;
|
||||
class ExplainQuery;
|
||||
class ProfileQuery;
|
||||
class IndexQuery;
|
||||
class EdgeIndexQuery;
|
||||
class DatabaseInfoQuery;
|
||||
class SystemInfoQuery;
|
||||
class ConstraintQuery;
|
||||
@ -143,11 +144,11 @@ class ExpressionVisitor
|
||||
|
||||
template <class TResult>
|
||||
class QueryVisitor
|
||||
: public utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery, IndexQuery, AuthQuery, DatabaseInfoQuery,
|
||||
SystemInfoQuery, ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery,
|
||||
FreeMemoryQuery, TriggerQuery, IsolationLevelQuery, CreateSnapshotQuery, StreamQuery,
|
||||
SettingQuery, VersionQuery, ShowConfigQuery, TransactionQueueQuery, StorageModeQuery,
|
||||
AnalyzeGraphQuery, MultiDatabaseQuery, ShowDatabasesQuery, EdgeImportModeQuery,
|
||||
CoordinatorQuery> {};
|
||||
: public utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery, IndexQuery, EdgeIndexQuery, AuthQuery,
|
||||
DatabaseInfoQuery, SystemInfoQuery, ConstraintQuery, DumpQuery, ReplicationQuery,
|
||||
LockPathQuery, FreeMemoryQuery, TriggerQuery, IsolationLevelQuery, CreateSnapshotQuery,
|
||||
StreamQuery, SettingQuery, VersionQuery, ShowConfigQuery, TransactionQueueQuery,
|
||||
StorageModeQuery, AnalyzeGraphQuery, MultiDatabaseQuery, ShowDatabasesQuery,
|
||||
EdgeImportModeQuery, CoordinatorQuery> {};
|
||||
|
||||
} // namespace memgraph::query
|
||||
|
@ -265,6 +265,27 @@ antlrcpp::Any CypherMainVisitor::visitDropIndex(MemgraphCypher::DropIndexContext
|
||||
return index_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitEdgeIndexQuery(MemgraphCypher::EdgeIndexQueryContext *ctx) {
|
||||
MG_ASSERT(ctx->children.size() == 1, "EdgeIndexQuery should have exactly one child!");
|
||||
auto *index_query = std::any_cast<EdgeIndexQuery *>(ctx->children[0]->accept(this));
|
||||
query_ = index_query;
|
||||
return index_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitCreateEdgeIndex(MemgraphCypher::CreateEdgeIndexContext *ctx) {
|
||||
auto *index_query = storage_->Create<EdgeIndexQuery>();
|
||||
index_query->action_ = EdgeIndexQuery::Action::CREATE;
|
||||
index_query->edge_type_ = AddEdgeType(std::any_cast<std::string>(ctx->labelName()->accept(this)));
|
||||
return index_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitDropEdgeIndex(MemgraphCypher::DropEdgeIndexContext *ctx) {
|
||||
auto *index_query = storage_->Create<EdgeIndexQuery>();
|
||||
index_query->action_ = EdgeIndexQuery::Action::DROP;
|
||||
index_query->edge_type_ = AddEdgeType(std::any_cast<std::string>(ctx->labelName()->accept(this)));
|
||||
return index_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitAuthQuery(MemgraphCypher::AuthQueryContext *ctx) {
|
||||
MG_ASSERT(ctx->children.size() == 1, "AuthQuery should have exactly one child!");
|
||||
auto *auth_query = std::any_cast<AuthQuery *>(ctx->children[0]->accept(this));
|
||||
|
@ -148,6 +148,11 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
||||
*/
|
||||
antlrcpp::Any visitIndexQuery(MemgraphCypher::IndexQueryContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return IndexQuery*
|
||||
*/
|
||||
antlrcpp::Any visitEdgeIndexQuery(MemgraphCypher::EdgeIndexQueryContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return ExplainQuery*
|
||||
*/
|
||||
@ -499,6 +504,16 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
||||
*/
|
||||
antlrcpp::Any visitDropIndex(MemgraphCypher::DropIndexContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return EdgeIndexQuery*
|
||||
*/
|
||||
antlrcpp::Any visitCreateEdgeIndex(MemgraphCypher::CreateEdgeIndexContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return DropEdgeIndex*
|
||||
*/
|
||||
antlrcpp::Any visitDropEdgeIndex(MemgraphCypher::DropEdgeIndexContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return AuthQuery*
|
||||
*/
|
||||
|
@ -133,6 +133,7 @@ symbolicName : UnescapedSymbolicName
|
||||
|
||||
query : cypherQuery
|
||||
| indexQuery
|
||||
| edgeIndexQuery
|
||||
| explainQuery
|
||||
| profileQuery
|
||||
| databaseInfoQuery
|
||||
@ -527,3 +528,9 @@ showDatabase : SHOW DATABASE ;
|
||||
showDatabases : SHOW DATABASES ;
|
||||
|
||||
edgeImportModeQuery : EDGE IMPORT MODE ( ACTIVE | INACTIVE ) ;
|
||||
|
||||
createEdgeIndex : CREATE EDGE INDEX ON ':' labelName ;
|
||||
|
||||
dropEdgeIndex : DROP EDGE INDEX ON ':' labelName ;
|
||||
|
||||
edgeIndexQuery : createEdgeIndex | dropEdgeIndex ;
|
||||
|
@ -27,6 +27,8 @@ class PrivilegeExtractor : public QueryVisitor<void>, public HierarchicalTreeVis
|
||||
|
||||
void Visit(IndexQuery & /*unused*/) override { AddPrivilege(AuthQuery::Privilege::INDEX); }
|
||||
|
||||
void Visit(EdgeIndexQuery & /*unused*/) override { AddPrivilege(AuthQuery::Privilege::INDEX); }
|
||||
|
||||
void Visit(AnalyzeGraphQuery & /*unused*/) override { AddPrivilege(AuthQuery::Privilege::INDEX); }
|
||||
|
||||
void Visit(AuthQuery & /*unused*/) override { AddPrivilege(AuthQuery::Privilege::AUTH); }
|
||||
|
@ -53,6 +53,8 @@ class Symbol {
|
||||
bool user_declared() const { return user_declared_; }
|
||||
int token_position() const { return token_position_; }
|
||||
|
||||
bool IsSymbolAnonym() const { return name_.substr(0U, 4U) == "anon"; }
|
||||
|
||||
std::string name_;
|
||||
int64_t position_;
|
||||
bool user_declared_{true};
|
||||
|
@ -2679,6 +2679,75 @@ PreparedQuery PrepareIndexQuery(ParsedQuery parsed_query, bool in_explicit_trans
|
||||
RWType::W};
|
||||
}
|
||||
|
||||
PreparedQuery PrepareEdgeIndexQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
||||
std::vector<Notification> *notifications, CurrentDB ¤t_db) {
|
||||
if (in_explicit_transaction) {
|
||||
throw IndexInMulticommandTxException();
|
||||
}
|
||||
|
||||
auto *index_query = utils::Downcast<EdgeIndexQuery>(parsed_query.query);
|
||||
std::function<void(Notification &)> handler;
|
||||
|
||||
MG_ASSERT(current_db.db_acc_, "Index query expects a current DB");
|
||||
auto &db_acc = *current_db.db_acc_;
|
||||
|
||||
MG_ASSERT(current_db.db_transactional_accessor_, "Index query expects a current DB transaction");
|
||||
auto *dba = &*current_db.execution_db_accessor_;
|
||||
|
||||
auto invalidate_plan_cache = [plan_cache = db_acc->plan_cache()] {
|
||||
plan_cache->WithLock([&](auto &cache) { cache.reset(); });
|
||||
};
|
||||
|
||||
auto *storage = db_acc->storage();
|
||||
auto edge_type = storage->NameToEdgeType(index_query->edge_type_.name);
|
||||
|
||||
Notification index_notification(SeverityLevel::INFO);
|
||||
switch (index_query->action_) {
|
||||
case EdgeIndexQuery::Action::CREATE: {
|
||||
index_notification.code = NotificationCode::CREATE_INDEX;
|
||||
index_notification.title = fmt::format("Created index on edge-type {}.", index_query->edge_type_.name);
|
||||
|
||||
handler = [dba, edge_type, label_name = index_query->edge_type_.name,
|
||||
invalidate_plan_cache = std::move(invalidate_plan_cache)](Notification &index_notification) {
|
||||
auto maybe_index_error = dba->CreateIndex(edge_type);
|
||||
utils::OnScopeExit invalidator(invalidate_plan_cache);
|
||||
|
||||
if (maybe_index_error.HasError()) {
|
||||
index_notification.code = NotificationCode::EXISTENT_INDEX;
|
||||
index_notification.title = fmt::format("Index on edge-type {} already exists.", label_name);
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
case EdgeIndexQuery::Action::DROP: {
|
||||
index_notification.code = NotificationCode::DROP_INDEX;
|
||||
index_notification.title = fmt::format("Dropped index on edge-type {}.", index_query->edge_type_.name);
|
||||
handler = [dba, edge_type, label_name = index_query->edge_type_.name,
|
||||
invalidate_plan_cache = std::move(invalidate_plan_cache)](Notification &index_notification) {
|
||||
auto maybe_index_error = dba->DropIndex(edge_type);
|
||||
utils::OnScopeExit invalidator(invalidate_plan_cache);
|
||||
|
||||
if (maybe_index_error.HasError()) {
|
||||
index_notification.code = NotificationCode::NONEXISTENT_INDEX;
|
||||
index_notification.title = fmt::format("Index on edge-type {} doesn't exist.", label_name);
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return PreparedQuery{
|
||||
{},
|
||||
std::move(parsed_query.required_privileges),
|
||||
[handler = std::move(handler), notifications, index_notification = std::move(index_notification)](
|
||||
AnyStream * /*stream*/, std::optional<int> /*unused*/) mutable {
|
||||
handler(index_notification);
|
||||
notifications->push_back(index_notification);
|
||||
return QueryHandlerResult::COMMIT;
|
||||
},
|
||||
RWType::W};
|
||||
}
|
||||
|
||||
PreparedQuery PrepareAuthQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
||||
InterpreterContext *interpreter_context, Interpreter &interpreter) {
|
||||
if (in_explicit_transaction) {
|
||||
@ -3483,6 +3552,7 @@ PreparedQuery PrepareDatabaseInfoQuery(ParsedQuery parsed_query, bool in_explici
|
||||
auto *storage = database->storage();
|
||||
const std::string_view label_index_mark{"label"};
|
||||
const std::string_view label_property_index_mark{"label+property"};
|
||||
const std::string_view edge_type_index_mark{"edge-type"};
|
||||
auto info = dba->ListAllIndices();
|
||||
auto storage_acc = database->Access();
|
||||
std::vector<std::vector<TypedValue>> results;
|
||||
@ -3497,6 +3567,10 @@ PreparedQuery PrepareDatabaseInfoQuery(ParsedQuery parsed_query, bool in_explici
|
||||
TypedValue(storage->PropertyToName(item.second)),
|
||||
TypedValue(static_cast<int>(storage_acc->ApproximateVertexCount(item.first, item.second)))});
|
||||
}
|
||||
for (const auto &item : info.edge_type) {
|
||||
results.push_back({TypedValue(edge_type_index_mark), TypedValue(storage->EdgeTypeToName(item)), TypedValue(),
|
||||
TypedValue(static_cast<int>(storage_acc->ApproximateEdgeCount(item)))});
|
||||
}
|
||||
std::sort(results.begin(), results.end(), [&label_index_mark](const auto &record_1, const auto &record_2) {
|
||||
const auto type_1 = record_1[0].ValueString();
|
||||
const auto type_2 = record_2[0].ValueString();
|
||||
@ -4283,13 +4357,14 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
utils::Downcast<CypherQuery>(parsed_query.query) || utils::Downcast<ExplainQuery>(parsed_query.query) ||
|
||||
utils::Downcast<ProfileQuery>(parsed_query.query) || utils::Downcast<DumpQuery>(parsed_query.query) ||
|
||||
utils::Downcast<TriggerQuery>(parsed_query.query) || utils::Downcast<AnalyzeGraphQuery>(parsed_query.query) ||
|
||||
utils::Downcast<IndexQuery>(parsed_query.query) || utils::Downcast<DatabaseInfoQuery>(parsed_query.query) ||
|
||||
utils::Downcast<ConstraintQuery>(parsed_query.query);
|
||||
utils::Downcast<IndexQuery>(parsed_query.query) || utils::Downcast<EdgeIndexQuery>(parsed_query.query) ||
|
||||
utils::Downcast<DatabaseInfoQuery>(parsed_query.query) || utils::Downcast<ConstraintQuery>(parsed_query.query);
|
||||
|
||||
if (!in_explicit_transaction_ && requires_db_transaction) {
|
||||
// TODO: ATM only a single database, will change when we have multiple database transactions
|
||||
bool could_commit = utils::Downcast<CypherQuery>(parsed_query.query) != nullptr;
|
||||
bool unique = utils::Downcast<IndexQuery>(parsed_query.query) != nullptr ||
|
||||
utils::Downcast<EdgeIndexQuery>(parsed_query.query) != nullptr ||
|
||||
utils::Downcast<ConstraintQuery>(parsed_query.query) != nullptr ||
|
||||
upper_case_query.find(kSchemaAssert) != std::string::npos;
|
||||
SetupDatabaseTransaction(could_commit, unique);
|
||||
@ -4326,6 +4401,9 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
} else if (utils::Downcast<IndexQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareIndexQuery(std::move(parsed_query), in_explicit_transaction_,
|
||||
&query_execution->notifications, current_db_);
|
||||
} else if (utils::Downcast<EdgeIndexQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareEdgeIndexQuery(std::move(parsed_query), in_explicit_transaction_,
|
||||
&query_execution->notifications, current_db_);
|
||||
} else if (utils::Downcast<AnalyzeGraphQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareAnalyzeGraphQuery(std::move(parsed_query), in_explicit_transaction_, current_db_);
|
||||
} else if (utils::Downcast<AuthQuery>(parsed_query.query)) {
|
||||
|
@ -114,6 +114,9 @@ class PlanHintsProvider final : public HierarchicalLogicalOperatorVisitor {
|
||||
bool PreVisit(ScanAllById & /*unused*/) override { return true; }
|
||||
bool PostVisit(ScanAllById & /*unused*/) override { return true; }
|
||||
|
||||
bool PreVisit(ScanAllByEdgeType & /*unused*/) override { return true; }
|
||||
bool PostVisit(ScanAllByEdgeType & /*unused*/) override { return true; }
|
||||
|
||||
bool PreVisit(ConstructNamedPath & /*unused*/) override { return true; }
|
||||
bool PostVisit(ConstructNamedPath & /*unused*/) override { return true; }
|
||||
|
||||
|
@ -105,6 +105,7 @@ extern const Event ScanAllByLabelPropertyRangeOperator;
|
||||
extern const Event ScanAllByLabelPropertyValueOperator;
|
||||
extern const Event ScanAllByLabelPropertyOperator;
|
||||
extern const Event ScanAllByIdOperator;
|
||||
extern const Event ScanAllByEdgeTypeOperator;
|
||||
extern const Event ExpandOperator;
|
||||
extern const Event ExpandVariableOperator;
|
||||
extern const Event ConstructNamedPathOperator;
|
||||
@ -517,6 +518,60 @@ class ScanAllCursor : public Cursor {
|
||||
const char *op_name_;
|
||||
};
|
||||
|
||||
template <typename TEdgesFun>
|
||||
class ScanAllByEdgeTypeCursor : public Cursor {
|
||||
public:
|
||||
explicit ScanAllByEdgeTypeCursor(const ScanAllByEdgeType &self, Symbol output_symbol, UniqueCursorPtr input_cursor,
|
||||
storage::View view, TEdgesFun get_edges, const char *op_name)
|
||||
: self_(self),
|
||||
output_symbol_(std::move(output_symbol)),
|
||||
input_cursor_(std::move(input_cursor)),
|
||||
view_(view),
|
||||
get_edges_(std::move(get_edges)),
|
||||
op_name_(op_name) {}
|
||||
|
||||
bool Pull(Frame &frame, ExecutionContext &context) override {
|
||||
OOMExceptionEnabler oom_exception;
|
||||
SCOPED_PROFILE_OP_BY_REF(self_);
|
||||
|
||||
AbortCheck(context);
|
||||
|
||||
while (!vertices_ || vertices_it_.value() == vertices_end_it_.value()) {
|
||||
if (!input_cursor_->Pull(frame, context)) return false;
|
||||
auto next_vertices = get_edges_(frame, context);
|
||||
if (!next_vertices) continue;
|
||||
|
||||
vertices_.emplace(std::move(next_vertices.value()));
|
||||
vertices_it_.emplace(vertices_.value().begin());
|
||||
vertices_end_it_.emplace(vertices_.value().end());
|
||||
}
|
||||
|
||||
frame[output_symbol_] = *vertices_it_.value();
|
||||
++vertices_it_.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Shutdown() override { input_cursor_->Shutdown(); }
|
||||
|
||||
void Reset() override {
|
||||
input_cursor_->Reset();
|
||||
vertices_ = std::nullopt;
|
||||
vertices_it_ = std::nullopt;
|
||||
vertices_end_it_ = std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
const ScanAllByEdgeType &self_;
|
||||
const Symbol output_symbol_;
|
||||
const UniqueCursorPtr input_cursor_;
|
||||
storage::View view_;
|
||||
TEdgesFun get_edges_;
|
||||
std::optional<typename std::result_of<TEdgesFun(Frame &, ExecutionContext &)>::type::value_type> vertices_;
|
||||
std::optional<decltype(vertices_.value().begin())> vertices_it_;
|
||||
std::optional<decltype(vertices_.value().end())> vertices_end_it_;
|
||||
const char *op_name_;
|
||||
};
|
||||
|
||||
ScanAll::ScanAll(const std::shared_ptr<LogicalOperator> &input, Symbol output_symbol, storage::View view)
|
||||
: input_(input ? input : std::make_shared<Once>()), output_symbol_(std::move(output_symbol)), view_(view) {}
|
||||
|
||||
@ -556,6 +611,33 @@ UniqueCursorPtr ScanAllByLabel::MakeCursor(utils::MemoryResource *mem) const {
|
||||
view_, std::move(vertices), "ScanAllByLabel");
|
||||
}
|
||||
|
||||
ScanAllByEdgeType::ScanAllByEdgeType(const std::shared_ptr<LogicalOperator> &input, Symbol output_symbol,
|
||||
storage::EdgeTypeId edge_type, storage::View view)
|
||||
: input_(input ? input : std::make_shared<Once>()),
|
||||
output_symbol_(std::move(output_symbol)),
|
||||
view_(view),
|
||||
edge_type_(edge_type) {}
|
||||
|
||||
ACCEPT_WITH_INPUT(ScanAllByEdgeType)
|
||||
|
||||
UniqueCursorPtr ScanAllByEdgeType::MakeCursor(utils::MemoryResource *mem) const {
|
||||
memgraph::metrics::IncrementCounter(memgraph::metrics::ScanAllByEdgeTypeOperator);
|
||||
|
||||
auto edges = [this](Frame &, ExecutionContext &context) {
|
||||
auto *db = context.db_accessor;
|
||||
return std::make_optional(db->Edges(view_, edge_type_));
|
||||
};
|
||||
|
||||
return MakeUniqueCursorPtr<ScanAllByEdgeTypeCursor<decltype(edges)>>(
|
||||
mem, *this, output_symbol_, input_->MakeCursor(mem), view_, std::move(edges), "ScanAllByEdgeType");
|
||||
}
|
||||
|
||||
std::vector<Symbol> ScanAllByEdgeType::ModifiedSymbols(const SymbolTable &table) const {
|
||||
auto symbols = input_->ModifiedSymbols(table);
|
||||
symbols.emplace_back(output_symbol_);
|
||||
return symbols;
|
||||
}
|
||||
|
||||
// TODO(buda): Implement ScanAllByLabelProperty operator to iterate over
|
||||
// vertices that have the label and some value for the given property.
|
||||
|
||||
|
@ -99,6 +99,7 @@ class ScanAllByLabelPropertyRange;
|
||||
class ScanAllByLabelPropertyValue;
|
||||
class ScanAllByLabelProperty;
|
||||
class ScanAllById;
|
||||
class ScanAllByEdgeType;
|
||||
class Expand;
|
||||
class ExpandVariable;
|
||||
class ConstructNamedPath;
|
||||
@ -134,10 +135,10 @@ class RollUpApply;
|
||||
|
||||
using LogicalOperatorCompositeVisitor =
|
||||
utils::CompositeVisitor<Once, CreateNode, CreateExpand, ScanAll, ScanAllByLabel, ScanAllByLabelPropertyRange,
|
||||
ScanAllByLabelPropertyValue, ScanAllByLabelProperty, ScanAllById, Expand, ExpandVariable,
|
||||
ConstructNamedPath, Filter, Produce, Delete, SetProperty, SetProperties, SetLabels,
|
||||
RemoveProperty, RemoveLabels, EdgeUniquenessFilter, Accumulate, Aggregate, Skip, Limit,
|
||||
OrderBy, Merge, Optional, Unwind, Distinct, Union, Cartesian, CallProcedure, LoadCsv,
|
||||
ScanAllByLabelPropertyValue, ScanAllByLabelProperty, ScanAllById, ScanAllByEdgeType, Expand,
|
||||
ExpandVariable, ConstructNamedPath, Filter, Produce, Delete, SetProperty, SetProperties,
|
||||
SetLabels, RemoveProperty, RemoveLabels, EdgeUniquenessFilter, Accumulate, Aggregate, Skip,
|
||||
Limit, OrderBy, Merge, Optional, Unwind, Distinct, Union, Cartesian, CallProcedure, LoadCsv,
|
||||
Foreach, EmptyResult, EvaluatePatternFilter, Apply, IndexedJoin, HashJoin, RollUpApply>;
|
||||
|
||||
using LogicalOperatorLeafVisitor = utils::LeafVisitor<Once>;
|
||||
@ -592,6 +593,42 @@ class ScanAllByLabel : public memgraph::query::plan::ScanAll {
|
||||
}
|
||||
};
|
||||
|
||||
class ScanAllByEdgeType : public memgraph::query::plan::LogicalOperator {
|
||||
public:
|
||||
static const utils::TypeInfo kType;
|
||||
const utils::TypeInfo &GetTypeInfo() const override { return kType; }
|
||||
|
||||
ScanAllByEdgeType() = default;
|
||||
ScanAllByEdgeType(const std::shared_ptr<LogicalOperator> &input, Symbol output_symbol, storage::EdgeTypeId edge_type,
|
||||
storage::View view = storage::View::OLD);
|
||||
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
|
||||
UniqueCursorPtr MakeCursor(utils::MemoryResource *) const override;
|
||||
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
|
||||
|
||||
bool HasSingleInput() const override { return true; }
|
||||
std::shared_ptr<LogicalOperator> input() const override { return input_; }
|
||||
void set_input(std::shared_ptr<LogicalOperator> input) override { input_ = input; }
|
||||
|
||||
std::string ToString() const override {
|
||||
return fmt::format("ScanAllByEdgeType ({} :{})", output_symbol_.name(), dba_->EdgeTypeToName(edge_type_));
|
||||
}
|
||||
|
||||
std::shared_ptr<memgraph::query::plan::LogicalOperator> input_;
|
||||
Symbol output_symbol_;
|
||||
storage::View view_;
|
||||
|
||||
storage::EdgeTypeId edge_type_;
|
||||
|
||||
std::unique_ptr<LogicalOperator> Clone(AstStorage *storage) const override {
|
||||
auto object = std::make_unique<ScanAllByEdgeType>();
|
||||
object->input_ = input_ ? input_->Clone(storage) : nullptr;
|
||||
object->output_symbol_ = output_symbol_;
|
||||
object->view_ = view_;
|
||||
object->edge_type_ = edge_type_;
|
||||
return object;
|
||||
}
|
||||
};
|
||||
|
||||
/// Behaves like @c ScanAll, but produces only vertices with given label and
|
||||
/// property value which is inside a range (inclusive or exlusive).
|
||||
///
|
||||
|
@ -49,6 +49,8 @@ constexpr utils::TypeInfo query::plan::ScanAllByLabelProperty::kType{
|
||||
|
||||
constexpr utils::TypeInfo query::plan::ScanAllById::kType{utils::TypeId::SCAN_ALL_BY_ID, "ScanAllById",
|
||||
&query::plan::ScanAll::kType};
|
||||
constexpr utils::TypeInfo query::plan::ScanAllByEdgeType::kType{utils::TypeId::SCAN_ALL_BY_EDGE_TYPE,
|
||||
"ScanAllByEdgeType", &query::plan::ScanAll::kType};
|
||||
|
||||
constexpr utils::TypeInfo query::plan::ExpandCommon::kType{utils::TypeId::EXPAND_COMMON, "ExpandCommon", nullptr};
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "query/plan/operator.hpp"
|
||||
#include "query/plan/preprocess.hpp"
|
||||
#include "query/plan/pretty_print.hpp"
|
||||
#include "query/plan/rewrite/edge_type_index_lookup.hpp"
|
||||
#include "query/plan/rewrite/index_lookup.hpp"
|
||||
#include "query/plan/rewrite/join.hpp"
|
||||
#include "query/plan/rule_based_planner.hpp"
|
||||
@ -54,8 +55,11 @@ class PostProcessor final {
|
||||
std::unique_ptr<LogicalOperator> Rewrite(std::unique_ptr<LogicalOperator> plan, TPlanningContext *context) {
|
||||
auto index_lookup_plan =
|
||||
RewriteWithIndexLookup(std::move(plan), context->symbol_table, context->ast_storage, context->db, index_hints_);
|
||||
return RewriteWithJoinRewriter(std::move(index_lookup_plan), context->symbol_table, context->ast_storage,
|
||||
context->db);
|
||||
auto join_plan =
|
||||
RewriteWithJoinRewriter(std::move(index_lookup_plan), context->symbol_table, context->ast_storage, context->db);
|
||||
auto edge_index_plan = RewriteWithEdgeTypeIndexRewriter(std::move(join_plan), context->symbol_table,
|
||||
context->ast_storage, context->db);
|
||||
return edge_index_plan;
|
||||
}
|
||||
|
||||
template <class TVertexCounts>
|
||||
|
@ -76,6 +76,13 @@ bool PlanPrinter::PreVisit(ScanAllById &op) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlanPrinter::PreVisit(query::plan::ScanAllByEdgeType &op) {
|
||||
op.dba_ = dba_;
|
||||
WithPrintLn([&op](auto &out) { out << "* " << op.ToString(); });
|
||||
op.dba_ = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlanPrinter::PreVisit(query::plan::Expand &op) {
|
||||
op.dba_ = dba_;
|
||||
WithPrintLn([&op](auto &out) { out << "* " << op.ToString(); });
|
||||
@ -464,6 +471,19 @@ bool PlanToJsonVisitor::PreVisit(ScanAllById &op) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PlanToJsonVisitor::PreVisit(ScanAllByEdgeType &op) {
|
||||
json self;
|
||||
self["name"] = "ScanAllByEdgeType";
|
||||
self["edge_type"] = ToJson(op.edge_type_, *dba_);
|
||||
self["output_symbol"] = ToJson(op.output_symbol_);
|
||||
|
||||
op.input_->Accept(*this);
|
||||
self["input"] = PopOutput();
|
||||
|
||||
output_ = std::move(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PlanToJsonVisitor::PreVisit(CreateNode &op) {
|
||||
json self;
|
||||
self["name"] = "CreateNode";
|
||||
|
@ -67,6 +67,7 @@ class PlanPrinter : public virtual HierarchicalLogicalOperatorVisitor {
|
||||
bool PreVisit(ScanAllByLabelPropertyRange &) override;
|
||||
bool PreVisit(ScanAllByLabelProperty &) override;
|
||||
bool PreVisit(ScanAllById &) override;
|
||||
bool PreVisit(ScanAllByEdgeType &) override;
|
||||
|
||||
bool PreVisit(Expand &) override;
|
||||
bool PreVisit(ExpandVariable &) override;
|
||||
@ -204,6 +205,7 @@ class PlanToJsonVisitor : public virtual HierarchicalLogicalOperatorVisitor {
|
||||
bool PreVisit(ScanAllByLabelPropertyValue &) override;
|
||||
bool PreVisit(ScanAllByLabelProperty &) override;
|
||||
bool PreVisit(ScanAllById &) override;
|
||||
bool PreVisit(ScanAllByEdgeType &) override;
|
||||
|
||||
bool PreVisit(EmptyResult &) override;
|
||||
bool PreVisit(Produce &) override;
|
||||
|
534
src/query/plan/rewrite/edge_type_index_lookup.hpp
Normal file
534
src/query/plan/rewrite/edge_type_index_lookup.hpp
Normal file
@ -0,0 +1,534 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
/// @file
|
||||
/// This file provides a plan rewriter which replaces `ScanAll` and `Expand`
|
||||
/// operations with `ScanAllByEdgeType` if possible. The public entrypoint is
|
||||
/// `RewriteWithEdgeTypeIndexRewriter`.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include "query/plan/operator.hpp"
|
||||
#include "query/plan/preprocess.hpp"
|
||||
#include "query/plan/rewrite/index_lookup.hpp"
|
||||
#include "utils/algorithm.hpp"
|
||||
|
||||
namespace memgraph::query::plan {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template <class TDbAccessor>
|
||||
class EdgeTypeIndexRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
public:
|
||||
EdgeTypeIndexRewriter(SymbolTable *symbol_table, AstStorage *ast_storage, TDbAccessor *db)
|
||||
: symbol_table_(symbol_table), ast_storage_(ast_storage), db_(db) {}
|
||||
|
||||
using HierarchicalLogicalOperatorVisitor::PostVisit;
|
||||
using HierarchicalLogicalOperatorVisitor::PreVisit;
|
||||
using HierarchicalLogicalOperatorVisitor::Visit;
|
||||
|
||||
bool Visit(Once &) override { return true; }
|
||||
|
||||
bool PreVisit(Filter &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PostVisit(Filter & /*op*/) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(ScanAll &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
|
||||
if (op.input()->GetTypeInfo() == Once::kType) {
|
||||
const bool is_node_anon = op.output_symbol_.IsSymbolAnonym();
|
||||
once_under_scanall_ = is_node_anon;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PostVisit(ScanAll &op) override {
|
||||
prev_ops_.pop_back();
|
||||
|
||||
if (EdgeTypeIndexingPossible()) {
|
||||
SetOnParent(op.input());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Expand &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
|
||||
if (op.input()->GetTypeInfo() == ScanAll::kType) {
|
||||
const bool only_one_edge_type = (op.common_.edge_types.size() == 1U);
|
||||
const bool expansion_is_named = !(op.common_.edge_symbol.IsSymbolAnonym());
|
||||
const bool expdanded_node_not_named = op.common_.node_symbol.IsSymbolAnonym();
|
||||
|
||||
edge_type_index_exist = only_one_edge_type ? db_->EdgeTypeIndexExists(op.common_.edge_types.front()) : false;
|
||||
|
||||
scanall_under_expand_ = only_one_edge_type && expansion_is_named && expdanded_node_not_named;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PostVisit(Expand &op) override {
|
||||
prev_ops_.pop_back();
|
||||
|
||||
if (EdgeTypeIndexingPossible()) {
|
||||
auto indexed_scan = GenEdgeTypeScan(op);
|
||||
SetOnParent(std::move(indexed_scan));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(ExpandVariable &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PostVisit(ExpandVariable &expand) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Merge &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
op.input()->Accept(*this);
|
||||
RewriteBranch(&op.merge_match_);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PostVisit(Merge &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Optional &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
op.input()->Accept(*this);
|
||||
RewriteBranch(&op.optional_);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PostVisit(Optional &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Cartesian &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PostVisit(Cartesian &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(IndexedJoin &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
RewriteBranch(&op.main_branch_);
|
||||
RewriteBranch(&op.sub_branch_);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PostVisit(IndexedJoin &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(HashJoin &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PostVisit(HashJoin &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Union &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
RewriteBranch(&op.left_op_);
|
||||
RewriteBranch(&op.right_op_);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PostVisit(Union &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(CreateNode &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(CreateNode &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(CreateExpand &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(CreateExpand &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(ScanAllByLabel &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(ScanAllByLabel &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(ScanAllByLabelPropertyRange &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(ScanAllByLabelPropertyRange &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(ScanAllByLabelPropertyValue &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(ScanAllByLabelPropertyValue &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(ScanAllByLabelProperty &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(ScanAllByLabelProperty &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(ScanAllById &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(ScanAllById &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(ScanAllByEdgeType &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(ScanAllByEdgeType &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(ConstructNamedPath &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(ConstructNamedPath &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Produce &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
|
||||
if (op.input()->GetTypeInfo() == Expand::kType) {
|
||||
expand_under_produce_ = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(Produce &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(EmptyResult &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(EmptyResult &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Delete &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(Delete &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(SetProperty &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(SetProperty &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(SetProperties &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(SetProperties &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(SetLabels &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(SetLabels &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(RemoveProperty &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(RemoveProperty &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(RemoveLabels &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(RemoveLabels &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(EdgeUniquenessFilter &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(EdgeUniquenessFilter &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Accumulate &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(Accumulate &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Aggregate &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(Aggregate &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Skip &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(Skip &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Limit &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(Limit &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(OrderBy &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(OrderBy &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Unwind &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(Unwind &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Distinct &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(Distinct &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(CallProcedure &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
bool PostVisit(CallProcedure &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Foreach &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
op.input()->Accept(*this);
|
||||
RewriteBranch(&op.update_clauses_);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PostVisit(Foreach &) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(EvaluatePatternFilter &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PostVisit(EvaluatePatternFilter & /*op*/) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(Apply &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
op.input()->Accept(*this);
|
||||
RewriteBranch(&op.subquery_);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PostVisit(Apply & /*op*/) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PreVisit(LoadCsv &op) override {
|
||||
prev_ops_.push_back(&op);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PostVisit(LoadCsv & /*op*/) override {
|
||||
prev_ops_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<LogicalOperator> new_root_;
|
||||
|
||||
private:
|
||||
SymbolTable *symbol_table_;
|
||||
AstStorage *ast_storage_;
|
||||
TDbAccessor *db_;
|
||||
// Collected filters, pending for examination if they can be used for advanced
|
||||
// lookup operations (by index, node ID, ...).
|
||||
Filters filters_;
|
||||
// Expressions which no longer need a plain Filter operator.
|
||||
std::unordered_set<Expression *> filter_exprs_for_removal_;
|
||||
std::vector<LogicalOperator *> prev_ops_;
|
||||
std::unordered_set<Symbol> cartesian_symbols_;
|
||||
|
||||
bool EdgeTypeIndexingPossible() const {
|
||||
return expand_under_produce_ && scanall_under_expand_ && once_under_scanall_ && edge_type_index_exist;
|
||||
}
|
||||
bool expand_under_produce_ = false;
|
||||
bool scanall_under_expand_ = false;
|
||||
bool once_under_scanall_ = false;
|
||||
bool edge_type_index_exist = false;
|
||||
|
||||
bool DefaultPreVisit() override {
|
||||
throw utils::NotYetImplemented("Operator not yet covered by EdgeTypeIndexRewriter");
|
||||
}
|
||||
|
||||
std::unique_ptr<ScanAllByEdgeType> GenEdgeTypeScan(const Expand &expand) {
|
||||
const auto &input = expand.input();
|
||||
const auto &output_symbol = expand.common_.edge_symbol;
|
||||
const auto &view = expand.view_;
|
||||
|
||||
// Extract edge_type from symbol
|
||||
auto edge_type = expand.common_.edge_types.front();
|
||||
return std::make_unique<ScanAllByEdgeType>(input, output_symbol, edge_type, view);
|
||||
}
|
||||
|
||||
void SetOnParent(const std::shared_ptr<LogicalOperator> &input) {
|
||||
MG_ASSERT(input);
|
||||
if (prev_ops_.empty()) {
|
||||
MG_ASSERT(!new_root_);
|
||||
new_root_ = input;
|
||||
return;
|
||||
}
|
||||
prev_ops_.back()->set_input(input);
|
||||
}
|
||||
|
||||
void RewriteBranch(std::shared_ptr<LogicalOperator> *branch) {
|
||||
EdgeTypeIndexRewriter<TDbAccessor> rewriter(symbol_table_, ast_storage_, db_);
|
||||
(*branch)->Accept(rewriter);
|
||||
if (rewriter.new_root_) {
|
||||
*branch = rewriter.new_root_;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
template <class TDbAccessor>
|
||||
std::unique_ptr<LogicalOperator> RewriteWithEdgeTypeIndexRewriter(std::unique_ptr<LogicalOperator> root_op,
|
||||
SymbolTable *symbol_table, AstStorage *ast_storage,
|
||||
TDbAccessor *db) {
|
||||
impl::EdgeTypeIndexRewriter<TDbAccessor> rewriter(symbol_table, ast_storage, db);
|
||||
root_op->Accept(rewriter);
|
||||
return root_op;
|
||||
}
|
||||
|
||||
} // namespace memgraph::query::plan
|
@ -78,6 +78,8 @@ class VertexCountCache {
|
||||
return db_->LabelPropertyIndexExists(label, property);
|
||||
}
|
||||
|
||||
bool EdgeTypeIndexExists(storage::EdgeTypeId edge_type) { return db_->EdgeTypeIndexExists(edge_type); }
|
||||
|
||||
std::optional<storage::LabelIndexStats> GetIndexStats(const storage::LabelId &label) const {
|
||||
return db_->GetIndexStats(label);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
|
@ -21,8 +21,10 @@ add_library(mg-storage-v2 STATIC
|
||||
storage.cpp
|
||||
indices/indices.cpp
|
||||
all_vertices_iterable.cpp
|
||||
edges_iterable.cpp
|
||||
vertices_iterable.cpp
|
||||
inmemory/storage.cpp
|
||||
inmemory/edge_type_index.cpp
|
||||
inmemory/label_index.cpp
|
||||
inmemory/label_property_index.cpp
|
||||
inmemory/unique_constraints.cpp
|
||||
@ -30,6 +32,7 @@ add_library(mg-storage-v2 STATIC
|
||||
disk/edge_import_mode_cache.cpp
|
||||
disk/storage.cpp
|
||||
disk/rocksdb_storage.cpp
|
||||
disk/edge_type_index.cpp
|
||||
disk/label_index.cpp
|
||||
disk/label_property_index.cpp
|
||||
disk/unique_constraints.cpp
|
||||
|
49
src/storage/v2/disk/edge_type_index.cpp
Normal file
49
src/storage/v2/disk/edge_type_index.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
#include "edge_type_index.hpp"
|
||||
|
||||
#include "utils/exceptions.hpp"
|
||||
|
||||
namespace memgraph::storage {
|
||||
|
||||
bool DiskEdgeTypeIndex::DropIndex(EdgeTypeId /*edge_type*/) {
|
||||
spdlog::warn("Edge-type index related operations are not yet supported using on-disk storage mode.");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskEdgeTypeIndex::IndexExists(EdgeTypeId /*edge_type*/) const {
|
||||
spdlog::warn("Edge-type index related operations are not yet supported using on-disk storage mode.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<EdgeTypeId> DiskEdgeTypeIndex::ListIndices() const {
|
||||
spdlog::warn("Edge-type index related operations are not yet supported using on-disk storage mode.");
|
||||
return {};
|
||||
}
|
||||
|
||||
uint64_t DiskEdgeTypeIndex::ApproximateEdgeCount(EdgeTypeId /*edge_type*/) const {
|
||||
spdlog::warn("Edge-type index related operations are not yet supported using on-disk storage mode.");
|
||||
return 0U;
|
||||
}
|
||||
|
||||
void DiskEdgeTypeIndex::UpdateOnEdgeCreation(Vertex * /*from*/, Vertex * /*to*/, EdgeRef /*edge_ref*/,
|
||||
EdgeTypeId /*edge_type*/, const Transaction & /*tx*/) {
|
||||
spdlog::warn("Edge-type index related operations are not yet supported using on-disk storage mode.");
|
||||
}
|
||||
|
||||
void DiskEdgeTypeIndex::UpdateOnEdgeModification(Vertex * /*old_from*/, Vertex * /*old_to*/, Vertex * /*new_from*/,
|
||||
Vertex * /*new_to*/, EdgeRef /*edge_ref*/, EdgeTypeId /*edge_type*/,
|
||||
const Transaction & /*tx*/) {
|
||||
spdlog::warn("Edge-type index related operations are not yet supported using on-disk storage mode.");
|
||||
}
|
||||
|
||||
} // namespace memgraph::storage
|
35
src/storage/v2/disk/edge_type_index.hpp
Normal file
35
src/storage/v2/disk/edge_type_index.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2024 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "storage/v2/indices/edge_type_index.hpp"
|
||||
|
||||
namespace memgraph::storage {
|
||||
|
||||
class DiskEdgeTypeIndex : public storage::EdgeTypeIndex {
|
||||
public:
|
||||
bool DropIndex(EdgeTypeId edge_type) override;
|
||||
|
||||
bool IndexExists(EdgeTypeId edge_type) const override;
|
||||
|
||||
std::vector<EdgeTypeId> ListIndices() const override;
|
||||
|
||||
uint64_t ApproximateEdgeCount(EdgeTypeId edge_type) const override;
|
||||
|
||||
void UpdateOnEdgeCreation(Vertex *from, Vertex *to, EdgeRef edge_ref, EdgeTypeId edge_type,
|
||||
const Transaction &tx) override;
|
||||
|
||||
void UpdateOnEdgeModification(Vertex *old_from, Vertex *old_to, Vertex *new_from, Vertex *new_to, EdgeRef edge_ref,
|
||||
EdgeTypeId edge_type, const Transaction &tx) override;
|
||||
};
|
||||
|
||||
} // namespace memgraph::storage
|
@ -41,6 +41,7 @@
|
||||
#include "storage/v2/edge_accessor.hpp"
|
||||
#include "storage/v2/edge_import_mode.hpp"
|
||||
#include "storage/v2/edge_ref.hpp"
|
||||
#include "storage/v2/edges_iterable.hpp"
|
||||
#include "storage/v2/id_types.hpp"
|
||||
#include "storage/v2/modified_edge.hpp"
|
||||
#include "storage/v2/mvcc.hpp"
|
||||
@ -807,11 +808,21 @@ void DiskStorage::LoadVerticesFromDiskLabelPropertyIndexForIntervalSearch(
|
||||
}
|
||||
}
|
||||
|
||||
EdgesIterable DiskStorage::DiskAccessor::Edges(EdgeTypeId /*edge_type*/, View /*view*/) {
|
||||
throw utils::NotYetImplemented(
|
||||
"Edge-type index related operations are not yet supported using on-disk storage mode.");
|
||||
}
|
||||
|
||||
uint64_t DiskStorage::DiskAccessor::ApproximateVertexCount() const {
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
return disk_storage->vertex_count_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
uint64_t DiskStorage::DiskAccessor::ApproximateEdgeCount(EdgeTypeId /*edge_type*/) const {
|
||||
spdlog::info("Edge-type index related operations are not yet supported using on-disk storage mode.");
|
||||
return 0U;
|
||||
}
|
||||
|
||||
uint64_t DiskStorage::GetDiskSpaceUsage() const {
|
||||
uint64_t main_disk_storage_size = utils::GetDirDiskUsage(config_.disk.main_storage_directory);
|
||||
uint64_t index_disk_storage_size = utils::GetDirDiskUsage(config_.disk.label_index_directory) +
|
||||
@ -1629,6 +1640,9 @@ utils::BasicResult<StorageManipulationError, void> DiskStorage::DiskAccessor::Co
|
||||
return StorageManipulationError{PersistenceError{}};
|
||||
}
|
||||
} break;
|
||||
case MetadataDelta::Action::EDGE_INDEX_CREATE: {
|
||||
throw utils::NotYetImplemented("Edge-type indexing is not yet implemented on on-disk storage mode.");
|
||||
}
|
||||
case MetadataDelta::Action::LABEL_INDEX_DROP: {
|
||||
if (!disk_storage->durable_metadata_.PersistLabelIndexDeletion(md_delta.label)) {
|
||||
return StorageManipulationError{PersistenceError{}};
|
||||
@ -1641,6 +1655,9 @@ utils::BasicResult<StorageManipulationError, void> DiskStorage::DiskAccessor::Co
|
||||
return StorageManipulationError{PersistenceError{}};
|
||||
}
|
||||
} break;
|
||||
case MetadataDelta::Action::EDGE_INDEX_DROP: {
|
||||
throw utils::NotYetImplemented("Edge-type indexing is not yet implemented on on-disk storage mode.");
|
||||
}
|
||||
case MetadataDelta::Action::LABEL_INDEX_STATS_SET: {
|
||||
throw utils::NotYetImplemented("SetIndexStats(stats) is not implemented for DiskStorage.");
|
||||
} break;
|
||||
@ -1917,6 +1934,11 @@ utils::BasicResult<StorageIndexDefinitionError, void> DiskStorage::DiskAccessor:
|
||||
return {};
|
||||
}
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> DiskStorage::DiskAccessor::CreateIndex(EdgeTypeId /*edge_type*/) {
|
||||
throw utils::NotYetImplemented(
|
||||
"Edge-type index related operations are not yet supported using on-disk storage mode.");
|
||||
}
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> DiskStorage::DiskAccessor::DropIndex(LabelId label) {
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Create index requires a unique access to the storage!");
|
||||
auto *on_disk = static_cast<DiskStorage *>(storage_);
|
||||
@ -1945,6 +1967,11 @@ utils::BasicResult<StorageIndexDefinitionError, void> DiskStorage::DiskAccessor:
|
||||
return {};
|
||||
}
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> DiskStorage::DiskAccessor::DropIndex(EdgeTypeId /*edge_type*/) {
|
||||
throw utils::NotYetImplemented(
|
||||
"Edge-type index related operations are not yet supported using on-disk storage mode.");
|
||||
}
|
||||
|
||||
utils::BasicResult<StorageExistenceConstraintDefinitionError, void>
|
||||
DiskStorage::DiskAccessor::CreateExistenceConstraint(LabelId label, PropertyId property) {
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Create existence constraint requires a unique access to the storage!");
|
||||
@ -2053,6 +2080,12 @@ std::unique_ptr<Storage::Accessor> DiskStorage::UniqueAccess(
|
||||
return std::unique_ptr<DiskAccessor>(
|
||||
new DiskAccessor{Storage::Accessor::unique_access, this, isolation_level, storage_mode_});
|
||||
}
|
||||
|
||||
bool DiskStorage::DiskAccessor::EdgeTypeIndexExists(EdgeTypeId /*edge_type*/) const {
|
||||
spdlog::info("Edge-type index related operations are not yet supported using on-disk storage mode.");
|
||||
return false;
|
||||
}
|
||||
|
||||
IndicesInfo DiskStorage::DiskAccessor::ListAllIndices() const {
|
||||
auto *on_disk = static_cast<DiskStorage *>(storage_);
|
||||
auto *disk_label_index = static_cast<DiskLabelIndex *>(on_disk->indices_.label_index_.get());
|
||||
|
@ -72,6 +72,8 @@ class DiskStorage final : public Storage {
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) override;
|
||||
|
||||
EdgesIterable Edges(EdgeTypeId edge_type, View view) override;
|
||||
|
||||
uint64_t ApproximateVertexCount() const override;
|
||||
|
||||
uint64_t ApproximateVertexCount(LabelId /*label*/) const override { return 10; }
|
||||
@ -89,6 +91,8 @@ class DiskStorage final : public Storage {
|
||||
return 10;
|
||||
}
|
||||
|
||||
uint64_t ApproximateEdgeCount(EdgeTypeId edge_type) const override;
|
||||
|
||||
std::optional<storage::LabelIndexStats> GetIndexStats(const storage::LabelId & /*label*/) const override {
|
||||
return {};
|
||||
}
|
||||
@ -140,6 +144,8 @@ class DiskStorage final : public Storage {
|
||||
return disk_storage->indices_.label_property_index_->IndexExists(label, property);
|
||||
}
|
||||
|
||||
bool EdgeTypeIndexExists(EdgeTypeId edge_type) const override;
|
||||
|
||||
IndicesInfo ListAllIndices() const override;
|
||||
|
||||
ConstraintsInfo ListAllConstraints() const override;
|
||||
@ -158,10 +164,14 @@ class DiskStorage final : public Storage {
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(LabelId label, PropertyId property) override;
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(EdgeTypeId edge_type) override;
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(LabelId label) override;
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(LabelId label, PropertyId property) override;
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(EdgeTypeId edge_type) override;
|
||||
|
||||
utils::BasicResult<StorageExistenceConstraintDefinitionError, void> CreateExistenceConstraint(
|
||||
LabelId label, PropertyId property) override;
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "storage/v2/durability/paths.hpp"
|
||||
#include "storage/v2/durability/snapshot.hpp"
|
||||
#include "storage/v2/durability/wal.hpp"
|
||||
#include "storage/v2/inmemory/edge_type_index.hpp"
|
||||
#include "storage/v2/inmemory/label_index.hpp"
|
||||
#include "storage/v2/inmemory/label_property_index.hpp"
|
||||
#include "storage/v2/inmemory/unique_constraints.hpp"
|
||||
@ -199,9 +200,18 @@ void RecoverIndicesAndStats(const RecoveredIndicesAndConstraints::IndicesMetadat
|
||||
}
|
||||
spdlog::info("Label+property indices statistics are recreated.");
|
||||
|
||||
spdlog::info("Indices are recreated.");
|
||||
// Recover edge-type indices.
|
||||
spdlog::info("Recreating {} edge-type indices from metadata.", indices_metadata.edge.size());
|
||||
auto *mem_edge_type_index = static_cast<InMemoryEdgeTypeIndex *>(indices->edge_type_index_.get());
|
||||
for (const auto &item : indices_metadata.edge) {
|
||||
if (!mem_edge_type_index->CreateIndex(item, vertices->access())) {
|
||||
throw RecoveryFailure("The edge-type index must be created here!");
|
||||
}
|
||||
spdlog::info("Index on :{} is recreated from metadata", name_id_mapper->IdToName(item.AsUint()));
|
||||
}
|
||||
spdlog::info("Edge-type indices are recreated.");
|
||||
|
||||
spdlog::info("Recreating constraints from metadata.");
|
||||
spdlog::info("Indices are recreated.");
|
||||
}
|
||||
|
||||
void RecoverExistenceConstraints(const RecoveredIndicesAndConstraints::ConstraintsMetadata &constraints_metadata,
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -37,6 +37,8 @@ enum class Marker : uint8_t {
|
||||
SECTION_CONSTRAINTS = 0x25,
|
||||
SECTION_DELTA = 0x26,
|
||||
SECTION_EPOCH_HISTORY = 0x27,
|
||||
SECTION_EDGE_INDICES = 0x28,
|
||||
|
||||
SECTION_OFFSETS = 0x42,
|
||||
|
||||
DELTA_VERTEX_CREATE = 0x50,
|
||||
@ -60,6 +62,8 @@ enum class Marker : uint8_t {
|
||||
DELTA_LABEL_INDEX_STATS_CLEAR = 0x62,
|
||||
DELTA_LABEL_PROPERTY_INDEX_STATS_SET = 0x63,
|
||||
DELTA_LABEL_PROPERTY_INDEX_STATS_CLEAR = 0x64,
|
||||
DELTA_EDGE_TYPE_INDEX_CREATE = 0x65,
|
||||
DELTA_EDGE_TYPE_INDEX_DROP = 0x66,
|
||||
|
||||
VALUE_FALSE = 0x00,
|
||||
VALUE_TRUE = 0xff,
|
||||
@ -85,6 +89,7 @@ static const Marker kMarkersAll[] = {
|
||||
Marker::SECTION_CONSTRAINTS,
|
||||
Marker::SECTION_DELTA,
|
||||
Marker::SECTION_EPOCH_HISTORY,
|
||||
Marker::SECTION_EDGE_INDICES,
|
||||
Marker::SECTION_OFFSETS,
|
||||
Marker::DELTA_VERTEX_CREATE,
|
||||
Marker::DELTA_VERTEX_DELETE,
|
||||
@ -103,6 +108,8 @@ static const Marker kMarkersAll[] = {
|
||||
Marker::DELTA_LABEL_PROPERTY_INDEX_STATS_CLEAR,
|
||||
Marker::DELTA_LABEL_PROPERTY_INDEX_CREATE,
|
||||
Marker::DELTA_LABEL_PROPERTY_INDEX_DROP,
|
||||
Marker::DELTA_EDGE_TYPE_INDEX_CREATE,
|
||||
Marker::DELTA_EDGE_TYPE_INDEX_DROP,
|
||||
Marker::DELTA_EXISTENCE_CONSTRAINT_CREATE,
|
||||
Marker::DELTA_EXISTENCE_CONSTRAINT_DROP,
|
||||
Marker::DELTA_UNIQUE_CONSTRAINT_CREATE,
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -43,6 +43,7 @@ struct RecoveredIndicesAndConstraints {
|
||||
std::vector<std::pair<LabelId, PropertyId>> label_property;
|
||||
std::vector<std::pair<LabelId, LabelIndexStats>> label_stats;
|
||||
std::vector<std::pair<LabelId, std::pair<PropertyId, LabelPropertyIndexStats>>> label_property_stats;
|
||||
std::vector<EdgeTypeId> edge;
|
||||
} indices;
|
||||
|
||||
struct ConstraintsMetadata {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -332,6 +332,7 @@ std::optional<PropertyValue> Decoder::ReadPropertyValue() {
|
||||
case Marker::SECTION_CONSTRAINTS:
|
||||
case Marker::SECTION_DELTA:
|
||||
case Marker::SECTION_EPOCH_HISTORY:
|
||||
case Marker::SECTION_EDGE_INDICES:
|
||||
case Marker::SECTION_OFFSETS:
|
||||
case Marker::DELTA_VERTEX_CREATE:
|
||||
case Marker::DELTA_VERTEX_DELETE:
|
||||
@ -350,6 +351,8 @@ std::optional<PropertyValue> Decoder::ReadPropertyValue() {
|
||||
case Marker::DELTA_LABEL_PROPERTY_INDEX_STATS_CLEAR:
|
||||
case Marker::DELTA_LABEL_PROPERTY_INDEX_CREATE:
|
||||
case Marker::DELTA_LABEL_PROPERTY_INDEX_DROP:
|
||||
case Marker::DELTA_EDGE_TYPE_INDEX_CREATE:
|
||||
case Marker::DELTA_EDGE_TYPE_INDEX_DROP:
|
||||
case Marker::DELTA_EXISTENCE_CONSTRAINT_CREATE:
|
||||
case Marker::DELTA_EXISTENCE_CONSTRAINT_DROP:
|
||||
case Marker::DELTA_UNIQUE_CONSTRAINT_CREATE:
|
||||
@ -435,6 +438,7 @@ bool Decoder::SkipPropertyValue() {
|
||||
case Marker::SECTION_CONSTRAINTS:
|
||||
case Marker::SECTION_DELTA:
|
||||
case Marker::SECTION_EPOCH_HISTORY:
|
||||
case Marker::SECTION_EDGE_INDICES:
|
||||
case Marker::SECTION_OFFSETS:
|
||||
case Marker::DELTA_VERTEX_CREATE:
|
||||
case Marker::DELTA_VERTEX_DELETE:
|
||||
@ -453,6 +457,8 @@ bool Decoder::SkipPropertyValue() {
|
||||
case Marker::DELTA_LABEL_PROPERTY_INDEX_STATS_CLEAR:
|
||||
case Marker::DELTA_LABEL_PROPERTY_INDEX_CREATE:
|
||||
case Marker::DELTA_LABEL_PROPERTY_INDEX_DROP:
|
||||
case Marker::DELTA_EDGE_TYPE_INDEX_CREATE:
|
||||
case Marker::DELTA_EDGE_TYPE_INDEX_DROP:
|
||||
case Marker::DELTA_EXISTENCE_CONSTRAINT_CREATE:
|
||||
case Marker::DELTA_EXISTENCE_CONSTRAINT_DROP:
|
||||
case Marker::DELTA_UNIQUE_CONSTRAINT_CREATE:
|
||||
|
@ -153,6 +153,11 @@ SnapshotInfo ReadSnapshotInfo(const std::filesystem::path &path) {
|
||||
info.offset_edges = read_offset();
|
||||
info.offset_vertices = read_offset();
|
||||
info.offset_indices = read_offset();
|
||||
if (*version >= 17) {
|
||||
info.offset_edge_indices = read_offset();
|
||||
} else {
|
||||
info.offset_edge_indices = 0U;
|
||||
}
|
||||
info.offset_constraints = read_offset();
|
||||
info.offset_mapper = read_offset();
|
||||
info.offset_epoch_history = read_offset();
|
||||
@ -1379,10 +1384,11 @@ RecoveredSnapshot LoadSnapshotVersion15(const std::filesystem::path &path, utils
|
||||
return {info, recovery_info, std::move(indices_constraints)};
|
||||
}
|
||||
|
||||
RecoveredSnapshot LoadSnapshot(const std::filesystem::path &path, utils::SkipList<Vertex> *vertices,
|
||||
utils::SkipList<Edge> *edges,
|
||||
std::deque<std::pair<std::string, uint64_t>> *epoch_history,
|
||||
NameIdMapper *name_id_mapper, std::atomic<uint64_t> *edge_count, const Config &config) {
|
||||
RecoveredSnapshot LoadSnapshotVersion16(const std::filesystem::path &path, utils::SkipList<Vertex> *vertices,
|
||||
utils::SkipList<Edge> *edges,
|
||||
std::deque<std::pair<std::string, uint64_t>> *epoch_history,
|
||||
NameIdMapper *name_id_mapper, std::atomic<uint64_t> *edge_count,
|
||||
const Config &config) {
|
||||
RecoveryInfo recovery_info;
|
||||
RecoveredIndicesAndConstraints indices_constraints;
|
||||
|
||||
@ -1391,13 +1397,7 @@ RecoveredSnapshot LoadSnapshot(const std::filesystem::path &path, utils::SkipLis
|
||||
if (!version) throw RecoveryFailure("Couldn't read snapshot magic and/or version!");
|
||||
|
||||
if (!IsVersionSupported(*version)) throw RecoveryFailure(fmt::format("Invalid snapshot version {}", *version));
|
||||
if (*version == 14U) {
|
||||
return LoadSnapshotVersion14(path, vertices, edges, epoch_history, name_id_mapper, edge_count,
|
||||
config.salient.items);
|
||||
}
|
||||
if (*version == 15U) {
|
||||
return LoadSnapshotVersion15(path, vertices, edges, epoch_history, name_id_mapper, edge_count, config);
|
||||
}
|
||||
if (*version != 16U) throw RecoveryFailure(fmt::format("Expected snapshot version is 16, but got {}", *version));
|
||||
|
||||
// Cleanup of loaded data in case of failure.
|
||||
bool success = false;
|
||||
@ -1727,6 +1727,380 @@ RecoveredSnapshot LoadSnapshot(const std::filesystem::path &path, utils::SkipLis
|
||||
return {info, recovery_info, std::move(indices_constraints)};
|
||||
}
|
||||
|
||||
RecoveredSnapshot LoadSnapshot(const std::filesystem::path &path, utils::SkipList<Vertex> *vertices,
|
||||
utils::SkipList<Edge> *edges,
|
||||
std::deque<std::pair<std::string, uint64_t>> *epoch_history,
|
||||
NameIdMapper *name_id_mapper, std::atomic<uint64_t> *edge_count, const Config &config) {
|
||||
RecoveryInfo recovery_info;
|
||||
RecoveredIndicesAndConstraints indices_constraints;
|
||||
|
||||
Decoder snapshot;
|
||||
const auto version = snapshot.Initialize(path, kSnapshotMagic);
|
||||
if (!version) throw RecoveryFailure("Couldn't read snapshot magic and/or version!");
|
||||
|
||||
if (!IsVersionSupported(*version)) throw RecoveryFailure(fmt::format("Invalid snapshot version {}", *version));
|
||||
if (*version == 14U) {
|
||||
return LoadSnapshotVersion14(path, vertices, edges, epoch_history, name_id_mapper, edge_count,
|
||||
config.salient.items);
|
||||
}
|
||||
if (*version == 15U) {
|
||||
return LoadSnapshotVersion15(path, vertices, edges, epoch_history, name_id_mapper, edge_count, config);
|
||||
}
|
||||
if (*version == 16U) {
|
||||
return LoadSnapshotVersion16(path, vertices, edges, epoch_history, name_id_mapper, edge_count, config);
|
||||
}
|
||||
|
||||
// Cleanup of loaded data in case of failure.
|
||||
bool success = false;
|
||||
utils::OnScopeExit cleanup([&] {
|
||||
if (!success) {
|
||||
edges->clear();
|
||||
vertices->clear();
|
||||
epoch_history->clear();
|
||||
}
|
||||
});
|
||||
|
||||
// Read snapshot info.
|
||||
const auto info = ReadSnapshotInfo(path);
|
||||
spdlog::info("Recovering {} vertices and {} edges.", info.vertices_count, info.edges_count);
|
||||
// Check for edges.
|
||||
bool snapshot_has_edges = info.offset_edges != 0;
|
||||
|
||||
// Recover mapper.
|
||||
std::unordered_map<uint64_t, uint64_t> snapshot_id_map;
|
||||
{
|
||||
spdlog::info("Recovering mapper metadata.");
|
||||
if (!snapshot.SetPosition(info.offset_mapper)) throw RecoveryFailure("Couldn't read data from snapshot!");
|
||||
|
||||
auto marker = snapshot.ReadMarker();
|
||||
if (!marker || *marker != Marker::SECTION_MAPPER) throw RecoveryFailure("Failed to read section mapper!");
|
||||
|
||||
auto size = snapshot.ReadUint();
|
||||
if (!size) throw RecoveryFailure("Failed to read name-id mapper size!");
|
||||
|
||||
for (uint64_t i = 0; i < *size; ++i) {
|
||||
auto id = snapshot.ReadUint();
|
||||
if (!id) throw RecoveryFailure("Failed to read id for name-id mapper!");
|
||||
auto name = snapshot.ReadString();
|
||||
if (!name) throw RecoveryFailure("Failed to read name for name-id mapper!");
|
||||
auto my_id = name_id_mapper->NameToId(*name);
|
||||
snapshot_id_map.emplace(*id, my_id);
|
||||
SPDLOG_TRACE("Mapping \"{}\"from snapshot id {} to actual id {}.", *name, *id, my_id);
|
||||
}
|
||||
}
|
||||
auto get_label_from_id = [&snapshot_id_map](uint64_t label_id) {
|
||||
auto it = snapshot_id_map.find(label_id);
|
||||
if (it == snapshot_id_map.end()) throw RecoveryFailure("Couldn't find label id in snapshot_id_map!");
|
||||
return LabelId::FromUint(it->second);
|
||||
};
|
||||
auto get_property_from_id = [&snapshot_id_map](uint64_t property_id) {
|
||||
auto it = snapshot_id_map.find(property_id);
|
||||
if (it == snapshot_id_map.end()) throw RecoveryFailure("Couldn't find property id in snapshot_id_map!");
|
||||
return PropertyId::FromUint(it->second);
|
||||
};
|
||||
auto get_edge_type_from_id = [&snapshot_id_map](uint64_t edge_type_id) {
|
||||
auto it = snapshot_id_map.find(edge_type_id);
|
||||
if (it == snapshot_id_map.end()) throw RecoveryFailure("Couldn't find edge type id in snapshot_id_map!");
|
||||
return EdgeTypeId::FromUint(it->second);
|
||||
};
|
||||
|
||||
// Reset current edge count.
|
||||
edge_count->store(0, std::memory_order_release);
|
||||
|
||||
{
|
||||
spdlog::info("Recovering edges.");
|
||||
// Recover edges.
|
||||
if (snapshot_has_edges) {
|
||||
// We don't need to check whether we store properties on edge or not, because `LoadPartialEdges` will always
|
||||
// iterate over the edges in the snapshot (if they exist) and the current configuration of properties on edge only
|
||||
// affect what it does:
|
||||
// 1. If properties are allowed on edges, then it loads the edges.
|
||||
// 2. If properties are not allowed on edges, then it checks that none of the edges have any properties.
|
||||
if (!snapshot.SetPosition(info.offset_edge_batches)) {
|
||||
throw RecoveryFailure("Couldn't read data from snapshot!");
|
||||
}
|
||||
const auto edge_batches = ReadBatchInfos(snapshot);
|
||||
|
||||
RecoverOnMultipleThreads(
|
||||
config.durability.recovery_thread_count,
|
||||
[path, edges, items = config.salient.items, &get_property_from_id](const size_t /*batch_index*/,
|
||||
const BatchInfo &batch) {
|
||||
LoadPartialEdges(path, *edges, batch.offset, batch.count, items, get_property_from_id);
|
||||
},
|
||||
edge_batches);
|
||||
}
|
||||
spdlog::info("Edges are recovered.");
|
||||
|
||||
// Recover vertices (labels and properties).
|
||||
spdlog::info("Recovering vertices.", info.vertices_count);
|
||||
uint64_t last_vertex_gid{0};
|
||||
|
||||
if (!snapshot.SetPosition(info.offset_vertex_batches)) {
|
||||
throw RecoveryFailure("Couldn't read data from snapshot!");
|
||||
}
|
||||
|
||||
const auto vertex_batches = ReadBatchInfos(snapshot);
|
||||
RecoverOnMultipleThreads(
|
||||
config.durability.recovery_thread_count,
|
||||
[path, vertices, &vertex_batches, &get_label_from_id, &get_property_from_id, &last_vertex_gid](
|
||||
const size_t batch_index, const BatchInfo &batch) {
|
||||
const auto last_vertex_gid_in_batch =
|
||||
LoadPartialVertices(path, *vertices, batch.offset, batch.count, get_label_from_id, get_property_from_id);
|
||||
if (batch_index == vertex_batches.size() - 1) {
|
||||
last_vertex_gid = last_vertex_gid_in_batch;
|
||||
}
|
||||
},
|
||||
vertex_batches);
|
||||
|
||||
spdlog::info("Vertices are recovered.");
|
||||
|
||||
// Recover vertices (in/out edges).
|
||||
spdlog::info("Recover connectivity.");
|
||||
recovery_info.vertex_batches.reserve(vertex_batches.size());
|
||||
for (const auto batch : vertex_batches) {
|
||||
recovery_info.vertex_batches.emplace_back(Gid::FromUint(0), batch.count);
|
||||
}
|
||||
std::atomic<uint64_t> highest_edge_gid{0};
|
||||
|
||||
RecoverOnMultipleThreads(
|
||||
config.durability.recovery_thread_count,
|
||||
[path, vertices, edges, edge_count, items = config.salient.items, snapshot_has_edges, &get_edge_type_from_id,
|
||||
&highest_edge_gid, &recovery_info](const size_t batch_index, const BatchInfo &batch) {
|
||||
const auto result = LoadPartialConnectivity(path, *vertices, *edges, batch.offset, batch.count, items,
|
||||
snapshot_has_edges, get_edge_type_from_id);
|
||||
edge_count->fetch_add(result.edge_count);
|
||||
auto known_highest_edge_gid = highest_edge_gid.load();
|
||||
while (known_highest_edge_gid < result.highest_edge_id) {
|
||||
highest_edge_gid.compare_exchange_weak(known_highest_edge_gid, result.highest_edge_id);
|
||||
}
|
||||
recovery_info.vertex_batches[batch_index].first = result.first_vertex_gid;
|
||||
},
|
||||
vertex_batches);
|
||||
|
||||
spdlog::info("Connectivity is recovered.");
|
||||
|
||||
// Set initial values for edge/vertex ID generators.
|
||||
recovery_info.next_edge_id = highest_edge_gid + 1;
|
||||
recovery_info.next_vertex_id = last_vertex_gid + 1;
|
||||
}
|
||||
|
||||
// Recover indices.
|
||||
{
|
||||
spdlog::info("Recovering metadata of indices.");
|
||||
if (!snapshot.SetPosition(info.offset_indices)) throw RecoveryFailure("Couldn't read data from snapshot!");
|
||||
|
||||
auto marker = snapshot.ReadMarker();
|
||||
if (!marker || *marker != Marker::SECTION_INDICES) throw RecoveryFailure("Couldn't read section indices!");
|
||||
|
||||
// Recover label indices.
|
||||
{
|
||||
auto size = snapshot.ReadUint();
|
||||
if (!size) throw RecoveryFailure("Couldn't read the number of label indices");
|
||||
spdlog::info("Recovering metadata of {} label indices.", *size);
|
||||
for (uint64_t i = 0; i < *size; ++i) {
|
||||
auto label = snapshot.ReadUint();
|
||||
if (!label) throw RecoveryFailure("Couldn't read label of label index!");
|
||||
AddRecoveredIndexConstraint(&indices_constraints.indices.label, get_label_from_id(*label),
|
||||
"The label index already exists!");
|
||||
SPDLOG_TRACE("Recovered metadata of label index for :{}", name_id_mapper->IdToName(snapshot_id_map.at(*label)));
|
||||
}
|
||||
spdlog::info("Metadata of label indices are recovered.");
|
||||
}
|
||||
|
||||
// Recover label indices statistics.
|
||||
{
|
||||
auto size = snapshot.ReadUint();
|
||||
if (!size) throw RecoveryFailure("Couldn't read the number of entries for label index statistics!");
|
||||
spdlog::info("Recovering metadata of {} label indices statistics.", *size);
|
||||
for (uint64_t i = 0; i < *size; ++i) {
|
||||
const auto label = snapshot.ReadUint();
|
||||
if (!label) throw RecoveryFailure("Couldn't read label while recovering label index statistics!");
|
||||
const auto count = snapshot.ReadUint();
|
||||
if (!count) throw RecoveryFailure("Couldn't read count for label index statistics!");
|
||||
const auto avg_degree = snapshot.ReadDouble();
|
||||
if (!avg_degree) throw RecoveryFailure("Couldn't read average degree for label index statistics");
|
||||
const auto label_id = get_label_from_id(*label);
|
||||
indices_constraints.indices.label_stats.emplace_back(label_id, LabelIndexStats{*count, *avg_degree});
|
||||
SPDLOG_TRACE("Recovered metadata of label index statistics for :{}",
|
||||
name_id_mapper->IdToName(snapshot_id_map.at(*label)));
|
||||
}
|
||||
spdlog::info("Metadata of label indices are recovered.");
|
||||
}
|
||||
|
||||
// Recover label+property indices.
|
||||
{
|
||||
auto size = snapshot.ReadUint();
|
||||
if (!size) throw RecoveryFailure("Couldn't recover the number of label property indices!");
|
||||
spdlog::info("Recovering metadata of {} label+property indices.", *size);
|
||||
for (uint64_t i = 0; i < *size; ++i) {
|
||||
auto label = snapshot.ReadUint();
|
||||
if (!label) throw RecoveryFailure("Couldn't read label for label property index!");
|
||||
auto property = snapshot.ReadUint();
|
||||
if (!property) throw RecoveryFailure("Couldn't read property for label property index");
|
||||
AddRecoveredIndexConstraint(&indices_constraints.indices.label_property,
|
||||
{get_label_from_id(*label), get_property_from_id(*property)},
|
||||
"The label+property index already exists!");
|
||||
SPDLOG_TRACE("Recovered metadata of label+property index for :{}({})",
|
||||
name_id_mapper->IdToName(snapshot_id_map.at(*label)),
|
||||
name_id_mapper->IdToName(snapshot_id_map.at(*property)));
|
||||
}
|
||||
spdlog::info("Metadata of label+property indices are recovered.");
|
||||
}
|
||||
|
||||
// Recover label+property indices statistics.
|
||||
{
|
||||
auto size = snapshot.ReadUint();
|
||||
if (!size) throw RecoveryFailure("Couldn't recover the number of entries for label property statistics!");
|
||||
spdlog::info("Recovering metadata of {} label+property indices statistics.", *size);
|
||||
for (uint64_t i = 0; i < *size; ++i) {
|
||||
const auto label = snapshot.ReadUint();
|
||||
if (!label) throw RecoveryFailure("Couldn't read label for label property index statistics!");
|
||||
const auto property = snapshot.ReadUint();
|
||||
if (!property) throw RecoveryFailure("Couldn't read property for label property index statistics!");
|
||||
const auto count = snapshot.ReadUint();
|
||||
if (!count) throw RecoveryFailure("Couldn't read count for label property index statistics!!");
|
||||
const auto distinct_values_count = snapshot.ReadUint();
|
||||
if (!distinct_values_count)
|
||||
throw RecoveryFailure("Couldn't read distinct values count for label property index statistics!");
|
||||
const auto statistic = snapshot.ReadDouble();
|
||||
if (!statistic) throw RecoveryFailure("Couldn't read statistics value for label-property index statistics!");
|
||||
const auto avg_group_size = snapshot.ReadDouble();
|
||||
if (!avg_group_size)
|
||||
throw RecoveryFailure("Couldn't read average group size for label property index statistics!");
|
||||
const auto avg_degree = snapshot.ReadDouble();
|
||||
if (!avg_degree) throw RecoveryFailure("Couldn't read average degree for label property index statistics!");
|
||||
const auto label_id = get_label_from_id(*label);
|
||||
const auto property_id = get_property_from_id(*property);
|
||||
indices_constraints.indices.label_property_stats.emplace_back(
|
||||
label_id, std::make_pair(property_id, LabelPropertyIndexStats{*count, *distinct_values_count, *statistic,
|
||||
*avg_group_size, *avg_degree}));
|
||||
SPDLOG_TRACE("Recovered metadata of label+property index statistics for :{}({})",
|
||||
name_id_mapper->IdToName(snapshot_id_map.at(*label)),
|
||||
name_id_mapper->IdToName(snapshot_id_map.at(*property)));
|
||||
}
|
||||
spdlog::info("Metadata of label+property indices are recovered.");
|
||||
}
|
||||
|
||||
// Recover edge-type indices.
|
||||
spdlog::info("Recovering metadata of indices.");
|
||||
if (!snapshot.SetPosition(info.offset_edge_indices)) throw RecoveryFailure("Couldn't read data from snapshot!");
|
||||
|
||||
marker = snapshot.ReadMarker();
|
||||
if (!marker || *marker != Marker::SECTION_EDGE_INDICES)
|
||||
throw RecoveryFailure("Couldn't read section edge-indices!");
|
||||
|
||||
{
|
||||
auto size = snapshot.ReadUint();
|
||||
if (!size) throw RecoveryFailure("Couldn't read the number of edge-type indices");
|
||||
spdlog::info("Recovering metadata of {} edge-type indices.", *size);
|
||||
for (uint64_t i = 0; i < *size; ++i) {
|
||||
auto edge_type = snapshot.ReadUint();
|
||||
if (!edge_type) throw RecoveryFailure("Couldn't read edge-type of edge-type index!");
|
||||
AddRecoveredIndexConstraint(&indices_constraints.indices.edge, get_edge_type_from_id(*edge_type),
|
||||
"The edge-type index already exists!");
|
||||
SPDLOG_TRACE("Recovered metadata of edge-type index for :{}",
|
||||
name_id_mapper->IdToName(snapshot_id_map.at(*edge_type)));
|
||||
}
|
||||
spdlog::info("Metadata of edge-type indices are recovered.");
|
||||
}
|
||||
|
||||
spdlog::info("Metadata of indices are recovered.");
|
||||
}
|
||||
|
||||
// Recover constraints.
|
||||
{
|
||||
spdlog::info("Recovering metadata of constraints.");
|
||||
if (!snapshot.SetPosition(info.offset_constraints)) throw RecoveryFailure("Couldn't read data from snapshot!");
|
||||
|
||||
auto marker = snapshot.ReadMarker();
|
||||
if (!marker || *marker != Marker::SECTION_CONSTRAINTS)
|
||||
throw RecoveryFailure("Couldn't read section constraints marker!");
|
||||
|
||||
// Recover existence constraints.
|
||||
{
|
||||
auto size = snapshot.ReadUint();
|
||||
if (!size) throw RecoveryFailure("Couldn't read the number of existence constraints!");
|
||||
spdlog::info("Recovering metadata of {} existence constraints.", *size);
|
||||
for (uint64_t i = 0; i < *size; ++i) {
|
||||
auto label = snapshot.ReadUint();
|
||||
if (!label) throw RecoveryFailure("Couldn't read label of existence constraints!");
|
||||
auto property = snapshot.ReadUint();
|
||||
if (!property) throw RecoveryFailure("Couldn't read property of existence constraints!");
|
||||
AddRecoveredIndexConstraint(&indices_constraints.constraints.existence,
|
||||
{get_label_from_id(*label), get_property_from_id(*property)},
|
||||
"The existence constraint already exists!");
|
||||
SPDLOG_TRACE("Recovered metadata of existence constraint for :{}({})",
|
||||
name_id_mapper->IdToName(snapshot_id_map.at(*label)),
|
||||
name_id_mapper->IdToName(snapshot_id_map.at(*property)));
|
||||
}
|
||||
spdlog::info("Metadata of existence constraints are recovered.");
|
||||
}
|
||||
|
||||
// Recover unique constraints.
|
||||
// Snapshot version should be checked since unique constraints were
|
||||
// implemented in later versions of snapshot.
|
||||
if (*version >= kUniqueConstraintVersion) {
|
||||
auto size = snapshot.ReadUint();
|
||||
if (!size) throw RecoveryFailure("Couldn't read the number of unique constraints!");
|
||||
spdlog::info("Recovering metadata of {} unique constraints.", *size);
|
||||
for (uint64_t i = 0; i < *size; ++i) {
|
||||
auto label = snapshot.ReadUint();
|
||||
if (!label) throw RecoveryFailure("Couldn't read label of unique constraints!");
|
||||
auto properties_count = snapshot.ReadUint();
|
||||
if (!properties_count) throw RecoveryFailure("Couldn't read the number of properties in unique constraint!");
|
||||
std::set<PropertyId> properties;
|
||||
for (uint64_t j = 0; j < *properties_count; ++j) {
|
||||
auto property = snapshot.ReadUint();
|
||||
if (!property) throw RecoveryFailure("Couldn't read property of unique constraint!");
|
||||
properties.insert(get_property_from_id(*property));
|
||||
}
|
||||
AddRecoveredIndexConstraint(&indices_constraints.constraints.unique, {get_label_from_id(*label), properties},
|
||||
"The unique constraint already exists!");
|
||||
SPDLOG_TRACE("Recovered metadata of unique constraints for :{}",
|
||||
name_id_mapper->IdToName(snapshot_id_map.at(*label)));
|
||||
}
|
||||
spdlog::info("Metadata of unique constraints are recovered.");
|
||||
}
|
||||
spdlog::info("Metadata of constraints are recovered.");
|
||||
}
|
||||
|
||||
spdlog::info("Recovering metadata.");
|
||||
// Recover epoch history
|
||||
{
|
||||
if (!snapshot.SetPosition(info.offset_epoch_history)) throw RecoveryFailure("Couldn't read data from snapshot!");
|
||||
|
||||
const auto marker = snapshot.ReadMarker();
|
||||
if (!marker || *marker != Marker::SECTION_EPOCH_HISTORY)
|
||||
throw RecoveryFailure("Couldn't read section epoch history marker!");
|
||||
|
||||
const auto history_size = snapshot.ReadUint();
|
||||
if (!history_size) {
|
||||
throw RecoveryFailure("Couldn't read history size!");
|
||||
}
|
||||
|
||||
for (int i = 0; i < *history_size; ++i) {
|
||||
auto maybe_epoch_id = snapshot.ReadString();
|
||||
if (!maybe_epoch_id) {
|
||||
throw RecoveryFailure("Couldn't read maybe epoch id!");
|
||||
}
|
||||
const auto maybe_last_commit_timestamp = snapshot.ReadUint();
|
||||
if (!maybe_last_commit_timestamp) {
|
||||
throw RecoveryFailure("Couldn't read maybe last commit timestamp!");
|
||||
}
|
||||
epoch_history->emplace_back(std::move(*maybe_epoch_id), *maybe_last_commit_timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
spdlog::info("Metadata recovered.");
|
||||
// Recover timestamp.
|
||||
recovery_info.next_timestamp = info.start_timestamp + 1;
|
||||
|
||||
// Set success flag (to disable cleanup).
|
||||
success = true;
|
||||
|
||||
return {info, recovery_info, std::move(indices_constraints)};
|
||||
}
|
||||
|
||||
using OldSnapshotFiles = std::vector<std::pair<uint64_t, std::filesystem::path>>;
|
||||
void EnsureNecessaryWalFilesExist(const std::filesystem::path &wal_directory, const std::string &uuid,
|
||||
OldSnapshotFiles old_snapshot_files, Transaction *transaction,
|
||||
@ -1835,6 +2209,7 @@ void CreateSnapshot(Storage *storage, Transaction *transaction, const std::files
|
||||
uint64_t offset_edges = 0;
|
||||
uint64_t offset_vertices = 0;
|
||||
uint64_t offset_indices = 0;
|
||||
uint64_t offset_edge_indices = 0;
|
||||
uint64_t offset_constraints = 0;
|
||||
uint64_t offset_mapper = 0;
|
||||
uint64_t offset_metadata = 0;
|
||||
@ -1847,6 +2222,7 @@ void CreateSnapshot(Storage *storage, Transaction *transaction, const std::files
|
||||
snapshot.WriteUint(offset_edges);
|
||||
snapshot.WriteUint(offset_vertices);
|
||||
snapshot.WriteUint(offset_indices);
|
||||
snapshot.WriteUint(offset_edge_indices);
|
||||
snapshot.WriteUint(offset_constraints);
|
||||
snapshot.WriteUint(offset_mapper);
|
||||
snapshot.WriteUint(offset_epoch_history);
|
||||
@ -2106,6 +2482,17 @@ void CreateSnapshot(Storage *storage, Transaction *transaction, const std::files
|
||||
snapshot.SetPosition(last_pos);
|
||||
}
|
||||
}
|
||||
|
||||
// Write edge-type indices.
|
||||
offset_edge_indices = snapshot.GetPosition();
|
||||
snapshot.WriteMarker(Marker::SECTION_EDGE_INDICES);
|
||||
{
|
||||
auto edge_type = storage->indices_.edge_type_index_->ListIndices();
|
||||
snapshot.WriteUint(edge_type.size());
|
||||
for (const auto &item : edge_type) {
|
||||
write_mapping(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write constraints.
|
||||
@ -2196,6 +2583,7 @@ void CreateSnapshot(Storage *storage, Transaction *transaction, const std::files
|
||||
snapshot.WriteUint(offset_edges);
|
||||
snapshot.WriteUint(offset_vertices);
|
||||
snapshot.WriteUint(offset_indices);
|
||||
snapshot.WriteUint(offset_edge_indices);
|
||||
snapshot.WriteUint(offset_constraints);
|
||||
snapshot.WriteUint(offset_mapper);
|
||||
snapshot.WriteUint(offset_epoch_history);
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -34,6 +34,7 @@ struct SnapshotInfo {
|
||||
uint64_t offset_edges;
|
||||
uint64_t offset_vertices;
|
||||
uint64_t offset_indices;
|
||||
uint64_t offset_edge_indices;
|
||||
uint64_t offset_constraints;
|
||||
uint64_t offset_mapper;
|
||||
uint64_t offset_epoch_history;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -23,6 +23,8 @@ enum class StorageMetadataOperation {
|
||||
LABEL_PROPERTY_INDEX_DROP,
|
||||
LABEL_PROPERTY_INDEX_STATS_SET,
|
||||
LABEL_PROPERTY_INDEX_STATS_CLEAR,
|
||||
EDGE_TYPE_INDEX_CREATE,
|
||||
EDGE_TYPE_INDEX_DROP,
|
||||
EXISTENCE_CONSTRAINT_CREATE,
|
||||
EXISTENCE_CONSTRAINT_DROP,
|
||||
UNIQUE_CONSTRAINT_CREATE,
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -20,7 +20,7 @@ namespace memgraph::storage::durability {
|
||||
// The current version of snapshot and WAL encoding / decoding.
|
||||
// IMPORTANT: Please bump this version for every snapshot and/or WAL format
|
||||
// change!!!
|
||||
const uint64_t kVersion{16};
|
||||
const uint64_t kVersion{17};
|
||||
|
||||
const uint64_t kOldestSupportedVersion{14};
|
||||
const uint64_t kUniqueConstraintVersion{13};
|
||||
|
@ -95,6 +95,10 @@ Marker OperationToMarker(StorageMetadataOperation operation) {
|
||||
return Marker::DELTA_LABEL_PROPERTY_INDEX_STATS_SET;
|
||||
case StorageMetadataOperation::LABEL_PROPERTY_INDEX_STATS_CLEAR:
|
||||
return Marker::DELTA_LABEL_PROPERTY_INDEX_STATS_CLEAR;
|
||||
case StorageMetadataOperation::EDGE_TYPE_INDEX_CREATE:
|
||||
return Marker::DELTA_EDGE_TYPE_INDEX_CREATE;
|
||||
case StorageMetadataOperation::EDGE_TYPE_INDEX_DROP:
|
||||
return Marker::DELTA_EDGE_TYPE_INDEX_DROP;
|
||||
case StorageMetadataOperation::EXISTENCE_CONSTRAINT_CREATE:
|
||||
return Marker::DELTA_EXISTENCE_CONSTRAINT_CREATE;
|
||||
case StorageMetadataOperation::EXISTENCE_CONSTRAINT_DROP:
|
||||
@ -172,6 +176,10 @@ WalDeltaData::Type MarkerToWalDeltaDataType(Marker marker) {
|
||||
return WalDeltaData::Type::LABEL_PROPERTY_INDEX_STATS_SET;
|
||||
case Marker::DELTA_LABEL_PROPERTY_INDEX_STATS_CLEAR:
|
||||
return WalDeltaData::Type::LABEL_PROPERTY_INDEX_STATS_CLEAR;
|
||||
case Marker::DELTA_EDGE_TYPE_INDEX_CREATE:
|
||||
return WalDeltaData::Type::EDGE_INDEX_CREATE;
|
||||
case Marker::DELTA_EDGE_TYPE_INDEX_DROP:
|
||||
return WalDeltaData::Type::EDGE_INDEX_DROP;
|
||||
case Marker::DELTA_EXISTENCE_CONSTRAINT_CREATE:
|
||||
return WalDeltaData::Type::EXISTENCE_CONSTRAINT_CREATE;
|
||||
case Marker::DELTA_EXISTENCE_CONSTRAINT_DROP:
|
||||
@ -198,6 +206,7 @@ WalDeltaData::Type MarkerToWalDeltaDataType(Marker marker) {
|
||||
case Marker::SECTION_CONSTRAINTS:
|
||||
case Marker::SECTION_DELTA:
|
||||
case Marker::SECTION_EPOCH_HISTORY:
|
||||
case Marker::SECTION_EDGE_INDICES:
|
||||
case Marker::SECTION_OFFSETS:
|
||||
case Marker::VALUE_FALSE:
|
||||
case Marker::VALUE_TRUE:
|
||||
@ -280,6 +289,7 @@ WalDeltaData ReadSkipWalDeltaData(BaseDecoder *decoder) {
|
||||
}
|
||||
case WalDeltaData::Type::TRANSACTION_END:
|
||||
break;
|
||||
// NOLINTNEXTLINE(bugprone-branch-clone)
|
||||
case WalDeltaData::Type::LABEL_INDEX_CREATE:
|
||||
case WalDeltaData::Type::LABEL_INDEX_DROP:
|
||||
case WalDeltaData::Type::LABEL_INDEX_STATS_CLEAR:
|
||||
@ -295,6 +305,17 @@ WalDeltaData ReadSkipWalDeltaData(BaseDecoder *decoder) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WalDeltaData::Type::EDGE_INDEX_CREATE:
|
||||
case WalDeltaData::Type::EDGE_INDEX_DROP: {
|
||||
if constexpr (read_data) {
|
||||
auto edge_type = decoder->ReadString();
|
||||
if (!edge_type) throw RecoveryFailure("Invalid WAL data!");
|
||||
delta.operation_edge_type.edge_type = std::move(*edge_type);
|
||||
} else {
|
||||
if (!decoder->SkipString()) throw RecoveryFailure("Invalid WAL data!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WalDeltaData::Type::LABEL_INDEX_STATS_SET: {
|
||||
if constexpr (read_data) {
|
||||
auto label = decoder->ReadString();
|
||||
@ -522,6 +543,9 @@ bool operator==(const WalDeltaData &a, const WalDeltaData &b) {
|
||||
case WalDeltaData::Type::UNIQUE_CONSTRAINT_DROP:
|
||||
return a.operation_label_properties.label == b.operation_label_properties.label &&
|
||||
a.operation_label_properties.properties == b.operation_label_properties.properties;
|
||||
case WalDeltaData::Type::EDGE_INDEX_CREATE:
|
||||
case WalDeltaData::Type::EDGE_INDEX_DROP:
|
||||
return a.operation_edge_type.edge_type == b.operation_edge_type.edge_type;
|
||||
}
|
||||
}
|
||||
bool operator!=(const WalDeltaData &a, const WalDeltaData &b) { return !(a == b); }
|
||||
@ -703,6 +727,37 @@ void EncodeOperation(BaseEncoder *encoder, NameIdMapper *name_id_mapper, Storage
|
||||
}
|
||||
break;
|
||||
}
|
||||
case StorageMetadataOperation::EDGE_TYPE_INDEX_CREATE:
|
||||
case StorageMetadataOperation::EDGE_TYPE_INDEX_DROP: {
|
||||
MG_ASSERT(false, "Invalid function call!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EncodeOperation(BaseEncoder *encoder, NameIdMapper *name_id_mapper, StorageMetadataOperation operation,
|
||||
EdgeTypeId edge_type, uint64_t timestamp) {
|
||||
encoder->WriteMarker(Marker::SECTION_DELTA);
|
||||
encoder->WriteUint(timestamp);
|
||||
switch (operation) {
|
||||
case StorageMetadataOperation::EDGE_TYPE_INDEX_CREATE:
|
||||
case StorageMetadataOperation::EDGE_TYPE_INDEX_DROP: {
|
||||
encoder->WriteMarker(OperationToMarker(operation));
|
||||
encoder->WriteString(name_id_mapper->IdToName(edge_type.AsUint()));
|
||||
break;
|
||||
}
|
||||
case StorageMetadataOperation::LABEL_INDEX_CREATE:
|
||||
case StorageMetadataOperation::LABEL_INDEX_DROP:
|
||||
case StorageMetadataOperation::LABEL_INDEX_STATS_CLEAR:
|
||||
case StorageMetadataOperation::LABEL_PROPERTY_INDEX_STATS_CLEAR:
|
||||
case StorageMetadataOperation::LABEL_INDEX_STATS_SET:
|
||||
case StorageMetadataOperation::LABEL_PROPERTY_INDEX_CREATE:
|
||||
case StorageMetadataOperation::LABEL_PROPERTY_INDEX_DROP:
|
||||
case StorageMetadataOperation::EXISTENCE_CONSTRAINT_CREATE:
|
||||
case StorageMetadataOperation::EXISTENCE_CONSTRAINT_DROP:
|
||||
case StorageMetadataOperation::LABEL_PROPERTY_INDEX_STATS_SET:
|
||||
case StorageMetadataOperation::UNIQUE_CONSTRAINT_CREATE:
|
||||
case StorageMetadataOperation::UNIQUE_CONSTRAINT_DROP:
|
||||
MG_ASSERT(false, "Invalid function call!");
|
||||
}
|
||||
}
|
||||
|
||||
@ -887,6 +942,18 @@ RecoveryInfo LoadWal(const std::filesystem::path &path, RecoveredIndicesAndConst
|
||||
"The label index doesn't exist!");
|
||||
break;
|
||||
}
|
||||
case WalDeltaData::Type::EDGE_INDEX_CREATE: {
|
||||
auto edge_type_id = EdgeTypeId::FromUint(name_id_mapper->NameToId(delta.operation_edge_type.edge_type));
|
||||
AddRecoveredIndexConstraint(&indices_constraints->indices.edge, edge_type_id,
|
||||
"The edge-type index already exists!");
|
||||
break;
|
||||
}
|
||||
case WalDeltaData::Type::EDGE_INDEX_DROP: {
|
||||
auto edge_type_id = EdgeTypeId::FromUint(name_id_mapper->NameToId(delta.operation_edge_type.edge_type));
|
||||
RemoveRecoveredIndexConstraint(&indices_constraints->indices.edge, edge_type_id,
|
||||
"The edge-type index doesn't exist!");
|
||||
break;
|
||||
}
|
||||
case WalDeltaData::Type::LABEL_INDEX_STATS_SET: {
|
||||
auto label_id = LabelId::FromUint(name_id_mapper->NameToId(delta.operation_label_stats.label));
|
||||
LabelIndexStats stats{};
|
||||
@ -1088,6 +1155,11 @@ void WalFile::AppendOperation(StorageMetadataOperation operation, LabelId label,
|
||||
UpdateStats(timestamp);
|
||||
}
|
||||
|
||||
void WalFile::AppendOperation(StorageMetadataOperation operation, EdgeTypeId edge_type, uint64_t timestamp) {
|
||||
EncodeOperation(&wal_, name_id_mapper_, operation, edge_type, timestamp);
|
||||
UpdateStats(timestamp);
|
||||
}
|
||||
|
||||
void WalFile::Sync() { wal_.Sync(); }
|
||||
|
||||
uint64_t WalFile::GetSize() { return wal_.GetSize(); }
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -67,6 +67,8 @@ struct WalDeltaData {
|
||||
LABEL_PROPERTY_INDEX_DROP,
|
||||
LABEL_PROPERTY_INDEX_STATS_SET,
|
||||
LABEL_PROPERTY_INDEX_STATS_CLEAR,
|
||||
EDGE_INDEX_CREATE,
|
||||
EDGE_INDEX_DROP,
|
||||
EXISTENCE_CONSTRAINT_CREATE,
|
||||
EXISTENCE_CONSTRAINT_DROP,
|
||||
UNIQUE_CONSTRAINT_CREATE,
|
||||
@ -111,6 +113,10 @@ struct WalDeltaData {
|
||||
std::set<std::string, std::less<>> properties;
|
||||
} operation_label_properties;
|
||||
|
||||
struct {
|
||||
std::string edge_type;
|
||||
} operation_edge_type;
|
||||
|
||||
struct {
|
||||
std::string label;
|
||||
std::string stats;
|
||||
@ -155,6 +161,8 @@ constexpr bool IsWalDeltaDataTypeTransactionEndVersion15(const WalDeltaData::Typ
|
||||
case WalDeltaData::Type::LABEL_PROPERTY_INDEX_DROP:
|
||||
case WalDeltaData::Type::LABEL_PROPERTY_INDEX_STATS_SET:
|
||||
case WalDeltaData::Type::LABEL_PROPERTY_INDEX_STATS_CLEAR:
|
||||
case WalDeltaData::Type::EDGE_INDEX_CREATE:
|
||||
case WalDeltaData::Type::EDGE_INDEX_DROP:
|
||||
case WalDeltaData::Type::EXISTENCE_CONSTRAINT_CREATE:
|
||||
case WalDeltaData::Type::EXISTENCE_CONSTRAINT_DROP:
|
||||
case WalDeltaData::Type::UNIQUE_CONSTRAINT_CREATE:
|
||||
@ -164,7 +172,7 @@ constexpr bool IsWalDeltaDataTypeTransactionEndVersion15(const WalDeltaData::Typ
|
||||
}
|
||||
|
||||
constexpr bool IsWalDeltaDataTypeTransactionEnd(const WalDeltaData::Type type, const uint64_t version = kVersion) {
|
||||
if (version < 16U) {
|
||||
if (version < 17U) {
|
||||
return IsWalDeltaDataTypeTransactionEndVersion15(type);
|
||||
}
|
||||
// All deltas are now handled in a transactional scope
|
||||
@ -208,6 +216,9 @@ void EncodeOperation(BaseEncoder *encoder, NameIdMapper *name_id_mapper, Storage
|
||||
LabelId label, const std::set<PropertyId> &properties, const LabelIndexStats &stats,
|
||||
const LabelPropertyIndexStats &property_stats, uint64_t timestamp);
|
||||
|
||||
void EncodeOperation(BaseEncoder *encoder, NameIdMapper *name_id_mapper, StorageMetadataOperation operation,
|
||||
EdgeTypeId edge_type, uint64_t timestamp);
|
||||
|
||||
/// Function used to load the WAL data into the storage.
|
||||
/// @throw RecoveryFailure
|
||||
RecoveryInfo LoadWal(const std::filesystem::path &path, RecoveredIndicesAndConstraints *indices_constraints,
|
||||
@ -240,6 +251,8 @@ class WalFile {
|
||||
void AppendOperation(StorageMetadataOperation operation, LabelId label, const std::set<PropertyId> &properties,
|
||||
const LabelIndexStats &stats, const LabelPropertyIndexStats &property_stats, uint64_t timestamp);
|
||||
|
||||
void AppendOperation(StorageMetadataOperation operation, EdgeTypeId edge_type, uint64_t timestamp);
|
||||
|
||||
void Sync();
|
||||
|
||||
uint64_t GetSize();
|
||||
|
149
src/storage/v2/edges_iterable.cpp
Normal file
149
src/storage/v2/edges_iterable.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
#include "storage/v2/edges_iterable.hpp"
|
||||
|
||||
namespace memgraph::storage {
|
||||
|
||||
EdgesIterable::EdgesIterable(InMemoryEdgeTypeIndex::Iterable edges) : type_(Type::BY_EDGE_TYPE_IN_MEMORY) {
|
||||
new (&in_memory_edges_by_edge_type_) InMemoryEdgeTypeIndex::Iterable(std::move(edges));
|
||||
}
|
||||
|
||||
EdgesIterable::EdgesIterable(EdgesIterable &&other) noexcept : type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
case Type::BY_EDGE_TYPE_IN_MEMORY:
|
||||
new (&in_memory_edges_by_edge_type_)
|
||||
InMemoryEdgeTypeIndex::Iterable(std::move(other.in_memory_edges_by_edge_type_));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EdgesIterable &EdgesIterable::operator=(EdgesIterable &&other) noexcept {
|
||||
Destroy();
|
||||
type_ = other.type_;
|
||||
switch (other.type_) {
|
||||
case Type::BY_EDGE_TYPE_IN_MEMORY:
|
||||
new (&in_memory_edges_by_edge_type_)
|
||||
InMemoryEdgeTypeIndex::Iterable(std::move(other.in_memory_edges_by_edge_type_));
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
EdgesIterable::~EdgesIterable() { Destroy(); }
|
||||
|
||||
void EdgesIterable::Destroy() noexcept {
|
||||
switch (type_) {
|
||||
case Type::BY_EDGE_TYPE_IN_MEMORY:
|
||||
in_memory_edges_by_edge_type_.InMemoryEdgeTypeIndex::Iterable::~Iterable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EdgesIterable::Iterator EdgesIterable::begin() {
|
||||
switch (type_) {
|
||||
case Type::BY_EDGE_TYPE_IN_MEMORY:
|
||||
return Iterator(in_memory_edges_by_edge_type_.begin());
|
||||
}
|
||||
}
|
||||
|
||||
EdgesIterable::Iterator EdgesIterable::end() {
|
||||
switch (type_) {
|
||||
case Type::BY_EDGE_TYPE_IN_MEMORY:
|
||||
return Iterator(in_memory_edges_by_edge_type_.end());
|
||||
}
|
||||
}
|
||||
|
||||
EdgesIterable::Iterator::Iterator(InMemoryEdgeTypeIndex::Iterable::Iterator it) : type_(Type::BY_EDGE_TYPE_IN_MEMORY) {
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
new (&in_memory_edges_by_edge_type_) InMemoryEdgeTypeIndex::Iterable::Iterator(std::move(it));
|
||||
}
|
||||
|
||||
EdgesIterable::Iterator::Iterator(const EdgesIterable::Iterator &other) : type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
case Type::BY_EDGE_TYPE_IN_MEMORY:
|
||||
new (&in_memory_edges_by_edge_type_)
|
||||
InMemoryEdgeTypeIndex::Iterable::Iterator(other.in_memory_edges_by_edge_type_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(cert-oop54-cpp)
|
||||
EdgesIterable::Iterator &EdgesIterable::Iterator::operator=(const EdgesIterable::Iterator &other) {
|
||||
Destroy();
|
||||
type_ = other.type_;
|
||||
switch (other.type_) {
|
||||
case Type::BY_EDGE_TYPE_IN_MEMORY:
|
||||
new (&in_memory_edges_by_edge_type_)
|
||||
InMemoryEdgeTypeIndex::Iterable::Iterator(other.in_memory_edges_by_edge_type_);
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
EdgesIterable::Iterator::Iterator(EdgesIterable::Iterator &&other) noexcept : type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
case Type::BY_EDGE_TYPE_IN_MEMORY:
|
||||
new (&in_memory_edges_by_edge_type_)
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
InMemoryEdgeTypeIndex::Iterable::Iterator(std::move(other.in_memory_edges_by_edge_type_));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EdgesIterable::Iterator &EdgesIterable::Iterator::operator=(EdgesIterable::Iterator &&other) noexcept {
|
||||
Destroy();
|
||||
type_ = other.type_;
|
||||
switch (other.type_) {
|
||||
case Type::BY_EDGE_TYPE_IN_MEMORY:
|
||||
new (&in_memory_edges_by_edge_type_)
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
|
||||
InMemoryEdgeTypeIndex::Iterable::Iterator(std::move(other.in_memory_edges_by_edge_type_));
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
EdgesIterable::Iterator::~Iterator() { Destroy(); }
|
||||
|
||||
void EdgesIterable::Iterator::Destroy() noexcept {
|
||||
switch (type_) {
|
||||
case Type::BY_EDGE_TYPE_IN_MEMORY:
|
||||
in_memory_edges_by_edge_type_.InMemoryEdgeTypeIndex::Iterable::Iterator::~Iterator();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EdgeAccessor const &EdgesIterable::Iterator::operator*() const {
|
||||
switch (type_) {
|
||||
;
|
||||
case Type::BY_EDGE_TYPE_IN_MEMORY:
|
||||
return *in_memory_edges_by_edge_type_;
|
||||
}
|
||||
}
|
||||
|
||||
EdgesIterable::Iterator &EdgesIterable::Iterator::operator++() {
|
||||
switch (type_) {
|
||||
case Type::BY_EDGE_TYPE_IN_MEMORY:
|
||||
++in_memory_edges_by_edge_type_;
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool EdgesIterable::Iterator::operator==(const Iterator &other) const {
|
||||
switch (type_) {
|
||||
case Type::BY_EDGE_TYPE_IN_MEMORY:
|
||||
return in_memory_edges_by_edge_type_ == other.in_memory_edges_by_edge_type_;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace memgraph::storage
|
73
src/storage/v2/edges_iterable.hpp
Normal file
73
src/storage/v2/edges_iterable.hpp
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright 2024 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "storage/v2/all_vertices_iterable.hpp"
|
||||
#include "storage/v2/inmemory/edge_type_index.hpp"
|
||||
|
||||
namespace memgraph::storage {
|
||||
|
||||
class InMemoryEdgeTypeIndex;
|
||||
|
||||
class EdgesIterable final {
|
||||
enum class Type { BY_EDGE_TYPE_IN_MEMORY };
|
||||
|
||||
Type type_;
|
||||
union {
|
||||
InMemoryEdgeTypeIndex::Iterable in_memory_edges_by_edge_type_;
|
||||
};
|
||||
|
||||
void Destroy() noexcept;
|
||||
|
||||
public:
|
||||
explicit EdgesIterable(InMemoryEdgeTypeIndex::Iterable);
|
||||
|
||||
EdgesIterable(const EdgesIterable &) = delete;
|
||||
EdgesIterable &operator=(const EdgesIterable &) = delete;
|
||||
|
||||
EdgesIterable(EdgesIterable &&) noexcept;
|
||||
EdgesIterable &operator=(EdgesIterable &&) noexcept;
|
||||
|
||||
~EdgesIterable();
|
||||
|
||||
class Iterator final {
|
||||
Type type_;
|
||||
union {
|
||||
InMemoryEdgeTypeIndex::Iterable::Iterator in_memory_edges_by_edge_type_;
|
||||
};
|
||||
|
||||
void Destroy() noexcept;
|
||||
|
||||
public:
|
||||
explicit Iterator(InMemoryEdgeTypeIndex::Iterable::Iterator);
|
||||
|
||||
Iterator(const Iterator &);
|
||||
Iterator &operator=(const Iterator &);
|
||||
|
||||
Iterator(Iterator &&) noexcept;
|
||||
Iterator &operator=(Iterator &&) noexcept;
|
||||
|
||||
~Iterator();
|
||||
|
||||
EdgeAccessor const &operator*() const;
|
||||
|
||||
Iterator &operator++();
|
||||
|
||||
bool operator==(const Iterator &other) const;
|
||||
bool operator!=(const Iterator &other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
Iterator begin();
|
||||
Iterator end();
|
||||
};
|
||||
|
||||
} // namespace memgraph::storage
|
46
src/storage/v2/indices/edge_type_index.hpp
Normal file
46
src/storage/v2/indices/edge_type_index.hpp
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2024 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 <vector>
|
||||
|
||||
#include "storage/v2/transaction.hpp"
|
||||
|
||||
namespace memgraph::storage {
|
||||
|
||||
class EdgeTypeIndex {
|
||||
public:
|
||||
EdgeTypeIndex() = default;
|
||||
|
||||
EdgeTypeIndex(const EdgeTypeIndex &) = delete;
|
||||
EdgeTypeIndex(EdgeTypeIndex &&) = delete;
|
||||
EdgeTypeIndex &operator=(const EdgeTypeIndex &) = delete;
|
||||
EdgeTypeIndex &operator=(EdgeTypeIndex &&) = delete;
|
||||
|
||||
virtual ~EdgeTypeIndex() = default;
|
||||
|
||||
virtual bool DropIndex(EdgeTypeId edge_type) = 0;
|
||||
|
||||
virtual bool IndexExists(EdgeTypeId edge_type) const = 0;
|
||||
|
||||
virtual std::vector<EdgeTypeId> ListIndices() const = 0;
|
||||
|
||||
virtual uint64_t ApproximateEdgeCount(EdgeTypeId edge_type) const = 0;
|
||||
|
||||
virtual void UpdateOnEdgeCreation(Vertex *from, Vertex *to, EdgeRef edge_ref, EdgeTypeId edge_type,
|
||||
const Transaction &tx) = 0;
|
||||
|
||||
virtual void UpdateOnEdgeModification(Vertex *old_from, Vertex *old_to, Vertex *new_from, Vertex *new_to,
|
||||
EdgeRef edge_ref, EdgeTypeId edge_type, const Transaction &tx) = 0;
|
||||
};
|
||||
|
||||
} // namespace memgraph::storage
|
@ -10,8 +10,10 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "storage/v2/indices/indices.hpp"
|
||||
#include "storage/v2/disk/edge_type_index.hpp"
|
||||
#include "storage/v2/disk/label_index.hpp"
|
||||
#include "storage/v2/disk/label_property_index.hpp"
|
||||
#include "storage/v2/inmemory/edge_type_index.hpp"
|
||||
#include "storage/v2/inmemory/label_index.hpp"
|
||||
#include "storage/v2/inmemory/label_property_index.hpp"
|
||||
|
||||
@ -35,6 +37,8 @@ void Indices::AbortEntries(LabelId label, std::span<std::pair<PropertyValue, Ver
|
||||
void Indices::RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp, std::stop_token token) const {
|
||||
static_cast<InMemoryLabelIndex *>(label_index_.get())->RemoveObsoleteEntries(oldest_active_start_timestamp, token);
|
||||
static_cast<InMemoryLabelPropertyIndex *>(label_property_index_.get())
|
||||
->RemoveObsoleteEntries(oldest_active_start_timestamp, token);
|
||||
static_cast<InMemoryEdgeTypeIndex *>(edge_type_index_.get())
|
||||
->RemoveObsoleteEntries(oldest_active_start_timestamp, std::move(token));
|
||||
}
|
||||
|
||||
@ -53,14 +57,21 @@ void Indices::UpdateOnSetProperty(PropertyId property, const PropertyValue &valu
|
||||
label_property_index_->UpdateOnSetProperty(property, value, vertex, tx);
|
||||
}
|
||||
|
||||
void Indices::UpdateOnEdgeCreation(Vertex *from, Vertex *to, EdgeRef edge_ref, EdgeTypeId edge_type,
|
||||
const Transaction &tx) const {
|
||||
edge_type_index_->UpdateOnEdgeCreation(from, to, edge_ref, edge_type, tx);
|
||||
}
|
||||
|
||||
Indices::Indices(const Config &config, StorageMode storage_mode) {
|
||||
std::invoke([this, config, storage_mode]() {
|
||||
if (storage_mode == StorageMode::IN_MEMORY_TRANSACTIONAL || storage_mode == StorageMode::IN_MEMORY_ANALYTICAL) {
|
||||
label_index_ = std::make_unique<InMemoryLabelIndex>();
|
||||
label_property_index_ = std::make_unique<InMemoryLabelPropertyIndex>();
|
||||
edge_type_index_ = std::make_unique<InMemoryEdgeTypeIndex>();
|
||||
} else {
|
||||
label_index_ = std::make_unique<DiskLabelIndex>(config);
|
||||
label_property_index_ = std::make_unique<DiskLabelPropertyIndex>(config);
|
||||
edge_type_index_ = std::make_unique<DiskEdgeTypeIndex>();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <span>
|
||||
|
||||
#include "storage/v2/id_types.hpp"
|
||||
#include "storage/v2/indices/edge_type_index.hpp"
|
||||
#include "storage/v2/indices/label_index.hpp"
|
||||
#include "storage/v2/indices/label_property_index.hpp"
|
||||
#include "storage/v2/storage_mode.hpp"
|
||||
@ -64,8 +65,12 @@ struct Indices {
|
||||
void UpdateOnSetProperty(PropertyId property, const PropertyValue &value, Vertex *vertex,
|
||||
const Transaction &tx) const;
|
||||
|
||||
void UpdateOnEdgeCreation(Vertex *from, Vertex *to, EdgeRef edge_ref, EdgeTypeId edge_type,
|
||||
const Transaction &tx) const;
|
||||
|
||||
std::unique_ptr<LabelIndex> label_index_;
|
||||
std::unique_ptr<LabelPropertyIndex> label_property_index_;
|
||||
std::unique_ptr<EdgeTypeIndex> edge_type_index_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::storage
|
||||
|
318
src/storage/v2/inmemory/edge_type_index.cpp
Normal file
318
src/storage/v2/inmemory/edge_type_index.cpp
Normal file
@ -0,0 +1,318 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
#include "storage/v2/inmemory/edge_type_index.hpp"
|
||||
|
||||
#include "storage/v2/constraints/constraints.hpp"
|
||||
#include "storage/v2/indices/indices_utils.hpp"
|
||||
#include "utils/counter.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
using Delta = memgraph::storage::Delta;
|
||||
using Vertex = memgraph::storage::Vertex;
|
||||
using Edge = memgraph::storage::Edge;
|
||||
using EdgeRef = memgraph::storage::EdgeRef;
|
||||
using EdgeTypeId = memgraph::storage::EdgeTypeId;
|
||||
using Transaction = memgraph::storage::Transaction;
|
||||
using View = memgraph::storage::View;
|
||||
|
||||
bool IsIndexEntryVisible(Edge *edge, const Transaction *transaction, View view) {
|
||||
bool exists = true;
|
||||
bool deleted = true;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
auto guard = std::shared_lock{edge->lock};
|
||||
deleted = edge->deleted;
|
||||
delta = edge->delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction, delta, view, [&](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::ADD_LABEL:
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
case Delta::Action::SET_PROPERTY:
|
||||
case Delta::Action::ADD_IN_EDGE:
|
||||
case Delta::Action::ADD_OUT_EDGE:
|
||||
case Delta::Action::REMOVE_IN_EDGE:
|
||||
case Delta::Action::REMOVE_OUT_EDGE:
|
||||
break;
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
deleted = false;
|
||||
break;
|
||||
}
|
||||
case Delta::Action::DELETE_DESERIALIZED_OBJECT:
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
exists = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return exists && !deleted;
|
||||
}
|
||||
|
||||
using ReturnType = std::optional<std::tuple<EdgeTypeId, Vertex *, EdgeRef>>;
|
||||
ReturnType VertexDeletedConnectedEdges(Vertex *vertex, Edge *edge, const Transaction *transaction, View view) {
|
||||
ReturnType link;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
auto guard = std::shared_lock{vertex->lock};
|
||||
delta = vertex->delta;
|
||||
}
|
||||
ApplyDeltasForRead(transaction, delta, view, [&](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::ADD_LABEL:
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
case Delta::Action::SET_PROPERTY:
|
||||
break;
|
||||
case Delta::Action::ADD_IN_EDGE: {
|
||||
if (edge == delta.vertex_edge.edge.ptr) {
|
||||
link = {delta.vertex_edge.edge_type, delta.vertex_edge.vertex, delta.vertex_edge.edge};
|
||||
auto it = std::find(vertex->in_edges.begin(), vertex->in_edges.end(), link);
|
||||
MG_ASSERT(it == vertex->in_edges.end(), "Invalid database state!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
case Delta::Action::ADD_OUT_EDGE: {
|
||||
if (edge == delta.vertex_edge.edge.ptr) {
|
||||
link = {delta.vertex_edge.edge_type, delta.vertex_edge.vertex, delta.vertex_edge.edge};
|
||||
auto it = std::find(vertex->out_edges.begin(), vertex->out_edges.end(), link);
|
||||
MG_ASSERT(it == vertex->out_edges.end(), "Invalid database state!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
case Delta::Action::REMOVE_IN_EDGE:
|
||||
case Delta::Action::REMOVE_OUT_EDGE:
|
||||
case Delta::Action::RECREATE_OBJECT:
|
||||
case Delta::Action::DELETE_DESERIALIZED_OBJECT:
|
||||
case Delta::Action::DELETE_OBJECT:
|
||||
break;
|
||||
}
|
||||
});
|
||||
return link;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace memgraph::storage {
|
||||
|
||||
bool InMemoryEdgeTypeIndex::CreateIndex(EdgeTypeId edge_type, utils::SkipList<Vertex>::Accessor vertices) {
|
||||
auto [it, emplaced] = index_.try_emplace(edge_type);
|
||||
if (!emplaced) {
|
||||
return false;
|
||||
}
|
||||
|
||||
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
|
||||
try {
|
||||
auto edge_acc = it->second.access();
|
||||
for (auto &from_vertex : vertices) {
|
||||
if (from_vertex.deleted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto &edge : from_vertex.out_edges) {
|
||||
const auto type = std::get<kEdgeTypeIdPos>(edge);
|
||||
if (type == edge_type) {
|
||||
auto *to_vertex = std::get<kVertexPos>(edge);
|
||||
if (to_vertex->deleted) {
|
||||
continue;
|
||||
}
|
||||
edge_acc.insert({&from_vertex, to_vertex, std::get<kEdgeRefPos>(edge).ptr, 0});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const utils::OutOfMemoryException &) {
|
||||
utils::MemoryTracker::OutOfMemoryExceptionBlocker oom_exception_blocker;
|
||||
index_.erase(it);
|
||||
throw;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InMemoryEdgeTypeIndex::DropIndex(EdgeTypeId edge_type) { return index_.erase(edge_type) > 0; }
|
||||
|
||||
bool InMemoryEdgeTypeIndex::IndexExists(EdgeTypeId edge_type) const { return index_.find(edge_type) != index_.end(); }
|
||||
|
||||
std::vector<EdgeTypeId> InMemoryEdgeTypeIndex::ListIndices() const {
|
||||
std::vector<EdgeTypeId> ret;
|
||||
ret.reserve(index_.size());
|
||||
for (const auto &item : index_) {
|
||||
ret.push_back(item.first);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void InMemoryEdgeTypeIndex::RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp, std::stop_token token) {
|
||||
auto maybe_stop = utils::ResettableCounter<2048>();
|
||||
|
||||
for (auto &label_storage : index_) {
|
||||
if (token.stop_requested()) return;
|
||||
|
||||
auto edges_acc = label_storage.second.access();
|
||||
for (auto it = edges_acc.begin(); it != edges_acc.end();) {
|
||||
if (maybe_stop() && token.stop_requested()) return;
|
||||
|
||||
auto next_it = it;
|
||||
++next_it;
|
||||
|
||||
if (it->timestamp >= oldest_active_start_timestamp) {
|
||||
it = next_it;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (next_it != edges_acc.end() || it->from_vertex->deleted || it->to_vertex->deleted ||
|
||||
!std::ranges::all_of(it->from_vertex->out_edges, [&](const auto &edge) {
|
||||
auto *to_vertex = std::get<InMemoryEdgeTypeIndex::kVertexPos>(edge);
|
||||
return to_vertex != it->to_vertex;
|
||||
})) {
|
||||
edges_acc.remove(*it);
|
||||
}
|
||||
|
||||
it = next_it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t InMemoryEdgeTypeIndex::ApproximateEdgeCount(EdgeTypeId edge_type) const {
|
||||
if (auto it = index_.find(edge_type); it != index_.end()) {
|
||||
return it->second.size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InMemoryEdgeTypeIndex::UpdateOnEdgeCreation(Vertex *from, Vertex *to, EdgeRef edge_ref, EdgeTypeId edge_type,
|
||||
const Transaction &tx) {
|
||||
auto it = index_.find(edge_type);
|
||||
if (it == index_.end()) {
|
||||
return;
|
||||
}
|
||||
auto acc = it->second.access();
|
||||
acc.insert(Entry{from, to, edge_ref.ptr, tx.start_timestamp});
|
||||
}
|
||||
|
||||
void InMemoryEdgeTypeIndex::UpdateOnEdgeModification(Vertex *old_from, Vertex *old_to, Vertex *new_from, Vertex *new_to,
|
||||
EdgeRef edge_ref, EdgeTypeId edge_type, const Transaction &tx) {
|
||||
auto it = index_.find(edge_type);
|
||||
if (it == index_.end()) {
|
||||
return;
|
||||
}
|
||||
auto acc = it->second.access();
|
||||
|
||||
auto entry_to_update = std::ranges::find_if(acc, [&](const auto &entry) {
|
||||
return entry.from_vertex == old_from && entry.to_vertex == old_to && entry.edge == edge_ref.ptr;
|
||||
});
|
||||
|
||||
acc.remove(Entry{entry_to_update->from_vertex, entry_to_update->to_vertex, entry_to_update->edge,
|
||||
entry_to_update->timestamp});
|
||||
acc.insert(Entry{new_from, new_to, edge_ref.ptr, tx.start_timestamp});
|
||||
}
|
||||
|
||||
InMemoryEdgeTypeIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_accessor, EdgeTypeId edge_type,
|
||||
View view, Storage *storage, Transaction *transaction)
|
||||
: index_accessor_(std::move(index_accessor)),
|
||||
edge_type_(edge_type),
|
||||
view_(view),
|
||||
storage_(storage),
|
||||
transaction_(transaction) {}
|
||||
|
||||
InMemoryEdgeTypeIndex::Iterable::Iterator::Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator)
|
||||
: self_(self),
|
||||
index_iterator_(index_iterator),
|
||||
current_edge_accessor_(EdgeRef{nullptr}, EdgeTypeId::FromInt(0), nullptr, nullptr, self_->storage_, nullptr),
|
||||
current_edge_(nullptr) {
|
||||
AdvanceUntilValid();
|
||||
}
|
||||
|
||||
InMemoryEdgeTypeIndex::Iterable::Iterator &InMemoryEdgeTypeIndex::Iterable::Iterator::operator++() {
|
||||
++index_iterator_;
|
||||
AdvanceUntilValid();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void InMemoryEdgeTypeIndex::Iterable::Iterator::AdvanceUntilValid() {
|
||||
for (; index_iterator_ != self_->index_accessor_.end(); ++index_iterator_) {
|
||||
auto *from_vertex = index_iterator_->from_vertex;
|
||||
auto *to_vertex = index_iterator_->to_vertex;
|
||||
|
||||
if (!IsIndexEntryVisible(index_iterator_->edge, self_->transaction_, self_->view_) || from_vertex->deleted ||
|
||||
to_vertex->deleted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool edge_was_deleted = index_iterator_->edge->deleted;
|
||||
auto [edge_ref, edge_type, deleted_from_vertex, deleted_to_vertex] = GetEdgeInfo();
|
||||
MG_ASSERT(edge_ref != EdgeRef(nullptr), "Invalid database state!");
|
||||
|
||||
if (edge_was_deleted) {
|
||||
from_vertex = deleted_from_vertex;
|
||||
to_vertex = deleted_to_vertex;
|
||||
}
|
||||
|
||||
auto accessor = EdgeAccessor{edge_ref, edge_type, from_vertex, to_vertex, self_->storage_, self_->transaction_};
|
||||
if (!accessor.IsVisible(self_->view_)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
current_edge_accessor_ = accessor;
|
||||
current_edge_ = edge_ref;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<EdgeRef, EdgeTypeId, Vertex *, Vertex *> InMemoryEdgeTypeIndex::Iterable::Iterator::GetEdgeInfo() {
|
||||
auto *from_vertex = index_iterator_->from_vertex;
|
||||
auto *to_vertex = index_iterator_->to_vertex;
|
||||
|
||||
if (index_iterator_->edge->deleted) {
|
||||
const auto missing_in_edge =
|
||||
VertexDeletedConnectedEdges(from_vertex, index_iterator_->edge, self_->transaction_, self_->view_);
|
||||
const auto missing_out_edge =
|
||||
VertexDeletedConnectedEdges(to_vertex, index_iterator_->edge, self_->transaction_, self_->view_);
|
||||
if (missing_in_edge && missing_out_edge &&
|
||||
std::get<kEdgeRefPos>(*missing_in_edge) == std::get<kEdgeRefPos>(*missing_out_edge)) {
|
||||
return std::make_tuple(std::get<kEdgeRefPos>(*missing_in_edge), std::get<kEdgeTypeIdPos>(*missing_in_edge),
|
||||
to_vertex, from_vertex);
|
||||
}
|
||||
}
|
||||
|
||||
const auto &from_edges = from_vertex->out_edges;
|
||||
const auto &to_edges = to_vertex->in_edges;
|
||||
|
||||
auto it = std::find_if(from_edges.begin(), from_edges.end(), [&](const auto &from_entry) {
|
||||
const auto &from_edge = std::get<kEdgeRefPos>(from_entry);
|
||||
return std::any_of(to_edges.begin(), to_edges.end(), [&](const auto &to_entry) {
|
||||
const auto &to_edge = std::get<kEdgeRefPos>(to_entry);
|
||||
return index_iterator_->edge->gid == from_edge.ptr->gid && from_edge.ptr->gid == to_edge.ptr->gid;
|
||||
});
|
||||
});
|
||||
|
||||
if (it != from_edges.end()) {
|
||||
const auto &from_edge = std::get<kEdgeRefPos>(*it);
|
||||
return std::make_tuple(from_edge, std::get<kEdgeTypeIdPos>(*it), from_vertex, to_vertex);
|
||||
}
|
||||
|
||||
return {EdgeRef(nullptr), EdgeTypeId::FromUint(0U), nullptr, nullptr};
|
||||
}
|
||||
|
||||
void InMemoryEdgeTypeIndex::RunGC() {
|
||||
for (auto &index_entry : index_) {
|
||||
index_entry.second.run_gc();
|
||||
}
|
||||
}
|
||||
|
||||
InMemoryEdgeTypeIndex::Iterable InMemoryEdgeTypeIndex::Edges(EdgeTypeId edge_type, View view, Storage *storage,
|
||||
Transaction *transaction) {
|
||||
const auto it = index_.find(edge_type);
|
||||
MG_ASSERT(it != index_.end(), "Index for edge-type {} doesn't exist", edge_type.AsUint());
|
||||
return {it->second.access(), edge_type, view, storage, transaction};
|
||||
}
|
||||
|
||||
} // namespace memgraph::storage
|
113
src/storage/v2/inmemory/edge_type_index.hpp
Normal file
113
src/storage/v2/inmemory/edge_type_index.hpp
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright 2024 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 <map>
|
||||
#include <utility>
|
||||
|
||||
#include "storage/v2/constraints/constraints.hpp"
|
||||
#include "storage/v2/edge_accessor.hpp"
|
||||
#include "storage/v2/id_types.hpp"
|
||||
#include "storage/v2/indices/edge_type_index.hpp"
|
||||
#include "storage/v2/indices/label_index_stats.hpp"
|
||||
#include "utils/rw_lock.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
|
||||
namespace memgraph::storage {
|
||||
|
||||
class InMemoryEdgeTypeIndex : public storage::EdgeTypeIndex {
|
||||
private:
|
||||
struct Entry {
|
||||
Vertex *from_vertex;
|
||||
Vertex *to_vertex;
|
||||
|
||||
Edge *edge;
|
||||
|
||||
uint64_t timestamp;
|
||||
|
||||
bool operator<(const Entry &rhs) const { return edge->gid < rhs.edge->gid; }
|
||||
bool operator==(const Entry &rhs) const { return edge->gid == rhs.edge->gid; }
|
||||
};
|
||||
|
||||
public:
|
||||
InMemoryEdgeTypeIndex() = default;
|
||||
|
||||
/// @throw std::bad_alloc
|
||||
bool CreateIndex(EdgeTypeId edge_type, utils::SkipList<Vertex>::Accessor vertices);
|
||||
|
||||
/// Returns false if there was no index to drop
|
||||
bool DropIndex(EdgeTypeId edge_type) override;
|
||||
|
||||
bool IndexExists(EdgeTypeId edge_type) const override;
|
||||
|
||||
std::vector<EdgeTypeId> ListIndices() const override;
|
||||
|
||||
void RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp, std::stop_token token);
|
||||
|
||||
uint64_t ApproximateEdgeCount(EdgeTypeId edge_type) const override;
|
||||
|
||||
void UpdateOnEdgeCreation(Vertex *from, Vertex *to, EdgeRef edge_ref, EdgeTypeId edge_type,
|
||||
const Transaction &tx) override;
|
||||
|
||||
void UpdateOnEdgeModification(Vertex *old_from, Vertex *old_to, Vertex *new_from, Vertex *new_to, EdgeRef edge_ref,
|
||||
EdgeTypeId edge_type, const Transaction &tx) override;
|
||||
|
||||
static constexpr std::size_t kEdgeTypeIdPos = 0U;
|
||||
static constexpr std::size_t kVertexPos = 1U;
|
||||
static constexpr std::size_t kEdgeRefPos = 2U;
|
||||
|
||||
class Iterable {
|
||||
public:
|
||||
Iterable(utils::SkipList<Entry>::Accessor index_accessor, EdgeTypeId edge_type, View view, Storage *storage,
|
||||
Transaction *transaction);
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator);
|
||||
|
||||
EdgeAccessor const &operator*() const { return current_edge_accessor_; }
|
||||
|
||||
bool operator==(const Iterator &other) const { return index_iterator_ == other.index_iterator_; }
|
||||
bool operator!=(const Iterator &other) const { return index_iterator_ != other.index_iterator_; }
|
||||
|
||||
Iterator &operator++();
|
||||
|
||||
private:
|
||||
void AdvanceUntilValid();
|
||||
std::tuple<EdgeRef, EdgeTypeId, Vertex *, Vertex *> GetEdgeInfo();
|
||||
|
||||
Iterable *self_;
|
||||
utils::SkipList<Entry>::Iterator index_iterator_;
|
||||
EdgeAccessor current_edge_accessor_;
|
||||
EdgeRef current_edge_{nullptr};
|
||||
};
|
||||
|
||||
Iterator begin() { return {this, index_accessor_.begin()}; }
|
||||
Iterator end() { return {this, index_accessor_.end()}; }
|
||||
|
||||
private:
|
||||
utils::SkipList<Entry>::Accessor index_accessor_;
|
||||
EdgeTypeId edge_type_;
|
||||
View view_;
|
||||
Storage *storage_;
|
||||
Transaction *transaction_;
|
||||
};
|
||||
|
||||
void RunGC();
|
||||
|
||||
Iterable Edges(EdgeTypeId edge_type, View view, Storage *storage, Transaction *transaction);
|
||||
|
||||
private:
|
||||
std::map<EdgeTypeId, utils::SkipList<Entry>> index_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::storage
|
@ -20,6 +20,7 @@
|
||||
#include "storage/v2/durability/snapshot.hpp"
|
||||
#include "storage/v2/edge_direction.hpp"
|
||||
#include "storage/v2/id_types.hpp"
|
||||
#include "storage/v2/inmemory/edge_type_index.hpp"
|
||||
#include "storage/v2/metadata_delta.hpp"
|
||||
|
||||
/// REPLICATION ///
|
||||
@ -350,6 +351,9 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::CreateEdge(VertexAccesso
|
||||
transaction_.manyDeltasCache.Invalidate(from_vertex, edge_type, EdgeDirection::OUT);
|
||||
transaction_.manyDeltasCache.Invalidate(to_vertex, edge_type, EdgeDirection::IN);
|
||||
|
||||
// Update indices if they exist.
|
||||
storage_->indices_.UpdateOnEdgeCreation(from_vertex, to_vertex, edge, edge_type, transaction_);
|
||||
|
||||
// Increment edge count.
|
||||
storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
|
||||
}};
|
||||
@ -553,6 +557,11 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::EdgeSetFrom(EdgeAccessor
|
||||
CreateAndLinkDelta(&transaction_, to_vertex, Delta::RemoveInEdgeTag(), edge_type, new_from_vertex, edge_ref);
|
||||
to_vertex->in_edges.emplace_back(edge_type, new_from_vertex, edge_ref);
|
||||
|
||||
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
|
||||
auto *mem_edge_type_index = static_cast<InMemoryEdgeTypeIndex *>(in_memory->indices_.edge_type_index_.get());
|
||||
mem_edge_type_index->UpdateOnEdgeModification(old_from_vertex, to_vertex, new_from_vertex, to_vertex, edge_ref,
|
||||
edge_type, transaction_);
|
||||
|
||||
transaction_.manyDeltasCache.Invalidate(new_from_vertex, edge_type, EdgeDirection::OUT);
|
||||
transaction_.manyDeltasCache.Invalidate(old_from_vertex, edge_type, EdgeDirection::OUT);
|
||||
transaction_.manyDeltasCache.Invalidate(to_vertex, edge_type, EdgeDirection::IN);
|
||||
@ -659,6 +668,11 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::EdgeSetTo(EdgeAccessor *
|
||||
CreateAndLinkDelta(&transaction_, new_to_vertex, Delta::RemoveInEdgeTag(), edge_type, from_vertex, edge_ref);
|
||||
new_to_vertex->in_edges.emplace_back(edge_type, from_vertex, edge_ref);
|
||||
|
||||
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
|
||||
auto *mem_edge_type_index = static_cast<InMemoryEdgeTypeIndex *>(in_memory->indices_.edge_type_index_.get());
|
||||
mem_edge_type_index->UpdateOnEdgeModification(from_vertex, old_to_vertex, from_vertex, new_to_vertex, edge_ref,
|
||||
edge_type, transaction_);
|
||||
|
||||
transaction_.manyDeltasCache.Invalidate(from_vertex, edge_type, EdgeDirection::OUT);
|
||||
transaction_.manyDeltasCache.Invalidate(old_to_vertex, edge_type, EdgeDirection::IN);
|
||||
transaction_.manyDeltasCache.Invalidate(new_to_vertex, edge_type, EdgeDirection::IN);
|
||||
@ -1264,6 +1278,18 @@ utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryA
|
||||
return {};
|
||||
}
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryAccessor::CreateIndex(
|
||||
EdgeTypeId edge_type) {
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Create index requires a unique access to the storage!");
|
||||
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
|
||||
auto *mem_edge_type_index = static_cast<InMemoryEdgeTypeIndex *>(in_memory->indices_.edge_type_index_.get());
|
||||
if (!mem_edge_type_index->CreateIndex(edge_type, in_memory->vertices_.access())) {
|
||||
return StorageIndexDefinitionError{IndexDefinitionError{}};
|
||||
}
|
||||
transaction_.md_deltas.emplace_back(MetadataDelta::edge_index_create, edge_type);
|
||||
return {};
|
||||
}
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryAccessor::DropIndex(LabelId label) {
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Dropping label index requires a unique access to the storage!");
|
||||
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
|
||||
@ -1292,6 +1318,18 @@ utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryA
|
||||
return {};
|
||||
}
|
||||
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::InMemoryAccessor::DropIndex(
|
||||
EdgeTypeId edge_type) {
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Drop index requires a unique access to the storage!");
|
||||
auto *in_memory = static_cast<InMemoryStorage *>(storage_);
|
||||
auto *mem_edge_type_index = static_cast<InMemoryEdgeTypeIndex *>(in_memory->indices_.edge_type_index_.get());
|
||||
if (!mem_edge_type_index->DropIndex(edge_type)) {
|
||||
return StorageIndexDefinitionError{IndexDefinitionError{}};
|
||||
}
|
||||
transaction_.md_deltas.emplace_back(MetadataDelta::edge_index_drop, edge_type);
|
||||
return {};
|
||||
}
|
||||
|
||||
utils::BasicResult<StorageExistenceConstraintDefinitionError, void>
|
||||
InMemoryStorage::InMemoryAccessor::CreateExistenceConstraint(LabelId label, PropertyId property) {
|
||||
MG_ASSERT(unique_guard_.owns_lock(), "Creating existence requires a unique access to the storage!");
|
||||
@ -1383,6 +1421,11 @@ VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(
|
||||
mem_label_property_index->Vertices(label, property, lower_bound, upper_bound, view, storage_, &transaction_));
|
||||
}
|
||||
|
||||
EdgesIterable InMemoryStorage::InMemoryAccessor::Edges(EdgeTypeId edge_type, View view) {
|
||||
auto *mem_edge_type_index = static_cast<InMemoryEdgeTypeIndex *>(storage_->indices_.edge_type_index_.get());
|
||||
return EdgesIterable(mem_edge_type_index->Edges(edge_type, view, storage_, &transaction_));
|
||||
}
|
||||
|
||||
Transaction InMemoryStorage::CreateTransaction(
|
||||
IsolationLevel isolation_level, StorageMode storage_mode,
|
||||
memgraph::replication_coordination_glue::ReplicationRole replication_role) {
|
||||
@ -2017,6 +2060,10 @@ bool InMemoryStorage::AppendToWal(const Transaction &transaction, uint64_t final
|
||||
AppendToWalDataDefinition(durability::StorageMetadataOperation::LABEL_INDEX_CREATE, md_delta.label,
|
||||
final_commit_timestamp);
|
||||
} break;
|
||||
case MetadataDelta::Action::EDGE_INDEX_CREATE: {
|
||||
AppendToWalDataDefinition(durability::StorageMetadataOperation::EDGE_TYPE_INDEX_CREATE, md_delta.edge_type,
|
||||
final_commit_timestamp);
|
||||
} break;
|
||||
case MetadataDelta::Action::LABEL_PROPERTY_INDEX_CREATE: {
|
||||
const auto &info = md_delta.label_property;
|
||||
AppendToWalDataDefinition(durability::StorageMetadataOperation::LABEL_PROPERTY_INDEX_CREATE, info.label,
|
||||
@ -2026,6 +2073,10 @@ bool InMemoryStorage::AppendToWal(const Transaction &transaction, uint64_t final
|
||||
AppendToWalDataDefinition(durability::StorageMetadataOperation::LABEL_INDEX_DROP, md_delta.label,
|
||||
final_commit_timestamp);
|
||||
} break;
|
||||
case MetadataDelta::Action::EDGE_INDEX_DROP: {
|
||||
AppendToWalDataDefinition(durability::StorageMetadataOperation::EDGE_TYPE_INDEX_DROP, md_delta.edge_type,
|
||||
final_commit_timestamp);
|
||||
} break;
|
||||
case MetadataDelta::Action::LABEL_PROPERTY_INDEX_DROP: {
|
||||
const auto &info = md_delta.label_property;
|
||||
AppendToWalDataDefinition(durability::StorageMetadataOperation::LABEL_PROPERTY_INDEX_DROP, info.label,
|
||||
@ -2091,6 +2142,12 @@ void InMemoryStorage::AppendToWalDataDefinition(durability::StorageMetadataOpera
|
||||
repl_storage_state_.AppendOperation(operation, label, properties, stats, property_stats, final_commit_timestamp);
|
||||
}
|
||||
|
||||
void InMemoryStorage::AppendToWalDataDefinition(durability::StorageMetadataOperation operation, EdgeTypeId edge_type,
|
||||
uint64_t final_commit_timestamp) {
|
||||
wal_file_->AppendOperation(operation, edge_type, final_commit_timestamp);
|
||||
repl_storage_state_.AppendOperation(operation, edge_type, final_commit_timestamp);
|
||||
}
|
||||
|
||||
void InMemoryStorage::AppendToWalDataDefinition(durability::StorageMetadataOperation operation, LabelId label,
|
||||
const std::set<PropertyId> &properties,
|
||||
LabelPropertyIndexStats property_stats,
|
||||
@ -2240,7 +2297,8 @@ IndicesInfo InMemoryStorage::InMemoryAccessor::ListAllIndices() const {
|
||||
auto *mem_label_index = static_cast<InMemoryLabelIndex *>(in_memory->indices_.label_index_.get());
|
||||
auto *mem_label_property_index =
|
||||
static_cast<InMemoryLabelPropertyIndex *>(in_memory->indices_.label_property_index_.get());
|
||||
return {mem_label_index->ListIndices(), mem_label_property_index->ListIndices()};
|
||||
auto *mem_edge_type_index = static_cast<InMemoryEdgeTypeIndex *>(in_memory->indices_.edge_type_index_.get());
|
||||
return {mem_label_index->ListIndices(), mem_label_property_index->ListIndices(), mem_edge_type_index->ListIndices()};
|
||||
}
|
||||
ConstraintsInfo InMemoryStorage::InMemoryAccessor::ListAllConstraints() const {
|
||||
const auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "storage/v2/indices/label_index_stats.hpp"
|
||||
#include "storage/v2/inmemory/edge_type_index.hpp"
|
||||
#include "storage/v2/inmemory/label_index.hpp"
|
||||
#include "storage/v2/inmemory/label_property_index.hpp"
|
||||
#include "storage/v2/inmemory/replication/recovery.hpp"
|
||||
@ -53,6 +54,7 @@ class InMemoryStorage final : public Storage {
|
||||
const InMemoryStorage *storage);
|
||||
friend class InMemoryLabelIndex;
|
||||
friend class InMemoryLabelPropertyIndex;
|
||||
friend class InMemoryEdgeTypeIndex;
|
||||
|
||||
public:
|
||||
enum class CreateSnapshotError : uint8_t { DisabledForReplica, ReachedMaxNumTries };
|
||||
@ -107,6 +109,8 @@ class InMemoryStorage final : public Storage {
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) override;
|
||||
|
||||
EdgesIterable Edges(EdgeTypeId edge_type, View view) override;
|
||||
|
||||
/// Return approximate number of all vertices in the database.
|
||||
/// Note that this is always an over-estimate and never an under-estimate.
|
||||
uint64_t ApproximateVertexCount() const override {
|
||||
@ -145,6 +149,10 @@ class InMemoryStorage final : public Storage {
|
||||
label, property, lower, upper);
|
||||
}
|
||||
|
||||
uint64_t ApproximateEdgeCount(EdgeTypeId id) const override {
|
||||
return static_cast<InMemoryStorage *>(storage_)->indices_.edge_type_index_->ApproximateEdgeCount(id);
|
||||
}
|
||||
|
||||
template <typename TResult, typename TIndex, typename TIndexKey>
|
||||
std::optional<TResult> GetIndexStatsForIndex(TIndex *index, TIndexKey &&key) const {
|
||||
return index->GetIndexStats(key);
|
||||
@ -204,6 +212,10 @@ class InMemoryStorage final : public Storage {
|
||||
return static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index_->IndexExists(label, property);
|
||||
}
|
||||
|
||||
bool EdgeTypeIndexExists(EdgeTypeId edge_type) const override {
|
||||
return static_cast<InMemoryStorage *>(storage_)->indices_.edge_type_index_->IndexExists(edge_type);
|
||||
}
|
||||
|
||||
IndicesInfo ListAllIndices() const override;
|
||||
|
||||
ConstraintsInfo ListAllConstraints() const override;
|
||||
@ -239,6 +251,14 @@ class InMemoryStorage final : public Storage {
|
||||
/// @throw std::bad_alloc
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(LabelId label, PropertyId property) override;
|
||||
|
||||
/// Create an index.
|
||||
/// Returns void if the index has been created.
|
||||
/// Returns `StorageIndexDefinitionError` if an error occures. Error can be:
|
||||
/// * `ReplicationError`: there is at least one SYNC replica that has not confirmed receiving the transaction.
|
||||
/// * `IndexDefinitionError`: the index already exists.
|
||||
/// @throw std::bad_alloc
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(EdgeTypeId edge_type) override;
|
||||
|
||||
/// Drop an existing index.
|
||||
/// Returns void if the index has been dropped.
|
||||
/// Returns `StorageIndexDefinitionError` if an error occures. Error can be:
|
||||
@ -253,6 +273,13 @@ class InMemoryStorage final : public Storage {
|
||||
/// * `IndexDefinitionError`: the index does not exist.
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(LabelId label, PropertyId property) override;
|
||||
|
||||
/// Drop an existing index.
|
||||
/// Returns void if the index has been dropped.
|
||||
/// Returns `StorageIndexDefinitionError` if an error occures. Error can be:
|
||||
/// * `ReplicationError`: there is at least one SYNC replica that has not confirmed receiving the transaction.
|
||||
/// * `IndexDefinitionError`: the index does not exist.
|
||||
utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(EdgeTypeId edge_type) override;
|
||||
|
||||
/// Returns void if the existence constraint has been created.
|
||||
/// Returns `StorageExistenceConstraintDefinitionError` if an error occures. Error can be:
|
||||
/// * `ReplicationError`: there is at least one SYNC replica that has not confirmed receiving the transaction.
|
||||
@ -374,20 +401,17 @@ class InMemoryStorage final : public Storage {
|
||||
/// Return true in all cases excepted if any sync replicas have not sent confirmation.
|
||||
[[nodiscard]] bool AppendToWal(const Transaction &transaction, uint64_t final_commit_timestamp,
|
||||
DatabaseAccessProtector db_acc);
|
||||
/// Return true in all cases excepted if any sync replicas have not sent confirmation.
|
||||
void AppendToWalDataDefinition(durability::StorageMetadataOperation operation, LabelId label,
|
||||
uint64_t final_commit_timestamp);
|
||||
/// Return true in all cases excepted if any sync replicas have not sent confirmation.
|
||||
void AppendToWalDataDefinition(durability::StorageMetadataOperation operation, EdgeTypeId edge_type,
|
||||
uint64_t final_commit_timestamp);
|
||||
void AppendToWalDataDefinition(durability::StorageMetadataOperation operation, LabelId label,
|
||||
const std::set<PropertyId> &properties, uint64_t final_commit_timestamp);
|
||||
/// Return true in all cases excepted if any sync replicas have not sent confirmation.
|
||||
void AppendToWalDataDefinition(durability::StorageMetadataOperation operation, LabelId label, LabelIndexStats stats,
|
||||
uint64_t final_commit_timestamp);
|
||||
/// Return true in all cases excepted if any sync replicas have not sent confirmation.
|
||||
void AppendToWalDataDefinition(durability::StorageMetadataOperation operation, LabelId label,
|
||||
const std::set<PropertyId> &properties, LabelPropertyIndexStats property_stats,
|
||||
uint64_t final_commit_timestamp);
|
||||
/// Return true in all cases excepted if any sync replicas have not sent confirmation.
|
||||
void AppendToWalDataDefinition(durability::StorageMetadataOperation operation, LabelId label,
|
||||
const std::set<PropertyId> &properties, LabelIndexStats stats,
|
||||
LabelPropertyIndexStats property_stats, uint64_t final_commit_timestamp);
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -35,6 +35,8 @@ struct MetadataDelta {
|
||||
LABEL_PROPERTY_INDEX_DROP,
|
||||
LABEL_PROPERTY_INDEX_STATS_SET,
|
||||
LABEL_PROPERTY_INDEX_STATS_CLEAR,
|
||||
EDGE_INDEX_CREATE,
|
||||
EDGE_INDEX_DROP,
|
||||
EXISTENCE_CONSTRAINT_CREATE,
|
||||
EXISTENCE_CONSTRAINT_DROP,
|
||||
UNIQUE_CONSTRAINT_CREATE,
|
||||
@ -57,6 +59,10 @@ struct MetadataDelta {
|
||||
} label_property_index_stats_set;
|
||||
static constexpr struct LabelPropertyIndexStatsClear {
|
||||
} label_property_index_stats_clear;
|
||||
static constexpr struct EdgeIndexCreate {
|
||||
} edge_index_create;
|
||||
static constexpr struct EdgeIndexDrop {
|
||||
} edge_index_drop;
|
||||
static constexpr struct ExistenceConstraintCreate {
|
||||
} existence_constraint_create;
|
||||
static constexpr struct ExistenceConstraintDrop {
|
||||
@ -87,6 +93,11 @@ struct MetadataDelta {
|
||||
MetadataDelta(LabelPropertyIndexStatsClear /*tag*/, LabelId label)
|
||||
: action(Action::LABEL_PROPERTY_INDEX_STATS_CLEAR), label{label} {}
|
||||
|
||||
MetadataDelta(EdgeIndexCreate /*tag*/, EdgeTypeId edge_type)
|
||||
: action(Action::EDGE_INDEX_CREATE), edge_type(edge_type) {}
|
||||
|
||||
MetadataDelta(EdgeIndexDrop /*tag*/, EdgeTypeId edge_type) : action(Action::EDGE_INDEX_DROP), edge_type(edge_type) {}
|
||||
|
||||
MetadataDelta(ExistenceConstraintCreate /*tag*/, LabelId label, PropertyId property)
|
||||
: action(Action::EXISTENCE_CONSTRAINT_CREATE), label_property{label, property} {}
|
||||
|
||||
@ -114,6 +125,8 @@ struct MetadataDelta {
|
||||
case Action::LABEL_PROPERTY_INDEX_DROP:
|
||||
case Action::LABEL_PROPERTY_INDEX_STATS_SET:
|
||||
case Action::LABEL_PROPERTY_INDEX_STATS_CLEAR:
|
||||
case Action::EDGE_INDEX_CREATE:
|
||||
case Action::EDGE_INDEX_DROP:
|
||||
case Action::EXISTENCE_CONSTRAINT_CREATE:
|
||||
case Action::EXISTENCE_CONSTRAINT_DROP:
|
||||
break;
|
||||
@ -129,6 +142,8 @@ struct MetadataDelta {
|
||||
union {
|
||||
LabelId label;
|
||||
|
||||
EdgeTypeId edge_type;
|
||||
|
||||
struct {
|
||||
LabelId label;
|
||||
PropertyId property;
|
||||
|
@ -407,6 +407,12 @@ void ReplicaStream::AppendOperation(durability::StorageMetadataOperation operati
|
||||
timestamp);
|
||||
}
|
||||
|
||||
void ReplicaStream::AppendOperation(durability::StorageMetadataOperation operation, EdgeTypeId edge_type,
|
||||
uint64_t timestamp) {
|
||||
replication::Encoder encoder(stream_.GetBuilder());
|
||||
EncodeOperation(&encoder, storage_->name_id_mapper_.get(), operation, edge_type, timestamp);
|
||||
}
|
||||
|
||||
replication::AppendDeltasRes ReplicaStream::Finalize() { return stream_.AwaitResponse(); }
|
||||
|
||||
} // namespace memgraph::storage
|
||||
|
@ -65,6 +65,9 @@ class ReplicaStream {
|
||||
const std::set<PropertyId> &properties, const LabelIndexStats &stats,
|
||||
const LabelPropertyIndexStats &property_stats, uint64_t timestamp);
|
||||
|
||||
/// @throw rpc::RpcFailedException
|
||||
void AppendOperation(durability::StorageMetadataOperation operation, EdgeTypeId edge_type, uint64_t timestamp);
|
||||
|
||||
/// @throw rpc::RpcFailedException
|
||||
replication::AppendDeltasRes Finalize();
|
||||
|
||||
|
@ -53,6 +53,16 @@ void ReplicationStorageState::AppendOperation(durability::StorageMetadataOperati
|
||||
});
|
||||
}
|
||||
|
||||
void ReplicationStorageState::AppendOperation(durability::StorageMetadataOperation operation, EdgeTypeId edge_type,
|
||||
uint64_t final_commit_timestamp) {
|
||||
replication_clients_.WithLock([&](auto &clients) {
|
||||
for (auto &client : clients) {
|
||||
client->IfStreamingTransaction(
|
||||
[&](auto &stream) { stream.AppendOperation(operation, edge_type, final_commit_timestamp); });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool ReplicationStorageState::FinalizeTransaction(uint64_t timestamp, Storage *storage,
|
||||
DatabaseAccessProtector db_acc) {
|
||||
return replication_clients_.WithLock([=, db_acc = std::move(db_acc)](auto &clients) mutable {
|
||||
|
@ -46,6 +46,8 @@ struct ReplicationStorageState {
|
||||
void AppendOperation(durability::StorageMetadataOperation operation, LabelId label,
|
||||
const std::set<PropertyId> &properties, const LabelIndexStats &stats,
|
||||
const LabelPropertyIndexStats &property_stats, uint64_t final_commit_timestamp);
|
||||
void AppendOperation(durability::StorageMetadataOperation operation, EdgeTypeId edge_type,
|
||||
uint64_t final_commit_timestamp);
|
||||
bool FinalizeTransaction(uint64_t timestamp, Storage *storage, DatabaseAccessProtector db_acc);
|
||||
|
||||
// Getters
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "storage/v2/durability/paths.hpp"
|
||||
#include "storage/v2/durability/wal.hpp"
|
||||
#include "storage/v2/edge_accessor.hpp"
|
||||
#include "storage/v2/edges_iterable.hpp"
|
||||
#include "storage/v2/indices/indices.hpp"
|
||||
#include "storage/v2/mvcc.hpp"
|
||||
#include "storage/v2/replication/enums.hpp"
|
||||
@ -61,6 +62,7 @@ class EdgeAccessor;
|
||||
struct IndicesInfo {
|
||||
std::vector<LabelId> label;
|
||||
std::vector<std::pair<LabelId, PropertyId>> label_property;
|
||||
std::vector<EdgeTypeId> edge_type;
|
||||
};
|
||||
|
||||
struct ConstraintsInfo {
|
||||
@ -172,6 +174,8 @@ class Storage {
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) = 0;
|
||||
|
||||
virtual EdgesIterable Edges(EdgeTypeId edge_type, View view) = 0;
|
||||
|
||||
virtual Result<std::optional<VertexAccessor>> DeleteVertex(VertexAccessor *vertex);
|
||||
|
||||
virtual Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> DetachDeleteVertex(
|
||||
@ -192,6 +196,8 @@ class Storage {
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper) const = 0;
|
||||
|
||||
virtual uint64_t ApproximateEdgeCount(EdgeTypeId id) const = 0;
|
||||
|
||||
virtual std::optional<storage::LabelIndexStats> GetIndexStats(const storage::LabelId &label) const = 0;
|
||||
|
||||
virtual std::optional<storage::LabelPropertyIndexStats> GetIndexStats(
|
||||
@ -224,6 +230,8 @@ class Storage {
|
||||
|
||||
virtual bool LabelPropertyIndexExists(LabelId label, PropertyId property) const = 0;
|
||||
|
||||
virtual bool EdgeTypeIndexExists(EdgeTypeId edge_type) const = 0;
|
||||
|
||||
virtual IndicesInfo ListAllIndices() const = 0;
|
||||
|
||||
virtual ConstraintsInfo ListAllConstraints() const = 0;
|
||||
@ -268,10 +276,14 @@ class Storage {
|
||||
|
||||
virtual utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(LabelId label, PropertyId property) = 0;
|
||||
|
||||
virtual utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(EdgeTypeId edge_type) = 0;
|
||||
|
||||
virtual utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(LabelId label) = 0;
|
||||
|
||||
virtual utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(LabelId label, PropertyId property) = 0;
|
||||
|
||||
virtual utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(EdgeTypeId edge_type) = 0;
|
||||
|
||||
virtual utils::BasicResult<StorageExistenceConstraintDefinitionError, void> CreateExistenceConstraint(
|
||||
LabelId label, PropertyId property) = 0;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -10,7 +10,6 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "storage/v2/vertices_iterable.hpp"
|
||||
|
||||
namespace memgraph::storage {
|
||||
|
||||
VerticesIterable::VerticesIterable(AllVerticesIterable vertices) : type_(Type::ALL) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
|
@ -26,6 +26,7 @@
|
||||
M(ScanAllByLabelPropertyValueOperator, Operator, "Number of times ScanAllByLabelPropertyValue operator was used.") \
|
||||
M(ScanAllByLabelPropertyOperator, Operator, "Number of times ScanAllByLabelProperty operator was used.") \
|
||||
M(ScanAllByIdOperator, Operator, "Number of times ScanAllById operator was used.") \
|
||||
M(ScanAllByEdgeTypeOperator, Operator, "Number of times ScanAllByEdgeTypeOperator operator was used.") \
|
||||
M(ExpandOperator, Operator, "Number of times Expand operator was used.") \
|
||||
M(ExpandVariableOperator, Operator, "Number of times ExpandVariable operator was used.") \
|
||||
M(ConstructNamedPathOperator, Operator, "Number of times ConstructNamedPath operator was used.") \
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
|
@ -32,6 +32,7 @@ enum class TypeId : uint64_t {
|
||||
SCAN_ALL_BY_LABEL_PROPERTY_VALUE,
|
||||
SCAN_ALL_BY_LABEL_PROPERTY,
|
||||
SCAN_ALL_BY_ID,
|
||||
SCAN_ALL_BY_EDGE_TYPE,
|
||||
EXPAND_COMMON,
|
||||
EXPAND,
|
||||
EXPANSION_LAMBDA,
|
||||
@ -185,6 +186,7 @@ enum class TypeId : uint64_t {
|
||||
AST_EXPLAIN_QUERY,
|
||||
AST_PROFILE_QUERY,
|
||||
AST_INDEX_QUERY,
|
||||
AST_EDGE_INDEX_QUERY,
|
||||
AST_CREATE,
|
||||
AST_CALL_PROCEDURE,
|
||||
AST_MATCH,
|
||||
|
@ -0,0 +1,22 @@
|
||||
// --storage-items-per-batch is set to 10
|
||||
CREATE INDEX ON :`label2`(`prop2`);
|
||||
CREATE INDEX ON :`label2`(`prop`);
|
||||
CREATE INDEX ON :`label`;
|
||||
CREATE INDEX ON :__mg_vertex__(__mg_id__);
|
||||
CREATE EDGE INDEX ON :`edge_type`;
|
||||
CREATE (:`edge_index_from`), (:`edge_index_to`);
|
||||
MATCH (n:`edge_index_from`), (m:`edge_index_to`) CREATE (n)-[r:`edge_type`]->(m);
|
||||
CREATE (:__mg_vertex__:`label2` {__mg_id__: 0, `prop2`: ["kaj", 2, Null, {`prop4`: -1.341}], `ext`: 2, `prop`: "joj"});
|
||||
CREATE (:__mg_vertex__:`label2`:`label` {__mg_id__: 1, `ext`: 2, `prop`: "joj"});
|
||||
CREATE (:__mg_vertex__:`label2` {__mg_id__: 2, `prop2`: 2, `prop`: 1});
|
||||
CREATE (:__mg_vertex__:`label2` {__mg_id__: 3, `prop2`: 2, `prop`: 2});
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 0 CREATE (u)-[:`link` {`ext`: [false, {`k`: "l"}], `prop`: -1}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 1 CREATE (u)-[:`link` {`ext`: [false, {`k`: "l"}], `prop`: -1}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 2 CREATE (u)-[:`link` {`ext`: [false, {`k`: "l"}], `prop`: -1}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 3 CREATE (u)-[:`link` {`ext`: [false, {`k`: "l"}], `prop`: -1}]->(v);
|
||||
CREATE CONSTRAINT ON (u:`label`) ASSERT EXISTS (u.`ext`);
|
||||
CREATE CONSTRAINT ON (u:`label2`) ASSERT u.`prop2`, u.`prop` IS UNIQUE;
|
||||
ANALYZE GRAPH;
|
||||
DROP INDEX ON :__mg_vertex__(__mg_id__);
|
||||
DROP EDGE INDEX ON :`edge_type`;
|
||||
MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;
|
@ -0,0 +1,19 @@
|
||||
CREATE (:__mg_vertex__:`edge_index_from` {__mg_id__: 0});
|
||||
CREATE (:__mg_vertex__:`edge_index_to` {__mg_id__: 1});
|
||||
CREATE (:__mg_vertex__:`label2` {__mg_id__: 2, `prop2`: ["kaj", 2, Null, {`prop4`: -1.341}], `ext`: 2, `prop`: "joj"});
|
||||
CREATE (:__mg_vertex__:`label2` {__mg_id__: 4, `prop2`: 2, `prop`: 1});
|
||||
CREATE (:__mg_vertex__:`label2` {__mg_id__: 5, `prop2`: 2, `prop`: 2});
|
||||
CREATE (:__mg_vertex__:`label`:`label2` {__mg_id__: 3, `ext`: 2, `prop`: "joj"});
|
||||
CREATE CONSTRAINT ON (u:`label2`) ASSERT u.`prop2`, u.`prop` IS UNIQUE;
|
||||
CREATE CONSTRAINT ON (u:`label`) ASSERT EXISTS (u.`ext`);
|
||||
CREATE INDEX ON :__mg_vertex__(__mg_id__);
|
||||
CREATE INDEX ON :`label2`(`prop2`);
|
||||
CREATE INDEX ON :`label2`(`prop`);
|
||||
CREATE INDEX ON :`label`;
|
||||
DROP INDEX ON :__mg_vertex__(__mg_id__);
|
||||
MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE (u)-[:`edge_type`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 3 AND v.__mg_id__ = 2 CREATE (u)-[:`link` {`ext`: [false, {`k`: "l"}], `prop`: -1}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 3 AND v.__mg_id__ = 3 CREATE (u)-[:`link` {`ext`: [false, {`k`: "l"}], `prop`: -1}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 3 AND v.__mg_id__ = 4 CREATE (u)-[:`link` {`ext`: [false, {`k`: "l"}], `prop`: -1}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 3 AND v.__mg_id__ = 5 CREATE (u)-[:`link` {`ext`: [false, {`k`: "l"}], `prop`: -1}]->(v);
|
@ -0,0 +1,19 @@
|
||||
CREATE (:__mg_vertex__:`edge_index_from` {__mg_id__: 0});
|
||||
CREATE (:__mg_vertex__:`edge_index_to` {__mg_id__: 1});
|
||||
CREATE (:__mg_vertex__:`label2` {__mg_id__: 2, `prop2`: ["kaj", 2, Null, {`prop4`: -1.341}], `prop`: "joj", `ext`: 2});
|
||||
CREATE (:__mg_vertex__:`label2` {__mg_id__: 4, `prop2`: 2, `prop`: 1});
|
||||
CREATE (:__mg_vertex__:`label2` {__mg_id__: 5, `prop2`: 2, `prop`: 2});
|
||||
CREATE (:__mg_vertex__:`label`:`label2` {__mg_id__: 3, `prop`: "joj", `ext`: 2});
|
||||
CREATE CONSTRAINT ON (u:`label2`) ASSERT u.`prop2`, u.`prop` IS UNIQUE;
|
||||
CREATE CONSTRAINT ON (u:`label`) ASSERT EXISTS (u.`ext`);
|
||||
CREATE INDEX ON :__mg_vertex__(__mg_id__);
|
||||
CREATE INDEX ON :`label2`(`prop2`);
|
||||
CREATE INDEX ON :`label2`(`prop`);
|
||||
CREATE INDEX ON :`label`;
|
||||
DROP INDEX ON :__mg_vertex__(__mg_id__);
|
||||
MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE (u)-[:`edge_type`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 3 AND v.__mg_id__ = 2 CREATE (u)-[:`link` {`prop`: -1, `ext`: [false, {`k`: "l"}]}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 3 AND v.__mg_id__ = 3 CREATE (u)-[:`link` {`prop`: -1, `ext`: [false, {`k`: "l"}]}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 3 AND v.__mg_id__ = 4 CREATE (u)-[:`link` {`prop`: -1, `ext`: [false, {`k`: "l"}]}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 3 AND v.__mg_id__ = 5 CREATE (u)-[:`link` {`prop`: -1, `ext`: [false, {`k`: "l"}]}]->(v);
|
BIN
tests/integration/durability/tests/v17/test_all/snapshot.bin
Normal file
BIN
tests/integration/durability/tests/v17/test_all/snapshot.bin
Normal file
Binary file not shown.
BIN
tests/integration/durability/tests/v17/test_all/wal.bin
Normal file
BIN
tests/integration/durability/tests/v17/test_all/wal.bin
Normal file
Binary file not shown.
@ -0,0 +1,6 @@
|
||||
CREATE CONSTRAINT ON (u:`label2`) ASSERT EXISTS (u.`ext2`);
|
||||
CREATE CONSTRAINT ON (u:`label`) ASSERT EXISTS (u.`ext`);
|
||||
CREATE CONSTRAINT ON (u:`label`) ASSERT u.`a` IS UNIQUE;
|
||||
CREATE CONSTRAINT ON (u:`label`) ASSERT u.`b` IS UNIQUE;
|
||||
CREATE CONSTRAINT ON (u:`label`) ASSERT u.`c` IS UNIQUE;
|
||||
CREATE CONSTRAINT ON (u:`label2`) ASSERT u.`a`, u.`b` IS UNIQUE;
|
@ -0,0 +1,6 @@
|
||||
CREATE CONSTRAINT ON (u:`label2`) ASSERT EXISTS (u.`ext2`);
|
||||
CREATE CONSTRAINT ON (u:`label`) ASSERT EXISTS (u.`ext`);
|
||||
CREATE CONSTRAINT ON (u:`label`) ASSERT u.`c` IS UNIQUE;
|
||||
CREATE CONSTRAINT ON (u:`label`) ASSERT u.`b` IS UNIQUE;
|
||||
CREATE CONSTRAINT ON (u:`label`) ASSERT u.`a` IS UNIQUE;
|
||||
CREATE CONSTRAINT ON (u:`label2`) ASSERT u.`b`, u.`a` IS UNIQUE;
|
@ -0,0 +1,6 @@
|
||||
CREATE CONSTRAINT ON (u:`label2`) ASSERT EXISTS (u.`ext2`);
|
||||
CREATE CONSTRAINT ON (u:`label`) ASSERT EXISTS (u.`ext`);
|
||||
CREATE CONSTRAINT ON (u:`label2`) ASSERT u.`a`, u.`b` IS UNIQUE;
|
||||
CREATE CONSTRAINT ON (u:`label`) ASSERT u.`a` IS UNIQUE;
|
||||
CREATE CONSTRAINT ON (u:`label`) ASSERT u.`b` IS UNIQUE;
|
||||
CREATE CONSTRAINT ON (u:`label`) ASSERT u.`c` IS UNIQUE;
|
Binary file not shown.
BIN
tests/integration/durability/tests/v17/test_constraints/wal.bin
Normal file
BIN
tests/integration/durability/tests/v17/test_constraints/wal.bin
Normal file
Binary file not shown.
@ -0,0 +1,60 @@
|
||||
// --storage-items-per-batch is set to 7
|
||||
CREATE INDEX ON :__mg_vertex__(__mg_id__);
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 0});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 1});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 2});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 3});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 4});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 5});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 6});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 7});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 8});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 9});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 10});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 11});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 12});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 13});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 14});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 15});
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE (u)-[:`edge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 2 AND v.__mg_id__ = 3 CREATE (u)-[:`edge` {`prop`: 11}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 4 AND v.__mg_id__ = 5 CREATE (u)-[:`edge` {`prop`: true}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 6 AND v.__mg_id__ = 7 CREATE (u)-[:`edge2`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 8 AND v.__mg_id__ = 9 CREATE (u)-[:`edge2` {`prop`: -3.141}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 11 CREATE (u)-[:`edgelink` {`prop`: {`prop`: 1, `prop2`: {`prop4`: 9}}}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 12 AND v.__mg_id__ = 13 CREATE (u)-[:`edgelink` {`prop`: [1, Null, false, "\n\n\n\n\\\"\"\n\t"]}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 0 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 1 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 2 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 3 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 4 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 5 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 6 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 7 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 8 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 9 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 10 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 11 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 12 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 13 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 14 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 15 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 0 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 1 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 2 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 3 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 4 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 5 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 6 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 7 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 8 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 9 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 10 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 11 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 12 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 13 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 14 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 15 CREATE (u)-[:`testedge`]->(v);
|
||||
ANALYZE GRAPH;
|
||||
DROP INDEX ON :__mg_vertex__(__mg_id__);
|
||||
MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;
|
@ -0,0 +1,58 @@
|
||||
CREATE INDEX ON :__mg_vertex__(__mg_id__);
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 0});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 1});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 2});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 3});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 4});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 5});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 6});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 7});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 8});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 9});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 10});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 11});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 12});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 13});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 14});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 15});
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE (u)-[:`edge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 2 AND v.__mg_id__ = 3 CREATE (u)-[:`edge` {`prop`: 11}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 4 AND v.__mg_id__ = 5 CREATE (u)-[:`edge` {`prop`: true}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 6 AND v.__mg_id__ = 7 CREATE (u)-[:`edge2`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 8 AND v.__mg_id__ = 9 CREATE (u)-[:`edge2` {`prop`: -3.141}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 11 CREATE (u)-[:`edgelink` {`prop`: {`prop`: 1, `prop2`: {`prop4`: 9}}}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 12 AND v.__mg_id__ = 13 CREATE (u)-[:`edgelink` {`prop`: [1, Null, false, "\n\n\n\n\\\"\"\n\t"]}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 0 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 1 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 2 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 3 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 4 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 5 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 6 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 7 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 8 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 9 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 10 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 11 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 12 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 13 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 14 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 15 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 0 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 1 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 2 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 3 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 4 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 5 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 6 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 7 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 8 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 9 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 10 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 11 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 12 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 13 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 14 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 15 CREATE (u)-[:`testedge`]->(v);
|
||||
DROP INDEX ON :__mg_vertex__(__mg_id__);
|
||||
MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;
|
@ -0,0 +1,58 @@
|
||||
CREATE INDEX ON :__mg_vertex__(__mg_id__);
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 0});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 1});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 2});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 3});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 4});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 5});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 6});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 7});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 8});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 9});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 10});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 11});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 12});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 13});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 14});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 15});
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE (u)-[:`edge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 2 AND v.__mg_id__ = 3 CREATE (u)-[:`edge` {`prop`: 11}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 4 AND v.__mg_id__ = 5 CREATE (u)-[:`edge` {`prop`: true}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 6 AND v.__mg_id__ = 7 CREATE (u)-[:`edge2`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 8 AND v.__mg_id__ = 9 CREATE (u)-[:`edge2` {`prop`: -3.141}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 11 CREATE (u)-[:`edgelink` {`prop`: {`prop`: 1, `prop2`: {`prop4`: 9}}}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 12 AND v.__mg_id__ = 13 CREATE (u)-[:`edgelink` {`prop`: [1, Null, false, "\n\n\n\n\\\"\"\n\t"]}]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 0 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 1 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 2 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 3 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 4 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 5 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 6 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 7 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 8 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 9 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 10 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 11 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 12 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 13 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 14 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 15 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 0 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 1 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 2 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 3 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 4 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 5 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 6 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 7 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 8 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 9 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 10 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 11 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 12 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 13 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 14 CREATE (u)-[:`testedge`]->(v);
|
||||
MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 15 AND v.__mg_id__ = 15 CREATE (u)-[:`testedge`]->(v);
|
||||
DROP INDEX ON :__mg_vertex__(__mg_id__);
|
||||
MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;
|
BIN
tests/integration/durability/tests/v17/test_edges/snapshot.bin
Normal file
BIN
tests/integration/durability/tests/v17/test_edges/snapshot.bin
Normal file
Binary file not shown.
BIN
tests/integration/durability/tests/v17/test_edges/wal.bin
Normal file
BIN
tests/integration/durability/tests/v17/test_edges/wal.bin
Normal file
Binary file not shown.
@ -0,0 +1,6 @@
|
||||
CREATE INDEX ON :`label2`;
|
||||
CREATE INDEX ON :`label2`(`prop2`);
|
||||
CREATE INDEX ON :`label`(`prop2`);
|
||||
CREATE INDEX ON :`label`(`prop`);
|
||||
CREATE EDGE INDEX ON :`edgetype`;
|
||||
ANALYZE GRAPH;
|
@ -0,0 +1,5 @@
|
||||
CREATE INDEX ON :`label2`;
|
||||
CREATE INDEX ON :`label`(`prop`);
|
||||
CREATE INDEX ON :`label`(`prop2`);
|
||||
CREATE INDEX ON :`label2`(`prop2`);
|
||||
CREATE EDGE INDEX ON :`edgetype`;
|
@ -0,0 +1,5 @@
|
||||
CREATE INDEX ON :`label2`;
|
||||
CREATE INDEX ON :`label2`(`prop2`);
|
||||
CREATE INDEX ON :`label`(`prop2`);
|
||||
CREATE INDEX ON :`label`(`prop`);
|
||||
CREATE EDGE INDEX ON :`edgetype`;
|
BIN
tests/integration/durability/tests/v17/test_indices/snapshot.bin
Normal file
BIN
tests/integration/durability/tests/v17/test_indices/snapshot.bin
Normal file
Binary file not shown.
BIN
tests/integration/durability/tests/v17/test_indices/wal.bin
Normal file
BIN
tests/integration/durability/tests/v17/test_indices/wal.bin
Normal file
Binary file not shown.
@ -0,0 +1,18 @@
|
||||
// --storage-items-per-batch is set to 5
|
||||
CREATE INDEX ON :__mg_vertex__(__mg_id__);
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 0});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 1});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 2, `prop`: false});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 3, `prop`: true});
|
||||
CREATE (:__mg_vertex__:`label2` {__mg_id__: 4, `prop`: 1});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 5, `prop2`: 3.141});
|
||||
CREATE (:__mg_vertex__:`label6` {__mg_id__: 6, `prop3`: true, `prop2`: -314000000});
|
||||
CREATE (:__mg_vertex__:`label3`:`label1`:`label2` {__mg_id__: 7});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 8, `prop3`: "str", `prop2`: 2, `prop`: 1});
|
||||
CREATE (:__mg_vertex__:`label2`:`label1` {__mg_id__: 9, `prop`: {`prop_nes`: "kaj je"}});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 10, `prop_array`: [1, false, Null, "str", {`prop2`: 2}]});
|
||||
CREATE (:__mg_vertex__:`label3`:`label` {__mg_id__: 11, `prop`: {`prop`: [1, false], `prop2`: {}, `prop3`: "test2", `prop4`: "test"}});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 12, `prop`: " \n\"\'\t\\%"});
|
||||
ANALYZE GRAPH;
|
||||
DROP INDEX ON :__mg_vertex__(__mg_id__);
|
||||
MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;
|
@ -0,0 +1,16 @@
|
||||
CREATE INDEX ON :__mg_vertex__(__mg_id__);
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 0});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 1});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 2, `prop`: false});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 3, `prop`: true});
|
||||
CREATE (:__mg_vertex__:`label2` {__mg_id__: 4, `prop`: 1});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 5, `prop2`: 3.141});
|
||||
CREATE (:__mg_vertex__:`label6` {__mg_id__: 6, `prop3`: true, `prop2`: -314000000});
|
||||
CREATE (:__mg_vertex__:`label2`:`label3`:`label1` {__mg_id__: 7});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 8, `prop3`: "str", `prop2`: 2, `prop`: 1});
|
||||
CREATE (:__mg_vertex__:`label1`:`label2` {__mg_id__: 9, `prop`: {`prop_nes`: "kaj je"}});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 10, `prop_array`: [1, false, Null, "str", {`prop2`: 2}]});
|
||||
CREATE (:__mg_vertex__:`label`:`label3` {__mg_id__: 11, `prop`: {`prop`: [1, false], `prop2`: {}, `prop3`: "test2", `prop4`: "test"}});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 12, `prop`: " \n\"\'\t\\%"});
|
||||
DROP INDEX ON :__mg_vertex__(__mg_id__);
|
||||
MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;
|
@ -0,0 +1,16 @@
|
||||
CREATE INDEX ON :__mg_vertex__(__mg_id__);
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 0});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 1});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 2, `prop`: false});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 3, `prop`: true});
|
||||
CREATE (:__mg_vertex__:`label2` {__mg_id__: 4, `prop`: 1});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 5, `prop2`: 3.141});
|
||||
CREATE (:__mg_vertex__:`label6` {__mg_id__: 6, `prop2`: -314000000, `prop3`: true});
|
||||
CREATE (:__mg_vertex__:`label2`:`label3`:`label1` {__mg_id__: 7});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 8, `prop`: 1, `prop2`: 2, `prop3`: "str"});
|
||||
CREATE (:__mg_vertex__:`label1`:`label2` {__mg_id__: 9, `prop`: {`prop_nes`: "kaj je"}});
|
||||
CREATE (:__mg_vertex__:`label` {__mg_id__: 10, `prop_array`: [1, false, Null, "str", {`prop2`: 2}]});
|
||||
CREATE (:__mg_vertex__:`label`:`label3` {__mg_id__: 11, `prop`: {`prop`: [1, false], `prop2`: {}, `prop3`: "test2", `prop4`: "test"}});
|
||||
CREATE (:__mg_vertex__ {__mg_id__: 12, `prop`: " \n\"\'\t\\%"});
|
||||
DROP INDEX ON :__mg_vertex__(__mg_id__);
|
||||
MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;
|
Binary file not shown.
BIN
tests/integration/durability/tests/v17/test_vertices/wal.bin
Normal file
BIN
tests/integration/durability/tests/v17/test_vertices/wal.bin
Normal file
Binary file not shown.
@ -214,6 +214,8 @@ class InteractiveDbAccessor {
|
||||
return label_property_index_.at(key);
|
||||
}
|
||||
|
||||
bool EdgeTypeIndexExists(memgraph::storage::EdgeTypeId edge_type) { return true; }
|
||||
|
||||
std::optional<memgraph::storage::LabelIndexStats> GetIndexStats(const memgraph::storage::LabelId label) const {
|
||||
return dba_->GetIndexStats(label);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -808,13 +808,68 @@ TYPED_TEST(TestPlanner, MatchWhereBeforeExpand) {
|
||||
CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectFilter(), ExpectExpand(), ExpectProduce());
|
||||
}
|
||||
|
||||
TYPED_TEST(TestPlanner, MatchEdgeTypeIndex) {
|
||||
FakeDbAccessor dba;
|
||||
auto indexed_edge_type = dba.EdgeType("indexed_edgetype");
|
||||
dba.SetIndexCount(indexed_edge_type, 1);
|
||||
{
|
||||
// Test MATCH ()-[r:indexed_edgetype]->() RETURN r;
|
||||
auto *query = QUERY(SINGLE_QUERY(
|
||||
MATCH(PATTERN(NODE("anon1"), EDGE("r", memgraph::query::EdgeAtom::Direction::OUT, {"indexed_edgetype"}),
|
||||
NODE("anon2"))),
|
||||
RETURN("r")));
|
||||
auto symbol_table = memgraph::query::MakeSymbolTable(query);
|
||||
auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
|
||||
CheckPlan(planner.plan(), symbol_table, ExpectScanAllByEdgeType(), ExpectProduce());
|
||||
}
|
||||
{
|
||||
// Test MATCH (a)-[r:indexed_edgetype]->() RETURN r;
|
||||
auto *query = QUERY(SINGLE_QUERY(
|
||||
MATCH(PATTERN(NODE("a"), EDGE("r", memgraph::query::EdgeAtom::Direction::OUT, {"indexed_edgetype"}),
|
||||
NODE("anon2"))),
|
||||
RETURN("r")));
|
||||
auto symbol_table = memgraph::query::MakeSymbolTable(query);
|
||||
auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
|
||||
CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectExpand(), ExpectProduce());
|
||||
}
|
||||
{
|
||||
// Test MATCH ()-[r:indexed_edgetype]->(b) RETURN r;
|
||||
auto *query = QUERY(SINGLE_QUERY(
|
||||
MATCH(PATTERN(NODE("anon1"), EDGE("r", memgraph::query::EdgeAtom::Direction::OUT, {"indexed_edgetype"}),
|
||||
NODE("b"))),
|
||||
RETURN("r")));
|
||||
auto symbol_table = memgraph::query::MakeSymbolTable(query);
|
||||
auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
|
||||
CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectExpand(), ExpectProduce());
|
||||
}
|
||||
{
|
||||
// Test MATCH (a)-[r:indexed_edgetype]->(b) RETURN r;
|
||||
auto *query = QUERY(SINGLE_QUERY(
|
||||
MATCH(
|
||||
PATTERN(NODE("a"), EDGE("r", memgraph::query::EdgeAtom::Direction::OUT, {"indexed_edgetype"}), NODE("b"))),
|
||||
RETURN("r")));
|
||||
auto symbol_table = memgraph::query::MakeSymbolTable(query);
|
||||
auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
|
||||
CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectExpand(), ExpectProduce());
|
||||
}
|
||||
{
|
||||
// Test MATCH ()-[r:not_indexed_edgetype]->() RETURN r;
|
||||
auto *query = QUERY(SINGLE_QUERY(
|
||||
MATCH(PATTERN(NODE("anon1"), EDGE("r", memgraph::query::EdgeAtom::Direction::OUT, {"not_indexed_edgetype"}),
|
||||
NODE("anon2"))),
|
||||
RETURN("r")));
|
||||
auto symbol_table = memgraph::query::MakeSymbolTable(query);
|
||||
auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
|
||||
CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectExpand(), ExpectProduce());
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(TestPlanner, MatchFilterPropIsNotNull) {
|
||||
FakeDbAccessor dba;
|
||||
auto label = dba.Label("label");
|
||||
auto prop = PROPERTY_PAIR(dba, "prop");
|
||||
dba.SetIndexCount(label, 1);
|
||||
dba.SetIndexCount(label, prop.second, 1);
|
||||
|
||||
{
|
||||
// Test MATCH (n :label) -[r]- (m) WHERE n.prop IS NOT NULL RETURN n
|
||||
auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"), EDGE("r"), NODE("m"))),
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -65,6 +65,7 @@ class PlanChecker : public virtual HierarchicalLogicalOperatorVisitor {
|
||||
PRE_VISIT(ScanAllByLabelPropertyValue);
|
||||
PRE_VISIT(ScanAllByLabelPropertyRange);
|
||||
PRE_VISIT(ScanAllByLabelProperty);
|
||||
PRE_VISIT(ScanAllByEdgeType);
|
||||
PRE_VISIT(ScanAllById);
|
||||
PRE_VISIT(Expand);
|
||||
PRE_VISIT(ExpandVariable);
|
||||
@ -170,6 +171,7 @@ using ExpectCreateExpand = OpChecker<CreateExpand>;
|
||||
using ExpectDelete = OpChecker<Delete>;
|
||||
using ExpectScanAll = OpChecker<ScanAll>;
|
||||
using ExpectScanAllByLabel = OpChecker<ScanAllByLabel>;
|
||||
using ExpectScanAllByEdgeType = OpChecker<ScanAllByEdgeType>;
|
||||
using ExpectScanAllById = OpChecker<ScanAllById>;
|
||||
using ExpectExpand = OpChecker<Expand>;
|
||||
using ExpectConstructNamedPath = OpChecker<ConstructNamedPath>;
|
||||
@ -560,6 +562,12 @@ class FakeDbAccessor {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t EdgesCount(memgraph::storage::EdgeTypeId edge_type) const {
|
||||
auto found = edge_type_index_.find(edge_type);
|
||||
if (found != edge_type_index_.end()) return found->second;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool LabelIndexExists(memgraph::storage::LabelId label) const {
|
||||
return label_index_.find(label) != label_index_.end();
|
||||
}
|
||||
@ -573,6 +581,10 @@ class FakeDbAccessor {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EdgeTypeIndexExists(memgraph::storage::EdgeTypeId edge_type) const {
|
||||
return edge_type_index_.find(edge_type) != edge_type_index_.end();
|
||||
}
|
||||
|
||||
std::optional<memgraph::storage::LabelPropertyIndexStats> GetIndexStats(
|
||||
const memgraph::storage::LabelId label, const memgraph::storage::PropertyId property) const {
|
||||
return memgraph::storage::LabelPropertyIndexStats{.statistic = 0, .avg_group_size = 1}; // unique id
|
||||
@ -594,6 +606,8 @@ class FakeDbAccessor {
|
||||
label_property_index_.emplace_back(label, property, count);
|
||||
}
|
||||
|
||||
void SetIndexCount(memgraph::storage::EdgeTypeId edge_type, int64_t count) { edge_type_index_[edge_type] = count; }
|
||||
|
||||
memgraph::storage::LabelId NameToLabel(const std::string &name) {
|
||||
auto found = labels_.find(name);
|
||||
if (found != labels_.end()) return found->second;
|
||||
@ -608,6 +622,8 @@ class FakeDbAccessor {
|
||||
return edge_types_.emplace(name, memgraph::storage::EdgeTypeId::FromUint(edge_types_.size())).first->second;
|
||||
}
|
||||
|
||||
memgraph::storage::EdgeTypeId EdgeType(const std::string &name) { return NameToEdgeType(name); }
|
||||
|
||||
memgraph::storage::PropertyId NameToProperty(const std::string &name) {
|
||||
auto found = properties_.find(name);
|
||||
if (found != properties_.end()) return found->second;
|
||||
@ -632,6 +648,7 @@ class FakeDbAccessor {
|
||||
|
||||
std::unordered_map<memgraph::storage::LabelId, int64_t> label_index_;
|
||||
std::vector<std::tuple<memgraph::storage::LabelId, memgraph::storage::PropertyId, int64_t>> label_property_index_;
|
||||
std::unordered_map<memgraph::storage::EdgeTypeId, int64_t> edge_type_index_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::query::plan
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -337,6 +337,7 @@ TEST_F(DecoderEncoderTest, PropertyValueInvalidMarker) {
|
||||
case memgraph::storage::durability::Marker::SECTION_CONSTRAINTS:
|
||||
case memgraph::storage::durability::Marker::SECTION_DELTA:
|
||||
case memgraph::storage::durability::Marker::SECTION_EPOCH_HISTORY:
|
||||
case memgraph::storage::durability::Marker::SECTION_EDGE_INDICES:
|
||||
case memgraph::storage::durability::Marker::SECTION_OFFSETS:
|
||||
case memgraph::storage::durability::Marker::DELTA_VERTEX_CREATE:
|
||||
case memgraph::storage::durability::Marker::DELTA_VERTEX_DELETE:
|
||||
@ -355,6 +356,8 @@ TEST_F(DecoderEncoderTest, PropertyValueInvalidMarker) {
|
||||
case memgraph::storage::durability::Marker::DELTA_LABEL_PROPERTY_INDEX_DROP:
|
||||
case memgraph::storage::durability::Marker::DELTA_LABEL_PROPERTY_INDEX_STATS_SET:
|
||||
case memgraph::storage::durability::Marker::DELTA_LABEL_PROPERTY_INDEX_STATS_CLEAR:
|
||||
case memgraph::storage::durability::Marker::DELTA_EDGE_TYPE_INDEX_CREATE:
|
||||
case memgraph::storage::durability::Marker::DELTA_EDGE_TYPE_INDEX_DROP:
|
||||
case memgraph::storage::durability::Marker::DELTA_EXISTENCE_CONSTRAINT_CREATE:
|
||||
case memgraph::storage::durability::Marker::DELTA_EXISTENCE_CONSTRAINT_DROP:
|
||||
case memgraph::storage::durability::Marker::DELTA_UNIQUE_CONSTRAINT_CREATE:
|
||||
|
@ -69,6 +69,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
|
||||
ONLY_EXTENDED,
|
||||
ONLY_EXTENDED_WITH_BASE_INDICES_AND_CONSTRAINTS,
|
||||
BASE_WITH_EXTENDED,
|
||||
BASE_WITH_EDGE_TYPE_INDEXED,
|
||||
};
|
||||
|
||||
public:
|
||||
@ -270,6 +271,15 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
|
||||
if (single_transaction) ASSERT_FALSE(acc->Commit().HasError());
|
||||
}
|
||||
|
||||
void CreateEdgeIndex(memgraph::storage::Storage *store, memgraph::storage::EdgeTypeId edge_type) {
|
||||
{
|
||||
// Create edge-type index.
|
||||
auto unique_acc = store->UniqueAccess(ReplicationRole::MAIN);
|
||||
ASSERT_FALSE(unique_acc->CreateIndex(edge_type).HasError());
|
||||
ASSERT_FALSE(unique_acc->Commit().HasError());
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyDataset(memgraph::storage::Storage *store, DatasetType type, bool properties_on_edges,
|
||||
bool verify_info = true) {
|
||||
auto base_label_indexed = store->NameToLabel("base_indexed");
|
||||
@ -310,13 +320,19 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
|
||||
UnorderedElementsAre(std::make_pair(base_label_indexed, property_id),
|
||||
std::make_pair(extended_label_indexed, property_count)));
|
||||
break;
|
||||
case DatasetType::BASE_WITH_EDGE_TYPE_INDEXED:
|
||||
ASSERT_THAT(info.label, UnorderedElementsAre(base_label_unindexed));
|
||||
ASSERT_THAT(info.label_property, UnorderedElementsAre(std::make_pair(base_label_indexed, property_id)));
|
||||
ASSERT_THAT(info.edge_type, UnorderedElementsAre(et1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify index statistics
|
||||
{
|
||||
switch (type) {
|
||||
case DatasetType::ONLY_BASE: {
|
||||
case DatasetType::ONLY_BASE:
|
||||
case DatasetType::BASE_WITH_EDGE_TYPE_INDEXED: {
|
||||
const auto l_stats = acc->GetIndexStats(base_label_unindexed);
|
||||
ASSERT_TRUE(l_stats);
|
||||
ASSERT_EQ(l_stats->count, 1);
|
||||
@ -379,6 +395,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
|
||||
auto info = acc->ListAllConstraints();
|
||||
switch (type) {
|
||||
case DatasetType::ONLY_BASE:
|
||||
case DatasetType::BASE_WITH_EDGE_TYPE_INDEXED:
|
||||
ASSERT_THAT(info.existence, UnorderedElementsAre(std::make_pair(base_label_unindexed, property_id)));
|
||||
ASSERT_THAT(info.unique, UnorderedElementsAre(
|
||||
std::make_pair(base_label_unindexed, std::set{property_id, property_extra})));
|
||||
@ -402,6 +419,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
|
||||
|
||||
bool have_base_dataset = false;
|
||||
bool have_extended_dataset = false;
|
||||
bool have_edge_type_indexed_dataset = false;
|
||||
switch (type) {
|
||||
case DatasetType::ONLY_BASE:
|
||||
case DatasetType::ONLY_BASE_WITH_EXTENDED_INDICES_AND_CONSTRAINTS:
|
||||
@ -415,6 +433,9 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
|
||||
have_base_dataset = true;
|
||||
have_extended_dataset = true;
|
||||
break;
|
||||
case DatasetType::BASE_WITH_EDGE_TYPE_INDEXED:
|
||||
have_base_dataset = true;
|
||||
have_edge_type_indexed_dataset = true;
|
||||
}
|
||||
|
||||
// Verify base dataset.
|
||||
@ -675,6 +696,19 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
if (have_edge_type_indexed_dataset) {
|
||||
MG_ASSERT(properties_on_edges, "Edge-type indexing needs --properties-on-edges!");
|
||||
// Verify edge-type indices.
|
||||
{
|
||||
std::vector<memgraph::storage::EdgeAccessor> edges;
|
||||
edges.reserve(kNumBaseEdges / 2);
|
||||
for (auto edge : acc->Edges(et1, memgraph::storage::View::OLD)) {
|
||||
edges.push_back(edge);
|
||||
}
|
||||
ASSERT_EQ(edges.size(), kNumBaseEdges / 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (verify_info) {
|
||||
auto info = store->GetBaseInfo();
|
||||
if (have_base_dataset) {
|
||||
@ -2972,3 +3006,42 @@ TEST_P(DurabilityTest, ConstraintsRecoveryFunctionSetting) {
|
||||
&variant_existence_constraint_creation_func);
|
||||
MG_ASSERT(pval_existence, "Chose wrong type of function for recovery of existence constraint data");
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST_P(DurabilityTest, EdgeTypeIndexRecovered) {
|
||||
if (GetParam() == false) {
|
||||
return;
|
||||
}
|
||||
// Create snapshot.
|
||||
{
|
||||
memgraph::storage::Config config{.salient.items = {.properties_on_edges = GetParam()},
|
||||
.durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}};
|
||||
memgraph::replication::ReplicationState repl_state{memgraph::storage::ReplicationStateRootPath(config)};
|
||||
memgraph::dbms::Database db{config, repl_state};
|
||||
CreateBaseDataset(db.storage(), GetParam());
|
||||
VerifyDataset(db.storage(), DatasetType::ONLY_BASE, GetParam());
|
||||
CreateEdgeIndex(db.storage(), db.storage()->NameToEdgeType("base_et1"));
|
||||
VerifyDataset(db.storage(), DatasetType::BASE_WITH_EDGE_TYPE_INDEXED, GetParam());
|
||||
}
|
||||
|
||||
ASSERT_EQ(GetSnapshotsList().size(), 1);
|
||||
ASSERT_EQ(GetBackupSnapshotsList().size(), 0);
|
||||
ASSERT_EQ(GetWalsList().size(), 0);
|
||||
ASSERT_EQ(GetBackupWalsList().size(), 0);
|
||||
|
||||
// Recover snapshot.
|
||||
memgraph::storage::Config config{.salient.items = {.properties_on_edges = GetParam()},
|
||||
.durability = {.storage_directory = storage_directory, .recover_on_startup = true}};
|
||||
memgraph::replication::ReplicationState repl_state{memgraph::storage::ReplicationStateRootPath(config)};
|
||||
memgraph::dbms::Database db{config, repl_state};
|
||||
VerifyDataset(db.storage(), DatasetType::BASE_WITH_EDGE_TYPE_INDEXED, GetParam());
|
||||
|
||||
// Try to use the storage.
|
||||
{
|
||||
auto acc = db.Access();
|
||||
auto vertex = acc->CreateVertex();
|
||||
auto edge = acc->CreateEdge(&vertex, &vertex, db.storage()->NameToEdgeType("et"));
|
||||
ASSERT_TRUE(edge.HasValue());
|
||||
ASSERT_FALSE(acc->Commit().HasError());
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ class IndexTest : public testing::Test {
|
||||
this->prop_val = acc->NameToProperty("val");
|
||||
this->label1 = acc->NameToLabel("label1");
|
||||
this->label2 = acc->NameToLabel("label2");
|
||||
this->edge_type_id1 = acc->NameToEdgeType("edge_type_1");
|
||||
this->edge_type_id2 = acc->NameToEdgeType("edge_type_2");
|
||||
vertex_id = 0;
|
||||
}
|
||||
|
||||
@ -61,6 +63,8 @@ class IndexTest : public testing::Test {
|
||||
PropertyId prop_val;
|
||||
LabelId label1;
|
||||
LabelId label2;
|
||||
EdgeTypeId edge_type_id1;
|
||||
EdgeTypeId edge_type_id2;
|
||||
|
||||
VertexAccessor CreateVertex(Storage::Accessor *accessor) {
|
||||
VertexAccessor vertex = accessor->CreateVertex();
|
||||
@ -68,11 +72,23 @@ class IndexTest : public testing::Test {
|
||||
return vertex;
|
||||
}
|
||||
|
||||
VertexAccessor CreateVertexWithoutProperties(Storage::Accessor *accessor) {
|
||||
VertexAccessor vertex = accessor->CreateVertex();
|
||||
return vertex;
|
||||
}
|
||||
|
||||
EdgeAccessor CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type, Storage::Accessor *accessor) {
|
||||
auto edge = accessor->CreateEdge(from, to, edge_type);
|
||||
MG_ASSERT(!edge.HasError());
|
||||
MG_ASSERT(!edge->SetProperty(this->prop_id, PropertyValue(vertex_id++)).HasError());
|
||||
return edge.GetValue();
|
||||
}
|
||||
|
||||
template <class TIterable>
|
||||
std::vector<int64_t> GetIds(TIterable iterable, View view = View::OLD) {
|
||||
std::vector<int64_t> ret;
|
||||
for (auto vertex : iterable) {
|
||||
ret.push_back(vertex.GetProperty(this->prop_id, view)->ValueInt());
|
||||
for (auto item : iterable) {
|
||||
ret.push_back(item.GetProperty(this->prop_id, view)->ValueInt());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -1292,3 +1308,368 @@ TYPED_TEST(IndexTest, LabelPropertyIndexClearOldDataFromDisk) {
|
||||
ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TYPED_TEST(IndexTest, EdgeTypeIndexCreate) {
|
||||
if constexpr ((std::is_same_v<TypeParam, memgraph::storage::InMemoryStorage>)) {
|
||||
{
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
EXPECT_FALSE(acc->EdgeTypeIndexExists(this->edge_type_id1));
|
||||
EXPECT_EQ(acc->ListAllIndices().edge_type.size(), 0);
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
auto vertex_from = this->CreateVertexWithoutProperties(acc.get());
|
||||
auto vertex_to = this->CreateVertexWithoutProperties(acc.get());
|
||||
this->CreateEdge(&vertex_from, &vertex_to, i % 2 ? this->edge_type_id1 : this->edge_type_id2, acc.get());
|
||||
}
|
||||
ASSERT_NO_ERROR(acc->Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto unique_acc = this->storage->UniqueAccess(ReplicationRole::MAIN);
|
||||
EXPECT_FALSE(unique_acc->CreateIndex(this->edge_type_id1).HasError());
|
||||
ASSERT_NO_ERROR(unique_acc->Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::OLD), View::OLD),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
for (int i = 10; i < 20; ++i) {
|
||||
auto vertex_from = this->CreateVertexWithoutProperties(acc.get());
|
||||
auto vertex_to = this->CreateVertexWithoutProperties(acc.get());
|
||||
this->CreateEdge(&vertex_from, &vertex_to, i % 2 ? this->edge_type_id1 : this->edge_type_id2, acc.get());
|
||||
}
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::OLD), View::OLD),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
|
||||
|
||||
acc->AdvanceCommand();
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::OLD), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
|
||||
|
||||
acc->Abort();
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
for (int i = 10; i < 20; ++i) {
|
||||
auto vertex_from = this->CreateVertexWithoutProperties(acc.get());
|
||||
auto vertex_to = this->CreateVertexWithoutProperties(acc.get());
|
||||
this->CreateEdge(&vertex_from, &vertex_to, i % 2 ? this->edge_type_id1 : this->edge_type_id2, acc.get());
|
||||
}
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::OLD), View::OLD),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
|
||||
|
||||
acc->AdvanceCommand();
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::OLD), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
|
||||
|
||||
ASSERT_NO_ERROR(acc->Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::OLD), View::OLD),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
|
||||
|
||||
acc->AdvanceCommand();
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::OLD), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
|
||||
|
||||
ASSERT_NO_ERROR(acc->Commit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TYPED_TEST(IndexTest, EdgeTypeIndexDrop) {
|
||||
if constexpr ((std::is_same_v<TypeParam, memgraph::storage::InMemoryStorage>)) {
|
||||
{
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
EXPECT_FALSE(acc->EdgeTypeIndexExists(this->edge_type_id1));
|
||||
EXPECT_EQ(acc->ListAllIndices().edge_type.size(), 0);
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
auto vertex_from = this->CreateVertexWithoutProperties(acc.get());
|
||||
auto vertex_to = this->CreateVertexWithoutProperties(acc.get());
|
||||
this->CreateEdge(&vertex_from, &vertex_to, i % 2 ? this->edge_type_id1 : this->edge_type_id2, acc.get());
|
||||
}
|
||||
ASSERT_NO_ERROR(acc->Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto unique_acc = this->storage->UniqueAccess(ReplicationRole::MAIN);
|
||||
EXPECT_FALSE(unique_acc->CreateIndex(this->edge_type_id1).HasError());
|
||||
ASSERT_NO_ERROR(unique_acc->Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::OLD), View::OLD),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
}
|
||||
|
||||
{
|
||||
auto unique_acc = this->storage->UniqueAccess(ReplicationRole::MAIN);
|
||||
EXPECT_FALSE(unique_acc->DropIndex(this->edge_type_id1).HasError());
|
||||
ASSERT_NO_ERROR(unique_acc->Commit());
|
||||
}
|
||||
{
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
EXPECT_FALSE(acc->EdgeTypeIndexExists(this->edge_type_id1));
|
||||
EXPECT_EQ(acc->ListAllIndices().label.size(), 0);
|
||||
}
|
||||
|
||||
{
|
||||
auto unique_acc = this->storage->UniqueAccess(ReplicationRole::MAIN);
|
||||
EXPECT_TRUE(unique_acc->DropIndex(this->edge_type_id1).HasError());
|
||||
ASSERT_NO_ERROR(unique_acc->Commit());
|
||||
}
|
||||
{
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
EXPECT_FALSE(acc->EdgeTypeIndexExists(this->edge_type_id1));
|
||||
EXPECT_EQ(acc->ListAllIndices().label.size(), 0);
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
for (int i = 10; i < 20; ++i) {
|
||||
auto vertex_from = this->CreateVertexWithoutProperties(acc.get());
|
||||
auto vertex_to = this->CreateVertexWithoutProperties(acc.get());
|
||||
this->CreateEdge(&vertex_from, &vertex_to, i % 2 ? this->edge_type_id1 : this->edge_type_id2, acc.get());
|
||||
}
|
||||
ASSERT_NO_ERROR(acc->Commit());
|
||||
}
|
||||
|
||||
{
|
||||
auto unique_acc = this->storage->UniqueAccess(ReplicationRole::MAIN);
|
||||
EXPECT_FALSE(unique_acc->CreateIndex(this->edge_type_id1).HasError());
|
||||
ASSERT_NO_ERROR(unique_acc->Commit());
|
||||
}
|
||||
{
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
EXPECT_TRUE(acc->EdgeTypeIndexExists(this->edge_type_id1));
|
||||
EXPECT_THAT(acc->ListAllIndices().edge_type, UnorderedElementsAre(this->edge_type_id1));
|
||||
}
|
||||
|
||||
{
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::OLD), View::OLD),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
|
||||
|
||||
acc->AdvanceCommand();
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::OLD), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TYPED_TEST(IndexTest, EdgeTypeIndexBasic) {
|
||||
// The following steps are performed and index correctness is validated after
|
||||
// each step:
|
||||
// 1. Create 10 edges numbered from 0 to 9.
|
||||
// 2. Add EdgeType1 to odd numbered, and EdgeType2 to even numbered edges.
|
||||
// 3. Delete even numbered edges.
|
||||
if constexpr ((std::is_same_v<TypeParam, memgraph::storage::InMemoryStorage>)) {
|
||||
{
|
||||
auto unique_acc = this->storage->UniqueAccess(ReplicationRole::MAIN);
|
||||
EXPECT_FALSE(unique_acc->CreateIndex(this->edge_type_id1).HasError());
|
||||
ASSERT_NO_ERROR(unique_acc->Commit());
|
||||
}
|
||||
{
|
||||
auto unique_acc = this->storage->UniqueAccess(ReplicationRole::MAIN);
|
||||
EXPECT_FALSE(unique_acc->CreateIndex(this->edge_type_id2).HasError());
|
||||
ASSERT_NO_ERROR(unique_acc->Commit());
|
||||
}
|
||||
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
EXPECT_THAT(acc->ListAllIndices().edge_type, UnorderedElementsAre(this->edge_type_id1, this->edge_type_id2));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::OLD), View::OLD), IsEmpty());
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id2, View::OLD), View::OLD), IsEmpty());
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW), IsEmpty());
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id2, View::NEW), View::NEW), IsEmpty());
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
auto vertex_from = this->CreateVertexWithoutProperties(acc.get());
|
||||
auto vertex_to = this->CreateVertexWithoutProperties(acc.get());
|
||||
this->CreateEdge(&vertex_from, &vertex_to, i % 2 ? this->edge_type_id1 : this->edge_type_id2, acc.get());
|
||||
}
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::OLD), View::OLD), IsEmpty());
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id2, View::OLD), View::OLD), IsEmpty());
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id2, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
|
||||
acc->AdvanceCommand();
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::OLD), View::OLD),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id2, View::OLD), View::OLD),
|
||||
UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id2, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
|
||||
for (auto vertex : acc->Vertices(View::OLD)) {
|
||||
auto edges = vertex.OutEdges(View::OLD)->edges;
|
||||
for (auto &edge : edges) {
|
||||
int64_t id = edge.GetProperty(this->prop_id, View::OLD)->ValueInt();
|
||||
if (id % 2 == 0) {
|
||||
ASSERT_NO_ERROR(acc->DetachDelete({}, {&edge}, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::OLD), View::OLD),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id2, View::OLD), View::OLD),
|
||||
UnorderedElementsAre(0, 2, 4, 6, 8));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id2, View::NEW), View::NEW), IsEmpty());
|
||||
|
||||
acc->AdvanceCommand();
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::OLD), View::OLD),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id2, View::OLD), View::OLD), IsEmpty());
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(1, 3, 5, 7, 9));
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id2, View::NEW), View::NEW), IsEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TYPED_TEST(IndexTest, EdgeTypeIndexTransactionalIsolation) {
|
||||
if constexpr ((std::is_same_v<TypeParam, memgraph::storage::InMemoryStorage>)) {
|
||||
// Check that transactions only see entries they are supposed to see.
|
||||
{
|
||||
auto unique_acc = this->storage->UniqueAccess(ReplicationRole::MAIN);
|
||||
EXPECT_FALSE(unique_acc->CreateIndex(this->edge_type_id1).HasError());
|
||||
ASSERT_NO_ERROR(unique_acc->Commit());
|
||||
}
|
||||
{
|
||||
auto unique_acc = this->storage->UniqueAccess(ReplicationRole::MAIN);
|
||||
EXPECT_FALSE(unique_acc->CreateIndex(this->edge_type_id2).HasError());
|
||||
ASSERT_NO_ERROR(unique_acc->Commit());
|
||||
}
|
||||
|
||||
auto acc_before = this->storage->Access(ReplicationRole::MAIN);
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
auto acc_after = this->storage->Access(ReplicationRole::MAIN);
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
auto vertex_from = this->CreateVertexWithoutProperties(acc.get());
|
||||
auto vertex_to = this->CreateVertexWithoutProperties(acc.get());
|
||||
this->CreateEdge(&vertex_from, &vertex_to, this->edge_type_id1, acc.get());
|
||||
}
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc_before->Edges(this->edge_type_id1, View::NEW), View::NEW), IsEmpty());
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc_after->Edges(this->edge_type_id1, View::NEW), View::NEW), IsEmpty());
|
||||
|
||||
ASSERT_NO_ERROR(acc->Commit());
|
||||
|
||||
auto acc_after_commit = this->storage->Access(ReplicationRole::MAIN);
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc_before->Edges(this->edge_type_id1, View::NEW), View::NEW), IsEmpty());
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc_after->Edges(this->edge_type_id1, View::NEW), View::NEW), IsEmpty());
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc_after_commit->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TYPED_TEST(IndexTest, EdgeTypeIndexCountEstimate) {
|
||||
if constexpr ((std::is_same_v<TypeParam, memgraph::storage::InMemoryStorage>)) {
|
||||
{
|
||||
auto unique_acc = this->storage->UniqueAccess(ReplicationRole::MAIN);
|
||||
EXPECT_FALSE(unique_acc->CreateIndex(this->edge_type_id1).HasError());
|
||||
ASSERT_NO_ERROR(unique_acc->Commit());
|
||||
}
|
||||
{
|
||||
auto unique_acc = this->storage->UniqueAccess(ReplicationRole::MAIN);
|
||||
EXPECT_FALSE(unique_acc->CreateIndex(this->edge_type_id2).HasError());
|
||||
ASSERT_NO_ERROR(unique_acc->Commit());
|
||||
}
|
||||
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
auto vertex_from = this->CreateVertexWithoutProperties(acc.get());
|
||||
auto vertex_to = this->CreateVertexWithoutProperties(acc.get());
|
||||
this->CreateEdge(&vertex_from, &vertex_to, i % 3 ? this->edge_type_id1 : this->edge_type_id2, acc.get());
|
||||
}
|
||||
|
||||
EXPECT_EQ(acc->ApproximateEdgeCount(this->edge_type_id1), 13);
|
||||
EXPECT_EQ(acc->ApproximateEdgeCount(this->edge_type_id2), 7);
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TYPED_TEST(IndexTest, EdgeTypeIndexRepeatingEdgeTypesBetweenSameVertices) {
|
||||
if constexpr ((std::is_same_v<TypeParam, memgraph::storage::InMemoryStorage>)) {
|
||||
{
|
||||
auto unique_acc = this->storage->UniqueAccess(ReplicationRole::MAIN);
|
||||
EXPECT_FALSE(unique_acc->CreateIndex(this->edge_type_id1).HasError());
|
||||
ASSERT_NO_ERROR(unique_acc->Commit());
|
||||
}
|
||||
|
||||
auto acc = this->storage->Access(ReplicationRole::MAIN);
|
||||
auto vertex_from = this->CreateVertexWithoutProperties(acc.get());
|
||||
auto vertex_to = this->CreateVertexWithoutProperties(acc.get());
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
this->CreateEdge(&vertex_from, &vertex_to, this->edge_type_id1, acc.get());
|
||||
}
|
||||
|
||||
EXPECT_EQ(acc->ApproximateEdgeCount(this->edge_type_id1), 5);
|
||||
|
||||
EXPECT_THAT(this->GetIds(acc->Edges(this->edge_type_id1, View::NEW), View::NEW),
|
||||
UnorderedElementsAre(0, 1, 2, 3, 4));
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,10 @@ memgraph::storage::durability::WalDeltaData::Type StorageMetadataOperationToWalD
|
||||
return memgraph::storage::durability::WalDeltaData::Type::LABEL_INDEX_CREATE;
|
||||
case memgraph::storage::durability::StorageMetadataOperation::LABEL_INDEX_DROP:
|
||||
return memgraph::storage::durability::WalDeltaData::Type::LABEL_INDEX_DROP;
|
||||
case memgraph::storage::durability::StorageMetadataOperation::EDGE_TYPE_INDEX_CREATE:
|
||||
return memgraph::storage::durability::WalDeltaData::Type::EDGE_INDEX_CREATE;
|
||||
case memgraph::storage::durability::StorageMetadataOperation::EDGE_TYPE_INDEX_DROP:
|
||||
return memgraph::storage::durability::WalDeltaData::Type::EDGE_INDEX_DROP;
|
||||
case memgraph::storage::durability::StorageMetadataOperation::LABEL_INDEX_STATS_SET:
|
||||
return memgraph::storage::durability::WalDeltaData::Type::LABEL_INDEX_STATS_SET;
|
||||
case memgraph::storage::durability::StorageMetadataOperation::LABEL_INDEX_STATS_CLEAR:
|
||||
@ -280,6 +284,41 @@ class DeltaGenerator final {
|
||||
case memgraph::storage::durability::StorageMetadataOperation::UNIQUE_CONSTRAINT_DROP:
|
||||
data.operation_label_properties.label = label;
|
||||
data.operation_label_properties.properties = properties;
|
||||
break;
|
||||
case memgraph::storage::durability::StorageMetadataOperation::EDGE_TYPE_INDEX_CREATE:
|
||||
case memgraph::storage::durability::StorageMetadataOperation::EDGE_TYPE_INDEX_DROP:
|
||||
MG_ASSERT(false, "Invalid function call!");
|
||||
}
|
||||
data_.emplace_back(timestamp_, data);
|
||||
}
|
||||
}
|
||||
|
||||
void AppendEdgeTypeOperation(memgraph::storage::durability::StorageMetadataOperation operation,
|
||||
const std::string &edge_type) {
|
||||
auto edge_type_id = memgraph::storage::EdgeTypeId::FromUint(mapper_.NameToId(edge_type));
|
||||
wal_file_.AppendOperation(operation, edge_type_id, timestamp_);
|
||||
if (valid_) {
|
||||
UpdateStats(timestamp_, 1);
|
||||
memgraph::storage::durability::WalDeltaData data;
|
||||
data.type = StorageMetadataOperationToWalDeltaDataType(operation);
|
||||
switch (operation) {
|
||||
case memgraph::storage::durability::StorageMetadataOperation::EDGE_TYPE_INDEX_CREATE:
|
||||
case memgraph::storage::durability::StorageMetadataOperation::EDGE_TYPE_INDEX_DROP:
|
||||
data.operation_edge_type.edge_type = edge_type;
|
||||
break;
|
||||
case memgraph::storage::durability::StorageMetadataOperation::LABEL_INDEX_CREATE:
|
||||
case memgraph::storage::durability::StorageMetadataOperation::LABEL_INDEX_DROP:
|
||||
case memgraph::storage::durability::StorageMetadataOperation::LABEL_INDEX_STATS_CLEAR:
|
||||
case memgraph::storage::durability::StorageMetadataOperation::LABEL_PROPERTY_INDEX_STATS_CLEAR:
|
||||
case memgraph::storage::durability::StorageMetadataOperation::LABEL_INDEX_STATS_SET:
|
||||
case memgraph::storage::durability::StorageMetadataOperation::LABEL_PROPERTY_INDEX_CREATE:
|
||||
case memgraph::storage::durability::StorageMetadataOperation::LABEL_PROPERTY_INDEX_DROP:
|
||||
case memgraph::storage::durability::StorageMetadataOperation::EXISTENCE_CONSTRAINT_CREATE:
|
||||
case memgraph::storage::durability::StorageMetadataOperation::EXISTENCE_CONSTRAINT_DROP:;
|
||||
case memgraph::storage::durability::StorageMetadataOperation::LABEL_PROPERTY_INDEX_STATS_SET:
|
||||
case memgraph::storage::durability::StorageMetadataOperation::UNIQUE_CONSTRAINT_CREATE:
|
||||
case memgraph::storage::durability::StorageMetadataOperation::UNIQUE_CONSTRAINT_DROP:
|
||||
MG_ASSERT(false, "Invalid function call!");
|
||||
}
|
||||
data_.emplace_back(timestamp_, data);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user