Add CREATE SNAPSHOT query (#182)

This commit is contained in:
antonio2368 2021-06-30 12:31:30 +02:00 committed by GitHub
parent 715162e205
commit 3b336e3e0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 115 additions and 28 deletions

View File

@ -41,8 +41,8 @@ std::string PermissionToString(Permission permission) {
return "DUMP"; return "DUMP";
case Permission::REPLICATION: case Permission::REPLICATION:
return "REPLICATION"; return "REPLICATION";
case Permission::LOCK_PATH: case Permission::DURABILITY:
return "LOCK_PATH"; return "DURABILITY";
case Permission::READ_FILE: case Permission::READ_FILE:
return "READ_FILE"; return "READ_FILE";
case Permission::FREE_MEMORY: case Permission::FREE_MEMORY:

View File

@ -22,7 +22,7 @@ enum class Permission : uint64_t {
CONSTRAINT = 1U << 8U, CONSTRAINT = 1U << 8U,
DUMP = 1U << 9U, DUMP = 1U << 9U,
REPLICATION = 1U << 10U, REPLICATION = 1U << 10U,
LOCK_PATH = 1U << 11U, DURABILITY = 1U << 11U,
READ_FILE = 1U << 12U, READ_FILE = 1U << 12U,
FREE_MEMORY = 1U << 13U, FREE_MEMORY = 1U << 13U,
TRIGGER = 1U << 14U, TRIGGER = 1U << 14U,
@ -32,12 +32,12 @@ enum class Permission : uint64_t {
// clang-format on // clang-format on
// Constant list of all available permissions. // Constant list of all available permissions.
const std::vector<Permission> kPermissionsAll = {Permission::MATCH, Permission::CREATE, Permission::MERGE, const std::vector<Permission> kPermissionsAll = {Permission::MATCH, Permission::CREATE, Permission::MERGE,
Permission::DELETE, Permission::SET, Permission::REMOVE, Permission::DELETE, Permission::SET, Permission::REMOVE,
Permission::INDEX, Permission::STATS, Permission::CONSTRAINT, Permission::INDEX, Permission::STATS, Permission::CONSTRAINT,
Permission::DUMP, Permission::AUTH, Permission::REPLICATION, Permission::DUMP, Permission::AUTH, Permission::REPLICATION,
Permission::LOCK_PATH, Permission::READ_FILE, Permission::FREE_MEMORY, Permission::DURABILITY, Permission::READ_FILE, Permission::FREE_MEMORY,
Permission::TRIGGER, Permission::CONFIG}; Permission::TRIGGER, Permission::CONFIG};
// Function that converts a permission to its string representation. // Function that converts a permission to its string representation.
std::string PermissionToString(Permission permission); std::string PermissionToString(Permission permission);

View File

@ -26,8 +26,8 @@ auth::Permission PrivilegeToPermission(query::AuthQuery::Privilege privilege) {
return auth::Permission::DUMP; return auth::Permission::DUMP;
case query::AuthQuery::Privilege::REPLICATION: case query::AuthQuery::Privilege::REPLICATION:
return auth::Permission::REPLICATION; return auth::Permission::REPLICATION;
case query::AuthQuery::Privilege::LOCK_PATH: case query::AuthQuery::Privilege::DURABILITY:
return auth::Permission::LOCK_PATH; return auth::Permission::DURABILITY;
case query::AuthQuery::Privilege::READ_FILE: case query::AuthQuery::Privilege::READ_FILE:
return auth::Permission::READ_FILE; return auth::Permission::READ_FILE;
case query::AuthQuery::Privilege::FREE_MEMORY: case query::AuthQuery::Privilege::FREE_MEMORY:

View File

@ -181,4 +181,10 @@ class IsolationLevelModificationInMulticommandTxException : public QueryExceptio
IsolationLevelModificationInMulticommandTxException() IsolationLevelModificationInMulticommandTxException()
: QueryException("Isolation level cannot be modified in multicommand transactions.") {} : QueryException("Isolation level cannot be modified in multicommand transactions.") {}
}; };
class CreateSnapshotInMulticommandTxException final : public QueryException {
public:
CreateSnapshotInMulticommandTxException()
: QueryException("Snapshot cannot be created in multicommand transactions.") {}
};
} // namespace query } // namespace query

View File

@ -2193,7 +2193,7 @@ cpp<#
(:serialize)) (:serialize))
(lcp:define-enum privilege (lcp:define-enum privilege
(create delete match merge set remove index stats auth constraint (create delete match merge set remove index stats auth constraint
dump replication lock_path read_file free_memory trigger config) dump replication durability read_file free_memory trigger config)
(:serialize)) (:serialize))
#>cpp #>cpp
AuthQuery() = default; AuthQuery() = default;
@ -2231,7 +2231,7 @@ const std::vector<AuthQuery::Privilege> kPrivilegesAll = {
AuthQuery::Privilege::CONSTRAINT, AuthQuery::Privilege::DUMP, AuthQuery::Privilege::CONSTRAINT, AuthQuery::Privilege::DUMP,
AuthQuery::Privilege::REPLICATION, AuthQuery::Privilege::REPLICATION,
AuthQuery::Privilege::READ_FILE, AuthQuery::Privilege::READ_FILE,
AuthQuery::Privilege::LOCK_PATH, AuthQuery::Privilege::DURABILITY,
AuthQuery::Privilege::FREE_MEMORY, AuthQuery::Privilege::TRIGGER, AuthQuery::Privilege::FREE_MEMORY, AuthQuery::Privilege::TRIGGER,
AuthQuery::Privilege::CONFIG}; AuthQuery::Privilege::CONFIG};
cpp<# cpp<#
@ -2456,4 +2456,12 @@ cpp<#
(:serialize (:slk)) (:serialize (:slk))
(:clone)) (:clone))
(lcp:define-class create-snapshot-query (query) ()
(:public
#>cpp
DEFVISITABLE(QueryVisitor<void>);
cpp<#)
(:serialize (:slk))
(:clone))
(lcp:pop-namespace) ;; namespace query (lcp:pop-namespace) ;; namespace query

View File

@ -78,6 +78,7 @@ class LoadCsv;
class FreeMemoryQuery; class FreeMemoryQuery;
class TriggerQuery; class TriggerQuery;
class IsolationLevelQuery; class IsolationLevelQuery;
class CreateSnapshotQuery;
using TreeCompositeVisitor = ::utils::CompositeVisitor< using TreeCompositeVisitor = ::utils::CompositeVisitor<
SingleQuery, CypherUnion, NamedExpression, OrOperator, XorOperator, AndOperator, NotOperator, AdditionOperator, SingleQuery, CypherUnion, NamedExpression, OrOperator, XorOperator, AndOperator, NotOperator, AdditionOperator,
@ -111,6 +112,7 @@ class ExpressionVisitor
template <class TResult> template <class TResult>
class QueryVisitor : public ::utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery, IndexQuery, AuthQuery, class QueryVisitor : public ::utils::Visitor<TResult, CypherQuery, ExplainQuery, ProfileQuery, IndexQuery, AuthQuery,
InfoQuery, ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery, InfoQuery, ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery,
FreeMemoryQuery, TriggerQuery, IsolationLevelQuery> {}; FreeMemoryQuery, TriggerQuery, IsolationLevelQuery, CreateSnapshotQuery> {
};
} // namespace query } // namespace query

View File

@ -442,6 +442,11 @@ antlrcpp::Any CypherMainVisitor::visitIsolationLevelQuery(MemgraphCypher::Isolat
return isolation_level_query; return isolation_level_query;
} }
antlrcpp::Any CypherMainVisitor::visitCreateSnapshotQuery(MemgraphCypher::CreateSnapshotQueryContext *ctx) {
query_ = storage_->Create<CreateSnapshotQuery>();
return query_;
}
antlrcpp::Any CypherMainVisitor::visitCypherUnion(MemgraphCypher::CypherUnionContext *ctx) { antlrcpp::Any CypherMainVisitor::visitCypherUnion(MemgraphCypher::CypherUnionContext *ctx) {
bool distinct = !ctx->ALL(); bool distinct = !ctx->ALL();
auto *cypher_union = storage_->Create<CypherUnion>(distinct); auto *cypher_union = storage_->Create<CypherUnion>(distinct);
@ -870,11 +875,11 @@ antlrcpp::Any CypherMainVisitor::visitPrivilege(MemgraphCypher::PrivilegeContext
if (ctx->CONSTRAINT()) return AuthQuery::Privilege::CONSTRAINT; if (ctx->CONSTRAINT()) return AuthQuery::Privilege::CONSTRAINT;
if (ctx->DUMP()) return AuthQuery::Privilege::DUMP; if (ctx->DUMP()) return AuthQuery::Privilege::DUMP;
if (ctx->REPLICATION()) return AuthQuery::Privilege::REPLICATION; if (ctx->REPLICATION()) return AuthQuery::Privilege::REPLICATION;
if (ctx->LOCK_PATH()) return AuthQuery::Privilege::LOCK_PATH;
if (ctx->READ_FILE()) return AuthQuery::Privilege::READ_FILE; if (ctx->READ_FILE()) return AuthQuery::Privilege::READ_FILE;
if (ctx->FREE_MEMORY()) return AuthQuery::Privilege::FREE_MEMORY; if (ctx->FREE_MEMORY()) return AuthQuery::Privilege::FREE_MEMORY;
if (ctx->TRIGGER()) return AuthQuery::Privilege::TRIGGER; if (ctx->TRIGGER()) return AuthQuery::Privilege::TRIGGER;
if (ctx->CONFIG()) return AuthQuery::Privilege::CONFIG; if (ctx->CONFIG()) return AuthQuery::Privilege::CONFIG;
if (ctx->DURABILITY()) return AuthQuery::Privilege::DURABILITY;
LOG_FATAL("Should not get here - unknown privilege!"); LOG_FATAL("Should not get here - unknown privilege!");
} }

View File

@ -243,6 +243,11 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
*/ */
antlrcpp::Any visitIsolationLevelQuery(MemgraphCypher::IsolationLevelQueryContext *ctx) override; antlrcpp::Any visitIsolationLevelQuery(MemgraphCypher::IsolationLevelQueryContext *ctx) override;
/**
* @return CreateSnapshotQuery*
*/
antlrcpp::Any visitCreateSnapshotQuery(MemgraphCypher::CreateSnapshotQueryContext *ctx) override;
/** /**
* @return CypherUnion* * @return CypherUnion*
*/ */

View File

@ -86,6 +86,7 @@ query : cypherQuery
| freeMemoryQuery | freeMemoryQuery
| triggerQuery | triggerQuery
| isolationLevelQuery | isolationLevelQuery
| createSnapshotQuery
; ;
authQuery : createRole authQuery : createRole
@ -183,11 +184,11 @@ privilege : CREATE
| CONSTRAINT | CONSTRAINT
| DUMP | DUMP
| REPLICATION | REPLICATION
| LOCK_PATH
| READ_FILE | READ_FILE
| FREE_MEMORY | FREE_MEMORY
| TRIGGER | TRIGGER
| CONFIG | CONFIG
| DURABILITY
; ;
privilegeList : privilege ( ',' privilege )* ; privilegeList : privilege ( ',' privilege )* ;
@ -241,3 +242,5 @@ isolationLevel : SNAPSHOT ISOLATION | READ COMMITTED | READ UNCOMMITTED ;
isolationLevelScope : GLOBAL | SESSION | NEXT ; isolationLevelScope : GLOBAL | SESSION | NEXT ;
isolationLevelQuery : SET isolationLevelScope TRANSACTION ISOLATION LEVEL isolationLevel ; isolationLevelQuery : SET isolationLevelScope TRANSACTION ISOLATION LEVEL isolationLevel ;
createSnapshotQuery : CREATE SNAPSHOT ;

View File

@ -30,6 +30,7 @@ DENY : D E N Y ;
DIRECTORY : D I R E C T O R Y ; DIRECTORY : D I R E C T O R Y ;
DROP : D R O P ; DROP : D R O P ;
DUMP : D U M P ; DUMP : D U M P ;
DURABILITY : D U R A B I L I T Y ;
EXECUTE : E X E C U T E ; EXECUTE : E X E C U T E ;
FOR : F O R ; FOR : F O R ;
FREE : F R E E ; FREE : F R E E ;
@ -45,7 +46,6 @@ ISOLATION : I S O L A T I O N ;
LEVEL : L E V E L ; LEVEL : L E V E L ;
LOAD : L O A D ; LOAD : L O A D ;
LOCK : L O C K ; LOCK : L O C K ;
LOCK_PATH : L O C K UNDERSCORE P A T H ;
MAIN : M A I N ; MAIN : M A I N ;
MODE : M O D E ; MODE : M O D E ;
NEXT : N E X T ; NEXT : N E X T ;

View File

@ -49,7 +49,7 @@ class PrivilegeExtractor : public QueryVisitor<void>, public HierarchicalTreeVis
void Visit(DumpQuery &dump_query) override { AddPrivilege(AuthQuery::Privilege::DUMP); } void Visit(DumpQuery &dump_query) override { AddPrivilege(AuthQuery::Privilege::DUMP); }
void Visit(LockPathQuery &lock_path_query) override { AddPrivilege(AuthQuery::Privilege::LOCK_PATH); } void Visit(LockPathQuery &lock_path_query) override { AddPrivilege(AuthQuery::Privilege::DURABILITY); }
void Visit(FreeMemoryQuery &free_memory_query) override { AddPrivilege(AuthQuery::Privilege::FREE_MEMORY); } void Visit(FreeMemoryQuery &free_memory_query) override { AddPrivilege(AuthQuery::Privilege::FREE_MEMORY); }
@ -59,6 +59,8 @@ class PrivilegeExtractor : public QueryVisitor<void>, public HierarchicalTreeVis
void Visit(IsolationLevelQuery &isolation_level_query) override { AddPrivilege(AuthQuery::Privilege::CONFIG); } void Visit(IsolationLevelQuery &isolation_level_query) override { AddPrivilege(AuthQuery::Privilege::CONFIG); }
void Visit(CreateSnapshotQuery &create_snapshot_query) override { AddPrivilege(AuthQuery::Privilege::DURABILITY); }
bool PreVisit(Create & /*unused*/) override { bool PreVisit(Create & /*unused*/) override {
AddPrivilege(AuthQuery::Privilege::CREATE); AddPrivilege(AuthQuery::Privilege::CREATE);
return false; return false;

View File

@ -6,6 +6,7 @@
#include "glue/communication.hpp" #include "glue/communication.hpp"
#include "query/constants.hpp" #include "query/constants.hpp"
#include "query/context.hpp" #include "query/context.hpp"
#include "query/cypher_query_interpreter.hpp"
#include "query/db_accessor.hpp" #include "query/db_accessor.hpp"
#include "query/dump.hpp" #include "query/dump.hpp"
#include "query/exceptions.hpp" #include "query/exceptions.hpp"
@ -1207,6 +1208,28 @@ PreparedQuery PrepareIsolationLevelQuery(ParsedQuery parsed_query, const bool in
RWType::NONE}; RWType::NONE};
} }
PreparedQuery PrepareCreateSnapshotQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
InterpreterContext *interpreter_context) {
if (in_explicit_transaction) {
throw CreateSnapshotInMulticommandTxException();
}
return PreparedQuery{
{},
std::move(parsed_query.required_privileges),
[interpreter_context](AnyStream *stream, std::optional<int> n) -> std::optional<QueryHandlerResult> {
if (auto maybe_error = interpreter_context->db->CreateSnapshot(); maybe_error.HasError()) {
switch (maybe_error.GetError()) {
case storage::Storage::CreateSnapshotError::DisabledForReplica:
throw utils::BasicException(
"Failed to create a snapshot. Replica instances are not allowed to create them.");
}
}
return QueryHandlerResult::COMMIT;
},
RWType::NONE};
}
PreparedQuery PrepareInfoQuery(ParsedQuery parsed_query, bool in_explicit_transaction, PreparedQuery PrepareInfoQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
std::map<std::string, TypedValue> *summary, InterpreterContext *interpreter_context, std::map<std::string, TypedValue> *summary, InterpreterContext *interpreter_context,
storage::Storage *db, utils::MemoryResource *execution_memory) { storage::Storage *db, utils::MemoryResource *execution_memory) {
@ -1552,6 +1575,9 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
} else if (utils::Downcast<IsolationLevelQuery>(parsed_query.query)) { } else if (utils::Downcast<IsolationLevelQuery>(parsed_query.query)) {
prepared_query = prepared_query =
PrepareIsolationLevelQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_, this); PrepareIsolationLevelQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_, this);
} else if (utils::Downcast<CreateSnapshotQuery>(parsed_query.query)) {
prepared_query =
PrepareCreateSnapshotQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_);
} else { } else {
LOG_FATAL("Should not get here -- unknown query type!"); LOG_FATAL("Should not get here -- unknown query type!");
} }

View File

@ -361,7 +361,15 @@ Storage::Storage(Config config)
} }
} }
if (config_.durability.snapshot_wal_mode != Config::Durability::SnapshotWalMode::DISABLED) { if (config_.durability.snapshot_wal_mode != Config::Durability::SnapshotWalMode::DISABLED) {
snapshot_runner_.Run("Snapshot", config_.durability.snapshot_interval, [this] { this->CreateSnapshot(); }); snapshot_runner_.Run("Snapshot", config_.durability.snapshot_interval, [this] {
if (auto maybe_error = this->CreateSnapshot(); maybe_error.HasError()) {
switch (maybe_error.GetError()) {
case CreateSnapshotError::DisabledForReplica:
spdlog::warn("Snapshots are disabled for replicas!");
break;
}
}
});
} }
if (config_.gc.type == Config::Gc::Type::PERIODIC) { if (config_.gc.type == Config::Gc::Type::PERIODIC) {
gc_runner_.Run("Storage GC", config_.gc.interval, [this] { this->CollectGarbage<false>(); }); gc_runner_.Run("Storage GC", config_.gc.interval, [this] { this->CollectGarbage<false>(); });
@ -391,7 +399,13 @@ Storage::~Storage() {
snapshot_runner_.Stop(); snapshot_runner_.Stop();
} }
if (config_.durability.snapshot_on_exit) { if (config_.durability.snapshot_on_exit) {
CreateSnapshot(); if (auto maybe_error = this->CreateSnapshot(); maybe_error.HasError()) {
switch (maybe_error.GetError()) {
case CreateSnapshotError::DisabledForReplica:
spdlog::warn("Snapshots are disabled for replicas!");
break;
}
}
} }
} }
@ -1727,12 +1741,13 @@ void Storage::AppendToWal(durability::StorageGlobalOperation operation, LabelId
FinalizeWalFile(); FinalizeWalFile();
} }
void Storage::CreateSnapshot() { utils::BasicResult<Storage::CreateSnapshotError> Storage::CreateSnapshot() {
if (replication_role_.load() != ReplicationRole::MAIN) { if (replication_role_.load() != ReplicationRole::MAIN) {
spdlog::warn("Snapshots are disabled for replicas!"); return CreateSnapshotError::DisabledForReplica;
return;
} }
std::lock_guard snapshot_guard(snapshot_lock_);
// Take master RW lock (for reading). // Take master RW lock (for reading).
std::shared_lock<utils::RWLock> storage_guard(main_lock_); std::shared_lock<utils::RWLock> storage_guard(main_lock_);
@ -1747,6 +1762,7 @@ void Storage::CreateSnapshot() {
// Finalize snapshot transaction. // Finalize snapshot transaction.
commit_log_->MarkFinished(transaction.start_timestamp); commit_log_->MarkFinished(transaction.start_timestamp);
return {};
} }
bool Storage::LockPath() { bool Storage::LockPath() {

View File

@ -428,6 +428,10 @@ class Storage final {
void SetIsolationLevel(IsolationLevel isolation_level); void SetIsolationLevel(IsolationLevel isolation_level);
enum class CreateSnapshotError : uint8_t { DisabledForReplica };
utils::BasicResult<CreateSnapshotError> CreateSnapshot();
private: private:
Transaction CreateTransaction(IsolationLevel isolation_level); Transaction CreateTransaction(IsolationLevel isolation_level);
@ -452,8 +456,6 @@ class Storage final {
void AppendToWal(durability::StorageGlobalOperation operation, LabelId label, const std::set<PropertyId> &properties, void AppendToWal(durability::StorageGlobalOperation operation, LabelId label, const std::set<PropertyId> &properties,
uint64_t final_commit_timestamp); uint64_t final_commit_timestamp);
void CreateSnapshot();
uint64_t CommitTimestamp(std::optional<uint64_t> desired_commit_timestamp = {}); uint64_t CommitTimestamp(std::optional<uint64_t> desired_commit_timestamp = {});
// Main storage lock. // Main storage lock.
@ -518,6 +520,7 @@ class Storage final {
utils::OutputFile lock_file_handle_; utils::OutputFile lock_file_handle_;
utils::Scheduler snapshot_runner_; utils::Scheduler snapshot_runner_;
utils::SpinLock snapshot_lock_;
// UUID used to distinguish snapshots and to link snapshots to WALs // UUID used to distinguish snapshots and to link snapshots to WALs
std::string uuid_; std::string uuid_;

View File

@ -2057,8 +2057,8 @@ TEST_P(CypherMainVisitorTest, GrantPrivilege) {
{AuthQuery::Privilege::DUMP}); {AuthQuery::Privilege::DUMP});
check_auth_query(&ast_generator, "GRANT REPLICATION TO user", AuthQuery::Action::GRANT_PRIVILEGE, "", "", "user", {}, check_auth_query(&ast_generator, "GRANT REPLICATION TO user", AuthQuery::Action::GRANT_PRIVILEGE, "", "", "user", {},
{AuthQuery::Privilege::REPLICATION}); {AuthQuery::Privilege::REPLICATION});
check_auth_query(&ast_generator, "GRANT LOCK_PATH TO user", AuthQuery::Action::GRANT_PRIVILEGE, "", "", "user", {}, check_auth_query(&ast_generator, "GRANT DURABILITY TO user", AuthQuery::Action::GRANT_PRIVILEGE, "", "", "user", {},
{AuthQuery::Privilege::LOCK_PATH}); {AuthQuery::Privilege::DURABILITY});
check_auth_query(&ast_generator, "GRANT READ_FILE TO user", AuthQuery::Action::GRANT_PRIVILEGE, "", "", "user", {}, check_auth_query(&ast_generator, "GRANT READ_FILE TO user", AuthQuery::Action::GRANT_PRIVILEGE, "", "", "user", {},
{AuthQuery::Privilege::READ_FILE}); {AuthQuery::Privilege::READ_FILE});
check_auth_query(&ast_generator, "GRANT FREE_MEMORY TO user", AuthQuery::Action::GRANT_PRIVILEGE, "", "", "user", {}, check_auth_query(&ast_generator, "GRANT FREE_MEMORY TO user", AuthQuery::Action::GRANT_PRIVILEGE, "", "", "user", {},
@ -3198,4 +3198,9 @@ TEST_P(CypherMainVisitorTest, SetIsolationLevelQuery) {
} }
} }
} }
TEST_P(CypherMainVisitorTest, CreateSnapshotQuery) {
auto &ast_generator = *GetParam();
ASSERT_TRUE(dynamic_cast<CreateSnapshotQuery *>(ast_generator.ParseQuery("CREATE SNAPSHOT")));
}
} // namespace } // namespace

View File

@ -1,6 +1,7 @@
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "query/frontend/ast/ast.hpp"
#include "query/frontend/ast/ast_visitor.hpp" #include "query/frontend/ast/ast_visitor.hpp"
#include "query/frontend/semantic/required_privileges.hpp" #include "query/frontend/semantic/required_privileges.hpp"
#include "storage/v2/id_types.hpp" #include "storage/v2/id_types.hpp"
@ -142,7 +143,7 @@ TEST_F(TestPrivilegeExtractor, ReadFile) {
TEST_F(TestPrivilegeExtractor, LockPathQuery) { TEST_F(TestPrivilegeExtractor, LockPathQuery) {
auto *query = storage.Create<LockPathQuery>(); auto *query = storage.Create<LockPathQuery>();
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::LOCK_PATH)); EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::DURABILITY));
} }
TEST_F(TestPrivilegeExtractor, FreeMemoryQuery) { TEST_F(TestPrivilegeExtractor, FreeMemoryQuery) {
@ -159,3 +160,8 @@ TEST_F(TestPrivilegeExtractor, SetIsolationLevelQuery) {
auto *query = storage.Create<IsolationLevelQuery>(); auto *query = storage.Create<IsolationLevelQuery>();
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::CONFIG)); EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::CONFIG));
} }
TEST_F(TestPrivilegeExtractor, CreateSnapshotQuery) {
auto *query = storage.Create<CreateSnapshotQuery>();
EXPECT_THAT(GetRequiredPrivileges(query), UnorderedElementsAre(AuthQuery::Privilege::DURABILITY));
}