diff options
author | Alexey Botchkov <holyfoot@askmonty.org> | 2017-08-11 00:50:29 +0400 |
---|---|---|
committer | Alexey Botchkov <holyfoot@askmonty.org> | 2017-08-11 00:50:29 +0400 |
commit | 79d28533549d15e848b342cf518ae4b409ba3e64 (patch) | |
tree | defca6f922ea782facd0b36f5cec20e2fb4d0cc7 | |
parent | bfffe571accb93c80066b070688e6712d4cb5643 (diff) | |
download | mariadb-git-79d28533549d15e848b342cf518ae4b409ba3e64.tar.gz |
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.
-rw-r--r-- | sql/item_cmpfunc.cc | 48 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 6 | ||||
-rw-r--r-- | sql/item_func.h | 2 | ||||
-rw-r--r-- | sql/item_jsonfunc.cc | 280 | ||||
-rw-r--r-- | sql/item_jsonfunc.h | 6 |
5 files changed, 199 insertions, 143 deletions
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index efab3da4ac1..16452a0de84 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -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)) 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) @@ -670,15 +685,6 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg, &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); b= cache_converted_constant(thd, b, &b_cache, 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() { maybe_null= 0; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 8ff789d983a..131062dab36 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -106,6 +106,12 @@ public: int compare_e_datetime() { return compare_e_temporal(MYSQL_TYPE_DATETIME); } int compare_time() { return compare_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_result type); diff --git a/sql/item_func.h b/sql/item_func.h index 585b981ba05..9aa9b09db1d 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -67,7 +67,7 @@ public: NOW_FUNC, NOW_UTC_FUNC, SYSDATE_FUNC, TRIG_COND_FUNC, SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_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; } virtual enum Functype functype() const { return UNKNOWN_FUNC; } Item_func(THD *thd): Item_func_or_sum(thd), allowed_arg_cols(1) diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index d871175a3ba..88165c09426 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -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, 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, - 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++) { @@ -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, - 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++) { @@ -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); 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 || (paths[0].p.types_used & (JSON_PATH_WILD | JSON_PATH_DOUBLE_WILD)); - str->set_charset(js->charset()); - str->length(0); + *type= possible_multiple_values ? JSON_VALUE_ARRAY : JSON_VALUE_NULL; + + if (str) + { + str->set_charset(js->charset()); + str->length(0); + } if (possible_multiple_values && str->append("[", 1)) goto error; @@ -854,6 +786,18 @@ String *Item_func_json_extract::val_str(String *str) 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)) v_len= je.value_end - value; 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)) goto error; +return_ok: return &tmp_js; error: @@ -907,68 +852,74 @@ return_null: } -longlong Item_func_json_extract::val_int() +String *Item_func_json_extract::val_str(String *str) { - String *js= args[0]->val_json(&tmp_js); - json_engine_t je; - uint n_arg; - uint array_counters[JSON_DEPTH_LIMIT]; - - if ((null_value= args[0]->null_value)) - return 0; - - for (n_arg=1; n_arg < arg_count; n_arg++) - { - json_path_with_flags *c_path= paths + n_arg - 1; - if (!c_path->parsed) - { - String *s_p= args[n_arg]->val_str(tmp_paths+(n_arg-1)); - if (s_p && - json_path_setup(&c_path->p,s_p->charset(),(const uchar *) s_p->ptr(), - (const uchar *) s_p->ptr() + s_p->length())) - goto error; - c_path->parsed= c_path->constant; - } - - if (args[n_arg]->null_value) - goto error; + json_value_types type; + char *value; + int value_len; + return read_json(str, &type, &value, &value_len); +} - json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), - (const uchar *) js->ptr() + js->length()); - c_path->cur_step= c_path->p.steps; +longlong Item_func_json_extract::val_int() +{ + json_value_types type; + char *value; + int value_len; + longlong i; - if (json_find_path(&je, &c_path->p, &c_path->cur_step, array_counters)) + if (read_json(NULL, &type, &value, &value_len) != NULL) + { + switch (type) { - /* Path wasn't found. */ - if (je.s.error) - goto error; + case JSON_VALUE_NUMBER: + case JSON_VALUE_STRING: + { + char *end; + int err; + 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; +} - continue; - } - if (json_read_value(&je)) - goto error; +double Item_func_json_extract::val_real() +{ + json_value_types type; + char *value; + int value_len; + double d= 0.0; - if (json_value_scalar(&je)) + if (read_json(NULL, &type, &value, &value_len) != NULL) + { + switch (type) { - int err; - 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; + case JSON_VALUE_STRING: + case JSON_VALUE_NUMBER: + { + char *end; + int err; + d= my_strntod(collation.collation, value, value_len, &end, &err); + break; + } + case JSON_VALUE_TRUE: + d= 1.0; + break; + default: + break; + }; } - /* Nothing was found. */ - null_value= 1; - return 0; - -error: - /* TODO: launch error messages. */ - null_value= 1; - return 0; + return d; } @@ -3193,4 +3144,71 @@ String *Item_func_json_format::val_json(String *str) 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); +} + diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index cc089129556..b5c35ff551f 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -133,8 +133,6 @@ public: const char *func_name() const { return "json_unquote"; } void fix_length_and_dec(); String *val_str(String *); - double val_real(); - longlong val_int(); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { 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: String tmp_js; 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_json_str_multipath(thd, list) {} const char *func_name() const { return "json_extract"; } + enum Functype functype() const { return JSON_EXTRACT_FUNC; } void fix_length_and_dec(); String *val_str(String *); longlong val_int(); + double val_real(); uint get_n_paths() const { return arg_count - 1; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_json_extract>(thd, mem_root, this); } |