提交 9edea08b 编写于 作者: V Vitaly Baranov

Improve parsers of access managing SQL.

上级 8c383f24
......@@ -103,16 +103,16 @@ void InterpreterCreateQuotaQuery::updateQuotaFromQuery(Quota & quota, const ASTC
const auto & query_roles = *query.roles;
/// We keep `roles` sorted.
quota.roles = query_roles.roles;
quota.roles = query_roles.names;
if (query_roles.current_user)
quota.roles.push_back(context.getClientInfo().current_user);
boost::range::sort(quota.roles);
quota.roles.erase(std::unique(quota.roles.begin(), quota.roles.end()), quota.roles.end());
quota.all_roles = query_roles.all_roles;
quota.all_roles = query_roles.all;
/// We keep `except_roles` sorted.
quota.except_roles = query_roles.except_roles;
quota.except_roles = query_roles.except_names;
if (query_roles.except_current_user)
quota.except_roles.push_back(context.getClientInfo().current_user);
boost::range::sort(quota.except_roles);
......
......@@ -75,16 +75,16 @@ void InterpreterCreateRowPolicyQuery::updateRowPolicyFromQuery(RowPolicy & polic
const auto & query_roles = *query.roles;
/// We keep `roles` sorted.
policy.roles = query_roles.roles;
policy.roles = query_roles.names;
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;
policy.all_roles = query_roles.all;
/// We keep `except_roles` sorted.
policy.except_roles = query_roles.except_roles;
policy.except_roles = query_roles.except_names;
if (query_roles.except_current_user)
policy.except_roles.push_back(context.getClientInfo().current_user);
boost::range::sort(policy.except_roles);
......
......@@ -18,7 +18,7 @@ public:
BlockIO execute() override;
private:
void updateUserFromQuery(User & quota, const ASTCreateUserQuery & query);
void updateUserFromQuery(User & user, const ASTCreateUserQuery & query);
ASTPtr query_ptr;
Context & context;
......
......@@ -16,11 +16,6 @@ BlockIO InterpreterGrantQuery::execute()
context.getAccessRights()->checkGrantOption(query.access_rights_elements);
using Kind = ASTGrantQuery::Kind;
if (query.to_roles->all_roles)
throw Exception(
"Cannot " + String((query.kind == Kind::GRANT) ? "GRANT to" : "REVOKE from") + " ALL", ErrorCodes::NOT_IMPLEMENTED);
String current_database = context.getCurrentDatabase();
auto update_func = [&](const AccessEntityPtr & entity) -> AccessEntityPtr
......@@ -47,7 +42,7 @@ BlockIO InterpreterGrantQuery::execute()
return updated_user;
};
std::vector<UUID> ids = access_control.getIDs<User>(query.to_roles->roles);
std::vector<UUID> ids = access_control.getIDs<User>(query.to_roles->names);
if (query.to_roles->current_user)
ids.push_back(context.getUserID());
access_control.update(ids, update_func);
......
......@@ -118,9 +118,9 @@ ASTPtr InterpreterShowCreateAccessEntityQuery::getCreateQuotaQuery(const ASTShow
if (!quota->roles.empty() || quota->all_roles)
{
auto create_query_roles = std::make_shared<ASTRoleList>();
create_query_roles->roles = quota->roles;
create_query_roles->all_roles = quota->all_roles;
create_query_roles->except_roles = quota->except_roles;
create_query_roles->names = quota->roles;
create_query_roles->all = quota->all_roles;
create_query_roles->except_names = quota->except_roles;
create_query->roles = std::move(create_query_roles);
}
......@@ -152,9 +152,9 @@ ASTPtr InterpreterShowCreateAccessEntityQuery::getCreateRowPolicyQuery(const AST
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->names = policy->roles;
create_query_roles->all = policy->all_roles;
create_query_roles->except_names = policy->except_roles;
create_query->roles = std::move(create_query_roles);
}
......
......@@ -112,7 +112,7 @@ ASTs InterpreterShowGrantsQuery::getGrantQueries(const ASTShowGrantsQuery & show
grant_query->kind = kind;
grant_query->grant_option = grant_option;
grant_query->to_roles = std::make_shared<ASTRoleList>();
grant_query->to_roles->roles.push_back(user->getName());
grant_query->to_roles->names.push_back(user->getName());
grant_query->access_rights_elements = elements;
res.push_back(std::move(grant_query));
}
......
......@@ -94,7 +94,7 @@ namespace
}
}
void formatRoles(const ASTRoleList & roles, const IAST::FormatSettings & settings)
void formatToRoles(const ASTRoleList & roles, const IAST::FormatSettings & settings)
{
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " TO " << (settings.hilite ? IAST::hilite_none : "");
roles.format(settings);
......@@ -137,6 +137,6 @@ void ASTCreateQuotaQuery::formatImpl(const FormatSettings & settings, FormatStat
formatAllLimits(all_limits, settings);
if (roles)
formatRoles(*roles, settings);
formatToRoles(*roles, settings);
}
}
......@@ -19,7 +19,7 @@ namespace
}
void formatIsRestrictive(bool is_restrictive, const IAST::FormatSettings & settings)
void formatAsRestrictiveOrPermissive(bool is_restrictive, const IAST::FormatSettings & settings)
{
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " AS " << (is_restrictive ? "RESTRICTIVE" : "PERMISSIVE")
<< (settings.hilite ? IAST::hilite_none : "");
......@@ -112,7 +112,7 @@ namespace
}
}
void formatRoles(const ASTRoleList & roles, const IAST::FormatSettings & settings)
void formatToRoles(const ASTRoleList & roles, const IAST::FormatSettings & settings)
{
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " TO " << (settings.hilite ? IAST::hilite_none : "");
roles.format(settings);
......@@ -154,11 +154,11 @@ void ASTCreateRowPolicyQuery::formatImpl(const FormatSettings & settings, Format
formatRenameTo(new_policy_name, settings);
if (is_restrictive)
formatIsRestrictive(*is_restrictive, settings);
formatAsRestrictiveOrPermissive(*is_restrictive, settings);
formatMultipleConditions(conditions, alter, settings);
if (roles)
formatRoles(*roles, settings);
formatToRoles(*roles, settings);
}
}
......@@ -71,6 +71,34 @@ namespace
}
settings.ostr << ")";
}
void formatAccessRightsElements(const AccessRightsElements & elements, const IAST::FormatSettings & settings)
{
bool need_comma = false;
for (const auto & [database_and_table, keyword_to_columns] : prepareTableToAccessMap(elements))
{
for (const auto & [keyword, columns] : keyword_to_columns)
{
if (std::exchange(need_comma, true))
settings.ostr << ", ";
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << keyword << (settings.hilite ? IAST::hilite_none : "");
formatColumnNames(columns, settings);
}
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " ON " << (settings.hilite ? IAST::hilite_none : "") << database_and_table;
}
}
void formatToRoles(const ASTRoleList & to_roles, ASTGrantQuery::Kind kind, const IAST::FormatSettings & settings)
{
using Kind = ASTGrantQuery::Kind;
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << ((kind == Kind::GRANT) ? " TO " : " FROM ")
<< (settings.hilite ? IAST::hilite_none : "");
to_roles.format(settings);
}
}
......@@ -88,29 +116,14 @@ ASTPtr ASTGrantQuery::clone() const
void ASTGrantQuery::formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << ((kind == Kind::GRANT) ? "GRANT" : "REVOKE")
<< (settings.hilite ? hilite_none : "") << " ";
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << ((kind == Kind::GRANT) ? "GRANT" : "REVOKE")
<< (settings.hilite ? IAST::hilite_none : "") << " ";
if (grant_option && (kind == Kind::REVOKE))
settings.ostr << (settings.hilite ? hilite_keyword : "") << "GRANT OPTION FOR " << (settings.hilite ? hilite_none : "");
bool need_comma = false;
for (const auto & [database_and_table, keyword_to_columns] : prepareTableToAccessMap(access_rights_elements))
{
for (const auto & [keyword, columns] : keyword_to_columns)
{
if (std::exchange(need_comma, true))
settings.ostr << ", ";
settings.ostr << (settings.hilite ? hilite_keyword : "") << keyword << (settings.hilite ? hilite_none : "");
formatColumnNames(columns, settings);
}
settings.ostr << (settings.hilite ? hilite_keyword : "") << " ON " << (settings.hilite ? hilite_none : "") << database_and_table;
}
settings.ostr << (settings.hilite ? hilite_keyword : "") << ((kind == Kind::GRANT) ? " TO " : " FROM ") << (settings.hilite ? hilite_none : "");
to_roles->format(settings);
formatAccessRightsElements(access_rights_elements, settings);
formatToRoles(*to_roles, kind, settings);
if (grant_option && (kind == Kind::GRANT))
settings.ostr << (settings.hilite ? hilite_keyword : "") << " WITH GRANT OPTION" << (settings.hilite ? hilite_none : "");
......
......@@ -9,8 +9,8 @@ namespace DB
class ASTRoleList;
/** GRANT access_type[(column_name [,...])] [,...] ON {db.table|db.*|*.*|table|*} TO user_name
* REVOKE access_type[(column_name [,...])] [,...] ON {db.table|db.*|*.*|table|*} TO user_name
/** GRANT access_type[(column_name [,...])] [,...] ON {db.table|db.*|*.*|table|*} TO {user_name | CURRENT_USER} [,...] [WITH GRANT OPTION]
* REVOKE access_type[(column_name [,...])] [,...] ON {db.table|db.*|*.*|table|*} FROM {user_name | CURRENT_USER} [,...] | ALL | ALL EXCEPT {user_name | CURRENT_USER} [,...]
*/
class ASTGrantQuery : public IAST
{
......
......@@ -13,43 +13,46 @@ void ASTRoleList::formatImpl(const FormatSettings & settings, FormatState &, For
}
bool need_comma = false;
if (current_user)
if (all)
{
if (std::exchange(need_comma, true))
settings.ostr << ", ";
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << "CURRENT_USER" << (settings.hilite ? IAST::hilite_none : "");
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << "ALL" << (settings.hilite ? IAST::hilite_none : "");
}
for (auto & role : roles)
else
{
if (std::exchange(need_comma, true))
settings.ostr << ", ";
settings.ostr << backQuoteIfNeed(role);
for (auto & role : names)
{
if (std::exchange(need_comma, true))
settings.ostr << ", ";
settings.ostr << backQuoteIfNeed(role);
}
if (current_user)
{
if (std::exchange(need_comma, true))
settings.ostr << ", ";
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << "CURRENT_USER" << (settings.hilite ? IAST::hilite_none : "");
}
}
if (all_roles)
if (except_current_user || !except_names.empty())
{
if (std::exchange(need_comma, true))
settings.ostr << ", ";
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << "ALL" << (settings.hilite ? IAST::hilite_none : "");
if (except_current_user || !except_roles.empty())
{
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " EXCEPT " << (settings.hilite ? IAST::hilite_none : "");
need_comma = false;
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " EXCEPT " << (settings.hilite ? IAST::hilite_none : "");
need_comma = false;
if (except_current_user)
{
if (std::exchange(need_comma, true))
settings.ostr << ", ";
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << "CURRENT_USER" << (settings.hilite ? IAST::hilite_none : "");
}
for (auto & except_role : except_names)
{
if (std::exchange(need_comma, true))
settings.ostr << ", ";
settings.ostr << backQuoteIfNeed(except_role);
}
for (auto & except_role : except_roles)
{
if (std::exchange(need_comma, true))
settings.ostr << ", ";
settings.ostr << backQuoteIfNeed(except_role);
}
if (except_current_user)
{
if (std::exchange(need_comma, true))
settings.ostr << ", ";
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << "CURRENT_USER" << (settings.hilite ? IAST::hilite_none : "");
}
}
}
......
......@@ -10,13 +10,13 @@ namespace DB
class ASTRoleList : public IAST
{
public:
Strings roles;
Strings names;
bool current_user = false;
bool all_roles = false;
Strings except_roles;
bool all = false;
Strings except_names;
bool except_current_user = false;
bool empty() const { return roles.empty() && !current_user && !all_roles; }
bool empty() const { return names.empty() && !current_user && !all; }
String getID(char) const override { return "RoleList"; }
ASTPtr clone() const override { return std::make_shared<ASTRoleList>(*this); }
......
......@@ -46,7 +46,8 @@ void ASTShowCreateAccessEntityQuery::formatQueryImpl(const FormatSettings & sett
<< (settings.hilite ? hilite_none : "");
if ((kind == Kind::USER) && current_user)
settings.ostr << (settings.hilite ? hilite_keyword : "") << " CURRENT_USER" << (settings.hilite ? hilite_none : "");
{
}
else if ((kind == Kind::QUOTA) && current_quota)
settings.ostr << (settings.hilite ? hilite_keyword : "") << " CURRENT" << (settings.hilite ? hilite_none : "");
else if (kind == Kind::ROW_POLICY)
......
......@@ -18,13 +18,11 @@ ASTPtr ASTShowGrantsQuery::clone() const
void ASTShowGrantsQuery::formatQueryImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const
{
settings.ostr << (settings.hilite ? hilite_keyword : "") << "SHOW GRANTS FOR "
settings.ostr << (settings.hilite ? hilite_keyword : "") << "SHOW GRANTS"
<< (settings.hilite ? hilite_none : "");
if (current_user)
settings.ostr << (settings.hilite ? hilite_keyword : "") << "CURRENT_USER"
<< (settings.hilite ? hilite_none : "");
else
settings.ostr << backQuoteIfNeed(name);
if (!current_user)
settings.ostr << (settings.hilite ? hilite_keyword : "") << " FOR " << (settings.hilite ? hilite_none : "")
<< backQuoteIfNeed(name);
}
}
......@@ -25,13 +25,10 @@ namespace
using ResourceType = Quota::ResourceType;
using ResourceAmount = Quota::ResourceAmount;
bool parseRenameTo(IParserBase::Pos & pos, Expected & expected, String & new_name, bool alter)
bool parseRenameTo(IParserBase::Pos & pos, Expected & expected, String & new_name)
{
return IParserBase::wrapParseImpl(pos, [&]
{
if (!new_name.empty() || !alter)
return false;
if (!ParserKeyword{"RENAME TO"}.ignore(pos, expected))
return false;
......@@ -43,9 +40,6 @@ namespace
{
return IParserBase::wrapParseImpl(pos, [&]
{
if (key_type)
return false;
if (!ParserKeyword{"KEYED BY"}.ignore(pos, expected))
return false;
......@@ -123,7 +117,7 @@ namespace
});
}
bool parseLimits(IParserBase::Pos & pos, Expected & expected, ASTCreateQuotaQuery::Limits & limits, bool alter)
bool parseLimits(IParserBase::Pos & pos, Expected & expected, bool alter, ASTCreateQuotaQuery::Limits & limits)
{
return IParserBase::wrapParseImpl(pos, [&]
{
......@@ -173,15 +167,19 @@ namespace
});
}
bool parseAllLimits(IParserBase::Pos & pos, Expected & expected, std::vector<ASTCreateQuotaQuery::Limits> & all_limits, bool alter)
bool parseAllLimits(IParserBase::Pos & pos, Expected & expected, bool alter, std::vector<ASTCreateQuotaQuery::Limits> & all_limits)
{
return IParserBase::wrapParseImpl(pos, [&]
{
size_t old_size = all_limits.size();
do
{
ASTCreateQuotaQuery::Limits limits;
if (!parseLimits(pos, expected, limits, alter))
if (!parseLimits(pos, expected, alter, limits))
{
all_limits.resize(old_size);
return false;
}
all_limits.push_back(limits);
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
......@@ -189,7 +187,7 @@ namespace
});
}
bool parseRoles(IParserBase::Pos & pos, Expected & expected, std::shared_ptr<ASTRoleList> & roles)
bool parseToRoles(IParserBase::Pos & pos, Expected & expected, std::shared_ptr<ASTRoleList> & roles)
{
return IParserBase::wrapParseImpl(pos, [&]
{
......@@ -239,9 +237,22 @@ bool ParserCreateQuotaQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expe
std::vector<ASTCreateQuotaQuery::Limits> all_limits;
std::shared_ptr<ASTRoleList> roles;
while (parseRenameTo(pos, expected, new_name, alter) || parseKeyType(pos, expected, key_type)
|| parseAllLimits(pos, expected, all_limits, alter) || parseRoles(pos, expected, roles))
;
while (true)
{
if (alter && new_name.empty() && parseRenameTo(pos, expected, new_name))
continue;
if (!key_type && parseKeyType(pos, expected, key_type))
continue;
if (parseAllLimits(pos, expected, alter, all_limits))
continue;
if (!roles && parseToRoles(pos, expected, roles))
continue;
break;
}
auto query = std::make_shared<ASTCreateQuotaQuery>();
node = query;
......
......@@ -21,13 +21,10 @@ namespace
{
using ConditionIndex = RowPolicy::ConditionIndex;
bool parseRenameTo(IParserBase::Pos & pos, Expected & expected, String & new_policy_name, bool alter)
bool parseRenameTo(IParserBase::Pos & pos, Expected & expected, String & new_policy_name)
{
return IParserBase::wrapParseImpl(pos, [&]
{
if (!new_policy_name.empty() || !alter)
return false;
if (!ParserKeyword{"RENAME TO"}.ignore(pos, expected))
return false;
......@@ -35,46 +32,48 @@ namespace
});
}
bool parseIsRestrictive(IParserBase::Pos & pos, Expected & expected, std::optional<bool> & is_restrictive)
bool parseAsRestrictiveOrPermissive(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 true;
}
if (!ParserKeyword{"PERMISSIVE"}.ignore(pos, expected))
return false;
is_restrictive = 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))
return IParserBase::wrapParseImpl(pos, [&]
{
if (ParserKeyword("NONE").ignore(pos, expected))
{
expr = nullptr;
return true;
}
ParserExpression parser;
ASTPtr x;
if (!parser.parse(pos, x, expected))
return false;
expr = x;
return true;
}
expr.reset();
return false;
});
}
bool parseConditions(IParserBase::Pos & pos, Expected & expected, std::vector<std::pair<ConditionIndex, ASTPtr>> & conditions, bool alter)
bool parseConditions(IParserBase::Pos & pos, Expected & expected, bool alter, std::vector<std::pair<ConditionIndex, ASTPtr>> & conditions)
{
return IParserBase::wrapParseImpl(pos, [&]
{
......@@ -171,29 +170,32 @@ namespace
});
}
bool parseMultipleConditions(IParserBase::Pos & pos, Expected & expected, std::vector<std::pair<ConditionIndex, ASTPtr>> & conditions, bool alter)
bool parseMultipleConditions(IParserBase::Pos & pos, Expected & expected, bool alter, std::vector<std::pair<ConditionIndex, ASTPtr>> & conditions)
{
return IParserBase::wrapParseImpl(pos, [&]
{
std::vector<std::pair<ConditionIndex, ASTPtr>> res_conditions;
do
{
if (!parseConditions(pos, expected, conditions, alter))
if (!parseConditions(pos, expected, alter, res_conditions))
return false;
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
conditions = std::move(res_conditions);
return true;
});
}
bool parseRoles(IParserBase::Pos & pos, Expected & expected, std::shared_ptr<ASTRoleList> & roles)
bool parseToRoles(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))
ASTPtr ast;
if (roles || !ParserKeyword{"TO"}.ignore(pos, expected) || !ParserRoleList{}.parse(pos, ast, expected))
return false;
roles = std::static_pointer_cast<ASTRoleList>(node);
roles = std::static_pointer_cast<ASTRoleList>(ast);
return true;
});
}
......@@ -239,9 +241,22 @@ bool ParserCreateRowPolicyQuery::parseImpl(Pos & pos, ASTPtr & node, Expected &
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))
;
while (true)
{
if (alter && new_policy_name.empty() && parseRenameTo(pos, expected, new_policy_name))
continue;
if (!is_restrictive && parseAsRestrictiveOrPermissive(pos, expected, is_restrictive))
continue;
if (parseMultipleConditions(pos, expected, alter, conditions))
continue;
if (!roles && parseToRoles(pos, expected, roles))
continue;
break;
}
auto query = std::make_shared<ASTCreateRowPolicyQuery>();
node = query;
......
......@@ -24,9 +24,6 @@ namespace
{
return IParserBase::wrapParseImpl(pos, [&]
{
if (!new_name.empty())
return false;
if (!ParserKeyword{"RENAME TO"}.ignore(pos, expected))
return false;
......@@ -35,14 +32,20 @@ namespace
}
bool parsePassword(IParserBase::Pos & pos, Expected & expected, String & password)
bool parseByPassword(IParserBase::Pos & pos, Expected & expected, String & password)
{
ASTPtr ast;
if (!ParserStringLiteral{}.parse(pos, ast, expected))
return false;
return IParserBase::wrapParseImpl(pos, [&]
{
if (!ParserKeyword{"BY"}.ignore(pos, expected))
return false;
password = ast->as<const ASTLiteral &>().value.safeGet<String>();
return true;
ASTPtr ast;
if (!ParserStringLiteral{}.parse(pos, ast, expected))
return false;
password = ast->as<const ASTLiteral &>().value.safeGet<String>();
return true;
});
}
......@@ -50,70 +53,79 @@ namespace
{
return IParserBase::wrapParseImpl(pos, [&]
{
if (authentication)
return false;
if (!ParserKeyword{"IDENTIFIED"}.ignore(pos, expected))
return false;
if (ParserKeyword{"WITH"}.ignore(pos, expected))
if (!ParserKeyword{"WITH"}.ignore(pos, expected))
{
if (ParserKeyword{"NO_PASSWORD"}.ignore(pos, expected))
{
authentication = Authentication{Authentication::NO_PASSWORD};
}
else if (ParserKeyword{"PLAINTEXT_PASSWORD"}.ignore(pos, expected))
{
String password;
if (!ParserKeyword{"BY"}.ignore(pos, expected) || !parsePassword(pos, expected, password))
return false;
authentication = Authentication{Authentication::PLAINTEXT_PASSWORD};
authentication->setPassword(password);
}
else if (ParserKeyword{"SHA256_PASSWORD"}.ignore(pos, expected))
{
String password;
if (!ParserKeyword{"BY"}.ignore(pos, expected) || !parsePassword(pos, expected, password))
return false;
authentication = Authentication{Authentication::SHA256_PASSWORD};
authentication->setPassword(password);
}
else if (ParserKeyword{"SHA256_HASH"}.ignore(pos, expected))
{
String hash;
if (!ParserKeyword{"BY"}.ignore(pos, expected) || !parsePassword(pos, expected, hash))
return false;
authentication = Authentication{Authentication::SHA256_PASSWORD};
authentication->setPasswordHashHex(hash);
}
else if (ParserKeyword{"DOUBLE_SHA1_PASSWORD"}.ignore(pos, expected))
{
String password;
if (!ParserKeyword{"BY"}.ignore(pos, expected) || !parsePassword(pos, expected, password))
return false;
authentication = Authentication{Authentication::DOUBLE_SHA1_PASSWORD};
authentication->setPassword(password);
}
else if (ParserKeyword{"DOUBLE_SHA1_HASH"}.ignore(pos, expected))
{
String hash;
if (!ParserKeyword{"BY"}.ignore(pos, expected) || !parsePassword(pos, expected, hash))
return false;
authentication = Authentication{Authentication::DOUBLE_SHA1_PASSWORD};
authentication->setPasswordHashHex(hash);
}
else
String password;
if (!parseByPassword(pos, expected, password))
return false;
authentication = Authentication{Authentication::SHA256_PASSWORD};
authentication->setPassword(password);
return true;
}
else
if (ParserKeyword{"PLAINTEXT_PASSWORD"}.ignore(pos, expected))
{
String password;
if (!ParserKeyword{"BY"}.ignore(pos, expected) || !parsePassword(pos, expected, password))
if (!parseByPassword(pos, expected, password))
return false;
authentication = Authentication{Authentication::PLAINTEXT_PASSWORD};
authentication->setPassword(password);
return true;
}
if (ParserKeyword{"SHA256_PASSWORD"}.ignore(pos, expected))
{
String password;
if (!parseByPassword(pos, expected, password))
return false;
authentication = Authentication{Authentication::SHA256_PASSWORD};
authentication->setPassword(password);
return true;
}
if (ParserKeyword{"SHA256_HASH"}.ignore(pos, expected))
{
String hash;
if (!parseByPassword(pos, expected, hash))
return false;
authentication = Authentication{Authentication::SHA256_PASSWORD};
authentication->setPasswordHashHex(hash);
return true;
}
if (ParserKeyword{"DOUBLE_SHA1_PASSWORD"}.ignore(pos, expected))
{
String password;
if (!parseByPassword(pos, expected, password))
return false;
authentication = Authentication{Authentication::DOUBLE_SHA1_PASSWORD};
authentication->setPassword(password);
return true;
}
if (ParserKeyword{"DOUBLE_SHA1_HASH"}.ignore(pos, expected))
{
String hash;
if (!parseByPassword(pos, expected, hash))
return false;
authentication = Authentication{Authentication::DOUBLE_SHA1_PASSWORD};
authentication->setPasswordHashHex(hash);
return true;
}
if (!ParserKeyword{"NO_PASSWORD"}.ignore(pos, expected))
return false;
authentication = Authentication{Authentication::NO_PASSWORD};
return true;
});
}
......@@ -144,13 +156,12 @@ namespace
return true;
}
AllowedClientHosts new_hosts;
do
{
if (ParserKeyword{"LOCAL"}.ignore(pos, expected))
{
if (!hosts)
hosts.emplace();
hosts->addLocalHost();
new_hosts.addLocalHost();
}
else if (ParserKeyword{"NAME REGEXP"}.ignore(pos, expected))
{
......@@ -158,9 +169,7 @@ namespace
if (!ParserStringLiteral{}.parse(pos, ast, expected))
return false;
if (!hosts)
hosts.emplace();
hosts->addNameRegexp(ast->as<const ASTLiteral &>().value.safeGet<String>());
new_hosts.addNameRegexp(ast->as<const ASTLiteral &>().value.safeGet<String>());
}
else if (ParserKeyword{"NAME"}.ignore(pos, expected))
{
......@@ -168,9 +177,7 @@ namespace
if (!ParserStringLiteral{}.parse(pos, ast, expected))
return false;
if (!hosts)
hosts.emplace();
hosts->addName(ast->as<const ASTLiteral &>().value.safeGet<String>());
new_hosts.addName(ast->as<const ASTLiteral &>().value.safeGet<String>());
}
else if (ParserKeyword{"IP"}.ignore(pos, expected))
{
......@@ -178,9 +185,7 @@ namespace
if (!ParserStringLiteral{}.parse(pos, ast, expected))
return false;
if (!hosts)
hosts.emplace();
hosts->addSubnet(ast->as<const ASTLiteral &>().value.safeGet<String>());
new_hosts.addSubnet(ast->as<const ASTLiteral &>().value.safeGet<String>());
}
else if (ParserKeyword{"LIKE"}.ignore(pos, expected))
{
......@@ -188,14 +193,16 @@ namespace
if (!ParserStringLiteral{}.parse(pos, ast, expected))
return false;
if (!hosts)
hosts.emplace();
hosts->addLikePattern(ast->as<const ASTLiteral &>().value.safeGet<String>());
new_hosts.addLikePattern(ast->as<const ASTLiteral &>().value.safeGet<String>());
}
else
return false;
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
if (!hosts)
hosts.emplace();
hosts->add(new_hosts);
return true;
});
}
......@@ -205,9 +212,6 @@ namespace
{
return IParserBase::wrapParseImpl(pos, [&]
{
if (profile)
return false;
if (!ParserKeyword{"PROFILE"}.ignore(pos, expected))
return false;
......@@ -261,13 +265,28 @@ bool ParserCreateUserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
std::optional<AllowedClientHosts> remove_hosts;
std::optional<String> profile;
while (parseAuthentication(pos, expected, authentication)
|| parseHosts(pos, expected, nullptr, hosts)
|| parseProfileName(pos, expected, profile)
|| (alter && parseRenameTo(pos, expected, new_name, new_host_pattern))
|| (alter && parseHosts(pos, expected, "ADD", add_hosts))
|| (alter && parseHosts(pos, expected, "REMOVE", remove_hosts)))
;
while (true)
{
if (!authentication && parseAuthentication(pos, expected, authentication))
continue;
if (parseHosts(pos, expected, nullptr, hosts))
continue;
if (!profile && parseProfileName(pos, expected, profile))
continue;
if (alter)
{
if (new_name.empty() && parseRenameTo(pos, expected, new_name, new_host_pattern))
continue;
if (parseHosts(pos, expected, "ADD", add_hosts) || parseHosts(pos, expected, "REMOVE", remove_hosts))
continue;
}
break;
}
if (!hosts)
{
......
......@@ -13,47 +13,64 @@ namespace
{
bool parseNames(IParserBase::Pos & pos, Expected & expected, Strings & names)
{
do
return IParserBase::wrapParseImpl(pos, [&]
{
String name;
if (!parseIdentifierOrStringLiteral(pos, expected, name))
return false;
names.push_back(std::move(name));
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
return true;
Strings res_names;
do
{
String name;
if (!parseIdentifierOrStringLiteral(pos, expected, name))
return false;
res_names.push_back(std::move(name));
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
names = std::move(res_names);
return true;
});
}
bool parseRowPolicyNames(IParserBase::Pos & pos, Expected & expected, std::vector<RowPolicy::FullNameParts> & row_policies_names)
bool parseRowPolicyNames(IParserBase::Pos & pos, Expected & expected, std::vector<RowPolicy::FullNameParts> & names)
{
do
return IParserBase::wrapParseImpl(pos, [&]
{
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));
return true;
std::vector<RowPolicy::FullNameParts> res_names;
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)
res_names.push_back({database, table_name, policy_name});
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
names = std::move(res_names);
return true;
});
}
bool parseUserNames(IParserBase::Pos & pos, Expected & expected, Strings & names)
{
do
return IParserBase::wrapParseImpl(pos, [&]
{
String name;
if (!parseUserName(pos, expected, name))
return false;
names.push_back(std::move(name));
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
return true;
Strings res_names;
do
{
String name;
if (!parseUserName(pos, expected, name))
return false;
res_names.emplace_back(std::move(name));
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
names = std::move(res_names);
return true;
});
}
}
......
......@@ -12,9 +12,18 @@ namespace DB
{
namespace
{
bool parseRoundBrackets(IParser::Pos & pos, Expected & expected)
{
return IParserBase::wrapParseImpl(pos, [&]
{
return ParserToken{TokenType::OpeningRoundBracket}.ignore(pos, expected)
&& ParserToken{TokenType::ClosingRoundBracket}.ignore(pos, expected);
});
}
bool parseAccessFlags(IParser::Pos & pos, Expected & expected, AccessFlags & access_flags)
{
auto is_one_of_access_type_words = [](IParser::Pos & pos_)
static constexpr auto is_one_of_access_type_words = [](IParser::Pos & pos_)
{
if (pos_->type != TokenType::BareWord)
return false;
......@@ -24,86 +33,97 @@ namespace
return true;
};
if (!is_one_of_access_type_words(pos))
{
expected.add(pos, "access type");
return false;
}
expected.add(pos, "access type");
String str;
do
{
if (!str.empty())
str += " ";
std::string_view word{pos->begin, pos->size()};
str += std::string_view(pos->begin, pos->size());
++pos;
}
while (is_one_of_access_type_words(pos));
if (pos->type == TokenType::OpeningRoundBracket)
return IParserBase::wrapParseImpl(pos, [&]
{
auto old_pos = pos;
++pos;
if (pos->type == TokenType::ClosingRoundBracket)
if (!is_one_of_access_type_words(pos))
return false;
String str;
do
{
if (!str.empty())
str += " ";
std::string_view word{pos->begin, pos->size()};
str += std::string_view(pos->begin, pos->size());
++pos;
str += "()";
}
else
pos = old_pos;
}
while (is_one_of_access_type_words(pos));
access_flags = AccessFlags{str};
return true;
try
{
access_flags = AccessFlags{str};
}
catch (...)
{
return false;
}
parseRoundBrackets(pos, expected);
return true;
});
}
bool parseColumnNames(IParser::Pos & pos, Expected & expected, Strings & columns)
{
if (!ParserToken{TokenType::OpeningRoundBracket}.ignore(pos, expected))
return false;
do
return IParserBase::wrapParseImpl(pos, [&]
{
ASTPtr column_ast;
if (!ParserIdentifier().parse(pos, column_ast, expected))
if (!ParserToken{TokenType::OpeningRoundBracket}.ignore(pos, expected))
return false;
Strings res_columns;
do
{
ASTPtr column_ast;
if (!ParserIdentifier().parse(pos, column_ast, expected))
return false;
res_columns.emplace_back(getIdentifierName(column_ast));
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
if (!ParserToken{TokenType::ClosingRoundBracket}.ignore(pos, expected))
return false;
columns.push_back(getIdentifierName(column_ast));
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
return ParserToken{TokenType::ClosingRoundBracket}.ignore(pos, expected);
columns = std::move(res_columns);
return true;
});
}
bool parseDatabaseAndTableNameOrMaybeAsterisks(
IParser::Pos & pos, Expected & expected, String & database_name, bool & any_database, String & table_name, bool & any_table)
{
ASTPtr ast[2];
if (ParserToken{TokenType::Asterisk}.ignore(pos, expected))
return IParserBase::wrapParseImpl(pos, [&]
{
if (ParserToken{TokenType::Dot}.ignore(pos, expected))
ASTPtr ast[2];
if (ParserToken{TokenType::Asterisk}.ignore(pos, expected))
{
if (!ParserToken{TokenType::Asterisk}.ignore(pos, expected))
return false;
if (ParserToken{TokenType::Dot}.ignore(pos, expected))
{
if (!ParserToken{TokenType::Asterisk}.ignore(pos, expected))
return false;
/// *.* (any table in any database)
any_database = true;
database_name.clear();
any_table = true;
table_name.clear();
return true;
}
/// *.* (any table in any database)
any_database = true;
any_table = true;
return true;
}
else
{
/// * (any table in the current database)
any_database = false;
database_name = "";
database_name.clear();
any_table = true;
table_name.clear();
return true;
}
}
else if (ParserIdentifier().parse(pos, ast[0], expected))
{
if (!ParserIdentifier().parse(pos, ast[0], expected))
return false;
if (ParserToken{TokenType::Dot}.ignore(pos, expected))
{
if (ParserToken{TokenType::Asterisk}.ignore(pos, expected))
......@@ -112,31 +132,103 @@ namespace
any_database = false;
database_name = getIdentifierName(ast[0]);
any_table = true;
table_name.clear();
return true;
}
else if (ParserIdentifier().parse(pos, ast[1], expected))
if (!ParserIdentifier().parse(pos, ast[1], expected))
return false;
/// <database_name>.<table_name>
any_database = false;
database_name = getIdentifierName(ast[0]);
any_table = false;
table_name = getIdentifierName(ast[1]);
return true;
}
/// <table_name> - the current database, specified table
any_database = false;
database_name.clear();
any_table = false;
table_name = getIdentifierName(ast[0]);
return true;
});
}
bool parseAccessRightsElements(IParser::Pos & pos, Expected & expected, AccessRightsElements & elements)
{
return IParserBase::wrapParseImpl(pos, [&]
{
AccessRightsElements res_elements;
do
{
std::vector<std::pair<AccessFlags, Strings>> access_and_columns;
do
{
AccessFlags access_flags;
if (!parseAccessFlags(pos, expected, access_flags))
return false;
Strings columns;
parseColumnNames(pos, expected, columns);
access_and_columns.emplace_back(access_flags, std::move(columns));
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
if (!ParserKeyword{"ON"}.ignore(pos, expected))
return false;
String database_name, table_name;
bool any_database = false, any_table = false;
if (!parseDatabaseAndTableNameOrMaybeAsterisks(pos, expected, database_name, any_database, table_name, any_table))
return false;
for (auto & [access_flags, columns] : access_and_columns)
{
/// <database_name>.<table_name>
any_database = false;
database_name = getIdentifierName(ast[0]);
any_table = false;
table_name = getIdentifierName(ast[1]);
return true;
AccessRightsElement element;
element.access_flags = access_flags;
element.any_column = columns.empty();
element.columns = std::move(columns);
element.any_database = any_database;
element.database = database_name;
element.any_table = any_table;
element.table = table_name;
res_elements.emplace_back(std::move(element));
}
else
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
elements = std::move(res_elements);
return true;
});
}
bool parseToRoles(IParser::Pos & pos, Expected & expected, ASTGrantQuery::Kind kind, std::shared_ptr<ASTRoleList> & to_roles)
{
return IParserBase::wrapParseImpl(pos, [&]
{
using Kind = ASTGrantQuery::Kind;
if (kind == Kind::GRANT)
{
if (!ParserKeyword{"TO"}.ignore(pos, expected))
return false;
}
else
{
/// <table_name> - the current database, specified table
any_database = false;
database_name = "";
table_name = getIdentifierName(ast[0]);
return true;
if (!ParserKeyword{"FROM"}.ignore(pos, expected))
return false;
}
}
else
return false;
ASTPtr ast;
if (!ParserRoleList{false, false}.parse(pos, ast, expected))
return false;
to_roles = typeid_cast<std::shared_ptr<ASTRoleList>>(ast);
return true;
});
}
}
......@@ -160,56 +252,8 @@ bool ParserGrantQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
}
AccessRightsElements elements;
do
{
std::vector<std::pair<AccessFlags, Strings>> access_and_columns;
do
{
AccessFlags access_flags;
if (!parseAccessFlags(pos, expected, access_flags))
return false;
Strings columns;
parseColumnNames(pos, expected, columns);
access_and_columns.emplace_back(access_flags, std::move(columns));
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
if (!ParserKeyword{"ON"}.ignore(pos, expected))
return false;
String database_name, table_name;
bool any_database = false, any_table = false;
if (!parseDatabaseAndTableNameOrMaybeAsterisks(pos, expected, database_name, any_database, table_name, any_table))
return false;
for (auto & [access_flags, columns] : access_and_columns)
{
AccessRightsElement element;
element.access_flags = access_flags;
element.any_column = columns.empty();
element.columns = std::move(columns);
element.any_database = any_database;
element.database = database_name;
element.any_table = any_table;
element.table = table_name;
elements.emplace_back(std::move(element));
}
}
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
ASTPtr to_roles;
if (kind == Kind::GRANT)
{
if (!ParserKeyword{"TO"}.ignore(pos, expected))
return false;
}
else
{
if (!ParserKeyword{"FROM"}.ignore(pos, expected))
return false;
}
if (!ParserRoleList{}.parse(pos, to_roles, expected))
std::shared_ptr<ASTRoleList> to_roles;
if (!parseAccessRightsElements(pos, expected, elements) && !parseToRoles(pos, expected, kind, to_roles))
return false;
if (kind == Kind::GRANT)
......@@ -218,13 +262,12 @@ bool ParserGrantQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
grant_option = true;
}
auto query = std::make_shared<ASTGrantQuery>();
node = query;
query->kind = kind;
query->access_rights_elements = std::move(elements);
query->to_roles = std::static_pointer_cast<ASTRoleList>(to_roles);
query->to_roles = std::move(to_roles);
query->grant_option = grant_option;
return true;
......
......@@ -6,8 +6,8 @@
namespace DB
{
/** Parses queries like
* GRANT access_type[(column_name [,...])] [,...] ON {db.table|db.*|*.*|table|*} TO user_name
* REVOKE access_type[(column_name [,...])] [,...] ON {db.table|db.*|*.*|table|*} TO user_name
* GRANT access_type[(column_name [,...])] [,...] ON {db.table|db.*|*.*|table|*} TO {user_name | CURRENT_USER} [,...] [WITH GRANT OPTION]
* REVOKE access_type[(column_name [,...])] [,...] ON {db.table|db.*|*.*|table|*} FROM {user_name | CURRENT_USER} [,...] | ALL | ALL EXCEPT {user_name | CURRENT_USER} [,...]
*/
class ParserGrantQuery : public IParserBase
{
......
......@@ -7,69 +7,93 @@
namespace DB
{
bool ParserRoleList::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
namespace
{
Strings roles;
bool current_user = false;
bool all_roles = false;
Strings except_roles;
bool except_current_user = false;
bool except_mode = false;
while (true)
bool parseRoleListBeforeExcept(IParserBase::Pos & pos, Expected & expected, bool * all, bool * current_user, Strings & names)
{
if (ParserKeyword{"NONE"}.ignore(pos, expected))
{
}
else if (ParserKeyword{"CURRENT_USER"}.ignore(pos, expected) ||
ParserKeyword{"currentUser"}.ignore(pos, expected))
{
if (ParserToken{TokenType::OpeningRoundBracket}.ignore(pos, expected))
{
if (!ParserToken{TokenType::ClosingRoundBracket}.ignore(pos, expected))
return false;
}
if (except_mode && !current_user)
except_current_user = true;
else
current_user = true;
}
else if (ParserKeyword{"ALL"}.ignore(pos, expected))
return IParserBase::wrapParseImpl(pos, [&]
{
all_roles = true;
if (ParserKeyword{"EXCEPT"}.ignore(pos, expected))
bool res_all = false;
bool res_current_user = false;
Strings res_names;
while (true)
{
except_mode = true;
continue;
if (ParserKeyword{"NONE"}.ignore(pos, expected))
{
}
else if (
current_user && (ParserKeyword{"CURRENT_USER"}.ignore(pos, expected) || ParserKeyword{"currentUser"}.ignore(pos, expected)))
{
if (ParserToken{TokenType::OpeningRoundBracket}.ignore(pos, expected))
{
if (!ParserToken{TokenType::ClosingRoundBracket}.ignore(pos, expected))
return false;
}
res_current_user = true;
}
else if (all && ParserKeyword{"ALL"}.ignore(pos, expected))
{
res_all = true;
}
else
{
String name;
if (!parseUserName(pos, expected, name))
return false;
res_names.push_back(name);
}
if (!ParserToken{TokenType::Comma}.ignore(pos, expected))
break;
}
}
else
{
String name;
if (!parseUserName(pos, expected, name))
return false;
if (except_mode && (boost::range::find(roles, name) == roles.end()))
except_roles.push_back(name);
else
roles.push_back(name);
}
if (!ParserToken{TokenType::Comma}.ignore(pos, expected))
break;
if (all)
*all = res_all;
if (current_user)
*current_user = res_current_user;
names = std::move(res_names);
return true;
});
}
if (all_roles)
bool parseRoleListExcept(IParserBase::Pos & pos, Expected & expected, bool * except_current_user, Strings & except_names)
{
current_user = false;
roles.clear();
return IParserBase::wrapParseImpl(pos, [&]
{
if (!ParserKeyword{"EXCEPT"}.ignore(pos, expected))
return false;
return parseRoleListBeforeExcept(pos, expected, nullptr, except_current_user, except_names);
});
}
}
ParserRoleList::ParserRoleList(bool allow_all_, bool allow_current_user_)
: allow_all(allow_all_), allow_current_user(allow_current_user_) {}
bool ParserRoleList::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
{
Strings names;
bool current_user = false;
bool all = false;
Strings except_names;
bool except_current_user = false;
if (!parseRoleListBeforeExcept(pos, expected, (allow_all ? &all : nullptr), (allow_current_user ? &current_user : nullptr), names))
return false;
parseRoleListExcept(pos, expected, (allow_current_user ? &except_current_user : nullptr), except_names);
if (all)
names.clear();
auto result = std::make_shared<ASTRoleList>();
result->roles = std::move(roles);
result->names = std::move(names);
result->current_user = current_user;
result->all_roles = all_roles;
result->except_roles = std::move(except_roles);
result->all = all;
result->except_names = std::move(except_names);
result->except_current_user = except_current_user;
node = result;
return true;
......
......@@ -10,9 +10,16 @@ namespace DB
*/
class ParserRoleList : public IParserBase
{
public:
ParserRoleList(bool allow_all_ = true, bool allow_current_user_ = true);
protected:
const char * getName() const override { return "RoleList"; }
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
private:
bool allow_all;
bool allow_current_user;
};
}
......@@ -68,6 +68,7 @@ bool ParserShowCreateAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expe
query->name = std::move(name);
query->current_quota = current_quota;
query->current_user = current_user;
query->row_policy_name = std::move(row_policy_name);
return true;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册