diff --git a/mysql-test/main/func_json.result b/mysql-test/main/func_json.result index bcf0477a0f9..9957fb3dd26 100644 --- a/mysql-test/main/func_json.result +++ b/mysql-test/main/func_json.result @@ -5272,6 +5272,8 @@ SET @obj1='{ "a": 1,"b": 2,"c": 3}'; SELECT JSON_OBJECT_FILTER_KEYS (@obj1,@arr1); JSON_OBJECT_FILTER_KEYS (@obj1,@arr1) NULL +SET character_set_database=default; +SET CHARACTER SET default; # End of 11.2 Test # Beginning of 11.4 Test # @@ -5283,4 +5285,16 @@ NULL SELECT json_array_intersect(@a,@b); json_array_intersect(@a,@b) NULL +# MDEV-36809: json_array_intersect crashs when unused table ref provided +# +select json_array_intersect('[["1", "7"], ["2", "6"], ["4", "5"], ["3", "8"]]', '[["2","6"],["3","8"],["4","5"],["1","7"]]') as result from mysql.user; +result +[["2", "6"], ["3", "8"], ["4", "5"], ["1", "7"]] +[["2", "6"], ["3", "8"], ["4", "5"], ["1", "7"]] +[["2", "6"], ["3", "8"], ["4", "5"], ["1", "7"]] +[["2", "6"], ["3", "8"], ["4", "5"], ["1", "7"]] +[["2", "6"], ["3", "8"], ["4", "5"], ["1", "7"]] +SELECT ( WITH x AS ( WITH x ( x ) AS ( SELECT ( 1.000000 ) ) SELECT x FROM x ) SELECT * FROM x WHERE ( SELECT AVG ( x ) OVER ( ORDER BY JSON_ARRAY_INTERSECT ( '[["1", "7"], ["2", "6"], ["3", "8"]]' , '[["2","6"],["3","8"],["4","5"],["1","7"]]' ) ) FROM x ) ); +( WITH x AS ( WITH x ( x ) AS ( SELECT ( 1.000000 ) ) SELECT x FROM x ) SELECT * FROM x WHERE ( SELECT AVG ( x ) OVER ( ORDER BY JSON_ARRAY_INTERSECT ( '[["1", "7"], ["2", "6"], ["3", "8"]]' , '[["2","6"],["3","8"],["4","5"],["1","7"]]' ) ) FROM x ) ) +1.000000 # End of 11.4 Test diff --git a/mysql-test/main/func_json.test b/mysql-test/main/func_json.test index e56c5ee4820..6219d7ac9c1 100644 --- a/mysql-test/main/func_json.test +++ b/mysql-test/main/func_json.test @@ -4175,6 +4175,9 @@ SET CHARACTER SET utf8; SET @obj1='{ "a": 1,"b": 2,"c": 3}'; SELECT JSON_OBJECT_FILTER_KEYS (@obj1,@arr1); +SET character_set_database=default; +SET CHARACTER SET default; + --echo # End of 11.2 Test --echo # Beginning of 11.4 Test @@ -4188,4 +4191,9 @@ SELECT JSON_OBJECT_FILTER_KEYS (@obj1,@arr1); SELECT json_array_intersect(@a,@b); +--echo # MDEV-36809: json_array_intersect crashs when unused table ref provided +--echo # +select json_array_intersect('[["1", "7"], ["2", "6"], ["4", "5"], ["3", "8"]]', '[["2","6"],["3","8"],["4","5"],["1","7"]]') as result from mysql.user; +SELECT ( WITH x AS ( WITH x ( x ) AS ( SELECT ( 1.000000 ) ) SELECT x FROM x ) SELECT * FROM x WHERE ( SELECT AVG ( x ) OVER ( ORDER BY JSON_ARRAY_INTERSECT ( '[["1", "7"], ["2", "6"], ["3", "8"]]' , '[["2","6"],["3","8"],["4","5"],["1","7"]]' ) ) FROM x ) ); + --echo # End of 11.4 Test diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index c2fc76f07ca..ac9f5b57f4d 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -5242,14 +5242,14 @@ bool Item_func_json_key_value::fix_length_and_dec(THD *thd) } -static bool create_hash(json_engine_t *value, HASH *items, bool &hash_inited, +static bool create_hash(json_engine_t *value, HASH *items, bool &item_hash_inited, MEM_ROOT *hash_root) { int level= value->stack_p; if (my_hash_init(PSI_INSTRUMENT_ME, items, value->s.cs, 0, 0, 0, get_key_name, NULL, 0)) return true; - hash_inited= true; + item_hash_inited= true; while (json_scan_next(value) == 0 && value->stack_p >= level) { @@ -5318,6 +5318,11 @@ static bool get_current_value(json_engine_t *js, const uchar *&value_start, return false; } +static my_bool restore_entry(void *element, void *arg) +{ + HASH *items = (HASH*) arg; + return my_hash_insert(items, (const uchar*) element); +} /* If the outermost layer of JSON is an array, @@ -5330,14 +5335,16 @@ static bool get_current_value(json_engine_t *js, const uchar *&value_start, FALSE - if two array documents have intersection TRUE - If two array documents do not have intersection */ -static bool get_intersect_between_arrays(String *str, json_engine_t *value, - HASH items) +bool Item_func_json_array_intersect:: + get_intersect_between_arrays(String *str, json_engine_t *value, + HASH *items, HASH *seen) { bool res= true, has_value= false; int level= value->stack_p; - String temp_str(0); + temp_str.length(0); temp_str.append('['); + while (json_scan_next(value) == 0 && value->stack_p >= level) { const uchar *value_start= NULL; @@ -5373,14 +5380,14 @@ static bool get_intersect_between_arrays(String *str, json_engine_t *value, of times the value appears in the hash table. */ uchar * found= NULL; - if ((found= my_hash_search(&items, + if ((found= my_hash_search(items, (const uchar *) new_entry, strlen(new_entry)))) { has_value= true; temp_str.append( (const char*) value_start, value_len); temp_str.append(','); - if (my_hash_delete(&items, found)) + if (my_hash_delete(items, found) || my_hash_insert(seen, (const uchar *)found)) { free(new_entry); goto error; @@ -5399,6 +5406,8 @@ static bool get_intersect_between_arrays(String *str, json_engine_t *value, } error: + my_hash_iterate(seen, restore_entry, items); + my_hash_reset(seen); return res; } @@ -5417,12 +5426,14 @@ String* Item_func_json_array_intersect::val_str(String *str) { if (args[0]->null_value) goto null_return; - if (hash_inited) + if (item_hash_inited) my_hash_free(&items); + if (seen_hash_inited) + my_hash_free(&seen); if (root_inited) free_root(&hash_root, MYF(0)); root_inited= false; - hash_inited= false; + item_hash_inited= false; prepare_json_and_create_hash(&je1, js1); } @@ -5438,7 +5449,7 @@ String* Item_func_json_array_intersect::val_str(String *str) if (json_read_value(&je2) || je2.value_type != JSON_VALUE_ARRAY) goto error_return; - if (get_intersect_between_arrays(str, &je2, items)) + if (get_intersect_between_arrays(str, &je2, &items, &seen)) goto error_return; if (str->length()) @@ -5465,7 +5476,7 @@ null_return: return NULL; } -void Item_func_json_array_intersect::prepare_json_and_create_hash(json_engine_t *je1, String *js) +bool Item_func_json_array_intersect::prepare_json_and_create_hash(json_engine_t *je1, String *js) { json_scan_start(je1, js->charset(), (const uchar *) js->ptr(), (const uchar *) js->ptr() + js->length()); @@ -5473,12 +5484,17 @@ void Item_func_json_array_intersect::prepare_json_and_create_hash(json_engine_t Scan value uses the hash table to get the intersection of two arrays. */ + if (my_hash_init(PSI_INSTRUMENT_ME, &seen, je1->s.cs, 0, 0, 0, + get_key_name, NULL, 0)) + return true; + seen_hash_inited= true; + if (!root_inited) init_alloc_root(PSI_NOT_INSTRUMENTED, &hash_root, 1024, 0, MYF(0)); root_inited= true; if (json_read_value(je1) || je1->value_type != JSON_VALUE_ARRAY || - create_hash(je1, &items, hash_inited, &hash_root)) + create_hash(je1, &items, item_hash_inited, &hash_root)) { if (je1->s.error) report_json_error(js, je1, 0); @@ -5487,6 +5503,8 @@ void Item_func_json_array_intersect::prepare_json_and_create_hash(json_engine_t max_length= (args[0]->max_length < args[1]->max_length) ? args[0]->max_length : args[1]->max_length; + + return false; } bool Item_func_json_array_intersect::fix_length_and_dec(THD *thd) @@ -5509,8 +5527,10 @@ bool Item_func_json_array_intersect::fix_length_and_dec(THD *thd) js1= args[0]->val_json(&tmp_js1); - if (js1) - prepare_json_and_create_hash(&je1, js1); + if (js1 && prepare_json_and_create_hash(&je1, js1)) + { + return TRUE; + } end: set_maybe_null(); diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index 14080b2b242..3d48cf95dd7 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -881,14 +881,15 @@ public: class Item_func_json_array_intersect: public Item_str_func { protected: - String tmp_js1, tmp_js2; - bool hash_inited, root_inited; - HASH items; + String tmp_js1, tmp_js2, temp_str; + bool item_hash_inited, seen_hash_inited, root_inited; + HASH items, seen; MEM_ROOT hash_root; bool parse_for_each_row; public: Item_func_json_array_intersect(THD *thd, Item *a, Item *b): - Item_str_func(thd, a, b) { hash_inited= root_inited= parse_for_each_row= false; } + Item_str_func(thd, a, b) + { item_hash_inited= seen_hash_inited= root_inited= parse_for_each_row= false; } String *val_str(String *) override; bool fix_length_and_dec(THD *thd) override; LEX_CSTRING func_name_cstring() const override @@ -901,12 +902,16 @@ public: void cleanup() override { Item_str_func::cleanup(); - if (hash_inited) + if (item_hash_inited) my_hash_free(&items); + if (seen_hash_inited) + my_hash_free(&seen); if (root_inited) free_root(&hash_root, MYF(0)); } - void prepare_json_and_create_hash(json_engine_t *je1, String *js); + bool prepare_json_and_create_hash(json_engine_t *je1, String *js); + bool get_intersect_between_arrays(String *str, json_engine_t *value, + HASH *items, HASH *seen); }; class Item_func_json_object_filter_keys: public Item_str_func