1 /*
2  * Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved.
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 "update_statement_builder.h"
26 #include "mysqld_error.h"
27 #include "ngs_common/protocol_protobuf.h"
28 
29 #include <gtest/gtest.h>
30 
31 namespace xpl
32 {
33 namespace test
34 {
35 
36 class Update_statement_builder_impl: public Update_statement_builder
37 {
38 public:
Update_statement_builder_impl(Expression_generator & gen)39   Update_statement_builder_impl(Expression_generator &gen) : Update_statement_builder(gen) {}
40   using Update_statement_builder::add_operation;
41   using Update_statement_builder::add_table_operation;
42   using Update_statement_builder::add_document_operation;
43   using Update_statement_builder::add_document_operation_item;
44   using Update_statement_builder::Operation_list;
45   using Update_statement_builder::Operation_item;
46   using Update_statement_builder::Generator;
47 };
48 
49 
50 class Update_statement_builder_test : public ::testing::Test
51 {
52 public:
Update_statement_builder_test()53   Update_statement_builder_test()
54   : args(*msg.mutable_args()),
55     expr_gen(query, args, schema, true),
56     builder(expr_gen),
57     oper(-1)
58   {}
59   Update_statement_builder::Update msg;
60   Expression_generator::Args &args;
61   Query_string_builder query;
62   std::string schema;
63   Expression_generator expr_gen;
64   Update_statement_builder_impl builder;
65   int oper;
66 
67   enum {DM_DOCUMENT = 0, DM_TABLE = 1};
68 
69   typedef ::Mysqlx::Crud::UpdateOperation UpdateOperation;
70 };
71 
72 
73 
74 namespace
75 {
76 
operator <<(::google::protobuf::Message & msg,const std::string & txt)77 void operator<< (::google::protobuf::Message &msg, const std::string& txt)
78 {
79   ASSERT_TRUE(::google::protobuf::TextFormat::ParseFromString(txt, &msg));
80 }
81 
82 const std::string value_ = "value: {type: LITERAL literal {type: ";
83 const std::string value_1 = value_ + "V_DOUBLE v_double: 1.0}}";
84 const std::string value_2 = value_ + "V_STRING v_string: {value: 'two'}}}";
85 const std::string value_3 = value_ + "V_SINT v_signed_int: -3}}";
86 const std::string placeholder_0 = "value: {type: PLACEHOLDER position: 0}";
87 } // namespace
88 
89 
TEST_F(Update_statement_builder_test,add_operation_empty_list)90 TEST_F(Update_statement_builder_test, add_operation_empty_list)
91 {
92   Update_statement_builder_impl::Operation_list operation;
93   EXPECT_THROW(builder.add_operation(operation, DM_TABLE), ngs::Error_code);
94 }
95 
96 
TEST_F(Update_statement_builder_test,add_table_operation_one_item)97 TEST_F(Update_statement_builder_test, add_table_operation_one_item)
98 {
99   Update_statement_builder_impl::Operation_list operation;
100   *operation.Add() << "source {name: 'xfield'}"
101       "operation: SET " + value_1;
102   EXPECT_NO_THROW(builder.add_table_operation(operation));
103   EXPECT_EQ("`xfield`=1", query.get());
104 }
105 
106 
TEST_F(Update_statement_builder_test,add_table_operation_two_items)107 TEST_F(Update_statement_builder_test, add_table_operation_two_items)
108 {
109   Update_statement_builder_impl::Operation_list operation;
110   *operation.Add() << "source {name: 'xfield'}"
111       "operation: SET " + value_1;;
112   *operation.Add() << "source {name: 'yfield'}"
113       "operation: SET "  + value_2;
114   EXPECT_NO_THROW(builder.add_table_operation(operation));
115   EXPECT_EQ("`xfield`=1,`yfield`='two'", query.get());
116 }
117 
118 
TEST_F(Update_statement_builder_test,add_table_operation_two_items_same_source)119 TEST_F(Update_statement_builder_test, add_table_operation_two_items_same_source)
120 {
121   Update_statement_builder_impl::Operation_list operation;
122   *operation.Add() << "source {name: 'xfield'}"
123       "operation: SET " + value_1;;
124   *operation.Add() << "source {name: 'xfield'}"
125       "operation: SET "  + value_2;
126   EXPECT_NO_THROW(builder.add_table_operation(operation));
127   EXPECT_EQ("`xfield`=1,`xfield`='two'", query.get());
128 }
129 
130 
TEST_F(Update_statement_builder_test,add_table_operation_two_items_placeholder)131 TEST_F(Update_statement_builder_test, add_table_operation_two_items_placeholder)
132 {
133   *args.Add() << "type: V_DOUBLE v_double: 2.2";
134 
135   Update_statement_builder_impl::Operation_list operation;
136   *operation.Add() << "source {name: 'xfield'}"
137       "operation: SET " + value_1;;
138   *operation.Add() << "source {name: 'yfield'}"
139       "operation: SET " + placeholder_0;
140   EXPECT_NO_THROW(builder.add_table_operation(operation));
141   EXPECT_EQ("`xfield`=1,`yfield`=2.2", query.get());
142 }
143 
144 
TEST_F(Update_statement_builder_test,add_table_operation_empty_name)145 TEST_F(Update_statement_builder_test, add_table_operation_empty_name)
146 {
147   Update_statement_builder_impl::Operation_list operation;
148   *operation.Add() << "source {} operation: SET " + value_1;;
149   EXPECT_THROW(builder.add_table_operation(operation), ngs::Error_code);
150 }
151 
152 
TEST_F(Update_statement_builder_test,add_table_operation_item_name_with_table)153 TEST_F(Update_statement_builder_test, add_table_operation_item_name_with_table)
154 {
155   Update_statement_builder_impl::Operation_list operation;
156   *operation.Add() << "source {name: 'xfield' table_name: 'xtable'}"
157       "operation: SET " + value_1;
158   EXPECT_THROW(builder.add_table_operation(operation), ngs::Error_code);
159 }
160 
161 
TEST_F(Update_statement_builder_test,add_table_operation_item_name_with_table_and_schema)162 TEST_F(Update_statement_builder_test, add_table_operation_item_name_with_table_and_schema)
163 {
164   Update_statement_builder_impl::Operation_list operation;
165   *operation.Add() << "source {name: 'xfield' table_name: 'xtable' schema_name: 'xschema'}"
166       "operation: SET " + value_1;
167   EXPECT_THROW(builder.add_table_operation(operation), ngs::Error_code);
168 }
169 
170 
TEST_F(Update_statement_builder_test,add_operation_one_item_for_table)171 TEST_F(Update_statement_builder_test, add_operation_one_item_for_table)
172 {
173   Update_statement_builder_impl::Operation_list operation;
174   *operation.Add() << "source {name: 'xfield'}"
175         "operation: SET " + value_1;
176   EXPECT_NO_THROW(builder.add_operation(operation, DM_TABLE));
177   EXPECT_EQ(" SET `xfield`=1", query.get());
178 }
179 
180 namespace
181 {
182 const std::string table_full_message(
183     "collection {name: 'xtable' schema: 'xschema'}"
184     "data_model: TABLE "
185     "operation {source {name: 'yfield'}"
186     "           operation: SET"
187     "           value {type: LITERAL literal {type: V_OCTETS"
188     "                                     v_octets {value: 'booom'}}}}"
189     "criteria {type: OPERATOR "
190     "          operator {name: '>' "
191     "                    param {type: IDENT identifier {name: 'xfield'}}"
192     "                    param {type: LITERAL literal {type: V_DOUBLE"
193     "                                                     v_double: 1.0}}}}"
194     "order {expr {type: IDENT identifier {name: 'xfield'}}"
195     "       direction: DESC}");
196 } // namespace
197 
198 
TEST_F(Update_statement_builder_test,build_update_for_table)199 TEST_F(Update_statement_builder_test, build_update_for_table)
200 {
201   msg << table_full_message + "limit {row_count: 2}";
202   EXPECT_NO_THROW(builder.build(msg));
203   EXPECT_EQ("UPDATE `xschema`.`xtable`"
204       " SET `yfield`='booom'"
205       " WHERE (`xfield` > 1)"
206       " ORDER BY `xfield` DESC"
207       " LIMIT 2", query.get());
208 }
209 
TEST_F(Update_statement_builder_test,build_update_for_table_forrbiden_offset_in_limit)210 TEST_F(Update_statement_builder_test, build_update_for_table_forrbiden_offset_in_limit)
211 {
212   msg << table_full_message + "limit {row_count: 2 offset: 5}";
213   EXPECT_THROW(builder.build(msg), ngs::Error_code);
214 }
215 
216 
217 namespace
218 {
219 const std::string source_ = "source {document_path {type: MEMBER value: ";
220 const std::string source_index_ = "source {document_path {type: ARRAY_INDEX index: ";
221 const std::string source_first = source_ + "'first'}}";
222 const std::string source_second = source_ + "'second'}}";
223 const std::string source_third = source_ + "'third'}}";
224 const std::string source_index_first_0 = source_ + "'first'} document_path {type:ARRAY_INDEX index: 0}}";
225 const std::string source_index_0 = source_index_ + " 0}}";
226 const std::string source_index_1 = source_index_ + " 1}}";
227 
228 const std::string document_full_message(
229     "collection {name: 'xtable' schema: 'xschema'}"
230     "data_model: DOCUMENT "
231     "operation {source {document_path {type: MEMBER value: 'first'}}"
232     "           operation: ITEM_SET"
233     "           value: {type: LITERAL literal {type: V_DOUBLE v_double: 1.0}}}"
234     "criteria {type: OPERATOR "
235     "          operator {name: '>' "
236     "                    param {type: IDENT identifier {document_path {type: MEMBER value: 'second'}}}"
237     "                    param {type: LITERAL literal {type: V_DOUBLE"
238     "                                              v_double: 1.0}}}}"
239     "order {expr {type: IDENT identifier {document_path {type: MEMBER value: 'third'}}}"
240     "       direction: DESC}");
241 } // namespace
242 
243 
TEST_F(Update_statement_builder_test,add_document_operation_not_allowed_set)244 TEST_F(Update_statement_builder_test, add_document_operation_not_allowed_set)
245 {
246   Update_statement_builder_impl::Operation_list operation;
247   *operation.Add() << source_first + "operation: SET " + value_1;
248   EXPECT_THROW(builder.add_document_operation(operation), ngs::Error_code);
249 }
250 
251 
TEST_F(Update_statement_builder_test,add_document_operation_remove)252 TEST_F(Update_statement_builder_test, add_document_operation_remove)
253 {
254   Update_statement_builder_impl::Operation_list operation;
255   *operation.Add() << source_first + "operation: ITEM_REMOVE ";
256   EXPECT_NO_THROW(builder.add_document_operation(operation));
257   EXPECT_EQ("doc=JSON_REMOVE(doc,'$.first')", query.get());
258 }
259 
260 
TEST_F(Update_statement_builder_test,add_document_operation_set)261 TEST_F(Update_statement_builder_test, add_document_operation_set)
262 {
263   Update_statement_builder_impl::Operation_list operation;
264   *operation.Add() << source_first + "operation: ITEM_SET " + value_1;
265   EXPECT_NO_THROW(builder.add_document_operation(operation));
266   EXPECT_EQ("doc=JSON_SET(doc,'$.first',1)",
267             query.get());
268 }
269 
270 
TEST_F(Update_statement_builder_test,add_document_operation_replace)271 TEST_F(Update_statement_builder_test, add_document_operation_replace)
272 {
273   Update_statement_builder_impl::Operation_list operation;
274   *operation.Add() << source_first + "operation: ITEM_REPLACE " + value_1;
275   EXPECT_NO_THROW(builder.add_document_operation(operation));
276   EXPECT_EQ("doc=JSON_REPLACE(doc,'$.first',1)",
277             query.get());
278 }
279 
280 
TEST_F(Update_statement_builder_test,add_document_operation_merge)281 TEST_F(Update_statement_builder_test, add_document_operation_merge)
282 {
283   Update_statement_builder_impl::Operation_list operation;
284   *operation.Add() << source_first +
285       "operation: ITEM_MERGE "
286       "value {type: LITERAL literal {type: V_OCTETS v_octets {value: '{\\\"two\\\": 2.0}'}}}";
287   ASSERT_NO_THROW(builder.add_document_operation(operation));
288   EXPECT_EQ(
289       "doc=JSON_MERGE(doc,IF(JSON_TYPE('{\\\"two\\\": 2.0}')='OBJECT',"
290       "JSON_REMOVE('{\\\"two\\\": 2.0}','$._id'),'_ERROR_'))",
291             query.get());
292 }
293 
294 
TEST_F(Update_statement_builder_test,add_document_operation_array_insert)295 TEST_F(Update_statement_builder_test, add_document_operation_array_insert)
296 {
297   Update_statement_builder_impl::Operation_list operation;
298   *operation.Add() << source_index_first_0 + "operation: ARRAY_INSERT " + value_1;
299   EXPECT_NO_THROW(builder.add_document_operation(operation));
300   EXPECT_EQ("doc=JSON_ARRAY_INSERT(doc,'$.first[0]',1)",
301         query.get());
302 }
303 
304 
TEST_F(Update_statement_builder_test,add_document_operation_array_append)305 TEST_F(Update_statement_builder_test, add_document_operation_array_append)
306 {
307   Update_statement_builder_impl::Operation_list operation;
308   *operation.Add() << source_first + "operation: ARRAY_APPEND " + value_1;
309   EXPECT_NO_THROW(builder.add_document_operation(operation));
310   EXPECT_EQ("doc=JSON_ARRAY_APPEND(doc,'$.first',1)",
311             query.get());
312 }
313 
314 
TEST_F(Update_statement_builder_test,add_document_operation_array_append_twice)315 TEST_F(Update_statement_builder_test, add_document_operation_array_append_twice)
316 {
317   Update_statement_builder_impl::Operation_list operation;
318   *operation.Add() << source_first + "operation: ARRAY_APPEND " + value_1;
319   *operation.Add() << source_first + "operation: ARRAY_APPEND " + value_2;
320   EXPECT_NO_THROW(builder.add_document_operation(operation));
321   EXPECT_EQ("doc=JSON_ARRAY_APPEND(doc,'$.first',1,'$.first','two')",
322             query.get());
323 }
324 
325 
TEST_F(Update_statement_builder_test,add_document_operation_remove_twice)326 TEST_F(Update_statement_builder_test, add_document_operation_remove_twice)
327 {
328   Update_statement_builder_impl::Operation_list operation;
329   *operation.Add() << source_first + "operation: ITEM_REMOVE ";
330   *operation.Add() << source_second + "operation: ITEM_REMOVE ";
331   EXPECT_NO_THROW(builder.add_document_operation(operation));
332   EXPECT_EQ("doc=JSON_REMOVE(doc,'$.first','$.second')",
333             query.get());
334 }
335 
336 
TEST_F(Update_statement_builder_test,add_document_operation_set_twice)337 TEST_F(Update_statement_builder_test, add_document_operation_set_twice)
338 {
339   Update_statement_builder_impl::Operation_list operation;
340   *operation.Add() << source_first + "operation: ITEM_SET " + value_1;
341   *operation.Add() << source_second + "operation: ITEM_SET " + value_2;
342   EXPECT_NO_THROW(builder.add_document_operation(operation));
343   EXPECT_EQ("doc=JSON_SET(doc,'$.first',1,'$.second','two')",
344             query.get());
345 }
346 
347 
TEST_F(Update_statement_builder_test,add_document_operation_set_twice_placeholder)348 TEST_F(Update_statement_builder_test, add_document_operation_set_twice_placeholder)
349 {
350   *args.Add() << "type: V_DOUBLE v_double: 2.2";
351   *args.Add() << "type: V_OCTETS v_octets {value: '$.second'}";
352   Update_statement_builder_impl::Operation_list operation;
353   *operation.Add() << source_first + "operation: ITEM_SET " + value_1;
354   *operation.Add() << source_second + "operation: ITEM_SET " + placeholder_0;
355   EXPECT_NO_THROW(builder.add_document_operation(operation));
356   EXPECT_EQ("doc=JSON_SET(doc,'$.first',1,'$.second',2.2)",
357             query.get());
358 }
359 
360 
TEST_F(Update_statement_builder_test,add_document_operation_merge_twice)361 TEST_F(Update_statement_builder_test, add_document_operation_merge_twice)
362 {
363   Update_statement_builder_impl::Operation_list operation;
364   *operation.Add() << "source {} operation: ITEM_MERGE "
365       "value {type: LITERAL literal {type: V_OCTETS v_octets {value: '{\\\"two\\\": 2.0}'}}}";
366   *operation.Add() << "source {} operation: ITEM_MERGE "
367       "value {type: LITERAL literal {type: V_OCTETS v_octets {value: '{\\\"three\\\": 3.0}'}}}";
368   ASSERT_NO_THROW(builder.add_document_operation(operation));
369   EXPECT_EQ(
370       "doc=JSON_MERGE(doc,IF(JSON_TYPE('{\\\"two\\\": 2.0}')='OBJECT',"
371       "JSON_REMOVE('{\\\"two\\\": 2.0}','$._id'),'_ERROR_'),"
372       "IF(JSON_TYPE('{\\\"three\\\": 3.0}')='OBJECT',"
373       "JSON_REMOVE('{\\\"three\\\": 3.0}','$._id'),'_ERROR_'))",
374             query.get());
375 }
376 
377 
TEST_F(Update_statement_builder_test,add_document_operation_remove_set)378 TEST_F(Update_statement_builder_test, add_document_operation_remove_set)
379 {
380   Update_statement_builder_impl::Operation_list operation;
381   *operation.Add() << source_first + "operation: ITEM_REMOVE ";
382   *operation.Add() << source_second + "operation: ITEM_SET " + value_2;
383   EXPECT_NO_THROW(builder.add_document_operation(operation));
384   EXPECT_EQ("doc=JSON_SET(JSON_REMOVE(doc,'$.first'),'$.second','two')",
385             query.get());
386 }
387 
388 
TEST_F(Update_statement_builder_test,add_document_operation_remove_twice_set)389 TEST_F(Update_statement_builder_test, add_document_operation_remove_twice_set)
390 {
391   Update_statement_builder_impl::Operation_list operation;
392   *operation.Add() << source_first + "operation: ITEM_REMOVE ";
393   *operation.Add() << source_second + "operation: ITEM_REMOVE ";
394   *operation.Add() << source_third + "operation: ITEM_SET " + value_3;
395   EXPECT_NO_THROW(builder.add_document_operation(operation));
396   EXPECT_EQ("doc=JSON_SET(JSON_REMOVE(doc,'$.first','$.second'),'$.third',-3)",
397             query.get());
398 }
399 
400 
TEST_F(Update_statement_builder_test,add_document_operation_set_remove_set)401 TEST_F(Update_statement_builder_test, add_document_operation_set_remove_set)
402 {
403   Update_statement_builder_impl::Operation_list operation;
404   *operation.Add() << source_first + "operation: ITEM_SET " + value_1;
405   *operation.Add() << source_second + "operation: ITEM_REMOVE ";
406   *operation.Add() << source_third + "operation: ITEM_SET "  + value_3;
407   EXPECT_NO_THROW(builder.add_document_operation(operation));
408   EXPECT_EQ("doc=JSON_SET(JSON_REMOVE("
409             "JSON_SET(doc,'$.first',1),'$.second'),'$.third',-3)",
410             query.get());
411 }
412 
413 
TEST_F(Update_statement_builder_test,add_document_operation_set_merge)414 TEST_F(Update_statement_builder_test, add_document_operation_set_merge)
415 {
416   Update_statement_builder_impl::Operation_list operation;
417   *operation.Add() << source_first + "operation: ITEM_SET " + value_1;
418   *operation.Add() << "source {} operation: ITEM_MERGE "
419       "value {type: LITERAL literal {type: V_OCTETS v_octets {value: '{\\\"three\\\": 3.0}'}}}";
420   ASSERT_NO_THROW(builder.add_document_operation(operation));
421   EXPECT_EQ(
422       "doc=JSON_MERGE(JSON_SET(doc,'$.first',1),"
423       "IF(JSON_TYPE('{\\\"three\\\": 3.0}')='OBJECT',"
424       "JSON_REMOVE('{\\\"three\\\": 3.0}','$._id'),'_ERROR_'))",
425             query.get());
426 }
427 
428 
TEST_F(Update_statement_builder_test,add_document_operation_item_forbiden_column)429 TEST_F(Update_statement_builder_test, add_document_operation_item_forbiden_column)
430 {
431   Update_statement_builder_impl::Operation_item operation;
432   operation << "source {name: 'xcolumn'} operation: ITEM_SET " + value_3;
433   ASSERT_THROW(builder.add_document_operation_item(operation, oper),
434                ngs::Error_code);
435   ASSERT_EQ(UpdateOperation::ITEM_SET, oper);
436 }
437 
438 
TEST_F(Update_statement_builder_test,add_document_operation_item_forbiden_schema)439 TEST_F(Update_statement_builder_test, add_document_operation_item_forbiden_schema)
440 {
441   Update_statement_builder_impl::Operation_item operation;
442   operation << "source {schema_name: 'xschema'} operation: ITEM_SET "
443       + value_3;
444   ASSERT_THROW(builder.add_document_operation_item(operation, oper),
445                ngs::Error_code);
446   ASSERT_EQ(UpdateOperation::ITEM_SET, oper);
447 }
448 
449 
TEST_F(Update_statement_builder_test,add_document_operation_item_forbiden_table)450 TEST_F(Update_statement_builder_test, add_document_operation_item_forbiden_table)
451 {
452   Update_statement_builder_impl::Operation_item operation;
453   operation << "source {table_name: 'xtable'} operation: ITEM_SET "
454       + value_3;
455   ASSERT_THROW(builder.add_document_operation_item(operation, oper),
456                ngs::Error_code);
457   ASSERT_EQ(UpdateOperation::ITEM_SET, oper);
458 }
459 
460 
TEST_F(Update_statement_builder_test,add_document_operation_item_forbiden_id_change)461 TEST_F(Update_statement_builder_test, add_document_operation_item_forbiden_id_change)
462 {
463   Update_statement_builder_impl::Operation_item operation;
464   operation << "source {document_path {type: MEMBER value: '_id'}} operation: ITEM_SET "
465       + value_3;
466   ASSERT_THROW(builder.add_document_operation_item(operation, oper),
467                ngs::Error_code);
468   ASSERT_EQ(UpdateOperation::ITEM_SET, oper);
469 }
470 
471 
TEST_F(Update_statement_builder_test,add_document_operation_item_empty_document_path)472 TEST_F(Update_statement_builder_test, add_document_operation_item_empty_document_path)
473 {
474   Update_statement_builder_impl::Operation_item operation;
475   operation << "source {} operation: ITEM_SET " + value_3;
476   ASSERT_THROW(builder.add_document_operation_item(operation, oper),
477                ngs::Error_code);
478   ASSERT_EQ(UpdateOperation::ITEM_SET, oper);
479 }
480 
481 
TEST_F(Update_statement_builder_test,add_document_operation_item_root_path)482 TEST_F(Update_statement_builder_test, add_document_operation_item_root_path)
483 {
484   Update_statement_builder_impl::Operation_item operation;
485   operation << "source {document_path {type: MEMBER value: ''}} operation: ITEM_SET " + value_3;
486   ASSERT_NO_THROW(builder.add_document_operation_item(operation, oper));
487   ASSERT_EQ(UpdateOperation::ITEM_SET, oper);
488 }
489 
490 
TEST_F(Update_statement_builder_test,add_document_operation_item_empty_member)491 TEST_F(Update_statement_builder_test, add_document_operation_item_empty_member)
492 {
493   Update_statement_builder_impl::Operation_item operation;
494   operation << "source {document_path {type: MEMBER value: 'first'} "
495       "document_path {type: MEMBER value: ''}} "
496       "operation: ITEM_SET " + value_3;
497   ASSERT_THROW(builder.add_document_operation_item(operation, oper),
498                xpl::Expression_generator::Error);
499   ASSERT_EQ(UpdateOperation::ITEM_SET, oper);
500 }
501 
502 
TEST_F(Update_statement_builder_test,add_document_operation_item_empty_member_reverse)503 TEST_F(Update_statement_builder_test, add_document_operation_item_empty_member_reverse)
504 {
505   Update_statement_builder_impl::Operation_item operation;
506   operation << "source {document_path {type: MEMBER value: ''} "
507       "document_path {type: MEMBER value: 'first'}} "
508       "operation: ITEM_SET " + value_3;
509   ASSERT_THROW(builder.add_document_operation_item(operation, oper),
510                xpl::Expression_generator::Error);
511   ASSERT_EQ(UpdateOperation::ITEM_SET, oper);
512 }
513 
514 
TEST_F(Update_statement_builder_test,add_document_operation_item_root_as_array)515 TEST_F(Update_statement_builder_test, add_document_operation_item_root_as_array)
516 {
517   Update_statement_builder_impl::Operation_item operation;
518   operation << source_index_0 + "operation: ITEM_SET " + value_3;
519   ASSERT_THROW(builder.add_document_operation_item(operation, oper),
520                ngs::Error_code);
521   ASSERT_EQ(UpdateOperation::ITEM_SET, oper);
522 }
523 
524 
TEST_F(Update_statement_builder_test,add_document_operation_item_root_as_array_asterisk)525 TEST_F(Update_statement_builder_test, add_document_operation_item_root_as_array_asterisk)
526 {
527   Update_statement_builder_impl::Operation_item operation;
528   operation << "source {document_path {type: ARRAY_INDEX_ASTERISK}} "
529       "operation: ITEM_SET " + value_3;
530   ASSERT_THROW(builder.add_document_operation_item(operation, oper),
531                ngs::Error_code);
532   ASSERT_EQ(UpdateOperation::ITEM_SET, oper);
533 }
534 
535 
TEST_F(Update_statement_builder_test,add_document_operation_item_root_double_asterisk)536 TEST_F(Update_statement_builder_test, add_document_operation_item_root_double_asterisk)
537 {
538   Update_statement_builder_impl::Operation_item operation;
539   operation << "source {document_path {type: DOUBLE_ASTERISK}} "
540       "operation: ITEM_SET " + value_3;
541   ASSERT_THROW(builder.add_document_operation_item(operation, oper),
542                ngs::Error_code);
543   ASSERT_EQ(UpdateOperation::ITEM_SET, oper);
544 }
545 
546 
TEST_F(Update_statement_builder_test,add_operation_one_item_for_document)547 TEST_F(Update_statement_builder_test, add_operation_one_item_for_document)
548 {
549   Update_statement_builder_impl::Operation_list operation;
550   *operation.Add() << source_first + "operation: ITEM_SET " + value_1;
551   EXPECT_NO_THROW(builder.add_operation(operation, DM_DOCUMENT));
552   EXPECT_EQ(" SET doc=JSON_SET(doc,'$.first',1)",
553             query.get());
554 }
555 
556 
TEST_F(Update_statement_builder_test,build_update_for_document)557 TEST_F(Update_statement_builder_test, build_update_for_document)
558 {
559   msg << document_full_message + "limit {row_count: 2}";
560   EXPECT_NO_THROW(builder.build(msg));
561   EXPECT_EQ("UPDATE `xschema`.`xtable` "
562       "SET doc=JSON_SET(doc,'$.first',1) "
563       "WHERE (JSON_EXTRACT(doc,'$.second') > 1) "
564       "ORDER BY JSON_EXTRACT(doc,'$.third') "
565       "DESC LIMIT 2",
566       query.get());
567 }
568 
569 
570 namespace
571 {
get_operation(const std::string & name,const std::string & member,const std::string & oper,const std::string & value)572 std::string get_operation(const std::string &name, const std::string &member,
573                           const std::string &oper, const std::string &value)
574 {
575   std::string str("source {");
576   if (!name.empty())
577     str += "name: '" + name + "' ";
578   if (!member.empty())
579   {
580     str += "document_path {type: ";
581     if (isdigit(member[0]))
582     {
583       str += "ARRAY_INDEX index: " + member;
584     }
585     else
586     {
587       str += "MEMBER ";
588       if (member != "$")
589         str += "value: '" + member + "' ";
590     }
591     str += "}";
592   }
593   str += "} operation: " + oper;
594   if (!value.empty())
595     str += " " + value;
596   return str;
597 }
598 
599 } // namespace
600 
601 
TEST_F(Update_statement_builder_test,add_document_operation_set_whole_doc)602 TEST_F(Update_statement_builder_test, add_document_operation_set_whole_doc)
603 {
604   Update_statement_builder_impl::Operation_list operation;
605   *operation.Add() << get_operation("", "$", "ITEM_SET", value_2);
606   EXPECT_NO_THROW(builder.add_document_operation(operation));
607   EXPECT_EQ("doc=JSON_SET(doc,'$','two')",
608             query.get());
609 }
610 
611 
TEST_F(Update_statement_builder_test,add_table_operation_set_needless_doc_path)612 TEST_F(Update_statement_builder_test, add_table_operation_set_needless_doc_path)
613 {
614   Update_statement_builder_impl::Operation_list operation;
615   *operation.Add() << get_operation("xfield", "first", "SET", value_1);
616   EXPECT_THROW(builder.add_table_operation(operation), ngs::Error_code);
617 }
618 
619 
TEST_F(Update_statement_builder_test,add_table_operation_item_set_missing_doc_path)620 TEST_F(Update_statement_builder_test, add_table_operation_item_set_missing_doc_path)
621 {
622   Update_statement_builder_impl::Operation_list operation;
623   *operation.Add() << get_operation("xfield", "", "ITEM_SET", value_1);
624   EXPECT_THROW(builder.add_table_operation(operation), ngs::Error_code);
625 }
626 
627 
TEST_F(Update_statement_builder_test,add_table_operation_item_set)628 TEST_F(Update_statement_builder_test, add_table_operation_item_set)
629 {
630   Update_statement_builder_impl::Operation_list operation;
631   *operation.Add() << get_operation("xfield", "first", "ITEM_SET", value_1);
632   ASSERT_NO_THROW(builder.add_table_operation(operation));
633   EXPECT_EQ("`xfield`=JSON_SET(`xfield`,'$.first',1)",
634             query.get());
635 }
636 
637 
TEST_F(Update_statement_builder_test,add_table_operation_item_set_twice)638 TEST_F(Update_statement_builder_test, add_table_operation_item_set_twice)
639 {
640   Update_statement_builder_impl::Operation_list operation;
641   *operation.Add() << get_operation("xfield", "first", "ITEM_SET", value_1);
642   *operation.Add() << get_operation("xfield", "second", "ITEM_SET", value_2);
643   ASSERT_NO_THROW(builder.add_table_operation(operation));
644   EXPECT_EQ(
645       "`xfield`=JSON_SET(`xfield`,'$.first',1,'$.second','two')",
646             query.get());
647 }
648 
649 
TEST_F(Update_statement_builder_test,add_table_operation_item_set_twice_but_different)650 TEST_F(Update_statement_builder_test, add_table_operation_item_set_twice_but_different)
651 {
652   Update_statement_builder_impl::Operation_list operation;
653   *operation.Add() << get_operation("xfield", "first", "ITEM_SET", value_1);
654   *operation.Add() << get_operation("yfield", "second", "ITEM_SET", value_2);
655   ASSERT_NO_THROW(builder.add_table_operation(operation));
656   EXPECT_EQ(
657       "`xfield`=JSON_SET(`xfield`,'$.first',1),"
658       "`yfield`=JSON_SET(`yfield`,'$.second','two')",
659             query.get());
660 }
661 
662 
TEST_F(Update_statement_builder_test,add_table_operation_item_set_triple)663 TEST_F(Update_statement_builder_test, add_table_operation_item_set_triple)
664 {
665   Update_statement_builder_impl::Operation_list operation;
666   *operation.Add() << get_operation("xfield", "first", "ITEM_SET", value_1);
667   *operation.Add() << get_operation("xfield", "second", "ITEM_SET", value_2);
668   *operation.Add() << get_operation("xfield", "third", "ITEM_SET", value_3);
669   ASSERT_NO_THROW(builder.add_table_operation(operation));
670   EXPECT_EQ(
671       "`xfield`=JSON_SET(`xfield`,'$.first',1,'$.second','two','$.third',-3)",
672             query.get());
673 }
674 
675 
TEST_F(Update_statement_builder_test,add_table_operation_item_set_mix_first)676 TEST_F(Update_statement_builder_test, add_table_operation_item_set_mix_first)
677 {
678   Update_statement_builder_impl::Operation_list operation;
679   *operation.Add() << get_operation("xfield", "", "SET", value_1);
680   *operation.Add() << get_operation("xfield", "second", "ITEM_SET", value_2);
681   *operation.Add() << get_operation("xfield", "third", "ITEM_SET", value_3);
682   ASSERT_NO_THROW(builder.add_table_operation(operation));
683   EXPECT_EQ(
684       "`xfield`=1,"
685       "`xfield`=JSON_SET(`xfield`,'$.second','two','$.third',-3)",
686             query.get());
687 }
688 
689 
TEST_F(Update_statement_builder_test,add_table_operation_item_set_mix_last)690 TEST_F(Update_statement_builder_test, add_table_operation_item_set_mix_last)
691 {
692   Update_statement_builder_impl::Operation_list operation;
693   *operation.Add() << get_operation("xfield", "second", "ITEM_SET", value_2);
694   *operation.Add() << get_operation("xfield", "third", "ITEM_SET", value_3);
695   *operation.Add() << get_operation("xfield", "", "SET", value_1);
696   ASSERT_NO_THROW(builder.add_table_operation(operation));
697   EXPECT_EQ(
698       "`xfield`=JSON_SET(`xfield`,'$.second','two','$.third',-3),"
699       "`xfield`=1",
700             query.get());
701 }
702 
703 
TEST_F(Update_statement_builder_test,add_table_operation_item_set_mix_middle)704 TEST_F(Update_statement_builder_test, add_table_operation_item_set_mix_middle)
705 {
706   Update_statement_builder_impl::Operation_list operation;
707   *operation.Add() << get_operation("xfield", "second", "ITEM_SET", value_2);
708   *operation.Add() << get_operation("xfield", "", "SET", value_1);
709   *operation.Add() << get_operation("xfield", "third", "ITEM_SET", value_3);
710   ASSERT_NO_THROW(builder.add_table_operation(operation));
711   EXPECT_EQ(
712       "`xfield`=JSON_SET(`xfield`,'$.second','two'),"
713       "`xfield`=1,"
714       "`xfield`=JSON_SET(`xfield`,'$.third',-3)",
715             query.get());
716 }
717 
718 
TEST_F(Update_statement_builder_test,add_table_operation_item_set_fourth)719 TEST_F(Update_statement_builder_test, add_table_operation_item_set_fourth)
720 {
721   Update_statement_builder_impl::Operation_list operation;
722   *operation.Add() << get_operation("xfield", "first", "ITEM_SET", value_1);
723   *operation.Add() << get_operation("xfield", "second", "ITEM_SET", value_2);
724   *operation.Add() << get_operation("yfield", "first", "ITEM_SET", value_1);
725   *operation.Add() << get_operation("yfield", "second", "ITEM_SET", value_2);
726   ASSERT_NO_THROW(builder.add_table_operation(operation));
727   EXPECT_EQ(
728       "`xfield`=JSON_SET(`xfield`,'$.first',1,'$.second','two'),"
729       "`yfield`=JSON_SET(`yfield`,'$.first',1,'$.second','two')",
730             query.get());
731 }
732 
733 
TEST_F(Update_statement_builder_test,add_table_operation_item_remove_one)734 TEST_F(Update_statement_builder_test, add_table_operation_item_remove_one)
735 {
736   Update_statement_builder_impl::Operation_list operation;
737   *operation.Add() << get_operation("xfield", "first", "ITEM_REMOVE", "");
738   ASSERT_NO_THROW(builder.add_table_operation(operation));
739   EXPECT_EQ(
740       "`xfield`=JSON_REMOVE(`xfield`,'$.first')",
741             query.get());
742 }
743 
744 
TEST_F(Update_statement_builder_test,add_table_operation_item_remove_twice)745 TEST_F(Update_statement_builder_test, add_table_operation_item_remove_twice)
746 {
747   Update_statement_builder_impl::Operation_list operation;
748   *operation.Add() << get_operation("xfield", "first", "ITEM_REMOVE", "");
749   *operation.Add() << get_operation("xfield", "second", "ITEM_REMOVE", "");
750   ASSERT_NO_THROW(builder.add_table_operation(operation));
751   EXPECT_EQ(
752       "`xfield`=JSON_REMOVE(`xfield`,'$.first','$.second')",
753             query.get());
754 }
755 
756 
TEST_F(Update_statement_builder_test,add_table_operation_item_replace_one)757 TEST_F(Update_statement_builder_test, add_table_operation_item_replace_one)
758 {
759   Update_statement_builder_impl::Operation_list operation;
760   *operation.Add() << get_operation("xfield", "first", "ITEM_REPLACE", value_1);
761   ASSERT_NO_THROW(builder.add_table_operation(operation));
762   EXPECT_EQ(
763       "`xfield`=JSON_REPLACE(`xfield`,'$.first',1)",
764             query.get());
765 }
766 
767 
TEST_F(Update_statement_builder_test,add_table_operation_item_replace_twice)768 TEST_F(Update_statement_builder_test, add_table_operation_item_replace_twice)
769 {
770   Update_statement_builder_impl::Operation_list operation;
771   *operation.Add() << get_operation("xfield", "first", "ITEM_REPLACE", value_1);
772   *operation.Add() << get_operation("xfield", "second", "ITEM_REPLACE", value_2);
773   ASSERT_NO_THROW(builder.add_table_operation(operation));
774   EXPECT_EQ(
775       "`xfield`=JSON_REPLACE(`xfield`,'$.first',1,'$.second','two')",
776             query.get());
777 }
778 
779 
TEST_F(Update_statement_builder_test,add_table_operation_item_merge_one)780 TEST_F(Update_statement_builder_test, add_table_operation_item_merge_one)
781 {
782   Update_statement_builder_impl::Operation_list operation;
783   *operation.Add() << get_operation("xfield", "first", "ITEM_MERGE", value_1);
784   ASSERT_NO_THROW(builder.add_table_operation(operation));
785   EXPECT_EQ(
786       "`xfield`=JSON_MERGE(`xfield`,1)",
787             query.get());
788 }
789 
790 
TEST_F(Update_statement_builder_test,add_table_operation_item_merge_twice)791 TEST_F(Update_statement_builder_test, add_table_operation_item_merge_twice)
792 {
793   Update_statement_builder_impl::Operation_list operation;
794   *operation.Add() << get_operation("xfield", "first", "ITEM_MERGE", value_1);
795   *operation.Add() << get_operation("xfield", "second", "ITEM_MERGE", value_2);
796   ASSERT_NO_THROW(builder.add_table_operation(operation));
797   EXPECT_EQ(
798       "`xfield`=JSON_MERGE(`xfield`,1,'two')",
799             query.get());
800 }
801 
802 
TEST_F(Update_statement_builder_test,add_table_operation_array_insert_one)803 TEST_F(Update_statement_builder_test, add_table_operation_array_insert_one)
804 {
805   Update_statement_builder_impl::Operation_list operation;
806   *operation.Add() << get_operation("xfield", "0", "ARRAY_INSERT", value_1);
807   ASSERT_NO_THROW(builder.add_table_operation(operation));
808   EXPECT_EQ(
809       "`xfield`=JSON_ARRAY_INSERT(`xfield`,'$[0]',1)",
810             query.get());
811 }
812 
813 
TEST_F(Update_statement_builder_test,add_table_operation_array_insert_twice)814 TEST_F(Update_statement_builder_test, add_table_operation_array_insert_twice)
815 {
816   Update_statement_builder_impl::Operation_list operation;
817   *operation.Add() << get_operation("xfield", "0", "ARRAY_INSERT", value_1);
818   *operation.Add() << get_operation("xfield", "1", "ARRAY_INSERT", value_2);
819   ASSERT_NO_THROW(builder.add_table_operation(operation));
820   EXPECT_EQ(
821       "`xfield`=JSON_ARRAY_INSERT(`xfield`,'$[0]',1,'$[1]','two')",
822             query.get());
823 }
824 
825 
TEST_F(Update_statement_builder_test,add_table_operation_array_append_one)826 TEST_F(Update_statement_builder_test, add_table_operation_array_append_one)
827 {
828   Update_statement_builder_impl::Operation_list operation;
829   *operation.Add() << get_operation("xfield", "first", "ARRAY_APPEND", value_1);
830   ASSERT_NO_THROW(builder.add_table_operation(operation));
831   EXPECT_EQ(
832       "`xfield`=JSON_ARRAY_APPEND(`xfield`,'$.first',1)",
833             query.get());
834 }
835 
836 
TEST_F(Update_statement_builder_test,add_table_operation_array_append_twice)837 TEST_F(Update_statement_builder_test, add_table_operation_array_append_twice)
838 {
839   Update_statement_builder_impl::Operation_list operation;
840   *operation.Add() << get_operation("xfield", "first", "ARRAY_APPEND", value_1);
841   *operation.Add() << get_operation("xfield", "second", "ARRAY_APPEND", value_2);
842   ASSERT_NO_THROW(builder.add_table_operation(operation));
843   EXPECT_EQ(
844       "`xfield`=JSON_ARRAY_APPEND(`xfield`,'$.first',1,'$.second','two')",
845             query.get());
846 }
847 
848 
TEST_F(Update_statement_builder_test,add_table_operation_array_append_twice_placeholder)849 TEST_F(Update_statement_builder_test, add_table_operation_array_append_twice_placeholder)
850 {
851   *args.Add() << "type: V_DOUBLE v_double: 2.2";
852   Update_statement_builder_impl::Operation_list operation;
853   *operation.Add() << get_operation("xfield", "first", "ARRAY_APPEND", value_1);
854   *operation.Add() << get_operation("xfield", "second", "ARRAY_APPEND", placeholder_0);
855   ASSERT_NO_THROW(builder.add_table_operation(operation));
856   EXPECT_EQ(
857       "`xfield`=JSON_ARRAY_APPEND(`xfield`,'$.first',1,'$.second',2.2)",
858             query.get());
859 }
860 
861 } // namespace test
862 } // namespace xpl
863 
864 
865