|
|
|
|
@@ -44,9 +44,7 @@
|
|
|
|
|
#include "query/stream/common.hpp"
|
|
|
|
|
#include "query/trigger.hpp"
|
|
|
|
|
#include "query/typed_value.hpp"
|
|
|
|
|
#include "storage/v2/id_types.hpp"
|
|
|
|
|
#include "storage/v2/property_value.hpp"
|
|
|
|
|
#include "storage/v2/replication/enums.hpp"
|
|
|
|
|
#include "utils/algorithm.hpp"
|
|
|
|
|
#include "utils/csv_parsing.hpp"
|
|
|
|
|
#include "utils/event_counter.hpp"
|
|
|
|
|
@@ -279,9 +277,6 @@ class AccessChecker final : public memgraph::query::AccessChecker {
|
|
|
|
|
memgraph::auth::User *user_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// returns false if the replication role can't be set
|
|
|
|
|
/// @throw QueryRuntimeException if an error ocurred.
|
|
|
|
|
|
|
|
|
|
Callback HandleAuthQuery(AuthQuery *auth_query, AuthQueryHandler *auth, const Parameters ¶meters,
|
|
|
|
|
DbAccessor *db_accessor) {
|
|
|
|
|
// Empty frame for evaluation of password expression. This is OK since
|
|
|
|
|
@@ -293,6 +288,7 @@ Callback HandleAuthQuery(AuthQuery *auth_query, AuthQueryHandler *auth, const Pa
|
|
|
|
|
// TODO: MemoryResource for EvaluationContext, it should probably be passed as
|
|
|
|
|
// the argument to Callback.
|
|
|
|
|
evaluation_context.timestamp = QueryTimestamp();
|
|
|
|
|
|
|
|
|
|
evaluation_context.parameters = parameters;
|
|
|
|
|
ExpressionEvaluator evaluator(&frame, symbol_table, evaluation_context, db_accessor, storage::View::OLD);
|
|
|
|
|
|
|
|
|
|
@@ -301,6 +297,7 @@ Callback HandleAuthQuery(AuthQuery *auth_query, AuthQueryHandler *auth, const Pa
|
|
|
|
|
std::string user_or_role = auth_query->user_or_role_;
|
|
|
|
|
std::vector<AuthQuery::Privilege> privileges = auth_query->privileges_;
|
|
|
|
|
std::vector<std::string> edgeTypes = auth_query->edgetypes_;
|
|
|
|
|
// std::vector<storage::LabelId> labels = NamesToLabels(labels, db_accessor);
|
|
|
|
|
auto password = EvaluateOptionalExpression(auth_query->password_, &evaluator);
|
|
|
|
|
|
|
|
|
|
Callback callback;
|
|
|
|
|
@@ -313,10 +310,11 @@ Callback HandleAuthQuery(AuthQuery *auth_query, AuthQueryHandler *auth, const Pa
|
|
|
|
|
AuthQuery::Action::REVOKE_PRIVILEGE, AuthQuery::Action::SHOW_PRIVILEGES, AuthQuery::Action::SHOW_USERS_FOR_ROLE,
|
|
|
|
|
AuthQuery::Action::SHOW_ROLE_FOR_USER};
|
|
|
|
|
|
|
|
|
|
if (license_check_result.HasError() && enterprise_only_methods.contains(auth_query->action_)) {
|
|
|
|
|
throw utils::BasicException(
|
|
|
|
|
utils::license::LicenseCheckErrorToString(license_check_result.GetError(), "advanced authentication features"));
|
|
|
|
|
}
|
|
|
|
|
// if (license_check_result.HasError() && enterprise_only_methods.contains(auth_query->action_)) {
|
|
|
|
|
// throw utils::BasicException(
|
|
|
|
|
// utils::license::LicenseCheckErrorToString(license_check_result.GetError(), "advanced authentication
|
|
|
|
|
// features"));
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
switch (auth_query->action_) {
|
|
|
|
|
case AuthQuery::Action::CREATE_USER:
|
|
|
|
|
@@ -330,7 +328,7 @@ Callback HandleAuthQuery(AuthQuery *auth_query, AuthQueryHandler *auth, const Pa
|
|
|
|
|
// If the license is not valid we create users with admin access
|
|
|
|
|
if (!valid_enterprise_license) {
|
|
|
|
|
spdlog::warn("Granting all the privileges to {}.", username);
|
|
|
|
|
auth->GrantPrivilege(username, kPrivilegesAll, {"*"});
|
|
|
|
|
auth->GrantPrivilege(username, kPrivilegesAll, {});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return std::vector<std::vector<TypedValue>>();
|
|
|
|
|
@@ -918,7 +916,7 @@ struct PullPlanVector {
|
|
|
|
|
struct PullPlan {
|
|
|
|
|
explicit PullPlan(std::shared_ptr<CachedPlan> plan, const Parameters ¶meters, bool is_profile_query,
|
|
|
|
|
DbAccessor *dba, InterpreterContext *interpreter_context, utils::MemoryResource *execution_memory,
|
|
|
|
|
TriggerContextCollector *trigger_context_collector = nullptr,
|
|
|
|
|
std::optional<std::string> username, TriggerContextCollector *trigger_context_collector = nullptr,
|
|
|
|
|
std::optional<size_t> memory_limit = {});
|
|
|
|
|
std::optional<plan::ProfilingStatsWithTotalTime> Pull(AnyStream *stream, std::optional<int> n,
|
|
|
|
|
const std::vector<Symbol> &output_symbols,
|
|
|
|
|
@@ -947,7 +945,8 @@ struct PullPlan {
|
|
|
|
|
|
|
|
|
|
PullPlan::PullPlan(const std::shared_ptr<CachedPlan> plan, const Parameters ¶meters, const bool is_profile_query,
|
|
|
|
|
DbAccessor *dba, InterpreterContext *interpreter_context, utils::MemoryResource *execution_memory,
|
|
|
|
|
TriggerContextCollector *trigger_context_collector, const std::optional<size_t> memory_limit)
|
|
|
|
|
std::optional<std::string> username, TriggerContextCollector *trigger_context_collector,
|
|
|
|
|
const std::optional<size_t> memory_limit)
|
|
|
|
|
: plan_(plan),
|
|
|
|
|
cursor_(plan->plan().MakeCursor(execution_memory)),
|
|
|
|
|
frame_(plan->symbol_table().max_position(), execution_memory),
|
|
|
|
|
@@ -958,6 +957,12 @@ PullPlan::PullPlan(const std::shared_ptr<CachedPlan> plan, const Parameters &par
|
|
|
|
|
ctx_.evaluation_context.parameters = parameters;
|
|
|
|
|
ctx_.evaluation_context.properties = NamesToProperties(plan->ast_storage().properties_, dba);
|
|
|
|
|
ctx_.evaluation_context.labels = NamesToLabels(plan->ast_storage().labels_, dba);
|
|
|
|
|
#ifdef MG_ENTERPRISE
|
|
|
|
|
if (username.has_value()) {
|
|
|
|
|
memgraph::auth::User *user = interpreter_context->auth->GetUser(*username);
|
|
|
|
|
ctx_.access_checker = new AccessChecker{user};
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
if (interpreter_context->config.execution_timeout_sec > 0) {
|
|
|
|
|
ctx_.timer = utils::AsyncTimer{interpreter_context->config.execution_timeout_sec};
|
|
|
|
|
}
|
|
|
|
|
@@ -1131,6 +1136,7 @@ PreparedQuery Interpreter::PrepareTransactionQuery(std::string_view query_upper)
|
|
|
|
|
PreparedQuery PrepareCypherQuery(ParsedQuery parsed_query, std::map<std::string, TypedValue> *summary,
|
|
|
|
|
InterpreterContext *interpreter_context, DbAccessor *dba,
|
|
|
|
|
utils::MemoryResource *execution_memory, std::vector<Notification> *notifications,
|
|
|
|
|
const std::string *username,
|
|
|
|
|
TriggerContextCollector *trigger_context_collector = nullptr) {
|
|
|
|
|
auto *cypher_query = utils::Downcast<CypherQuery>(parsed_query.query);
|
|
|
|
|
|
|
|
|
|
@@ -1139,6 +1145,7 @@ PreparedQuery PrepareCypherQuery(ParsedQuery parsed_query, std::map<std::string,
|
|
|
|
|
EvaluationContext evaluation_context;
|
|
|
|
|
evaluation_context.timestamp = QueryTimestamp();
|
|
|
|
|
evaluation_context.parameters = parsed_query.parameters;
|
|
|
|
|
|
|
|
|
|
ExpressionEvaluator evaluator(&frame, symbol_table, evaluation_context, dba, storage::View::OLD);
|
|
|
|
|
const auto memory_limit = EvaluateMemoryLimit(&evaluator, cypher_query->memory_limit_, cypher_query->memory_scale_);
|
|
|
|
|
if (memory_limit) {
|
|
|
|
|
@@ -1174,8 +1181,9 @@ PreparedQuery PrepareCypherQuery(ParsedQuery parsed_query, std::map<std::string,
|
|
|
|
|
header.push_back(
|
|
|
|
|
utils::FindOr(parsed_query.stripped_query.named_expressions(), symbol.token_position(), symbol.name()).first);
|
|
|
|
|
}
|
|
|
|
|
auto pull_plan = std::make_shared<PullPlan>(plan, parsed_query.parameters, false, dba, interpreter_context,
|
|
|
|
|
execution_memory, trigger_context_collector, memory_limit);
|
|
|
|
|
auto pull_plan =
|
|
|
|
|
std::make_shared<PullPlan>(plan, parsed_query.parameters, false, dba, interpreter_context, execution_memory,
|
|
|
|
|
StringPointerToOptional(username), trigger_context_collector, memory_limit);
|
|
|
|
|
return PreparedQuery{std::move(header), std::move(parsed_query.required_privileges),
|
|
|
|
|
[pull_plan = std::move(pull_plan), output_symbols = std::move(output_symbols), summary](
|
|
|
|
|
AnyStream *stream, std::optional<int> n) -> std::optional<QueryHandlerResult> {
|
|
|
|
|
@@ -1235,7 +1243,8 @@ PreparedQuery PrepareExplainQuery(ParsedQuery parsed_query, std::map<std::string
|
|
|
|
|
|
|
|
|
|
PreparedQuery PrepareProfileQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
|
|
|
|
std::map<std::string, TypedValue> *summary, InterpreterContext *interpreter_context,
|
|
|
|
|
DbAccessor *dba, utils::MemoryResource *execution_memory) {
|
|
|
|
|
DbAccessor *dba, utils::MemoryResource *execution_memory,
|
|
|
|
|
const std::string *username) {
|
|
|
|
|
const std::string kProfileQueryStart = "profile ";
|
|
|
|
|
|
|
|
|
|
MG_ASSERT(utils::StartsWith(utils::ToLowerCase(parsed_query.stripped_query.query()), kProfileQueryStart),
|
|
|
|
|
@@ -1286,11 +1295,12 @@ PreparedQuery PrepareProfileQuery(ParsedQuery parsed_query, bool in_explicit_tra
|
|
|
|
|
parsed_inner_query.parameters, parsed_inner_query.is_cacheable ? &interpreter_context->plan_cache : nullptr, dba);
|
|
|
|
|
auto rw_type_checker = plan::ReadWriteTypeChecker();
|
|
|
|
|
rw_type_checker.InferRWType(const_cast<plan::LogicalOperator &>(cypher_query_plan->plan()));
|
|
|
|
|
auto optional_username = StringPointerToOptional(username);
|
|
|
|
|
|
|
|
|
|
return PreparedQuery{{"OPERATOR", "ACTUAL HITS", "RELATIVE TIME", "ABSOLUTE TIME"},
|
|
|
|
|
std::move(parsed_query.required_privileges),
|
|
|
|
|
[plan = std::move(cypher_query_plan), parameters = std::move(parsed_inner_query.parameters),
|
|
|
|
|
summary, dba, interpreter_context, execution_memory, memory_limit,
|
|
|
|
|
summary, dba, interpreter_context, execution_memory, memory_limit, optional_username,
|
|
|
|
|
// We want to execute the query we are profiling lazily, so we delay
|
|
|
|
|
// the construction of the corresponding context.
|
|
|
|
|
stats_and_total_time = std::optional<plan::ProfilingStatsWithTotalTime>{},
|
|
|
|
|
@@ -1299,7 +1309,7 @@ PreparedQuery PrepareProfileQuery(ParsedQuery parsed_query, bool in_explicit_tra
|
|
|
|
|
// No output symbols are given so that nothing is streamed.
|
|
|
|
|
if (!stats_and_total_time) {
|
|
|
|
|
stats_and_total_time = PullPlan(plan, parameters, true, dba, interpreter_context,
|
|
|
|
|
execution_memory, nullptr, memory_limit)
|
|
|
|
|
execution_memory, optional_username, nullptr, memory_limit)
|
|
|
|
|
.Pull(stream, {}, {}, summary);
|
|
|
|
|
pull_plan = std::make_shared<PullPlanVector>(ProfilingStatsToTable(*stats_and_total_time));
|
|
|
|
|
}
|
|
|
|
|
@@ -1434,7 +1444,7 @@ PreparedQuery PrepareIndexQuery(ParsedQuery parsed_query, bool in_explicit_trans
|
|
|
|
|
|
|
|
|
|
PreparedQuery PrepareAuthQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
|
|
|
|
std::map<std::string, TypedValue> *summary, InterpreterContext *interpreter_context,
|
|
|
|
|
DbAccessor *dba, utils::MemoryResource *execution_memory) {
|
|
|
|
|
DbAccessor *dba, utils::MemoryResource *execution_memory, const std::string *username) {
|
|
|
|
|
if (in_explicit_transaction) {
|
|
|
|
|
throw UserModificationInMulticommandTxException();
|
|
|
|
|
}
|
|
|
|
|
@@ -1454,8 +1464,8 @@ PreparedQuery PrepareAuthQuery(ParsedQuery parsed_query, bool in_explicit_transa
|
|
|
|
|
[fn = callback.fn](Frame *, ExecutionContext *) { return fn(); }),
|
|
|
|
|
0.0, AstStorage{}, symbol_table));
|
|
|
|
|
|
|
|
|
|
auto pull_plan =
|
|
|
|
|
std::make_shared<PullPlan>(plan, parsed_query.parameters, false, dba, interpreter_context, execution_memory);
|
|
|
|
|
auto pull_plan = std::make_shared<PullPlan>(plan, parsed_query.parameters, false, dba, interpreter_context,
|
|
|
|
|
execution_memory, StringPointerToOptional(username));
|
|
|
|
|
return PreparedQuery{
|
|
|
|
|
callback.header, std::move(parsed_query.required_privileges),
|
|
|
|
|
[pull_plan = std::move(pull_plan), callback = std::move(callback), output_symbols = std::move(output_symbols),
|
|
|
|
|
@@ -2168,7 +2178,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
|
|
|
|
if (utils::Downcast<CypherQuery>(parsed_query.query)) {
|
|
|
|
|
prepared_query = PrepareCypherQuery(std::move(parsed_query), &query_execution->summary, interpreter_context_,
|
|
|
|
|
&*execution_db_accessor_, &query_execution->execution_memory,
|
|
|
|
|
&query_execution->notifications,
|
|
|
|
|
&query_execution->notifications, username,
|
|
|
|
|
trigger_context_collector_ ? &*trigger_context_collector_ : nullptr);
|
|
|
|
|
} else if (utils::Downcast<ExplainQuery>(parsed_query.query)) {
|
|
|
|
|
prepared_query = PrepareExplainQuery(std::move(parsed_query), &query_execution->summary, interpreter_context_,
|
|
|
|
|
@@ -2176,7 +2186,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
|
|
|
|
} else if (utils::Downcast<ProfileQuery>(parsed_query.query)) {
|
|
|
|
|
prepared_query = PrepareProfileQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->summary,
|
|
|
|
|
interpreter_context_, &*execution_db_accessor_,
|
|
|
|
|
&query_execution->execution_memory_with_exception);
|
|
|
|
|
&query_execution->execution_memory_with_exception, username);
|
|
|
|
|
} else if (utils::Downcast<DumpQuery>(parsed_query.query)) {
|
|
|
|
|
prepared_query = PrepareDumpQuery(std::move(parsed_query), &query_execution->summary, &*execution_db_accessor_,
|
|
|
|
|
&query_execution->execution_memory);
|
|
|
|
|
@@ -2186,7 +2196,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
|
|
|
|
} else if (utils::Downcast<AuthQuery>(parsed_query.query)) {
|
|
|
|
|
prepared_query = PrepareAuthQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->summary,
|
|
|
|
|
interpreter_context_, &*execution_db_accessor_,
|
|
|
|
|
&query_execution->execution_memory_with_exception);
|
|
|
|
|
&query_execution->execution_memory_with_exception, username);
|
|
|
|
|
} else if (utils::Downcast<InfoQuery>(parsed_query.query)) {
|
|
|
|
|
prepared_query = PrepareInfoQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->summary,
|
|
|
|
|
interpreter_context_, interpreter_context_->db,
|
|
|
|
|
|