[Commits] a88bbb8: MDEV-9143 JSON_xxx functions.

Alexey Botchkov holyfoot at askmonty.org
Tue May 31 15:12:04 EEST 2016


revision-id: a88bbb838d59985a325cfd2e34e335889d9afcd3 (mariadb-10.2.0-53-ga88bbb8)
parent(s): eb86c32225db2a76c6f256130e7bb316900a9408
committer: Alexey Botchkov
timestamp: 2016-05-31 16:10:40 +0400
message:

MDEV-9143 JSON_xxx functions.

JSON parsing library implemented and JSON_VALUE, JSON_VALID functions
added.

---
 sql/CMakeLists.txt   |    4 +-
 sql/item.h           |    1 +
 sql/item_create.cc   |   46 +++
 sql/item_jsonfunc.cc |  180 +++++++++
 sql/item_jsonfunc.h  |   67 ++++
 sql/sql_json.cc      | 1032 ++++++++++++++++++++++++++++++++++++++++++++++++++
 sql/sql_json.h       |  154 ++++++++
 7 files changed, 1482 insertions(+), 2 deletions(-)

diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 062e59b..d027407 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -134,12 +134,12 @@ SET (SQL_SOURCE
                opt_table_elimination.cc sql_expression_cache.cc
                gcalc_slicescan.cc gcalc_tools.cc
                threadpool_common.cc ../sql-common/mysql_async.c
-               my_apc.cc my_apc.h mf_iocache_encr.cc
+               my_apc.cc my_apc.h mf_iocache_encr.cc item_jsonfunc.cc
                my_json_writer.cc my_json_writer.h
                rpl_gtid.cc rpl_parallel.cc
                sql_type.cc sql_type.h
                item_windowfunc.cc sql_window.cc
-	       sql_cte.cc sql_cte.h
+	       sql_cte.cc sql_cte.h sql_json.cc sql_json.h
 	       ${WSREP_SOURCES}
                table_cache.cc encryption.cc
                ${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc
diff --git a/sql/item.h b/sql/item.h
index 674ff6e..8de1100 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -4559,6 +4559,7 @@ class Item_int_with_ref :public Item_int
 #include "item_timefunc.h"
 #include "item_subselect.h"
 #include "item_xmlfunc.h"
+#include "item_jsonfunc.h"
 #include "item_create.h"
 #endif
 
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 82f6bbd..44cd481 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -1708,6 +1708,32 @@ class Create_func_issimple : public Create_func_arg1
 #endif
 
 
+class Create_func_json_valid : public Create_func_arg1
+{
+public:
+  virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+  static Create_func_json_valid s_singleton;
+
+protected:
+  Create_func_json_valid() {}
+  virtual ~Create_func_json_valid() {}
+};
+
+
+class Create_func_json_value : public Create_func_arg2
+{
+public:
+  virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
+
+  static Create_func_json_value s_singleton;
+
+protected:
+  Create_func_json_value() {}
+  virtual ~Create_func_json_value() {}
+};
+
+
 class Create_func_last_day : public Create_func_arg1
 {
 public:
@@ -4572,6 +4598,24 @@ Create_func_issimple::create_1_arg(THD *thd, Item *arg1)
 #endif
 
 
+Create_func_json_valid Create_func_json_valid::s_singleton;
+
+Item*
+Create_func_json_valid::create_1_arg(THD *thd, Item *arg1)
+{
+  return new (thd->mem_root) Item_func_json_valid(thd, arg1);
+}
+
+
+Create_func_json_value Create_func_json_value::s_singleton;
+
+Item*
+Create_func_json_value::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+{
+  return new (thd->mem_root) Item_func_json_value(thd, arg1, arg2);
+}
+
+
 Create_func_last_day Create_func_last_day::s_singleton;
 
 Item*
@@ -5852,6 +5896,8 @@ static Native_func_registry func_array[] =
   { { C_STRING_WITH_LEN("ISSIMPLE") }, GEOM_BUILDER(Create_func_issimple)},
   { { C_STRING_WITH_LEN("IS_FREE_LOCK") }, BUILDER(Create_func_is_free_lock)},
   { { C_STRING_WITH_LEN("IS_USED_LOCK") }, BUILDER(Create_func_is_used_lock)},
+  { { C_STRING_WITH_LEN("JSON_VALID") }, BUILDER(Create_func_json_valid)},
+  { { C_STRING_WITH_LEN("JSON_VALUE") }, BUILDER(Create_func_json_value)},
   { { C_STRING_WITH_LEN("LAST_DAY") }, BUILDER(Create_func_last_day)},
   { { C_STRING_WITH_LEN("LAST_INSERT_ID") }, BUILDER(Create_func_last_insert_id)},
   { { C_STRING_WITH_LEN("LCASE") }, BUILDER(Create_func_lcase)},
diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc
new file mode 100644
index 0000000..0e7a11e
--- /dev/null
+++ b/sql/item_jsonfunc.cc
@@ -0,0 +1,180 @@
+/* Copyright (c) 2016, Monty Program Ab.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
+
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#include <my_global.h>
+#include "sql_priv.h"
+#include "item.h"
+
+
+longlong Item_func_json_valid::val_int()
+{
+  String *js= args[0]->val_str(&tmp_value);
+  json_engine je;
+
+  if ((null_value= args[0]->null_value) || js == NULL)
+    return 0;
+
+  je.scan_start(js->charset(), (const uchar *)js->ptr(),
+                (const uchar *)js->ptr()+js->length());
+
+  while (je.scan_next() == 0) {}
+
+  return je.error == 0;
+}
+
+
+void Item_func_json_value::fix_length_and_dec()
+{
+  collation.collation= &my_charset_utf8mb4_bin;
+  fix_char_length(args[0]->max_char_length());
+}
+
+
+int Item_func_json_value::handle_match(json_engine *je,
+                                       json_path_step **cur_step)
+{
+  if (je->read_value())
+    return 1;
+
+  if (*cur_step == path.last_step)
+  {
+    if (je->value_scalar())
+      return 1;
+
+    return je->skip_level();
+  }
+
+  if (je->value_scalar())
+    return 0;
+
+  (*cur_step)++;
+  n_arrays[*cur_step - path.steps]= 0;
+
+  if ((int) je->value_type != (int) (*cur_step)->type)
+  {
+    (*cur_step)--;
+    return je->skip_level();
+  }
+
+  return 0;
+}
+
+
+static int json_key_matches(json_engine *je, json_string *k)
+{
+  while (je->read_keyname_chr() == 0)
+  {
+    if (k->read_str_chr() ||
+        je->next_chr() != k->next_chr())
+      return 0;
+  }
+
+  if (k->read_str_chr())
+    return 1;
+
+  return 0;
+}
+
+
+int Item_func_json_value::find_value(json_engine *je)
+{
+  String *js= args[0]->val_str(&tmp_js);
+  String *s_p= args[1]->val_str(&tmp_path);
+  json_string kn;
+  json_path_step *cur_step= path.steps;
+
+  if ((null_value= args[0]->null_value || args[1]->null_value))
+    return 1;
+
+  if (path.setup(s_p->charset(), (const uchar *)s_p->ptr(),
+                 (const uchar *)s_p->ptr() + s_p->length()))
+    return 1;
+
+  kn.set_cs(path.cs);
+
+  je->scan_start(js->charset(),(const uchar *)js->ptr(),
+                 (const uchar *)js->ptr() + js->length());
+
+  do
+  {
+    switch (je->state)
+    {
+    case json_engine::KEY:
+      DBUG_ASSERT(cur_step->type == json_path_step::KEY);
+      if (!cur_step->wild)
+      {
+        kn.set_str(cur_step->key, cur_step->key_end);
+        if (!json_key_matches(je, &kn))
+        {
+          if (je->skip_key())
+            goto exit;
+          continue;
+        }
+      }
+      if (handle_match(je, &cur_step))
+        goto exit;
+      break;
+    case json_engine::VALUE:
+      DBUG_ASSERT(cur_step->type == json_path_step::ARRAY);
+      if (cur_step->wild ||
+          cur_step->n_item == n_arrays[cur_step - path.steps])
+      {
+        /* Array item matches. */
+        if (handle_match(je, &cur_step))
+          goto exit;
+      }
+      else
+      {
+        je->skip_array_item();
+        n_arrays[cur_step - path.steps]++;
+      }
+      break;
+    case json_engine::OB_E:
+    case json_engine::AR_E:
+      cur_step--;
+      break;
+    default:
+      DBUG_ASSERT(0);
+      break;
+    };
+  } while (je->scan_next() == 0);
+
+  /* No luck. */
+  return 1;
+
+exit:
+  return je->error;
+}
+
+
+String *Item_func_json_value::val_str(String *str)
+{
+  json_engine je;
+
+  if (find_value(&je))
+  {
+    null_value= 1;
+    return 0;
+  }
+
+  str->set((const char *) je.value, je.value_len, je.cs);
+  return str;
+}
+
+
diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h
new file mode 100644
index 0000000..c653f7f
--- /dev/null
+++ b/sql/item_jsonfunc.h
@@ -0,0 +1,67 @@
+#ifndef ITEM_JSONFUNC_INCLUDED
+#define ITEM_JSONFUNC_INCLUDED
+
+/* Copyright (c) 2016, MariaDB
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
+
+
+/* This file defines all XML functions */
+
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface			/* gcc class implementation */
+#endif
+
+#include "item_cmpfunc.h"      // Item_bool_func
+#include "item_strfunc.h"      // Item_str_func
+#include "sql_json.h"
+
+
+class Item_func_json_valid: public Item_bool_func
+{
+protected:
+  String tmp_value;
+
+public:
+  Item_func_json_valid(THD *thd, Item *json) : Item_bool_func(thd, json) {}
+  longlong val_int();
+  const char *func_name() const { return "json_valid"; }
+  void fix_length_and_dec()
+  {
+    Item_bool_func::fix_length_and_dec();
+    maybe_null= 1;
+  }
+};
+
+
+class Item_func_json_value: public Item_str_func
+{
+protected:
+  json_path path;
+  uint n_arrays[json_depth_limit];
+  String tmp_js, tmp_path;
+  int handle_match(json_engine *je, json_path_step **cur_step);
+  int find_value(json_engine *je);
+
+public:
+  Item_func_json_value(THD *thd, Item *js, Item *path):
+    Item_str_func(thd, js, path) {}
+  const char *func_name() const { return "json_value"; }
+  void fix_length_and_dec();
+  String *val_str(String *);
+};
+
+
+#endif /* ITEM_JSONFUNC_INCLUDED */
diff --git a/sql/sql_json.cc b/sql/sql_json.cc
new file mode 100644
index 0000000..7dcf950
--- /dev/null
+++ b/sql/sql_json.cc
@@ -0,0 +1,1032 @@
+#include <my_global.h>
+
+#include "sql_json.h"
+
+
+/* Copied from ctype-ucs2.c. */
+
+/*
+  D800..DB7F - Non-provate surrogate high (896 pages)
+  DB80..DBFF - Private surrogate high     (128 pages)
+  DC00..DFFF - Surrogate low              (1024 codes in a page)
+*/
+#define MY_UTF16_SURROGATE_HIGH_FIRST 0xD800
+#define MY_UTF16_SURROGATE_HIGH_LAST  0xDBFF
+#define MY_UTF16_SURROGATE_LOW_FIRST  0xDC00
+#define MY_UTF16_SURROGATE_LOW_LAST   0xDFFF
+
+#define MY_UTF16_HIGH_HEAD(x)      ((((uchar) (x)) & 0xFC) == 0xD8)
+#define MY_UTF16_LOW_HEAD(x)       ((((uchar) (x)) & 0xFC) == 0xDC)
+/* Test if a byte is a leading byte of a high or low surrogate head: */
+#define MY_UTF16_SURROGATE_HEAD(x) ((((uchar) (x)) & 0xF8) == 0xD8)
+/* Test if a Unicode code point is a high or low surrogate head */
+#define MY_UTF16_SURROGATE(x)      (((x) & 0xF800) == 0xD800)
+
+#define MY_UTF16_WC2(a, b)         ((a << 8) + b)
+
+/*
+  a= 110110??  (<< 18)
+  b= ????????  (<< 10)
+  c= 110111??  (<<  8)
+  d= ????????  (<<  0)
+*/
+#define MY_UTF16_WC4(a, b, c, d) (((a & 3) << 18) + (b << 10) + \
+                                  ((c & 3) << 8) + d + 0x10000)
+
+int
+my_utf16_uni(CHARSET_INFO *cs __attribute__((unused)),
+             my_wc_t *pwc, const uchar *s, const uchar *e)
+{
+  if (s + 2 > e)
+    return MY_CS_TOOSMALL2;
+  
+  /*
+    High bytes: 0xD[89AB] = B'110110??'
+    Low bytes:  0xD[CDEF] = B'110111??'
+    Surrogate mask:  0xFC = B'11111100'
+  */
+
+  if (MY_UTF16_HIGH_HEAD(*s)) /* Surrogate head */
+  {
+    if (s + 4 > e)
+      return MY_CS_TOOSMALL4;
+
+    if (!MY_UTF16_LOW_HEAD(s[2]))  /* Broken surrigate pair */
+      return MY_CS_ILSEQ;
+
+    *pwc= MY_UTF16_WC4(s[0], s[1], s[2], s[3]);
+    return 4;
+  }
+
+  if (MY_UTF16_LOW_HEAD(*s)) /* Low surrogate part without high part */
+    return MY_CS_ILSEQ;
+
+  *pwc= MY_UTF16_WC2(s[0], s[1]);
+  return 2;
+}
+
+/* End of ctype-ucs2.c code. */
+
+enum json_c_classes {
+  C_EOS,    /* end of string */
+  C_LCURB,  /* {  */
+  C_RCURB,  /* } */
+  C_LSQRB,  /* [ */
+  C_RSQRB,  /* ] */
+  C_COLON,  /* : */
+  C_COMMA,  /* , */
+  C_QUOTE,  /* " */
+  C_DIGIT,  /* -0123456789 */
+  C_LOW_F,  /* f */
+  C_LOW_N,  /* n */
+  C_LOW_T,  /* t */
+  C_ETC,    /* everything else */
+  C_ERR,    /* character disallowed in JSON*/
+  C_BAD,    /* invalid character */
+  NR_C_CLASSES,
+  C_SPACE   /* space */
+};
+
+
+/*
+  This array maps first 128 Unicode Code Points into classes.
+  The remaining Unicode characters should be mapped to C_ETC.
+*/
+
+static int json_chr_map[128] = {
+  C_ERR,   C_ERR,   C_ERR,   C_ERR,   C_ERR,   C_ERR,   C_ERR,   C_ERR,
+  C_ERR,   C_SPACE, C_SPACE, C_ERR,   C_ERR,   C_SPACE, C_ERR,   C_ERR,
+  C_ERR,   C_ERR,   C_ERR,   C_ERR,   C_ERR,   C_ERR,   C_ERR,   C_ERR,
+  C_ERR,   C_ERR,   C_ERR,   C_ERR,   C_ERR,   C_ERR,   C_ERR,   C_ERR,
+
+  C_SPACE, C_ETC,   C_QUOTE, C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,
+  C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_COMMA, C_DIGIT, C_ETC,   C_ETC,
+  C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT,
+  C_DIGIT, C_DIGIT, C_COLON, C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,
+
+  C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,
+  C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,
+  C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,
+  C_ETC,   C_ETC,   C_ETC,   C_LSQRB, C_ETC,   C_RSQRB, C_ETC,   C_ETC,
+
+  C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_LOW_F, C_ETC,
+  C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_LOW_N, C_ETC,
+  C_ETC,   C_ETC,   C_ETC,   C_ETC,   C_LOW_T, C_ETC,   C_ETC,   C_ETC,
+  C_ETC,   C_ETC,   C_ETC,   C_LCURB, C_ETC,   C_RCURB, C_ETC,   C_ETC
+};
+
+
+typedef int (*json_state_handler)(json_engine *);
+
+
+/* The string is broken. */
+static int er_en(json_engine *j)
+{
+  j->error= JE_EOS;
+  return 1;
+}
+
+
+/* This symbol here breaks the JSON syntax. */
+static int __(json_engine *j)
+{
+  j->error= JE_UEX;
+  return 1;
+}
+
+
+/* Value of object. */
+static int ky_ob(json_engine *j)
+{
+  j->state= json_engine::OB_B;
+  *(++j->stack_p)= json_engine::OB_C;
+  return 0;
+}
+
+/* Read value of object. */
+static int rd_ob(json_engine *j)
+{
+  j->state= json_engine::OB_B;
+  j->value_type= json_engine::OBJECT;
+  j->value= j->value_begin;
+  *(++j->stack_p)= json_engine::OB_C;
+  return 0;
+}
+
+
+/* Value of array. */
+static int ky_ar(json_engine *j)
+{
+  j->state= json_engine::AR_B;
+  *(++j->stack_p)= json_engine::AR_C;
+  j->value= j->value_begin;
+  return 0;
+}
+
+/* Read value of object. */
+static int rd_ar(json_engine *j)
+{
+  j->state= json_engine::AR_B;
+  j->value_type= json_engine::ARRAY;
+  j->value= j->value_begin;
+  *(++j->stack_p)= json_engine::AR_C;
+  return 0;
+}
+
+
+
+/* \b 8  \f 12 \n 10  \r 13 \t 9 */
+enum json_s_classes {
+  S_0= 0,
+  S_1= 1,
+  S_2= 2,
+  S_3= 3,
+  S_4= 4,
+  S_5= 5,
+  S_6= 6,
+  S_7= 7,
+  S_8= 8,
+  S_9= 9,
+  S_A= 10,
+  S_B= 11,
+  S_C= 12,
+  S_D= 13,
+  S_E= 14,
+  S_F= 15,
+  S_ETC= 36,    /* rest of characters. */
+  S_QUOTE= 37,
+  S_SOLID= 38, /* \ */
+  S_ERR= 100,   /* disallowed */
+};
+
+
+/* This maps characters to their types inside a string constant. */
+static const int json_instr_chr_map[128] = {
+  S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,
+  S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,
+  S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,
+  S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,   S_ERR,
+
+  S_ETC,   S_ETC,   S_QUOTE, S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,
+  S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,
+  S_0,     S_1,     S_2,     S_3,     S_4,     S_5,     S_6,     S_7,
+  S_8,     S_9,     S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,
+
+  S_ETC,   S_A,     S_B,     S_C,     S_D,     S_E,     S_F,     S_ETC,
+  S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,
+  S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,
+  S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_SOLID, S_ETC,   S_ETC,   S_ETC,
+
+  S_ETC,   S_A,     S_B,     S_C,     S_D,     S_E,     S_F,     S_ETC,
+  S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,
+  S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,
+  S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC,   S_ETC
+};
+
+
+static int read_4digit(json_string *j, uchar *s)
+{
+  int i, t, c_len;
+  for (i=0; i<4; i++)
+  {
+    if ((c_len= j->get_next_char()) <= 0)
+      return j->error= j->eos() ? JE_EOS : JE_BAD;
+
+    if (j->c_next >= 128 || (t= json_instr_chr_map[j->c_next]) >= S_F)
+      return j->error= JE_UEX;
+
+    j->c_str+= c_len;
+    s[i/2]+= (i % 2) ? t : t*16;
+  }
+  return 0;
+}
+
+
+/* \b 8  \f 12 \n 10  \r 13 \t 9 */
+int json_string::handle_esc()
+{
+  int t, c_len;
+  
+  if ((c_len= get_next_char()) <= 0)
+    return error= eos() ? JE_EOS : JE_BAD;
+
+  c_str+= c_len;
+  switch (c_next)
+  {
+    case 'b':
+      c_next= 8;
+      return 0;
+    case 'f':
+      c_next= 12;
+      return 0;
+    case 'n':
+      c_next= 10;
+      return 0;
+    case 'r':
+      c_next= 13;
+      return 0;
+    case 't':
+      c_next= 9;
+      return 0;
+  }
+
+  if (c_next < 128 && (t= json_instr_chr_map[c_next]) == S_ERR)
+  {
+    c_str-= c_len;
+    return error= JE_ESCAPING;
+  }
+
+
+  if (c_next != 'u')
+    return 0;
+
+  /*
+    Read the four-hex-digits code.
+    If symbol is not in the Basic Multilingual Plane, we're reading
+    the string for the next four digits to compose the UTF-16 surrogate pair.
+  */
+  uchar s[4]= {0,0,0,0};
+
+  if (read_4digit(this, s))
+    return 1;
+  
+  if ((c_len= my_utf16_uni(0, &c_next, s, s+2)) == 2)
+    return 0;
+
+  if (c_len != MY_CS_TOOSMALL4)
+    return error= JE_BAD;
+
+  if ((c_len= get_next_char()) <= 0)
+    return error= eos() ? JE_EOS : JE_BAD;
+  if (c_next != '\\')
+    return error= JE_UEX;
+
+  if ((c_len= get_next_char()) <= 0)
+    return error= eos() ? JE_EOS : JE_BAD;
+  if (c_next != 'u')
+    return error= JE_UEX;
+
+  if (read_4digit(this, s+2))
+    return 1;
+
+  if ((c_len= my_utf16_uni(0, &c_next, s, s+4)) == 2)
+    return 0;
+
+  return error= JE_BAD;
+}
+
+
+int json_string::read_str_chr()
+{
+  int c_len;
+
+  if ((c_len= get_next_char()) > 0)
+  {
+    c_str+= c_len;
+    return (c_next == '\\') ? handle_esc() : 0;
+  }
+  error= eos() ? JE_EOS : JE_BAD; 
+  return 1;
+}
+
+
+inline int pass_str(json_engine *j)
+{
+  int t, c_len;
+  for (;;)
+  {
+    if ((c_len= j->get_next_char()) > 0)
+    {
+      j->c_str+= c_len;
+      if (j->c_next >= 128 || ((t=json_instr_chr_map[j->c_next]) <= S_ETC))
+        continue;
+
+      if (j->c_next == '"')
+        break;
+      if (j->c_next == '\\')
+      {
+        if (j->handle_esc())
+          return 1;
+        continue;
+      }
+      /* Symbol not allowed in JSON. */
+      return j->error= JE_IMP;
+    }
+    else
+      return j->error= j->eos() ? JE_EOS : JE_BAD; 
+  }
+
+  j->state= *j->stack_p;
+  return 0;
+}
+
+
+/* Scalar string. */
+static int scr_s(json_engine *j)
+{
+  return pass_str(j) || j->scan_next();
+}
+
+
+/* Read scalar string. */
+static int rd_s(json_engine *j)
+{
+  j->value= j->c_str;
+
+  if (pass_str(j))
+    return 1;
+
+  j->state= *j->stack_p;
+  j->value_type= json_engine::STRING;
+  j->value_len= (j->c_str - j->value) - 1;
+  return 0;
+}
+
+
+enum json_num_classes {
+  N_MINUS,
+  N_PLUS,
+  N_ZERO,
+  N_DIGIT,
+  N_POINT,
+  N_E,
+  N_END,
+  N_EEND,
+  N_ERR,
+  N_NUM_CLASSES
+};
+
+
+static int json_num_chr_map[128] = {
+  N_ERR,   N_ERR,   N_ERR,   N_ERR,   N_ERR,   N_ERR,   N_ERR,   N_ERR,
+  N_ERR,   N_END,   N_END,   N_ERR,   N_ERR,   N_END,   N_ERR,   N_ERR,
+  N_ERR,   N_ERR,   N_ERR,   N_ERR,   N_ERR,   N_ERR,   N_ERR,   N_ERR,
+  N_ERR,   N_ERR,   N_ERR,   N_ERR,   N_ERR,   N_ERR,   N_ERR,   N_ERR,
+
+  N_END,   N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,
+  N_EEND,  N_EEND,  N_EEND,  N_PLUS,  N_END,   N_MINUS, N_POINT, N_EEND,
+  N_ZERO,  N_DIGIT, N_DIGIT, N_DIGIT, N_DIGIT, N_DIGIT, N_DIGIT, N_DIGIT,
+  N_DIGIT, N_DIGIT, N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,
+
+  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_E,     N_EEND,  N_EEND,
+  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,
+  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,
+  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_END,   N_EEND,  N_EEND,
+
+  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_E,     N_EEND,  N_EEND,
+  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,
+  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,  N_EEND,
+  N_EEND,  N_EEND,  N_EEND,  N_EEND,  C_ETC,   N_END,   N_EEND,  N_EEND,
+};
+
+enum json_num_states {
+  NS_OK,
+  NS_GO,
+  NS_GO1,
+  NS_Z,
+  NS_Z1,
+  NS_INT,
+  NS_FRAC,
+  NS_EX,
+  NS_EX1,
+  NS_NUM_STATES
+};
+
+
+static int json_num_states[NS_NUM_STATES][N_NUM_CLASSES]=
+{
+/*            -        +       0        1..9    POINT    E       END_OK   ERROR */
+/* OK */   { JE_UEX,  JE_UEX, JE_UEX,   JE_UEX, JE_UEX,  JE_UEX, JE_UEX, JE_BAD },
+/* GO */   { NS_GO1,  JE_UEX, NS_Z,     NS_INT, JE_UEX,  JE_UEX, JE_UEX, JE_BAD },
+/* GO1 */  { JE_UEX,  JE_UEX, NS_Z1,    NS_INT, JE_UEX,  JE_UEX, JE_UEX, JE_BAD },
+/* ZERO */ { JE_UEX,  JE_UEX, JE_UEX,   JE_UEX, NS_FRAC, JE_UEX, NS_OK,  JE_BAD },
+/* ZE1 */  { JE_UEX,  JE_UEX, JE_UEX,   JE_UEX, NS_FRAC, JE_UEX, JE_UEX, JE_BAD },
+/* INT */  { JE_UEX,  JE_UEX, NS_INT,   NS_INT, NS_FRAC, NS_EX,  NS_OK,  JE_BAD },
+/* FRAC */ { JE_UEX,  JE_UEX, NS_FRAC,  NS_FRAC,JE_UEX,  NS_EX,  NS_OK,  JE_BAD },
+/* EX */   { NS_EX1,  NS_EX1, NS_EX1,   NS_EX1, JE_UEX,  JE_UEX, JE_UEX, JE_BAD }, 
+/* EX1 */  { JE_UEX,  JE_UEX, NS_EX1,   NS_EX1, JE_UEX,  JE_UEX, JE_UEX, JE_BAD }
+};
+
+
+inline int pass_n(json_engine *j)
+{
+  int state= json_num_states[NS_GO][json_num_chr_map[j->c_next]];
+  int c_len;
+
+  for (;;)
+  {
+    if ((c_len= j->get_next_char()) > 0)
+    {
+      if ((state= json_num_states[state][json_num_chr_map[j->c_next]]) > 0)
+      {
+        j->c_str+= c_len;
+        continue;
+      }
+      break;
+    }
+
+    if ((j->error= j->eos() ? json_num_states[state][N_END] : JE_BAD) < 0)
+      return 1;
+    else
+      break;
+  }
+
+  j->state= *j->stack_p;
+  return 0;
+}
+
+
+/* Scalar numeric. */
+static int scr_n(json_engine *j)
+{
+  return pass_n(j) || j->scan_next();
+}
+
+
+/* Read number. */
+static int rd_n(json_engine *j)
+{
+  j->value= j->value_begin;
+  if (pass_n(j) == 0)
+  {
+    j->value_type= json_engine::NUMBER;
+    j->value_len= j->c_str - j->value_begin;
+    return 0;
+  }
+  return 1;
+}
+
+
+static int assure_string(json_string *j, const char *str)
+{
+  int c_len;
+  while (*str)
+  {
+    if ((c_len= j->get_next_char()) > 0)
+    {
+      if (j->c_next == (my_wc_t) *(str++))
+      {
+        j->c_str+= c_len;
+        continue;
+      }
+      return JE_UEX;
+    }
+    return j->eos() ? JE_EOS : JE_BAD; 
+  }
+
+  return 0;
+}
+
+
+/* Scalar false. */
+static int scr_f(json_engine *j)
+{
+  return assure_string(j, "alse");
+}
+
+
+/* Scalar null. */
+static int scr_l(json_engine *j)
+{
+  return assure_string(j, "ull");
+}
+
+
+/* Scalar true. */
+static int scr_t(json_engine *j)
+{
+  return assure_string(j, "rue");
+}
+
+
+/* Read false. */
+static int rd_f(json_engine *j)
+{
+  j->value_type= json_engine::V_FALSE;
+  j->value= j->value_begin;
+  return assure_string(j, "alse");
+}
+
+
+/* Read null. */
+static int rd_l(json_engine *j)
+{
+  j->value_type= json_engine::V_NULL;
+  j->value= j->value_begin;
+  return assure_string(j, "ull");
+}
+
+
+/* Read true. */
+static int rd_t(json_engine *j)
+{
+  j->value_type= json_engine::V_TRUE;
+  j->value= j->value_begin;
+  return assure_string(j, "rue");
+}
+
+
+/* Disallowed character. */
+static int e_chr(json_engine *j)
+{
+  j->error= JE_IMP;
+  return 1;
+}
+
+
+/* Bad character. */
+static int b_chr(json_engine *j)
+{
+  j->error= JE_BAD;
+  return 1;
+}
+
+
+/* Correct finish. */
+static int done(json_engine *j)
+{
+  return 1;
+}
+
+
+/* End of the object. */
+static int en_ob(json_engine *j)
+{
+  j->stack_p--;
+  j->state= json_engine::OB_E;
+  return 0;
+}
+
+
+/* End of the array. */
+static int en_ar(json_engine *j)
+{
+  j->stack_p--;
+  j->state= json_engine::AR_E;
+  return 0;
+}
+
+
+/* Start reading key name. */
+static int get_k(json_engine *j)
+{
+  j->state= json_engine::KEY;
+  return 0;
+}
+
+
+inline void json_string::skip_s_getchar(int &t_next, int &c_len)
+{
+  do
+  {
+    if ((c_len= get_next_char()) <= 0)
+      t_next= eos() ? C_EOS : C_BAD;
+    else
+    {
+      t_next= (c_next < 128) ? json_chr_map[c_next] : C_ETC;
+      c_str+= c_len;
+    }
+  } while (t_next == C_SPACE);
+}
+
+
+/* Next key name. */
+static int nex_k(json_engine *j)
+{
+  int t_next, c_len;
+  j->skip_s_getchar(t_next, c_len);
+
+  if (t_next == C_QUOTE)
+  {
+    j->state= json_engine::KEY;
+    return 0;
+  }
+
+  j->error= (t_next == C_EOS)  ? JE_EOS :
+            ((t_next == C_BAD) ? JE_BAD :
+                                 JE_UEX);
+  return 1;
+}
+
+
+/* Forward declarations. */
+static int skp_c(json_engine *j);
+static int skp_k(json_engine *j);
+static int ee_cb(json_engine *j);
+static int ee_qb(json_engine *j);
+static int ee_cm(json_engine *j);
+static int ee_dn(json_engine *j);
+
+
+static int ar_c(json_engine *j)
+{
+  j->state= json_engine::VALUE;
+  return 0;
+}
+
+
+static int arb(json_engine *j)
+{
+  j->state= json_engine::VALUE;
+  j->c_str-= j->sav_c_len;
+  return 0;
+}
+
+
+static json_state_handler json_actions[json_engine::NR_STATES][NR_C_CLASSES]=
+/*        EOS    {      }      [      ]      :      ,      "      -0..9  f      n      t      ETC ERR   BAD*/
+{
+/*VALUE*/{er_en, ky_ob, __,    ky_ar, __,    __,    __,    scr_s, scr_n, scr_f, scr_l, scr_t, __,    e_chr, b_chr},
+/*DONE*/ {done,  __,    __,    __,    __,    __,    __,    __,    __,    __,    __,    __,    __,    e_chr, b_chr},
+/*KEY*/  {er_en, skp_k, skp_k, skp_k, skp_k, skp_k, skp_k, skp_c, skp_k, skp_k, skp_k, skp_k, skp_k, e_chr, b_chr},
+/*OB_B*/ {er_en, __,    en_ob, __,    __,    __,    __,    get_k, __,    __,    __,    __,    __,    e_chr, b_chr},
+/*OB_E*/ {ee_dn, __,    ee_cb,  __,   ee_qb, __,    ee_cm, __,    __,    __,    __,    __,    __,    e_chr, b_chr},
+/*OB_C*/ {er_en, __,    en_ob, __,    en_ar, __,    nex_k, __,    __,    __,    __,    __,    __,    e_chr, b_chr},
+/*AR_B*/ {er_en, arb,   __,    arb,   en_ar, __,    __,    arb,   arb,   arb,   arb,   arb,   __,    e_chr, b_chr},
+/*AR_E*/ {ee_dn, __,    ee_cb, __,    ee_qb, __,    ee_cm, __,    __,    __,    __,    __,    __,    e_chr, b_chr},
+/*AR_C*/ {er_en, __,    __,    __,    en_ar, __,    ar_c,  __,    __,    __,    __,    __,    __,    e_chr, b_chr},
+/*RD_V*/ {er_en, rd_ob, __,    rd_ar, __,    __,    __,    rd_s,  rd_n,  rd_f,  rd_l,  rd_t,  __,    e_chr, b_chr},
+};
+
+
+void json_string::set_cs(CHARSET_INFO *i_cs)
+{
+  cs= i_cs;
+  error= 0;
+  wc= i_cs->cset->mb_wc;
+}
+
+
+int json_engine::scan_start(CHARSET_INFO *i_cs, const uchar *str, const uchar *end)
+{
+  json_string::setup(i_cs, str, end);
+  stack[0]= json_engine::DONE;
+  stack_p= stack;
+  state= json_engine::VALUE;
+  return 0;
+}
+
+
+/* Skip colon and the value. */
+static int skp_c(json_engine *j)
+{
+  int t_next, c_len;
+
+  j->skip_s_getchar(t_next, c_len);
+
+  if (t_next == C_COLON)
+  {
+    j->skip_s_getchar(t_next, c_len);
+    return json_actions[json_engine::VALUE][t_next](j);
+ }
+
+  j->error= (t_next == C_EOS)  ? JE_EOS :
+            ((t_next == C_BAD) ? JE_BAD :
+                                 JE_UEX);
+
+  return 1;
+}
+
+
+/* Skip colon and the value. */
+static int skp_k(json_engine *j)
+{
+  int t_next, c_len;
+  while (j->read_keyname_chr() == 0) {}
+
+  if (j->error)
+    return 1;
+
+  j->skip_s_getchar(t_next, c_len);
+  return json_actions[json_engine::VALUE][t_next](j);
+}
+
+
+/* Continue after the end of the structure. */
+static int ee_dn(json_engine *j)
+{ return json_actions[*j->stack_p][C_EOS](j); }
+
+
+/* Continue after the end of the structure. */
+static int ee_cb(json_engine *j)
+{ return json_actions[*j->stack_p][C_RCURB](j); }
+
+
+/* Continue after the end of the structure. */
+static int ee_qb(json_engine *j)
+{ return json_actions[*j->stack_p][C_RSQRB](j); }
+
+
+/* Continue after the end of the structure. */
+static int ee_cm(json_engine *j)
+{ return json_actions[*j->stack_p][C_COMMA](j); }
+
+
+int json_engine::read_keyname_chr()
+{
+  int c_len, t;
+
+  if ((c_len= get_next_char()) > 0)
+  {
+    c_str+= c_len;
+    if (c_next>= 128 || (t= json_instr_chr_map[c_next]) <= S_ETC)
+      return 0;
+
+    switch (t)
+    {
+    case S_QUOTE:
+      for (;;)  /* Skip spaces until ':'. */
+      {
+        if ((c_len= get_next_char() > 0))
+        {
+          if (c_next == ':')
+          {
+            c_str+= c_len;
+            state= VALUE;
+            return 1;
+          }
+
+          if (c_next < 128 && json_chr_map[c_next] == C_SPACE)
+          {
+            c_str+= c_len;
+            continue;
+          }
+          error= JE_UEX;
+          break;
+        }
+        error= eos() ? JE_EOS : JE_BAD;
+        break;
+      }
+      return 1;
+    case S_SOLID:
+      return handle_esc();
+    case S_ERR:
+      c_str-= c_len;
+      error= JE_IMS;
+      return 1;
+    }
+  }
+  error= eos() ? JE_EOS : JE_BAD; 
+  return 1;
+}
+
+
+int json_engine::read_value()
+{
+  int t_next, c_len, res;
+
+  skip_s_getchar(t_next, c_len);
+
+  value_begin= c_str-c_len;
+  res= json_actions[READ_VALUE][t_next](this);
+  value_end= c_str;
+  return res;
+}
+
+
+int json_engine::scan_next()
+{
+  int t_next;
+
+  skip_s_getchar(t_next, sav_c_len);
+  return json_actions[state][t_next](this);
+}
+
+
+enum json_path_c_classes {
+  P_EOS,    /* end of string */
+  P_USD,    /* $ */
+  P_ASTER,  /* * */
+  P_LSQRB,  /* [ */
+  P_RSQRB,  /* ] */
+  P_POINT,  /* . */
+  P_ZERO,   /* 0 */
+  P_DIGIT,  /* 123456789 */
+  P_L,      /* l */
+  P_S,      /* s */
+  P_SPACE,  /* space */
+  P_SOLID,  /* \ */
+  P_ETC,    /* everything else */
+  P_ERR,    /* character disallowed in JSON*/
+  P_BAD,    /* invalid character */
+  N_PATH_CLASSES,
+};
+
+
+static int json_path_chr_map[128] = {
+  P_ERR,   P_ERR,   P_ERR,   P_ERR,   P_ERR,   P_ERR,   P_ERR,   P_ERR,
+  P_ERR,   P_SPACE, P_SPACE, P_ERR,   P_ERR,   P_SPACE, P_ERR,   P_ERR,
+  P_ERR,   P_ERR,   P_ERR,   P_ERR,   P_ERR,   P_ERR,   P_ERR,   P_ERR,
+  P_ERR,   P_ERR,   P_ERR,   P_ERR,   P_ERR,   P_ERR,   P_ERR,   P_ERR,
+
+  P_SPACE, P_ETC,   P_ETC,   P_ETC,   P_USD,   P_ETC,   P_ETC,   P_ETC,
+  P_ETC,   P_ASTER, P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_POINT, P_ETC,
+  P_ZERO,  P_DIGIT, P_DIGIT, P_DIGIT, P_DIGIT, P_DIGIT, P_DIGIT, P_DIGIT,
+  P_DIGIT, P_DIGIT, P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC,
+
+  P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC,
+  P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_L,     P_ETC,   P_ETC,   P_ETC,
+  P_ETC,   P_ETC,   P_S,     P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC,
+  P_ETC,   P_ETC,   P_ETC,   P_LSQRB, P_SOLID, P_RSQRB, P_ETC,   P_ETC,
+
+  P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC,
+  P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_L,     P_ETC,   P_ETC,   P_ETC,
+  P_ETC,   P_ETC,   P_S,     P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC,
+  P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC,   P_ETC
+};
+
+
+enum json_path_states {
+  PS_GO,
+  PS_LAX,
+  PS_PT,
+  PS_AR,
+  PS_AWD,
+  PS_Z,
+  PS_INT,
+  PS_AS,
+  PS_KEY,
+  PS_KNM,
+  PS_KWD,
+  N_PATH_STATES,
+  PS_SCT,
+  PS_EKY,
+  PS_EAR,
+  PS_ESC,
+  PS_OK,
+  PS_KOK
+};
+
+
+static int json_path_transitions[N_PATH_STATES][N_PATH_CLASSES]=
+{
+/*        EOS       $,      *       [       ]       .       0       1..9    L       S       SPACE   SOLID   ETC     ERR     BAD  */
+/* GO  */ { JE_EOS, PS_PT,  JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_UEX, PS_LAX, PS_SCT, PS_GO,  JE_UEX, JE_UEX, JE_IMP, JE_BAD},
+/* LAX */ { JE_EOS, JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_UEX, PS_LAX, JE_UEX, PS_GO,  JE_UEX, JE_UEX, JE_IMP, JE_BAD},
+/* PT */  { PS_OK,  JE_UEX, JE_UEX, PS_AR,  JE_UEX, PS_KEY, JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_IMP, JE_BAD},
+/* AR */  { JE_EOS, JE_UEX, PS_AWD, JE_UEX, PS_PT,  JE_UEX, PS_Z,   PS_INT, JE_UEX, JE_UEX, PS_AR,  JE_UEX, JE_UEX, JE_IMP, JE_BAD},
+/* AWD */ { JE_EOS, JE_UEX, JE_UEX, JE_UEX, PS_PT,  JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_UEX, PS_AS,  JE_UEX, JE_UEX, JE_IMP, JE_BAD},
+/* Z */   { JE_EOS, JE_UEX, JE_UEX, JE_UEX, PS_PT,  JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_UEX, PS_AS,  JE_UEX, JE_UEX, JE_IMP, JE_BAD},
+/* INT */ { JE_EOS, JE_UEX, JE_UEX, JE_UEX, PS_PT,  JE_UEX, PS_INT, PS_INT, JE_UEX, JE_UEX, PS_AS,  JE_UEX, JE_UEX, JE_IMP, JE_BAD},
+/* AS */  { JE_EOS, JE_UEX, JE_UEX, JE_UEX, PS_PT,  JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_UEX, PS_AS,  JE_UEX, JE_UEX, JE_IMP, JE_BAD},
+/* KEY */ { JE_EOS, PS_KNM, PS_KWD, JE_UEX, PS_KNM, JE_UEX, PS_KNM, PS_KNM, PS_KNM, PS_KNM, PS_KNM, JE_UEX, PS_KNM, JE_IMP, JE_BAD},
+/* KNM */ { PS_KOK, PS_KNM, PS_KNM, PS_EAR, PS_KNM, PS_EKY, PS_KNM, PS_KNM, PS_KNM, PS_KNM, PS_KNM, PS_ESC, PS_KNM, JE_IMP, JE_BAD},
+/* KWD */ { PS_OK,  JE_UEX, JE_UEX, PS_AR,  JE_UEX, PS_EKY, JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_UEX, JE_IMP, JE_BAD}
+};
+
+
+int json_path::setup(CHARSET_INFO *i_cs, const uchar *str, const uchar *end)
+{
+  int c_len, t_next, state= PS_GO;
+
+  json_string::setup(i_cs, str, end);
+
+  steps[0].type= json_path_step::ARRAY;
+  steps[0].wild= 1;
+  last_step= steps;
+  mode_strict= false;
+
+  do
+  {
+    if ((c_len= get_next_char()) <= 0)
+      t_next= eos() ? P_EOS : P_BAD;
+    else
+      t_next= (c_next >= 128) ? P_ETC : json_path_chr_map[c_next];
+
+    if ((state= json_path_transitions[state][t_next]) < 0)
+      return error= state;
+
+    c_str+= c_len;
+
+    switch (state)
+    {
+    case PS_LAX:
+      if ((error= assure_string(this, "ax")))
+        return 1;
+      mode_strict= false;
+      continue;
+    case PS_SCT:
+      if ((error= assure_string(this, "rict")))
+        return 1;
+      mode_strict= true;
+      state= PS_LAX;
+      continue;
+    case PS_AWD:
+      last_step->wild= 1;
+      continue;
+    case PS_INT:
+      last_step->n_item*= 10;
+      last_step->n_item+= c_next - '0';
+      continue;
+    case PS_EKY:
+      last_step->key_end= c_str - c_len;
+      state= PS_KEY;
+      /* Note no 'continue' here. */
+    case PS_KEY:
+      last_step++;
+      last_step->type= json_path_step::KEY;
+      last_step->wild= 0;
+      last_step->key= c_str;
+      continue;
+    case PS_EAR:
+      last_step->key_end= c_str - c_len;
+      state= PS_AR;
+      /* Note no 'continue' here. */
+    case PS_AR:
+      last_step++;
+      last_step->type= json_path_step::ARRAY;
+      last_step->wild= 0;
+      last_step->n_item= 0;
+      continue;
+    case PS_KWD:
+      last_step->wild= 1;
+      continue;
+    case PS_ESC:
+      if (handle_esc())
+        return 1;
+      continue;
+    case PS_KOK:
+      last_step->key_end= c_str - c_len;
+      state= PS_OK;
+      break;
+    };
+  } while (state != PS_OK);
+
+  return 0;
+}
+
+
+int json_engine::skip_level()
+{
+  int ct= 0;
+
+  while (scan_next())
+  {
+    switch (state) {
+    case OB_B:
+    case AR_B:
+      ct++;
+      break;
+    case OB_E:
+    case AR_E:
+      if (ct == 0)
+        return 0;
+      ct--;
+      break;
+    }
+  }
+
+  return 1;
+}
+
+
+int json_engine::skip_key()
+{
+  if (read_value())
+    return 1;
+
+  if (value_scalar())
+    return 0;
+
+  return skip_level();
+}
diff --git a/sql/sql_json.h b/sql/sql_json.h
new file mode 100644
index 0000000..d46bd78
--- /dev/null
+++ b/sql/sql_json.h
@@ -0,0 +1,154 @@
+#ifndef SQL_JSON_INCLUDED
+#define SQL_JSON_INCLUDED
+
+#include <m_ctype.h>
+
+static const int json_depth_limit= 32;
+
+enum json_errors {
+  JE_BAD= -1,       /* Invalid character - cannot read the string. */
+  JE_IMP= -2,       /* This character disallowed in JSON. */
+  JE_EOS= -3,       /* Unexpected end of string. */
+  JE_UEX= -4,       /* Syntax error - unexpected character. */
+  JE_IMS= -5,       /* This character disallowed in string constant. */
+  JE_ESCAPING= -6,  /* Wrong escaping. */
+  JE_TOODEEP= -7,   /* The limit on the depth was overrun. */
+};
+
+
+class json_string
+{
+public:
+  const uchar *c_str;
+  const uchar *str_end;
+  my_wc_t c_next;
+  int error;
+
+  CHARSET_INFO *cs;
+  my_charset_conv_mb_wc wc;
+  int get_next_char()
+  {
+    return wc(cs, &c_next, c_str, str_end);
+  }
+  my_wc_t next_chr() const { return c_next; }
+  bool eos() const { return c_str >= str_end; }
+  int handle_esc();
+  void skip_s_getchar(int &t_next, int &c_len);
+  void set_cs(CHARSET_INFO *i_cs);
+  void set_str(const uchar *str, const uchar *end)
+  {
+    c_str= str;
+    str_end= end;
+  }
+  void setup(CHARSET_INFO *i_cs, const uchar *str, const uchar *end)
+  {
+    set_cs(i_cs);
+    set_str(str, end);
+  }
+  int read_str_chr();
+};
+
+
+struct json_path_step
+{
+  enum types { KEY=0, ARRAY=1 };
+
+  types type;
+  int wild;
+  const uchar *key;
+  const uchar *key_end;
+  uint n_item;
+};
+
+
+/*
+  Stores information about JSON path.
+  Actually the 'setup()' method parses the path string and fills members.
+*/
+class json_path : public json_string
+{
+public:
+  json_path_step steps[json_depth_limit];
+  json_path_step *last_step;
+
+  bool mode_strict;
+  int setup(CHARSET_INFO *i_cs, const uchar *str, const uchar *end);
+};
+
+
+/*
+  Repesents the JSON parsing process.
+  Normally supposed to be used like this:
+
+  js_end.scan_start(&ci, buf, buf+n_size);
+  while (js_eng.scan_next() == 0)
+  {
+    switch (js_eng.state)
+    {
+    case json_engine::KEY:
+      printf("Next key met.\n");
+      break;
+    case json_engine::OB_B:
+      printf("Object starts.\n");
+      break;
+    case json_engine::OB_E:
+      printf("Object ends.\n");
+      break;
+    case json_engine::AR_B:
+      printf("Array starts.\n");
+      break;
+    case json_engine::VALUE:
+      printf("Next array item met. \n");
+      break;
+    case json_engine::AR_E:
+      printf("Array ends.\n");
+      break;
+    }
+ }
+*/
+class json_engine : public json_string
+{
+public:
+  enum states {
+    VALUE, /* value met        */
+    DONE,  /* ok to finish     */
+    KEY,   /* key met          */
+    OB_B,  /* object           */
+    OB_E,  /* object ended     */
+    OB_C,  /* object continues */
+    AR_B,  /* array            */
+    AR_E,  /* array ended      */
+    AR_C,  /* array continues  */
+    READ_VALUE, /* value is beeing read */
+    NR_STATES
+  };
+
+  enum value_types
+  { OBJECT=0, ARRAY=1, STRING, NUMBER, V_TRUE, V_FALSE, V_NULL };
+
+  int sav_c_len;
+  value_types value_type;
+  const uchar *value;
+  const uchar *value_begin;
+  const uchar *value_end;
+  int value_len;
+
+  int stack[json_depth_limit];
+  int *stack_p;
+
+  int state;
+
+  int scan_start(CHARSET_INFO *i_cs, const uchar *str, const uchar *end);
+  int scan_next();
+
+  int read_keyname_chr();
+
+  int read_value();
+  int skip_key();
+  int skip_level();
+  int skip_array_item() { return skip_key(); }
+  bool value_scalar() const { return value_type > ARRAY; }
+};
+
+#endif /* SQL_JSON_INCLUDED */
+



More information about the commits mailing list