Skip to content

Commit b347d18

Browse files
Catalin Besleagakahatlen
Catalin Besleaga
authored andcommitted
Bug#35640263: Refactoring of json_diff/json_binary to enable HW
support for partial json [5/6, apply_json_diff, noclose] Refactor apply_json_diffs() to make it easier to reuse the code from plugins. A function for applying a single Json_diff to a Json_dom instance is extracted into a new helper function called apply_json_diff(). The code is moved from item_json_func.cc to json_diff.{h,cc}. Change-Id: I8202b8f8a4daad15ddc56399c08102db5081dccf
1 parent 59d7202 commit b347d18

File tree

4 files changed

+160
-128
lines changed

4 files changed

+160
-128
lines changed

sql-common/json_diff.cc

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,3 +385,112 @@ bool Json_diff_vector::read_binary(const char **from, const TABLE *table,
385385
table->s->table_name.str, field_name);
386386
return true;
387387
}
388+
389+
/**
390+
Find the value at the specified path in a JSON DOM. The path should
391+
not contain any wildcard or ellipsis, only simple array cells or
392+
member names. Auto-wrapping is not performed.
393+
394+
@param dom the root of the DOM
395+
@param first_leg the first path leg
396+
@param last_leg the last path leg (exclusive)
397+
@return the JSON DOM at the given path, or `nullptr` if the path is not found
398+
*/
399+
static Json_dom *seek_exact_path(Json_dom *dom,
400+
const Json_path_iterator &first_leg,
401+
const Json_path_iterator &last_leg) {
402+
for (auto it = first_leg; it != last_leg; ++it) {
403+
const Json_path_leg *leg = *it;
404+
const auto leg_type = leg->get_type();
405+
assert(leg_type == jpl_member || leg_type == jpl_array_cell);
406+
switch (dom->json_type()) {
407+
case enum_json_type::J_ARRAY: {
408+
const auto array = down_cast<Json_array *>(dom);
409+
if (leg_type != jpl_array_cell) return nullptr;
410+
Json_array_index idx = leg->first_array_index(array->size());
411+
if (!idx.within_bounds()) return nullptr;
412+
dom = (*array)[idx.position()];
413+
continue;
414+
}
415+
case enum_json_type::J_OBJECT: {
416+
const auto object = down_cast<Json_object *>(dom);
417+
if (leg_type != jpl_member) return nullptr;
418+
dom = object->get(leg->get_member_name());
419+
if (dom == nullptr) return nullptr;
420+
continue;
421+
}
422+
default:
423+
return nullptr;
424+
}
425+
}
426+
427+
return dom;
428+
}
429+
430+
enum_json_diff_status apply_json_diff(const Json_diff &diff, Json_dom *dom) {
431+
Json_wrapper val_to_apply = diff.value();
432+
const Json_path &path = diff.path();
433+
434+
switch (diff.operation()) {
435+
case enum_json_diff_operation::REPLACE: {
436+
assert(path.leg_count() > 0);
437+
Json_dom *old = seek_exact_path(dom, path.begin(), path.end());
438+
if (old == nullptr) return enum_json_diff_status::REJECTED;
439+
assert(old->parent() != nullptr);
440+
old->parent()->replace_dom_in_container(old, val_to_apply.clone_dom());
441+
return enum_json_diff_status::SUCCESS;
442+
}
443+
case enum_json_diff_operation::INSERT: {
444+
assert(path.leg_count() > 0);
445+
Json_dom *parent = seek_exact_path(dom, path.begin(), path.end() - 1);
446+
if (parent == nullptr) return enum_json_diff_status::REJECTED;
447+
const Json_path_leg *last_leg = path.last_leg();
448+
if (parent->json_type() == enum_json_type::J_OBJECT &&
449+
last_leg->get_type() == jpl_member) {
450+
auto obj = down_cast<Json_object *>(parent);
451+
if (obj->get(last_leg->get_member_name()) != nullptr)
452+
return enum_json_diff_status::REJECTED;
453+
if (obj->add_alias(last_leg->get_member_name(),
454+
val_to_apply.clone_dom()))
455+
return enum_json_diff_status::ERROR; /* purecov: inspected */
456+
return enum_json_diff_status::SUCCESS;
457+
}
458+
if (parent->json_type() == enum_json_type::J_ARRAY &&
459+
last_leg->get_type() == jpl_array_cell) {
460+
auto array = down_cast<Json_array *>(parent);
461+
Json_array_index idx = last_leg->first_array_index(array->size());
462+
if (array->insert_alias(idx.position(), val_to_apply.clone_dom()))
463+
return enum_json_diff_status::ERROR; /* purecov: inspected */
464+
return enum_json_diff_status::SUCCESS;
465+
}
466+
return enum_json_diff_status::REJECTED;
467+
}
468+
case enum_json_diff_operation::REMOVE: {
469+
assert(path.leg_count() > 0);
470+
Json_dom *parent = seek_exact_path(dom, path.begin(), path.end() - 1);
471+
if (parent == nullptr) return enum_json_diff_status::REJECTED;
472+
const Json_path_leg *last_leg = path.last_leg();
473+
if (parent->json_type() == enum_json_type::J_OBJECT) {
474+
auto object = down_cast<Json_object *>(parent);
475+
if (last_leg->get_type() != jpl_member ||
476+
!object->remove(last_leg->get_member_name()))
477+
return enum_json_diff_status::REJECTED;
478+
} else if (parent->json_type() == enum_json_type::J_ARRAY) {
479+
if (last_leg->get_type() != jpl_array_cell)
480+
return enum_json_diff_status::REJECTED;
481+
auto array = down_cast<Json_array *>(parent);
482+
Json_array_index idx = last_leg->first_array_index(array->size());
483+
if (!idx.within_bounds() || !array->remove(idx.position()))
484+
return enum_json_diff_status::REJECTED;
485+
} else {
486+
return enum_json_diff_status::REJECTED;
487+
}
488+
return enum_json_diff_status::SUCCESS;
489+
}
490+
}
491+
492+
/* purecov: begin deadcode */
493+
assert(false);
494+
return enum_json_diff_status::ERROR;
495+
/* purecov: end */
496+
}

sql-common/json_diff.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,4 +251,44 @@ class Json_diff_vector {
251251
static const size_t ENCODED_LENGTH_BYTES = 4;
252252
};
253253

254+
/**
255+
The result of applying JSON diffs on a JSON value using apply_json_diff().
256+
*/
257+
enum class enum_json_diff_status {
258+
/**
259+
The JSON diffs were applied and the JSON value in the column was updated
260+
successfully.
261+
*/
262+
SUCCESS,
263+
264+
/**
265+
An error was raised while applying one of the diffs. The value in the
266+
column was not updated.
267+
*/
268+
ERROR,
269+
270+
/**
271+
One of the diffs was rejected. This could happen if the path specified in
272+
the diff does not exist in the JSON value, or if the diff is supposed to
273+
add a new value at a given path, but there already is a value at the path.
274+
275+
This return code would usually indicate that the replication slave where
276+
the diff is applied, is out of sync with the replication master where the
277+
diff was created.
278+
279+
The value in the column was not updated, but no error was raised.
280+
*/
281+
REJECTED,
282+
};
283+
284+
/**
285+
Apply one JSON diff to the DOM provided.
286+
287+
@param diff The diff which contains the path to apply it and the new value.
288+
@param dom The DOM to apply the diff to.
289+
@return An enum_json_diff_status value that tells if the diff was applied
290+
successfully.
291+
*/
292+
enum_json_diff_status apply_json_diff(const Json_diff &diff, Json_dom *dom);
293+
254294
#endif /* JSON_DIFF_INCLUDED */

sql/item_json_func.cc

Lines changed: 9 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -153,47 +153,6 @@ bool parse_json(const String &res, Json_dom_ptr *dom, bool require_str_or_json,
153153
return *dom == nullptr;
154154
}
155155

156-
/**
157-
Find the value at the specified path in a JSON DOM. The path should
158-
not contain any wildcard or ellipsis, only simple array cells or
159-
member names. Auto-wrapping is not performed.
160-
161-
@param dom the root of the DOM
162-
@param first_leg the first path leg
163-
@param last_leg the last path leg (exclusive)
164-
@return the JSON DOM at the given path, or `nullptr` if the path is not found
165-
*/
166-
static Json_dom *seek_exact_path(Json_dom *dom,
167-
const Json_path_iterator &first_leg,
168-
const Json_path_iterator &last_leg) {
169-
for (auto it = first_leg; it != last_leg; ++it) {
170-
const Json_path_leg *leg = *it;
171-
const auto leg_type = leg->get_type();
172-
assert(leg_type == jpl_member || leg_type == jpl_array_cell);
173-
switch (dom->json_type()) {
174-
case enum_json_type::J_ARRAY: {
175-
const auto array = down_cast<Json_array *>(dom);
176-
if (leg_type != jpl_array_cell) return nullptr;
177-
Json_array_index idx = leg->first_array_index(array->size());
178-
if (!idx.within_bounds()) return nullptr;
179-
dom = (*array)[idx.position()];
180-
continue;
181-
}
182-
case enum_json_type::J_OBJECT: {
183-
const auto object = down_cast<Json_object *>(dom);
184-
if (leg_type != jpl_member) return nullptr;
185-
dom = object->get(leg->get_member_name());
186-
if (dom == nullptr) return nullptr;
187-
continue;
188-
}
189-
default:
190-
return nullptr;
191-
}
192-
}
193-
194-
return dom;
195-
}
196-
197156
enum_json_diff_status apply_json_diffs(Field_json *field,
198157
const Json_diff_vector *diffs) {
199158
DBUG_TRACE;
@@ -273,64 +232,16 @@ enum_json_diff_status apply_json_diffs(Field_json *field,
273232
if (doc.to_dom() == nullptr)
274233
return enum_json_diff_status::ERROR; /* purecov: inspected */
275234

276-
switch (diff.operation()) {
277-
case enum_json_diff_operation::REPLACE: {
278-
assert(path.leg_count() > 0);
279-
Json_dom *old = seek_exact_path(dom, path.begin(), path.end());
280-
if (old == nullptr) return enum_json_diff_status::REJECTED;
281-
assert(old->parent() != nullptr);
282-
old->parent()->replace_dom_in_container(old, val.clone_dom());
283-
continue;
284-
}
285-
case enum_json_diff_operation::INSERT: {
286-
assert(path.leg_count() > 0);
287-
Json_dom *parent = seek_exact_path(dom, path.begin(), path.end() - 1);
288-
if (parent == nullptr) return enum_json_diff_status::REJECTED;
289-
const Json_path_leg *last_leg = path.last_leg();
290-
if (parent->json_type() == enum_json_type::J_OBJECT &&
291-
last_leg->get_type() == jpl_member) {
292-
auto obj = down_cast<Json_object *>(parent);
293-
if (obj->get(last_leg->get_member_name()) != nullptr)
294-
return enum_json_diff_status::REJECTED;
295-
if (obj->add_alias(last_leg->get_member_name(), val.clone_dom()))
296-
return enum_json_diff_status::ERROR; /* purecov: inspected */
297-
continue;
298-
}
299-
if (parent->json_type() == enum_json_type::J_ARRAY &&
300-
last_leg->get_type() == jpl_array_cell) {
301-
auto array = down_cast<Json_array *>(parent);
302-
Json_array_index idx = last_leg->first_array_index(array->size());
303-
if (array->insert_alias(idx.position(), val.clone_dom()))
304-
return enum_json_diff_status::ERROR; /* purecov: inspected */
305-
continue;
306-
}
307-
return enum_json_diff_status::REJECTED;
308-
}
309-
case enum_json_diff_operation::REMOVE: {
310-
assert(path.leg_count() > 0);
311-
Json_dom *parent = seek_exact_path(dom, path.begin(), path.end() - 1);
312-
if (parent == nullptr) return enum_json_diff_status::REJECTED;
313-
const Json_path_leg *last_leg = path.last_leg();
314-
if (parent->json_type() == enum_json_type::J_OBJECT) {
315-
auto object = down_cast<Json_object *>(parent);
316-
if (last_leg->get_type() != jpl_member ||
317-
!object->remove(last_leg->get_member_name()))
318-
return enum_json_diff_status::REJECTED;
319-
} else if (parent->json_type() == enum_json_type::J_ARRAY) {
320-
if (last_leg->get_type() != jpl_array_cell)
321-
return enum_json_diff_status::REJECTED;
322-
auto array = down_cast<Json_array *>(parent);
323-
Json_array_index idx = last_leg->first_array_index(array->size());
324-
if (!idx.within_bounds() || !array->remove(idx.position()))
325-
return enum_json_diff_status::REJECTED;
326-
} else {
327-
return enum_json_diff_status::REJECTED;
328-
}
329-
continue;
330-
}
331-
}
235+
enum_json_diff_status res = apply_json_diff(diff, dom);
332236

333-
assert(false); /* purecov: deadcode */
237+
// If the diff was not applied successfully exit with the error status,
238+
// otherwise continue to the next diff
239+
if (res == enum_json_diff_status::ERROR ||
240+
res == enum_json_diff_status::REJECTED) {
241+
return res;
242+
} else {
243+
continue;
244+
}
334245
}
335246

336247
if (field->store_json(&doc) != TYPE_OK)

sql/item_json_func.h

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ class THD;
6565
class my_decimal;
6666
enum Cast_target : unsigned char;
6767
enum class Json_on_response_type : uint16;
68+
enum class enum_json_diff_status;
69+
6870
struct Cast_type;
6971
struct TABLE;
7072

@@ -1252,36 +1254,6 @@ bool parse_json(const String &res, Json_dom_ptr *dom, bool require_str_or_json,
12521254
const JsonParseErrorHandler &error_handler,
12531255
const JsonErrorHandler &depth_handler);
12541256

1255-
/**
1256-
The result of applying JSON diffs on a JSON value using apply_json_diffs().
1257-
*/
1258-
enum class enum_json_diff_status {
1259-
/**
1260-
The JSON diffs were applied and the JSON value in the column was updated
1261-
successfully.
1262-
*/
1263-
SUCCESS,
1264-
1265-
/**
1266-
An error was raised while applying one of the diffs. The value in the
1267-
column was not updated.
1268-
*/
1269-
ERROR,
1270-
1271-
/**
1272-
One of the diffs was rejected. This could happen if the path specified in
1273-
the diff does not exist in the JSON value, or if the diff is supposed to
1274-
add a new value at a given path, but there already is a value at the path.
1275-
1276-
This return code would usually indicate that the replication slave where
1277-
the diff is applied, is out of sync with the replication master where the
1278-
diff was created.
1279-
1280-
The value in the column was not updated, but no error was raised.
1281-
*/
1282-
REJECTED,
1283-
};
1284-
12851257
/**
12861258
Apply a sequence of JSON diffs to the value stored in a JSON column.
12871259

0 commit comments

Comments
 (0)