[Commits] 8cd65cd: MDEV-9143 JSON_xxx functions.

Alexey Botchkov holyfoot at askmonty.org
Tue Sep 13 14:54:17 EEST 2016


revision-id: 8cd65cd3e31aa6129573c6fbba9feb06c714e00c (mariadb-10.1.8-247-g8cd65cd)
parent(s): 76a0ed2e03c6ae1ff791534e742c812d5e83ba63
committer: Alexey Botchkov
timestamp: 2016-09-13 15:48:03 +0400
message:

MDEV-9143 JSON_xxx functions.

        Library added to operate JSON format.
        SQL functions to handle JSON data added:
           required by SQL standard:
                JSON_VALUE
                JSON_QUERY
                JSON_EXISTS
           MySQL functions:
                JSON_VALID
                JSON_ARRAY
                JSON_ARRAY_APPEND
                JSON_CONTAINS_PATH
                JSON_EXTRACT
                JSON_OBJECT
                JSON_QUOTE
                JSON_MERGE
         Some MySQL functions still missing, but no specific
         difficulties to implement them too in same manner.
         JSON_TABLE of the SQL standard is missing as it's not
         clean how and whether we'd like to implement it.

---
 include/CMakeLists.txt        |    1 +
 include/json_lib.h            |  338 ++++++++++
 mysql-test/r/func_json.result |   99 +++
 mysql-test/t/func_json.test   |   44 ++
 sql/CMakeLists.txt            |    2 +-
 sql/item.h                    |   15 +
 sql/item_create.cc            |  351 ++++++++++
 sql/item_jsonfunc.cc          |  898 ++++++++++++++++++++++++++
 sql/item_jsonfunc.h           |  240 +++++++
 sql/item_xmlfunc.cc           |   21 +-
 sql/item_xmlfunc.h            |    5 -
 sql/sql_yacc.yy               |    4 +-
 strings/CMakeLists.txt        |    2 +-
 strings/ctype-ucs2.c          |   19 +-
 strings/json_lib.c            | 1426 +++++++++++++++++++++++++++++++++++++++++
 15 files changed, 3433 insertions(+), 32 deletions(-)

diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index d0c4768..3b2c2c6 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -60,6 +60,7 @@ SET(HEADERS
   my_compiler.h
   handler_state.h
   handler_ername.h
+  json_lib.h
 )
 
 INSTALL(FILES ${HEADERS} DESTINATION ${INSTALL_INCLUDEDIR} COMPONENT Development)
diff --git a/include/json_lib.h b/include/json_lib.h
new file mode 100644
index 0000000..592096f
--- /dev/null
+++ b/include/json_lib.h
@@ -0,0 +1,338 @@
+#ifndef JSON_LIB_INCLUDED
+#define JSON_LIB_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define JSON_DEPTH_LIMIT 32
+
+/*
+  When error happens, the c_next of the JSON engine contains the
+  character that caused the error, and the c_str is the position
+  in string where the error occurs.
+*/
+enum json_errors {
+  JE_BAD_CHR= -1,      /* Invalid character, charset handler cannot read it. */
+
+  JE_NOT_JSON_CHR= -2, /* Character met not used in JSON. */
+                       /* ASCII 00-08 for instance.       */
+
+  JE_EOS= -3,          /* Unexpected end of string. */
+
+  JE_SYN= -4,          /* The next character breaks the JSON syntax. */
+
+  JE_STRING_CONST= -5, /* Character disallowed in string constant. */
+
+  JE_ESCAPING= -6,     /* Error in the escaping. */
+
+  JE_DEPTH= -7,        /* The limit on the JSON depth was overrun. */
+};
+
+
+typedef struct st_json_string_t
+{
+  const uchar *c_str;    /* Current position in JSON string */
+  const uchar *str_end;  /* The end on the string. */
+  my_wc_t c_next;        /* UNICODE of the last read character */
+  int error;             /* error code. */
+
+  CHARSET_INFO *cs;      /* Character set of the JSON string. */
+
+  my_charset_conv_mb_wc wc; /* UNICODE conversion function. */
+                            /* It's taken out of the cs just to speed calls. */
+} json_string_t;
+
+
+void json_string_set_cs(json_string_t *s, CHARSET_INFO *i_cs);
+void json_string_set_str(json_string_t *s,
+                         const uchar *str, const uchar *end);
+#define json_next_char(j) \
+  (j)->wc((j)->cs, &(j)->c_next, (j)->c_str, (j)->str_end)
+#define json_eos(j) ((j)->c_str >= (j)->str_end)
+/*
+  read_string_const_chr() reads the next character of the string constant
+  and saves it to the js->c_next.
+  It takes into account possible escapings, so if for instance
+  the string is '\b', the read_string_const_chr() sets 8.
+*/
+int json_read_string_const_chr(json_string_t *js);
+
+
+/*
+  Various JSON-related operations expect JSON path as a parameter.
+  The path is a string like this "$.keyA[2].*"
+  The path itself is a number of steps specifying either a key or a position
+  in an array. Some of them can be wildcards.
+  So the representation of the JSON path is the json_path_t class
+  containing an array of json_path_step_t objects.
+*/
+
+
+enum json_path_step_types
+{
+  JSON_PATH_KEY=0,
+  JSON_PATH_ARRAY=1
+};
+
+
+typedef struct st_json_path_step_t
+{
+  enum json_path_step_types type;  /* The type of the step - KEY or ARRAY */
+  int wild;         /* If the step is a wildcard */
+  const uchar *key; /* Pointer to the beginning of the key. */
+  const uchar *key_end;  /* Pointer to the end of the key. */
+  uint n_item;      /* Item number in an array. No meaning for the key step. */
+} json_path_step_t;
+
+
+typedef struct st_json_path_t
+{
+  json_string_t s;  /* The string to be parsed. */
+  json_path_step_t steps[JSON_DEPTH_LIMIT]; /* Steps of the path. */
+  json_path_step_t *last_step; /* Points to the last step. */
+
+  int mode_strict; /* TRUE if the path specified as 'strict' */
+} json_path_t;
+
+
+int json_path_setup(json_path_t *p,
+                    CHARSET_INFO *i_cs, const uchar *str, const uchar *end);
+
+
+/*
+  The set of functions and structures below provides interface
+  to the JSON text parser.
+  Running the parser normally goes like this:
+
+    json_engine_t j_eng;   // structure keeps parser's data
+    json_scan_start(j_eng) // begin the parsing
+
+    do
+    {
+      // The parser has read next piece of JSON
+      // and set fields of j_eng structure accordingly.
+      // So let's see what we have:
+      switch (j_eng.state)
+      {
+        case JST_KEY:
+           // Handle key name. See the json_read_keyname_chr()
+           // Probably compare it with the keyname we're looking for
+        case JST_VALUE:
+           // Handle value. It is either value of the key or an array item.
+           // see the json_read_value()
+        case JST_OBJECT_BEGIN:
+          // parser found an object (the '{' in JSON)
+        case JST_OBJECT_END:
+          // parser found the end of the object (the '}' in JSON)
+        case JST_ARRAY_BEGIN:
+          // parser found an array (the '[' in JSON)
+        case JST_ARRAY_END:
+          // parser found the end of the array (the ']' in JSON)
+
+      };
+    } while (json_scan_next() == 0);  // parse next structure
+
+    
+    if (j_eng.s.error)  // we need to check why the loop ended.
+                        // Did we get to the end of JSON, or came upon error.
+    {
+       signal_error_in_JSON()
+    }
+
+
+  Parts of JSON can be quickly skipped. If we are not interested
+  in a particular key, we can just skip it with json_skip_key() call.
+  Similarly json_skip_level() goes right to the end of an object
+  or an array.
+*/
+
+
+/* These are JSON parser states that user can expect and handle.  */
+enum json_states {
+  JST_VALUE,       /* value found      */
+  JST_KEY,         /* key found        */
+  JST_OBJ_START,   /* object           */
+  JST_OBJ_END,     /* object ended     */
+  JST_ARRAY_START, /* array            */
+  JST_ARRAY_END,   /* array ended      */
+  NR_JSON_USER_STATES
+};
+
+
+enum json_value_types
+{
+  JSON_VALUE_OBJECT=0,
+  JSON_VALUE_ARRAY=1,
+  JSON_VALUE_STRING,
+  JSON_VALUE_NUMBER,
+  JSON_VALUE_TRUE,
+  JSON_VALUE_FALSE,
+  JSON_VALUE_NULL
+};
+
+
+typedef struct st_json_engine_t
+{
+  json_string_t s;  /* String to parse. */
+  int sav_c_len;    /* Length of the current character.
+                       Can be more than 1 for multibyte charsets */
+
+  int state; /* The state of the parser. One of 'enum json_states'.
+                It tells us what construction of JSON we've just read. */
+
+  /* These values are only set after the json_read_value() call. */
+  enum json_value_types value_type; /* type of the value.*/
+  const uchar *value;      /* Points to the value. */
+  const uchar *value_begin;/* Points to where the value starts in the JSON. */
+
+  /*
+    In most cases the 'value' and 'value_begin' are equal.
+    They only differ if the value is a string constants. Then 'value_begin'
+    points to the starting quotation mark, while the 'value' - to
+    the first character of the string.
+  */
+
+  const uchar *value_end; /* Points to the next character after the value. */
+  int value_len; /* The length of the value. Does not count quotations for */
+                 /* string constants. */
+
+  int stack[JSON_DEPTH_LIMIT]; /* Keeps the stack of nested JSON structures. */
+  int *stack_p;                /* The 'stack' pointer. */
+} json_engine_t;
+
+
+int json_scan_start(json_engine_t *je,
+                        CHARSET_INFO *i_cs, const uchar *str, const uchar *end);
+int json_scan_next(json_engine_t *j);
+
+
+/*
+  json_read_keyname_chr() function assists parsing the name of an JSON key.
+  It only can be called when the json_engine is in JST_KEY.
+  The json_read_keyname_chr() reads one character of the name of the key,
+  and puts it in j_eng.s.next_c.
+  Typical usage is like this:
+
+  if (j_eng.state == JST_KEY)
+  {
+    while (json_read_keyname_chr(&j) == 0)
+    {
+      //handle next character i.e. match it against the pattern
+    }
+  }
+*/
+
+int json_read_keyname_chr(json_engine_t *j);
+
+
+/*
+  json_read_value() function parses the JSON value syntax,
+  so that we can handle the value of a key or an array item.
+  It only returns meaningful result when the engine is in
+  the JST_VALUE state.
+
+  Typical usage is like this:
+
+  if (j_eng.state ==  JST_VALUE)
+  {
+    json_read_value(&j_eng);
+    switch(j_eng.value_type)
+    {
+      case JSON_VALUE_STRING:
+        // get the string
+        str= j_eng.value;
+        str_length= j_eng.value_len;
+      case JSON_VALUE_NUMBER:
+        // get the number
+      ... etc
+    }
+*/
+int json_read_value(json_engine_t *j);
+
+
+/*
+  json_skip_key() makes parser skip the content of the current
+  JSON key quickly.
+  It can be called only when the json_engine state is JST_KEY.
+  Typical usage is:
+
+  if (j_eng.state == JST_KEY)
+  {
+    if (key_does_not_match(j_eng))
+      json_skip_key(j_eng);
+  }
+*/
+
+int json_skip_key(json_engine_t *j);
+
+
+/*
+  json_skip_level() makes parser quickly skip the JSON content
+  to the end of the current object or array.
+  It is used when we're not interested in the rest of an array
+  or the rest of the keys of an object.
+*/
+int json_skip_level(json_engine_t *j);
+
+
+#define json_skip_array_item json_skip_key
+
+/*
+  Checks if the current value is of scalar type -
+  not an OBJECT nor ARRAY.
+*/
+#define json_value_scalar(je)  ((je)->value_type > JSON_VALUE_ARRAY)
+
+/*
+  Look for the JSON PATH in the json string.
+  Function can be called several times with same JSON/PATH to
+  find multiple matches.
+  On the first call, the json_engine_t parameter should be
+  initialized with the JSON string, and the json_path_t with the JSON path
+  appropriately. The 'p_cur_step' should point at the first
+  step of the path.
+  If function returns 0, it means it found the match. The position of
+  the match is je->s.c_str. Then we can call the json_find_value()
+  with same engine/path/p_cur_step to get the next match.
+  Non-zero return means no matches found.
+  Check je->s.error to see if there was an error in JSON.
+*/
+int json_find_value(json_engine_t *je,
+                    json_path_t *p, json_path_step_t **p_cur_step);
+
+
+/*
+  Converst JSON string constant into ordinary string constant
+  which can involve unpacking json escapes and changing character set.
+  Returns negative integer in the case of an error,
+  the length of the result otherwise.
+*/
+int json_unescape(CHARSET_INFO *json_cs,
+                  const uchar *json_str, const uchar *json_end,
+                  CHARSET_INFO *res_cs,
+                  uchar *res, uchar *res_end);
+
+/*
+  Converst ordinary string constant into JSON string constant.
+  which can involve appropriate escaping and changing character set.
+  Returns negative integer in the case of an error,
+  the length of the result otherwise.
+*/
+int json_escape(CHARSET_INFO *str_cs, const uchar *str, const uchar *str_end,
+                CHARSET_INFO *json_cs, uchar *json, uchar *json_end);
+
+
+/*
+  Appends the ASCII string to the json with the charset conversion.
+*/
+int json_append_ascii(CHARSET_INFO *json_cs,
+                      uchar *json, uchar *json_end,
+                      const uchar *ascii, const uchar *ascii_end);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* JSON_LIB_INCLUDED */
+
diff --git a/mysql-test/r/func_json.result b/mysql-test/r/func_json.result
new file mode 100644
index 0000000..1ebe0b2
--- /dev/null
+++ b/mysql-test/r/func_json.result
@@ -0,0 +1,99 @@
+select json_valid('[1, 2]');
+json_valid('[1, 2]')
+1
+select json_valid('"string"}');
+json_valid('"string"}')
+0
+select json_valid('{"key1":1, "key2":[2,3]}');
+json_valid('{"key1":1, "key2":[2,3]}')
+1
+select json_valid('[false, true, null]');
+json_valid('[false, true, null]')
+1
+select json_value('{"key1":123}', '$.key2');
+json_value('{"key1":123}', '$.key2')
+NULL
+select json_value('{"key1":123}', '$.key1');
+json_value('{"key1":123}', '$.key1')
+123
+select json_value('{"key1":[1,2,3]}', '$.key1');
+json_value('{"key1":[1,2,3]}', '$.key1')
+NULL
+select json_value('{"key1": [1,2,3], "key1":123}', '$.key1');
+json_value('{"key1": [1,2,3], "key1":123}', '$.key1')
+123
+select json_query('{"key1":{"a":1, "b":[1,2]}}', '$.key2');
+json_query('{"key1":{"a":1, "b":[1,2]}}', '$.key2')
+NULL
+select json_query('{"key1":{"a":1, "b":[1,2]}}', '$.key1');
+json_query('{"key1":{"a":1, "b":[1,2]}}', '$.key1')
+{"a":1, "b":[1,2]}
+select json_query('{"key1": 1}', '$.key1');
+json_query('{"key1": 1}', '$.key1')
+NULL
+select json_query('{"key1":123, "key1": [1,2,3]}', '$.key1');
+json_query('{"key1":123, "key1": [1,2,3]}', '$.key1')
+[1,2,3]
+select json_array(1);
+json_array(1)
+[1]
+select json_array(1, "text", false, null);
+json_array(1, "text", false, null)
+[1, "text", false, null]
+select json_array_append('["a", "b"]', '$', FALSE);
+json_array_append('["a", "b"]', '$', FALSE)
+["a", "b", false]
+select json_array_append('{"k1":1, "k2":["a", "b"]}', '$.k2', 2);
+json_array_append('{"k1":1, "k2":["a", "b"]}', '$.k2', 2)
+{"k1":1, "k2":["a", "b", 2]}
+select json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.key2[1]");
+json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.key2[1]")
+1
+select json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.key2[10]");
+json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.key2[10]")
+0
+select json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.ma");
+json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.ma")
+0
+select json_contains_path('{"key1":1, "key2":[2,3]}', "one", "$.key1");
+json_contains_path('{"key1":1, "key2":[2,3]}', "one", "$.key1")
+1
+select json_contains_path('{"key1":1, "key2":[2,3]}', "one", "$.key1", "$.ma");
+json_contains_path('{"key1":1, "key2":[2,3]}', "one", "$.key1", "$.ma")
+1
+select json_contains_path('{"key1":1, "key2":[2,3]}', "aLl", "$.key1", "$.ma");
+json_contains_path('{"key1":1, "key2":[2,3]}', "aLl", "$.key1", "$.ma")
+0
+select json_contains_path('{"key1":1, "key2":[2,3]}', "aLl", "$.key1", "$.key2");
+json_contains_path('{"key1":1, "key2":[2,3]}', "aLl", "$.key1", "$.key2")
+1
+select json_extract('{"key1":"asd", "key2":[2,3]}', "$.key1");
+json_extract('{"key1":"asd", "key2":[2,3]}', "$.key1")
+asd
+select json_extract('{"key1":"asd", "key2":[2,3]}', "$.keyX", "$.keyY");
+json_extract('{"key1":"asd", "key2":[2,3]}', "$.keyX", "$.keyY")
+NULL
+select json_extract('{"key1":"asd", "key2":[2,3]}', "$.key1", "$.key2");
+json_extract('{"key1":"asd", "key2":[2,3]}', "$.key1", "$.key2")
+["asd", [2,3]]
+select json_extract('{"key1":5, "key2":[2,3]}', "$.key1", "$.key2");
+json_extract('{"key1":5, "key2":[2,3]}', "$.key1", "$.key2")
+[5, [2,3]]
+select json_extract('{"key0":true, "key1":"qwe"}', "$.key1");
+json_extract('{"key0":true, "key1":"qwe"}', "$.key1")
+qwe
+select json_object("ki", 1, "mi", "ya");
+json_object("ki", 1, "mi", "ya")
+{"ki": 1, "mi": "ya"}
+select json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2");
+json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2")
+1
+select json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[1]");
+json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[1]")
+1
+select json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[10]");
+json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[10]")
+0
+select json_quote('"string"');
+json_quote('"string"')
+\"string\"
diff --git a/mysql-test/t/func_json.test b/mysql-test/t/func_json.test
new file mode 100644
index 0000000..a5b84e0c
--- /dev/null
+++ b/mysql-test/t/func_json.test
@@ -0,0 +1,44 @@
+select json_valid('[1, 2]');
+select json_valid('"string"}');
+select json_valid('{"key1":1, "key2":[2,3]}');
+select json_valid('[false, true, null]');
+
+select json_value('{"key1":123}', '$.key2');
+select json_value('{"key1":123}', '$.key1');
+select json_value('{"key1":[1,2,3]}', '$.key1');
+select json_value('{"key1": [1,2,3], "key1":123}', '$.key1');
+
+select json_query('{"key1":{"a":1, "b":[1,2]}}', '$.key2');
+select json_query('{"key1":{"a":1, "b":[1,2]}}', '$.key1');
+select json_query('{"key1": 1}', '$.key1');
+select json_query('{"key1":123, "key1": [1,2,3]}', '$.key1');
+
+select json_array(1);
+select json_array(1, "text", false, null);
+
+select json_array_append('["a", "b"]', '$', FALSE);
+select json_array_append('{"k1":1, "k2":["a", "b"]}', '$.k2', 2);
+
+select json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.key2[1]");
+select json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.key2[10]");
+select json_contains_path('{"key1":1, "key2":[2,3]}', "oNE", "$.ma");
+select json_contains_path('{"key1":1, "key2":[2,3]}', "one", "$.key1");
+select json_contains_path('{"key1":1, "key2":[2,3]}', "one", "$.key1", "$.ma");
+select json_contains_path('{"key1":1, "key2":[2,3]}', "aLl", "$.key1", "$.ma");
+select json_contains_path('{"key1":1, "key2":[2,3]}', "aLl", "$.key1", "$.key2");
+ 
+select json_extract('{"key1":"asd", "key2":[2,3]}', "$.key1");
+select json_extract('{"key1":"asd", "key2":[2,3]}', "$.keyX", "$.keyY");
+select json_extract('{"key1":"asd", "key2":[2,3]}', "$.key1", "$.key2");
+select json_extract('{"key1":5, "key2":[2,3]}', "$.key1", "$.key2");
+select json_extract('{"key0":true, "key1":"qwe"}', "$.key1");
+
+select json_object("ki", 1, "mi", "ya");
+
+select json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2");
+select json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[1]");
+select json_exists('{"key1":"xxxx", "key2":[1, 2, 3]}', "$.key2[10]");
+
+select json_quote('"string"');
+
+select json_merge('string', 123);
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index a18294e..2211990 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -136,7 +136,7 @@ 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
diff --git a/sql/item.h b/sql/item.h
index 5b82548..e434801 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -2899,6 +2899,20 @@ class Item_int :public Item_num
 };
 
 
+/*
+  We sometimes need to distinguish a number from a boolean:
+  a[1] and a[true] are different things in XPath.
+  Also in JSON boolean values should be treated differently.
+*/
+class Item_bool :public Item_int
+{
+public:
+  Item_bool(THD *thd, const char *str_arg, longlong i):
+    Item_int(thd, str_arg, i, 1) {}
+  bool is_bool_type() { return true; }
+};
+
+
 class Item_uint :public Item_int
 {
 public:
@@ -4652,6 +4666,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..c577db6 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -1708,6 +1708,149 @@ class Create_func_issimple : public Create_func_arg1
 #endif
 
 
+class Create_func_json_exists : public Create_func_arg2
+{
+public:
+  virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
+
+  static Create_func_json_exists s_singleton;
+
+protected:
+  Create_func_json_exists() {}
+  virtual ~Create_func_json_exists() {}
+};
+
+
+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_json_query : public Create_func_arg2
+{
+public:
+  virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
+
+  static Create_func_json_query s_singleton;
+
+protected:
+  Create_func_json_query() {}
+  virtual ~Create_func_json_query() {}
+};
+
+
+class Create_func_json_contains_path : public Create_native_func
+{
+public:
+  virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+  static Create_func_json_contains_path s_singleton;
+
+protected:
+  Create_func_json_contains_path() {}
+  virtual ~Create_func_json_contains_path() {}
+};
+
+
+class Create_func_json_extract : public Create_native_func
+{
+public:
+  virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+  static Create_func_json_extract s_singleton;
+
+protected:
+  Create_func_json_extract() {}
+  virtual ~Create_func_json_extract() {}
+};
+
+
+class Create_func_json_array : public Create_native_func
+{
+public:
+  virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+  static Create_func_json_array s_singleton;
+
+protected:
+  Create_func_json_array() {}
+  virtual ~Create_func_json_array() {}
+};
+
+
+class Create_func_json_array_append : public Create_native_func
+{
+public:
+  virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+  static Create_func_json_array_append s_singleton;
+
+protected:
+  Create_func_json_array_append() {}
+  virtual ~Create_func_json_array_append() {}
+};
+
+
+class Create_func_json_object : public Create_native_func
+{
+public:
+  virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+  static Create_func_json_object s_singleton;
+
+protected:
+  Create_func_json_object() {}
+  virtual ~Create_func_json_object() {}
+};
+
+
+class Create_func_json_merge : public Create_native_func
+{
+public:
+  virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+  static Create_func_json_merge s_singleton;
+
+protected:
+  Create_func_json_merge() {}
+  virtual ~Create_func_json_merge() {}
+};
+
+
+class Create_func_json_quote : public Create_func_arg1
+{
+public:
+  virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+  static Create_func_json_quote s_singleton;
+
+protected:
+  Create_func_json_quote() {}
+  virtual ~Create_func_json_quote() {}
+};
+
+
 class Create_func_last_day : public Create_func_arg1
 {
 public:
@@ -4572,6 +4715,51 @@ Create_func_issimple::create_1_arg(THD *thd, Item *arg1)
 #endif
 
 
+Create_func_json_exists Create_func_json_exists::s_singleton;
+
+Item*
+Create_func_json_exists::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+{
+  return new (thd->mem_root) Item_func_json_exists(thd, arg1, arg2);
+}
+
+
+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_json_query Create_func_json_query::s_singleton;
+
+Item*
+Create_func_json_query::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+{
+  return new (thd->mem_root) Item_func_json_query(thd, arg1, arg2);
+}
+
+
+Create_func_json_quote Create_func_json_quote::s_singleton;
+
+Item*
+Create_func_json_quote::create_1_arg(THD *thd, Item *arg1)
+{
+  return new (thd->mem_root) Item_func_json_quote(thd, arg1);
+}
+
+
 Create_func_last_day Create_func_last_day::s_singleton;
 
 Item*
@@ -4581,6 +4769,158 @@ Create_func_last_day::create_1_arg(THD *thd, Item *arg1)
 }
 
 
+Create_func_json_array Create_func_json_array::s_singleton;
+
+Item*
+Create_func_json_array::create_native(THD *thd, LEX_STRING name,
+                                      List<Item> *item_list)
+{
+  Item *func;
+
+  if (item_list != NULL)
+  {
+    func= new (thd->mem_root) Item_func_json_array(thd, *item_list);
+  }
+  else
+  {
+    func= new (thd->mem_root) Item_func_json_array(thd);
+  }
+
+  return func;
+}
+
+
+Create_func_json_array_append Create_func_json_array_append::s_singleton;
+
+Item*
+Create_func_json_array_append::create_native(THD *thd, LEX_STRING name,
+                                                 List<Item> *item_list)
+{
+  Item *func= NULL;
+  int arg_count= 0;
+
+  if (item_list != NULL)
+    arg_count= item_list->elements;
+
+  if (arg_count < 3 || (arg_count & 1) == 0 /*is even*/)
+  {
+    my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+  }
+  else
+  {
+    func= new (thd->mem_root) Item_func_json_array_append(thd, *item_list);
+  }
+
+  return func;
+}
+
+
+Create_func_json_object Create_func_json_object::s_singleton;
+
+Item*
+Create_func_json_object::create_native(THD *thd, LEX_STRING name,
+                                       List<Item> *item_list)
+{
+  Item *func;
+  int arg_count;
+
+  if (item_list != NULL)
+  {
+    arg_count= item_list->elements;
+    if ((arg_count & 1) != 0 /*is odd*/)
+    {
+      my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+      func= NULL;
+    }
+    else
+    {
+      func= new (thd->mem_root) Item_func_json_object(thd, *item_list);
+    }
+  }
+  else
+  {
+    arg_count= 0;
+    func= new (thd->mem_root) Item_func_json_object(thd);
+  }
+
+  return func;
+}
+
+
+Create_func_json_merge Create_func_json_merge::s_singleton;
+
+Item*
+Create_func_json_merge::create_native(THD *thd, LEX_STRING name,
+                                      List<Item> *item_list)
+{
+  Item *func;
+  int arg_count;
+
+  if (item_list == NULL ||
+      (arg_count= item_list->elements) == 0)
+  {
+    my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+    func= NULL;
+  }
+  else
+  {
+    func= new (thd->mem_root) Item_func_json_merge(thd, *item_list);
+  }
+
+  return func;
+}
+
+
+Create_func_json_contains_path Create_func_json_contains_path::s_singleton;
+
+Item*
+Create_func_json_contains_path::create_native(THD *thd, LEX_STRING name,
+                                                 List<Item> *item_list)
+{
+  Item *func= NULL;
+  int arg_count= 0;
+
+  if (item_list != NULL)
+    arg_count= item_list->elements;
+
+  if (arg_count < 3 /* json_doc, one_or_all, path, [path]...*/)
+  {
+    my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+  }
+  else
+  {
+    func= new (thd->mem_root) Item_func_json_contains_path(thd, *item_list);
+  }
+
+  return func;
+}
+
+
+Create_func_json_extract Create_func_json_extract::s_singleton;
+
+Item*
+Create_func_json_extract::create_native(THD *thd, LEX_STRING name,
+                                                 List<Item> *item_list)
+{
+  Item *func= NULL;
+  int arg_count= 0;
+
+  if (item_list != NULL)
+    arg_count= item_list->elements;
+
+  if (arg_count < 2 /* json_doc, path, [path]...*/)
+  {
+    my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+  }
+  else
+  {
+    func= new (thd->mem_root) Item_func_json_extract(thd, *item_list);
+  }
+
+  return func;
+}
+
+
 Create_func_last_insert_id Create_func_last_insert_id::s_singleton;
 
 Item*
@@ -5852,6 +6192,17 @@ 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_ARRAY") }, BUILDER(Create_func_json_array)},
+  { { C_STRING_WITH_LEN("JSON_ARRAY_APPEND") }, BUILDER(Create_func_json_array_append)},
+  { { C_STRING_WITH_LEN("JSON_CONTAINS_PATH") }, BUILDER(Create_func_json_contains_path)},
+  { { C_STRING_WITH_LEN("JSON_EXISTS") }, BUILDER(Create_func_json_exists)},
+  { { C_STRING_WITH_LEN("JSON_EXTRACT") }, BUILDER(Create_func_json_extract)},
+  { { C_STRING_WITH_LEN("JSON_MERGE") }, BUILDER(Create_func_json_merge)},
+  { { C_STRING_WITH_LEN("JSON_QUERY") }, BUILDER(Create_func_json_query)},
+  { { C_STRING_WITH_LEN("JSON_QUOTE") }, BUILDER(Create_func_json_quote)},
+  { { C_STRING_WITH_LEN("JSON_OBJECT") }, BUILDER(Create_func_json_object)},
+  { { 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..5649989
--- /dev/null
+++ b/sql/item_jsonfunc.cc
@@ -0,0 +1,898 @@
+/* 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 */
+
+
+#include <my_global.h>
+#include "sql_priv.h"
+#include "sql_class.h"
+#include "item.h"
+
+
+/*
+  Compare ASCII string against the string with the specified
+  character set.
+  Only compares the equality, case insencitive.
+*/
+static bool eq_ascii_string(const CHARSET_INFO *cs,
+                            const char *ascii,
+                            const char *s,  uint32 s_len)
+{
+  const char *s_end= s + s_len;
+
+  while (*ascii && s < s_end)
+  {
+    my_wc_t wc;
+    int wc_len;
+
+    wc_len= cs->cset->mb_wc(cs, &wc, (uchar *) s, (uchar *) s_end);
+    if (wc_len <= 0 || (wc | 0x20) != (my_wc_t) *ascii)
+      return 0;
+
+    ascii++;
+    s+= wc_len;
+  }
+
+  return *ascii == 0 && s >= s_end;
+}
+
+
+/*
+  Appends ASCII string to the String object taking it's charset in
+  consideration.
+*/
+static int st_append_ascii(String *s, const char *ascii, uint ascii_len)
+{
+  int str_len= ascii_len * s->charset()->mbmaxlen;
+
+  if (!s->reserve(str_len, 1024) &&
+      (str_len= json_append_ascii(s->charset(),
+                    (uchar *) s->end(), (uchar *) s->end() + str_len,
+                    (uchar *) ascii, (uchar *) (ascii + ascii_len))) > 0)
+  {
+    s->length(s->length() + str_len);
+    return 0;
+  }
+
+  return ascii_len;
+}
+
+
+/*
+  Appends JSON string to the String object taking charsets in
+  consideration.
+*/
+static int st_append_json(String *s,
+             CHARSET_INFO *json_cs, const uchar *js, uint js_len)
+{
+  int str_len= js_len * s->charset()->mbmaxlen;
+
+  if (!s->reserve(str_len, 1024) &&
+      (str_len= json_unescape(json_cs, js, js + js_len,
+         s->charset(), (uchar *) s->end(), (uchar *) s->end() + str_len)) > 0)
+  {
+    s->length(s->length() + str_len);
+    return 0;
+  }
+
+  return js_len;
+}
+
+
+/*
+  Appends arbitrary String to the JSON string taking charsets in
+  consideration.
+*/
+static int st_append_escaped(String *s, const String *a)
+{
+  /*
+    In the worst case one character from the 'a' string
+    turns into '\uXXXX\uXXXX' which is 12.
+  */
+  int str_len= a->length() * 12 * s->charset()->mbmaxlen /
+               a->charset()->mbminlen;
+  if (!s->reserve(str_len, 1024) &&
+      (str_len=
+         json_escape(a->charset(), (uchar *) a->ptr(), (uchar *)a->end(),
+                     s->charset(),
+                     (uchar *) s->end(), (uchar *)s->end() + str_len)) > 0)
+  {
+    s->length(s->length() + str_len);
+    return 0;
+  }
+
+  return a->length();
+}
+
+
+longlong Item_func_json_valid::val_int()
+{
+  String *js= args[0]->val_str(&tmp_value);
+  json_engine_t je;
+
+  if ((null_value= args[0]->null_value) || js == NULL)
+    return 0;
+
+  json_scan_start(&je, js->charset(), (const uchar *) js->ptr(),
+                  (const uchar *) js->ptr()+js->length());
+
+  while (json_scan_next(&je) == 0) {}
+
+  return je.s.error == 0;
+}
+
+
+void Item_func_json_exists::fix_length_and_dec()
+{
+  Item_int_func::fix_length_and_dec();
+  maybe_null= 1;
+  path.set_constant_flag(args[1]->const_item());
+}
+
+
+longlong Item_func_json_exists::val_int()
+{
+  json_engine_t je;
+  String *js= args[0]->val_str(&tmp_js);
+
+  if (!path.parsed)
+  {
+    String *s_p= args[1]->val_str(&tmp_path);
+    if (s_p &&
+        json_path_setup(&path.p, s_p->charset(), (const uchar *) s_p->ptr(),
+                        (const uchar *) s_p->ptr() + s_p->length()))
+      goto err_return;
+    path.parsed= path.constant;
+  }
+
+  if ((null_value= args[0]->null_value || args[1]->null_value))
+  {
+    null_value= 1;
+    return 0;
+  }
+
+  null_value= 0;
+  json_scan_start(&je, js->charset(),(const uchar *) js->ptr(),
+                  (const uchar *) js->ptr() + js->length());
+
+  path.cur_step= path.p.steps;
+  if (json_find_value(&je, &path.p, &path.cur_step))
+  {
+    if (je.s.error)
+      goto err_return;
+    return 0;
+  }
+
+  return 1;
+
+err_return:
+  null_value= 1;
+  return 0;
+}
+
+
+void Item_func_json_value::fix_length_and_dec()
+{
+  collation.set(args[0]->collation);
+  max_length= args[0]->max_length;
+  path.set_constant_flag(args[1]->const_item());
+}
+
+
+String *Item_func_json_value::val_str(String *str)
+{
+  json_engine_t je;
+  String *js= args[0]->val_str(&tmp_js);
+  int error= 0;
+
+  if (!path.parsed)
+  {
+    String *s_p= args[1]->val_str(&tmp_path);
+    if (s_p &&
+        json_path_setup(&path.p, s_p->charset(), (const uchar *) s_p->ptr(),
+                        (const uchar *) s_p->ptr() + s_p->length()))
+      goto err_return;
+    path.parsed= path.constant;
+  }
+
+  if ((null_value= args[0]->null_value || args[1]->null_value))
+    return NULL;
+
+  json_scan_start(&je, js->charset(),(const uchar *) js->ptr(),
+                  (const uchar *) js->ptr() + js->length());
+
+  path.cur_step= path.p.steps;
+continue_search:
+  if (json_find_value(&je, &path.p, &path.cur_step))
+  {
+    if (je.s.error)
+      goto err_return;
+
+    null_value= 1;
+    return 0;
+  }
+
+  if (json_read_value(&je))
+    goto err_return;
+
+  if (check_and_get_value(&je, str, &error))
+  {
+    if (error)
+      goto err_return;
+    goto continue_search;
+  }
+
+  return str;
+
+err_return:
+  null_value= 1;
+  return 0;
+}
+
+
+bool Item_func_json_value::check_and_get_value(json_engine_t *je, String *res,
+                                               int *error)
+{
+  if (!json_value_scalar(je))
+  {
+    /* We only look for scalar values! */
+    if (json_skip_level(je) || json_scan_next(je))
+      *error= 1;
+    return true;
+  }
+
+  res->set((const char *) je->value, je->value_len, je->s.cs);
+  return false;
+}
+
+
+bool Item_func_json_query::check_and_get_value(json_engine_t *je, String *res,
+                                               int *error)
+{
+  const uchar *value;
+  if (json_value_scalar(je))
+  {
+    /* We skip scalar values. */
+    if (json_scan_next(je))
+      *error= 1;
+    return true;
+  }
+
+  value= je->value;
+  if (json_skip_level(je))
+  {
+    *error= 1;
+    return true;
+  }
+
+  res->set((const char *) je->value, je->s.c_str - value, je->s.cs);
+  return false;
+}
+
+
+void Item_func_json_quote::fix_length_and_dec()
+{
+  collation.set(args[0]->collation);
+  /*
+    Odd but realistic worst case is when all characters
+    of the argument turn into '\uXXXX\uXXXX', which is 12.
+  */
+  max_length= args[0]->max_length * 12;
+}
+
+
+String *Item_func_json_quote::val_str(String *str)
+{
+  String *s= args[0]->val_str(&tmp_s);
+
+  if ((null_value= args[0]->null_value))
+    return NULL;
+
+  str->length(0);
+  str->set_charset(s->charset());
+
+  if (st_append_escaped(str, s))
+  {
+    /* Report an error. */
+    null_value= 1;
+    return 0;
+  }
+
+  return str;
+}
+
+
+static int alloc_tmp_paths(THD *thd, uint n_paths,
+                           json_path_with_flags **paths,String **tmp_paths)
+{
+  *paths= (json_path_with_flags *) alloc_root(thd->mem_root,
+                                     sizeof(json_path_with_flags) * n_paths);
+  *tmp_paths= (String *) alloc_root(thd->mem_root, sizeof(String) * n_paths);
+  if (*paths == 0 || *tmp_paths == 0)
+    return 1;
+
+  bzero(*tmp_paths, sizeof(String) * n_paths);
+  
+  return 0;
+}
+
+
+static void mark_constant_paths(json_path_with_flags *p,
+                                Item** args, uint n_args)
+{
+  uint n;
+  for (n= 0; n < n_args; n++)
+    p[n].set_constant_flag(args[n]->const_item());
+}
+
+
+bool Item_json_str_multipath::fix_fields(THD *thd, Item **ref)
+{
+  return alloc_tmp_paths(thd, get_n_paths(), &paths, &tmp_paths) ||
+         Item_str_func::fix_fields(thd, ref);
+}
+
+
+void Item_json_str_multipath::cleanup()
+{
+  if (tmp_paths)
+  {
+    for (uint i= get_n_paths(); i>0; i--)
+      tmp_paths[i-1].free();
+    tmp_paths= 0;
+  }
+  Item_str_func::cleanup();
+}
+
+
+void Item_func_json_extract::fix_length_and_dec()
+{
+  collation.set(args[0]->collation);
+  max_length= args[0]->max_length * (arg_count - 1);
+
+  mark_constant_paths(paths, args+1, arg_count-1);
+}
+
+
+String *Item_func_json_extract::val_str(String *str)
+{
+  String *js= args[0]->val_str(&tmp_js);
+  json_engine_t je;
+  bool multiple_values_found= FALSE;
+  const uchar *value;
+  const char *first_value= NULL, *first_p_value;
+  uint n_arg, v_len, first_len, first_p_len;
+
+  if ((null_value= args[0]->null_value))
+    return 0;
+
+  str->set_charset(js->charset());
+  str->length(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;
+    }
+
+    json_scan_start(&je, js->charset(),(const uchar *) js->ptr(),
+                    (const uchar *) js->ptr() + js->length());
+
+    c_path->cur_step= c_path->p.steps;
+
+    if (json_find_value(&je, &c_path->p, &c_path->cur_step))
+    {
+      /* Path wasn't found. */
+      if (je.s.error)
+        goto error;
+
+      continue;
+    }
+
+    if (json_read_value(&je))
+      goto error;
+
+    value= je.value_begin;
+    if (json_value_scalar(&je))
+      v_len= je.value_end - value;
+    else
+    {
+      if (json_skip_level(&je))
+        goto error;
+      v_len= je.s.c_str - value;
+    }
+
+    if (!multiple_values_found)
+    {
+      if (first_value == NULL)
+      {
+        /*
+          Just remember the first value as we don't know yet
+          if we need to create an array out of it or not.
+        */
+        first_value= (const char *) value;
+        first_len= v_len;
+        /*
+         We need this as we have to preserve quotes around string
+          constants if we use the value to create an array. Otherwise
+          we get the value without the quotes.
+        */
+        first_p_value= (const char *) je.value;
+        first_p_len= je.value_len;
+        continue;
+      }
+      else
+      {
+        multiple_values_found= TRUE; /* We have to make an JSON array. */
+        if (st_append_ascii(str, "[", 1) ||
+            str->append(first_value, first_len))
+          goto error; /* Out of memory. */
+      }
+
+    }
+    if (str->append(", ", 2) ||
+        str->append((const char *) value, v_len))
+      goto error; /* Out of memory. */
+  }
+
+  if (first_value == NULL)
+  {
+    /* Nothing was found. */
+    null_value= 1;
+    return 0;
+  }
+
+  if (multiple_values_found ?
+        str->append("]") :
+        str->append(first_p_value, first_p_len))
+    goto error; /* Out of memory. */
+
+  return str;
+
+error:
+  /* TODO: launch error messages. */
+  null_value= 1;
+  return 0;
+}
+
+
+longlong Item_func_json_extract::val_int()
+{
+  String *js= args[0]->val_str(&tmp_js);
+  json_engine_t je;
+  uint n_arg;
+
+  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;
+    }
+
+    json_scan_start(&je, js->charset(),(const uchar *) js->ptr(),
+                    (const uchar *) js->ptr() + js->length());
+
+    c_path->cur_step= c_path->p.steps;
+
+    if (json_find_value(&je, &c_path->p, &c_path->cur_step))
+    {
+      /* Path wasn't found. */
+      if (je.s.error)
+        goto error;
+
+      continue;
+    }
+
+    if (json_read_value(&je))
+      goto error;
+
+    if (json_value_scalar(&je))
+    {
+      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;
+  }
+
+  /* Nothing was found. */
+  null_value= 1;
+  return 0;
+
+error:
+  /* TODO: launch error messages. */
+  null_value= 1;
+  return 0;
+}
+
+
+bool Item_func_json_contains_path::fix_fields(THD *thd, Item **ref)
+{
+  return alloc_tmp_paths(thd, arg_count-2, &paths, &tmp_paths) ||
+         Item_int_func::fix_fields(thd, ref);
+}
+
+
+void Item_func_json_contains_path::fix_length_and_dec()
+{
+  ooa_constant= args[1]->const_item();
+  ooa_parsed= FALSE;
+  mark_constant_paths(paths, args+2, arg_count-2);
+  Item_int_func::fix_length_and_dec();
+}
+
+
+void Item_func_json_contains_path::cleanup()
+{
+  if (tmp_paths)
+  {
+    for (uint i= arg_count-2; i>0; i--)
+      tmp_paths[i-1].free();
+    tmp_paths= 0;
+  }
+  Item_int_func::cleanup();
+}
+
+
+longlong Item_func_json_contains_path::val_int()
+{
+  String *js= args[0]->val_str(&tmp_js);
+  json_engine_t je;
+  uint n_arg;
+  longlong result;
+
+  if ((null_value= args[0]->null_value))
+    return 0;
+
+  if (!ooa_parsed)
+  {
+    char buff[20];
+    String *res, tmp(buff, sizeof(buff), &my_charset_bin);
+    res= args[1]->val_str(&tmp);
+    mode_one=eq_ascii_string(res->charset(), "one",
+                             res->ptr(), res->length());
+    if (!mode_one)
+    {
+      if (!eq_ascii_string(res->charset(), "all", res->ptr(), res->length()))
+        goto error;
+    }
+    ooa_parsed= ooa_constant;
+  }
+
+  result= !mode_one;
+  for (n_arg=2; n_arg < arg_count; n_arg++)
+  {
+    json_path_with_flags *c_path= paths + n_arg - 2;
+    if (!c_path->parsed)
+    {
+      String *s_p= args[n_arg]->val_str(tmp_paths+(n_arg-2));
+      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= TRUE;
+    }
+
+    json_scan_start(&je, js->charset(),(const uchar *) js->ptr(),
+                    (const uchar *) js->ptr() + js->length());
+
+    c_path->cur_step= c_path->p.steps;
+    if (json_find_value(&je, &c_path->p, &c_path->cur_step))
+    {
+      /* Path wasn't found. */
+      if (je.s.error)
+        goto error;
+
+      if (!mode_one)
+      {
+        result= 0;
+        break;
+      }
+    }
+    else if (mode_one)
+    {
+      result= 1;
+      break;
+    }
+  }
+
+
+  return result;
+
+error:
+  null_value= 1;
+  return 0;
+}
+
+
+static int append_json_value(String *str, Item *item, String *tmp_val)
+{
+  if (item->is_bool_type())
+  {
+    longlong v_int= item->val_int();
+    const char *t_f;
+    int t_f_len;
+
+    if (item->null_value)
+      goto append_null;
+
+    if (v_int)
+    {
+      t_f= "true";
+      t_f_len= 4;
+    }
+    else
+    {
+      t_f= "false";
+      t_f_len= 5;
+    }
+
+    return st_append_ascii(str, t_f, t_f_len);
+  }
+  {
+    String *sv= item->val_str(tmp_val);
+    if (item->null_value)
+      goto append_null;
+    if (item->result_type() == STRING_RESULT)
+    {
+      return st_append_ascii(str, "\"", 1) ||
+             st_append_escaped(str, sv) ||
+             st_append_ascii(str, "\"", 1);
+    }
+    return st_append_escaped(str, sv);
+  }
+
+append_null:
+  return st_append_ascii(str, "null", 4);
+}
+
+
+static int append_json_keyname(String *str, Item *item, String *tmp_val)
+{
+  String *sv= item->val_str(tmp_val);
+  if (item->null_value)
+    goto append_null;
+
+  return st_append_ascii(str, "\"", 1) ||
+         st_append_escaped(str, sv) ||
+         st_append_ascii(str, "\": ", 3);
+
+append_null:
+  return st_append_ascii(str, "\"\": ", 4);
+}
+
+
+void Item_func_json_array::fix_length_and_dec()
+{
+  ulonglong char_length= 4;
+  uint n_arg;
+
+  if (agg_arg_charsets_for_string_result(collation, args, arg_count))
+    return;
+
+  for (n_arg=0 ; n_arg < arg_count ; n_arg++)
+    char_length+= args[n_arg]->max_char_length() + 2;
+
+  fix_char_length_ulonglong(char_length);
+  tmp_val.set_charset(collation.collation);
+}
+
+
+String *Item_func_json_array::val_str(String *str)
+{
+  DBUG_ASSERT(fixed == 1);
+  uint n_arg;
+
+  str->length(0);
+
+  if (str->append("[", 1) ||
+      ((arg_count > 0) && append_json_value(str, args[0],&tmp_val)))
+    goto err_return;
+
+  for (n_arg=1; n_arg < arg_count; n_arg++)
+  {
+    if (str->append(", ", 2) ||
+        append_json_value(str, args[n_arg], &tmp_val))
+      goto err_return;
+  }
+
+  if (str->append("]", 1))
+    goto err_return;
+
+  return str;
+
+err_return:
+  /*TODO: Launch out of memory error. */
+  null_value= 1;
+  return NULL;
+}
+
+
+void Item_func_json_array_append::fix_length_and_dec()
+{
+  uint n_arg;
+  ulonglong char_length;
+
+  collation.set(args[0]->collation);
+  char_length= args[0]->max_char_length();
+
+  for (n_arg= 1; n_arg < arg_count; n_arg+= 2)
+  {
+    paths[n_arg-1].set_constant_flag(args[n_arg]->const_item());
+    char_length+= args[n_arg+1]->max_char_length() + 4;
+  }
+
+  fix_char_length_ulonglong(char_length);
+}
+
+
+String *Item_func_json_array_append::val_str(String *str)
+{
+  json_engine_t je;
+  String *js= args[0]->val_str(&tmp_js);
+  uint n_arg, n_path, str_rest_len;
+  const uchar *ar_end;
+
+  DBUG_ASSERT(fixed == 1);
+
+  if ((null_value= args[0]->null_value))
+    return 0;
+
+  for (n_arg=1, n_path=0; n_arg < arg_count; n_arg+=2, n_path++)
+  {
+    json_path_with_flags *c_path= paths + n_path;
+    if (!c_path->parsed)
+    {
+      String *s_p= args[n_arg]->val_str(tmp_paths+n_path);
+      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)
+    {
+      null_value= 1;
+      return 0;
+    }
+
+    json_scan_start(&je, js->charset(),(const uchar *) js->ptr(),
+                    (const uchar *) js->ptr() + js->length());
+    c_path->cur_step= c_path->p.steps;
+
+    if (json_find_value(&je, &c_path->p, &c_path->cur_step))
+    {
+      if (je.s.error)
+        goto error;
+      null_value= 1;
+      return 0;
+    }
+
+    if (json_read_value(&je))
+      goto error;
+
+    if (je.value_type != JSON_VALUE_ARRAY)
+    {
+      /* Must be an array. */
+      goto error;
+    }
+
+    if (json_skip_level(&je))
+      goto error;
+
+    str->length(0);
+    str->set_charset(js->charset());
+    if (str->reserve(js->length() + 8, 1024))
+      goto error; /* Out of memory. */
+    ar_end= je.s.c_str - je.sav_c_len;
+    str_rest_len= js->length() - (ar_end - (const uchar *) js->ptr());
+    str->q_append(js->ptr(), ar_end-(const uchar *) js->ptr());
+    st_append_ascii(str, ", ", 2);
+    if (append_json_value(str, args[n_arg+1], &tmp_val))
+      goto error; /* Out of memory. */
+
+    if (str->reserve(str_rest_len, 1024))
+      goto error; /* Out of memory. */
+    str->q_append((const char *) ar_end, str_rest_len);
+  }
+
+  return str;
+
+error:
+  null_value= 1;
+  return 0;
+}
+
+
+String *Item_func_json_object::val_str(String *str)
+{
+  DBUG_ASSERT(fixed == 1);
+  uint n_arg;
+
+  str->length(0);
+
+  if (st_append_ascii(str, "{", 1) ||
+      (arg_count > 0 &&
+       (append_json_keyname(str, args[0], &tmp_val) ||
+        append_json_value(str, args[1], &tmp_val))))
+    goto err_return;
+
+  for (n_arg=2; n_arg < arg_count; n_arg+=2)
+  {
+    if (st_append_ascii(str, ", ", 2) ||
+        append_json_keyname(str, args[n_arg], &tmp_val) ||
+        append_json_value(str, args[n_arg+1], &tmp_val))
+      goto err_return;
+  }
+
+  if (st_append_ascii(str, "}", 1))
+    goto err_return;
+
+  return str;
+
+err_return:
+  /*TODO: Launch out of memory error. */
+  null_value= 1;
+  return NULL;
+}
+
+
+String *Item_func_json_merge::val_str(String *str)
+{
+  DBUG_ASSERT(fixed == 1);
+  uint n_arg;
+
+  str->length(0);
+
+  if (str->append("[", 1) ||
+      ((arg_count > 0) && append_json_value(str, args[0], &tmp_val)))
+    goto err_return;
+
+  for (n_arg=1; n_arg < arg_count; n_arg++)
+  {
+    if (str->append(", ", 2) ||
+        append_json_value(str, args[n_arg], &tmp_val))
+      goto err_return;
+  }
+
+  if (str->append("]", 1))
+    goto err_return;
+
+  return str;
+
+err_return:
+  /*TODO: Launch out of memory error. */
+  null_value= 1;
+  return NULL;
+}
+
+
diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h
new file mode 100644
index 0000000..aff19ee
--- /dev/null
+++ b/sql/item_jsonfunc.h
@@ -0,0 +1,240 @@
+#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 JSON functions */
+
+
+#include <json_lib.h>
+#include "item_cmpfunc.h"      // Item_bool_func
+#include "item_strfunc.h"      // Item_str_func
+
+
+class json_path_with_flags
+{
+public:
+  json_path_t p;
+  bool constant;
+  bool parsed;
+  json_path_step_t *cur_step;
+  void set_constant_flag(bool s_constant)
+  {
+    constant= s_constant;
+    parsed= FALSE;
+  }
+};
+
+
+class Item_func_json_valid: public Item_int_func
+{
+protected:
+  String tmp_value;
+
+public:
+  Item_func_json_valid(THD *thd, Item *json) : Item_int_func(thd, json) {}
+  longlong val_int();
+  const char *func_name() const { return "json_valid"; }
+  void fix_length_and_dec()
+  {
+    Item_int_func::fix_length_and_dec();
+    maybe_null= 1;
+  }
+  bool is_bool_type() { return true; }
+  Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+  { return get_item_copy<Item_func_json_valid>(thd, mem_root, this); }
+};
+
+
+class Item_func_json_exists: public Item_int_func
+{
+protected:
+  json_path_with_flags path;
+  String tmp_js, tmp_path;
+
+public:
+  Item_func_json_exists(THD *thd, Item *js, Item *path):
+    Item_int_func(thd, js, path) {}
+  const char *func_name() const { return "json_exists"; }
+  bool is_bool_type() { return true; }
+  void fix_length_and_dec();
+  Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+  { return get_item_copy<Item_func_json_exists>(thd, mem_root, this); }
+  longlong val_int();
+};
+
+
+class Item_func_json_value: public Item_str_func
+{
+protected:
+  json_path_with_flags path;
+  String tmp_js, tmp_path;
+
+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 *);
+  virtual bool check_and_get_value(json_engine_t *je, String *res, int *error);
+  Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+  { return get_item_copy<Item_func_json_value>(thd, mem_root, this); }
+};
+
+
+class Item_func_json_query: public Item_func_json_value
+{
+public:
+  Item_func_json_query(THD *thd, Item *js, Item *path):
+    Item_func_json_value(thd, js, path) {}
+  const char *func_name() const { return "json_query"; }
+  bool check_and_get_value(json_engine_t *je, String *res, int *error);
+  Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+  { return get_item_copy<Item_func_json_query>(thd, mem_root, this); }
+};
+
+
+class Item_func_json_quote: public Item_str_func
+{
+protected:
+  String tmp_s;
+
+public:
+  Item_func_json_quote(THD *thd, Item *s): Item_str_func(thd, s) {}
+  const char *func_name() const { return "json_quote"; }
+  void fix_length_and_dec();
+  String *val_str(String *);
+  Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+  { return get_item_copy<Item_func_json_quote>(thd, mem_root, this); }
+};
+
+
+class Item_json_str_multipath: public Item_str_func
+{
+protected:
+  json_path_with_flags *paths;
+  String *tmp_paths;
+public:
+  Item_json_str_multipath(THD *thd, List<Item> &list):
+    Item_str_func(thd, list), tmp_paths(0) {}
+  bool fix_fields(THD *thd, Item **ref);
+  void cleanup();
+  virtual uint get_n_paths() const = 0;
+};
+
+
+class Item_func_json_extract: public Item_json_str_multipath
+{
+protected:
+  String tmp_js;
+public:
+  Item_func_json_extract(THD *thd, List<Item> &list):
+    Item_json_str_multipath(thd, list) {}
+  const char *func_name() const { return "json_extract"; }
+  void fix_length_and_dec();
+  String *val_str(String *);
+  longlong val_int();
+  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); }
+};
+
+
+class Item_func_json_contains_path: public Item_int_func
+{
+protected:
+  String tmp_js;
+  json_path_with_flags *paths;
+  String *tmp_paths;
+  bool mode_one;
+  bool ooa_constant, ooa_parsed;
+
+public:
+  Item_func_json_contains_path(THD *thd, List<Item> &list):
+    Item_int_func(thd, list), tmp_paths(0) {}
+  const char *func_name() const { return "json_contains_path"; }
+  bool fix_fields(THD *thd, Item **ref);
+  void fix_length_and_dec();
+  void cleanup();
+  longlong val_int();
+  Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+  { return get_item_copy<Item_func_json_contains_path>(thd, mem_root, this); }
+};
+
+
+class Item_func_json_array: public Item_str_func
+{
+protected:
+  String tmp_val;
+public:
+  Item_func_json_array(THD *thd):
+    Item_str_func(thd) {}
+  Item_func_json_array(THD *thd, List<Item> &list):
+    Item_str_func(thd, list) {}
+  String *val_str(String *);
+  void fix_length_and_dec();
+  const char *func_name() const { return "json_array"; }
+  Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+  { return get_item_copy<Item_func_json_array>(thd, mem_root, this); }
+};
+
+
+class Item_func_json_array_append: public Item_json_str_multipath
+{
+protected:
+  String tmp_js;
+  String tmp_val;
+public:
+  Item_func_json_array_append(THD *thd, List<Item> &list):
+    Item_json_str_multipath(thd, list) {}
+  void fix_length_and_dec();
+  String *val_str(String *);
+  uint get_n_paths() const { return arg_count/2; }
+  const char *func_name() const { return "json_array_append"; }
+  Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+  { return get_item_copy<Item_func_json_array_append>(thd, mem_root, this); }
+};
+
+
+class Item_func_json_object: public Item_func_json_array
+{
+public:
+  Item_func_json_object(THD *thd):
+    Item_func_json_array(thd) {}
+  Item_func_json_object(THD *thd, List<Item> &list):
+    Item_func_json_array(thd, list) {}
+  String *val_str(String *);
+  const char *func_name() const { return "json_object"; }
+  Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+  { return get_item_copy<Item_func_json_object>(thd, mem_root, this); }
+};
+
+
+class Item_func_json_merge: public Item_func_json_array
+{
+protected:
+  String tmp_val;
+public:
+  Item_func_json_merge(THD *thd, List<Item> &list):
+    Item_func_json_array(thd, list) {}
+  String *val_str(String *);
+  const char *func_name() const { return "json_merge"; }
+  Item *get_copy(THD *thd, MEM_ROOT *mem_root)
+  { return get_item_copy<Item_func_json_merge>(thd, mem_root, this); }
+};
+
+#endif /* ITEM_JSONFUNC_INCLUDED */
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
index cbbdeea..4d848a0 100644
--- a/sql/item_xmlfunc.cc
+++ b/sql/item_xmlfunc.cc
@@ -13,10 +13,6 @@
    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"
 /*
@@ -407,19 +403,6 @@ class Item_nodeset_func_elementbyindex :public Item_nodeset_func
 
 
 /*
-  We need to distinguish a number from a boolean:
-  a[1] and a[true] are different things in XPath.
-*/
-class Item_bool :public Item_int
-{
-public:
-  Item_bool(THD *thd, int32 i): Item_int(thd, i) {}
-  const char *func_name() const { return "xpath_bool"; }
-  bool is_bool_type() { return true; }
-};
-
-
-/*
   Converts its argument into a boolean value.
   * a number is true if it is non-zero
   * a node-set is true if and only if it is non-empty
@@ -1214,13 +1197,13 @@ my_xpath_keyword(MY_XPATH *x,
 
 static Item *create_func_true(MY_XPATH *xpath, Item **args, uint nargs)
 {
-  return new (xpath->thd->mem_root) Item_bool(xpath->thd, 1);
+  return new (xpath->thd->mem_root) Item_bool(xpath->thd, "xpath_bool", 1);
 }
 
 
 static Item *create_func_false(MY_XPATH *xpath, Item **args, uint nargs)
 {
-  return new (xpath->thd->mem_root) Item_bool(xpath->thd, 0);
+  return new (xpath->thd->mem_root) Item_bool(xpath->thd, "xpath_bool", 0);
 }
 
 
diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h
index 3c58955..3c071b8 100644
--- a/sql/item_xmlfunc.h
+++ b/sql/item_xmlfunc.h
@@ -21,11 +21,6 @@
 /* This file defines all XML functions */
 
 
-#ifdef USE_PRAGMA_INTERFACE
-#pragma interface			/* gcc class implementation */
-#endif
-
-
 typedef struct my_xml_node_st MY_XML_NODE;
 
 
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 551a86e..763b8fe 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -13753,13 +13753,13 @@ literal:
           }
         | FALSE_SYM
           {
-            $$= new (thd->mem_root) Item_int(thd, (char*) "FALSE",0,1);
+            $$= new (thd->mem_root) Item_bool(thd, (char*) "FALSE",0);
             if ($$ == NULL)
               MYSQL_YYABORT;
           }
         | TRUE_SYM
           {
-            $$= new (thd->mem_root) Item_int(thd, (char*) "TRUE",1,1);
+            $$= new (thd->mem_root) Item_bool(thd, (char*) "TRUE",1);
             if ($$ == NULL)
               MYSQL_YYABORT;
           }
diff --git a/strings/CMakeLists.txt b/strings/CMakeLists.txt
index 1e364bc..96de24b 100644
--- a/strings/CMakeLists.txt
+++ b/strings/CMakeLists.txt
@@ -23,7 +23,7 @@ SET(STRINGS_SOURCES bchange.c bmove_upp.c ctype-big5.c ctype-bin.c ctype-cp932.c
                 str2int.c str_alloc.c strcend.c strend.c strfill.c strmake.c strmov.c strnmov.c 
                 strxmov.c strxnmov.c xml.c
                 strmov_overlapp.c
-		my_strchr.c strcont.c strappend.c)
+		my_strchr.c strcont.c strappend.c json_lib.c)
 
 IF(NOT HAVE_STRNLEN)
   # OSX below 10.7 did not have strnlen
diff --git a/strings/ctype-ucs2.c b/strings/ctype-ucs2.c
index 9d83531..81487d9 100644
--- a/strings/ctype-ucs2.c
+++ b/strings/ctype-ucs2.c
@@ -1190,10 +1190,13 @@ my_lengthsp_mb2(CHARSET_INFO *cs __attribute__((unused)),
 #endif /* HAVE_CHARSET_mb2*/
 
 
+/*
+  Next part is actually HAVE_CHARSET_utf16-specific,
+  but the JSON functions needed my_utf16_uni()
+  so the #ifdef was moved lower.
+*/
 
 
-#ifdef HAVE_CHARSET_utf16
-
 /*
   D800..DB7F - Non-provate surrogate high (896 pages)
   DB80..DBFF - Private surrogate high     (128 pages)
@@ -1260,7 +1263,12 @@ static inline int my_weight_mb2_utf16mb2_general_ci(uchar b0, uchar b1)
 #undef IS_MB2_CHAR
 #undef IS_MB4_CHAR
 
-static int
+/*
+  These two functions are used in JSON library, so made exportable
+  and unconditionally compiled into the library.
+*/
+
+/*static*/ int
 my_utf16_uni(CHARSET_INFO *cs __attribute__((unused)),
              my_wc_t *pwc, const uchar *s, const uchar *e)
 {
@@ -1293,7 +1301,7 @@ my_utf16_uni(CHARSET_INFO *cs __attribute__((unused)),
 }
 
 
-static int
+/*static*/ int
 my_uni_utf16(CHARSET_INFO *cs __attribute__((unused)),
              my_wc_t wc, uchar *s, uchar *e)
 {
@@ -1323,6 +1331,9 @@ my_uni_utf16(CHARSET_INFO *cs __attribute__((unused)),
 }
 
 
+#ifdef HAVE_CHARSET_utf16
+
+
 static inline void
 my_tolower_utf16(MY_UNICASE_INFO *uni_plane, my_wc_t *wc)
 {
diff --git a/strings/json_lib.c b/strings/json_lib.c
new file mode 100644
index 0000000..43bbdf8
--- /dev/null
+++ b/strings/json_lib.c
@@ -0,0 +1,1426 @@
+#include <my_global.h>
+#include <m_ctype.h>
+
+
+#include "json_lib.h"
+
+/*
+  JSON escaping lets user specify UTF16 codes of characters.
+  So we're going to need the UTF16 charset capabilities. Let's import
+  them from the utf16 charset.
+*/
+int my_utf16_uni(CHARSET_INFO *cs,
+                 my_wc_t *pwc, const uchar *s, const uchar *e);
+int my_uni_utf16(CHARSET_INFO *cs, my_wc_t wc, uchar *s, uchar *e);
+
+
+void json_string_set_str(json_string_t *s,
+                         const uchar *str, const uchar *end)
+{
+  s->c_str= str;
+  s->str_end= end;
+}
+
+
+void json_string_set_cs(json_string_t *s, CHARSET_INFO *i_cs)
+{
+  s->cs= i_cs;
+  s->error= 0;
+  s->wc= i_cs->cset->mb_wc;
+}
+
+
+static void json_string_setup(json_string_t *s,
+                              CHARSET_INFO *i_cs, const uchar *str,
+                              const uchar *end)
+{
+  json_string_set_cs(s, i_cs);
+  json_string_set_str(s, str, end);
+}
+
+
+enum json_char_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' (for "false") */
+  C_LOW_N,  /* 'n' (for "null") */
+  C_LOW_T,  /* 't' (for "true") */
+  C_ETC,    /* everything else */
+  C_ERR,    /* character disallowed in JSON */
+  C_BAD,    /* invalid character, charset handler cannot read it */
+  NR_C_CLASSES, /* Counter for classes that handled with functions. */
+  C_SPACE   /* space. Doesn't need specific handlers, so after the counter.*/
+};
+
+
+/*
+  This array maps first 128 Unicode Code Points into classes.
+  The remaining Unicode characters should be mapped to C_ETC.
+*/
+
+static enum json_char_classes 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
+};
+
+
+/*
+  JSON parser actually has more states than the 'enum json_states'
+  declares. But the rest of the states aren't seen to the user so let's
+  specify them here to avoid confusion.
+*/
+
+enum json_all_states {
+  JST_DONE= NR_JSON_USER_STATES,         /* ok to finish     */
+  JST_OBJ_CONT= NR_JSON_USER_STATES+1,   /* object continues */
+  JST_ARRAY_CONT= NR_JSON_USER_STATES+2, /* array continues  */
+  JST_READ_VALUE= NR_JSON_USER_STATES+3, /* value is being read */
+  NR_JSON_STATES= NR_JSON_USER_STATES+4
+};
+
+
+typedef int (*json_state_handler)(json_engine_t *);
+
+
+/* The string is broken. */
+static int unexpected_eos(json_engine_t *j)
+{
+  j->s.error= JE_EOS;
+  return 1;
+}
+
+
+/* This symbol here breaks the JSON syntax. */
+static int syntax_error(json_engine_t *j)
+{
+  j->s.error= JE_SYN;
+  return 1;
+}
+
+
+/* Value of object. */
+static int mark_object(json_engine_t *j)
+{
+  j->state= JST_OBJ_START;
+  *(++j->stack_p)= JST_OBJ_CONT;
+  return 0;
+}
+
+
+/* Read value of object. */
+static int read_obj(json_engine_t *j)
+{
+  j->state= JST_OBJ_START;
+  j->value_type= JSON_VALUE_OBJECT;
+  j->value= j->value_begin;
+  *(++j->stack_p)= JST_OBJ_CONT;
+  return 0;
+}
+
+
+/* Value of array. */
+static int mark_array(json_engine_t *j)
+{
+  j->state= JST_ARRAY_START;
+  *(++j->stack_p)= JST_ARRAY_CONT;
+  j->value= j->value_begin;
+  return 0;
+}
+
+/* Read value of object. */
+static int read_array(json_engine_t *j)
+{
+  j->state= JST_ARRAY_START;
+  j->value_type= JSON_VALUE_ARRAY;
+  j->value= j->value_begin;
+  *(++j->stack_p)= JST_ARRAY_CONT;
+  return 0;
+}
+
+
+
+/*
+  Character classes inside the JSON string constant.
+  We mostly need this to parse escaping properly.
+  Escapings availabe in JSON are:
+  \" - quotation mark
+  \\ - backslash
+  \b - backspace UNICODE 8
+  \f - formfeed UNICODE 12
+  \n - newline UNICODE 10
+  \r - carriage return UNICODE 13
+  \t - horizontal tab UNICODE 9
+  \u{four-hex-digits} - code in UCS16 character set
+*/
+enum json_string_char_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_BKSL= 38, /* \ */
+  S_ERR= 100,   /* disallowed */
+};
+
+
+/* This maps characters to their types inside a string constant. */
+static enum json_string_char_classes 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_BKSL,  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_4_hexdigits(json_string_t *s, uchar *dest)
+{
+  int i, t, c_len;
+  for (i=0; i<4; i++)
+  {
+    if ((c_len= json_next_char(s)) <= 0)
+      return s->error= json_eos(s) ? JE_EOS : JE_BAD_CHR;
+
+    if (s->c_next >= 128 || (t= json_instr_chr_map[s->c_next]) >= S_F)
+      return s->error= JE_SYN;
+
+    s->c_str+= c_len;
+    dest[i/2]+= (i % 2) ? t : t*16;
+  }
+  return 0;
+}
+
+
+static int json_handle_esc(json_string_t *s)
+{
+  int t, c_len;
+  
+  if ((c_len= json_next_char(s)) <= 0)
+    return s->error= json_eos(s) ? JE_EOS : JE_BAD_CHR;
+
+  s->c_str+= c_len;
+  switch (s->c_next)
+  {
+    case 'b':
+      s->c_next= 8;
+      return 0;
+    case 'f':
+      s->c_next= 12;
+      return 0;
+    case 'n':
+      s->c_next= 10;
+      return 0;
+    case 'r':
+      s->c_next= 13;
+      return 0;
+    case 't':
+      s->c_next= 9;
+      return 0;
+  }
+
+  if (s->c_next < 128 && (t= json_instr_chr_map[s->c_next]) == S_ERR)
+  {
+    s->c_str-= c_len;
+    return s->error= JE_ESCAPING;
+  }
+
+
+  if (s->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 code[4]= {0,0,0,0};
+
+    if (read_4_hexdigits(s, code))
+      return 1;
+
+    if ((c_len= my_utf16_uni(0, &s->c_next, code, code+2)) == 2)
+      return 0;
+
+    if (c_len != MY_CS_TOOSMALL4)
+      return s->error= JE_BAD_CHR;
+
+    if ((c_len= json_next_char(s)) <= 0)
+      return s->error= json_eos(s) ? JE_EOS : JE_BAD_CHR;
+    if (s->c_next != '\\')
+      return s->error= JE_SYN;
+
+    if ((c_len= json_next_char(s)) <= 0)
+      return s->error= json_eos(s) ? JE_EOS : JE_BAD_CHR;
+    if (s->c_next != 'u')
+      return s->error= JE_SYN;
+
+    if (read_4_hexdigits(s, code+2))
+      return 1;
+
+    if ((c_len= my_utf16_uni(0, &s->c_next, code, code+4)) == 2)
+      return 0;
+  }
+  return s->error= JE_BAD_CHR;
+}
+
+
+int json_read_string_const_chr(json_string_t *js)
+{
+  int c_len;
+
+  if ((c_len= json_next_char(js)) > 0)
+  {
+    js->c_str+= c_len;
+    return (js->c_next == '\\') ? json_handle_esc(js) : 0;
+  }
+  js->error= json_eos(js) ? JE_EOS : JE_BAD_CHR; 
+  return 1;
+}
+
+
+static int skip_str_constant(json_engine_t *j)
+{
+  int t, c_len;
+  for (;;)
+  {
+    if ((c_len= json_next_char(&j->s)) > 0)
+    {
+      j->s.c_str+= c_len;
+      if (j->s.c_next >= 128 || ((t=json_instr_chr_map[j->s.c_next]) <= S_ETC))
+        continue;
+
+      if (j->s.c_next == '"')
+        break;
+      if (j->s.c_next == '\\')
+      {
+        if (json_handle_esc(&j->s))
+          return 1;
+        continue;
+      }
+      /* Symbol not allowed in JSON. */
+      return j->s.error= JE_NOT_JSON_CHR;
+    }
+    else
+      return j->s.error= json_eos(&j->s) ? JE_EOS : JE_BAD_CHR; 
+  }
+
+  j->state= *j->stack_p;
+  return 0;
+}
+
+
+/* Scalar string. */
+static int v_string(json_engine_t *j)
+{
+  return skip_str_constant(j) || json_scan_next(j);
+}
+
+
+/* Read scalar string. */
+static int read_strn(json_engine_t *j)
+{
+  j->value= j->s.c_str;
+
+  if (skip_str_constant(j))
+    return 1;
+
+  j->state= *j->stack_p;
+  j->value_type= JSON_VALUE_STRING;
+  j->value_len= (j->s.c_str - j->value) - 1;
+  return 0;
+}
+
+
+/*
+  We have dedicated parser for numeric constants. It's similar
+  to the main JSON parser, we similarly define character classes,
+  map characters to classes and implement the state-per-class
+  table. Though we don't create functions that handle
+  particular classes, just specify what new state should parser
+  get in this case.
+*/
+enum json_num_char_classes {
+  N_MINUS,
+  N_PLUS,
+  N_ZERO,
+  N_DIGIT,
+  N_POINT,
+  N_E,
+  N_END,
+  N_EEND,
+  N_ERR,
+  N_NUM_CLASSES
+};
+
+
+static enum json_num_char_classes 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,  N_EEND,   N_END,   N_EEND,  N_EEND,
+};
+
+
+enum json_num_states {
+  NS_OK,  /* Number ended. */
+  NS_GO,  /* Initial state. */
+  NS_GO1, /* If the number starts with '-'. */
+  NS_Z,   /* If the number starts with '0'. */
+  NS_Z1,  /* If the numbers starts with '-0'. */
+  NS_INT, /* Integer part. */
+  NS_FRAC,/* Fractional part. */
+  NS_EX,  /* Exponential part begins. */
+  NS_EX1, /* Exponential part continues. */
+  NS_NUM_STATES
+};
+
+
+static int json_num_states[NS_NUM_STATES][N_NUM_CLASSES]=
+{
+/*         -        +       0        1..9    POINT    E       END_OK   ERROR */
+/*OK*/   { JE_SYN,  JE_SYN, JE_SYN,   JE_SYN, JE_SYN,  JE_SYN, JE_SYN, JE_BAD_CHR },
+/*GO*/   { NS_GO1,  JE_SYN, NS_Z,     NS_INT, JE_SYN,  JE_SYN, JE_SYN, JE_BAD_CHR },
+/*GO1*/  { JE_SYN,  JE_SYN, NS_Z1,    NS_INT, JE_SYN,  JE_SYN, JE_SYN, JE_BAD_CHR },
+/*ZERO*/ { JE_SYN,  JE_SYN, JE_SYN,   JE_SYN, NS_FRAC, JE_SYN, NS_OK,  JE_BAD_CHR },
+/*ZE1*/  { JE_SYN,  JE_SYN, JE_SYN,   JE_SYN, NS_FRAC, JE_SYN, JE_SYN, JE_BAD_CHR },
+/*INT*/  { JE_SYN,  JE_SYN, NS_INT,   NS_INT, NS_FRAC, NS_EX,  NS_OK,  JE_BAD_CHR },
+/*FRAC*/ { JE_SYN,  JE_SYN, NS_FRAC,  NS_FRAC,JE_SYN,  NS_EX,  NS_OK,  JE_BAD_CHR },
+/*EX*/   { NS_EX1,  NS_EX1, NS_EX1,   NS_EX1, JE_SYN,  JE_SYN, JE_SYN, JE_BAD_CHR }, 
+/*EX1*/  { JE_SYN,  JE_SYN, NS_EX1,   NS_EX1, JE_SYN,  JE_SYN, JE_SYN, JE_BAD_CHR }
+};
+
+
+static int skip_num_constant(json_engine_t *j)
+{
+  int state= json_num_states[NS_GO][json_num_chr_map[j->s.c_next]];
+  int c_len;
+
+  for (;;)
+  {
+    if ((c_len= json_next_char(&j->s)) > 0)
+    {
+      if ((state= json_num_states[state][json_num_chr_map[j->s.c_next]]) > 0)
+      {
+        j->s.c_str+= c_len;
+        continue;
+      }
+      break;
+    }
+
+    if ((j->s.error=
+          json_eos(&j->s) ? json_num_states[state][N_END] : JE_BAD_CHR) < 0)
+      return 1;
+    else
+      break;
+  }
+
+  j->state= *j->stack_p;
+  return 0;
+}
+
+
+/* Scalar numeric. */
+static int v_number(json_engine_t *j)
+{
+  return skip_num_constant(j) || json_scan_next(j);
+}
+
+
+/* Read numeric constant. */
+static int read_num(json_engine_t *j)
+{
+  j->value= j->value_begin;
+  if (skip_num_constant(j) == 0)
+  {
+    j->value_type= JSON_VALUE_NUMBER;
+    j->value_len= j->s.c_str - j->value_begin;
+    return 0;
+  }
+  return 1;
+}
+
+
+/* Check that the JSON string matches the argument and skip it. */
+static int skip_string_verbatim(json_string_t *s, const char *str)
+{
+  int c_len;
+  while (*str)
+  {
+    if ((c_len= json_next_char(s)) > 0)
+    {
+      if (s->c_next == (my_wc_t) *(str++))
+      {
+        s->c_str+= c_len;
+        continue;
+      }
+      return JE_SYN;
+    }
+    return json_eos(s) ? JE_EOS : JE_BAD_CHR; 
+  }
+
+  return 0;
+}
+
+
+/* Scalar false. */
+static int v_false(json_engine_t *j)
+{
+  if (skip_string_verbatim(&j->s, "alse"))
+   return 1;
+  j->state= *j->stack_p;
+  return json_scan_next(j);
+}
+
+
+/* Scalar null. */
+static int v_null(json_engine_t *j)
+{
+  if (skip_string_verbatim(&j->s, "ull"))
+   return 1;
+  j->state= *j->stack_p;
+  return json_scan_next(j);
+}
+
+
+/* Scalar true. */
+static int v_true(json_engine_t *j)
+{
+  if (skip_string_verbatim(&j->s, "rue"))
+   return 1;
+  j->state= *j->stack_p;
+  return json_scan_next(j);
+}
+
+
+/* Read false. */
+static int read_false(json_engine_t *j)
+{
+  j->value_type= JSON_VALUE_FALSE;
+  j->value= j->value_begin;
+  j->state= *j->stack_p;
+  j->value_len= 5;
+  return skip_string_verbatim(&j->s, "alse");
+}
+
+
+/* Read null. */
+static int read_null(json_engine_t *j)
+{
+  j->value_type= JSON_VALUE_NULL;
+  j->value= j->value_begin;
+  j->state= *j->stack_p;
+  j->value_len= 4;
+  return skip_string_verbatim(&j->s, "ull");
+}
+
+
+/* Read true. */
+static int read_true(json_engine_t *j)
+{
+  j->value_type= JSON_VALUE_TRUE;
+  j->value= j->value_begin;
+  j->state= *j->stack_p;
+  j->value_len= 4;
+  return skip_string_verbatim(&j->s, "rue");
+}
+
+
+/* Disallowed character. */
+static int not_json_chr(json_engine_t *j)
+{
+  j->s.error= JE_NOT_JSON_CHR;
+  return 1;
+}
+
+
+/* Bad character. */
+static int bad_chr(json_engine_t *j)
+{
+  j->s.error= JE_BAD_CHR;
+  return 1;
+}
+
+
+/* Correct finish. */
+static int done(json_engine_t *j  __attribute__((unused)))
+{
+  return 1;
+}
+
+
+/* End of the object. */
+static int end_object(json_engine_t *j)
+{
+  j->stack_p--;
+  j->state= JST_OBJ_END;
+  return 0;
+}
+
+
+/* End of the array. */
+static int end_array(json_engine_t *j)
+{
+  j->stack_p--;
+  j->state= JST_ARRAY_END;
+  return 0;
+}
+
+
+/* Start reading key name. */
+static int read_keyname(json_engine_t *j)
+{
+  j->state= JST_KEY;
+  return 0;
+}
+
+
+static void get_first_nonspace(json_string_t *js, int *t_next, int *c_len)
+{
+  do
+  {
+    if ((*c_len= json_next_char(js)) <= 0)
+      *t_next= json_eos(js) ? C_EOS : C_BAD;
+    else
+    {
+      *t_next= (js->c_next < 128) ? json_chr_map[js->c_next] : C_ETC;
+      js->c_str+= *c_len;
+    }
+  } while (*t_next == C_SPACE);
+}
+
+
+/* Next key name. */
+static int next_key(json_engine_t *j)
+{
+  int t_next, c_len;
+  get_first_nonspace(&j->s, &t_next, &c_len);
+
+  if (t_next == C_QUOTE)
+  {
+    j->state= JST_KEY;
+    return 0;
+  }
+
+  j->s.error= (t_next == C_EOS)  ? JE_EOS :
+              ((t_next == C_BAD) ? JE_BAD_CHR :
+                                   JE_SYN);
+  return 1;
+}
+
+
+/* Forward declarations. */
+static int skip_colon(json_engine_t *j);
+static int skip_key(json_engine_t *j);
+static int struct_end_cb(json_engine_t *j);
+static int struct_end_qb(json_engine_t *j);
+static int struct_end_cm(json_engine_t *j);
+static int struct_end_eos(json_engine_t *j);
+
+
+static int next_item(json_engine_t *j)
+{
+  j->state= JST_VALUE;
+  return 0;
+}
+
+
+static int array_item(json_engine_t *j)
+{
+  j->state= JST_VALUE;
+  j->s.c_str-= j->sav_c_len;
+  return 0;
+}
+
+
+static json_state_handler json_actions[NR_JSON_STATES][NR_C_CLASSES]=
+/*
+   EOS              {            }             [             ]
+   :                ,            "             -0..9         f
+   n                t              ETC          ERR           BAD
+*/
+{
+  {/*VALUE*/
+    unexpected_eos, mark_object, syntax_error, mark_array,   syntax_error,
+    syntax_error,   syntax_error,v_string,     v_number,     v_false,
+    v_null,         v_true,       syntax_error, not_json_chr, bad_chr},
+  {/*KEY*/
+    unexpected_eos, skip_key,    skip_key,     skip_key,     skip_key,
+    skip_key,       skip_key,    skip_colon,   skip_key,     skip_key,
+    skip_key,       skip_key,     skip_key,     not_json_chr, bad_chr},
+  {/*OBJ_START*/
+    unexpected_eos, syntax_error, end_object,  syntax_error, syntax_error,
+    syntax_error,   syntax_error, read_keyname, syntax_error, syntax_error,
+    syntax_error,   syntax_error,   syntax_error,    not_json_chr, bad_chr},
+  {/*OBJ_END*/
+    struct_end_eos, syntax_error, struct_end_cb, syntax_error, struct_end_qb,
+    syntax_error,   struct_end_cm,syntax_error,  syntax_error, syntax_error,
+    syntax_error,   syntax_error,  syntax_error,    not_json_chr, bad_chr},
+  {/*ARRAY_START*/
+    unexpected_eos, array_item,   syntax_error, array_item,   end_array,
+    syntax_error,   syntax_error, array_item,  array_item,  array_item,
+    array_item,    array_item,    syntax_error,    not_json_chr, bad_chr},
+  {/*ARRAY_END*/
+    struct_end_eos, syntax_error, struct_end_cb, syntax_error,  struct_end_qb,
+    syntax_error,   struct_end_cm, syntax_error, syntax_error,  syntax_error,
+    syntax_error,   syntax_error,  syntax_error,    not_json_chr, bad_chr},
+  {/*DONE*/
+    done,           syntax_error, syntax_error, syntax_error, syntax_error,
+    syntax_error,   syntax_error, syntax_error, syntax_error, syntax_error,
+    syntax_error,   syntax_error, syntax_error, not_json_chr, bad_chr},
+  {/*OBJ_CONT*/
+    unexpected_eos, syntax_error, end_object,    syntax_error,   end_array,
+    syntax_error,   next_key,     syntax_error,  syntax_error,   syntax_error,
+    syntax_error,    syntax_error,    syntax_error,    not_json_chr, bad_chr},
+  {/*ARRAY_CONT*/
+    unexpected_eos, syntax_error, syntax_error,  syntax_error, end_array,
+    syntax_error,   next_item,    syntax_error,  syntax_error, syntax_error,
+    syntax_error,    syntax_error,    syntax_error,    not_json_chr, bad_chr},
+  {/*READ_VALUE*/
+    unexpected_eos, read_obj,     syntax_error,  read_array,    syntax_error,
+    syntax_error,   syntax_error, read_strn,     read_num,      read_false,
+    read_null,      read_true,    syntax_error,    not_json_chr, bad_chr},
+};
+
+
+
+int json_scan_start(json_engine_t *je,
+                    CHARSET_INFO *i_cs, const uchar *str, const uchar *end)
+{
+  json_string_setup(&je->s, i_cs, str, end);
+  je->stack[0]= JST_DONE;
+  je->stack_p= je->stack;
+  je->state= JST_VALUE;
+  return 0;
+}
+
+
+/* Skip colon and the value. */
+static int skip_colon(json_engine_t *j)
+{
+  int t_next, c_len;
+
+  get_first_nonspace(&j->s, &t_next, &c_len);
+
+  if (t_next == C_COLON)
+  {
+    get_first_nonspace(&j->s, &t_next, &c_len);
+    return json_actions[JST_VALUE][t_next](j);
+ }
+
+  j->s.error= (t_next == C_EOS)  ? JE_EOS :
+              ((t_next == C_BAD) ? JE_BAD_CHR:
+                                   JE_SYN);
+
+  return 1;
+}
+
+
+/* Skip colon and the value. */
+static int skip_key(json_engine_t *j)
+{
+  int t_next, c_len;
+  while (json_read_keyname_chr(j) == 0) {}
+
+  if (j->s.error)
+    return 1;
+
+  get_first_nonspace(&j->s, &t_next, &c_len);
+  return json_actions[JST_VALUE][t_next](j);
+}
+
+
+/*
+  Handle EOS after the end of an object or array.
+  To do that we should pop the stack to see if
+  we are inside an object, or an array, and
+  run our 'state machine' accordingly.
+*/
+static int struct_end_eos(json_engine_t *j)
+{ return json_actions[*j->stack_p][C_EOS](j); }
+
+
+/*
+  Handle '}' after the end of an object or array.
+  To do that we should pop the stack to see if
+  we are inside an object, or an array, and
+  run our 'state machine' accordingly.
+*/
+static int struct_end_cb(json_engine_t *j)
+{ return json_actions[*j->stack_p][C_RCURB](j); }
+
+
+/*
+  Handle ']' after the end of an object or array.
+  To do that we should pop the stack to see if
+  we are inside an object, or an array, and
+  run our 'state machine' accordingly.
+*/
+static int struct_end_qb(json_engine_t *j)
+{ return json_actions[*j->stack_p][C_RSQRB](j); }
+
+
+/*
+  Handle ',' after the end of an object or array.
+  To do that we should pop the stack to see if
+  we are inside an object, or an array, and
+  run our 'state machine' accordingly.
+*/
+static int struct_end_cm(json_engine_t *j)
+{ return json_actions[*j->stack_p][C_COMMA](j); }
+
+
+int json_read_keyname_chr(json_engine_t *j)
+{
+  int c_len, t;
+
+  if ((c_len= json_next_char(&j->s)) > 0)
+  {
+    j->s.c_str+= c_len;
+    if (j->s.c_next>= 128 || (t= json_instr_chr_map[j->s.c_next]) <= S_ETC)
+      return 0;
+
+    switch (t)
+    {
+    case S_QUOTE:
+      for (;;)  /* Skip spaces until ':'. */
+      {
+        if ((c_len= json_next_char(&j->s) > 0))
+        {
+          if (j->s.c_next == ':')
+          {
+            j->s.c_str+= c_len;
+            j->state= JST_VALUE;
+            return 1;
+          }
+
+          if (j->s.c_next < 128 && json_chr_map[j->s.c_next] == C_SPACE)
+          {
+            j->s.c_str+= c_len;
+            continue;
+          }
+          j->s.error= JE_SYN;
+          break;
+        }
+        j->s.error= json_eos(&j->s) ? JE_EOS : JE_BAD_CHR;
+        break;
+      }
+      return 1;
+    case S_BKSL:
+      return json_handle_esc(&j->s);
+    case S_ERR:
+      j->s.c_str-= c_len;
+      j->s.error= JE_STRING_CONST;
+      return 1;
+    }
+  }
+  j->s.error= json_eos(&j->s) ? JE_EOS : JE_BAD_CHR; 
+  return 1;
+}
+
+
+int json_read_value(json_engine_t *j)
+{
+  int t_next, c_len, res;
+
+  if (j->state == JST_KEY)
+  {
+    while (json_read_keyname_chr(j) == 0) {}
+
+    if (j->s.error)
+      return 1;
+  }
+
+  get_first_nonspace(&j->s, &t_next, &c_len);
+
+  j->value_begin= j->s.c_str-c_len;
+  res= json_actions[JST_READ_VALUE][t_next](j);
+  j->value_end= j->s.c_str;
+  return res;
+}
+
+
+int json_scan_next(json_engine_t *j)
+{
+  int t_next;
+
+  get_first_nonspace(&j->s, &t_next, &j->sav_c_len);
+  return json_actions[j->state][t_next](j);
+}
+
+
+enum json_path_chr_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 (for "lax") */
+  P_S,      /* s (for "strict") */
+  P_SPACE,  /* space */
+  P_BKSL,   /* \ */
+  P_ETC,    /* everything else */
+  P_ERR,    /* character disallowed in JSON*/
+  P_BAD,    /* invalid character */
+  N_PATH_CLASSES,
+};
+
+
+static enum json_path_chr_classes 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_ETC,   P_ASTER, 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_BKSL, 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,  /* Initial state. */
+  PS_LAX, /* Parse the 'lax' keyword. */
+  PS_PT,  /* New path's step begins. */
+  PS_AR,  /* Parse array step. */
+  PS_AWD, /* Array wildcard. */
+  PS_Z,   /* '0' (as an array item number). */
+  PS_INT, /* Parse integer (as an array item number). */
+  PS_AS,  /* Space. */
+  PS_KEY, /* Key. */
+  PS_KNM, /* Parse key name. */
+  PS_KWD, /* Key wildcard. */
+  N_PATH_STATES, /* Below are states that aren't in the transitions table. */
+  PS_SCT,  /* Parse the 'strict' keyword. */
+  PS_EKY,  /* '.' after the keyname so next step is the key. */
+  PS_EAR,  /* '[' after the keyname so next step is the array. */
+  PS_ESC,  /* Escaping in the keyname. */
+  PS_OK,   /* Path normally ended. */
+  PS_KOK   /* EOS after the keyname so end the path normally. */
+};
+
+
+static int json_path_transitions[N_PATH_STATES][N_PATH_CLASSES]=
+{
+/*
+            EOS       $,      *       [       ]       .       0
+            1..9    L       S       SPACE   \       ETC     ERR
+            BAD
+*/
+/* GO  */ { JE_EOS, PS_PT,  JE_SYN, JE_SYN, JE_SYN, JE_SYN, JE_SYN,
+            JE_SYN, PS_LAX, PS_SCT, PS_GO,  JE_SYN, JE_SYN, JE_NOT_JSON_CHR,
+            JE_BAD_CHR},
+/* LAX */ { JE_EOS, JE_SYN, JE_SYN, JE_SYN, JE_SYN, JE_SYN, JE_SYN,
+            JE_SYN, PS_LAX, JE_SYN, PS_GO,  JE_SYN, JE_SYN, JE_NOT_JSON_CHR,
+            JE_BAD_CHR},
+/* PT */  { PS_OK,  JE_SYN, JE_SYN, PS_AR,  JE_SYN, PS_KEY, JE_SYN, JE_SYN,
+            JE_SYN, JE_SYN, JE_SYN, JE_SYN, JE_SYN, JE_NOT_JSON_CHR,
+            JE_BAD_CHR},
+/* AR */  { JE_EOS, JE_SYN, PS_AWD, JE_SYN, PS_PT,  JE_SYN, PS_Z,
+            PS_INT, JE_SYN, JE_SYN, PS_AR,  JE_SYN, JE_SYN, JE_NOT_JSON_CHR,
+            JE_BAD_CHR},
+/* AWD */ { JE_EOS, JE_SYN, JE_SYN, JE_SYN, PS_PT,  JE_SYN, JE_SYN,
+            JE_SYN, JE_SYN, JE_SYN, PS_AS,  JE_SYN, JE_SYN, JE_NOT_JSON_CHR,
+            JE_BAD_CHR},
+/* Z */   { JE_EOS, JE_SYN, JE_SYN, JE_SYN, PS_PT,  JE_SYN, JE_SYN,
+            JE_SYN, JE_SYN, JE_SYN, PS_AS,  JE_SYN, JE_SYN, JE_NOT_JSON_CHR,
+            JE_BAD_CHR},
+/* INT */ { JE_EOS, JE_SYN, JE_SYN, JE_SYN, PS_PT,  JE_SYN, PS_INT,
+            PS_INT, JE_SYN, JE_SYN, PS_AS,  JE_SYN, JE_SYN, JE_NOT_JSON_CHR,
+            JE_BAD_CHR},
+/* AS */  { JE_EOS, JE_SYN, JE_SYN, JE_SYN, PS_PT,  JE_SYN, JE_SYN, JE_SYN,
+            JE_SYN, JE_SYN, PS_AS,  JE_SYN, JE_SYN, JE_NOT_JSON_CHR,
+            JE_BAD_CHR},
+/* KEY */ { JE_EOS, PS_KNM, PS_KWD, JE_SYN, PS_KNM, JE_SYN, PS_KNM,
+            PS_KNM, PS_KNM, PS_KNM, PS_KNM, JE_SYN, PS_KNM, JE_NOT_JSON_CHR,
+            JE_BAD_CHR},
+/* 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_NOT_JSON_CHR,
+            JE_BAD_CHR},
+/* KWD */ { PS_OK,  JE_SYN, JE_SYN, PS_AR,  JE_SYN, PS_EKY, JE_SYN,
+            JE_SYN, JE_SYN, JE_SYN, JE_SYN, JE_SYN, JE_SYN, JE_NOT_JSON_CHR,
+            JE_BAD_CHR}
+};
+
+
+int json_path_setup(json_path_t *p,
+                    CHARSET_INFO *i_cs, const uchar *str, const uchar *end)
+{
+  int c_len, t_next, state= PS_GO;
+
+  json_string_setup(&p->s, i_cs, str, end);
+
+  p->steps[0].type= JSON_PATH_ARRAY;
+  p->steps[0].wild= 1;
+  p->last_step= p->steps;
+  p->mode_strict= FALSE;
+
+  do
+  {
+    if ((c_len= json_next_char(&p->s)) <= 0)
+      t_next= json_eos(&p->s) ? P_EOS : P_BAD;
+    else
+      t_next= (p->s.c_next >= 128) ? P_ETC : json_path_chr_map[p->s.c_next];
+
+    if ((state= json_path_transitions[state][t_next]) < 0)
+      return p->s.error= state;
+
+    p->s.c_str+= c_len;
+
+    switch (state)
+    {
+    case PS_LAX:
+      if ((p->s.error= skip_string_verbatim(&p->s, "ax")))
+        return 1;
+      p->mode_strict= FALSE;
+      continue;
+    case PS_SCT:
+      if ((p->s.error= skip_string_verbatim(&p->s, "rict")))
+        return 1;
+      p->mode_strict= TRUE;
+      state= PS_LAX;
+      continue;
+    case PS_AWD:
+      p->last_step->wild= 1;
+      continue;
+    case PS_INT:
+      p->last_step->n_item*= 10;
+      p->last_step->n_item+= p->s.c_next - '0';
+      continue;
+    case PS_EKY:
+      p->last_step->key_end= p->s.c_str - c_len;
+      state= PS_KEY;
+      /* Note no 'continue' here. */
+    case PS_KEY:
+      p->last_step++;
+      p->last_step->type= JSON_PATH_KEY;
+      p->last_step->wild= 0;
+      p->last_step->key= p->s.c_str;
+      continue;
+    case PS_EAR:
+      p->last_step->key_end= p->s.c_str - c_len;
+      state= PS_AR;
+      /* Note no 'continue' here. */
+    case PS_AR:
+      p->last_step++;
+      p->last_step->type= JSON_PATH_ARRAY;
+      p->last_step->wild= 0;
+      p->last_step->n_item= 0;
+      continue;
+    case PS_KWD:
+      p->last_step->wild= 1;
+      continue;
+    case PS_ESC:
+      if (json_handle_esc(&p->s))
+        return 1;
+      continue;
+    case PS_KOK:
+      p->last_step->key_end= p->s.c_str - c_len;
+      state= PS_OK;
+      break;
+    };
+  } while (state != PS_OK);
+
+  return 0;
+}
+
+
+int json_skip_level(json_engine_t *j)
+{
+  int ct= 0;
+
+  while (json_scan_next(j) == 0)
+  {
+    switch (j->state) {
+    case JST_OBJ_START:
+    case JST_ARRAY_START:
+      ct++;
+      break;
+    case JST_OBJ_END:
+    case JST_ARRAY_END:
+      if (ct == 0)
+        return 0;
+      ct--;
+      break;
+    }
+  }
+
+  return 1;
+}
+
+
+int json_skip_key(json_engine_t *j)
+{
+  if (json_read_value(j))
+    return 1;
+
+  if (json_value_scalar(j))
+    return 0;
+
+  return json_skip_level(j);
+}
+
+
+/*
+  Current step of the patch matches the JSON construction.
+  Now we should either stop the search or go to the next
+  step of the path.
+*/
+static int handle_match(json_engine_t *je, json_path_t *p,
+                        json_path_step_t **p_cur_step, uint *n_arrays)
+{
+  DBUG_ASSERT(*p_cur_step < p->last_step);
+
+  if (json_read_value(je))
+    return 1;
+
+  if (json_value_scalar(je))
+    return 0;
+
+  (*p_cur_step)++;
+  n_arrays[*p_cur_step - p->steps]= 0;
+
+  if ((int) je->value_type != (int) (*p_cur_step)->type)
+  {
+    (*p_cur_step)--;
+    return json_skip_level(je);
+  }
+
+  return 0;
+}
+
+
+/*
+  Check if the name of the current JSON key matches
+  the step of the path.
+*/
+static int json_key_matches(json_engine_t *je, json_string_t *k)
+{
+  while (json_read_keyname_chr(je) == 0)
+  {
+    if (json_read_string_const_chr(k) ||
+        je->s.c_next != k->c_next)
+      return 0;
+  }
+
+  if (json_read_string_const_chr(k))
+    return 1;
+
+  return 0;
+}
+
+
+int json_find_value(json_engine_t *je,
+                    json_path_t *p, json_path_step_t **p_cur_step)
+{
+  json_string_t key_name;
+  uint n_arrays[JSON_DEPTH_LIMIT];
+
+  json_string_set_cs(&key_name, p->s.cs);
+
+  do
+  {
+    json_path_step_t *cur_step= *p_cur_step;
+    switch (je->state)
+    {
+    case JST_KEY:
+      DBUG_ASSERT(cur_step->type == JSON_PATH_KEY);
+      if (!cur_step->wild)
+      {
+        json_string_set_str(&key_name, cur_step->key, cur_step->key_end);
+        if (!json_key_matches(je, &key_name))
+        {
+          if (json_skip_key(je))
+            goto exit;
+          continue;
+        }
+      }
+      if (cur_step == p->last_step ||
+          handle_match(je, p, p_cur_step, n_arrays))
+        goto exit;
+      break;
+    case JST_VALUE:
+      DBUG_ASSERT(cur_step->type == JSON_PATH_ARRAY);
+      if (cur_step->wild ||
+          cur_step->n_item == n_arrays[cur_step - p->steps])
+      {
+        /* Array item matches. */
+        if (cur_step == p->last_step ||
+            handle_match(je, p, p_cur_step, n_arrays))
+          goto exit;
+      }
+      else
+      {
+        json_skip_array_item(je);
+        n_arrays[cur_step - p->steps]++;
+      }
+      break;
+    case JST_OBJ_END:
+    case JST_ARRAY_END:
+      (*p_cur_step)--;
+      break;
+    default:
+      DBUG_ASSERT(0);
+      break;
+    };
+  } while (json_scan_next(je) == 0);
+
+  /* No luck. */
+  return 1;
+
+exit:
+  return je->s.error;
+}
+
+
+int json_append_ascii(CHARSET_INFO *json_cs,
+                      uchar *json, uchar *json_end,
+                      const uchar *ascii, const uchar *ascii_end)
+{
+  const uchar *json_start= json;
+  while (ascii < ascii_end)
+  {
+    int c_len;
+    if ((c_len= json_cs->cset->wc_mb(json_cs, (my_wc_t) *ascii,
+                                     json, json_end)) > 0)
+    {
+      json+= c_len;
+      ascii++;
+      continue;
+    }
+
+    /* Error return. */
+    return c_len;
+  }
+
+  return json - json_start;
+}
+
+
+int json_unescape(CHARSET_INFO *json_cs,
+                  const uchar *json_str, const uchar *json_end,
+                  CHARSET_INFO *res_cs, uchar *res, uchar *res_end)
+{
+  json_string_t s;
+  json_string_setup(&s, json_cs, json_str, json_end);
+  while (json_read_string_const_chr(&s) == 0)
+  {
+    int c_len;
+    if ((c_len= res_cs->cset->wc_mb(res_cs, s.c_next, res, res_end)) > 0)
+    {
+      res+= c_len;
+      continue;
+    }
+    if (c_len == MY_CS_ILUNI)
+    {
+      /*
+        Result charset doesn't support the json's character.
+        Let's replace it with the '?' symbol.
+      */
+      if ((c_len= res_cs->cset->wc_mb(res_cs, '?', res, res_end)) > 0)
+      {
+        res+= c_len;
+        continue;
+      }
+    }
+    /* Result buffer is too small. */
+    return -1;
+  }
+
+  return s.error ? 1 : 0;
+}
+
+
+/* When we need to replace a character with the escaping. */
+enum json_esc_char_classes {
+  ESC_= 0,    /* No need to escape. */
+  ESC_U= 'u', /* Character not allowed in JSON. Always escape as \uXXXX. */
+  ESC_B= 'b', /* Backspace. Escape as \b */
+  ESC_F= 'f', /* Formfeed. Escape as \f */
+  ESC_N= 'n', /* Newline. Escape as \n */
+  ESC_R= 'r', /* Return. Escape as \r */
+  ESC_T= 't', /* Tab. Escape as \s */
+  ESC_BS= '\\'  /* Backslash or '"'. Escape by the \\ prefix. */
+};
+
+
+/* This specifies how we should escape the character. */
+static enum json_esc_char_classes json_escape_chr_map[0x60] = {
+  ESC_U,   ESC_U,   ESC_U,   ESC_U,   ESC_U,   ESC_U,   ESC_U,   ESC_U,
+  ESC_B,   ESC_T,   ESC_N,   ESC_U,   ESC_F,   ESC_R,   ESC_U,   ESC_U,
+  ESC_U,   ESC_U,   ESC_U,   ESC_U,   ESC_U,   ESC_U,   ESC_U,   ESC_U,
+  ESC_U,   ESC_U,   ESC_U,   ESC_U,   ESC_U,   ESC_U,   ESC_U,   ESC_U,
+
+  ESC_,    ESC_,    ESC_BS,  ESC_,    ESC_,    ESC_,    ESC_,    ESC_,
+  ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,
+  ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,
+  ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,
+
+  ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,
+  ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,
+  ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,    ESC_,
+  ESC_,    ESC_,    ESC_,    ESC_,    ESC_BS,  ESC_,    ESC_,    ESC_,
+};
+
+
+static const char hexconv[16] = "0123456789ABCDEF";
+
+
+int json_escape(CHARSET_INFO *str_cs,
+                const uchar *str, const uchar *str_end,
+                CHARSET_INFO *json_cs, uchar *json, uchar *json_end)
+{
+  const uchar *json_start= json;
+
+  while (str < str_end)
+  {
+    my_wc_t c_chr;
+    int c_len;
+    if ((c_len= str_cs->cset->mb_wc(str_cs, &c_chr, str, str_end)) > 0)
+    {
+      enum json_esc_char_classes c_class;
+      
+      str+= c_len;
+      if (c_chr > 0x60 || (c_class= json_escape_chr_map[c_chr]) == ESC_)
+      {
+        if ((c_len= json_cs->cset->wc_mb(json_cs, c_chr, json, json_end)) > 0)
+        {
+          json+= c_len;
+          continue;
+        }
+        if (c_len < 0)
+        {
+          /* JSON buffer is depleted. */
+          return -1;
+        }
+
+        /* JSON charset cannot convert this character. */
+        c_class= ESC_U;
+      }
+
+      if ((c_len= json_cs->cset->wc_mb(json_cs, '\\', json, json_end)) <= 0 ||
+          (c_len= json_cs->cset->wc_mb(json_cs,
+                                       (c_class == ESC_BS) ? c_chr : c_class,
+                                       json+= c_len, json_end)) <= 0)
+      {
+        /* JSON buffer is depleted. */
+        return -1;
+      }
+      json+= c_len;
+
+      if (c_class != ESC_U)
+        continue;
+
+      {
+        /* We have to use /uXXXX escaping. */
+        uchar utf16buf[4];
+        uchar code_str[8];
+        int u_len= my_uni_utf16(0, c_chr, utf16buf, utf16buf + 4);
+
+        code_str[0]= hexconv[utf16buf[0] >> 4];
+        code_str[1]= hexconv[utf16buf[0] & 15];
+        code_str[2]= hexconv[utf16buf[1] >> 4];
+        code_str[3]= hexconv[utf16buf[1] & 15];
+
+        if (u_len > 2)
+        {
+          code_str[4]= hexconv[utf16buf[2] >> 4];
+          code_str[5]= hexconv[utf16buf[2] & 15];
+          code_str[6]= hexconv[utf16buf[3] >> 4];
+          code_str[7]= hexconv[utf16buf[3] & 15];
+        }
+        
+        if ((c_len= json_append_ascii(json_cs, json, json_end,
+                                      code_str, code_str+u_len*2)) > 0)
+        {
+          json+= c_len;
+          continue;
+        }
+        /* JSON buffer is depleted. */
+        return -1;
+      }
+    }
+  }
+
+  return json - json_start;
+}


More information about the commits mailing list