[Commits] 2ebb138: MDEV-12604 Comparison of JSON_EXTRACT result differs with Mysql.

Alexey Botchkov holyfoot at askmonty.org
Fri Aug 11 09:21:48 EEST 2017


revision-id: 2ebb1380d69524ef7aa042530d3d38a8805000ff (mariadb-10.2.2-583-g2ebb138)
parent(s): e22375247272564b55bb45a3c711fda86923aa3c
committer: Alexey Botchkov
timestamp: 2017-08-11 09:02:55 +0400
message:

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.

Conflicts:
	sql/item_cmpfunc.cc

---
 sql/item_cmpfunc.cc  |  46 +++++++--
 sql/item_cmpfunc.h   |   6 ++
 sql/item_func.h      |   2 +-
 sql/item_jsonfunc.cc | 280 +++++++++++++++++++++++++++------------------------
 sql/item_jsonfunc.h  |   6 +-
 5 files changed, 198 insertions(+), 142 deletions(-)

diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 64f2a44..4bcb739 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -544,15 +544,21 @@ bool Arg_comparator::set_cmp_func_string()
     */
     if (owner->agg_arg_charsets_for_comparison(&m_compare_collation, a, b))
       return true;
-  }
 
-  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;
+    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 false;
+    }
+    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 false;
+    }
   }
 
   a= cache_converted_constant(thd, a, &a_cache, compare_type_handler());
@@ -1144,6 +1150,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 604ca5b..08e47d3 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -116,6 +116,12 @@ class Arg_comparator: public Sql_alloc
   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,
                                   const Type_handler *type);
diff --git a/sql/item_func.h b/sql/item_func.h
index 8616bdf..d537648 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -67,7 +67,7 @@ class Item_func :public Item_func_or_sum
                   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)
diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc
index bdfce15..3d24c81 100644
--- a/sql/item_jsonfunc.cc
+++ b/sql/item_jsonfunc.cc
@@ -642,81 +642,6 @@ String *Item_func_json_unquote::val_str(String *str)
 }
 
 
-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 @@ String *Item_func_json_extract::val_str(String *str)
 }
 
 
-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;
 }
 
 
@@ -3196,4 +3147,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 abed194..97f144c 100644
--- a/sql/item_jsonfunc.h
+++ b/sql/item_jsonfunc.h
@@ -131,8 +131,6 @@ class Item_func_json_unquote: public Item_str_func
   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); }
 };
@@ -158,12 +156,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); }


More information about the commits mailing list