1 /*
2 * Copyright (c) 2015, 2021, Oracle and/or its affiliates.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2.0,
6 * as published by the Free Software Foundation.
7 *
8 * This program is also distributed with certain software (including
9 * but not limited to OpenSSL) that is licensed under separate terms,
10 * as designated in a particular file or component or in included license
11 * documentation. The authors of MySQL hereby grant you an additional
12 * permission to link the program and your derivative works with the
13 * separately licensed software that they have included with MySQL.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License, version 2.0, for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include "find_statement_builder.h"
26 #include "query_string_builder.h"
27 #include "mysqld_error.h"
28 #include "expr_generator.h"
29 #include "ngs_common/protocol_protobuf.h"
30
31 #include <gtest/gtest.h>
32
33 namespace xpl
34 {
35 namespace test
36 {
37
38 class Find_statement_builder_impl: public Find_statement_builder
39 {
40 public:
Find_statement_builder_impl(Expression_generator & gen)41 Find_statement_builder_impl(Expression_generator &gen) : Find_statement_builder(gen) {}
42 using Find_statement_builder::add_table_projection;
43 using Find_statement_builder::add_document_projection;
44 using Find_statement_builder::add_grouping;
45 using Find_statement_builder::add_grouping_criteria;
46 using Find_statement_builder::Grouping_list;
47 using Find_statement_builder::Grouping_criteria;
48 };
49
50
51 class Find_statement_builder_test : public ::testing::Test
52 {
53 public:
Find_statement_builder_test()54 Find_statement_builder_test()
55 : args(*msg.mutable_args()),
56 expr_gen(query, args, schema, true),
57 builder(expr_gen)
58 {}
59 Find_statement_builder::Find msg;
60 Expression_generator::Args &args;
61 Query_string_builder query;
62 std::string schema;
63 Expression_generator expr_gen;
64 Find_statement_builder_impl builder;
65
66 enum {DM_DOCUMENT = 0, DM_TABLE = 1};
67
68 typedef ::google::protobuf::RepeatedPtrField< ::Mysqlx::Crud::Projection > Projection_list;
69 };
70
71
72 namespace
73 {
operator <<(::google::protobuf::Message & msg,const std::string & txt)74 void operator<< (::google::protobuf::Message &msg, const std::string& txt)
75 {
76 ASSERT_TRUE(::google::protobuf::TextFormat::ParseFromString(txt, &msg));
77 }
78 } // namespace
79
80
TEST_F(Find_statement_builder_test,add_projection_table_empty)81 TEST_F(Find_statement_builder_test, add_projection_table_empty)
82 {
83 Projection_list projection;
84 ASSERT_NO_THROW(builder.add_table_projection(projection));
85 EXPECT_EQ("*", query.get());
86 }
87
88
TEST_F(Find_statement_builder_test,add_document_projection_empty)89 TEST_F(Find_statement_builder_test, add_document_projection_empty)
90 {
91 Projection_list projection;
92 ASSERT_NO_THROW(builder.add_document_projection(projection));
93 EXPECT_EQ("doc", query.get());
94 }
95
96
TEST_F(Find_statement_builder_test,add_projection_table_one_member_item)97 TEST_F(Find_statement_builder_test, add_projection_table_one_member_item)
98 {
99 Projection_list projection;
100 *projection.Add() << "source { type: IDENT identifier "
101 "{ document_path { type: MEMBER value: \"alpha\" } } }";
102 ASSERT_NO_THROW(builder.add_table_projection(projection));
103 EXPECT_EQ("JSON_EXTRACT(doc,'$.alpha')", query.get());
104 }
105
106
TEST_F(Find_statement_builder_test,add_projection_table_one_item)107 TEST_F(Find_statement_builder_test, add_projection_table_one_item)
108 {
109 Projection_list projection;
110 *projection.Add() << "source { type: IDENT identifier "
111 "{ name: 'alpha' } }";
112 ASSERT_NO_THROW(builder.add_table_projection(projection));
113 EXPECT_EQ("`alpha`", query.get());
114 }
115
116
TEST_F(Find_statement_builder_test,add_projection_table_two_items)117 TEST_F(Find_statement_builder_test, add_projection_table_two_items)
118 {
119 Projection_list projection;
120 *projection.Add() << "source { type: IDENT identifier "
121 "{ name: 'alpha' } }";
122 *projection.Add() << "source { type: IDENT identifier "
123 "{ name: 'beta' } }";
124 ASSERT_NO_THROW(builder.add_table_projection(projection));
125 EXPECT_EQ("`alpha`,`beta`", query.get());
126 }
127
128
TEST_F(Find_statement_builder_test,add_projection_table_two_items_placeholder)129 TEST_F(Find_statement_builder_test, add_projection_table_two_items_placeholder)
130 {
131 *args.Add() << "type: V_DOUBLE v_double: 2.2";
132
133 Projection_list projection;
134 *projection.Add() << "source { type: IDENT identifier "
135 "{ name: 'alpha' } }";
136 *projection.Add() << "source { type: PLACEHOLDER position: 0 }";
137 ASSERT_NO_THROW(builder.add_table_projection(projection));
138 EXPECT_EQ("`alpha`,2.2", query.get());
139 }
140
141
TEST_F(Find_statement_builder_test,add_projection_table_one_item_with_alias)142 TEST_F(Find_statement_builder_test, add_projection_table_one_item_with_alias)
143 {
144 Projection_list projection;
145 *projection.Add() << "source { type: IDENT identifier "
146 "{ name: 'alpha' } } alias: 'beta'";
147 ASSERT_NO_THROW(builder.add_table_projection(projection));
148 EXPECT_EQ("`alpha` AS `beta`", query.get());
149 }
150
151
TEST_F(Find_statement_builder_test,add_projection_document_one_item_no_alias)152 TEST_F(Find_statement_builder_test, add_projection_document_one_item_no_alias)
153 {
154 Projection_list projection;
155 *projection.Add() << "source { type: IDENT identifier "
156 "{ name: 'alpha' } }";
157 EXPECT_THROW(builder.add_document_projection(projection), ngs::Error_code);
158 }
159
160
TEST_F(Find_statement_builder_test,add_projection_document_one_member_item)161 TEST_F(Find_statement_builder_test, add_projection_document_one_member_item)
162 {
163 Projection_list projection;
164 *projection.Add() << "source { type: IDENT identifier "
165 "{ document_path { type: MEMBER value: \"alpha\" } } }"
166 "alias: \"beta\"";
167 ASSERT_NO_THROW(builder.add_document_projection(projection));
168 EXPECT_EQ("JSON_OBJECT('beta', JSON_EXTRACT(doc,'$.alpha')) AS doc",
169 query.get());
170 }
171
172
TEST_F(Find_statement_builder_test,add_projection_document_two_member_items)173 TEST_F(Find_statement_builder_test, add_projection_document_two_member_items)
174 {
175 Projection_list projection;
176 *projection.Add() << "source { type: IDENT identifier "
177 "{ document_path { type: MEMBER value: \"alpha\" } } }"
178 "alias: \"beta\"";
179 *projection.Add() << "source { type: IDENT identifier "
180 "{ document_path { type: MEMBER value: \"first\" } } }"
181 "alias: \"second\"";
182 ASSERT_NO_THROW(builder.add_document_projection(projection));
183 EXPECT_EQ("JSON_OBJECT('beta', JSON_EXTRACT(doc,'$.alpha'),"
184 "'second', JSON_EXTRACT(doc,'$.first')) AS doc",
185 query.get());
186 }
187
188
TEST_F(Find_statement_builder_test,add_projection_document_two_member_items_placeholder)189 TEST_F(Find_statement_builder_test, add_projection_document_two_member_items_placeholder)
190 {
191 *args.Add() << "type: V_DOUBLE v_double: 2.2";
192 Projection_list projection;
193 *projection.Add() << "source { type: IDENT identifier "
194 "{ document_path { type: MEMBER value: \"alpha\" } } }"
195 "alias: \"beta\"";
196 *projection.Add() << "source {type: PLACEHOLDER position: 0}"
197 "alias: \"second\"";
198 ASSERT_NO_THROW(builder.add_document_projection(projection));
199 EXPECT_EQ("JSON_OBJECT('beta', JSON_EXTRACT(doc,'$.alpha'),"
200 "'second', 2.2) AS doc",
201 query.get());
202 }
203
204
TEST_F(Find_statement_builder_test,add_gruping_empty)205 TEST_F(Find_statement_builder_test, add_gruping_empty)
206 {
207 Find_statement_builder_impl::Grouping_list group;
208 ASSERT_NO_THROW(builder.add_grouping(group));
209 EXPECT_EQ("", query.get());
210 }
211
212
TEST_F(Find_statement_builder_test,add_gruping_one_item)213 TEST_F(Find_statement_builder_test, add_gruping_one_item)
214 {
215 Find_statement_builder_impl::Grouping_list group;
216 *group.Add() << "type: IDENT identifier { name: 'alpha' }";
217 ASSERT_NO_THROW(builder.add_grouping(group));
218 EXPECT_EQ(" GROUP BY `alpha`", query.get());
219 }
220
221
TEST_F(Find_statement_builder_test,add_gruping_two_items)222 TEST_F(Find_statement_builder_test, add_gruping_two_items)
223 {
224 Find_statement_builder_impl::Grouping_list group;
225 *group.Add() << "type: IDENT identifier { name: 'alpha' }";
226 *group.Add() << "type: IDENT identifier { name: 'beta' }";
227 ASSERT_NO_THROW(builder.add_grouping(group));
228 EXPECT_EQ(" GROUP BY `alpha`,`beta`", query.get());
229 }
230
231
TEST_F(Find_statement_builder_test,add_gruping_two_items_placeholder)232 TEST_F(Find_statement_builder_test, add_gruping_two_items_placeholder)
233 {
234 *args.Add() << "type: V_SINT v_signed_int: 2";
235
236 Find_statement_builder_impl::Grouping_list group;
237 *group.Add() << "type: IDENT identifier { name: 'alpha' }";
238 *group.Add() << "type: PLACEHOLDER position: 0";
239 ASSERT_NO_THROW(builder.add_grouping(group));
240 EXPECT_EQ(" GROUP BY `alpha`,2", query.get());
241 }
242
243
TEST_F(Find_statement_builder_test,add_gruping_criteria)244 TEST_F(Find_statement_builder_test, add_gruping_criteria)
245 {
246 Find_statement_builder_impl::Grouping_criteria criteria;
247 criteria << "type: OPERATOR operator {name: '>'"
248 "param {type: IDENT identifier {name: 'alpha'}}"
249 "param {type: LITERAL literal {type: V_DOUBLE v_double: 1.0}}}";
250 ASSERT_NO_THROW(builder.add_grouping_criteria(criteria));
251 EXPECT_EQ(" HAVING (`alpha` > 1)", query.get());
252 }
253
254
TEST_F(Find_statement_builder_test,add_gruping_criteria_placeholder)255 TEST_F(Find_statement_builder_test, add_gruping_criteria_placeholder)
256 {
257 *args.Add() << "type: V_DOUBLE v_double: 2.3";
258
259 Find_statement_builder_impl::Grouping_criteria criteria;
260 criteria << "type: OPERATOR operator {name: '>'"
261 "param {type: IDENT identifier {name: 'alpha'}}"
262 "param {type: PLACEHOLDER position: 0}}";
263 ASSERT_NO_THROW(builder.add_grouping_criteria(criteria));
264 EXPECT_EQ(" HAVING (`alpha` > 2.3)", query.get());
265 }
266
267
TEST_F(Find_statement_builder_test,build_table)268 TEST_F(Find_statement_builder_test, build_table)
269 {
270 msg <<
271 "collection {name: 'xtable' schema: 'xschema'}"
272 "data_model: TABLE "
273 "projection {source {type: IDENT identifier {name: 'alpha'}}"
274 " alias: 'zeta'} "
275 "criteria {type: OPERATOR "
276 " operator {name: '>' "
277 " param {type: IDENT identifier {name: 'delta'}}"
278 " param {type: LITERAL literal "
279 " {type: V_DOUBLE"
280 " v_double: 1.0}}}}"
281 "order {expr {type: IDENT identifier {name: 'gamma'}}"
282 " direction: DESC} "
283 "grouping {type: IDENT identifier {name: 'beta'}}"
284 "grouping_criteria {type: OPERATOR "
285 " operator {name: '<' "
286 " param {type: IDENT identifier {name: 'lambda'}}"
287 " param {type: LITERAL literal"
288 " {type: V_DOUBLE"
289 " v_double: 2.0}}}}";
290 ASSERT_NO_THROW(builder.build(msg));
291 EXPECT_EQ(
292 "SELECT `alpha` AS `zeta` "
293 "FROM `xschema`.`xtable` "
294 "WHERE (`delta` > 1) "
295 "GROUP BY `beta` "
296 "HAVING (`lambda` < 2) "
297 "ORDER BY `gamma` DESC", query.get());
298 }
299
300
TEST_F(Find_statement_builder_test,build_document_no_grouping)301 TEST_F(Find_statement_builder_test, build_document_no_grouping)
302 {
303 msg <<
304 "collection {name: 'xtable' schema: 'xschema'}"
305 "data_model: DOCUMENT "
306 "projection {source {type: IDENT identifier {document_path {type: MEMBER "
307 " value: 'alpha'}}}"
308 " alias: 'zeta'} "
309 "criteria {type: OPERATOR "
310 " operator {name: '>' "
311 " param {type: IDENT identifier {document_path {type: MEMBER "
312 " value: 'delta'}}}"
313 " param {type: LITERAL literal"
314 " {type: V_DOUBLE"
315 " v_double: 1.0}}}}"
316 "order {expr {type: IDENT identifier {document_path {type: MEMBER "
317 " value: 'gamma'}}}"
318 " direction: DESC}";
319 ASSERT_NO_THROW(builder.build(msg));
320 EXPECT_EQ(
321 "SELECT JSON_OBJECT('zeta', JSON_EXTRACT(doc,'$.alpha')) AS doc "
322 "FROM `xschema`.`xtable` "
323 "WHERE (JSON_EXTRACT(doc,'$.delta') > 1) "
324 "ORDER BY JSON_EXTRACT(doc,'$.gamma') DESC",
325 query.get());
326 }
327
328
TEST_F(Find_statement_builder_test,build_document_with_grouping_and_criteria)329 TEST_F(Find_statement_builder_test, build_document_with_grouping_and_criteria)
330 {
331 msg <<
332 "collection {name: 'xtable' schema: 'xschema'}"
333 "data_model: DOCUMENT "
334 "projection {source {type: IDENT identifier {document_path {type: MEMBER "
335 " value: 'alpha'}}}"
336 " alias: 'zeta'} "
337 "criteria {type: OPERATOR "
338 " operator {name: '>' "
339 " param {type: IDENT identifier {document_path {type: MEMBER "
340 " value: 'delta'}}}"
341 " param {type: LITERAL literal"
342 " {type: V_DOUBLE"
343 " v_double: 1.0}}}}"
344 "order {expr {type: IDENT identifier {document_path {type: MEMBER "
345 " value: 'beta'}}}"
346 " direction: DESC} "
347 "grouping {type: IDENT identifier {document_path {type: MEMBER "
348 " value: 'alpha'}}}"
349 "grouping_criteria {type: OPERATOR "
350 " operator {name: '<' "
351 " param {type: IDENT identifier {document_path {type: MEMBER "
352 " value: 'lambda'}}}"
353 " param {type: LITERAL literal"
354 " {type: V_DOUBLE"
355 " v_double: 2.0}}}}";
356 ASSERT_NO_THROW(builder.build(msg));
357 EXPECT_STREQ(
358 "SELECT JSON_OBJECT('zeta', `_DERIVED_TABLE_`.`zeta`) AS doc FROM ("
359 "SELECT JSON_EXTRACT(doc,'$.alpha') AS `zeta` "
360 "FROM `xschema`.`xtable` "
361 "WHERE (JSON_EXTRACT(doc,'$.delta') > 1) "
362 "GROUP BY JSON_EXTRACT(doc,'$.alpha') "
363 "HAVING (JSON_EXTRACT(doc,'$.lambda') < 2) "
364 "ORDER BY JSON_EXTRACT(doc,'$.beta') DESC"
365 ") AS `_DERIVED_TABLE_`",
366 query.get().c_str());
367 }
368
369
TEST_F(Find_statement_builder_test,build_document_with_grouping)370 TEST_F(Find_statement_builder_test, build_document_with_grouping)
371 {
372 msg <<
373 "collection {name: 'xtable' schema: 'xschema'}"
374 "data_model: DOCUMENT "
375 "projection {source {type: IDENT identifier {document_path {type: MEMBER "
376 " value: 'alpha'}}}"
377 " alias: 'zeta'} "
378 "projection {source {type: IDENT identifier {document_path {type: MEMBER "
379 " value: 'gama'}}}"
380 " alias: 'ksi'} "
381 "grouping {type: IDENT identifier {document_path {type: MEMBER "
382 " value: 'alpha'}}}"
383 "grouping {type: IDENT identifier {document_path {type: MEMBER "
384 " value: 'gama'}}}";
385 ASSERT_NO_THROW(builder.build(msg));
386 EXPECT_EQ(
387 "SELECT JSON_OBJECT('zeta', `_DERIVED_TABLE_`.`zeta`,'ksi', `_DERIVED_TABLE_`.`ksi`) AS doc FROM ("
388 "SELECT JSON_EXTRACT(doc,'$.alpha') AS `zeta`,JSON_EXTRACT(doc,'$.gama') AS `ksi` "
389 "FROM `xschema`.`xtable` "
390 "GROUP BY JSON_EXTRACT(doc,'$.alpha'),JSON_EXTRACT(doc,'$.gama')"
391 ") AS `_DERIVED_TABLE_`",
392 query.get());
393 }
394
395
TEST_F(Find_statement_builder_test,build_document_with_grouping_no_projection)396 TEST_F(Find_statement_builder_test, build_document_with_grouping_no_projection)
397 {
398 msg <<
399 "collection {name: 'xtable' schema: 'xschema'}"
400 "data_model: DOCUMENT "
401 "grouping {type: IDENT identifier {document_path {type: MEMBER "
402 " value: 'beta'}}}";
403 EXPECT_THROW(builder.build(msg), ngs::Error_code);
404 }
405
406 } // namespace test
407 } // namespace xpl
408