2019-12-01 22:01:05 +00:00
|
|
|
#include <Parsers/ParserCreateQuotaQuery.h>
|
|
|
|
#include <Parsers/ASTCreateQuotaQuery.h>
|
|
|
|
#include <Parsers/CommonParsers.h>
|
|
|
|
#include <Parsers/parseIntervalKind.h>
|
|
|
|
#include <Parsers/parseIdentifierOrStringLiteral.h>
|
2020-03-07 17:37:38 +00:00
|
|
|
#include <Parsers/ParserExtendedRoleSet.h>
|
2019-12-01 22:01:05 +00:00
|
|
|
#include <Parsers/ExpressionElementParsers.h>
|
|
|
|
#include <Parsers/ASTLiteral.h>
|
2020-03-07 17:37:38 +00:00
|
|
|
#include <Parsers/ASTExtendedRoleSet.h>
|
2019-12-01 22:01:05 +00:00
|
|
|
#include <ext/range.h>
|
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int SYNTAX_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
using KeyType = Quota::KeyType;
|
|
|
|
using ResourceType = Quota::ResourceType;
|
|
|
|
using ResourceAmount = Quota::ResourceAmount;
|
|
|
|
|
2020-02-10 15:24:33 +00:00
|
|
|
bool parseRenameTo(IParserBase::Pos & pos, Expected & expected, String & new_name)
|
2019-12-01 22:01:05 +00:00
|
|
|
{
|
|
|
|
return IParserBase::wrapParseImpl(pos, [&]
|
|
|
|
{
|
|
|
|
if (!ParserKeyword{"RENAME TO"}.ignore(pos, expected))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return parseIdentifierOrStringLiteral(pos, expected, new_name);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
bool parseKeyType(IParserBase::Pos & pos, Expected & expected, std::optional<Quota::KeyType> & key_type)
|
|
|
|
{
|
|
|
|
return IParserBase::wrapParseImpl(pos, [&]
|
|
|
|
{
|
|
|
|
if (!ParserKeyword{"KEYED BY"}.ignore(pos, expected))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ASTPtr key_type_ast;
|
|
|
|
if (!ParserStringLiteral().parse(pos, key_type_ast, expected))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const String & key_type_str = key_type_ast->as<ASTLiteral &>().value.safeGet<const String &>();
|
|
|
|
for (auto kt : ext::range_with_static_cast<Quota::KeyType>(Quota::MAX_KEY_TYPE))
|
|
|
|
if (boost::iequals(Quota::getNameOfKeyType(kt), key_type_str))
|
|
|
|
{
|
|
|
|
key_type = kt;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
String all_key_types_str;
|
|
|
|
for (auto kt : ext::range_with_static_cast<Quota::KeyType>(Quota::MAX_KEY_TYPE))
|
|
|
|
all_key_types_str += String(all_key_types_str.empty() ? "" : ", ") + "'" + Quota::getNameOfKeyType(kt) + "'";
|
|
|
|
String msg = "Quota cannot be keyed by '" + key_type_str + "'. Expected one of these literals: " + all_key_types_str;
|
|
|
|
throw Exception(msg, ErrorCodes::SYNTAX_ERROR);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
bool parseLimit(IParserBase::Pos & pos, Expected & expected, ResourceType & resource_type, ResourceAmount & max)
|
|
|
|
{
|
|
|
|
return IParserBase::wrapParseImpl(pos, [&]
|
|
|
|
{
|
|
|
|
if (!ParserKeyword{"MAX"}.ignore(pos, expected))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bool resource_type_set = false;
|
|
|
|
for (auto rt : ext::range_with_static_cast<Quota::ResourceType>(Quota::MAX_RESOURCE_TYPE))
|
|
|
|
{
|
|
|
|
if (ParserKeyword{Quota::resourceTypeToKeyword(rt)}.ignore(pos, expected))
|
|
|
|
{
|
|
|
|
resource_type = rt;
|
|
|
|
resource_type_set = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!resource_type_set)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!ParserToken{TokenType::Equals}.ignore(pos, expected))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ASTPtr max_ast;
|
|
|
|
if (ParserNumber{}.parse(pos, max_ast, expected))
|
|
|
|
{
|
|
|
|
const Field & max_field = max_ast->as<ASTLiteral &>().value;
|
|
|
|
if (resource_type == Quota::EXECUTION_TIME)
|
|
|
|
max = Quota::secondsToExecutionTime(applyVisitor(FieldVisitorConvertToNumber<double>(), max_field));
|
|
|
|
else
|
|
|
|
max = applyVisitor(FieldVisitorConvertToNumber<ResourceAmount>(), max_field);
|
|
|
|
}
|
|
|
|
else if (ParserKeyword{"ANY"}.ignore(pos, expected))
|
|
|
|
{
|
|
|
|
max = Quota::UNLIMITED;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
bool parseCommaAndLimit(IParserBase::Pos & pos, Expected & expected, ResourceType & resource_type, ResourceAmount & max)
|
|
|
|
{
|
|
|
|
return IParserBase::wrapParseImpl(pos, [&]
|
|
|
|
{
|
|
|
|
if (!ParserToken{TokenType::Comma}.ignore(pos, expected))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return parseLimit(pos, expected, resource_type, max);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-02-10 15:24:33 +00:00
|
|
|
bool parseLimits(IParserBase::Pos & pos, Expected & expected, bool alter, ASTCreateQuotaQuery::Limits & limits)
|
2019-12-01 22:01:05 +00:00
|
|
|
{
|
|
|
|
return IParserBase::wrapParseImpl(pos, [&]
|
|
|
|
{
|
|
|
|
ASTCreateQuotaQuery::Limits new_limits;
|
|
|
|
if (!ParserKeyword{"FOR"}.ignore(pos, expected))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
new_limits.randomize_interval = ParserKeyword{"RANDOMIZED"}.ignore(pos, expected);
|
|
|
|
|
|
|
|
if (!ParserKeyword{"INTERVAL"}.ignore(pos, expected))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ASTPtr num_intervals_ast;
|
|
|
|
if (!ParserNumber{}.parse(pos, num_intervals_ast, expected))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
double num_intervals = applyVisitor(FieldVisitorConvertToNumber<double>(), num_intervals_ast->as<ASTLiteral &>().value);
|
|
|
|
|
|
|
|
IntervalKind interval_kind;
|
|
|
|
if (!parseIntervalKind(pos, expected, interval_kind))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
new_limits.duration = std::chrono::seconds(static_cast<UInt64>(num_intervals * interval_kind.toAvgSeconds()));
|
|
|
|
|
|
|
|
if (alter && ParserKeyword{"UNSET TRACKING"}.ignore(pos, expected))
|
|
|
|
{
|
|
|
|
new_limits.unset_tracking = true;
|
|
|
|
}
|
|
|
|
else if (ParserKeyword{"SET TRACKING"}.ignore(pos, expected) || ParserKeyword{"TRACKING"}.ignore(pos, expected))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ParserKeyword{"SET"}.ignore(pos, expected);
|
|
|
|
ResourceType resource_type;
|
|
|
|
ResourceAmount max;
|
|
|
|
if (!parseLimit(pos, expected, resource_type, max))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
new_limits.max[resource_type] = max;
|
|
|
|
while (parseCommaAndLimit(pos, expected, resource_type, max))
|
|
|
|
new_limits.max[resource_type] = max;
|
|
|
|
}
|
|
|
|
|
|
|
|
limits = new_limits;
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-02-10 15:24:33 +00:00
|
|
|
bool parseAllLimits(IParserBase::Pos & pos, Expected & expected, bool alter, std::vector<ASTCreateQuotaQuery::Limits> & all_limits)
|
2019-12-01 22:01:05 +00:00
|
|
|
{
|
|
|
|
return IParserBase::wrapParseImpl(pos, [&]
|
|
|
|
{
|
2020-02-10 15:24:33 +00:00
|
|
|
size_t old_size = all_limits.size();
|
2019-12-01 22:01:05 +00:00
|
|
|
do
|
|
|
|
{
|
|
|
|
ASTCreateQuotaQuery::Limits limits;
|
2020-02-10 15:24:33 +00:00
|
|
|
if (!parseLimits(pos, expected, alter, limits))
|
|
|
|
{
|
|
|
|
all_limits.resize(old_size);
|
2019-12-01 22:01:05 +00:00
|
|
|
return false;
|
2020-02-10 15:24:33 +00:00
|
|
|
}
|
2019-12-01 22:01:05 +00:00
|
|
|
all_limits.push_back(limits);
|
|
|
|
}
|
|
|
|
while (ParserToken{TokenType::Comma}.ignore(pos, expected));
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-03-07 17:37:38 +00:00
|
|
|
bool parseToRoles(IParserBase::Pos & pos, Expected & expected, bool id_mode, std::shared_ptr<ASTExtendedRoleSet> & roles)
|
2019-12-01 22:01:05 +00:00
|
|
|
{
|
|
|
|
return IParserBase::wrapParseImpl(pos, [&]
|
|
|
|
{
|
|
|
|
ASTPtr node;
|
2020-03-07 17:37:38 +00:00
|
|
|
if (roles || !ParserKeyword{"TO"}.ignore(pos, expected) || !ParserExtendedRoleSet{}.useIDMode(id_mode).parse(pos, node, expected))
|
2019-12-01 22:01:05 +00:00
|
|
|
return false;
|
|
|
|
|
2020-03-07 17:37:38 +00:00
|
|
|
roles = std::static_pointer_cast<ASTExtendedRoleSet>(node);
|
2019-12-01 22:01:05 +00:00
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ParserCreateQuotaQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
|
|
|
{
|
2020-02-21 19:27:12 +00:00
|
|
|
bool alter = false;
|
|
|
|
bool attach = false;
|
|
|
|
if (attach_mode)
|
|
|
|
{
|
|
|
|
if (!ParserKeyword{"ATTACH QUOTA"}.ignore(pos, expected))
|
|
|
|
return false;
|
|
|
|
attach = true;
|
|
|
|
}
|
2019-12-01 22:01:05 +00:00
|
|
|
else
|
2020-02-21 19:27:12 +00:00
|
|
|
{
|
|
|
|
if (ParserKeyword{"ALTER QUOTA"}.ignore(pos, expected))
|
|
|
|
alter = true;
|
|
|
|
else if (!ParserKeyword{"CREATE QUOTA"}.ignore(pos, expected))
|
|
|
|
return false;
|
|
|
|
}
|
2019-12-01 22:01:05 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
String name;
|
|
|
|
if (!parseIdentifierOrStringLiteral(pos, expected, name))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
String new_name;
|
|
|
|
std::optional<KeyType> key_type;
|
|
|
|
std::vector<ASTCreateQuotaQuery::Limits> all_limits;
|
2020-03-07 17:37:38 +00:00
|
|
|
std::shared_ptr<ASTExtendedRoleSet> roles;
|
2019-12-01 22:01:05 +00:00
|
|
|
|
2020-02-10 15:24:33 +00:00
|
|
|
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;
|
|
|
|
|
2020-02-21 19:27:12 +00:00
|
|
|
if (!roles && parseToRoles(pos, expected, attach, roles))
|
2020-02-10 15:24:33 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2019-12-01 22:01:05 +00:00
|
|
|
|
|
|
|
auto query = std::make_shared<ASTCreateQuotaQuery>();
|
|
|
|
node = query;
|
|
|
|
|
|
|
|
query->alter = alter;
|
|
|
|
query->if_exists = if_exists;
|
|
|
|
query->if_not_exists = if_not_exists;
|
|
|
|
query->or_replace = or_replace;
|
|
|
|
query->name = std::move(name);
|
|
|
|
query->new_name = std::move(new_name);
|
|
|
|
query->key_type = key_type;
|
|
|
|
query->all_limits = std::move(all_limits);
|
|
|
|
query->roles = std::move(roles);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|