1 /* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 #include <gtest/gtest.h>
24 #include <cstring>
25 
26 #include "sql/item_json_func.h"
27 #include "sql/json_diff.h"
28 #include "sql/json_dom.h"
29 #include "sql/sql_class.h"
30 #include "sql/sql_list.h"
31 #include "unittest/gunit/base_mock_field.h"
32 #include "unittest/gunit/benchmark.h"
33 #include "unittest/gunit/fake_table.h"
34 #include "unittest/gunit/test_utils.h"
35 
36 namespace item_json_func_unittest {
37 
38 class ItemJsonFuncTest : public ::testing::Test {
39  protected:
SetUp()40   void SetUp() override {
41     initializer.SetUp();
42     m_table.in_use = thd();
43   }
44 
TearDown()45   void TearDown() override {
46     m_table.cleanup_partial_update();
47     initializer.TearDown();
48   }
49 
thd()50   THD *thd() { return initializer.thd(); }
51 
52   my_testing::Server_initializer initializer;
53 
54   Base_mock_field_json m_field{};
55 
56   Fake_TABLE m_table{&m_field};
57 };
58 
59 /**
60   Parse a JSON text and return its DOM representation.
61   @param json_text null-terminated string of JSON text
62   @return a DOM representing the JSON document
63 */
parse_json(const char * json_text)64 static Json_dom_ptr parse_json(const char *json_text) {
65   auto dom =
66       Json_dom::parse(json_text, std::strlen(json_text), nullptr, nullptr);
67   EXPECT_NE(nullptr, dom);
68   return dom;
69 }
70 
new_item_string(const char * str)71 static Item_string *new_item_string(const char *str) {
72   return new Item_string(str, std::strlen(str), &my_charset_utf8mb4_bin);
73 }
74 
store_json(Field_json * field,const char * json_text)75 static void store_json(Field_json *field, const char *json_text) {
76   if (json_text == nullptr) {
77     EXPECT_EQ(TYPE_OK, set_field_to_null(field));
78   } else {
79     field->set_notnull();
80     Json_wrapper doc(parse_json(json_text));
81     EXPECT_EQ(TYPE_OK, field->store_json(&doc));
82   }
83 }
84 
85 /**
86   Perform a partial update on a JSON column and verify the result.
87 
88   @param func   the JSON function to invoke
89   @param field  the JSON column to update
90   @param orig_json  text representation of the original JSON value
91   @param new_json   text representation of the expected value in the
92                     column after the partial update
93   @param binary_update   whether binary diffs can be used
94   @param logical_update  whether logical diffs can be used
95 */
do_partial_update(Item_json_func * func,Field_json * field,const char * orig_json,const char * new_json,bool binary_update,bool logical_update)96 static void do_partial_update(Item_json_func *func, Field_json *field,
97                               const char *orig_json, const char *new_json,
98                               bool binary_update, bool logical_update) {
99   const auto table = field->table;
100 
101   if (!func->fixed) {
102     EXPECT_FALSE(func->fix_fields(table->in_use, nullptr));
103     EXPECT_TRUE(func->supports_partial_update(field));
104     func->mark_for_partial_update(field);
105   }
106 
107   table->clear_partial_update_diffs();
108 
109   store_json(field, orig_json);
110 
111   EXPECT_TRUE(table->is_binary_diff_enabled(field));
112   EXPECT_TRUE(table->is_logical_diff_enabled(field));
113 
114   Json_wrapper res1;
115   EXPECT_FALSE(func->val_json(&res1));
116   EXPECT_EQ(new_json == nullptr, func->null_value);
117   EXPECT_EQ(binary_update, table->is_binary_diff_enabled(field));
118   EXPECT_EQ(logical_update, table->is_logical_diff_enabled(field));
119 
120   if (new_json == nullptr) return;
121 
122   Json_wrapper new_doc(parse_json(new_json));
123   EXPECT_EQ(0, res1.compare(new_doc));
124 
125   if (!logical_update) {
126     EXPECT_EQ(nullptr, table->get_logical_diffs(field));
127     return;
128   }
129 
130   /*
131     Take a copy of the JSON diffs, since the call to
132     clear_partial_update_diffs() below will clear the original
133     Json_diff_vector.
134   */
135   const auto thd = table->in_use;
136   Json_diff_vector diffs(Json_diff_vector::allocator_type(thd->mem_root));
137   for (const auto &diff : *table->get_logical_diffs(field)) {
138     diffs.add_diff(diff.path(), diff.operation(), diff.value().clone_dom(thd));
139   }
140 
141   /*
142     apply_json_diffs() will try to collect binary diffs for the
143     changes that it applies to the column, so we should clear the
144     already collected diffs.
145   */
146   table->clear_partial_update_diffs();
147 
148   EXPECT_TRUE(table->is_binary_diff_enabled(field));
149   EXPECT_EQ(enum_json_diff_status::SUCCESS, apply_json_diffs(field, &diffs));
150   EXPECT_EQ(binary_update, table->is_binary_diff_enabled(field));
151 
152   Json_wrapper res2;
153   EXPECT_FALSE(field->val_json(&res2));
154   EXPECT_EQ(0, res2.compare(new_doc));
155 
156   // apply_json_diffs() should produce new JSON diffs.
157   EXPECT_TRUE(table->is_logical_diff_enabled(field));
158   const Json_diff_vector *new_diffs = table->get_logical_diffs(field);
159   EXPECT_NE(nullptr, new_diffs);
160   EXPECT_EQ(diffs.size(), new_diffs->size());
161 
162   // ... and applying those new diffs should produce the same result again ...
163   diffs.clear();
164   for (const auto &diff : *new_diffs) {
165     diffs.add_diff(diff.path(), diff.operation(), diff.value().clone_dom(thd));
166   }
167   table->clear_partial_update_diffs();
168   store_json(field, orig_json);
169   EXPECT_EQ(enum_json_diff_status::SUCCESS, apply_json_diffs(field, &diffs));
170   Json_wrapper res3;
171   EXPECT_FALSE(field->val_json(&res3));
172   EXPECT_EQ(0, res3.compare(new_doc));
173 }
174 
175 /*
176   Test partial update using various JSON functions.
177 */
TEST_F(ItemJsonFuncTest,PartialUpdate)178 TEST_F(ItemJsonFuncTest, PartialUpdate) {
179   m_field.make_writable();
180 
181   auto json_set = new Item_func_json_set(
182       thd(), new Item_field(&m_field), new_item_string("$[1]"),
183       new_item_string("abc"), new_item_string("$[2]"), new Item_int(100));
184 
185   EXPECT_FALSE(m_table.mark_column_for_partial_update(&m_field));
186   EXPECT_FALSE(m_table.setup_partial_update(true));
187 
188   // Logical update OK, but not enough space for binary update.
189   {
190     SCOPED_TRACE("");
191     do_partial_update(json_set, &m_field, "[1,2,3]", "[1,\"abc\",100]", false,
192                       true);
193   }
194 
195   // Both logical update and binary update OK.
196   {
197     SCOPED_TRACE("");
198     do_partial_update(json_set, &m_field, "[4,\"XYZ\",5]", "[4,\"abc\",100]",
199                       true, true);
200   }
201 
202   // No-op update.
203   {
204     SCOPED_TRACE("");
205     do_partial_update(json_set, &m_field, "[0,\"abc\",100]", "[0,\"abc\",100]",
206                       true, true);
207     EXPECT_EQ(0U, m_table.get_binary_diffs(&m_field)->size());
208     EXPECT_EQ(0U, m_table.get_logical_diffs(&m_field)->size());
209   }
210 
211   // The array grows, so only logical update is OK.
212   {
213     SCOPED_TRACE("");
214     do_partial_update(json_set, &m_field, "[6,\"XYZ\"]", "[6,\"abc\",100]",
215                       false, true);
216   }
217 
218   // The root document is auto-wrapped, so no partial update at all.
219   {
220     SCOPED_TRACE("");
221     do_partial_update(json_set, &m_field, "true", "[true,\"abc\",100]", false,
222                       false);
223   }
224 
225   // A sub-document is auto-wrapped. OK for logical update, but not for binary.
226   {
227     SCOPED_TRACE("");
228     auto wrap_set = new Item_func_json_set(
229         thd(), new Item_field(&m_field), new_item_string("$.x[2]"),
230         new Item_int(2), new_item_string("$.x[1]"), new Item_int(1));
231     do_partial_update(wrap_set, &m_field, "{\"x\":123}", "{\"x\":[123,1]}",
232                       false, true);
233   }
234 
235   // Replacing the root of the document leads to full update.
236   {
237     SCOPED_TRACE("");
238     auto replace_root = new Item_func_json_replace(
239         thd(), new Item_field(&m_field), new_item_string("$"), new Item_int(1));
240     do_partial_update(replace_root, &m_field, "{\"a\":[1,2,3]}", "1", false,
241                       false);
242   }
243 
244   // A nested call.
245   {
246     auto inner_func =
247         new Item_func_json_set(thd(), new Item_field(&m_field),
248                                new_item_string("$.a[1]"), new Item_int(1));
249 
250     auto outer_func = new Item_func_json_replace(
251         thd(), inner_func, new_item_string("$.b"), new Item_int(2));
252 
253     {
254       SCOPED_TRACE("");
255       do_partial_update(outer_func, &m_field, "{\"a\":[1,2,3]}",
256                         "{\"a\":[1,1,3]}", true, true);
257     }
258     {
259       SCOPED_TRACE("");
260       do_partial_update(outer_func, &m_field, "{\"a\":[1,2,3],\"b\":47}",
261                         "{\"a\":[1,1,3],\"b\":2}", true, true);
262     }
263     {
264       SCOPED_TRACE("");
265       do_partial_update(outer_func, &m_field, "{\"a\":8}", "{\"a\":[8,1]}",
266                         false, true);
267     }
268     {
269       SCOPED_TRACE("");
270       do_partial_update(outer_func, &m_field, nullptr, nullptr, false, false);
271     }
272   }
273 
274   // A nested call where the inner function causes a full update.
275   {
276     auto inner_func = new Item_func_json_set(
277         thd(), new Item_field(&m_field), new_item_string("$"),
278         new Item_func_json_array(thd(), new Item_int(1), new Item_int(2)));
279     auto outer_func = new Item_func_json_set(
280         thd(), inner_func, new_item_string("$[1]"), new Item_int(3));
281 
282     SCOPED_TRACE("");
283     do_partial_update(outer_func, &m_field, "[true,false]", "[1,3]", false,
284                       false);
285   }
286 
287   // Returning NULL should cause full update.
288   {
289     SCOPED_TRACE("");
290     auto null_path = new Item_func_json_set(thd(), new Item_field(&m_field),
291                                             new Item_null(), new Item_int(1));
292     do_partial_update(null_path, &m_field, "[1,2,3]", nullptr, false, false);
293   }
294 
295   // Input document being NULL should cause full update.
296   {
297     SCOPED_TRACE("");
298     auto null_doc =
299         new Item_func_json_set(thd(), new Item_field(&m_field),
300                                new_item_string("$.a.b.c"), new Item_int(1));
301     do_partial_update(null_doc, &m_field, nullptr, nullptr, false, false);
302   }
303 
304   // Setting object member.
305   {
306     auto set_member =
307         new Item_func_json_set(thd(), new Item_field(&m_field),
308                                new_item_string("$.a"), new Item_int(1));
309 
310     // Existing member can be replaced with both binary and logical update.
311     {
312       SCOPED_TRACE("");
313       do_partial_update(set_member, &m_field, "{\"a\":\"b\"}", "{\"a\":1}",
314                         true, true);
315     }
316 
317     // Non-existing member can be added with logical update.
318     {
319       SCOPED_TRACE("");
320       do_partial_update(set_member, &m_field, "{}", "{\"a\":1}", false, true);
321     }
322     {
323       SCOPED_TRACE("");
324       do_partial_update(set_member, &m_field, "[5,6,7]", "[5,6,7]", true, true);
325     }
326     {
327       SCOPED_TRACE("");
328       do_partial_update(set_member, &m_field, "123", "123", true, true);
329     }
330     {
331       SCOPED_TRACE("");
332       do_partial_update(set_member, &m_field, nullptr, nullptr, false, false);
333     }
334   }
335 
336   // Replacing object member.
337   {
338     auto replace =
339         new Item_func_json_replace(thd(), new Item_field(&m_field),
340                                    new_item_string("$.a"), new Item_int(1));
341 
342     // Existing member can be replaced with both binary and logical update.
343     {
344       SCOPED_TRACE("");
345       do_partial_update(replace, &m_field, "{\"a\":\"b\"}", "{\"a\":1}", true,
346                         true);
347     }
348 
349     // Replacing non-existing member is a no-op.
350     {
351       SCOPED_TRACE("");
352       do_partial_update(replace, &m_field, "{}", "{}", true, true);
353     }
354     {
355       SCOPED_TRACE("");
356       do_partial_update(replace, &m_field, "[5,6,7]", "[5,6,7]", true, true);
357     }
358     {
359       SCOPED_TRACE("");
360       do_partial_update(replace, &m_field, "123", "123", true, true);
361     }
362     {
363       SCOPED_TRACE("");
364       do_partial_update(replace, &m_field, nullptr, nullptr, false, false);
365     }
366   }
367 
368   // Setting array element.
369   {
370     auto set_element =
371         new Item_func_json_set(thd(), new Item_field(&m_field),
372                                new_item_string("$[1]"), new Item_int(1));
373     {
374       SCOPED_TRACE("");
375       do_partial_update(set_element, &m_field, "[4,5,6]", "[4,1,6]", true,
376                         true);
377     }
378     {
379       SCOPED_TRACE("");
380       do_partial_update(set_element, &m_field, "[]", "[1]", false, true);
381     }
382     {
383       SCOPED_TRACE("");
384       do_partial_update(set_element, &m_field, "[2]", "[2,1]", false, true);
385     }
386     {
387       SCOPED_TRACE("");
388       do_partial_update(set_element, &m_field, "{\"a\":2}", "[{\"a\":2},1]",
389                         false, false);
390     }
391     {
392       SCOPED_TRACE("");
393       do_partial_update(set_element, &m_field, "123", "[123,1]", false, false);
394     }
395     {
396       SCOPED_TRACE("");
397       do_partial_update(set_element, &m_field, nullptr, nullptr, false, false);
398     }
399   }
400   {
401     auto set_element =
402         new Item_func_json_set(thd(), new Item_field(&m_field),
403                                new_item_string("$.a[1]"), new Item_int(1));
404     {
405       SCOPED_TRACE("");
406       do_partial_update(set_element, &m_field, "{\"a\":[4,5,6]}",
407                         "{\"a\":[4,1,6]}", true, true);
408     }
409     {
410       SCOPED_TRACE("");
411       do_partial_update(set_element, &m_field, "{\"a\":[]}", "{\"a\":[1]}",
412                         false, true);
413     }
414     {
415       SCOPED_TRACE("");
416       do_partial_update(set_element, &m_field, "{\"a\":{\"b\":2}}",
417                         "{\"a\":[{\"b\":2},1]}", false, true);
418     }
419     {
420       SCOPED_TRACE("");
421       do_partial_update(set_element, &m_field, "{\"a\":123}", "{\"a\":[123,1]}",
422                         false, true);
423     }
424   }
425 
426   // Replacing array element.
427   {
428     auto replace =
429         new Item_func_json_replace(thd(), new Item_field(&m_field),
430                                    new_item_string("$[1]"), new Item_int(1));
431     {
432       SCOPED_TRACE("");
433       do_partial_update(replace, &m_field, "[4,5,6]", "[4,1,6]", true, true);
434     }
435     {
436       SCOPED_TRACE("");
437       do_partial_update(replace, &m_field, "[]", "[]", true, true);
438     }
439     {
440       SCOPED_TRACE("");
441       do_partial_update(replace, &m_field, "[2]", "[2]", true, true);
442     }
443     {
444       SCOPED_TRACE("");
445       do_partial_update(replace, &m_field, "{\"a\":2}", "{\"a\":2}", true,
446                         true);
447     }
448     {
449       SCOPED_TRACE("");
450       do_partial_update(replace, &m_field, "123", "123", true, true);
451     }
452     {
453       SCOPED_TRACE("");
454       do_partial_update(replace, &m_field, nullptr, nullptr, false, false);
455     }
456   }
457   {
458     auto replace =
459         new Item_func_json_replace(thd(), new Item_field(&m_field),
460                                    new_item_string("$.a[1]"), new Item_int(1));
461     {
462       SCOPED_TRACE("");
463       do_partial_update(replace, &m_field, "{\"a\":[4,5,6]}", "{\"a\":[4,1,6]}",
464                         true, true);
465     }
466     {
467       SCOPED_TRACE("");
468       do_partial_update(replace, &m_field, "{\"a\":[]}", "{\"a\":[]}", true,
469                         true);
470     }
471     {
472       SCOPED_TRACE("");
473       do_partial_update(replace, &m_field, "{\"a\":{\"b\":2}}",
474                         "{\"a\":{\"b\":2}}", true, true);
475     }
476     {
477       SCOPED_TRACE("");
478       do_partial_update(replace, &m_field, "{\"a\":123}", "{\"a\":123}", true,
479                         true);
480     }
481   }
482 
483   // Remove an element in an array.
484   {
485     auto remove = new Item_func_json_remove(thd(), new Item_field(&m_field),
486                                             new_item_string("$[1]"));
487     {
488       SCOPED_TRACE("");
489       do_partial_update(remove, &m_field, "{}", "{}", true, true);
490     }
491     {
492       SCOPED_TRACE("");
493       do_partial_update(remove, &m_field, "[]", "[]", true, true);
494     }
495     {
496       SCOPED_TRACE("");
497       do_partial_update(remove, &m_field, nullptr, nullptr, false, false);
498     }
499     {
500       SCOPED_TRACE("");
501       do_partial_update(remove, &m_field, "[1,2,3]", "[1,3]", true, true);
502     }
503     {
504       SCOPED_TRACE("");
505       do_partial_update(remove, &m_field, "[1,2]", "[1]", true, true);
506     }
507     {
508       SCOPED_TRACE("");
509       do_partial_update(remove, &m_field, "[1]", "[1]", true, true);
510     }
511     {
512       SCOPED_TRACE("");
513       do_partial_update(remove, &m_field, "{\"a\":1}", "{\"a\":1}", true, true);
514     }
515     {
516       SCOPED_TRACE("");
517       do_partial_update(remove, &m_field, "123", "123", true, true);
518     }
519     {
520       SCOPED_TRACE("");
521       do_partial_update(remove, &m_field, nullptr, nullptr, false, false);
522     }
523   }
524 
525   // Remove a member from an object.
526   {
527     auto remove = new Item_func_json_remove(thd(), new Item_field(&m_field),
528                                             new_item_string("$.x"));
529     {
530       SCOPED_TRACE("");
531       do_partial_update(remove, &m_field, "{}", "{}", true, true);
532     }
533     {
534       SCOPED_TRACE("");
535       do_partial_update(remove, &m_field, "{\"a\":1}", "{\"a\":1}", true, true);
536     }
537     {
538       SCOPED_TRACE("");
539       do_partial_update(remove, &m_field, "{\"x\":1}", "{}", true, true);
540     }
541     {
542       SCOPED_TRACE("");
543       do_partial_update(remove, &m_field,
544                         "{\"a\":\"b\",\"c\":\"d\",\"x\":\"y\",\"z\":\"w\"}",
545                         "{\"a\":\"b\",\"c\":\"d\",\"z\":\"w\"}", true, true);
546     }
547     {
548       SCOPED_TRACE("");
549       do_partial_update(remove, &m_field, "[1,2,3]", "[1,2,3]", true, true);
550     }
551     {
552       SCOPED_TRACE("");
553       do_partial_update(remove, &m_field, "123", "123", true, true);
554     }
555     {
556       SCOPED_TRACE("");
557       do_partial_update(remove, &m_field, nullptr, nullptr, false, false);
558     }
559   }
560 
561   // Remove multiple paths.
562   {
563     auto remove = new Item_func_json_remove(thd(), new Item_field(&m_field),
564                                             new_item_string("$.a.b"),
565                                             new_item_string("$.c[1]"));
566     {
567       SCOPED_TRACE("");
568       do_partial_update(remove, &m_field, "{}", "{}", true, true);
569     }
570     {
571       SCOPED_TRACE("");
572       do_partial_update(remove, &m_field,
573                         "{\"a\":{\"b\":\"c\"}, \"b\":{\"c\":\"d\"}, "
574                         "\"c\":[1,2,3]}",
575                         "{\"a\":{}, \"b\":{\"c\":\"d\"}, \"c\":[1,3]}", true,
576                         true);
577     }
578     {
579       SCOPED_TRACE("");
580       do_partial_update(remove, &m_field,
581                         "{\"a\":{\"b\":\"c\"}, \"b\":{\"c\":\"d\"}}",
582                         "{\"a\":{}, \"b\":{\"c\":\"d\"}}", true, true);
583     }
584     {
585       SCOPED_TRACE("");
586       do_partial_update(remove, &m_field,
587                         "{\"b\":{\"c\":\"d\"}, \"c\":[1,2,3]}",
588                         "{\"b\":{\"c\":\"d\"}, \"c\":[1,3]}", true, true);
589     }
590   }
591 
592   // JSON_REMOVE with NULL as path.
593   {
594     SCOPED_TRACE("");
595     auto remove = new Item_func_json_remove(thd(), new Item_field(&m_field),
596                                             new Item_null());
597     do_partial_update(remove, &m_field, "[1,2]", nullptr, false, false);
598   }
599 
600   // Mixed JSON_REMOVE/JSON_SET.
601   {
602     auto set =
603         new Item_func_json_set(thd(), new Item_field(&m_field),
604                                new_item_string("$.a"), new_item_string("abc"));
605     auto remove = new Item_func_json_remove(thd(), set, new_item_string("$.b"));
606 
607     {
608       SCOPED_TRACE("");
609       do_partial_update(remove, &m_field, "{}", "{\"a\":\"abc\"}", false, true);
610     }
611     {
612       SCOPED_TRACE("");
613       do_partial_update(remove, &m_field, nullptr, nullptr, false, false);
614     }
615     {
616       SCOPED_TRACE("");
617       do_partial_update(remove, &m_field, "{\"b\":123}", "{\"a\":\"abc\"}",
618                         false, true);
619     }
620     {
621       SCOPED_TRACE("");
622       do_partial_update(remove, &m_field, "{\"a\":\"xyz\",\"b\":123}",
623                         "{\"a\":\"abc\"}", true, true);
624     }
625     {
626       SCOPED_TRACE("");
627       do_partial_update(remove, &m_field, nullptr, nullptr, false, false);
628     }
629   }
630 
631   // Remove with auto-wrap.
632   {
633     auto remove = new Item_func_json_remove(
634         thd(), new Item_field(&m_field), new_item_string("$[0][0].a[0][0].b"));
635     {
636       SCOPED_TRACE("");
637       do_partial_update(remove, &m_field, "{}", "{}", true, true);
638     }
639     {
640       SCOPED_TRACE("");
641       do_partial_update(remove, &m_field, "[]", "[]", true, true);
642     }
643     {
644       SCOPED_TRACE("");
645       do_partial_update(remove, &m_field, "{\"a\":{\"b\":1,\"c\":2}}",
646                         "{\"a\":{\"c\":2}}", true, true);
647     }
648     {
649       SCOPED_TRACE("");
650       do_partial_update(remove, &m_field,
651                         "[{\"a\":[{\"b\":1,\"c\":2},123]}, 456]",
652                         "[{\"a\":[{\"c\":2},123]}, 456]", true, true);
653     }
654     {
655       SCOPED_TRACE("");
656       do_partial_update(remove, &m_field, nullptr, nullptr, false, false);
657     }
658   }
659 
660   // Append or prepend when setting with out-of-bounds array indexes.
661   {
662     SCOPED_TRACE("");
663     auto set = new Item_func_json_set(
664         thd(), new Item_field(&m_field), new_item_string("$[2]"),
665         new Item_int(88), new_item_string("$[last-2]"), new Item_int(99));
666     do_partial_update(set, &m_field, "[]", "[99,88]", false, true);
667     do_partial_update(set, &m_field, "[1]", "[99,1,88]", false, true);
668     do_partial_update(set, &m_field, "[1,2]", "[99,2,88]", false, true);
669     do_partial_update(set, &m_field, "[1,2,3]", "[99,2,88]", true, true);
670     do_partial_update(set, &m_field, "[1,2,3,4]", "[1,99,88,4]", true, true);
671     do_partial_update(set, &m_field, nullptr, nullptr, false, false);
672   }
673 }
674 
TEST(FieldJSONTest,TruncatedSortKey)675 TEST(FieldJSONTest, TruncatedSortKey) {
676   my_testing::Server_initializer initializer;
677   initializer.SetUp();
678 
679   const Json_wrapper doc(parse_json("47"));
680 
681   Base_mock_field_json field;
682   Fake_TABLE table(&field);
683   field.make_writable();
684   EXPECT_EQ(TYPE_OK, field.store_json(&doc));
685 
686   uchar reference[1024];
687   size_t reference_len = field.make_sort_key(reference, sizeof(reference));
688   ASSERT_LT(reference_len, sizeof(reference));
689 
690   for (size_t max_len = 0; max_len < 100; ++max_len) {
691     std::unique_ptr<uchar[]> buf(new uchar[max_len]);
692     size_t len = field.make_sort_key(buf.get(), max_len);
693     EXPECT_EQ(len, std::min(max_len, reference_len));
694     EXPECT_EQ(0, memcmp(buf.get(), reference, len));
695   }
696   initializer.TearDown();
697 }
698 
699 /**
700   Microbenchmark which tests the performance of the JSON_SEARCH function.
701 */
BM_JsonSearch(size_t num_iterations)702 static void BM_JsonSearch(size_t num_iterations) {
703   StopBenchmarkTiming();
704 
705   my_testing::Server_initializer initializer;
706   initializer.SetUp();
707 
708   const Json_wrapper doc(
709       parse_json("[\"Apple\", \"Orange\", \"Peach\","
710                  "{\"key1\":\"Apple\", \"key2\":\"Orange\","
711                  "\"key3\":\"Peach\"}, 1, 2, 3, 4, 5, 6]"));
712 
713   Base_mock_field_json field;
714   Fake_TABLE table(&field);
715   field.make_writable();
716   EXPECT_EQ(TYPE_OK, field.store_json(&doc));
717 
718   auto search = new Item_func_json_search(table.in_use, new Item_field(&field),
719                                           new_item_string("all"),
720                                           new_item_string("Apple"));
721   EXPECT_FALSE(search->fix_fields(table.in_use, nullptr));
722 
723   StartBenchmarkTiming();
724 
725   for (size_t i = 0; i < num_iterations; ++i) {
726     Json_wrapper wr;
727     EXPECT_FALSE(search->val_json(&wr));
728     EXPECT_FALSE(search->null_value);
729     EXPECT_EQ(2U, wr.length());
730   }
731 
732   StopBenchmarkTiming();
733   initializer.TearDown();
734 }
BENCHMARK(BM_JsonSearch)735 BENCHMARK(BM_JsonSearch)
736 
737 /**
738   Microbenchmark which tests the performance of the JSON_SEARCH
739   function when it's called with arguments that contain wildcards.
740 */
741 static void BM_JsonSearch_Wildcard(size_t num_iterations) {
742   StopBenchmarkTiming();
743 
744   my_testing::Server_initializer initializer;
745   initializer.SetUp();
746 
747   const Json_wrapper doc(
748       parse_json("[{\"key1\": \"abc\","
749                  "  \"key2\": \"def\","
750                  "  \"key3\": \"abc\"},"
751                  " {\"key1\": \"def\","
752                  "  \"key2\": \"abc\","
753                  "  \"key3\": \"def\"},"
754                  " {\"key4\": {\"key4\":\"abc\"}}]"));
755 
756   Base_mock_field_json field;
757   Fake_TABLE table(&field);
758   field.make_writable();
759   EXPECT_EQ(TYPE_OK, field.store_json(&doc));
760 
761   List<Item> args;
762   args.push_back(new Item_field(&field));
763   args.push_back(new_item_string("all"));
764   args.push_back(new_item_string("abc"));
765   args.push_back(new_item_string(""));  // escape character
766   args.push_back(new_item_string("$[*].key1"));
767   args.push_back(new_item_string("$**.key4"));
768 
769   auto search = new Item_func_json_search(table.in_use, args);
770   EXPECT_FALSE(search->fix_fields(table.in_use, nullptr));
771 
772   StartBenchmarkTiming();
773 
774   for (size_t i = 0; i < num_iterations; ++i) {
775     Json_wrapper wr;
776     EXPECT_FALSE(search->val_json(&wr));
777     EXPECT_FALSE(search->null_value);
778     EXPECT_EQ(2U, wr.length());
779   }
780 
781   StopBenchmarkTiming();
782   initializer.TearDown();
783 }
784 BENCHMARK(BM_JsonSearch_Wildcard)
785 
786 }  // namespace item_json_func_unittest
787