mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-30 19:42:00 +00:00
Add DCL to manage row policies.
This commit is contained in:
parent
6baccb963d
commit
c3161b7610
@ -627,6 +627,13 @@ void Context::checkQuotaManagementIsAllowed()
|
|||||||
"User " + client_info.current_user + " doesn't have enough privileges to manage quotas", ErrorCodes::NOT_ENOUGH_PRIVILEGES);
|
"User " + client_info.current_user + " doesn't have enough privileges to manage quotas", ErrorCodes::NOT_ENOUGH_PRIVILEGES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Context::checkRowPolicyManagementIsAllowed()
|
||||||
|
{
|
||||||
|
if (!is_row_policy_management_allowed)
|
||||||
|
throw Exception(
|
||||||
|
"User " + client_info.current_user + " doesn't have enough privileges to manage row policies", ErrorCodes::NOT_ENOUGH_PRIVILEGES);
|
||||||
|
}
|
||||||
|
|
||||||
void Context::setUsersConfig(const ConfigurationPtr & config)
|
void Context::setUsersConfig(const ConfigurationPtr & config)
|
||||||
{
|
{
|
||||||
auto lock = getLock();
|
auto lock = getLock();
|
||||||
@ -666,6 +673,7 @@ void Context::calculateUserSettings()
|
|||||||
client_info.current_user, client_info.current_address.host(), client_info.quota_key);
|
client_info.current_user, client_info.current_address.host(), client_info.quota_key);
|
||||||
is_quota_management_allowed = user->is_quota_management_allowed;
|
is_quota_management_allowed = user->is_quota_management_allowed;
|
||||||
row_policy = getAccessControlManager().getRowPolicyContext(client_info.current_user);
|
row_policy = getAccessControlManager().getRowPolicyContext(client_info.current_user);
|
||||||
|
is_row_policy_management_allowed = user->is_row_policy_management_allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,6 +142,7 @@ private:
|
|||||||
std::shared_ptr<QuotaContext> quota; /// Current quota. By default - empty quota, that have no limits.
|
std::shared_ptr<QuotaContext> quota; /// Current quota. By default - empty quota, that have no limits.
|
||||||
bool is_quota_management_allowed = false; /// Whether the current user is allowed to manage quotas via SQL commands.
|
bool is_quota_management_allowed = false; /// Whether the current user is allowed to manage quotas via SQL commands.
|
||||||
std::shared_ptr<RowPolicyContext> row_policy;
|
std::shared_ptr<RowPolicyContext> row_policy;
|
||||||
|
bool is_row_policy_management_allowed = false; /// Whether the current user is allowed to manage row policies via SQL commands.
|
||||||
String current_database;
|
String current_database;
|
||||||
Settings settings; /// Setting for query execution.
|
Settings settings; /// Setting for query execution.
|
||||||
std::shared_ptr<const SettingsConstraints> settings_constraints;
|
std::shared_ptr<const SettingsConstraints> settings_constraints;
|
||||||
@ -212,6 +213,7 @@ public:
|
|||||||
std::shared_ptr<QuotaContext> getQuota() const { return quota; }
|
std::shared_ptr<QuotaContext> getQuota() const { return quota; }
|
||||||
void checkQuotaManagementIsAllowed();
|
void checkQuotaManagementIsAllowed();
|
||||||
std::shared_ptr<RowPolicyContext> getRowPolicy() const { return row_policy; }
|
std::shared_ptr<RowPolicyContext> getRowPolicy() const { return row_policy; }
|
||||||
|
void checkRowPolicyManagementIsAllowed();
|
||||||
|
|
||||||
/** Take the list of users, quotas and configuration profiles from this config.
|
/** Take the list of users, quotas and configuration profiles from this config.
|
||||||
* The list of users is completely replaced.
|
* The list of users is completely replaced.
|
||||||
|
93
dbms/src/Interpreters/InterpreterCreateRowPolicyQuery.cpp
Normal file
93
dbms/src/Interpreters/InterpreterCreateRowPolicyQuery.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#include <Interpreters/InterpreterCreateRowPolicyQuery.h>
|
||||||
|
#include <Parsers/ASTCreateRowPolicyQuery.h>
|
||||||
|
#include <Parsers/ASTRoleList.h>
|
||||||
|
#include <Parsers/formatAST.h>
|
||||||
|
#include <Interpreters/Context.h>
|
||||||
|
#include <Access/AccessControlManager.h>
|
||||||
|
#include <boost/range/algorithm/sort.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
BlockIO InterpreterCreateRowPolicyQuery::execute()
|
||||||
|
{
|
||||||
|
context.checkRowPolicyManagementIsAllowed();
|
||||||
|
const auto & query = query_ptr->as<const ASTCreateRowPolicyQuery &>();
|
||||||
|
auto & access_control = context.getAccessControlManager();
|
||||||
|
|
||||||
|
if (query.alter)
|
||||||
|
{
|
||||||
|
auto update_func = [&](const AccessEntityPtr & entity) -> AccessEntityPtr
|
||||||
|
{
|
||||||
|
auto updated_policy = typeid_cast<std::shared_ptr<RowPolicy>>(entity->clone());
|
||||||
|
updateRowPolicyFromQuery(*updated_policy, query);
|
||||||
|
return updated_policy;
|
||||||
|
};
|
||||||
|
String full_name = query.name_parts.getFullName(context);
|
||||||
|
if (query.if_exists)
|
||||||
|
{
|
||||||
|
if (auto id = access_control.find<RowPolicy>(full_name))
|
||||||
|
access_control.tryUpdate(*id, update_func);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
access_control.update(access_control.getID<RowPolicy>(full_name), update_func);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto new_policy = std::make_shared<RowPolicy>();
|
||||||
|
updateRowPolicyFromQuery(*new_policy, query);
|
||||||
|
|
||||||
|
if (query.if_not_exists)
|
||||||
|
access_control.tryInsert(new_policy);
|
||||||
|
else if (query.or_replace)
|
||||||
|
access_control.insertOrReplace(new_policy);
|
||||||
|
else
|
||||||
|
access_control.insert(new_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InterpreterCreateRowPolicyQuery::updateRowPolicyFromQuery(RowPolicy & policy, const ASTCreateRowPolicyQuery & query)
|
||||||
|
{
|
||||||
|
if (query.alter)
|
||||||
|
{
|
||||||
|
if (!query.new_policy_name.empty())
|
||||||
|
policy.setName(query.new_policy_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
policy.setDatabase(query.name_parts.database.empty() ? context.getCurrentDatabase() : query.name_parts.database);
|
||||||
|
policy.setTableName(query.name_parts.table_name);
|
||||||
|
policy.setName(query.name_parts.policy_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.is_restrictive)
|
||||||
|
policy.setRestrictive(*query.is_restrictive);
|
||||||
|
|
||||||
|
for (const auto & [index, condition] : query.conditions)
|
||||||
|
policy.conditions[index] = condition ? serializeAST(*condition) : String{};
|
||||||
|
|
||||||
|
if (query.roles)
|
||||||
|
{
|
||||||
|
const auto & query_roles = *query.roles;
|
||||||
|
|
||||||
|
/// We keep `roles` sorted.
|
||||||
|
policy.roles = query_roles.roles;
|
||||||
|
if (query_roles.current_user)
|
||||||
|
policy.roles.push_back(context.getClientInfo().current_user);
|
||||||
|
boost::range::sort(policy.roles);
|
||||||
|
policy.roles.erase(std::unique(policy.roles.begin(), policy.roles.end()), policy.roles.end());
|
||||||
|
|
||||||
|
policy.all_roles = query_roles.all_roles;
|
||||||
|
|
||||||
|
/// We keep `except_roles` sorted.
|
||||||
|
policy.except_roles = query_roles.except_roles;
|
||||||
|
if (query_roles.except_current_user)
|
||||||
|
policy.except_roles.push_back(context.getClientInfo().current_user);
|
||||||
|
boost::range::sort(policy.except_roles);
|
||||||
|
policy.except_roles.erase(std::unique(policy.except_roles.begin(), policy.except_roles.end()), policy.except_roles.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
dbms/src/Interpreters/InterpreterCreateRowPolicyQuery.h
Normal file
26
dbms/src/Interpreters/InterpreterCreateRowPolicyQuery.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Interpreters/IInterpreter.h>
|
||||||
|
#include <Parsers/IAST_fwd.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
class ASTCreateRowPolicyQuery;
|
||||||
|
struct RowPolicy;
|
||||||
|
|
||||||
|
|
||||||
|
class InterpreterCreateRowPolicyQuery : public IInterpreter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InterpreterCreateRowPolicyQuery(const ASTPtr & query_ptr_, Context & context_) : query_ptr(query_ptr_), context(context_) {}
|
||||||
|
|
||||||
|
BlockIO execute() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateRowPolicyFromQuery(RowPolicy & policy, const ASTCreateRowPolicyQuery & query);
|
||||||
|
|
||||||
|
ASTPtr query_ptr;
|
||||||
|
Context & context;
|
||||||
|
};
|
||||||
|
}
|
@ -3,6 +3,8 @@
|
|||||||
#include <Interpreters/Context.h>
|
#include <Interpreters/Context.h>
|
||||||
#include <Access/AccessControlManager.h>
|
#include <Access/AccessControlManager.h>
|
||||||
#include <Access/Quota.h>
|
#include <Access/Quota.h>
|
||||||
|
#include <Access/RowPolicy.h>
|
||||||
|
#include <boost/range/algorithm/transform.hpp>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
@ -24,6 +26,19 @@ BlockIO InterpreterDropAccessEntityQuery::execute()
|
|||||||
access_control.remove(access_control.getIDs<Quota>(query.names));
|
access_control.remove(access_control.getIDs<Quota>(query.names));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
case Kind::ROW_POLICY:
|
||||||
|
{
|
||||||
|
context.checkRowPolicyManagementIsAllowed();
|
||||||
|
Strings full_names;
|
||||||
|
boost::range::transform(
|
||||||
|
query.row_policies_names, std::back_inserter(full_names),
|
||||||
|
[this](const RowPolicy::FullNameParts & row_policy_name) { return row_policy_name.getFullName(context); });
|
||||||
|
if (query.if_exists)
|
||||||
|
access_control.tryRemove(access_control.find<RowPolicy>(full_names));
|
||||||
|
else
|
||||||
|
access_control.remove(access_control.getIDs<RowPolicy>(full_names));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include <Parsers/ASTCheckQuery.h>
|
#include <Parsers/ASTCheckQuery.h>
|
||||||
#include <Parsers/ASTCreateQuery.h>
|
#include <Parsers/ASTCreateQuery.h>
|
||||||
#include <Parsers/ASTCreateQuotaQuery.h>
|
#include <Parsers/ASTCreateQuotaQuery.h>
|
||||||
|
#include <Parsers/ASTCreateRowPolicyQuery.h>
|
||||||
#include <Parsers/ASTDropAccessEntityQuery.h>
|
#include <Parsers/ASTDropAccessEntityQuery.h>
|
||||||
#include <Parsers/ASTDropQuery.h>
|
#include <Parsers/ASTDropQuery.h>
|
||||||
#include <Parsers/ASTInsertQuery.h>
|
#include <Parsers/ASTInsertQuery.h>
|
||||||
@ -14,6 +15,7 @@
|
|||||||
#include <Parsers/ASTShowCreateAccessEntityQuery.h>
|
#include <Parsers/ASTShowCreateAccessEntityQuery.h>
|
||||||
#include <Parsers/ASTShowProcesslistQuery.h>
|
#include <Parsers/ASTShowProcesslistQuery.h>
|
||||||
#include <Parsers/ASTShowQuotasQuery.h>
|
#include <Parsers/ASTShowQuotasQuery.h>
|
||||||
|
#include <Parsers/ASTShowRowPoliciesQuery.h>
|
||||||
#include <Parsers/ASTShowTablesQuery.h>
|
#include <Parsers/ASTShowTablesQuery.h>
|
||||||
#include <Parsers/ASTUseQuery.h>
|
#include <Parsers/ASTUseQuery.h>
|
||||||
#include <Parsers/ASTExplainQuery.h>
|
#include <Parsers/ASTExplainQuery.h>
|
||||||
@ -24,6 +26,7 @@
|
|||||||
#include <Interpreters/InterpreterCheckQuery.h>
|
#include <Interpreters/InterpreterCheckQuery.h>
|
||||||
#include <Interpreters/InterpreterCreateQuery.h>
|
#include <Interpreters/InterpreterCreateQuery.h>
|
||||||
#include <Interpreters/InterpreterCreateQuotaQuery.h>
|
#include <Interpreters/InterpreterCreateQuotaQuery.h>
|
||||||
|
#include <Interpreters/InterpreterCreateRowPolicyQuery.h>
|
||||||
#include <Interpreters/InterpreterDescribeQuery.h>
|
#include <Interpreters/InterpreterDescribeQuery.h>
|
||||||
#include <Interpreters/InterpreterExplainQuery.h>
|
#include <Interpreters/InterpreterExplainQuery.h>
|
||||||
#include <Interpreters/InterpreterDropAccessEntityQuery.h>
|
#include <Interpreters/InterpreterDropAccessEntityQuery.h>
|
||||||
@ -41,6 +44,7 @@
|
|||||||
#include <Interpreters/InterpreterShowCreateQuery.h>
|
#include <Interpreters/InterpreterShowCreateQuery.h>
|
||||||
#include <Interpreters/InterpreterShowProcesslistQuery.h>
|
#include <Interpreters/InterpreterShowProcesslistQuery.h>
|
||||||
#include <Interpreters/InterpreterShowQuotasQuery.h>
|
#include <Interpreters/InterpreterShowQuotasQuery.h>
|
||||||
|
#include <Interpreters/InterpreterShowRowPoliciesQuery.h>
|
||||||
#include <Interpreters/InterpreterShowTablesQuery.h>
|
#include <Interpreters/InterpreterShowTablesQuery.h>
|
||||||
#include <Interpreters/InterpreterSystemQuery.h>
|
#include <Interpreters/InterpreterSystemQuery.h>
|
||||||
#include <Interpreters/InterpreterUseQuery.h>
|
#include <Interpreters/InterpreterUseQuery.h>
|
||||||
@ -199,6 +203,10 @@ std::unique_ptr<IInterpreter> InterpreterFactory::get(ASTPtr & query, Context &
|
|||||||
{
|
{
|
||||||
return std::make_unique<InterpreterCreateQuotaQuery>(query, context);
|
return std::make_unique<InterpreterCreateQuotaQuery>(query, context);
|
||||||
}
|
}
|
||||||
|
else if (query->as<ASTCreateRowPolicyQuery>())
|
||||||
|
{
|
||||||
|
return std::make_unique<InterpreterCreateRowPolicyQuery>(query, context);
|
||||||
|
}
|
||||||
else if (query->as<ASTDropAccessEntityQuery>())
|
else if (query->as<ASTDropAccessEntityQuery>())
|
||||||
{
|
{
|
||||||
return std::make_unique<InterpreterDropAccessEntityQuery>(query, context);
|
return std::make_unique<InterpreterDropAccessEntityQuery>(query, context);
|
||||||
@ -211,6 +219,10 @@ std::unique_ptr<IInterpreter> InterpreterFactory::get(ASTPtr & query, Context &
|
|||||||
{
|
{
|
||||||
return std::make_unique<InterpreterShowQuotasQuery>(query, context);
|
return std::make_unique<InterpreterShowQuotasQuery>(query, context);
|
||||||
}
|
}
|
||||||
|
else if (query->as<ASTShowRowPoliciesQuery>())
|
||||||
|
{
|
||||||
|
return std::make_unique<InterpreterShowRowPoliciesQuery>(query, context);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
throw Exception("Unknown type of query: " + query->getID(), ErrorCodes::UNKNOWN_TYPE_OF_QUERY);
|
throw Exception("Unknown type of query: " + query->getID(), ErrorCodes::UNKNOWN_TYPE_OF_QUERY);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
#include <Interpreters/InterpreterShowCreateAccessEntityQuery.h>
|
#include <Interpreters/InterpreterShowCreateAccessEntityQuery.h>
|
||||||
#include <Interpreters/Context.h>
|
#include <Interpreters/Context.h>
|
||||||
#include <Parsers/ASTCreateQuotaQuery.h>
|
#include <Parsers/ASTCreateQuotaQuery.h>
|
||||||
|
#include <Parsers/ASTCreateRowPolicyQuery.h>
|
||||||
#include <Parsers/ASTShowCreateAccessEntityQuery.h>
|
#include <Parsers/ASTShowCreateAccessEntityQuery.h>
|
||||||
#include <Parsers/ASTRoleList.h>
|
#include <Parsers/ASTRoleList.h>
|
||||||
|
#include <Parsers/ExpressionListParsers.h>
|
||||||
#include <Parsers/formatAST.h>
|
#include <Parsers/formatAST.h>
|
||||||
|
#include <Parsers/parseQuery.h>
|
||||||
#include <Access/AccessControlManager.h>
|
#include <Access/AccessControlManager.h>
|
||||||
#include <Access/QuotaContext.h>
|
#include <Access/QuotaContext.h>
|
||||||
#include <Columns/ColumnString.h>
|
#include <Columns/ColumnString.h>
|
||||||
@ -28,7 +31,7 @@ BlockInputStreamPtr InterpreterShowCreateAccessEntityQuery::executeImpl()
|
|||||||
const auto & show_query = query_ptr->as<ASTShowCreateAccessEntityQuery &>();
|
const auto & show_query = query_ptr->as<ASTShowCreateAccessEntityQuery &>();
|
||||||
|
|
||||||
/// Build a create query.
|
/// Build a create query.
|
||||||
ASTPtr create_query = getCreateQuotaQuery(show_query);
|
ASTPtr create_query = getCreateQuery(show_query);
|
||||||
|
|
||||||
/// Build the result column.
|
/// Build the result column.
|
||||||
std::stringstream create_query_ss;
|
std::stringstream create_query_ss;
|
||||||
@ -49,6 +52,18 @@ BlockInputStreamPtr InterpreterShowCreateAccessEntityQuery::executeImpl()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ASTPtr InterpreterShowCreateAccessEntityQuery::getCreateQuery(const ASTShowCreateAccessEntityQuery & show_query) const
|
||||||
|
{
|
||||||
|
using Kind = ASTShowCreateAccessEntityQuery::Kind;
|
||||||
|
switch (show_query.kind)
|
||||||
|
{
|
||||||
|
case Kind::QUOTA: return getCreateQuotaQuery(show_query);
|
||||||
|
case Kind::ROW_POLICY: return getCreateRowPolicyQuery(show_query);
|
||||||
|
}
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ASTPtr InterpreterShowCreateAccessEntityQuery::getCreateQuotaQuery(const ASTShowCreateAccessEntityQuery & show_query) const
|
ASTPtr InterpreterShowCreateAccessEntityQuery::getCreateQuotaQuery(const ASTShowCreateAccessEntityQuery & show_query) const
|
||||||
{
|
{
|
||||||
auto & access_control = context.getAccessControlManager();
|
auto & access_control = context.getAccessControlManager();
|
||||||
@ -86,4 +101,38 @@ ASTPtr InterpreterShowCreateAccessEntityQuery::getCreateQuotaQuery(const ASTShow
|
|||||||
|
|
||||||
return create_query;
|
return create_query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ASTPtr InterpreterShowCreateAccessEntityQuery::getCreateRowPolicyQuery(const ASTShowCreateAccessEntityQuery & show_query) const
|
||||||
|
{
|
||||||
|
auto & access_control = context.getAccessControlManager();
|
||||||
|
RowPolicyPtr policy = access_control.read<RowPolicy>(show_query.row_policy_name.getFullName(context));
|
||||||
|
|
||||||
|
auto create_query = std::make_shared<ASTCreateRowPolicyQuery>();
|
||||||
|
create_query->name_parts = RowPolicy::FullNameParts{policy->getDatabase(), policy->getTableName(), policy->getName()};
|
||||||
|
if (policy->isRestrictive())
|
||||||
|
create_query->is_restrictive = policy->isRestrictive();
|
||||||
|
|
||||||
|
for (auto index : ext::range_with_static_cast<RowPolicy::ConditionIndex>(RowPolicy::MAX_CONDITION_INDEX))
|
||||||
|
{
|
||||||
|
const auto & condition = policy->conditions[index];
|
||||||
|
if (!condition.empty())
|
||||||
|
{
|
||||||
|
ParserExpression parser;
|
||||||
|
ASTPtr expr = parseQuery(parser, condition, 0);
|
||||||
|
create_query->conditions.push_back(std::pair{index, expr});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!policy->roles.empty() || policy->all_roles)
|
||||||
|
{
|
||||||
|
auto create_query_roles = std::make_shared<ASTRoleList>();
|
||||||
|
create_query_roles->roles = policy->roles;
|
||||||
|
create_query_roles->all_roles = policy->all_roles;
|
||||||
|
create_query_roles->except_roles = policy->except_roles;
|
||||||
|
create_query->roles = std::move(create_query_roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
return create_query;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,9 @@ private:
|
|||||||
const Context & context;
|
const Context & context;
|
||||||
|
|
||||||
BlockInputStreamPtr executeImpl();
|
BlockInputStreamPtr executeImpl();
|
||||||
|
ASTPtr getCreateQuery(const ASTShowCreateAccessEntityQuery & show_query) const;
|
||||||
ASTPtr getCreateQuotaQuery(const ASTShowCreateAccessEntityQuery & show_query) const;
|
ASTPtr getCreateQuotaQuery(const ASTShowCreateAccessEntityQuery & show_query) const;
|
||||||
|
ASTPtr getCreateRowPolicyQuery(const ASTShowCreateAccessEntityQuery & show_query) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
68
dbms/src/Interpreters/InterpreterShowRowPoliciesQuery.cpp
Normal file
68
dbms/src/Interpreters/InterpreterShowRowPoliciesQuery.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#include <Interpreters/InterpreterShowRowPoliciesQuery.h>
|
||||||
|
#include <Parsers/ASTShowRowPoliciesQuery.h>
|
||||||
|
#include <Parsers/formatAST.h>
|
||||||
|
#include <Interpreters/executeQuery.h>
|
||||||
|
#include <Common/quoteString.h>
|
||||||
|
#include <Interpreters/Context.h>
|
||||||
|
#include <ext/range.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
InterpreterShowRowPoliciesQuery::InterpreterShowRowPoliciesQuery(const ASTPtr & query_ptr_, Context & context_)
|
||||||
|
: query_ptr(query_ptr_), context(context_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BlockIO InterpreterShowRowPoliciesQuery::execute()
|
||||||
|
{
|
||||||
|
return executeQuery(getRewrittenQuery(), context, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String InterpreterShowRowPoliciesQuery::getRewrittenQuery() const
|
||||||
|
{
|
||||||
|
const auto & query = query_ptr->as<ASTShowRowPoliciesQuery &>();
|
||||||
|
|
||||||
|
const String & table_name = query.table_name;
|
||||||
|
String database;
|
||||||
|
if (!table_name.empty())
|
||||||
|
{
|
||||||
|
database = query.database;
|
||||||
|
if (database.empty())
|
||||||
|
database = context.getCurrentDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
String filter;
|
||||||
|
if (query.current)
|
||||||
|
{
|
||||||
|
if (table_name.empty())
|
||||||
|
filter = "has(currentRowPolicyIDs(), id)";
|
||||||
|
else
|
||||||
|
filter = "has(currentRowPolicyIDs(" + quoteString(database) + ", " + quoteString(table_name) + "), id)";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!table_name.empty())
|
||||||
|
filter = "database = " + quoteString(database) + " AND table = " + quoteString(table_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
String expr = table_name.empty() ? "full_name" : "name";
|
||||||
|
|
||||||
|
return "SELECT " + expr + " AS " + backQuote(getResultDescription()) + " from system.row_policies"
|
||||||
|
+ (filter.empty() ? "" : " WHERE " + filter) + " ORDER BY " + expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String InterpreterShowRowPoliciesQuery::getResultDescription() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
formatAST(*query_ptr, ss, false, true);
|
||||||
|
String desc = ss.str();
|
||||||
|
String prefix = "SHOW ";
|
||||||
|
if (startsWith(desc, prefix))
|
||||||
|
desc = desc.substr(prefix.length()); /// `desc` always starts with "SHOW ", so we can trim this prefix.
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
}
|
25
dbms/src/Interpreters/InterpreterShowRowPoliciesQuery.h
Normal file
25
dbms/src/Interpreters/InterpreterShowRowPoliciesQuery.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Interpreters/IInterpreter.h>
|
||||||
|
#include <Parsers/IAST_fwd.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
class Context;
|
||||||
|
|
||||||
|
class InterpreterShowRowPoliciesQuery : public IInterpreter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InterpreterShowRowPoliciesQuery(const ASTPtr & query_ptr_, Context & context_);
|
||||||
|
BlockIO execute() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
String getRewrittenQuery() const;
|
||||||
|
String getResultDescription() const;
|
||||||
|
|
||||||
|
ASTPtr query_ptr;
|
||||||
|
Context & context;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -105,6 +105,8 @@ User::User(const String & name_, const String & config_elem, const Poco::Util::A
|
|||||||
|
|
||||||
if (config.has(config_elem + ".allow_quota_management"))
|
if (config.has(config_elem + ".allow_quota_management"))
|
||||||
is_quota_management_allowed = config.getBool(config_elem + ".allow_quota_management");
|
is_quota_management_allowed = config.getBool(config_elem + ".allow_quota_management");
|
||||||
|
if (config.has(config_elem + ".allow_row_policy_management"))
|
||||||
|
is_row_policy_management_allowed = config.getBool(config_elem + ".allow_row_policy_management");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ struct User
|
|||||||
std::optional<DictionarySet> dictionaries;
|
std::optional<DictionarySet> dictionaries;
|
||||||
|
|
||||||
bool is_quota_management_allowed = false;
|
bool is_quota_management_allowed = false;
|
||||||
|
bool is_row_policy_management_allowed = false;
|
||||||
|
|
||||||
User(const String & name_, const String & config_elem, const Poco::Util::AbstractConfiguration & config);
|
User(const String & name_, const String & config_elem, const Poco::Util::AbstractConfiguration & config);
|
||||||
};
|
};
|
||||||
|
164
dbms/src/Parsers/ASTCreateRowPolicyQuery.cpp
Normal file
164
dbms/src/Parsers/ASTCreateRowPolicyQuery.cpp
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
#include <Parsers/ASTCreateRowPolicyQuery.h>
|
||||||
|
#include <Parsers/ASTRoleList.h>
|
||||||
|
#include <Parsers/formatAST.h>
|
||||||
|
#include <Common/quoteString.h>
|
||||||
|
#include <boost/range/algorithm/transform.hpp>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using ConditionIndex = RowPolicy::ConditionIndex;
|
||||||
|
|
||||||
|
void formatRenameTo(const String & new_policy_name, const IAST::FormatSettings & settings)
|
||||||
|
{
|
||||||
|
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " RENAME TO " << (settings.hilite ? IAST::hilite_none : "")
|
||||||
|
<< backQuote(new_policy_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void formatIsRestrictive(bool is_restrictive, const IAST::FormatSettings & settings)
|
||||||
|
{
|
||||||
|
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " AS " << (is_restrictive ? "RESTRICTIVE" : "PERMISSIVE")
|
||||||
|
<< (settings.hilite ? IAST::hilite_none : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void formatConditionalExpression(const ASTPtr & expr, const IAST::FormatSettings & settings)
|
||||||
|
{
|
||||||
|
if (!expr)
|
||||||
|
{
|
||||||
|
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " NONE" << (settings.hilite ? IAST::hilite_none : "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
expr->format(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<std::pair<ConditionIndex, String>>
|
||||||
|
conditionalExpressionsToStrings(const std::vector<std::pair<ConditionIndex, ASTPtr>> & exprs, const IAST::FormatSettings & settings)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<ConditionIndex, String>> result;
|
||||||
|
std::stringstream ss;
|
||||||
|
IAST::FormatSettings temp_settings(ss, settings);
|
||||||
|
boost::range::transform(exprs, std::back_inserter(result), [&](const std::pair<ConditionIndex, ASTPtr> & in)
|
||||||
|
{
|
||||||
|
formatConditionalExpression(in.second, temp_settings);
|
||||||
|
auto out = std::pair{in.first, ss.str()};
|
||||||
|
ss.str("");
|
||||||
|
return out;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void formatConditions(const char * op, const std::optional<String> & filter, const std::optional<String> & check, bool alter, const IAST::FormatSettings & settings)
|
||||||
|
{
|
||||||
|
if (op)
|
||||||
|
{
|
||||||
|
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " FOR" << (settings.hilite ? IAST::hilite_none : "");
|
||||||
|
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << ' ' << op << (settings.hilite ? IAST::hilite_none : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter)
|
||||||
|
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " USING " << (settings.hilite ? IAST::hilite_none : "") << *filter;
|
||||||
|
|
||||||
|
if (check && (alter || (check != filter)))
|
||||||
|
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " WITH CHECK " << (settings.hilite ? IAST::hilite_none : "") << *check;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void formatMultipleConditions(const std::vector<std::pair<ConditionIndex, ASTPtr>> & conditions, bool alter, const IAST::FormatSettings & settings)
|
||||||
|
{
|
||||||
|
std::optional<String> scond[RowPolicy::MAX_CONDITION_INDEX];
|
||||||
|
for (const auto & [index, scondition] : conditionalExpressionsToStrings(conditions, settings))
|
||||||
|
scond[index] = scondition;
|
||||||
|
|
||||||
|
if ((scond[RowPolicy::SELECT_FILTER] == scond[RowPolicy::UPDATE_FILTER])
|
||||||
|
&& (scond[RowPolicy::UPDATE_FILTER] == scond[RowPolicy::DELETE_FILTER])
|
||||||
|
&& (scond[RowPolicy::INSERT_CHECK] == scond[RowPolicy::UPDATE_CHECK])
|
||||||
|
&& (scond[RowPolicy::SELECT_FILTER] || scond[RowPolicy::INSERT_CHECK]))
|
||||||
|
{
|
||||||
|
formatConditions(nullptr, scond[RowPolicy::SELECT_FILTER], scond[RowPolicy::INSERT_CHECK], alter, settings);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool need_comma = false;
|
||||||
|
if (scond[RowPolicy::SELECT_FILTER])
|
||||||
|
{
|
||||||
|
if (std::exchange(need_comma, true))
|
||||||
|
settings.ostr << ',';
|
||||||
|
formatConditions("SELECT", scond[RowPolicy::SELECT_FILTER], {}, alter, settings);
|
||||||
|
}
|
||||||
|
if (scond[RowPolicy::INSERT_CHECK])
|
||||||
|
{
|
||||||
|
if (std::exchange(need_comma, true))
|
||||||
|
settings.ostr << ',';
|
||||||
|
formatConditions("INSERT", {}, scond[RowPolicy::INSERT_CHECK], alter, settings);
|
||||||
|
}
|
||||||
|
if (scond[RowPolicy::UPDATE_FILTER] || scond[RowPolicy::UPDATE_CHECK])
|
||||||
|
{
|
||||||
|
if (std::exchange(need_comma, true))
|
||||||
|
settings.ostr << ',';
|
||||||
|
formatConditions("UPDATE", scond[RowPolicy::UPDATE_FILTER], scond[RowPolicy::UPDATE_CHECK], alter, settings);
|
||||||
|
}
|
||||||
|
if (scond[RowPolicy::DELETE_FILTER])
|
||||||
|
{
|
||||||
|
if (std::exchange(need_comma, true))
|
||||||
|
settings.ostr << ',';
|
||||||
|
formatConditions("DELETE", scond[RowPolicy::DELETE_FILTER], {}, alter, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void formatRoles(const ASTRoleList & roles, const IAST::FormatSettings & settings)
|
||||||
|
{
|
||||||
|
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " TO " << (settings.hilite ? IAST::hilite_none : "");
|
||||||
|
roles.format(settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String ASTCreateRowPolicyQuery::getID(char) const
|
||||||
|
{
|
||||||
|
return "CREATE POLICY or ALTER POLICY query";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ASTPtr ASTCreateRowPolicyQuery::clone() const
|
||||||
|
{
|
||||||
|
return std::make_shared<ASTCreateRowPolicyQuery>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ASTCreateRowPolicyQuery::formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const
|
||||||
|
{
|
||||||
|
settings.ostr << (settings.hilite ? hilite_keyword : "") << (alter ? "ALTER POLICY" : "CREATE POLICY")
|
||||||
|
<< (settings.hilite ? hilite_none : "");
|
||||||
|
|
||||||
|
if (if_exists)
|
||||||
|
settings.ostr << (settings.hilite ? hilite_keyword : "") << " IF EXISTS" << (settings.hilite ? hilite_none : "");
|
||||||
|
else if (if_not_exists)
|
||||||
|
settings.ostr << (settings.hilite ? hilite_keyword : "") << " IF NOT EXISTS" << (settings.hilite ? hilite_none : "");
|
||||||
|
else if (or_replace)
|
||||||
|
settings.ostr << (settings.hilite ? hilite_keyword : "") << " OR REPLACE" << (settings.hilite ? hilite_none : "");
|
||||||
|
|
||||||
|
const String & database = name_parts.database;
|
||||||
|
const String & table_name = name_parts.table_name;
|
||||||
|
const String & policy_name = name_parts.policy_name;
|
||||||
|
settings.ostr << " " << backQuoteIfNeed(policy_name) << (settings.hilite ? hilite_keyword : "") << " ON "
|
||||||
|
<< (settings.hilite ? hilite_none : "") << (database.empty() ? String{} : backQuoteIfNeed(database) + ".") << table_name;
|
||||||
|
|
||||||
|
if (!new_policy_name.empty())
|
||||||
|
formatRenameTo(new_policy_name, settings);
|
||||||
|
|
||||||
|
if (is_restrictive)
|
||||||
|
formatIsRestrictive(*is_restrictive, settings);
|
||||||
|
|
||||||
|
formatMultipleConditions(conditions, alter, settings);
|
||||||
|
|
||||||
|
if (roles)
|
||||||
|
formatRoles(*roles, settings);
|
||||||
|
}
|
||||||
|
}
|
50
dbms/src/Parsers/ASTCreateRowPolicyQuery.h
Normal file
50
dbms/src/Parsers/ASTCreateRowPolicyQuery.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Parsers/IAST.h>
|
||||||
|
#include <Access/RowPolicy.h>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
class ASTRoleList;
|
||||||
|
|
||||||
|
/** CREATE [ROW] POLICY [IF NOT EXISTS | OR REPLACE] name ON [database.]table
|
||||||
|
* [AS {PERMISSIVE | RESTRICTIVE}]
|
||||||
|
* [FOR {SELECT | INSERT | UPDATE | DELETE | ALL}]
|
||||||
|
* [USING condition]
|
||||||
|
* [WITH CHECK condition] [,...]
|
||||||
|
* [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
|
||||||
|
*
|
||||||
|
* ALTER [ROW] POLICY [IF EXISTS] name ON [database.]table
|
||||||
|
* [RENAME TO new_name]
|
||||||
|
* [AS {PERMISSIVE | RESTRICTIVE}]
|
||||||
|
* [FOR {SELECT | INSERT | UPDATE | DELETE | ALL}]
|
||||||
|
* [USING {condition | NONE}]
|
||||||
|
* [WITH CHECK {condition | NONE}] [,...]
|
||||||
|
* [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
|
||||||
|
*/
|
||||||
|
class ASTCreateRowPolicyQuery : public IAST
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool alter = false;
|
||||||
|
|
||||||
|
bool if_exists = false;
|
||||||
|
bool if_not_exists = false;
|
||||||
|
bool or_replace = false;
|
||||||
|
|
||||||
|
RowPolicy::FullNameParts name_parts;
|
||||||
|
String new_policy_name;
|
||||||
|
|
||||||
|
std::optional<bool> is_restrictive;
|
||||||
|
using ConditionIndex = RowPolicy::ConditionIndex;
|
||||||
|
std::vector<std::pair<ConditionIndex, ASTPtr>> conditions;
|
||||||
|
|
||||||
|
std::shared_ptr<ASTRoleList> roles;
|
||||||
|
|
||||||
|
String getID(char) const override;
|
||||||
|
ASTPtr clone() const override;
|
||||||
|
void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override;
|
||||||
|
};
|
||||||
|
}
|
@ -13,6 +13,7 @@ namespace
|
|||||||
switch (kind)
|
switch (kind)
|
||||||
{
|
{
|
||||||
case Kind::QUOTA: return "QUOTA";
|
case Kind::QUOTA: return "QUOTA";
|
||||||
|
case Kind::ROW_POLICY: return "POLICY";
|
||||||
}
|
}
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
@ -44,13 +45,32 @@ void ASTDropAccessEntityQuery::formatImpl(const FormatSettings & settings, Forma
|
|||||||
<< (if_exists ? " IF EXISTS" : "")
|
<< (if_exists ? " IF EXISTS" : "")
|
||||||
<< (settings.hilite ? hilite_none : "");
|
<< (settings.hilite ? hilite_none : "");
|
||||||
|
|
||||||
bool need_comma = false;
|
if (kind == Kind::ROW_POLICY)
|
||||||
for (const auto & name : names)
|
|
||||||
{
|
{
|
||||||
if (need_comma)
|
bool need_comma = false;
|
||||||
settings.ostr << ',';
|
for (const auto & row_policy_name : row_policies_names)
|
||||||
need_comma = true;
|
{
|
||||||
settings.ostr << ' ' << backQuoteIfNeed(name);
|
if (need_comma)
|
||||||
|
settings.ostr << ',';
|
||||||
|
need_comma = true;
|
||||||
|
const String & database = row_policy_name.database;
|
||||||
|
const String & table_name = row_policy_name.table_name;
|
||||||
|
const String & policy_name = row_policy_name.policy_name;
|
||||||
|
settings.ostr << ' ' << backQuoteIfNeed(policy_name) << (settings.hilite ? hilite_keyword : "") << " ON "
|
||||||
|
<< (settings.hilite ? hilite_none : "") << (database.empty() ? String{} : backQuoteIfNeed(database) + ".")
|
||||||
|
<< backQuoteIfNeed(table_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool need_comma = false;
|
||||||
|
for (const auto & name : names)
|
||||||
|
{
|
||||||
|
if (need_comma)
|
||||||
|
settings.ostr << ',';
|
||||||
|
need_comma = true;
|
||||||
|
settings.ostr << ' ' << backQuoteIfNeed(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Parsers/IAST.h>
|
#include <Parsers/IAST.h>
|
||||||
|
#include <Access/RowPolicy.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
|
|
||||||
/** DROP QUOTA [IF EXISTS] name [,...]
|
/** DROP QUOTA [IF EXISTS] name [,...]
|
||||||
|
* DROP [ROW] POLICY [IF EXISTS] name [,...] ON [database.]table [,...]
|
||||||
*/
|
*/
|
||||||
class ASTDropAccessEntityQuery : public IAST
|
class ASTDropAccessEntityQuery : public IAST
|
||||||
{
|
{
|
||||||
@ -14,11 +16,13 @@ public:
|
|||||||
enum class Kind
|
enum class Kind
|
||||||
{
|
{
|
||||||
QUOTA,
|
QUOTA,
|
||||||
|
ROW_POLICY,
|
||||||
};
|
};
|
||||||
const Kind kind;
|
const Kind kind;
|
||||||
const char * const keyword;
|
const char * const keyword;
|
||||||
bool if_exists = false;
|
bool if_exists = false;
|
||||||
Strings names;
|
Strings names;
|
||||||
|
std::vector<RowPolicy::FullNameParts> row_policies_names;
|
||||||
|
|
||||||
ASTDropAccessEntityQuery(Kind kind_);
|
ASTDropAccessEntityQuery(Kind kind_);
|
||||||
String getID(char) const override;
|
String getID(char) const override;
|
||||||
|
@ -13,6 +13,7 @@ namespace
|
|||||||
switch (kind)
|
switch (kind)
|
||||||
{
|
{
|
||||||
case Kind::QUOTA: return "QUOTA";
|
case Kind::QUOTA: return "QUOTA";
|
||||||
|
case Kind::ROW_POLICY: return "POLICY";
|
||||||
}
|
}
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
@ -43,7 +44,16 @@ void ASTShowCreateAccessEntityQuery::formatQueryImpl(const FormatSettings & sett
|
|||||||
<< "SHOW CREATE " << keyword
|
<< "SHOW CREATE " << keyword
|
||||||
<< (settings.hilite ? hilite_none : "");
|
<< (settings.hilite ? hilite_none : "");
|
||||||
|
|
||||||
if (current_quota)
|
if (kind == Kind::ROW_POLICY)
|
||||||
|
{
|
||||||
|
const String & database = row_policy_name.database;
|
||||||
|
const String & table_name = row_policy_name.table_name;
|
||||||
|
const String & policy_name = row_policy_name.policy_name;
|
||||||
|
settings.ostr << ' ' << backQuoteIfNeed(policy_name) << (settings.hilite ? hilite_keyword : "") << " ON "
|
||||||
|
<< (settings.hilite ? hilite_none : "") << (database.empty() ? String{} : backQuoteIfNeed(database) + ".")
|
||||||
|
<< backQuoteIfNeed(table_name);
|
||||||
|
}
|
||||||
|
else if ((kind == Kind::QUOTA) && current_quota)
|
||||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << " CURRENT" << (settings.hilite ? hilite_none : "");
|
settings.ostr << (settings.hilite ? hilite_keyword : "") << " CURRENT" << (settings.hilite ? hilite_none : "");
|
||||||
else
|
else
|
||||||
settings.ostr << " " << backQuoteIfNeed(name);
|
settings.ostr << " " << backQuoteIfNeed(name);
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Parsers/ASTQueryWithOutput.h>
|
#include <Parsers/ASTQueryWithOutput.h>
|
||||||
|
#include <Access/RowPolicy.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
/** SHOW CREATE QUOTA [name | CURRENT]
|
/** SHOW CREATE QUOTA [name | CURRENT]
|
||||||
|
* SHOW CREATE [ROW] POLICY name ON [database.]table
|
||||||
*/
|
*/
|
||||||
class ASTShowCreateAccessEntityQuery : public ASTQueryWithOutput
|
class ASTShowCreateAccessEntityQuery : public ASTQueryWithOutput
|
||||||
{
|
{
|
||||||
@ -13,11 +15,13 @@ public:
|
|||||||
enum class Kind
|
enum class Kind
|
||||||
{
|
{
|
||||||
QUOTA,
|
QUOTA,
|
||||||
|
ROW_POLICY,
|
||||||
};
|
};
|
||||||
const Kind kind;
|
const Kind kind;
|
||||||
const char * const keyword;
|
const char * const keyword;
|
||||||
String name;
|
String name;
|
||||||
bool current_quota = false;
|
bool current_quota = false;
|
||||||
|
RowPolicy::FullNameParts row_policy_name;
|
||||||
|
|
||||||
ASTShowCreateAccessEntityQuery(Kind kind_);
|
ASTShowCreateAccessEntityQuery(Kind kind_);
|
||||||
String getID(char) const override;
|
String getID(char) const override;
|
||||||
|
22
dbms/src/Parsers/ASTShowRowPoliciesQuery.cpp
Normal file
22
dbms/src/Parsers/ASTShowRowPoliciesQuery.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include <Parsers/ASTShowRowPoliciesQuery.h>
|
||||||
|
#include <Common/quoteString.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
void ASTShowRowPoliciesQuery::formatQueryImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const
|
||||||
|
{
|
||||||
|
settings.ostr << (settings.hilite ? hilite_keyword : "") << "SHOW POLICIES" << (settings.hilite ? hilite_none : "");
|
||||||
|
|
||||||
|
if (current)
|
||||||
|
settings.ostr << (settings.hilite ? hilite_keyword : "") << " CURRENT" << (settings.hilite ? hilite_none : "");
|
||||||
|
|
||||||
|
if (!table_name.empty())
|
||||||
|
{
|
||||||
|
settings.ostr << (settings.hilite ? hilite_keyword : "") << " ON " << (settings.hilite ? hilite_none : "");
|
||||||
|
if (!database.empty())
|
||||||
|
settings.ostr << backQuoteIfNeed(database) << ".";
|
||||||
|
settings.ostr << backQuoteIfNeed(table_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
dbms/src/Parsers/ASTShowRowPoliciesQuery.h
Normal file
23
dbms/src/Parsers/ASTShowRowPoliciesQuery.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Parsers/ASTQueryWithOutput.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
/// SHOW [ROW] POLICIES [CURRENT] [ON [database.]table]
|
||||||
|
class ASTShowRowPoliciesQuery : public ASTQueryWithOutput
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool current = false;
|
||||||
|
String database;
|
||||||
|
String table_name;
|
||||||
|
|
||||||
|
String getID(char) const override { return "SHOW POLICIES query"; }
|
||||||
|
ASTPtr clone() const override { return std::make_shared<ASTShowRowPoliciesQuery>(*this); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void formatQueryImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
261
dbms/src/Parsers/ParserCreateRowPolicyQuery.cpp
Normal file
261
dbms/src/Parsers/ParserCreateRowPolicyQuery.cpp
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
#include <Parsers/ParserCreateRowPolicyQuery.h>
|
||||||
|
#include <Parsers/ASTCreateRowPolicyQuery.h>
|
||||||
|
#include <Access/RowPolicy.h>
|
||||||
|
#include <Parsers/ParserRoleList.h>
|
||||||
|
#include <Parsers/ASTRoleList.h>
|
||||||
|
#include <Parsers/parseIdentifierOrStringLiteral.h>
|
||||||
|
#include <Parsers/parseDatabaseAndTableName.h>
|
||||||
|
#include <Parsers/ExpressionListParsers.h>
|
||||||
|
#include <Parsers/ASTLiteral.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
namespace ErrorCodes
|
||||||
|
{
|
||||||
|
extern const int SYNTAX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using ConditionIndex = RowPolicy::ConditionIndex;
|
||||||
|
|
||||||
|
bool parseRenameTo(IParserBase::Pos & pos, Expected & expected, String & new_policy_name, bool alter)
|
||||||
|
{
|
||||||
|
return IParserBase::wrapParseImpl(pos, [&]
|
||||||
|
{
|
||||||
|
if (!new_policy_name.empty() || !alter)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ParserKeyword{"RENAME TO"}.ignore(pos, expected))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return parseIdentifierOrStringLiteral(pos, expected, new_policy_name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parseIsRestrictive(IParserBase::Pos & pos, Expected & expected, std::optional<bool> & is_restrictive)
|
||||||
|
{
|
||||||
|
return IParserBase::wrapParseImpl(pos, [&]
|
||||||
|
{
|
||||||
|
if (is_restrictive)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ParserKeyword{"AS"}.ignore(pos, expected))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ParserKeyword{"RESTRICTIVE"}.ignore(pos, expected))
|
||||||
|
is_restrictive = true;
|
||||||
|
else if (ParserKeyword{"PERMISSIVE"}.ignore(pos, expected))
|
||||||
|
is_restrictive = false;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parseConditionalExpression(IParserBase::Pos & pos, Expected & expected, std::optional<ASTPtr> & expr)
|
||||||
|
{
|
||||||
|
if (ParserKeyword("NONE").ignore(pos, expected))
|
||||||
|
{
|
||||||
|
expr = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ParserExpression parser;
|
||||||
|
ASTPtr x;
|
||||||
|
if (parser.parse(pos, x, expected))
|
||||||
|
{
|
||||||
|
expr = x;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
expr.reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parseConditions(IParserBase::Pos & pos, Expected & expected, std::vector<std::pair<ConditionIndex, ASTPtr>> & conditions, bool alter)
|
||||||
|
{
|
||||||
|
return IParserBase::wrapParseImpl(pos, [&]
|
||||||
|
{
|
||||||
|
static constexpr char select_op[] = "SELECT";
|
||||||
|
static constexpr char insert_op[] = "INSERT";
|
||||||
|
static constexpr char update_op[] = "UPDATE";
|
||||||
|
static constexpr char delete_op[] = "DELETE";
|
||||||
|
std::vector<const char *> ops;
|
||||||
|
|
||||||
|
bool keyword_for = false;
|
||||||
|
if (ParserKeyword{"FOR"}.ignore(pos, expected))
|
||||||
|
{
|
||||||
|
keyword_for = true;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (ParserKeyword{"SELECT"}.ignore(pos, expected))
|
||||||
|
ops.push_back(select_op);
|
||||||
|
else if (ParserKeyword{"INSERT"}.ignore(pos, expected))
|
||||||
|
ops.push_back(insert_op);
|
||||||
|
else if (ParserKeyword{"UPDATE"}.ignore(pos, expected))
|
||||||
|
ops.push_back(update_op);
|
||||||
|
else if (ParserKeyword{"DELETE"}.ignore(pos, expected))
|
||||||
|
ops.push_back(delete_op);
|
||||||
|
else if (ParserKeyword{"ALL"}.ignore(pos, expected))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ops.empty())
|
||||||
|
{
|
||||||
|
ops.push_back(select_op);
|
||||||
|
ops.push_back(insert_op);
|
||||||
|
ops.push_back(update_op);
|
||||||
|
ops.push_back(delete_op);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ASTPtr> filter;
|
||||||
|
std::optional<ASTPtr> check;
|
||||||
|
bool keyword_using = false, keyword_with_check = false;
|
||||||
|
if (ParserKeyword{"USING"}.ignore(pos, expected))
|
||||||
|
{
|
||||||
|
keyword_using = true;
|
||||||
|
if (!parseConditionalExpression(pos, expected, filter))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ParserKeyword{"WITH CHECK"}.ignore(pos, expected))
|
||||||
|
{
|
||||||
|
keyword_with_check = true;
|
||||||
|
if (!parseConditionalExpression(pos, expected, check))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keyword_for && !keyword_using && !keyword_with_check)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (filter && !check && !alter)
|
||||||
|
check = filter;
|
||||||
|
|
||||||
|
auto set_condition = [&](ConditionIndex index, const ASTPtr & condition)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(conditions.begin(), conditions.end(), [index](const std::pair<ConditionIndex, ASTPtr> & element)
|
||||||
|
{
|
||||||
|
return element.first == index;
|
||||||
|
});
|
||||||
|
if (it == conditions.end())
|
||||||
|
it = conditions.insert(conditions.end(), std::pair<ConditionIndex, ASTPtr>{index, nullptr});
|
||||||
|
it->second = condition;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto & op : ops)
|
||||||
|
{
|
||||||
|
if ((op == select_op) && filter)
|
||||||
|
set_condition(RowPolicy::SELECT_FILTER, *filter);
|
||||||
|
else if ((op == insert_op) && check)
|
||||||
|
set_condition(RowPolicy::INSERT_CHECK, *check);
|
||||||
|
else if (op == update_op)
|
||||||
|
{
|
||||||
|
if (filter)
|
||||||
|
set_condition(RowPolicy::UPDATE_FILTER, *filter);
|
||||||
|
if (check)
|
||||||
|
set_condition(RowPolicy::UPDATE_CHECK, *check);
|
||||||
|
}
|
||||||
|
else if ((op == delete_op) && filter)
|
||||||
|
set_condition(RowPolicy::DELETE_FILTER, *filter);
|
||||||
|
else
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parseMultipleConditions(IParserBase::Pos & pos, Expected & expected, std::vector<std::pair<ConditionIndex, ASTPtr>> & conditions, bool alter)
|
||||||
|
{
|
||||||
|
return IParserBase::wrapParseImpl(pos, [&]
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!parseConditions(pos, expected, conditions, alter))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parseRoles(IParserBase::Pos & pos, Expected & expected, std::shared_ptr<ASTRoleList> & roles)
|
||||||
|
{
|
||||||
|
return IParserBase::wrapParseImpl(pos, [&]
|
||||||
|
{
|
||||||
|
ASTPtr node;
|
||||||
|
if (roles || !ParserKeyword{"TO"}.ignore(pos, expected) || !ParserRoleList{}.parse(pos, node, expected))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
roles = std::static_pointer_cast<ASTRoleList>(node);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ParserCreateRowPolicyQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||||
|
{
|
||||||
|
bool alter;
|
||||||
|
if (ParserKeyword{"CREATE POLICY"}.ignore(pos, expected) || ParserKeyword{"CREATE ROW POLICY"}.ignore(pos, expected))
|
||||||
|
alter = false;
|
||||||
|
else if (ParserKeyword{"ALTER POLICY"}.ignore(pos, expected) || ParserKeyword{"ALTER ROW POLICY"}.ignore(pos, expected))
|
||||||
|
alter = true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool if_exists = false;
|
||||||
|
bool if_not_exists = false;
|
||||||
|
bool or_replace = false;
|
||||||
|
if (alter)
|
||||||
|
{
|
||||||
|
if (ParserKeyword{"IF EXISTS"}.ignore(pos, expected))
|
||||||
|
if_exists = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ParserKeyword{"IF NOT EXISTS"}.ignore(pos, expected))
|
||||||
|
if_not_exists = true;
|
||||||
|
else if (ParserKeyword{"OR REPLACE"}.ignore(pos, expected))
|
||||||
|
or_replace = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RowPolicy::FullNameParts name_parts;
|
||||||
|
String & database = name_parts.database;
|
||||||
|
String & table_name = name_parts.table_name;
|
||||||
|
String & policy_name = name_parts.policy_name;
|
||||||
|
if (!parseIdentifierOrStringLiteral(pos, expected, policy_name) || !ParserKeyword{"ON"}.ignore(pos, expected)
|
||||||
|
|| !parseDatabaseAndTableName(pos, expected, database, table_name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
String new_policy_name;
|
||||||
|
std::optional<bool> is_restrictive;
|
||||||
|
std::vector<std::pair<ConditionIndex, ASTPtr>> conditions;
|
||||||
|
std::shared_ptr<ASTRoleList> roles;
|
||||||
|
|
||||||
|
while (parseRenameTo(pos, expected, new_policy_name, alter) || parseIsRestrictive(pos, expected, is_restrictive)
|
||||||
|
|| parseMultipleConditions(pos, expected, conditions, alter) || parseRoles(pos, expected, roles))
|
||||||
|
;
|
||||||
|
|
||||||
|
auto query = std::make_shared<ASTCreateRowPolicyQuery>();
|
||||||
|
node = query;
|
||||||
|
|
||||||
|
query->alter = alter;
|
||||||
|
query->if_exists = if_exists;
|
||||||
|
query->if_not_exists = if_not_exists;
|
||||||
|
query->or_replace = or_replace;
|
||||||
|
query->name_parts = std::move(name_parts);
|
||||||
|
query->new_policy_name = std::move(new_policy_name);
|
||||||
|
query->is_restrictive = is_restrictive;
|
||||||
|
query->conditions = std::move(conditions);
|
||||||
|
query->roles = std::move(roles);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
30
dbms/src/Parsers/ParserCreateRowPolicyQuery.h
Normal file
30
dbms/src/Parsers/ParserCreateRowPolicyQuery.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Parsers/IParserBase.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
/** Parses queries like
|
||||||
|
* CREATE [ROW] POLICY [IF NOT EXISTS | OR REPLACE] name ON [database.]table
|
||||||
|
* [AS {PERMISSIVE | RESTRICTIVE}]
|
||||||
|
* [FOR {SELECT | INSERT | UPDATE | DELETE | ALL}]
|
||||||
|
* [USING condition]
|
||||||
|
* [WITH CHECK condition] [,...]
|
||||||
|
* [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
|
||||||
|
*
|
||||||
|
* ALTER [ROW] POLICY [IF EXISTS] name ON [database.]table
|
||||||
|
* [RENAME TO new_name]
|
||||||
|
* [AS {PERMISSIVE | RESTRICTIVE}]
|
||||||
|
* [FOR {SELECT | INSERT | UPDATE | DELETE | ALL}]
|
||||||
|
* [USING {condition | NONE}]
|
||||||
|
* [WITH CHECK {condition | NONE}] [,...]
|
||||||
|
* [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
|
||||||
|
*/
|
||||||
|
class ParserCreateRowPolicyQuery : public IParserBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
const char * getName() const override { return "CREATE ROW POLICY or ALTER ROW POLICY query"; }
|
||||||
|
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
|
||||||
|
};
|
||||||
|
}
|
@ -2,11 +2,30 @@
|
|||||||
#include <Parsers/ASTDropAccessEntityQuery.h>
|
#include <Parsers/ASTDropAccessEntityQuery.h>
|
||||||
#include <Parsers/CommonParsers.h>
|
#include <Parsers/CommonParsers.h>
|
||||||
#include <Parsers/parseIdentifierOrStringLiteral.h>
|
#include <Parsers/parseIdentifierOrStringLiteral.h>
|
||||||
|
#include <Parsers/parseDatabaseAndTableName.h>
|
||||||
#include <Access/Quota.h>
|
#include <Access/Quota.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool parseNames(IParserBase::Pos & pos, Expected & expected, Strings & names)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
String name;
|
||||||
|
if (!parseIdentifierOrStringLiteral(pos, expected, name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
names.push_back(std::move(name));
|
||||||
|
}
|
||||||
|
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool ParserDropAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
bool ParserDropAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||||
{
|
{
|
||||||
if (!ParserKeyword{"DROP"}.ignore(pos, expected))
|
if (!ParserKeyword{"DROP"}.ignore(pos, expected))
|
||||||
@ -16,6 +35,8 @@ bool ParserDropAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expected &
|
|||||||
Kind kind;
|
Kind kind;
|
||||||
if (ParserKeyword{"QUOTA"}.ignore(pos, expected))
|
if (ParserKeyword{"QUOTA"}.ignore(pos, expected))
|
||||||
kind = Kind::QUOTA;
|
kind = Kind::QUOTA;
|
||||||
|
else if (ParserKeyword{"POLICY"}.ignore(pos, expected) || ParserKeyword{"ROW POLICY"}.ignore(pos, expected))
|
||||||
|
kind = Kind::ROW_POLICY;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -24,21 +45,35 @@ bool ParserDropAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expected &
|
|||||||
if_exists = true;
|
if_exists = true;
|
||||||
|
|
||||||
Strings names;
|
Strings names;
|
||||||
do
|
std::vector<RowPolicy::FullNameParts> row_policies_names;
|
||||||
{
|
|
||||||
String name;
|
|
||||||
if (!parseIdentifierOrStringLiteral(pos, expected, name))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
names.push_back(std::move(name));
|
if (kind == Kind::ROW_POLICY)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Strings policy_names;
|
||||||
|
if (!parseNames(pos, expected, policy_names))
|
||||||
|
return false;
|
||||||
|
String database, table_name;
|
||||||
|
if (!ParserKeyword{"ON"}.ignore(pos, expected) || !parseDatabaseAndTableName(pos, expected, database, table_name))
|
||||||
|
return false;
|
||||||
|
for (const String & policy_name : policy_names)
|
||||||
|
row_policies_names.push_back({database, table_name, policy_name});
|
||||||
|
}
|
||||||
|
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!parseNames(pos, expected, names))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
|
|
||||||
|
|
||||||
auto query = std::make_shared<ASTDropAccessEntityQuery>(kind);
|
auto query = std::make_shared<ASTDropAccessEntityQuery>(kind);
|
||||||
node = query;
|
node = query;
|
||||||
|
|
||||||
query->if_exists = if_exists;
|
query->if_exists = if_exists;
|
||||||
query->names = std::move(names);
|
query->names = std::move(names);
|
||||||
|
query->row_policies_names = std::move(row_policies_names);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <Parsers/ParserAlterQuery.h>
|
#include <Parsers/ParserAlterQuery.h>
|
||||||
#include <Parsers/ParserSystemQuery.h>
|
#include <Parsers/ParserSystemQuery.h>
|
||||||
#include <Parsers/ParserCreateQuotaQuery.h>
|
#include <Parsers/ParserCreateQuotaQuery.h>
|
||||||
|
#include <Parsers/ParserCreateRowPolicyQuery.h>
|
||||||
#include <Parsers/ParserDropAccessEntityQuery.h>
|
#include <Parsers/ParserDropAccessEntityQuery.h>
|
||||||
|
|
||||||
|
|
||||||
@ -25,6 +26,7 @@ bool ParserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
|||||||
ParserSetQuery set_p;
|
ParserSetQuery set_p;
|
||||||
ParserSystemQuery system_p;
|
ParserSystemQuery system_p;
|
||||||
ParserCreateQuotaQuery create_quota_p;
|
ParserCreateQuotaQuery create_quota_p;
|
||||||
|
ParserCreateRowPolicyQuery create_row_policy_p;
|
||||||
ParserDropAccessEntityQuery drop_access_entity_p;
|
ParserDropAccessEntityQuery drop_access_entity_p;
|
||||||
|
|
||||||
bool res = query_with_output_p.parse(pos, node, expected)
|
bool res = query_with_output_p.parse(pos, node, expected)
|
||||||
@ -33,6 +35,7 @@ bool ParserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
|||||||
|| set_p.parse(pos, node, expected)
|
|| set_p.parse(pos, node, expected)
|
||||||
|| system_p.parse(pos, node, expected)
|
|| system_p.parse(pos, node, expected)
|
||||||
|| create_quota_p.parse(pos, node, expected)
|
|| create_quota_p.parse(pos, node, expected)
|
||||||
|
|| create_row_policy_p.parse(pos, node, expected)
|
||||||
|| drop_access_entity_p.parse(pos, node, expected);
|
|| drop_access_entity_p.parse(pos, node, expected);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <Parsers/ASTExplainQuery.h>
|
#include <Parsers/ASTExplainQuery.h>
|
||||||
#include <Parsers/ParserShowCreateAccessEntityQuery.h>
|
#include <Parsers/ParserShowCreateAccessEntityQuery.h>
|
||||||
#include <Parsers/ParserShowQuotasQuery.h>
|
#include <Parsers/ParserShowQuotasQuery.h>
|
||||||
|
#include <Parsers/ParserShowRowPoliciesQuery.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
@ -38,6 +39,7 @@ bool ParserQueryWithOutput::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|
|||||||
ParserWatchQuery watch_p;
|
ParserWatchQuery watch_p;
|
||||||
ParserShowCreateAccessEntityQuery show_create_access_entity_p;
|
ParserShowCreateAccessEntityQuery show_create_access_entity_p;
|
||||||
ParserShowQuotasQuery show_quotas_p;
|
ParserShowQuotasQuery show_quotas_p;
|
||||||
|
ParserShowRowPoliciesQuery show_row_policies_p;
|
||||||
|
|
||||||
ASTPtr query;
|
ASTPtr query;
|
||||||
|
|
||||||
@ -66,7 +68,8 @@ bool ParserQueryWithOutput::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|
|||||||
|| kill_query_p.parse(pos, query, expected)
|
|| kill_query_p.parse(pos, query, expected)
|
||||||
|| optimize_p.parse(pos, query, expected)
|
|| optimize_p.parse(pos, query, expected)
|
||||||
|| watch_p.parse(pos, query, expected)
|
|| watch_p.parse(pos, query, expected)
|
||||||
|| show_quotas_p.parse(pos, query, expected);
|
|| show_quotas_p.parse(pos, query, expected)
|
||||||
|
|| show_row_policies_p.parse(pos, query, expected);
|
||||||
|
|
||||||
if (!parsed)
|
if (!parsed)
|
||||||
return false;
|
return false;
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
#include <Parsers/ASTShowCreateAccessEntityQuery.h>
|
#include <Parsers/ASTShowCreateAccessEntityQuery.h>
|
||||||
#include <Parsers/CommonParsers.h>
|
#include <Parsers/CommonParsers.h>
|
||||||
#include <Parsers/parseIdentifierOrStringLiteral.h>
|
#include <Parsers/parseIdentifierOrStringLiteral.h>
|
||||||
|
#include <Parsers/parseDatabaseAndTableName.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
@ -15,25 +17,41 @@ bool ParserShowCreateAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expe
|
|||||||
Kind kind;
|
Kind kind;
|
||||||
if (ParserKeyword{"QUOTA"}.ignore(pos, expected))
|
if (ParserKeyword{"QUOTA"}.ignore(pos, expected))
|
||||||
kind = Kind::QUOTA;
|
kind = Kind::QUOTA;
|
||||||
|
else if (ParserKeyword{"POLICY"}.ignore(pos, expected) || ParserKeyword{"ROW POLICY"}.ignore(pos, expected))
|
||||||
|
kind = Kind::ROW_POLICY;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
String name;
|
String name;
|
||||||
bool current_quota = false;
|
bool current_quota = false;
|
||||||
|
RowPolicy::FullNameParts row_policy_name;
|
||||||
|
|
||||||
if ((kind == Kind::QUOTA) && ParserKeyword{"CURRENT"}.ignore(pos, expected))
|
if (kind == Kind::ROW_POLICY)
|
||||||
{
|
{
|
||||||
/// SHOW CREATE QUOTA CURRENT
|
String & database = row_policy_name.database;
|
||||||
current_quota = true;
|
String & table_name = row_policy_name.table_name;
|
||||||
}
|
String & policy_name = row_policy_name.policy_name;
|
||||||
else if (parseIdentifierOrStringLiteral(pos, expected, name))
|
if (!parseIdentifierOrStringLiteral(pos, expected, policy_name) || !ParserKeyword{"ON"}.ignore(pos, expected)
|
||||||
{
|
|| !parseDatabaseAndTableName(pos, expected, database, table_name))
|
||||||
/// SHOW CREATE QUOTA name
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/// SHOW CREATE QUOTA
|
assert(kind == Kind::QUOTA);
|
||||||
current_quota = true;
|
if (ParserKeyword{"CURRENT"}.ignore(pos, expected))
|
||||||
|
{
|
||||||
|
/// SHOW CREATE QUOTA CURRENT
|
||||||
|
current_quota = true;
|
||||||
|
}
|
||||||
|
else if (parseIdentifierOrStringLiteral(pos, expected, name))
|
||||||
|
{
|
||||||
|
/// SHOW CREATE QUOTA name
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/// SHOW CREATE QUOTA
|
||||||
|
current_quota = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto query = std::make_shared<ASTShowCreateAccessEntityQuery>(kind);
|
auto query = std::make_shared<ASTShowCreateAccessEntityQuery>(kind);
|
||||||
@ -41,6 +59,7 @@ bool ParserShowCreateAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expe
|
|||||||
|
|
||||||
query->name = std::move(name);
|
query->name = std::move(name);
|
||||||
query->current_quota = current_quota;
|
query->current_quota = current_quota;
|
||||||
|
query->row_policy_name = std::move(row_policy_name);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
40
dbms/src/Parsers/ParserShowRowPoliciesQuery.cpp
Normal file
40
dbms/src/Parsers/ParserShowRowPoliciesQuery.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#include <Parsers/ParserShowRowPoliciesQuery.h>
|
||||||
|
#include <Parsers/ASTShowRowPoliciesQuery.h>
|
||||||
|
#include <Parsers/CommonParsers.h>
|
||||||
|
#include <Parsers/parseDatabaseAndTableName.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool parseONDatabaseAndTableName(IParserBase::Pos & pos, Expected & expected, String & database, String & table_name)
|
||||||
|
{
|
||||||
|
return IParserBase::wrapParseImpl(pos, [&]
|
||||||
|
{
|
||||||
|
database.clear();
|
||||||
|
table_name.clear();
|
||||||
|
return ParserKeyword{"ON"}.ignore(pos, expected) && parseDatabaseAndTableName(pos, expected, database, table_name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ParserShowRowPoliciesQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||||
|
{
|
||||||
|
if (!ParserKeyword{"SHOW POLICIES"}.ignore(pos, expected) && !ParserKeyword{"SHOW ROW POLICIES"}.ignore(pos, expected))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool current = ParserKeyword{"CURRENT"}.ignore(pos, expected);
|
||||||
|
|
||||||
|
String database, table_name;
|
||||||
|
parseONDatabaseAndTableName(pos, expected, database, table_name);
|
||||||
|
|
||||||
|
auto query = std::make_shared<ASTShowRowPoliciesQuery>();
|
||||||
|
query->current = current;
|
||||||
|
query->database = std::move(database);
|
||||||
|
query->table_name = std::move(table_name);
|
||||||
|
node = query;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
17
dbms/src/Parsers/ParserShowRowPoliciesQuery.h
Normal file
17
dbms/src/Parsers/ParserShowRowPoliciesQuery.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Parsers/IParserBase.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
/** Parses queries like
|
||||||
|
* SHOW [ROW] POLICIES [CURRENT] [ON [database.]table]
|
||||||
|
*/
|
||||||
|
class ParserShowRowPoliciesQuery : public IParserBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
const char * getName() const override { return "SHOW POLICIES query"; }
|
||||||
|
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
|
||||||
|
};
|
||||||
|
}
|
@ -153,3 +153,57 @@ def test_introspection():
|
|||||||
assert instance.query("SELECT * from system.row_policies WHERE has(currentRowPolicyIDs('mydb', 'filtered_table3'), id) ORDER BY table, name") == policy3
|
assert instance.query("SELECT * from system.row_policies WHERE has(currentRowPolicyIDs('mydb', 'filtered_table3'), id) ORDER BY table, name") == policy3
|
||||||
assert instance.query("SELECT * from system.row_policies ORDER BY table, name") == policy1 + policy2 + policy3
|
assert instance.query("SELECT * from system.row_policies ORDER BY table, name") == policy1 + policy2 + policy3
|
||||||
assert instance.query("SELECT * from system.row_policies WHERE has(currentRowPolicyIDs(), id) ORDER BY table, name") == policy1 + policy2 + policy3
|
assert instance.query("SELECT * from system.row_policies WHERE has(currentRowPolicyIDs(), id) ORDER BY table, name") == policy1 + policy2 + policy3
|
||||||
|
|
||||||
|
|
||||||
|
def test_dcl_introspection():
|
||||||
|
assert instance.query("SHOW POLICIES ON mydb.filtered_table1") == "default\n"
|
||||||
|
assert instance.query("SHOW POLICIES CURRENT ON mydb.filtered_table2") == "default\n"
|
||||||
|
assert instance.query("SHOW POLICIES") == "default ON mydb.filtered_table1\ndefault ON mydb.filtered_table2\ndefault ON mydb.filtered_table3\n"
|
||||||
|
assert instance.query("SHOW POLICIES CURRENT") == "default ON mydb.filtered_table1\ndefault ON mydb.filtered_table2\ndefault ON mydb.filtered_table3\n"
|
||||||
|
|
||||||
|
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table1") == "CREATE POLICY default ON mydb.filtered_table1 FOR SELECT USING a = 1 TO default\n"
|
||||||
|
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table2") == "CREATE POLICY default ON mydb.filtered_table2 FOR SELECT USING ((a + b) < 1) OR ((c - d) > 5) TO default\n"
|
||||||
|
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table3") == "CREATE POLICY default ON mydb.filtered_table3 FOR SELECT USING c = 1 TO default\n"
|
||||||
|
|
||||||
|
copy_policy_xml('all_rows.xml')
|
||||||
|
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table1") == "CREATE POLICY default ON mydb.filtered_table1 FOR SELECT USING 1 TO default\n"
|
||||||
|
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table2") == "CREATE POLICY default ON mydb.filtered_table2 FOR SELECT USING 1 TO default\n"
|
||||||
|
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table3") == "CREATE POLICY default ON mydb.filtered_table3 FOR SELECT USING 1 TO default\n"
|
||||||
|
|
||||||
|
copy_policy_xml('no_rows.xml')
|
||||||
|
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table1") == "CREATE POLICY default ON mydb.filtered_table1 FOR SELECT USING NULL TO default\n"
|
||||||
|
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table2") == "CREATE POLICY default ON mydb.filtered_table2 FOR SELECT USING NULL TO default\n"
|
||||||
|
assert instance.query("SHOW CREATE POLICY default ON mydb.filtered_table3") == "CREATE POLICY default ON mydb.filtered_table3 FOR SELECT USING NULL TO default\n"
|
||||||
|
|
||||||
|
copy_policy_xml('no_filters.xml')
|
||||||
|
assert instance.query("SHOW POLICIES") == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_dcl_management():
|
||||||
|
copy_policy_xml('no_filters.xml')
|
||||||
|
assert instance.query("SHOW POLICIES") == ""
|
||||||
|
|
||||||
|
instance.query("CREATE POLICY pA ON mydb.filtered_table1 FOR SELECT USING a<b")
|
||||||
|
assert instance.query("SELECT * FROM mydb.filtered_table1") == "0\t0\n0\t1\n1\t0\n1\t1\n"
|
||||||
|
assert instance.query("SHOW POLICIES CURRENT ON mydb.filtered_table1") == ""
|
||||||
|
assert instance.query("SHOW POLICIES ON mydb.filtered_table1") == "pA\n"
|
||||||
|
|
||||||
|
instance.query("ALTER POLICY pA ON mydb.filtered_table1 TO default")
|
||||||
|
assert instance.query("SELECT * FROM mydb.filtered_table1") == "0\t1\n"
|
||||||
|
assert instance.query("SHOW POLICIES CURRENT ON mydb.filtered_table1") == "pA\n"
|
||||||
|
|
||||||
|
instance.query("ALTER POLICY pA ON mydb.filtered_table1 FOR SELECT USING a>b")
|
||||||
|
assert instance.query("SELECT * FROM mydb.filtered_table1") == "1\t0\n"
|
||||||
|
|
||||||
|
instance.query("ALTER POLICY pA ON mydb.filtered_table1 RENAME TO pB")
|
||||||
|
assert instance.query("SELECT * FROM mydb.filtered_table1") == "1\t0\n"
|
||||||
|
assert instance.query("SHOW POLICIES CURRENT ON mydb.filtered_table1") == "pB\n"
|
||||||
|
assert instance.query("SHOW CREATE POLICY pB ON mydb.filtered_table1") == "CREATE POLICY pB ON mydb.filtered_table1 FOR SELECT USING a > b TO default\n"
|
||||||
|
|
||||||
|
instance.query("DROP POLICY pB ON mydb.filtered_table1")
|
||||||
|
assert instance.query("SELECT * FROM mydb.filtered_table1") == "0\t0\n0\t1\n1\t0\n1\t1\n"
|
||||||
|
assert instance.query("SHOW POLICIES") == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_users_xml_is_readonly():
|
||||||
|
assert re.search("storage is readonly", instance.query_and_get_error("DROP POLICY default ON mydb.filtered_table1"))
|
||||||
|
2
dbms/tests/queries/0_stateless/01039_row_policy_dcl.sql
Normal file
2
dbms/tests/queries/0_stateless/01039_row_policy_dcl.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
SHOW POLICIES;
|
||||||
|
CREATE POLICY p1 ON dummytable; -- { serverError 497 }
|
Loading…
Reference in New Issue
Block a user