1#include <Parsers/ParserCreateQuotaQuery.h>
2#include <Parsers/ASTCreateQuotaQuery.h>
3#include <Parsers/CommonParsers.h>
4#include <Parsers/parseIntervalKind.h>
5#include <Parsers/parseIdentifierOrStringLiteral.h>
6#include <Parsers/ParserRoleList.h>
7#include <Parsers/ExpressionElementParsers.h>
8#include <Parsers/ASTLiteral.h>
9#include <Parsers/ASTRoleList.h>
10#include <ext/range.h>
11#include <boost/algorithm/string/predicate.hpp>
12
13
14namespace DB
15{
16namespace ErrorCodes
17{
18 extern const int SYNTAX_ERROR;
19}
20
21
22namespace
23{
24 using KeyType = Quota::KeyType;
25 using ResourceType = Quota::ResourceType;
26 using ResourceAmount = Quota::ResourceAmount;
27
28 bool parseRenameTo(IParserBase::Pos & pos, Expected & expected, String & new_name, bool alter)
29 {
30 return IParserBase::wrapParseImpl(pos, [&]
31 {
32 if (!new_name.empty() || !alter)
33 return false;
34
35 if (!ParserKeyword{"RENAME TO"}.ignore(pos, expected))
36 return false;
37
38 return parseIdentifierOrStringLiteral(pos, expected, new_name);
39 });
40 }
41
42 bool parseKeyType(IParserBase::Pos & pos, Expected & expected, std::optional<Quota::KeyType> & key_type)
43 {
44 return IParserBase::wrapParseImpl(pos, [&]
45 {
46 if (key_type)
47 return false;
48
49 if (!ParserKeyword{"KEYED BY"}.ignore(pos, expected))
50 return false;
51
52 ASTPtr key_type_ast;
53 if (!ParserStringLiteral().parse(pos, key_type_ast, expected))
54 return false;
55
56 const String & key_type_str = key_type_ast->as<ASTLiteral &>().value.safeGet<const String &>();
57 for (auto kt : ext::range_with_static_cast<Quota::KeyType>(Quota::MAX_KEY_TYPE))
58 if (boost::iequals(Quota::getNameOfKeyType(kt), key_type_str))
59 {
60 key_type = kt;
61 return true;
62 }
63
64 String all_key_types_str;
65 for (auto kt : ext::range_with_static_cast<Quota::KeyType>(Quota::MAX_KEY_TYPE))
66 all_key_types_str += String(all_key_types_str.empty() ? "" : ", ") + "'" + Quota::getNameOfKeyType(kt) + "'";
67 String msg = "Quota cannot be keyed by '" + key_type_str + "'. Expected one of these literals: " + all_key_types_str;
68 throw Exception(msg, ErrorCodes::SYNTAX_ERROR);
69 });
70 }
71
72 bool parseLimit(IParserBase::Pos & pos, Expected & expected, ResourceType & resource_type, ResourceAmount & max)
73 {
74 return IParserBase::wrapParseImpl(pos, [&]
75 {
76 if (!ParserKeyword{"MAX"}.ignore(pos, expected))
77 return false;
78
79 bool resource_type_set = false;
80 for (auto rt : ext::range_with_static_cast<Quota::ResourceType>(Quota::MAX_RESOURCE_TYPE))
81 {
82 if (ParserKeyword{Quota::resourceTypeToKeyword(rt)}.ignore(pos, expected))
83 {
84 resource_type = rt;
85 resource_type_set = true;
86 break;
87 }
88 }
89 if (!resource_type_set)
90 return false;
91
92 if (!ParserToken{TokenType::Equals}.ignore(pos, expected))
93 return false;
94
95 ASTPtr max_ast;
96 if (ParserNumber{}.parse(pos, max_ast, expected))
97 {
98 const Field & max_field = max_ast->as<ASTLiteral &>().value;
99 if (resource_type == Quota::EXECUTION_TIME)
100 max = Quota::secondsToExecutionTime(applyVisitor(FieldVisitorConvertToNumber<double>(), max_field));
101 else
102 max = applyVisitor(FieldVisitorConvertToNumber<ResourceAmount>(), max_field);
103 }
104 else if (ParserKeyword{"ANY"}.ignore(pos, expected))
105 {
106 max = Quota::UNLIMITED;
107 }
108 else
109 return false;
110
111 return true;
112 });
113 }
114
115 bool parseCommaAndLimit(IParserBase::Pos & pos, Expected & expected, ResourceType & resource_type, ResourceAmount & max)
116 {
117 return IParserBase::wrapParseImpl(pos, [&]
118 {
119 if (!ParserToken{TokenType::Comma}.ignore(pos, expected))
120 return false;
121
122 return parseLimit(pos, expected, resource_type, max);
123 });
124 }
125
126 bool parseLimits(IParserBase::Pos & pos, Expected & expected, ASTCreateQuotaQuery::Limits & limits, bool alter)
127 {
128 return IParserBase::wrapParseImpl(pos, [&]
129 {
130 ASTCreateQuotaQuery::Limits new_limits;
131 if (!ParserKeyword{"FOR"}.ignore(pos, expected))
132 return false;
133
134 new_limits.randomize_interval = ParserKeyword{"RANDOMIZED"}.ignore(pos, expected);
135
136 if (!ParserKeyword{"INTERVAL"}.ignore(pos, expected))
137 return false;
138
139 ASTPtr num_intervals_ast;
140 if (!ParserNumber{}.parse(pos, num_intervals_ast, expected))
141 return false;
142
143 double num_intervals = applyVisitor(FieldVisitorConvertToNumber<double>(), num_intervals_ast->as<ASTLiteral &>().value);
144
145 IntervalKind interval_kind;
146 if (!parseIntervalKind(pos, expected, interval_kind))
147 return false;
148
149 new_limits.duration = std::chrono::seconds(static_cast<UInt64>(num_intervals * interval_kind.toAvgSeconds()));
150
151 if (alter && ParserKeyword{"UNSET TRACKING"}.ignore(pos, expected))
152 {
153 new_limits.unset_tracking = true;
154 }
155 else if (ParserKeyword{"SET TRACKING"}.ignore(pos, expected) || ParserKeyword{"TRACKING"}.ignore(pos, expected))
156 {
157 }
158 else
159 {
160 ParserKeyword{"SET"}.ignore(pos, expected);
161 ResourceType resource_type;
162 ResourceAmount max;
163 if (!parseLimit(pos, expected, resource_type, max))
164 return false;
165
166 new_limits.max[resource_type] = max;
167 while (parseCommaAndLimit(pos, expected, resource_type, max))
168 new_limits.max[resource_type] = max;
169 }
170
171 limits = new_limits;
172 return true;
173 });
174 }
175
176 bool parseAllLimits(IParserBase::Pos & pos, Expected & expected, std::vector<ASTCreateQuotaQuery::Limits> & all_limits, bool alter)
177 {
178 return IParserBase::wrapParseImpl(pos, [&]
179 {
180 do
181 {
182 ASTCreateQuotaQuery::Limits limits;
183 if (!parseLimits(pos, expected, limits, alter))
184 return false;
185 all_limits.push_back(limits);
186 }
187 while (ParserToken{TokenType::Comma}.ignore(pos, expected));
188 return true;
189 });
190 }
191
192 bool parseRoles(IParserBase::Pos & pos, Expected & expected, std::shared_ptr<ASTRoleList> & roles)
193 {
194 return IParserBase::wrapParseImpl(pos, [&]
195 {
196 ASTPtr node;
197 if (roles || !ParserKeyword{"TO"}.ignore(pos, expected) || !ParserRoleList{}.parse(pos, node, expected))
198 return false;
199
200 roles = std::static_pointer_cast<ASTRoleList>(node);
201 return true;
202 });
203 }
204}
205
206
207bool ParserCreateQuotaQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
208{
209 bool alter;
210 if (ParserKeyword{"CREATE QUOTA"}.ignore(pos, expected))
211 alter = false;
212 else if (ParserKeyword{"ALTER QUOTA"}.ignore(pos, expected))
213 alter = true;
214 else
215 return false;
216
217 bool if_exists = false;
218 bool if_not_exists = false;
219 bool or_replace = false;
220 if (alter)
221 {
222 if (ParserKeyword{"IF EXISTS"}.ignore(pos, expected))
223 if_exists = true;
224 }
225 else
226 {
227 if (ParserKeyword{"IF NOT EXISTS"}.ignore(pos, expected))
228 if_not_exists = true;
229 else if (ParserKeyword{"OR REPLACE"}.ignore(pos, expected))
230 or_replace = true;
231 }
232
233 String name;
234 if (!parseIdentifierOrStringLiteral(pos, expected, name))
235 return false;
236
237 String new_name;
238 std::optional<KeyType> key_type;
239 std::vector<ASTCreateQuotaQuery::Limits> all_limits;
240 std::shared_ptr<ASTRoleList> roles;
241
242 while (parseRenameTo(pos, expected, new_name, alter) || parseKeyType(pos, expected, key_type)
243 || parseAllLimits(pos, expected, all_limits, alter) || parseRoles(pos, expected, roles))
244 ;
245
246 auto query = std::make_shared<ASTCreateQuotaQuery>();
247 node = query;
248
249 query->alter = alter;
250 query->if_exists = if_exists;
251 query->if_not_exists = if_not_exists;
252 query->or_replace = or_replace;
253 query->name = std::move(name);
254 query->new_name = std::move(new_name);
255 query->key_type = key_type;
256 query->all_limits = std::move(all_limits);
257 query->roles = std::move(roles);
258
259 return true;
260}
261}
262