mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
MDEV-22224: Support JSON Path negative index
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.
This commit is contained in:
@@ -467,7 +467,7 @@ bool Item_func_json_exists::fix_length_and_dec(THD *thd)
|
||||
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);
|
||||
|
||||
@@ -536,7 +536,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)
|
||||
{
|
||||
@@ -797,11 +797,12 @@ bool Item_func_json_extract::fix_length_and_dec(THD *thd)
|
||||
|
||||
|
||||
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;
|
||||
@@ -809,11 +810,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;
|
||||
@@ -832,6 +834,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;
|
||||
@@ -850,6 +854,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)
|
||||
@@ -875,7 +880,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;
|
||||
@@ -1245,7 +1255,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);
|
||||
@@ -1375,7 +1385,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)
|
||||
{
|
||||
@@ -1436,6 +1446,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;
|
||||
@@ -1457,6 +1469,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;
|
||||
@@ -1478,10 +1491,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)
|
||||
{
|
||||
@@ -1748,7 +1768,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)
|
||||
{
|
||||
@@ -1878,10 +1898,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)
|
||||
{
|
||||
@@ -1931,11 +1951,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;
|
||||
@@ -2666,7 +2695,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))
|
||||
@@ -2894,10 +2923,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)
|
||||
{
|
||||
@@ -2943,7 +2973,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)
|
||||
{
|
||||
@@ -2991,13 +3021,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))
|
||||
@@ -3148,11 +3186,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)
|
||||
{
|
||||
@@ -3197,17 +3235,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));
|
||||
@@ -3359,7 +3408,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;
|
||||
@@ -3566,6 +3615,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;
|
||||
@@ -3591,6 +3642,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;
|
||||
@@ -3601,9 +3653,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;
|
||||
|
Reference in New Issue
Block a user