Browse Source

MDEV-9143 JSON_xxx functions.

strings/json_lib.c added as a JSON library.
        SQL frunction added with sql/item_jsonfunc.h/cc
pull/241/merge
Alexey Botchkov 9 years ago
parent
commit
27025221fe
  1. 1
      CMakeLists.txt
  2. 1
      include/CMakeLists.txt
  3. 356
      include/json_lib.h
  4. 3
      libmysqld/CMakeLists.txt
  5. 123
      mysql-test/r/func_json.result
  6. 2
      mysql-test/r/parser_precedence.result
  7. 54
      mysql-test/t/func_json.test
  8. 2
      sql/CMakeLists.txt
  9. 15
      sql/item.h
  10. 474
      sql/item_create.cc
  11. 1109
      sql/item_jsonfunc.cc
  12. 304
      sql/item_jsonfunc.h
  13. 21
      sql/item_xmlfunc.cc
  14. 5
      sql/item_xmlfunc.h
  15. 4
      sql/sql_yacc.yy
  16. 2
      storage/connect/mysql-test/connect/disabled.def
  17. 2
      strings/CMakeLists.txt
  18. 19
      strings/ctype-ucs2.c
  19. 1553
      strings/json_lib.c
  20. 23
      unittest/json_lib/CMakeLists.txt
  21. 186
      unittest/json_lib/json_lib-t.c

1
CMakeLists.txt

@ -356,6 +356,7 @@ IF(WITH_UNIT_TESTS)
ADD_SUBDIRECTORY(unittest/examples)
ADD_SUBDIRECTORY(unittest/mysys)
ADD_SUBDIRECTORY(unittest/my_decimal)
ADD_SUBDIRECTORY(unittest/json_lib)
IF(NOT WITHOUT_SERVER)
ADD_SUBDIRECTORY(unittest/sql)
ENDIF()

1
include/CMakeLists.txt

@ -61,6 +61,7 @@ SET(HEADERS
my_compiler.h
handler_state.h
handler_ername.h
json_lib.h
)
INSTALL(FILES ${HEADERS} DESTINATION ${INSTALL_INCLUDEDIR} COMPONENT Development)

356
include/json_lib.h

@ -0,0 +1,356 @@
#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_OBJ_START:
// parser found an object (the '{' in JSON)
case JST_OBJ_END:
// parser found the end of the object (the '}' in JSON)
case JST_ARRAY_START:
// 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.
The 'array_counters' is the array of JSON_DEPTH_LIMIT size.
It stores the array counters of the parsed JSON.
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_path()
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_path(json_engine_t *je,
json_path_t *p, json_path_step_t **p_cur_step,
uint *array_counters);
typedef struct st_json_find_paths_t
{
uint n_paths;
json_path_t *paths;
uint cur_depth;
uint *path_depths;
uint array_counters[JSON_DEPTH_LIMIT];
} json_find_paths_t;
int json_find_paths_first(json_engine_t *je, json_find_paths_t *state,
uint n_paths, json_path_t *paths, uint *path_depths);
int json_find_paths_next(json_engine_t *je, json_find_paths_t *state);
/*
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 */

3
libmysqld/CMakeLists.txt

@ -50,7 +50,8 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
../sql/item.cc ../sql/item_create.cc ../sql/item_func.cc
../sql/item_geofunc.cc ../sql/item_row.cc ../sql/item_strfunc.cc
../sql/item_subselect.cc ../sql/item_sum.cc ../sql/item_timefunc.cc
../sql/item_xmlfunc.cc ../sql/key.cc ../sql/lock.cc ../sql/log.cc
../sql/item_xmlfunc.cc ../sql/item_jsonfunc.cc
../sql/key.cc ../sql/lock.cc ../sql/log.cc
../sql/log_event.cc ../sql/mf_iocache.cc ../sql/my_decimal.cc
../sql/net_serv.cc ../sql/opt_range.cc ../sql/opt_sum.cc
../sql/parse_file.cc ../sql/procedure.cc ../sql/protocol.cc

123
mysql-test/r/func_json.result

@ -0,0 +1,123 @@
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('{"k1":123, "k2":345}', '123', '$.k1');
json_contains('{"k1":123, "k2":345}', '123', '$.k1')
1
select json_contains('"you"', '"you"');
json_contains('"you"', '"you"')
1
select json_contains('"youth"', '"you"');
json_contains('"youth"', '"you"')
0
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\"
select json_merge('string', 123);
json_merge('string', 123)
["string", 123]
select json_type('{"k1":123, "k2":345}');
json_type('{"k1":123, "k2":345}')
OBJECT
select json_type('[123, "k2", 345]');
json_type('[123, "k2", 345]')
ARRAY
select json_type("true");
json_type("true")
BOOLEAN
select json_type('123');
json_type('123')
NUMBER

2
mysql-test/r/parser_precedence.result

@ -369,7 +369,7 @@ select (NOT FALSE) AND FALSE, NOT (FALSE AND FALSE), NOT FALSE AND FALSE;
0 1 0
Testing that NOT is associative
select NOT NOT TRUE, NOT NOT NOT FALSE;
NOT NOT TRUE NOT NOT NOT FALSE
TRUE NOT NOT NOT FALSE
1 1
Testing that IS has precedence over NOT
select (NOT NULL) IS TRUE, NOT (NULL IS TRUE), NOT NULL IS TRUE;

54
mysql-test/t/func_json.test

@ -0,0 +1,54 @@
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('{"k1":123, "k2":345}', '123', '$.k1');
select json_contains('"you"', '"you"');
select json_contains('"youth"', '"you"');
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);
select json_type('{"k1":123, "k2":345}');
select json_type('[123, "k2", 345]');
select json_type("true");
select json_type('123');

2
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

15
sql/item.h

@ -2997,6 +2997,20 @@ public:
};
/*
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:
@ -4750,6 +4764,7 @@ public:
#include "item_timefunc.h"
#include "item_subselect.h"
#include "item_xmlfunc.h"
#include "item_jsonfunc.h"
#include "item_create.h"
#endif

474
sql/item_create.cc

@ -1708,6 +1708,201 @@ protected:
#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_type : public Create_func_arg1
{
public:
virtual Item *create_1_arg(THD *thd, Item *arg1);
static Create_func_json_type s_singleton;
protected:
Create_func_json_type() {}
virtual ~Create_func_json_type() {}
};
class Create_func_json_depth : public Create_func_arg1
{
public:
virtual Item *create_1_arg(THD *thd, Item *arg1);
static Create_func_json_depth s_singleton;
protected:
Create_func_json_depth() {}
virtual ~Create_func_json_depth() {}
};
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: public Create_native_func
{
public:
virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
static Create_func_json_contains s_singleton;
protected:
Create_func_json_contains() {}
virtual ~Create_func_json_contains() {}
};
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_length : public Create_native_func
{
public:
virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
static Create_func_json_length s_singleton;
protected:
Create_func_json_length() {}
virtual ~Create_func_json_length() {}
};
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 +4767,69 @@ 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_type Create_func_json_type::s_singleton;
Item*
Create_func_json_type::create_1_arg(THD *thd, Item *arg1)
{
return new (thd->mem_root) Item_func_json_type(thd, arg1);
}
Create_func_json_depth Create_func_json_depth::s_singleton;
Item*
Create_func_json_depth::create_1_arg(THD *thd, Item *arg1)
{
return new (thd->mem_root) Item_func_json_depth(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 +4839,207 @@ 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_length Create_func_json_length::s_singleton;
Item*
Create_func_json_length::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_length(thd, *item_list);
}
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 Create_func_json_contains::s_singleton;
Item*
Create_func_json_contains::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, val, [path]...*/)
{
my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
}
else
{
func= new (thd->mem_root) Item_func_json_contains(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 +6311,21 @@ 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") }, BUILDER(Create_func_json_contains)},
{ { C_STRING_WITH_LEN("JSON_CONTAINS_PATH") }, BUILDER(Create_func_json_contains_path)},
{ { C_STRING_WITH_LEN("JSON_DEPTH") }, BUILDER(Create_func_json_depth)},
{ { 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_LENGTH") }, BUILDER(Create_func_json_length)},
{ { 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_TYPE") }, BUILDER(Create_func_json_type)},
{ { 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)},

1109
sql/item_jsonfunc.cc
File diff suppressed because it is too large
View File

304
sql/item_jsonfunc.h

@ -0,0 +1,304 @@
#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: public Item_int_func
{
protected:
String tmp_js;
json_path_with_flags *paths;
String *tmp_paths;
bool a2_constant, a2_parsed;
String tmp_val, *val;
public:
Item_func_json_contains(THD *thd, List<Item> &list):
Item_int_func(thd, list), tmp_paths(0) {}
const char *func_name() const { return "json_contains"; }
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>(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); }
};
class Item_func_json_length: public Item_int_func
{
protected:
String tmp_js;
String tmp_path;
public:
Item_func_json_length(THD *thd, List<Item> &list):
Item_int_func(thd, list) {}
const char *func_name() const { return "json_length"; }
longlong val_int();
Item *get_copy(THD *thd, MEM_ROOT *mem_root)
{ return get_item_copy<Item_func_json_length>(thd, mem_root, this); }
};
class Item_func_json_depth: public Item_int_func
{
protected:
String tmp_js;
public:
Item_func_json_depth(THD *thd, Item *js): Item_int_func(thd, js) {}
const char *func_name() const { return "json_depth"; }
longlong val_int();
Item *get_copy(THD *thd, MEM_ROOT *mem_root)
{ return get_item_copy<Item_func_json_depth>(thd, mem_root, this); }
};
class Item_func_json_type: public Item_str_func
{
protected:
String tmp_js;
public:
Item_func_json_type(THD *thd, Item *js): Item_str_func(thd, js) {}
const char *func_name() const { return "json_type"; }
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_type>(thd, mem_root, this); }
};
#endif /* ITEM_JSONFUNC_INCLUDED */

21
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"
/*
@ -406,19 +402,6 @@ public:
};
/*
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
@ -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);
}

5
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;

4
sql/sql_yacc.yy

@ -13869,13 +13869,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;
}

2
storage/connect/mysql-test/connect/disabled.def

@ -13,3 +13,5 @@ jdbc : Variable settings depend on machine configuration
jdbc_new : Variable settings depend on machine configuration
jdbc_oracle : Variable settings depend on machine configuration
jdbc_postgresql : Variable settings depend on machine configuration
json_udf : conflicts with the server JSON functions
json_udf_bin : conflicts with the server JSON functions

2
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

19
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)
{

1553
strings/json_lib.c
File diff suppressed because it is too large
View File

23
unittest/json_lib/CMakeLists.txt

@ -0,0 +1,23 @@
# Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
#
# 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_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/sql
${CMAKE_SOURCE_DIR}/regex
${CMAKE_SOURCE_DIR}/extra/yassl/include
${CMAKE_SOURCE_DIR}/unittest/mytap)
#
MY_ADD_TESTS(json_lib LINK_LIBRARIES strings dbug)

186
unittest/json_lib/json_lib-t.c

@ -0,0 +1,186 @@
/* Copyright (c) 2016, MariaDB Corp. All rights reserved.
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_config.h"
#include "config.h"
#include <tap.h>
#include <my_global.h>
#include <my_sys.h>
#include <json_lib.h>
/* The character set used for JSON all over this test. */
static CHARSET_INFO *ci;
#define s_e(j) j, j + strlen((const char *) j)
struct st_parse_result
{
int n_keys;
int n_values;
int n_arrays;
int n_objects;
int n_steps;
int error;
uchar keyname_csum;
};
static void parse_json(const uchar *j, struct st_parse_result *result)
{
json_engine_t je;
bzero(result, sizeof(*result));
if (json_scan_start(&je, ci, s_e(j)))
return;
do
{
result->n_steps++;
switch (je.state)
{
case JST_KEY:
result->n_keys++;
while (json_read_keyname_chr(&je) == 0)
{
result->keyname_csum^= je.s.c_next;
}
if (je.s.error)
return;
break;
case JST_VALUE:
result->n_values++;
break;
case JST_OBJ_START:
result->n_objects++;
break;
case JST_ARRAY_START:
result->n_arrays++;
break;
default:
break;
};
} while (json_scan_next(&je) == 0);
result->error= je.s.error;
}
static const uchar *js0= (const uchar *) "123";
static const uchar *js1= (const uchar *) "[123, \"text\"]";
static const uchar *js2= (const uchar *) "{\"key1\":123, \"key2\":\"text\"}";
static const uchar *js3= (const uchar *) "{\"key1\":{\"ikey1\":321},"
"\"key2\":[\"text\", 321]}";
/*
Test json_lib functions to parse JSON.
*/
static void
test_json_parsing()
{
struct st_parse_result r;
parse_json(js0, &r);
ok(r.n_steps == 1 && r.n_values == 1, "simple value");
parse_json(js1, &r);
ok(r.n_steps == 5 && r.n_values == 3 && r.n_arrays == 1, "array");
parse_json(js2, &r);
ok(r.n_steps == 5 && r.n_keys == 2 && r.n_objects == 1 && r.keyname_csum == 3,
"object");
parse_json(js3, &r);
ok(r.n_steps == 12 && r.n_keys == 3 && r.n_objects == 2 &&
r.n_arrays == 1 && r.keyname_csum == 44,
"complex json");
}
static const uchar *p0= (const uchar *) "$.key1[12].*[*]";
/*
Test json_lib functions to parse JSON path.
*/
static void
test_path_parsing()
{
json_path_t p;
if (json_path_setup(&p, ci, s_e(p0)))
return;
ok(p.last_step - p.steps == 4 &&
p.steps[0].type == JSON_PATH_ARRAY && p.steps[0].wild == 1 &&
p.steps[1].type == JSON_PATH_KEY && p.steps[1].wild == 0 &&
p.steps[2].type == JSON_PATH_ARRAY && p.steps[2].n_item == 12 &&
p.steps[3].type == JSON_PATH_KEY && p.steps[3].wild == 1 &&
p.steps[4].type == JSON_PATH_ARRAY && p.steps[4].wild == 1,
"path");
}
static const uchar *fj0=(const uchar *) "[{\"k0\":123, \"k1\":123, \"k1\":123},"
" {\"k3\":321, \"k4\":\"text\"},"
" {\"k1\":[\"text\"], \"k2\":123}]";
static const uchar *fp0= (const uchar *) "$[*].k1";
/*
Test json_lib functions to search through JSON.
*/
static void
test_search()
{
json_engine_t je;
json_path_t p;
json_path_step_t *cur_step;
int n_matches, scal_values;
uint array_counters[JSON_DEPTH_LIMIT];
if (json_scan_start(&je, ci, s_e(fj0)) ||
json_path_setup(&p, ci, s_e(fp0)))
return;
cur_step= p.steps;
n_matches= scal_values= 0;
while (json_find_path(&je, &p, &cur_step, array_counters) == 0)
{
n_matches++;
if (json_read_value(&je))
return;
if (json_value_scalar(&je))
{
scal_values++;
if (json_scan_next(&je))
return;
}
else
{
if (json_skip_level(&je) || json_scan_next(&je))
return;
}
}
ok(n_matches == 3, "search");
}
int main()
{
ci= &my_charset_utf8_general_ci;
plan(6);
diag("Testing json_lib functions.");
test_json_parsing();
test_path_parsing();
test_search();
return exit_status();
}
Loading…
Cancel
Save