From d26b9f670d30861885c6a8caebbd77fe4e9eda3e Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 13 Dec 2016 12:39:48 +0400 Subject: [PATCH] MDEV-11470 JSON_KEYS accepts arguments in invalid format. Now JSON functions return warnings if arguments are invalid. --- include/json_lib.h | 1 + mysql-test/r/func_json.result | 15 +- mysql-test/t/func_json.test | 1 + sql/item_jsonfunc.cc | 429 ++++++++++++++++++++++++---------- sql/share/errmsg-utf8.txt | 24 ++ strings/json_lib.c | 12 +- 6 files changed, 351 insertions(+), 131 deletions(-) diff --git a/include/json_lib.h b/include/json_lib.h index abef3e60db6..b26d865fd36 100644 --- a/include/json_lib.h +++ b/include/json_lib.h @@ -102,6 +102,7 @@ typedef struct st_json_path_t json_path_step_t *last_step; /* Points to the last step. */ int mode_strict; /* TRUE if the path specified as 'strict' */ + enum json_path_step_types types_used; /* The '|' of all step's 'type'-s */ } json_path_t; diff --git a/mysql-test/r/func_json.result b/mysql-test/r/func_json.result index c972057e37c..99e1e95372f 100644 --- a/mysql-test/r/func_json.result +++ b/mysql-test/r/func_json.result @@ -92,7 +92,9 @@ select json_contains('[1]', '[1]', '$', '$[0]'); ERROR 42000: Incorrect parameter count in the call to native function 'json_contains' select json_contains('', '', '$'); json_contains('', '', '$') -0 +NULL +Warnings: +Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_contains' select json_contains('null', 'null', '$'); json_contains('null', 'null', '$') 1 @@ -276,6 +278,8 @@ ERROR 42000: Incorrect parameter count in the call to native function 'json_merg select json_merge('string', 123); json_merge('string', 123) NULL +Warnings: +Warning 4038 Syntax error in JSON text in argument 1 to function 'json_merge' at position 1 select json_merge('"string"', 123); json_merge('"string"', 123) ["string", 123] @@ -294,6 +298,8 @@ NULL select json_merge('a','b'); json_merge('a','b') NULL +Warnings: +Warning 4038 Syntax error in JSON text in argument 1 to function 'json_merge' at position 1 select json_merge('{"a":"b"}','{"c":"d"}'); json_merge('{"a":"b"}','{"c":"d"}') {"a":"b", "c":"d"} @@ -321,6 +327,11 @@ json_keys('{"a":{"c":1, "d":2}, "b":2}', "$.a") select json_keys('{"a":{"c":1, "d":2}, "b":2}', "$.b"); json_keys('{"a":{"c":1, "d":2}, "b":2}', "$.b") NULL +select json_keys('foo'); +json_keys('foo') +NULL +Warnings: +Warning 4038 Syntax error in JSON text in argument 1 to function 'json_keys' at position 1 SET @j = '["abc", [{"k": "10"}, "def"], {"x":"abc"}, {"y":"bcd"}]'; select json_search(@j, 'one', 'abc'); json_search(@j, 'one', 'abc') @@ -385,6 +396,8 @@ json_depth('[[[1,2,3],"s"], {}, []]') select json_length(''); json_length('') NULL +Warnings: +Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_length' select json_length('{}'); json_length('{}') 0 diff --git a/mysql-test/t/func_json.test b/mysql-test/t/func_json.test index d3d75fa3913..8685d82b635 100644 --- a/mysql-test/t/func_json.test +++ b/mysql-test/t/func_json.test @@ -129,6 +129,7 @@ select json_type('123.12'); select json_keys('{"a":{"c":1, "d":2}, "b":2}'); select json_keys('{"a":{"c":1, "d":2}, "b":2}', "$.a"); select json_keys('{"a":{"c":1, "d":2}, "b":2}', "$.b"); +select json_keys('foo'); SET @j = '["abc", [{"k": "10"}, "def"], {"x":"abc"}, {"y":"bcd"}]'; select json_search(@j, 'one', 'abc'); diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index e3fa34cd72c..7bf2283dd53 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -113,12 +113,131 @@ static int st_append_escaped(String *s, const String *a) } +#define report_json_error(js, je, n_param) \ + report_json_error_ex(js, je, func_name(), n_param, \ + Sql_condition::WARN_LEVEL_WARN) + +static void report_json_error_ex(String *js, json_engine_t *je, + const char *fname, int n_param, + Sql_condition::enum_warning_level lv) +{ + THD *thd= current_thd; + int position= (const char *) je->s.c_str - js->ptr(); + uint code; + + n_param++; + + switch (je->s.error) + { + case JE_BAD_CHR: + code= ER_JSON_BAD_CHR; + break; + + case JE_NOT_JSON_CHR: + code= ER_JSON_NOT_JSON_CHR; + break; + + case JE_EOS: + code= ER_JSON_EOS; + break; + + case JE_SYN: + case JE_STRING_CONST: + code= ER_JSON_SYNTAX; + break; + + case JE_ESCAPING: + code= ER_JSON_ESCAPING; + break; + + case JE_DEPTH: + code= ER_JSON_DEPTH; + push_warning_printf(thd, lv, code, ER_THD(thd, code), JSON_DEPTH_LIMIT, + n_param, fname, position); + return; + + default: + return; + } + + push_warning_printf(thd, lv, code, ER_THD(thd, code), + n_param, fname, position); +} + + + +#define NO_WILDCARD_ALLOWED 1 +#define SHOULD_END_WITH_ARRAY 2 + +#define report_path_error(js, je, n_param) \ + report_path_error_ex(js, je, func_name(), n_param,\ + Sql_condition::WARN_LEVEL_WARN) + +static void report_path_error_ex(String *ps, json_path_t *p, + const char *fname, int n_param, + Sql_condition::enum_warning_level lv) +{ + THD *thd= current_thd; + int position= (const char *) p->s.c_str - ps->ptr() + 1; + uint code; + + n_param++; + + switch (p->s.error) + { + case JE_BAD_CHR: + case JE_NOT_JSON_CHR: + case JE_SYN: + code= ER_JSON_PATH_SYNTAX; + break; + + case JE_EOS: + code= ER_JSON_PATH_EOS; + break; + + case JE_DEPTH: + code= ER_JSON_PATH_DEPTH; + push_warning_printf(thd, lv, code, ER_THD(thd, code), + JSON_DEPTH_LIMIT, n_param, fname, position); + return; + + case NO_WILDCARD_ALLOWED: + code= ER_JSON_PATH_NO_WILDCARD; + break; + + default: + return; + } + push_warning_printf(thd, lv, code, ER_THD(thd, code), + n_param, fname, position); +} + + + +/* + Checks if the path has '.*' '[*]' or '**' constructions + and sets the NO_WILDCARD_ALLOWED error if the case. +*/ +static int path_setup_nwc(json_path_t *p, CHARSET_INFO *i_cs, + const uchar *str, const uchar *end) +{ + if (!json_path_setup(p, i_cs, str, end)) + { + if ((p->types_used & (JSON_PATH_WILD | JSON_PATH_DOUBLE_WILD)) == 0) + return 0; + p->s.error= NO_WILDCARD_ALLOWED; + } + + return 1; +} + + 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) + if ((null_value= args[0]->null_value)) return 0; json_scan_start(&je, js->charset(), (const uchar *) js->ptr(), @@ -339,6 +458,8 @@ String *Item_func_json_unquote::val_str(String *str) json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); + je.value_type= (enum json_value_types) -1; /* To report errors right. */ + if (json_read_value(&je)) goto error; @@ -359,6 +480,8 @@ String *Item_func_json_unquote::val_str(String *str) return str; error: + if (je.value_type == JSON_VALUE_STRING) + report_json_error(js, &je, 0); /* We just return the argument's value in the case of error. */ return js; } @@ -449,12 +572,12 @@ String *Item_func_json_extract::val_str(String *str) 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; + goto return_null; c_path->parsed= c_path->constant; } if (args[n_arg]->null_value) - goto error; + goto return_null; json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); @@ -513,8 +636,7 @@ String *Item_func_json_extract::val_str(String *str) if (first_value == NULL) { /* Nothing was found. */ - null_value= 1; - return 0; + goto return_null; } if (multiple_values_found ? @@ -525,6 +647,8 @@ String *Item_func_json_extract::val_str(String *str) return str; error: + report_json_error(js, &je, 0); +return_null: /* TODO: launch error messages. */ null_value= 1; return 0; @@ -778,24 +902,35 @@ longlong Item_func_json_contains::val_int() { String *s_p= args[2]->val_str(&tmp_path); if (s_p && - json_path_setup(&path.p,s_p->charset(),(const uchar *) s_p->ptr(), - (const uchar *) s_p->end())) - goto error; + path_setup_nwc(&path.p,s_p->charset(),(const uchar *) s_p->ptr(), + (const uchar *) s_p->end())) + { + report_path_error(s_p, &path.p, 2); + goto return_null; + } path.parsed= path.constant; } if (args[2]->null_value) - goto error; + goto return_null; path.cur_step= path.p.steps; if (json_find_path(&je, &path.p, &path.cur_step, array_counters)) - goto error; + { + if (je.s.error) + { + ve.s.error= 0; + goto error; + } + + return FALSE; + } } json_scan_start(&ve, val->charset(),(const uchar *) val->ptr(), (const uchar *) val->end()); if (json_read_value(&je) || json_read_value(&ve)) - return FALSE; + goto error; result= check_contains(&je, &ve); if (je.s.error || ve.s.error) @@ -804,6 +939,11 @@ longlong Item_func_json_contains::val_int() return result; error: + if (je.s.error) + report_json_error(js, &je, 0); + if (ve.s.error) + report_json_error(val, &ve, 1); +return_null: null_value= 1; return 0; } @@ -837,7 +977,7 @@ void Item_func_json_contains_path::cleanup() } -static int parse_one_or_all(Item *ooa_arg, +static int parse_one_or_all(const Item_func *f, Item *ooa_arg, bool *ooa_parsed, bool ooa_constant, bool *mode_one) { if (!*ooa_parsed) @@ -846,12 +986,20 @@ static int parse_one_or_all(Item *ooa_arg, String *res, tmp(buff, sizeof(buff), &my_charset_bin); if ((res= ooa_arg->val_str(&tmp)) == NULL) return TRUE; + *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())) + { + THD *thd= current_thd; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_JSON_ONE_OR_ALL, ER_THD(thd, ER_JSON_ONE_OR_ALL), + f->func_name()); + *mode_one= TRUE; return TRUE; + } } *ooa_parsed= ooa_constant; } @@ -869,8 +1017,8 @@ longlong Item_func_json_contains_path::val_int() if ((null_value= args[0]->null_value)) return 0; - if (parse_one_or_all(args[1], &ooa_parsed, ooa_constant, &mode_one)) - goto error; + if (parse_one_or_all(this, args[1], &ooa_parsed, ooa_constant, &mode_one)) + goto return_null; result= !mode_one; for (n_arg=2; n_arg < arg_count; n_arg++) @@ -881,14 +1029,17 @@ longlong Item_func_json_contains_path::val_int() { 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; + path_setup_nwc(&c_path->p,s_p->charset(),(const uchar *) s_p->ptr(), + (const uchar *) s_p->ptr() + s_p->length())) + { + report_path_error(s_p, &c_path->p, n_arg-2); + goto return_null; + } c_path->parsed= c_path->constant; } if (args[n_arg]->null_value) - goto error; + goto return_null; json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); @@ -898,7 +1049,7 @@ longlong Item_func_json_contains_path::val_int() { /* Path wasn't found. */ if (je.s.error) - goto error; + goto js_error; if (!mode_one) { @@ -916,7 +1067,9 @@ longlong Item_func_json_contains_path::val_int() return result; -error: +js_error: + report_json_error(js, &je, 0); +return_null: null_value= 1; return 0; } @@ -1014,7 +1167,7 @@ String *Item_func_json_array::val_str(String *str) str->length(0); if (str->append("[", 1) || - ((arg_count > 0) && append_json_value(str, args[0],&tmp_val))) + ((arg_count > 0) && append_json_value(str, args[0], &tmp_val))) goto err_return; for (n_arg=1; n_arg < arg_count; n_arg++) @@ -1074,16 +1227,16 @@ String *Item_func_json_array_append::val_str(String *str) { 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; + path_setup_nwc(&c_path->p,s_p->charset(),(const uchar *) s_p->ptr(), + (const uchar *) s_p->ptr() + s_p->length())) + { + report_path_error(s_p, &c_path->p, n_arg); + goto return_null; + } c_path->parsed= c_path->constant; } if (args[n_arg]->null_value) - { - null_value= 1; - return 0; - } + goto return_null; json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); @@ -1093,33 +1246,33 @@ String *Item_func_json_array_append::val_str(String *str) if (json_find_path(&je, &c_path->p, &c_path->cur_step, array_counters)) { if (je.s.error) - goto error; - null_value= 1; - return 0; + goto js_error; + + goto return_null; } if (json_read_value(&je)) - goto error; + goto js_error; str->length(0); str->set_charset(js->charset()); if (str->reserve(js->length() + 8, 1024)) - goto error; /* Out of memory. */ + goto return_null; /* Out of memory. */ if (je.value_type == JSON_VALUE_ARRAY) { if (json_skip_level(&je)) - goto error; + goto js_error; 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()); str->append(", ", 2); if (append_json_value(str, args[n_arg+1], &tmp_val)) - goto error; /* Out of memory. */ + goto return_null; /* Out of memory. */ if (str->reserve(str_rest_len, 1024)) - goto error; /* Out of memory. */ + goto return_null; /* Out of memory. */ str->q_append((const char *) ar_end, str_rest_len); } else @@ -1133,7 +1286,7 @@ String *Item_func_json_array_append::val_str(String *str) if (je.value_type == JSON_VALUE_OBJECT) { if (json_skip_level(&je)) - goto error; + goto js_error; c_to= je.s.c_str; } else @@ -1146,7 +1299,7 @@ String *Item_func_json_array_append::val_str(String *str) str->append("]", 1) || str->append((const char *) je.s.c_str, js->end() - (const char *) je.s.c_str)) - goto error; + goto return_null; /* Out of memory. */ } { /* Swap str and js. */ @@ -1165,7 +1318,10 @@ String *Item_func_json_array_append::val_str(String *str) return js; -error: +js_error: + report_json_error(js, &je, 0); + +return_null: null_value= 1; return 0; } @@ -1193,16 +1349,23 @@ String *Item_func_json_array_insert::val_str(String *str) { 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()) || + (path_setup_nwc(&c_path->p,s_p->charset(),(const uchar *) s_p->ptr(), + (const uchar *) s_p->ptr() + s_p->length()) || c_path->p.last_step - 1 < c_path->p.steps || c_path->p.last_step->type != JSON_PATH_ARRAY)) - goto error; + { + if (c_path->p.s.error == 0) + c_path->p.s.error= SHOULD_END_WITH_ARRAY; + + report_path_error(s_p, &c_path->p, n_arg); + + goto return_null; + } c_path->parsed= c_path->constant; c_path->p.last_step--; } if (args[n_arg]->null_value) - goto null_return; + goto return_null; json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); @@ -1212,19 +1375,19 @@ String *Item_func_json_array_insert::val_str(String *str) if (json_find_path(&je, &c_path->p, &c_path->cur_step, array_counters)) { if (je.s.error) - goto error; + goto js_error; /* Can't find the array to insert. */ - goto null_return; + continue; } if (json_read_value(&je)) - goto error; + goto js_error; if (je.value_type != JSON_VALUE_ARRAY) { /* Must be an array. */ - goto null_return; + continue; } item_pos= 0; @@ -1253,6 +1416,9 @@ String *Item_func_json_array_insert::val_str(String *str) } } + if (je.s.error) + goto js_error; + str->length(0); str->set_charset(js->charset()); if (!item_pos) @@ -1264,7 +1430,7 @@ String *Item_func_json_array_insert::val_str(String *str) append_json_value(str, args[n_arg+1], &tmp_val) || (je.state != JST_ARRAY_END && str->append(",", 1)) || append_simple(str, item_pos, js->end() - item_pos)) - goto error; /* Out of memory. */ + goto return_null; /* Out of memory. */ { /* Swap str and js. */ @@ -1283,8 +1449,9 @@ String *Item_func_json_array_insert::val_str(String *str) return js; -null_return: -error: +js_error: + report_json_error(js, &je, 0); +return_null: null_value= 1; return 0; } @@ -1327,17 +1494,17 @@ String *Item_func_json_merge::val_str(String *str) { DBUG_ASSERT(fixed == 1); json_engine_t je1, je2; - String *js1= args[0]->val_str(&tmp_js1); + String *js1= args[0]->val_str(&tmp_js1), *js2; uint n_arg; if (args[0]->null_value) - goto error_return; + goto null_return; for (n_arg=1; n_arg < arg_count; n_arg++) { - String *js2= args[n_arg]->val_str(&tmp_js2); + js2= args[n_arg]->val_str(&tmp_js2); if (args[n_arg]->null_value) - goto error_return; + goto null_return; json_scan_start(&je1, js1->charset(),(const uchar *) js1->ptr(), (const uchar *) js1->ptr() + js1->length()); @@ -1394,6 +1561,11 @@ String *Item_func_json_merge::val_str(String *str) return js1; error_return: + if (je1.s.error) + report_json_error(js1, &je1, 0); + if (je2.s.error) + report_json_error(js2, &je2, n_arg); +null_return: null_value= 1; return NULL; } @@ -1418,13 +1590,12 @@ longlong Item_func_json_length::val_int() length++; } while (json_scan_next(&je) == 0); - if (je.s.error) - { - null_value= 1; - return 0; - } - - return length - 1; + if (!je.s.error) + return length - 1; + + report_json_error(js, &je, 0); + null_value= 1; + return 0; } @@ -1470,13 +1641,12 @@ longlong Item_func_json_depth::val_int() } } while (json_scan_next(&je) == 0); - if (je.s.error) - { - null_value= 1; - return 0; - } - - return depth; + if (!je.s.error) + return depth; + + report_json_error(js, &je, 0); + null_value= 1; + return 0; } @@ -1530,6 +1700,7 @@ String *Item_func_json_type::val_str(String *str) return str; error: + report_json_error(js, &je, 0); null_value= 1; return 0; } @@ -1580,10 +1751,13 @@ String *Item_func_json_insert::val_str(String *str) String *s_p= args[n_arg]->val_str(tmp_paths+n_path); if (s_p) { - if (json_path_setup(&c_path->p,s_p->charset(), - (const uchar *) s_p->ptr(), - (const uchar *) s_p->ptr() + s_p->length())) - goto error; + if (path_setup_nwc(&c_path->p,s_p->charset(), + (const uchar *) s_p->ptr(), + (const uchar *) s_p->ptr() + s_p->length())) + { + report_path_error(s_p, &c_path->p, n_arg); + goto return_null; + } /* We search to the last step. */ c_path->p.last_step--; @@ -1591,10 +1765,7 @@ String *Item_func_json_insert::val_str(String *str) c_path->parsed= c_path->constant; } if (args[n_arg]->null_value) - { - null_value= 1; - return 0; - } + goto return_null; json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); @@ -1605,11 +1776,11 @@ String *Item_func_json_insert::val_str(String *str) json_find_path(&je, &c_path->p, &c_path->cur_step, array_counters)) { if (je.s.error) - goto error; + goto js_error; } if (json_read_value(&je)) - goto error; + goto js_error; lp= c_path->p.last_step+1; if (lp->type & JSON_PATH_ARRAY) @@ -1626,12 +1797,12 @@ String *Item_func_json_insert::val_str(String *str) /* Wrap the value as an array. */ if (append_simple(str, js->ptr(), (const char *) v_from - js->ptr()) || str->append("[", 1)) - goto error; /* Out of memory. */ + goto js_error; /* Out of memory. */ if (je.value_type == JSON_VALUE_OBJECT) { if (json_skip_level(&je)) - goto error; + goto js_error; } if (append_simple(str, v_from, je.s.c_str - v_from) || @@ -1639,7 +1810,7 @@ String *Item_func_json_insert::val_str(String *str) append_json_value(str, args[n_arg+1], &tmp_val) || str->append("]", 1) || append_simple(str, je.s.c_str, js->end()-(const char *) je.s.c_str)) - goto error; /* Out of memory. */ + goto js_error; /* Out of memory. */ goto continue_point; } @@ -1653,7 +1824,7 @@ String *Item_func_json_insert::val_str(String *str) goto v_found; n_item++; if (json_skip_array_item(&je)) - goto error; + goto js_error; break; default: break; @@ -1661,7 +1832,7 @@ String *Item_func_json_insert::val_str(String *str) } if (je.s.error) - goto error; + goto js_error; if (!mode_insert) continue; @@ -1672,7 +1843,7 @@ String *Item_func_json_insert::val_str(String *str) str->append(", ", 2) || append_json_value(str, args[n_arg+1], &tmp_val) || append_simple(str, v_to, js->end() - v_to)) - goto error; /* Out of memory. */ + goto js_error; /* Out of memory. */ } else /*JSON_PATH_KEY*/ { @@ -1688,7 +1859,7 @@ String *Item_func_json_insert::val_str(String *str) if (json_key_matches(&je, &key_name)) goto v_found; if (json_skip_key(&je)) - goto error; + goto js_error; break; default: break; @@ -1696,7 +1867,7 @@ String *Item_func_json_insert::val_str(String *str) } if (je.s.error) - goto error; + goto js_error; if (!mode_insert) continue; @@ -1709,7 +1880,7 @@ String *Item_func_json_insert::val_str(String *str) str->append("\":", 2) || append_json_value(str, args[n_arg+1], &tmp_val) || append_simple(str, v_to, js->end() - v_to)) - goto error; /* Out of memory. */ + goto js_error; /* Out of memory. */ } goto continue_point; @@ -1720,20 +1891,20 @@ v_found: continue; if (json_read_value(&je)) - goto error; + goto js_error; v_to= (const char *) je.value_begin; str->length(0); if (!json_value_scalar(&je)) { if (json_skip_level(&je)) - goto error; + goto js_error; } if (append_simple(str, js->ptr(), v_to - js->ptr()) || append_json_value(str, args[n_arg+1], &tmp_val) || append_simple(str, je.s.c_str, js->end()-(const char *) je.s.c_str)) - goto error; /* Out of memory. */ + goto js_error; /* Out of memory. */ continue_point: { /* Swap str and js. */ @@ -1752,7 +1923,9 @@ continue_point: return js; -error: +js_error: + report_json_error(js, &je, 0); +return_null: null_value= 1; return 0; } @@ -1795,10 +1968,13 @@ String *Item_func_json_remove::val_str(String *str) String *s_p= args[n_arg]->val_str(tmp_paths+n_path); if (s_p) { - if (json_path_setup(&c_path->p,s_p->charset(), - (const uchar *) s_p->ptr(), - (const uchar *) s_p->ptr() + s_p->length())) - goto error; + if (path_setup_nwc(&c_path->p,s_p->charset(), + (const uchar *) s_p->ptr(), + (const uchar *) s_p->ptr() + s_p->length())) + { + report_path_error(s_p, &c_path->p, n_arg); + goto null_return; + } /* We search to the last step. */ c_path->p.last_step--; @@ -1808,10 +1984,7 @@ String *Item_func_json_remove::val_str(String *str) c_path->parsed= c_path->constant; } if (args[n_arg]->null_value) - { - null_value= 1; - return 0; - } + goto null_return; json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); @@ -1821,11 +1994,11 @@ String *Item_func_json_remove::val_str(String *str) if (json_find_path(&je, &c_path->p, &c_path->cur_step, array_counters)) { if (je.s.error) - goto error; + goto js_error; } if (json_read_value(&je)) - goto error; + goto js_error; lp= c_path->p.last_step+1; if (lp->type & JSON_PATH_ARRAY) @@ -1846,7 +2019,7 @@ String *Item_func_json_remove::val_str(String *str) } n_item++; if (json_skip_array_item(&je)) - goto error; + goto js_error; break; default: break; @@ -1854,7 +2027,7 @@ String *Item_func_json_remove::val_str(String *str) } if (je.s.error) - goto error; + goto js_error; continue; } @@ -1875,7 +2048,7 @@ String *Item_func_json_remove::val_str(String *str) goto v_found; if (json_skip_key(&je)) - goto error; + goto js_error; rem_start= (const char *) je.s.c_str; n_item++; @@ -1886,7 +2059,7 @@ String *Item_func_json_remove::val_str(String *str) } if (je.s.error) - goto error; + goto js_error; continue; } @@ -1894,7 +2067,7 @@ String *Item_func_json_remove::val_str(String *str) v_found: if (json_skip_key(&je) || json_scan_next(&je)) - goto error; + goto js_error; rem_end= (je.state == JST_VALUE && n_item == 0) ? (const char *) je.s.c_str : (const char *) (je.s.c_str - je.sav_c_len); @@ -1903,7 +2076,7 @@ v_found: if (append_simple(str, js->ptr(), rem_start - js->ptr()) || append_simple(str, rem_end, js->end() - rem_end)) - goto error; /* Out of memory. */ + goto js_error; /* Out of memory. */ { /* Swap str and js. */ @@ -1922,8 +2095,9 @@ v_found: return js; +js_error: + report_json_error(js, &je, 0); null_return: -error: null_value= 1; return 0; } @@ -1958,9 +2132,12 @@ String *Item_func_json_keys::val_str(String *str) { 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_setup_nwc(&path.p, s_p->charset(), (const uchar *) s_p->ptr(), + (const uchar *) s_p->ptr() + s_p->length())) + { + report_path_error(s_p, &path.p, 1); + goto null_return; + } path.parsed= path.constant; } @@ -2024,8 +2201,9 @@ skip_search: null_value= 0; return str; -null_return: err_return: + report_json_error(js, &je, 0); +null_return: null_value= 1; return 0; } @@ -2173,10 +2351,7 @@ String *Item_func_json_search::val_str(String *str) if (args[0]->null_value || args[2]->null_value) goto null_return; - if (parse_one_or_all(args[1], &ooa_parsed, ooa_constant, &mode_one)) - goto error; - - if (args[1]->null_value) + if (parse_one_or_all(this, args[1], &ooa_parsed, ooa_constant, &mode_one)) goto null_return; n_path_found= 0; @@ -2192,7 +2367,10 @@ String *Item_func_json_search::val_str(String *str) 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; + { + report_path_error(s_p, &c_path->p, n_arg); + goto null_return; + } c_path->parsed= c_path->constant; } if (args[n_arg]->null_value) @@ -2215,12 +2393,12 @@ String *Item_func_json_search::val_str(String *str) while (json_read_keyname_chr(&je) == 0) p.last_step->key_end= je.s.c_str; if (je.s.error) - goto error; + goto js_error; /* Now we have je.state == JST_VALUE, so let's handle it. */ case JST_VALUE: if (json_read_value(&je)) - goto error; + goto js_error; if (json_value_scalar(&je)) { if ((arg_count < 5 || path_ok(paths, n_arg - 4, &p)) && @@ -2238,10 +2416,10 @@ String *Item_func_json_search::val_str(String *str) { if (str->append("[", 1) || append_json_path(str, &sav_path)) - goto error; + goto js_error; } if (str->append(", ", 2) || append_json_path(str, &p)) - goto error; + goto js_error; } if (mode_one) @@ -2270,7 +2448,7 @@ String *Item_func_json_search::val_str(String *str) } while (json_scan_next(&je) == 0); if (je.s.error) - goto error; + goto js_error; end: if (n_path_found == 0) @@ -2278,20 +2456,21 @@ end: if (n_path_found == 1) { if (append_json_path(str, &sav_path)) - goto error; + goto js_error; } else { if (str->append("]", 1)) - goto error; + goto js_error; } null_value= 0; return str; +js_error: + report_json_error(js, &je, 0); null_return: -error: /* TODO: launch error messages. */ null_value= 1; return 0; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 584cda9cb95..aa11b55da0d 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7414,3 +7414,27 @@ ER_BINLOG_NON_SUPPORTED_BULK eng "Only row based replication supported for bulk operations" ER_BINLOG_UNCOMPRESS_ERROR eng "Uncompress the compressed binlog failed" +ER_JSON_BAD_CHR + eng "Broken JSON string in argument %d to function '%s' at position %d" +ER_JSON_NOT_JSON_CHR + eng "Character disallowd in JSON in argument %d to function '%s' at position %d" +ER_JSON_EOS + eng "Unexpected end of JSON text in argument %d to function '%s'" +ER_JSON_SYNTAX + eng "Syntax error in JSON text in argument %d to function '%s' at position %d" +ER_JSON_ESCAPING + eng "Incorrect escaping in JSON text in argument %d to function '%s' at position %d" +ER_JSON_DEPTH + eng "Limit of %d on JSON nested strucures depth is reached in argument %d to function '%s' at position %d" +ER_JSON_PATH_EOS + eng "Unexpected end of JSON path in argument %d to function '%s'" +ER_JSON_PATH_SYNTAX + eng "Syntax error in JSON path in argument %d to function '%s' at position %d" +ER_JSON_PATH_DEPTH + eng "Limit of %d on JSON path depth is reached in argument %d to function '%s' at position %d" +ER_JSON_PATH_NO_WILDCARD + eng "Wildcards in JSON path not allowed in argument %d to function '%s'" +ER_JSON_PATH_ARRAY + eng "JSON path should end with an array identifier in argument %d to function '%s'" +ER_JSON_ONE_OR_ALL + eng "Argument 2 to function '%s' must be "one" or "all"." diff --git a/strings/json_lib.c b/strings/json_lib.c index 015ce1f39f8..acca7eb0739 100644 --- a/strings/json_lib.c +++ b/strings/json_lib.c @@ -392,12 +392,12 @@ static int v_string(json_engine_t *j) static int read_strn(json_engine_t *j) { j->value= j->s.c_str; + j->value_type= JSON_VALUE_STRING; 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; } @@ -556,9 +556,9 @@ static int skip_string_verbatim(json_string_t *s, const char *str) s->c_str+= c_len; continue; } - return JE_SYN; + return s->error= JE_SYN; } - return json_eos(s) ? JE_EOS : JE_BAD_CHR; + return s->error= json_eos(s) ? JE_EOS : JE_BAD_CHR; } return 0; @@ -1078,6 +1078,7 @@ int json_path_setup(json_path_t *p, p->steps[0].type= JSON_PATH_ARRAY_WILD; p->last_step= p->steps; p->mode_strict= FALSE; + p->types_used= JSON_PATH_KEY_NULL; do { @@ -1109,6 +1110,7 @@ int json_path_setup(json_path_t *p, if (p->last_step->type & JSON_PATH_DOUBLE_WILD) return p->s.error= JE_SYN; p->last_step->type|= JSON_PATH_WILD; + p->types_used|= JSON_PATH_WILD; continue; case PS_INT: p->last_step->n_item*= 10; @@ -1122,7 +1124,7 @@ int json_path_setup(json_path_t *p, p->last_step++; if (p->last_step - p->steps >= JSON_DEPTH_LIMIT) return p->s.error= JE_DEPTH; - p->last_step->type= JSON_PATH_KEY | double_wildcard; + p->types_used|= p->last_step->type= JSON_PATH_KEY | double_wildcard; double_wildcard= JSON_PATH_KEY_NULL; p->last_step->key= p->s.c_str; continue; @@ -1134,7 +1136,7 @@ int json_path_setup(json_path_t *p, p->last_step++; if (p->last_step - p->steps >= JSON_DEPTH_LIMIT) return p->s.error= JE_DEPTH; - p->last_step->type= JSON_PATH_ARRAY | double_wildcard; + p->types_used|= p->last_step->type= JSON_PATH_ARRAY | double_wildcard; double_wildcard= JSON_PATH_KEY_NULL; p->last_step->n_item= 0; continue;