1#include <iostream>
2
3#include <Core/Types.h>
4#include <Parsers/ASTCreateQuery.h>
5#include <Parsers/ASTDropQuery.h>
6#include <Parsers/ParserDictionary.h>
7#include <Parsers/ParserCreateQuery.h>
8#include <Parsers/ParserDropQuery.h>
9#include <Parsers/formatAST.h>
10#include <Parsers/parseQuery.h>
11#include <Parsers/DumpASTNode.h>
12#include <Parsers/TablePropertiesQueriesASTs.h>
13#include <Parsers/ParserTablePropertiesQuery.h>
14#include <sstream>
15
16#include <gtest/gtest.h>
17
18using namespace DB;
19
20#pragma GCC diagnostic ignored "-Wunused-function"
21
22static String astToString(IAST * ast)
23{
24 std::ostringstream oss;
25 dumpAST(*ast, oss);
26 return oss.str();
27}
28
29/// Tests for external dictionaries DDL parser
30
31TEST(ParserDictionaryDDL, SimpleDictionary)
32{
33 String input = " CREATE DICTIONARY test.dict1"
34 " ("
35 " key_column UInt64 DEFAULT 0,"
36 " second_column UInt8 DEFAULT 1,"
37 " third_column UInt8 DEFAULT 2"
38 " )"
39 " PRIMARY KEY key_column"
40 " SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' PASSWORD '' DB 'test' TABLE 'table_for_dict'))"
41 " LAYOUT(FLAT())"
42 " LIFETIME(MIN 1 MAX 10)"
43 " RANGE(MIN second_column MAX third_column)";
44
45 ParserCreateDictionaryQuery parser;
46 ASTPtr ast = parseQuery(parser, input.data(), input.data() + input.size(), "", 0);
47 ASTCreateQuery * create = ast->as<ASTCreateQuery>();
48 EXPECT_EQ(create->table, "dict1");
49 EXPECT_EQ(create->database, "test");
50 EXPECT_EQ(create->is_dictionary, true);
51 EXPECT_NE(create->dictionary, nullptr);
52 EXPECT_NE(create->dictionary->lifetime, nullptr);
53 EXPECT_NE(create->dictionary->source, nullptr);
54 EXPECT_NE(create->dictionary->layout, nullptr);
55 EXPECT_NE(create->dictionary->primary_key, nullptr);
56 EXPECT_NE(create->dictionary->range, nullptr);
57
58 /// source test
59 EXPECT_EQ(create->dictionary->source->name, "clickhouse");
60 auto children = create->dictionary->source->elements->children;
61 EXPECT_EQ(children[0]->as<ASTPair>() -> first, "host");
62 EXPECT_EQ(children[0]->as<ASTPair>()->second->as<ASTLiteral>()->value.get<String>(), "localhost");
63
64 EXPECT_EQ(children[1]->as<ASTPair>()->first, "port");
65 EXPECT_EQ(children[1]->as<ASTPair>()->second->as<ASTLiteral>()->value.get<UInt64>(), 9000);
66
67 EXPECT_EQ(children[2]->as<ASTPair>()->first, "user");
68 EXPECT_EQ(children[2]->as<ASTPair>()->second->as<ASTLiteral>()->value.get<String>(), "default");
69 EXPECT_EQ(children[3]->as<ASTPair>()->first, "password");
70 EXPECT_EQ(children[3]->as<ASTPair>()->second->as<ASTLiteral>()->value.get<String>(), "");
71
72 EXPECT_EQ(children[4]->as<ASTPair>()->first, "db");
73 EXPECT_EQ(children[4]->as<ASTPair>()->second->as<ASTLiteral>()->value.get<String>(), "test");
74
75 EXPECT_EQ(children[5]->as<ASTPair>()->first, "table");
76 EXPECT_EQ(children[5]->as<ASTPair>()->second->as<ASTLiteral>()->value.get<String>(), "table_for_dict");
77
78 /// layout test
79 auto layout = create->dictionary->layout;
80 EXPECT_EQ(layout->layout_type, "flat");
81 EXPECT_EQ(layout->children.size(), 0);
82
83 /// lifetime test
84 auto lifetime = create->dictionary->lifetime;
85
86 EXPECT_EQ(lifetime->min_sec, 1);
87 EXPECT_EQ(lifetime->max_sec, 10);
88
89 /// primary key test
90 auto primary_key = create->dictionary->primary_key;
91
92 EXPECT_EQ(primary_key->children.size(), 1);
93 EXPECT_EQ(primary_key->children[0]->as<ASTIdentifier>()->name, "key_column");
94
95 /// range test
96 auto range = create->dictionary->range;
97 EXPECT_EQ(range->min_attr_name, "second_column");
98 EXPECT_EQ(range->max_attr_name, "third_column");
99
100 /// test attributes
101 EXPECT_NE(create->dictionary_attributes_list, nullptr);
102
103 auto attributes_children = create->dictionary_attributes_list->children;
104 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->name, "key_column");
105 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->name, "second_column");
106 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->name, "third_column");
107
108 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->default_value->as<ASTLiteral>()->value.get<UInt64>(), 0);
109 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->default_value->as<ASTLiteral>()->value.get<UInt64>(), 1);
110 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->default_value->as<ASTLiteral>()->value.get<UInt64>(), 2);
111
112 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->expression, nullptr);
113 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->expression, nullptr);
114 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->expression, nullptr);
115
116 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->hierarchical, false);
117 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->hierarchical, false);
118 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->hierarchical, false);
119
120 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->injective, false);
121 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->injective, false);
122 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->injective, false);
123
124 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->is_object_id, false);
125 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->is_object_id, false);
126 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->is_object_id, false);
127}
128
129
130TEST(ParserDictionaryDDL, AttributesWithMultipleProperties)
131{
132 String input = " CREATE DICTIONARY dict2"
133 " ("
134 " key_column UInt64 IS_OBJECT_ID,"
135 " second_column UInt8 DEFAULT 1 HIERARCHICAL INJECTIVE,"
136 " third_column UInt8 DEFAULT 2 EXPRESSION rand() % 100 * 77"
137 " )"
138 " PRIMARY KEY key_column"
139 " SOURCE(CLICKHOUSE(HOST 'localhost'))";
140
141 ParserCreateDictionaryQuery parser;
142 ASTPtr ast = parseQuery(parser, input.data(), input.data() + input.size(), "", 0);
143 ASTCreateQuery * create = ast->as<ASTCreateQuery>();
144 EXPECT_EQ(create->table, "dict2");
145 EXPECT_EQ(create->database, "");
146
147 /// test attributes
148 EXPECT_NE(create->dictionary_attributes_list, nullptr);
149
150 auto attributes_children = create->dictionary_attributes_list->children;
151 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->name, "key_column");
152 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->name, "second_column");
153 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->name, "third_column");
154
155 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->default_value, nullptr);
156 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->default_value->as<ASTLiteral>()->value.get<UInt64>(), 1);
157 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->default_value->as<ASTLiteral>()->value.get<UInt64>(), 2);
158
159 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->expression, nullptr);
160 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->expression, nullptr);
161 EXPECT_EQ(serializeAST(*attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->expression, true), "(rand() % 100) * 77");
162
163 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->hierarchical, false);
164 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->hierarchical, true);
165 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->hierarchical, false);
166
167 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->injective, false);
168 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->injective, true);
169 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->injective, false);
170
171 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->is_object_id, true);
172 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->is_object_id, false);
173 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->is_object_id, false);
174}
175
176TEST(ParserDictionaryDDL, CustomAttributePropertiesOrder)
177{
178 String input = " CREATE DICTIONARY dict3"
179 " ("
180 " key_column UInt64 IS_OBJECT_ID DEFAULT 100,"
181 " second_column UInt8 INJECTIVE HIERARCHICAL DEFAULT 1,"
182 " third_column UInt8 EXPRESSION rand() % 100 * 77 DEFAULT 2 INJECTIVE HIERARCHICAL"
183 " )"
184 " PRIMARY KEY key_column"
185 " SOURCE(CLICKHOUSE(REPLICA(HOST '127.0.0.1' PRIORITY 1)))"
186 " LIFETIME(300)";
187
188 ParserCreateDictionaryQuery parser;
189 ASTPtr ast = parseQuery(parser, input.data(), input.data() + input.size(), "", 0);
190 ASTCreateQuery * create = ast->as<ASTCreateQuery>();
191
192 /// test attributes
193 EXPECT_NE(create->dictionary_attributes_list, nullptr);
194
195 auto attributes_children = create->dictionary_attributes_list->children;
196
197 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->name, "key_column");
198 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->name, "second_column");
199 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->name, "third_column");
200
201 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->default_value->as<ASTLiteral>()->value.get<UInt64>(), 100);
202 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->default_value->as<ASTLiteral>()->value.get<UInt64>(), 1);
203 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->default_value->as<ASTLiteral>()->value.get<UInt64>(), 2);
204
205 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->expression, nullptr);
206 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->expression, nullptr);
207 EXPECT_EQ(serializeAST(*attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->expression, true), "(rand() % 100) * 77");
208
209 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->hierarchical, false);
210 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->hierarchical, true);
211 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->hierarchical, true);
212
213 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->injective, false);
214 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->injective, true);
215 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->injective, true);
216
217 EXPECT_EQ(attributes_children[0]->as<ASTDictionaryAttributeDeclaration>()->is_object_id, true);
218 EXPECT_EQ(attributes_children[1]->as<ASTDictionaryAttributeDeclaration>()->is_object_id, false);
219 EXPECT_EQ(attributes_children[2]->as<ASTDictionaryAttributeDeclaration>()->is_object_id, false);
220
221 /// lifetime test
222 auto lifetime = create->dictionary->lifetime;
223
224 EXPECT_EQ(lifetime->min_sec, 0);
225 EXPECT_EQ(lifetime->max_sec, 300);
226}
227
228
229TEST(ParserDictionaryDDL, NestedSource)
230{
231 String input = " CREATE DICTIONARY dict4"
232 " ("
233 " key_column UInt64,"
234 " second_column UInt8,"
235 " third_column UInt8"
236 " )"
237 " PRIMARY KEY key_column"
238 " SOURCE(MYSQL(HOST 'localhost' PORT 9000 USER 'default' REPLICA(HOST '127.0.0.1' PRIORITY 1) PASSWORD ''))"
239 " LAYOUT(CACHE(size_in_cells 50))"
240 " LIFETIME(MIN 1 MAX 10)"
241 " RANGE(MIN second_column MAX third_column)";
242
243 ParserCreateDictionaryQuery parser;
244 ASTPtr ast = parseQuery(parser, input.data(), input.data() + input.size(), "", 0);
245 ASTCreateQuery * create = ast->as<ASTCreateQuery>();
246 EXPECT_EQ(create->table, "dict4");
247 EXPECT_EQ(create->database, "");
248
249 /// source test
250 EXPECT_EQ(create->dictionary->source->name, "mysql");
251 auto children = create->dictionary->source->elements->children;
252
253 EXPECT_EQ(children[0]->as<ASTPair>()->first, "host");
254 EXPECT_EQ(children[0]->as<ASTPair>()->second->as<ASTLiteral>()->value.get<String>(), "localhost");
255
256 EXPECT_EQ(children[1]->as<ASTPair>()->first, "port");
257 EXPECT_EQ(children[1]->as<ASTPair>()->second->as<ASTLiteral>()->value.get<UInt64>(), 9000);
258
259 EXPECT_EQ(children[2]->as<ASTPair>()->first, "user");
260 EXPECT_EQ(children[2]->as<ASTPair>()->second->as<ASTLiteral>()->value.get<String>(), "default");
261
262 EXPECT_EQ(children[3]->as<ASTPair>()->first, "replica");
263 auto replica = children[3]->as<ASTPair>()->second->children;
264
265 EXPECT_EQ(replica[0]->as<ASTPair>()->first, "host");
266 EXPECT_EQ(replica[0]->as<ASTPair>()->second->as<ASTLiteral>()->value.get<String>(), "127.0.0.1");
267
268 EXPECT_EQ(replica[1]->as<ASTPair>()->first, "priority");
269 EXPECT_EQ(replica[1]->as<ASTPair>()->second->as<ASTLiteral>()->value.get<UInt64>(), 1);
270
271 EXPECT_EQ(children[4]->as<ASTPair>()->first, "password");
272 EXPECT_EQ(children[4]->as<ASTPair>()->second->as<ASTLiteral>()->value.get<String>(), "");
273}
274
275
276
277TEST(ParserDictionaryDDL, Formatting)
278{
279 String input = " CREATE DICTIONARY test.dict5"
280 " ("
281 " key_column1 UInt64 DEFAULT 1 HIERARCHICAL INJECTIVE,"
282 " key_column2 String DEFAULT '',"
283 " second_column UInt8 EXPRESSION intDiv(50, rand() % 1000),"
284 " third_column UInt8"
285 " )"
286 " PRIMARY KEY key_column1, key_column2"
287 " SOURCE(MYSQL(HOST 'localhost' PORT 9000 USER 'default' REPLICA(HOST '127.0.0.1' PRIORITY 1) PASSWORD ''))"
288 " LAYOUT(CACHE(size_in_cells 50))"
289 " LIFETIME(MIN 1 MAX 10)"
290 " RANGE(MIN second_column MAX third_column)";
291
292 ParserCreateDictionaryQuery parser;
293 ASTPtr ast = parseQuery(parser, input.data(), input.data() + input.size(), "", 0);
294 ASTCreateQuery * create = ast->as<ASTCreateQuery>();
295 auto str = serializeAST(*create, true);
296 EXPECT_EQ(str, "CREATE DICTIONARY test.dict5 (`key_column1` UInt64 DEFAULT 1 HIERARCHICAL INJECTIVE, `key_column2` String DEFAULT '', `second_column` UInt8 EXPRESSION intDiv(50, rand() % 1000), `third_column` UInt8) PRIMARY KEY key_column1, key_column2 SOURCE(MYSQL(HOST 'localhost' PORT 9000 USER 'default' REPLICA (HOST '127.0.0.1' PRIORITY 1) PASSWORD '')) LIFETIME(MIN 1 MAX 10) LAYOUT(CACHE(SIZE_IN_CELLS 50)) RANGE(MIN second_column MAX third_column)");
297}
298
299TEST(ParserDictionaryDDL, ParseDropQuery)
300{
301 String input1 = "DROP DICTIONARY test.dict1";
302
303 ParserDropQuery parser;
304 ASTPtr ast1 = parseQuery(parser, input1.data(), input1.data() + input1.size(), "", 0);
305 ASTDropQuery * drop1 = ast1->as<ASTDropQuery>();
306
307 EXPECT_TRUE(drop1->is_dictionary);
308 EXPECT_EQ(drop1->database, "test");
309 EXPECT_EQ(drop1->table, "dict1");
310 auto str1 = serializeAST(*drop1, true);
311 EXPECT_EQ(input1, str1);
312
313 String input2 = "DROP DICTIONARY IF EXISTS dict2";
314
315 ASTPtr ast2 = parseQuery(parser, input2.data(), input2.data() + input2.size(), "", 0);
316 ASTDropQuery * drop2 = ast2->as<ASTDropQuery>();
317
318 EXPECT_TRUE(drop2->is_dictionary);
319 EXPECT_EQ(drop2->database, "");
320 EXPECT_EQ(drop2->table, "dict2");
321 auto str2 = serializeAST(*drop2, true);
322 EXPECT_EQ(input2, str2);
323}
324
325TEST(ParserDictionaryDDL, ParsePropertiesQueries)
326{
327 String input1 = "SHOW CREATE DICTIONARY test.dict1";
328
329 ParserTablePropertiesQuery parser;
330 ASTPtr ast1 = parseQuery(parser, input1.data(), input1.data() + input1.size(), "", 0);
331 ASTShowCreateDictionaryQuery * show1 = ast1->as<ASTShowCreateDictionaryQuery>();
332
333 EXPECT_EQ(show1->table, "dict1");
334 EXPECT_EQ(show1->database, "test");
335 EXPECT_EQ(serializeAST(*show1), input1);
336
337 String input2 = "EXISTS DICTIONARY dict2";
338
339 ASTPtr ast2 = parseQuery(parser, input2.data(), input2.data() + input2.size(), "", 0);
340 ASTExistsDictionaryQuery * show2 = ast2->as<ASTExistsDictionaryQuery>();
341
342 EXPECT_EQ(show2->table, "dict2");
343 EXPECT_EQ(show2->database, "");
344 EXPECT_EQ(serializeAST(*show2), input2);
345}
346