diff options
author | Alexey Botchkov <holyfoot@askmonty.org> | 2016-11-15 17:04:31 +0400 |
---|---|---|
committer | Alexey Botchkov <holyfoot@askmonty.org> | 2016-11-15 17:04:31 +0400 |
commit | ebe5ebba165863a134c145b1a3709b70925d5100 (patch) | |
tree | 049feb2aea5d44134eabb9b451b03d26bad1b707 /sql | |
parent | 1122c1f0c219a01cdbe5c760b2a846bba80b5949 (diff) | |
download | mariadb-git-ebe5ebba165863a134c145b1a3709b70925d5100.tar.gz |
MDEV-9143 JSON_xxx functions.
The rest of mysql/json functions implemented.
CAST AS JSON implemented.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item.h | 12 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 42 | ||||
-rw-r--r-- | sql/item_create.cc | 302 | ||||
-rw-r--r-- | sql/item_func.h | 2 | ||||
-rw-r--r-- | sql/item_jsonfunc.cc | 985 | ||||
-rw-r--r-- | sql/item_jsonfunc.h | 129 | ||||
-rw-r--r-- | sql/lex.h | 1 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 7 |
8 files changed, 1447 insertions, 33 deletions
diff --git a/sql/item.h b/sql/item.h index d0d845711f7..20f3be17cce 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1457,6 +1457,7 @@ public: virtual void set_result_field(Field *field) {} virtual bool is_result_field() { return 0; } virtual bool is_bool_type() { return false; } + virtual bool is_json_type() { return false; } /* This is to handle printing of default values */ virtual bool need_parentheses_in_default() { return false; } virtual void save_in_result_field(bool no_conversions) {} @@ -5763,4 +5764,15 @@ public: void close() {} }; + +/* + It's used in ::fix_fields() methods of LIKE and JSON_SEARCH + functions to handle the ESCAPE parameter. + This parameter is quite non-standard so the specific function. +*/ +bool fix_escape_item(THD *thd, Item *escape_item, String *tmp_str, + bool escape_used_in_parsing, CHARSET_INFO *cmp_cs, + int *escape); + + #endif /* SQL_ITEM_INCLUDED */ diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 193af8631ba..e10a4ba573f 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -5124,13 +5124,10 @@ bool Item_func_like::with_sargable_pattern() const } -bool Item_func_like::fix_fields(THD *thd, Item **ref) +bool fix_escape_item(THD *thd, Item *escape_item, String *tmp_str, + bool escape_used_in_parsing, CHARSET_INFO *cmp_cs, + int *escape) { - DBUG_ASSERT(fixed == 0); - if (Item_bool_func2::fix_fields(thd, ref) || - escape_item->fix_fields(thd, &escape_item)) - return TRUE; - if (!escape_item->const_during_execution()) { my_error(ER_WRONG_ARGUMENTS,MYF(0),"ESCAPE"); @@ -5140,7 +5137,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) if (escape_item->const_item()) { /* If we are on execution stage */ - String *escape_str= escape_item->val_str(&cmp_value1); + String *escape_str= escape_item->val_str(tmp_str); if (escape_str) { const char *escape_str_ptr= escape_str->ptr(); @@ -5153,7 +5150,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) return TRUE; } - if (use_mb(cmp_collation.collation)) + if (use_mb(cmp_cs)) { CHARSET_INFO *cs= escape_str->charset(); my_wc_t wc; @@ -5161,7 +5158,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) (const uchar*) escape_str_ptr, (const uchar*) escape_str_ptr + escape_str->length()); - escape= (int) (rc > 0 ? wc : '\\'); + *escape= (int) (rc > 0 ? wc : '\\'); } else { @@ -5170,25 +5167,40 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) code instead of Unicode code as "escape" argument. Convert to "cs" if charset of escape differs. */ - CHARSET_INFO *cs= cmp_collation.collation; uint32 unused; if (escape_str->needs_conversion(escape_str->length(), - escape_str->charset(), cs, &unused)) + escape_str->charset(),cmp_cs,&unused)) { char ch; uint errors; - uint32 cnvlen= copy_and_convert(&ch, 1, cs, escape_str_ptr, + uint32 cnvlen= copy_and_convert(&ch, 1, cmp_cs, escape_str_ptr, escape_str->length(), escape_str->charset(), &errors); - escape= cnvlen ? ch : '\\'; + *escape= cnvlen ? ch : '\\'; } else - escape= escape_str_ptr ? *escape_str_ptr : '\\'; + *escape= escape_str_ptr ? *escape_str_ptr : '\\'; } } else - escape= '\\'; + *escape= '\\'; + } + + return FALSE; +} + +bool Item_func_like::fix_fields(THD *thd, Item **ref) +{ + DBUG_ASSERT(fixed == 0); + if (Item_bool_func2::fix_fields(thd, ref) || + escape_item->fix_fields(thd, &escape_item) || + fix_escape_item(thd, escape_item, &cmp_value1, escape_used_in_parsing, + cmp_collation.collation, &escape)) + return TRUE; + + if (escape_item->const_item()) + { /* We could also do boyer-more for non-const items, but as we would have to recompute the tables for each row it's not worth it. diff --git a/sql/item_create.cc b/sql/item_create.cc index cf6f24eddb7..9a1a5c62b5b 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -1786,6 +1786,19 @@ protected: }; +class Create_func_json_keys: public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_json_keys s_singleton; + +protected: + Create_func_json_keys() {} + virtual ~Create_func_json_keys() {} +}; + + class Create_func_json_contains: public Create_native_func { public: @@ -1825,6 +1838,19 @@ protected: }; +class Create_func_json_search : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_json_search s_singleton; + +protected: + Create_func_json_search() {} + virtual ~Create_func_json_search() {} +}; + + class Create_func_json_array : public Create_native_func { public: @@ -1851,6 +1877,71 @@ protected: }; +class Create_func_json_array_insert : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_json_array_insert s_singleton; + +protected: + Create_func_json_array_insert() {} + virtual ~Create_func_json_array_insert() {} +}; + + +class Create_func_json_insert : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_json_insert s_singleton; + +protected: + Create_func_json_insert() {} + virtual ~Create_func_json_insert() {} +}; + + +class Create_func_json_set : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_json_set s_singleton; + +protected: + Create_func_json_set() {} + virtual ~Create_func_json_set() {} +}; + + +class Create_func_json_replace : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_json_replace s_singleton; + +protected: + Create_func_json_replace() {} + virtual ~Create_func_json_replace() {} +}; + + +class Create_func_json_remove : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + + static Create_func_json_remove s_singleton; + +protected: + Create_func_json_remove() {} + virtual ~Create_func_json_remove() {} +}; + + class Create_func_json_object : public Create_native_func { public: @@ -1903,6 +1994,19 @@ protected: }; +class Create_func_json_unquote : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_json_unquote s_singleton; + +protected: + Create_func_json_unquote() {} + virtual ~Create_func_json_unquote() {} +}; + + class Create_func_last_day : public Create_func_arg1 { public: @@ -4830,6 +4934,15 @@ Create_func_json_quote::create_1_arg(THD *thd, Item *arg1) } +Create_func_json_unquote Create_func_json_unquote::s_singleton; + +Item* +Create_func_json_unquote::create_1_arg(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_json_unquote(thd, arg1); +} + + Create_func_last_day Create_func_last_day::s_singleton; Item* @@ -4885,6 +4998,134 @@ Create_func_json_array_append::create_native(THD *thd, LEX_STRING name, } +Create_func_json_array_insert Create_func_json_array_insert::s_singleton; + +Item* +Create_func_json_array_insert::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_insert(thd, *item_list); + } + + return func; +} + + +Create_func_json_insert Create_func_json_insert::s_singleton; + +Item* +Create_func_json_insert::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_insert(true, false, + thd, *item_list); + } + + return func; +} + + +Create_func_json_set Create_func_json_set::s_singleton; + +Item* +Create_func_json_set::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_insert(true, true, + thd, *item_list); + } + + return func; +} + + +Create_func_json_replace Create_func_json_replace::s_singleton; + +Item* +Create_func_json_replace::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_insert(false, true, + thd, *item_list); + } + + return func; +} + + +Create_func_json_remove Create_func_json_remove::s_singleton; + +Item* +Create_func_json_remove::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_remove(thd, *item_list); + } + + return func; +} + + Create_func_json_object Create_func_json_object::s_singleton; Item* @@ -4990,6 +5231,31 @@ Create_func_json_contains::create_native(THD *thd, LEX_STRING name, } +Create_func_json_keys Create_func_json_keys::s_singleton; + +Item* +Create_func_json_keys::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 < 1 || arg_count > 2 /* json_doc, [path]...*/) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + } + else + { + func= new (thd->mem_root) Item_func_json_keys(thd, *item_list); + } + + return func; +} + + Create_func_json_contains_path Create_func_json_contains_path::s_singleton; Item* @@ -5040,6 +5306,31 @@ Create_func_json_extract::create_native(THD *thd, LEX_STRING name, } +Create_func_json_search Create_func_json_search::s_singleton; + +Item* +Create_func_json_search::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, search_str, [escape_char[, path]...*/) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + } + else + { + func= new (thd->mem_root) Item_func_json_search(thd, *item_list); + } + + return func; +} + + Create_func_last_insert_id Create_func_last_insert_id::s_singleton; Item* @@ -6313,17 +6604,25 @@ static Native_func_registry func_array[] = { { 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_ARRAY_INSERT") }, BUILDER(Create_func_json_array_insert)}, { { 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_INSERT") }, BUILDER(Create_func_json_insert)}, + { { C_STRING_WITH_LEN("JSON_KEYS") }, BUILDER(Create_func_json_keys)}, { { 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_REMOVE") }, BUILDER(Create_func_json_remove)}, + { { C_STRING_WITH_LEN("JSON_REPLACE") }, BUILDER(Create_func_json_replace)}, + { { C_STRING_WITH_LEN("JSON_SET") }, BUILDER(Create_func_json_set)}, + { { C_STRING_WITH_LEN("JSON_SEARCH") }, BUILDER(Create_func_json_search)}, { { C_STRING_WITH_LEN("JSON_TYPE") }, BUILDER(Create_func_json_type)}, + { { C_STRING_WITH_LEN("JSON_UNQUOTE") }, BUILDER(Create_func_json_unquote)}, { { 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)}, @@ -6716,6 +7015,9 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type, res= new (thd->mem_root) Item_char_typecast(thd, a, len, real_cs); break; } + case ITEM_CAST_JSON: + res= new (thd->mem_root) Item_json_typecast(thd, a); + break; default: { DBUG_ASSERT(0); diff --git a/sql/item_func.h b/sql/item_func.h index ca7c4819012..374c16e3932 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -2197,7 +2197,7 @@ enum Cast_target { ITEM_CAST_BINARY, ITEM_CAST_SIGNED_INT, ITEM_CAST_UNSIGNED_INT, ITEM_CAST_DATE, ITEM_CAST_TIME, ITEM_CAST_DATETIME, ITEM_CAST_CHAR, - ITEM_CAST_DECIMAL, ITEM_CAST_DOUBLE + ITEM_CAST_DECIMAL, ITEM_CAST_DOUBLE, ITEM_CAST_JSON }; diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 80713710927..4fa2f02bedc 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -48,6 +48,45 @@ static bool eq_ascii_string(const CHARSET_INFO *cs, } +static bool append_simple(String *s, const char *a, uint a_len) +{ + if (!s->realloc_with_extra_if_needed(s->length() + a_len)) + { + s->q_append(a, a_len); + return FALSE; + } + + return TRUE; +} + + +static inline bool append_simple(String *s, const uchar *a, uint a_len) +{ + return append_simple(s, (const char *) a, a_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. @@ -278,6 +317,50 @@ String *Item_func_json_quote::val_str(String *str) } +void Item_func_json_unquote::fix_length_and_dec() +{ + collation.set(&my_charset_utf8_general_ci); + max_length= args[0]->max_length; +} + + +String *Item_func_json_unquote::val_str(String *str) +{ + String *js= args[0]->val_str(&tmp_s); + json_engine_t je; + int c_len; + + if ((null_value= args[0]->null_value)) + return NULL; + + json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), + (const uchar *) js->ptr() + js->length()); + + if (json_read_value(&je)) + goto error; + + if (je.value_type != JSON_VALUE_STRING) + return js; + + str->length(0); + str->set_charset(&my_charset_utf8_general_ci); + + if (str->realloc_with_extra_if_needed(je.value_len) || + (c_len= json_unescape(js->charset(), + je.value, je.value + je.value_len, + &my_charset_utf8_general_ci, + (uchar *) str->ptr(), (uchar *) (str->ptr() + je.value_len))) < 0) + goto error; + + str->length(c_len); + return str; + +error: + null_value= 1; + return 0; +} + + static int alloc_tmp_paths(THD *thd, uint n_paths, json_path_with_flags **paths,String **tmp_paths) { @@ -642,6 +725,27 @@ void Item_func_json_contains_path::cleanup() } +static int parse_one_or_all(Item *ooa_arg, + bool *ooa_parsed, bool ooa_constant, bool *mode_one) +{ + if (!*ooa_parsed) + { + char buff[20]; + String *res, tmp(buff, sizeof(buff), &my_charset_bin); + res= ooa_arg->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())) + return TRUE; + } + *ooa_parsed= ooa_constant; + } + return FALSE; +} + + longlong Item_func_json_contains_path::val_int() { String *js= args[0]->val_str(&tmp_js); @@ -652,20 +756,8 @@ longlong Item_func_json_contains_path::val_int() 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; - } + if (parse_one_or_all(args[1], &ooa_parsed, ooa_constant, &mode_one)) + goto error; result= !mode_one; for (n_arg=2; n_arg < arg_count; n_arg++) @@ -742,6 +834,9 @@ static int append_json_value(String *str, Item *item, String *tmp_val) String *sv= item->val_str(tmp_val); if (item->null_value) goto append_null; + if (item->is_json_type()) + return str->append(sv->ptr(), sv->length()); + if (item->result_type() == STRING_RESULT) { return str->append("\"", 1) || @@ -915,6 +1010,117 @@ error: } +String *Item_func_json_array_insert::val_str(String *str) +{ + json_engine_t je; + String *js= args[0]->val_str(&tmp_js); + uint n_arg, n_path; + + 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++) + { + uint array_counters[JSON_DEPTH_LIMIT]; + json_path_with_flags *c_path= paths + n_path; + const char *item_pos; + uint n_item; + + 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()) || + c_path->p.last_step - 1 > c_path->p.steps || + c_path->p.last_step->type != JSON_PATH_ARRAY)) + goto error; + c_path->parsed= c_path->constant; + c_path->p.last_step--; + } + if (args[n_arg]->null_value) + goto null_return; + + 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_path(&je, &c_path->p, &c_path->cur_step, array_counters)) + { + if (je.s.error) + goto error; + + /* Can't find the array to insert. */ + goto null_return; + } + + if (json_read_value(&je)) + goto error; + + if (je.value_type != JSON_VALUE_ARRAY) + { + /* Must be an array. */ + goto null_return; + } + + item_pos= 0; + n_item= 0; + + while (json_scan_next(&je) == 0 && + je.state != JST_ARRAY_END && item_pos == 0) + { + switch (je.state) + { + case JST_VALUE: + if (n_item == c_path->p.last_step[1].n_item) + { + item_pos= (const char *) je.s.c_str; + break; + } + n_item++; + break; + case JST_OBJ_START: + case JST_ARRAY_START: + if (json_skip_level(&je)) + break; + break; + default: + break; + } + } + + str->length(0); + str->set_charset(js->charset()); + if (!item_pos) + item_pos= (const char *) (je.s.c_str - je.sav_c_len); + + if (append_simple(str, js->ptr(), item_pos - js->ptr()) || + ((je.state == JST_ARRAY_END) ? + (n_item > 0 && str->append(", ", 2)) : str->append(" ", 1)) || + 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. */ + + { + String *tmp_str= str; + str= &tmp_js; + js= tmp_str; + } + } + + return js; + +null_return: +error: + null_value= 1; + return 0; +} + + String *Item_func_json_object::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -1107,3 +1313,754 @@ error: } +void Item_func_json_insert::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_insert::val_str(String *str) +{ + json_engine_t je; + String *js= args[0]->val_str(&tmp_js); + uint n_arg, n_path; + json_string_t key_name; + + DBUG_ASSERT(fixed == 1); + + if ((null_value= args[0]->null_value)) + return 0; + + str->set_charset(js->charset()); + json_string_set_cs(&key_name, js->charset()); + + for (n_arg=1, n_path=0; n_arg < arg_count; n_arg+=2, n_path++) + { + uint array_counters[JSON_DEPTH_LIMIT]; + json_path_with_flags *c_path= paths + n_path; + const char *v_to; + const json_path_step_t *lp; + + if (!c_path->parsed) + { + 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; + + /* We search to the last step. */ + c_path->p.last_step--; + } + 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 (c_path->p.last_step >= c_path->p.steps && + json_find_path(&je, &c_path->p, &c_path->cur_step, array_counters)) + { + if (je.s.error) + goto error; + } + + if (json_read_value(&je)) + goto error; + + lp= c_path->p.last_step+1; + if (lp->type == JSON_PATH_ARRAY) + { + uint n_item= 0; + + if (je.value_type != JSON_VALUE_ARRAY) + { + const uchar *v_from= je.value_begin; + if (!mode_insert) + continue; + + str->length(0); + /* 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. */ + + if (je.value_type == JSON_VALUE_OBJECT) + { + if (json_skip_level(&je)) + goto error; + } + + if (append_simple(str, v_from, je.s.c_str - v_from) || + str->append(", ", 2) || + 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 continue_point; + } + + while (json_scan_next(&je) == 0 && je.state != JST_ARRAY_END) + { + switch (je.state) + { + case JST_VALUE: + if (n_item == lp->n_item) + goto v_found; + n_item++; + if (json_skip_array_item(&je)) + goto error; + break; + default: + break; + } + } + + if (je.s.error) + goto error; + + if (!mode_insert) + continue; + + v_to= (const char *) (je.s.c_str - je.sav_c_len); + str->length(0); + if (append_simple(str, js->ptr(), v_to - js->ptr()) || + 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. */ + } + else /*JSON_PATH_KEY*/ + { + if (je.value_type != JSON_VALUE_OBJECT) + continue; + + while (json_scan_next(&je) == 0 && je.state != JST_OBJ_END) + { + switch (je.state) + { + case JST_KEY: + json_string_set_str(&key_name, lp->key, lp->key_end); + if (json_key_matches(&je, &key_name)) + goto v_found; + if (json_skip_key(&je)) + goto error; + break; + default: + break; + } + } + + if (je.s.error) + goto error; + + if (!mode_insert) + continue; + + v_to= (const char *) (je.s.c_str - je.sav_c_len); + str->length(0); + if (append_simple(str, js->ptr(), v_to - js->ptr()) || + str->append(", \"", 3) || + append_simple(str, lp->key, lp->key_end - lp->key) || + 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 continue_point; + +v_found: + + if (!mode_replace) + continue; + + if (json_read_value(&je)) + goto error; + + v_to= (const char *) je.value_begin; + str->length(0); + if (!json_value_scalar(&je)) + { + if (json_skip_level(&je)) + goto 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. */ +continue_point: + { + String *tmp= str; + str= &tmp_js; + js= tmp; + } + } + + return js; + +error: + null_value= 1; + return 0; +} + + +void Item_func_json_remove::fix_length_and_dec() +{ + collation.set(args[0]->collation); + max_length= args[0]->max_length; + + mark_constant_paths(paths, args+1, arg_count-1); +} + + +String *Item_func_json_remove::val_str(String *str) +{ + json_engine_t je; + String *js= args[0]->val_str(&tmp_js); + uint n_arg, n_path; + json_string_t key_name; + + DBUG_ASSERT(fixed == 1); + + if (args[0]->null_value) + goto null_return; + + str->set_charset(js->charset()); + json_string_set_cs(&key_name, js->charset()); + + for (n_arg=1, n_path=0; n_arg < arg_count; n_arg+=2, n_path++) + { + uint array_counters[JSON_DEPTH_LIMIT]; + json_path_with_flags *c_path= paths + n_path; + const char *rem_start, *rem_end; + const json_path_step_t *lp; + uint n_item= 0; + + if (!c_path->parsed) + { + 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; + + /* We search to the last step. */ + c_path->p.last_step--; + if (c_path->p.last_step < c_path->p.steps) + goto null_return; + } + 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_path(&je, &c_path->p, &c_path->cur_step, array_counters)) + { + if (je.s.error) + goto error; + } + + if (json_read_value(&je)) + goto error; + + lp= c_path->p.last_step+1; + if (lp->type == JSON_PATH_ARRAY) + { + if (je.value_type != JSON_VALUE_ARRAY) + continue; + + while (json_scan_next(&je) == 0 && je.state != JST_ARRAY_END) + { + switch (je.state) + { + case JST_VALUE: + if (n_item == lp->n_item) + { + rem_start= (const char *) (je.s.c_str - + (n_item ? je.sav_c_len : 0)); + goto v_found; + } + n_item++; + if (json_skip_array_item(&je)) + goto error; + break; + default: + break; + } + } + + if (je.s.error) + goto error; + + continue; + } + else /*JSON_PATH_KEY*/ + { + if (je.value_type != JSON_VALUE_OBJECT) + continue; + + while (json_scan_next(&je) == 0 && je.state != JST_OBJ_END) + { + switch (je.state) + { + case JST_KEY: + if (n_item == 0) + rem_start= (const char *) (je.s.c_str - je.sav_c_len); + json_string_set_str(&key_name, lp->key, lp->key_end); + if (json_key_matches(&je, &key_name)) + { + goto v_found; + } + + if (json_skip_key(&je)) + goto error; + + rem_start= (const char *) je.s.c_str; + n_item++; + break; + default: + break; + } + } + + if (je.s.error) + goto error; + + continue; + } + +v_found: + + if (json_skip_key(&je) || json_scan_next(&je)) + goto error; + + rem_end= (je.state == JST_VALUE) ? + (const char *) je.s.c_str : (const char *) (je.s.c_str - je.sav_c_len); + + str->length(0); + + if (append_simple(str, js->ptr(), rem_start - js->ptr()) || + append_simple(str, rem_end, js->end() - rem_end)) + goto error; /* Out of memory. */ + + { + String *tmp= str; + str= &tmp_js; + js= tmp; + } + } + + return js; + +null_return: +error: + null_value= 1; + return 0; +} + + +void Item_func_json_keys::fix_length_and_dec() +{ + collation.set(args[0]->collation); + max_length= args[0]->max_length; + if (arg_count > 1) + path.set_constant_flag(args[1]->const_item()); +} + + +String *Item_func_json_keys::val_str(String *str) +{ + json_engine_t je; + String *js= args[0]->val_str(&tmp_js); + uint n_keys= 0; + uint array_counters[JSON_DEPTH_LIMIT]; + + if ((args[0]->null_value)) + goto null_return; + + json_scan_start(&je, js->charset(),(const uchar *) js->ptr(), + (const uchar *) js->ptr() + js->length()); + + if (arg_count < 2) + goto skip_search; + + 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 (args[1]->null_value) + goto null_return; + + path.cur_step= path.p.steps; + + if (json_find_path(&je, &path.p, &path.cur_step, array_counters)) + { + if (je.s.error) + goto err_return; + + goto null_return; + } + +skip_search: + if (json_read_value(&je)) + goto err_return; + + if (je.value_type != JSON_VALUE_OBJECT) + goto null_return; + + str->length(0); + if (str->append("[", 1)) + goto err_return; /* Out of memory. */ + /* Parse the OBJECT collecting the keys. */ + while (json_scan_next(&je) == 0 && je.state != JST_OBJ_END) + { + const uchar *key_start, *key_end; + + switch (je.state) + { + case JST_KEY: + key_start= je.s.c_str; + while (json_read_keyname_chr(&je) == 0) + { + key_end= je.s.c_str; + } + if (je.s.error || + (n_keys > 0 && str->append(", ", 2)) || + str->append("\"", 1) || + append_simple(str, key_start, key_end - key_start) || + str->append("\"", 1)) + goto err_return; + n_keys++; + break; + case JST_OBJ_START: + case JST_ARRAY_START: + if (json_skip_level(&je)) + break; + break; + default: + break; + } + } + + if (je.s.error || str->append("]", 1)) + goto err_return; + + null_value= 0; + return str; + +null_return: +err_return: + null_value= 1; + return 0; +} + + +bool Item_func_json_search::fix_fields(THD *thd, Item **ref) +{ + if (Item_json_str_multipath::fix_fields(thd, ref)) + return TRUE; + + if (arg_count < 4) + return FALSE; + + return fix_escape_item(thd, args[3], &tmp_js, true, + args[0]->collation.collation, &escape); +} + + +static const uint SQR_MAX_BLOB_WIDTH= sqrt(MAX_BLOB_WIDTH); + +void Item_func_json_search::fix_length_and_dec() +{ + collation.set(args[0]->collation); + + /* + It's rather difficult to estimate the length of the result. + I belive arglen^2 is the reasonable upper limit. + */ + if (args[0]->max_length > SQR_MAX_BLOB_WIDTH) + max_length= MAX_BLOB_WIDTH; + else + { + max_length= args[0]->max_length; + max_length*= max_length; + } + + ooa_constant= args[1]->const_item(); + ooa_parsed= FALSE; + + if (arg_count > 4) + mark_constant_paths(paths, args+4, arg_count-4); +} + + +int Item_func_json_search::compare_json_value_wild(json_engine_t *je, + const String *cmp_str) +{ + return my_wildcmp(collation.collation, + (const char *) je->value, (const char *) (je->value + je->value_len), + cmp_str->ptr(), cmp_str->end(), escape, wild_one, wild_many) ? 0 : 1; +} + + +static int append_json_path(String *str, const json_path_t *p) +{ + const json_path_step_t *c; + + if (str->append("\"$", 2)) + return TRUE; + + for (c= p->steps+1; c <= p->last_step; c++) + { + if (c->type == JSON_PATH_KEY) + { + if (str->append(".", 1) || + append_simple(str, c->key, c->key_end-c->key)) + return TRUE; + } + else /*JSON_PATH_ARRAY*/ + { + + if (str->append("[", 1) || + str->append_ulonglong(c->n_item) || + str->append("]", 1)) + return TRUE; + } + } + + return str->append("\"", 1); +} + + +static int json_path_compare(const json_path_t *a, const json_path_t *b) +{ + uint i, a_len= a->last_step - a->steps, b_len= b->last_step - b->steps; + + if (a_len > b_len) + return -2; + + for (i=0; i <= a_len; i++) + { + const json_path_step_t *sa= a->steps + i; + const json_path_step_t *sb= b->steps + i; + + if (sa->type != sb->type) + return -1; + + if (sa->type == JSON_PATH_ARRAY) + { + if (!sa->wild && sa->n_item != sb->n_item) + return -1; + } + else /* JSON_PATH_KEY */ + { + if (!sa->wild && + (sa->key_end - sa->key != sb->key_end - sb->key || + memcmp(sa->key, sb->key, sa->key_end - sa->key) != 0)) + return -1; + } + } + + return b_len > a_len; +} + + +static bool path_ok(const json_path_with_flags *paths_list, int n_paths, + const json_path_t *p) +{ + for (; n_paths > 0; n_paths--, paths_list++) + { + if (json_path_compare(&paths_list->p, p) >= 0) + return TRUE; + } + return FALSE; +} + + +String *Item_func_json_search::val_str(String *str) +{ + String *js= args[0]->val_str(&tmp_js); + String *s_str= args[2]->val_str(&tmp_js); + json_engine_t je; + json_path_t p, sav_path; + uint n_arg; + + 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) + goto null_return; + + n_path_found= 0; + str->set_charset(js->charset()); + str->length(0); + + for (n_arg=4; n_arg < arg_count; n_arg++) + { + json_path_with_flags *c_path= paths + n_arg - 4; + 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()); + + p.last_step= p.steps; + p.steps[0].wild= 0; + p.steps[0].type= JSON_PATH_ARRAY; + p.steps[0].n_item= 0; + + do + { + switch (je.state) + { + case JST_KEY: + p.last_step->key= je.s.c_str; + while (json_read_keyname_chr(&je) == 0) + p.last_step->key_end= je.s.c_str; + if (je.s.error) + goto error; + /* Now we have je.state == JST_VALUE, so let's handle it. */ + + case JST_VALUE: + if (json_read_value(&je)) + goto error; + if (json_value_scalar(&je)) + { + if ((arg_count < 5 || path_ok(paths, n_arg - 4, &p)) && + compare_json_value_wild(&je, s_str) != 0) + { + ++n_path_found; + if (n_path_found == 1) + { + sav_path= p; + sav_path.last_step= sav_path.steps + (p.last_step - p.steps); + } + else + { + if (n_path_found == 2) + { + if (str->append("[", 1) || + append_json_path(str, &sav_path)) + goto error; + } + if (str->append(", ", 2) || append_json_path(str, &p)) + goto error; + } + + if (mode_one) + goto end; + } + if (p.last_step->type == JSON_PATH_ARRAY) + p.last_step->n_item++; + + } + else + { + p.last_step++; + if (je.value_type == JSON_VALUE_ARRAY) + { + p.last_step->type= JSON_PATH_ARRAY; + p.last_step->n_item= 0; + } + else /*JSON_VALUE_OBJECT*/ + p.last_step->type= JSON_PATH_KEY; + } + + break; + case JST_OBJ_END: + case JST_ARRAY_END: + p.last_step--; + if (p.last_step->type == JSON_PATH_ARRAY) + p.last_step->n_item++; + break; + default: + break; + } + } while (json_scan_next(&je) == 0); + + if (je.s.error) + goto error; + +end: + if (n_path_found == 0) + goto null_return; + if (n_path_found == 1) + { + if (append_json_path(str, &sav_path)) + goto error; + } + else + { + if (str->append("]", 1)) + goto error; + } + + return str; + + +null_return: +error: + /* TODO: launch error messages. */ + null_value= 1; + return 0; +} + + +void Item_json_typecast::fix_length_and_dec() +{ + maybe_null= args[0]->maybe_null; + max_length= args[0]->max_length; +} + + diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index 54da67b5ab9..d2d68b4c391 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -123,6 +123,21 @@ public: }; +class Item_func_json_unquote: public Item_str_func +{ +protected: + String tmp_s; + +public: + Item_func_json_unquote(THD *thd, Item *s): Item_str_func(thd, s) {} + const char *func_name() const { return "json_unquote"; } + 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_unquote>(thd, mem_root, this); } +}; + + class Item_json_str_multipath: public Item_str_func { protected: @@ -134,6 +149,7 @@ public: bool fix_fields(THD *thd, Item **ref); void cleanup(); virtual uint get_n_paths() const = 0; + bool is_json_type() { return true; } }; @@ -207,6 +223,7 @@ public: Item_func_json_array(THD *thd, List<Item> &list): Item_str_func(thd, list) {} String *val_str(String *); + bool is_json_type() { return true; } void fix_length_and_dec(); const char *func_name() const { return "json_array"; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -231,6 +248,18 @@ public: }; +class Item_func_json_array_insert: public Item_func_json_array_append +{ +public: + Item_func_json_array_insert(THD *thd, List<Item> &list): + Item_func_json_array_append(thd, list) {} + String *val_str(String *); + const char *func_name() const { return "json_array_insert"; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_json_array_insert>(thd, mem_root, this); } +}; + + class Item_func_json_object: public Item_func_json_array { public: @@ -239,6 +268,7 @@ public: Item_func_json_object(THD *thd, List<Item> &list): Item_func_json_array(thd, list) {} String *val_str(String *); + bool is_json_type() { return true; } 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); } @@ -253,6 +283,7 @@ public: Item_func_json_merge(THD *thd, List<Item> &list): Item_func_json_array(thd, list) {} String *val_str(String *); + bool is_json_type() { return true; } 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); } @@ -301,4 +332,102 @@ public: }; +class Item_func_json_insert: public Item_json_str_multipath +{ +protected: + String tmp_js; + String tmp_val; + bool mode_insert, mode_replace; +public: + Item_func_json_insert(bool i_mode, bool r_mode, THD *thd, List<Item> &list): + Item_json_str_multipath(thd, list), + mode_insert(i_mode), mode_replace(r_mode) {} + void fix_length_and_dec(); + String *val_str(String *); + uint get_n_paths() const { return arg_count/2; } + const char *func_name() const + { + return mode_insert ? + (mode_replace ? "json_set" : "json_insert") : "json_update"; + } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_json_insert>(thd, mem_root, this); } +}; + + +class Item_func_json_remove: public Item_json_str_multipath +{ +protected: + String tmp_js; +public: + Item_func_json_remove(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 - 1; } + const char *func_name() const { return "json_remove"; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_json_remove>(thd, mem_root, this); } +}; + + +class Item_func_json_keys: public Item_str_func +{ +protected: + json_path_with_flags path; + String tmp_js, tmp_path; + +public: + Item_func_json_keys(THD *thd, List<Item> &list): + Item_str_func(thd, list) {} + const char *func_name() const { return "json_keys"; } + 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_keys>(thd, mem_root, this); } +}; + + +class Item_func_json_search: public Item_json_str_multipath +{ +protected: + String tmp_js; + bool mode_one; + bool ooa_constant, ooa_parsed; + int escape; + int n_path_found; + json_path_t sav_path; + + int compare_json_value_wild(json_engine_t *je, const String *cmp_str); + +public: + Item_func_json_search(THD *thd, List<Item> &list): + Item_json_str_multipath(thd, list) {} + const char *func_name() const { return "json_search"; } + bool fix_fields(THD *thd, Item **ref); + void fix_length_and_dec(); + String *val_str(String *); + uint get_n_paths() const { return arg_count > 4 ? arg_count - 4 : 0; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_json_search>(thd, mem_root, this); } +}; + + +class Item_json_typecast: public Item_str_func +{ +public: + Item_json_typecast(THD *thd, Item *a): Item_str_func(thd, a) {} + const char *func_name() const { return "cast_as_json"; } + bool is_json_type() { return true; } + void fix_length_and_dec(); + String *val_str(String *str) + { + return args[0]->val_str(str); + } + + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_json_typecast>(thd, mem_root, this); } +}; + + #endif /* ITEM_JSONFUNC_INCLUDED */ diff --git a/sql/lex.h b/sql/lex.h index 527ddb81c4b..114bafeee18 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -309,6 +309,7 @@ static SYMBOL symbols[] = { { "ITERATE", SYM(ITERATE_SYM)}, { "INVOKER", SYM(INVOKER_SYM)}, { "JOIN", SYM(JOIN_SYM)}, + { "JSON", SYM(JSON_SYM)}, { "KEY", SYM(KEY_SYM)}, { "KEYS", SYM(KEYS)}, { "KEY_BLOCK_SIZE", SYM(KEY_BLOCK_SIZE)}, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e17a514a391..f2aea2bee6e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1324,6 +1324,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token ISSUER_SYM %token ITERATE_SYM %token JOIN_SYM /* SQL-2003-R */ +%token JSON_SYM %token KEYS %token KEY_BLOCK_SIZE %token KEY_SYM /* SQL-2003-N */ @@ -10709,6 +10710,7 @@ cast_type: } | cast_type_numeric { $$= $1; Lex->charset= NULL; } | cast_type_temporal { $$= $1; Lex->charset= NULL; } + | JSON_SYM { $$.set(ITEM_CAST_JSON); } ; cast_type_numeric: @@ -13226,11 +13228,10 @@ opt_extended_describe: opt_format_json: /* empty */ {} + | FORMAT_SYM '=' JSON_SYM { Lex->explain_json= true; } | FORMAT_SYM '=' ident_or_text { - if (!my_strcasecmp(system_charset_info, $3.str, "JSON")) - Lex->explain_json= true; - else if (!my_strcasecmp(system_charset_info, $3.str, "TRADITIONAL")) + if (!my_strcasecmp(system_charset_info, $3.str, "TRADITIONAL")) DBUG_ASSERT(Lex->explain_json==false); else my_yyabort_error((ER_UNKNOWN_EXPLAIN_FORMAT, MYF(0), $3.str)); |