diff options
author | Rucha Deodhar <rucha.deodhar@mariadb.com> | 2021-11-22 22:59:30 +0530 |
---|---|---|
committer | Rucha Deodhar <rucha.deodhar@mariadb.com> | 2021-12-16 10:07:34 +0530 |
commit | a9235d587ea6637a998e2a885900aaefd51f0a3c (patch) | |
tree | 472c084365c792a060f91b659ffbb492d733b081 /sql/item_jsonfunc.cc | |
parent | 6208228b78917bff13b5dc34428b38596f7404b4 (diff) | |
download | mariadb-git-bb-10.8-MDEV-22224.tar.gz |
MDEV-22224: Support JSON Path negative indexbb-10.8-MDEV-22224
This patch can be viewed as combination of two parts:
1) Enabling '-' in the path so that the parser does not give out a warning.
2) Setting the negative index to a correct value and returning the
appropriate value.
1) To enable using the negative index in the path:
To make the parser not return warning when negative index is used in path
'-' needs to be allowed in json path characters. P_NEG is added
to enable this and is made recognizable by setting the 45th index of
json_path_chr_map[] to P_NEG (instead of previous P_ETC)
because 45 corresponds to '-' in unicode.
When the path is being parsed and '-' is encountered, the parser should
recognize it as parsing '-' sign, so a new json state PS_NEG is required.
When the state is PS_NEG, it means that a negative integer is
going to be parsed so set is_negative_index of current step to 1 and
n_item is set accordingly when integer is encountered after '-'.
Next proceed with parsing rest of the path and get the correct path.
Next thing is parsing the json and returning correct value.
2) Setting the negative index to a correct value and returning the value:
While parsing json if we encounter array and the path step for the array
is a negative index (n_item < 0), then we can count the number of elements
in the array and set n_item to correct corresponding value. This is done in
json_skip_array_and_count.
Diffstat (limited to 'sql/item_jsonfunc.cc')
-rw-r--r-- | sql/item_jsonfunc.cc | 107 |
1 files changed, 82 insertions, 25 deletions
diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index fefb2a1c662..75601354900 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -466,7 +466,7 @@ bool Item_func_json_exists::fix_length_and_dec() longlong Item_func_json_exists::val_int() { json_engine_t je; - uint array_counters[JSON_DEPTH_LIMIT]; + int array_counters[JSON_DEPTH_LIMIT]; String *js= args[0]->val_json(&tmp_js); @@ -535,7 +535,7 @@ bool Json_path_extractor::extract(String *str, Item *item_js, Item *item_jp, { String *js= item_js->val_json(&tmp_js); int error= 0; - uint array_counters[JSON_DEPTH_LIMIT]; + int array_counters[JSON_DEPTH_LIMIT]; if (!parsed) { @@ -796,11 +796,12 @@ bool Item_func_json_extract::fix_length_and_dec() static bool path_exact(const json_path_with_flags *paths_list, int n_paths, - const json_path_t *p, json_value_types vt) + const json_path_t *p, json_value_types vt, + const int *array_size_counter) { for (; n_paths > 0; n_paths--, paths_list++) { - if (json_path_compare(&paths_list->p, p, vt) == 0) + if (json_path_compare(&paths_list->p, p, vt, array_size_counter) == 0) return TRUE; } return FALSE; @@ -808,11 +809,12 @@ static bool path_exact(const json_path_with_flags *paths_list, int n_paths, static bool path_ok(const json_path_with_flags *paths_list, int n_paths, - const json_path_t *p, json_value_types vt) + const json_path_t *p, json_value_types vt, + const int *array_size_counter) { for (; n_paths > 0; n_paths--, paths_list++) { - if (json_path_compare(&paths_list->p, p, vt) >= 0) + if (json_path_compare(&paths_list->p, p, vt, array_size_counter) >= 0) return TRUE; } return FALSE; @@ -831,6 +833,8 @@ String *Item_func_json_extract::read_json(String *str, uint n_arg; size_t v_len; int possible_multiple_values; + int array_size_counter[JSON_DEPTH_LIMIT]; + uint has_negative_path= 0; if ((null_value= args[0]->null_value)) return 0; @@ -849,6 +853,7 @@ String *Item_func_json_extract::read_json(String *str, goto return_null; } c_path->parsed= c_path->constant; + has_negative_path|= c_path->p.types_used & JSON_PATH_NEGATIVE_INDEX; } if (args[n_arg]->null_value) @@ -874,7 +879,12 @@ String *Item_func_json_extract::read_json(String *str, while (json_get_path_next(&je, &p) == 0) { - if (!path_exact(paths, arg_count-1, &p, je.value_type)) + if (has_negative_path && je.value_type == JSON_VALUE_ARRAY && + json_skip_array_and_count(&je, + array_size_counter + (p.last_step - p.steps))) + goto error; + + if (!path_exact(paths, arg_count-1, &p, je.value_type, array_size_counter)) continue; value= je.value_begin; @@ -1246,7 +1256,7 @@ longlong Item_func_json_contains::val_int() if (arg_count>2) /* Path specified. */ { - uint array_counters[JSON_DEPTH_LIMIT]; + int array_counters[JSON_DEPTH_LIMIT]; if (!path.parsed) { String *s_p= args[2]->val_str(&tmp_path); @@ -1376,7 +1386,7 @@ longlong Item_func_json_contains_path::val_int() result= !mode_one; for (n_arg=2; n_arg < arg_count; n_arg++) { - uint array_counters[JSON_DEPTH_LIMIT]; + int array_counters[JSON_DEPTH_LIMIT]; json_path_with_flags *c_path= paths + n_arg - 2; if (!c_path->parsed) { @@ -1437,6 +1447,8 @@ longlong Item_func_json_contains_path::val_int() json_path_t p; int n_found; LINT_INIT(n_found); + int array_sizes[JSON_DEPTH_LIMIT]; + uint has_negative_path= 0; if ((null_value= args[0]->null_value)) return 0; @@ -1458,6 +1470,7 @@ longlong Item_func_json_contains_path::val_int() goto null_return; } c_path->parsed= c_path->constant; + has_negative_path|= c_path->p.types_used & JSON_PATH_NEGATIVE_INDEX; } if (args[n_arg]->null_value) goto null_return; @@ -1479,10 +1492,17 @@ longlong Item_func_json_contains_path::val_int() while (json_get_path_next(&je, &p) == 0) { int n_path= arg_count - 2; + if (has_negative_path && je.value_type == JSON_VALUE_ARRAY && + json_skip_array_and_count(&je, array_sizes + (p.last_step - p.steps))) + { + result= 1; + break; + } + json_path_with_flags *c_path= paths; for (; n_path > 0; n_path--, c_path++) { - if (json_path_compare(&c_path->p, &p, je.value_type) >= 0) + if (json_path_compare(&c_path->p, &p, je.value_type, array_sizes) >= 0) { if (mode_one) { @@ -1722,7 +1742,7 @@ String *Item_func_json_array_append::val_str(String *str) for (n_arg=1, n_path=0; n_arg < arg_count; n_arg+=2, n_path++) { - uint array_counters[JSON_DEPTH_LIMIT]; + int array_counters[JSON_DEPTH_LIMIT]; json_path_with_flags *c_path= paths + n_path; if (!c_path->parsed) { @@ -1850,10 +1870,10 @@ String *Item_func_json_array_insert::val_str(String *str) for (n_arg=1, n_path=0; n_arg < arg_count; n_arg+=2, n_path++) { - uint array_counters[JSON_DEPTH_LIMIT]; + int array_counters[JSON_DEPTH_LIMIT]; json_path_with_flags *c_path= paths + n_path; const char *item_pos; - uint n_item; + int n_item, corrected_n_item; if (!c_path->parsed) { @@ -1902,11 +1922,20 @@ String *Item_func_json_array_insert::val_str(String *str) item_pos= 0; n_item= 0; + corrected_n_item= c_path->p.last_step[1].n_item; + if (corrected_n_item < 0) + { + int array_size; + if (json_skip_array_and_count(&je, &array_size)) + goto js_error; + corrected_n_item+= array_size + 1; + } while (json_scan_next(&je) == 0 && je.state != JST_ARRAY_END) { DBUG_ASSERT(je.state == JST_VALUE); - if (n_item == c_path->p.last_step[1].n_item) + + if (n_item == corrected_n_item) { item_pos= (const char *) je.s.c_str; break; @@ -2632,7 +2661,7 @@ longlong Item_func_json_length::val_int() String *js= args[0]->val_json(&tmp_js); json_engine_t je; uint length= 0; - uint array_counters[JSON_DEPTH_LIMIT]; + int array_counters[JSON_DEPTH_LIMIT]; int err; if ((null_value= args[0]->null_value)) @@ -2859,10 +2888,11 @@ String *Item_func_json_insert::val_str(String *str) for (n_arg=1, n_path=0; n_arg < arg_count; n_arg+=2, n_path++) { - uint array_counters[JSON_DEPTH_LIMIT]; + int array_counters[JSON_DEPTH_LIMIT]; json_path_with_flags *c_path= paths + n_path; const char *v_to; - const json_path_step_t *lp; + json_path_step_t *lp; + int corrected_n_item; if (!c_path->parsed) { @@ -2907,7 +2937,7 @@ String *Item_func_json_insert::val_str(String *str) lp= c_path->p.last_step+1; if (lp->type & JSON_PATH_ARRAY) { - uint n_item= 0; + int n_item= 0; if (je.value_type != JSON_VALUE_ARRAY) { @@ -2955,13 +2985,21 @@ String *Item_func_json_insert::val_str(String *str) goto continue_point; } + corrected_n_item= lp->n_item; + if (corrected_n_item < 0) + { + int array_size; + if (json_skip_array_and_count(&je, &array_size)) + goto js_error; + corrected_n_item+= array_size; + } while (json_scan_next(&je) == 0 && je.state != JST_ARRAY_END) { switch (je.state) { case JST_VALUE: - if (n_item == lp->n_item) + if (n_item == corrected_n_item) goto v_found; n_item++; if (json_skip_array_item(&je)) @@ -3110,11 +3148,11 @@ String *Item_func_json_remove::val_str(String *str) for (n_arg=1, n_path=0; n_arg < arg_count; n_arg++, n_path++) { - uint array_counters[JSON_DEPTH_LIMIT]; + int array_counters[JSON_DEPTH_LIMIT]; json_path_with_flags *c_path= paths + n_path; const char *rem_start= 0, *rem_end; - const json_path_step_t *lp; - uint n_item= 0; + json_path_step_t *lp; + int n_item= 0; if (!c_path->parsed) { @@ -3158,17 +3196,28 @@ String *Item_func_json_remove::val_str(String *str) goto js_error; lp= c_path->p.last_step+1; + if (lp->type & JSON_PATH_ARRAY) { + int corrected_n_item; if (je.value_type != JSON_VALUE_ARRAY) continue; + corrected_n_item= lp->n_item; + if (corrected_n_item < 0) + { + int array_size; + if (json_skip_array_and_count(&je, &array_size)) + goto js_error; + corrected_n_item+= array_size; + } + while (json_scan_next(&je) == 0 && je.state != JST_ARRAY_END) { switch (je.state) { case JST_VALUE: - if (n_item == lp->n_item) + if (n_item == corrected_n_item) { rem_start= (const char *) (je.s.c_str - (n_item ? je.sav_c_len : 0)); @@ -3320,7 +3369,7 @@ String *Item_func_json_keys::val_str(String *str) json_engine_t je; String *js= args[0]->val_json(&tmp_js); uint n_keys= 0; - uint array_counters[JSON_DEPTH_LIMIT]; + int array_counters[JSON_DEPTH_LIMIT]; if ((args[0]->null_value)) goto null_return; @@ -3527,6 +3576,8 @@ String *Item_func_json_search::val_str(String *str) json_engine_t je; json_path_t p, sav_path; uint n_arg; + int array_sizes[JSON_DEPTH_LIMIT]; + uint has_negative_path= 0; if (args[0]->null_value || args[2]->null_value) goto null_return; @@ -3552,6 +3603,7 @@ String *Item_func_json_search::val_str(String *str) goto null_return; } c_path->parsed= c_path->constant; + has_negative_path|= c_path->p.types_used & JSON_PATH_NEGATIVE_INDEX; } if (args[n_arg]->null_value) goto null_return; @@ -3562,9 +3614,14 @@ String *Item_func_json_search::val_str(String *str) while (json_get_path_next(&je, &p) == 0) { + if (has_negative_path && je.value_type == JSON_VALUE_ARRAY && + json_skip_array_and_count(&je, array_sizes + (p.last_step - p.steps))) + goto js_error; + if (json_value_scalar(&je)) { - if ((arg_count < 5 || path_ok(paths, arg_count - 4, &p, je.value_type)) && + if ((arg_count < 5 || + path_ok(paths, arg_count - 4, &p, je.value_type, array_sizes)) && compare_json_value_wild(&je, s_str) != 0) { ++n_path_found; |