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