MDEV-12604 Comparison of JSON_EXTRACT result differs with Mysql.
JSON_EXTRACT behaves specifically in the comparison, so we have to implement specific method for that in Arg_comparator.
This commit is contained in:
parent
bfffe571ac
commit
79d2853354
@ -627,6 +627,21 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg,
|
|||||||
*/
|
*/
|
||||||
if (owner->agg_arg_charsets_for_comparison(&m_compare_collation, a, b))
|
if (owner->agg_arg_charsets_for_comparison(&m_compare_collation, a, b))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
if ((*a)->type() == Item::FUNC_ITEM &&
|
||||||
|
((Item_func *) (*a))->functype() == Item_func::JSON_EXTRACT_FUNC)
|
||||||
|
{
|
||||||
|
func= is_owner_equal_func() ? &Arg_comparator::compare_e_json_str:
|
||||||
|
&Arg_comparator::compare_json_str;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if ((*b)->type() == Item::FUNC_ITEM &&
|
||||||
|
((Item_func *) (*b))->functype() == Item_func::JSON_EXTRACT_FUNC)
|
||||||
|
{
|
||||||
|
func= is_owner_equal_func() ? &Arg_comparator::compare_e_json_str:
|
||||||
|
&Arg_comparator::compare_str_json;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_compare_type == TIME_RESULT)
|
if (m_compare_type == TIME_RESULT)
|
||||||
@ -670,15 +685,6 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg,
|
|||||||
&Arg_comparator::compare_datetime;
|
&Arg_comparator::compare_datetime;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((*a)->is_json_type() ^ (*b)->is_json_type())
|
|
||||||
{
|
|
||||||
Item **j_item= (*a)->is_json_type() ? a : b;
|
|
||||||
Item *uf= new(thd->mem_root) Item_func_json_unquote(thd, *j_item);
|
|
||||||
if (!uf || uf->fix_fields(thd, &uf))
|
|
||||||
return 1;
|
|
||||||
*j_item= uf;
|
|
||||||
}
|
|
||||||
|
|
||||||
a= cache_converted_constant(thd, a, &a_cache, m_compare_type);
|
a= cache_converted_constant(thd, a, &a_cache, m_compare_type);
|
||||||
b= cache_converted_constant(thd, b, &b_cache, m_compare_type);
|
b= cache_converted_constant(thd, b, &b_cache, m_compare_type);
|
||||||
return set_compare_func(owner_arg, m_compare_type);
|
return set_compare_func(owner_arg, m_compare_type);
|
||||||
@ -1169,6 +1175,30 @@ int Arg_comparator::compare_e_row()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Arg_comparator::compare_json_str()
|
||||||
|
{
|
||||||
|
return compare_json_str_basic(*a, *b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Arg_comparator::compare_str_json()
|
||||||
|
{
|
||||||
|
return -compare_json_str_basic(*b, *a);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Arg_comparator::compare_e_json_str()
|
||||||
|
{
|
||||||
|
return compare_e_json_str_basic(*a, *b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Arg_comparator::compare_e_str_json()
|
||||||
|
{
|
||||||
|
return compare_e_json_str_basic(*b, *a);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Item_func_truth::fix_length_and_dec()
|
void Item_func_truth::fix_length_and_dec()
|
||||||
{
|
{
|
||||||
maybe_null= 0;
|
maybe_null= 0;
|
||||||
|
@ -106,6 +106,12 @@ public:
|
|||||||
int compare_e_datetime() { return compare_e_temporal(MYSQL_TYPE_DATETIME); }
|
int compare_e_datetime() { return compare_e_temporal(MYSQL_TYPE_DATETIME); }
|
||||||
int compare_time() { return compare_temporal(MYSQL_TYPE_TIME); }
|
int compare_time() { return compare_temporal(MYSQL_TYPE_TIME); }
|
||||||
int compare_e_time() { return compare_e_temporal(MYSQL_TYPE_TIME); }
|
int compare_e_time() { return compare_e_temporal(MYSQL_TYPE_TIME); }
|
||||||
|
int compare_json_str_basic(Item *j, Item *s);
|
||||||
|
int compare_json_str();
|
||||||
|
int compare_str_json();
|
||||||
|
int compare_e_json_str_basic(Item *j, Item *s);
|
||||||
|
int compare_e_json_str();
|
||||||
|
int compare_e_str_json();
|
||||||
|
|
||||||
Item** cache_converted_constant(THD *thd, Item **value, Item **cache,
|
Item** cache_converted_constant(THD *thd, Item **value, Item **cache,
|
||||||
Item_result type);
|
Item_result type);
|
||||||
|
@ -67,7 +67,7 @@ public:
|
|||||||
NOW_FUNC, NOW_UTC_FUNC, SYSDATE_FUNC, TRIG_COND_FUNC,
|
NOW_FUNC, NOW_UTC_FUNC, SYSDATE_FUNC, TRIG_COND_FUNC,
|
||||||
SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_FUNC,
|
SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_FUNC,
|
||||||
EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC,
|
EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC,
|
||||||
NEG_FUNC, GSYSVAR_FUNC, DYNCOL_FUNC };
|
NEG_FUNC, GSYSVAR_FUNC, DYNCOL_FUNC, JSON_EXTRACT_FUNC };
|
||||||
enum Type type() const { return FUNC_ITEM; }
|
enum Type type() const { return FUNC_ITEM; }
|
||||||
virtual enum Functype functype() const { return UNKNOWN_FUNC; }
|
virtual enum Functype functype() const { return UNKNOWN_FUNC; }
|
||||||
Item_func(THD *thd): Item_func_or_sum(thd), allowed_arg_cols(1)
|
Item_func(THD *thd): Item_func_or_sum(thd), allowed_arg_cols(1)
|
||||||
|
@ -642,81 +642,6 @@ error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double Item_func_json_unquote::val_real()
|
|
||||||
{
|
|
||||||
json_engine_t je;
|
|
||||||
double d= 0.0;
|
|
||||||
String *js;
|
|
||||||
|
|
||||||
if ((js= read_json(&je)) != NULL)
|
|
||||||
{
|
|
||||||
switch (je.value_type)
|
|
||||||
{
|
|
||||||
case JSON_VALUE_NUMBER:
|
|
||||||
{
|
|
||||||
char *end;
|
|
||||||
int err;
|
|
||||||
d= my_strntod(je.s.cs, (char *) je.value, je.value_len, &end, &err);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case JSON_VALUE_TRUE:
|
|
||||||
d= 1.0;
|
|
||||||
break;
|
|
||||||
case JSON_VALUE_STRING:
|
|
||||||
{
|
|
||||||
char *end;
|
|
||||||
int err;
|
|
||||||
d= my_strntod(js->charset(), (char *) js->ptr(), js->length(),
|
|
||||||
&end, &err);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
longlong Item_func_json_unquote::val_int()
|
|
||||||
{
|
|
||||||
json_engine_t je;
|
|
||||||
longlong i= 0;
|
|
||||||
String *js;
|
|
||||||
|
|
||||||
if ((js= read_json(&je)) != NULL)
|
|
||||||
{
|
|
||||||
switch (je.value_type)
|
|
||||||
{
|
|
||||||
case JSON_VALUE_NUMBER:
|
|
||||||
{
|
|
||||||
char *end;
|
|
||||||
int err;
|
|
||||||
i= my_strntoll(je.s.cs, (char *) je.value, je.value_len, 10,
|
|
||||||
&end, &err);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case JSON_VALUE_TRUE:
|
|
||||||
i= 1;
|
|
||||||
break;
|
|
||||||
case JSON_VALUE_STRING:
|
|
||||||
{
|
|
||||||
char *end;
|
|
||||||
int err;
|
|
||||||
i= my_strntoll(js->charset(), (char *) js->ptr(), js->length(), 10,
|
|
||||||
&end, &err);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int alloc_tmp_paths(THD *thd, uint n_paths,
|
static int alloc_tmp_paths(THD *thd, uint n_paths,
|
||||||
json_path_with_flags **paths,String **tmp_paths)
|
json_path_with_flags **paths,String **tmp_paths)
|
||||||
{
|
{
|
||||||
@ -779,7 +704,7 @@ void Item_func_json_extract::fix_length_and_dec()
|
|||||||
|
|
||||||
|
|
||||||
static bool path_exact(const json_path_with_flags *paths_list, int n_paths,
|
static bool path_exact(const json_path_with_flags *paths_list, int n_paths,
|
||||||
const json_path_t *p, enum json_value_types vt)
|
const json_path_t *p, json_value_types vt)
|
||||||
{
|
{
|
||||||
for (; n_paths > 0; n_paths--, paths_list++)
|
for (; n_paths > 0; n_paths--, paths_list++)
|
||||||
{
|
{
|
||||||
@ -791,7 +716,7 @@ 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,
|
static bool path_ok(const json_path_with_flags *paths_list, int n_paths,
|
||||||
const json_path_t *p, enum json_value_types vt)
|
const json_path_t *p, json_value_types vt)
|
||||||
{
|
{
|
||||||
for (; n_paths > 0; n_paths--, paths_list++)
|
for (; n_paths > 0; n_paths--, paths_list++)
|
||||||
{
|
{
|
||||||
@ -802,7 +727,9 @@ static bool path_ok(const json_path_with_flags *paths_list, int n_paths,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String *Item_func_json_extract::val_str(String *str)
|
String *Item_func_json_extract::read_json(String *str,
|
||||||
|
json_value_types *type,
|
||||||
|
char **out_val, int *value_len)
|
||||||
{
|
{
|
||||||
String *js= args[0]->val_json(&tmp_js);
|
String *js= args[0]->val_json(&tmp_js);
|
||||||
json_engine_t je, sav_je;
|
json_engine_t je, sav_je;
|
||||||
@ -838,8 +765,13 @@ String *Item_func_json_extract::val_str(String *str)
|
|||||||
possible_multiple_values= arg_count > 2 ||
|
possible_multiple_values= arg_count > 2 ||
|
||||||
(paths[0].p.types_used & (JSON_PATH_WILD | JSON_PATH_DOUBLE_WILD));
|
(paths[0].p.types_used & (JSON_PATH_WILD | JSON_PATH_DOUBLE_WILD));
|
||||||
|
|
||||||
str->set_charset(js->charset());
|
*type= possible_multiple_values ? JSON_VALUE_ARRAY : JSON_VALUE_NULL;
|
||||||
str->length(0);
|
|
||||||
|
if (str)
|
||||||
|
{
|
||||||
|
str->set_charset(js->charset());
|
||||||
|
str->length(0);
|
||||||
|
}
|
||||||
|
|
||||||
if (possible_multiple_values && str->append("[", 1))
|
if (possible_multiple_values && str->append("[", 1))
|
||||||
goto error;
|
goto error;
|
||||||
@ -854,6 +786,18 @@ String *Item_func_json_extract::val_str(String *str)
|
|||||||
|
|
||||||
value= je.value_begin;
|
value= je.value_begin;
|
||||||
|
|
||||||
|
if (*type == JSON_VALUE_NULL)
|
||||||
|
{
|
||||||
|
*type= je.value_type;
|
||||||
|
*out_val= (char *) je.value;
|
||||||
|
*value_len= je.value_len;
|
||||||
|
}
|
||||||
|
if (!str)
|
||||||
|
{
|
||||||
|
/* If str is NULL, we only care about the first found value. */
|
||||||
|
goto return_ok;
|
||||||
|
}
|
||||||
|
|
||||||
if (json_value_scalar(&je))
|
if (json_value_scalar(&je))
|
||||||
v_len= je.value_end - value;
|
v_len= je.value_end - value;
|
||||||
else
|
else
|
||||||
@ -897,6 +841,7 @@ String *Item_func_json_extract::val_str(String *str)
|
|||||||
if (json_nice(&je, &tmp_js, Item_func_json_format::LOOSE))
|
if (json_nice(&je, &tmp_js, Item_func_json_format::LOOSE))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
return_ok:
|
||||||
return &tmp_js;
|
return &tmp_js;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@ -907,68 +852,74 @@ return_null:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String *Item_func_json_extract::val_str(String *str)
|
||||||
|
{
|
||||||
|
json_value_types type;
|
||||||
|
char *value;
|
||||||
|
int value_len;
|
||||||
|
return read_json(str, &type, &value, &value_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
longlong Item_func_json_extract::val_int()
|
longlong Item_func_json_extract::val_int()
|
||||||
{
|
{
|
||||||
String *js= args[0]->val_json(&tmp_js);
|
json_value_types type;
|
||||||
json_engine_t je;
|
char *value;
|
||||||
uint n_arg;
|
int value_len;
|
||||||
uint array_counters[JSON_DEPTH_LIMIT];
|
longlong i;
|
||||||
|
|
||||||
if ((null_value= args[0]->null_value))
|
if (read_json(NULL, &type, &value, &value_len) != NULL)
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (n_arg=1; n_arg < arg_count; n_arg++)
|
|
||||||
{
|
{
|
||||||
json_path_with_flags *c_path= paths + n_arg - 1;
|
switch (type)
|
||||||
if (!c_path->parsed)
|
|
||||||
{
|
{
|
||||||
String *s_p= args[n_arg]->val_str(tmp_paths+(n_arg-1));
|
case JSON_VALUE_NUMBER:
|
||||||
if (s_p &&
|
case JSON_VALUE_STRING:
|
||||||
json_path_setup(&c_path->p,s_p->charset(),(const uchar *) s_p->ptr(),
|
{
|
||||||
(const uchar *) s_p->ptr() + s_p->length()))
|
char *end;
|
||||||
goto error;
|
int err;
|
||||||
c_path->parsed= c_path->constant;
|
i= my_strntoll(collation.collation, value, value_len, 10, &end, &err);
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
case JSON_VALUE_TRUE:
|
||||||
|
i= 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
i= 0;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
if (args[n_arg]->null_value)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
json_scan_start(&je, js->charset(),(const uchar *) js->ptr(),
|
double Item_func_json_extract::val_real()
|
||||||
(const uchar *) js->ptr() + js->length());
|
{
|
||||||
|
json_value_types type;
|
||||||
|
char *value;
|
||||||
|
int value_len;
|
||||||
|
double d= 0.0;
|
||||||
|
|
||||||
c_path->cur_step= c_path->p.steps;
|
if (read_json(NULL, &type, &value, &value_len) != NULL)
|
||||||
|
{
|
||||||
if (json_find_path(&je, &c_path->p, &c_path->cur_step, array_counters))
|
switch (type)
|
||||||
{
|
{
|
||||||
/* Path wasn't found. */
|
case JSON_VALUE_STRING:
|
||||||
if (je.s.error)
|
case JSON_VALUE_NUMBER:
|
||||||
goto error;
|
{
|
||||||
|
char *end;
|
||||||
continue;
|
int err;
|
||||||
}
|
d= my_strntod(collation.collation, value, value_len, &end, &err);
|
||||||
|
break;
|
||||||
if (json_read_value(&je))
|
}
|
||||||
goto error;
|
case JSON_VALUE_TRUE:
|
||||||
|
d= 1.0;
|
||||||
if (json_value_scalar(&je))
|
break;
|
||||||
{
|
default:
|
||||||
int err;
|
break;
|
||||||
char *v_end= (char *) je.value_end;
|
};
|
||||||
return (je.s.cs->cset->strtoll10)(je.s.cs, (const char *) je.value_begin,
|
|
||||||
&v_end, &err);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Nothing was found. */
|
return d;
|
||||||
null_value= 1;
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error:
|
|
||||||
/* TODO: launch error messages. */
|
|
||||||
null_value= 1;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3193,4 +3144,71 @@ String *Item_func_json_format::val_json(String *str)
|
|||||||
return js;
|
return js;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Arg_comparator::compare_json_str_basic(Item *j, Item *s)
|
||||||
|
{
|
||||||
|
String *res1,*res2;
|
||||||
|
json_value_types type;
|
||||||
|
char *value;
|
||||||
|
int value_len, c_len;
|
||||||
|
Item_func_json_extract *e= (Item_func_json_extract *) j;
|
||||||
|
|
||||||
|
if ((res1= e->read_json(&value1, &type, &value, &value_len)))
|
||||||
|
{
|
||||||
|
if ((res2= s->val_str(&value2)))
|
||||||
|
{
|
||||||
|
if (type == JSON_VALUE_STRING)
|
||||||
|
{
|
||||||
|
if (value1.realloc_with_extra_if_needed(value_len) ||
|
||||||
|
(c_len= json_unescape(value1.charset(), (uchar *) value,
|
||||||
|
(uchar *) value+value_len,
|
||||||
|
&my_charset_utf8_general_ci,
|
||||||
|
(uchar *) value1.ptr(),
|
||||||
|
(uchar *) (value1.ptr() + value_len))) < 0)
|
||||||
|
goto error;
|
||||||
|
value1.length(c_len);
|
||||||
|
res1= &value1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set_null)
|
||||||
|
owner->null_value= 0;
|
||||||
|
return sortcmp(res1, res2, compare_collation());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error:
|
||||||
|
if (set_null)
|
||||||
|
owner->null_value= 1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Arg_comparator::compare_e_json_str_basic(Item *j, Item *s)
|
||||||
|
{
|
||||||
|
String *res1,*res2;
|
||||||
|
json_value_types type;
|
||||||
|
char *value;
|
||||||
|
int value_len, c_len;
|
||||||
|
Item_func_json_extract *e= (Item_func_json_extract *) j;
|
||||||
|
|
||||||
|
res1= e->read_json(&value1, &type, &value, &value_len);
|
||||||
|
res2= s->val_str(&value2);
|
||||||
|
|
||||||
|
if (!res1 || !res2)
|
||||||
|
return MY_TEST(res1 == res2);
|
||||||
|
|
||||||
|
if (type == JSON_VALUE_STRING)
|
||||||
|
{
|
||||||
|
if (value1.realloc_with_extra_if_needed(value_len) ||
|
||||||
|
(c_len= json_unescape(value1.charset(), (uchar *) value,
|
||||||
|
(uchar *) value+value_len,
|
||||||
|
&my_charset_utf8_general_ci,
|
||||||
|
(uchar *) value1.ptr(),
|
||||||
|
(uchar *) (value1.ptr() + value_len))) < 0)
|
||||||
|
return 1;
|
||||||
|
value1.length(c_len);
|
||||||
|
res1= &value1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MY_TEST(sortcmp(res1, res2, compare_collation()) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -133,8 +133,6 @@ public:
|
|||||||
const char *func_name() const { return "json_unquote"; }
|
const char *func_name() const { return "json_unquote"; }
|
||||||
void fix_length_and_dec();
|
void fix_length_and_dec();
|
||||||
String *val_str(String *);
|
String *val_str(String *);
|
||||||
double val_real();
|
|
||||||
longlong val_int();
|
|
||||||
Item *get_copy(THD *thd, MEM_ROOT *mem_root)
|
Item *get_copy(THD *thd, MEM_ROOT *mem_root)
|
||||||
{ return get_item_copy<Item_func_json_unquote>(thd, mem_root, this); }
|
{ return get_item_copy<Item_func_json_unquote>(thd, mem_root, this); }
|
||||||
};
|
};
|
||||||
@ -160,12 +158,16 @@ class Item_func_json_extract: public Item_json_str_multipath
|
|||||||
protected:
|
protected:
|
||||||
String tmp_js;
|
String tmp_js;
|
||||||
public:
|
public:
|
||||||
|
String *read_json(String *str, json_value_types *type,
|
||||||
|
char **out_val, int *value_len);
|
||||||
Item_func_json_extract(THD *thd, List<Item> &list):
|
Item_func_json_extract(THD *thd, List<Item> &list):
|
||||||
Item_json_str_multipath(thd, list) {}
|
Item_json_str_multipath(thd, list) {}
|
||||||
const char *func_name() const { return "json_extract"; }
|
const char *func_name() const { return "json_extract"; }
|
||||||
|
enum Functype functype() const { return JSON_EXTRACT_FUNC; }
|
||||||
void fix_length_and_dec();
|
void fix_length_and_dec();
|
||||||
String *val_str(String *);
|
String *val_str(String *);
|
||||||
longlong val_int();
|
longlong val_int();
|
||||||
|
double val_real();
|
||||||
uint get_n_paths() const { return arg_count - 1; }
|
uint get_n_paths() const { return arg_count - 1; }
|
||||||
Item *get_copy(THD *thd, MEM_ROOT *mem_root)
|
Item *get_copy(THD *thd, MEM_ROOT *mem_root)
|
||||||
{ return get_item_copy<Item_func_json_extract>(thd, mem_root, this); }
|
{ return get_item_copy<Item_func_json_extract>(thd, mem_root, this); }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user