From 79815d35fb392f3614c3e5cedd0435155ed25fca Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Wed, 15 Jul 2026 18:20:55 +1000 Subject: [PATCH 1/3] MDEV-39941 Incorrect result of JSON_OBJECT_FILTER_KEYS JSON_OBJECT_FILTER_KEYS was only written to handle a constant array argument. The handing of arguments in Item_func_json_object_filter_keys::fix_length_and_dec was incorrect as ::val_str was called with different values as it iterates over table for instance. Moved implementation into Item_func_json_object_filter_keys::val_str. Preserved the caching of the arg2, (array of keys) when it was a constant by making create_hash reset the item list if it was already initialised. Adjusted filter_keys use the arg provided as output and avoid inline mallocs. Added error handing in filter_keys. The JSON_OBJECT_FILTER_KEYS also now respects max_statement_time and is killable with KILL {queryid}. --- mysql-test/main/func_json.result | 37 ++++++ mysql-test/main/func_json.test | 17 +++ sql/item_jsonfunc.cc | 186 +++++++++++++++++-------------- sql/item_jsonfunc.h | 4 +- 4 files changed, 160 insertions(+), 84 deletions(-) diff --git a/mysql-test/main/func_json.result b/mysql-test/main/func_json.result index 2af8dbfceffb3..9e7b7530f5c91 100644 --- a/mysql-test/main/func_json.result +++ b/mysql-test/main/func_json.result @@ -5454,14 +5454,20 @@ NULL SELECT JSON_OBJECT_FILTER_KEYS(@obj1, @obj1); JSON_OBJECT_FILTER_KEYS(@obj1, @obj1) NULL +Warnings: +Warning 4038 Syntax error in JSON text in argument 2 to function 'json_object_filter_keys' at position 1 SELECT JSON_OBJECT_FILTER_KEYS(@arr1, @arr1); JSON_OBJECT_FILTER_KEYS(@arr1, @arr1) NULL +Warnings: +Warning 4038 Syntax error in JSON text in argument 1 to function 'json_object_filter_keys' at position 1 SET @obj1= '{ "a": 1, "b": {"key1": {"key2":"val2"}}, "c": [1, 2, 3] }'; SET @scalar1='2'; SELECT JSON_OBJECT_FILTER_KEYS(@obj1, @scalar1); JSON_OBJECT_FILTER_KEYS(@obj1, @scalar1) NULL +Warnings: +Warning 4038 Syntax error in JSON text in argument 2 to function 'json_object_filter_keys' at position 1 # Checking syntax error SET @obj1= '{ "a": 1, "b": 2, "c": 3}'; SET @obj2= '{"b" : 10, "c": 20, "d" 30}'; @@ -5622,4 +5628,35 @@ SET @value= ' SELECT JSON_SCHEMA_VALID(@schema, @value); JSON_SCHEMA_VALID(@schema, @value) 1 +# +# MDEV-39931 JSON_OBJECT_FILTER_KEYS only handle constant +# +create table t(c1 JSON, c2 JSON); +insert into t values ('{"a":1,"b":2}', '["a", "c"]'), +(NULL, '["a", "b"]'), +('{"b": 3, "c": [1, 2, 3]}', '["a", "d"]'), +('{"r": 1}', NULL), +('{"a": 3, "d": [1, 2, 3]}', '["a", "d"]'); +SELECT c1, JSON_OBJECT_FILTER_KEYS(c1, '["a"]') AS a_s FROM t; +c1 a_s +{"a":1,"b":2} {"a": 1} +NULL NULL +{"b": 3, "c": [1, 2, 3]} NULL +{"r": 1} NULL +{"a": 3, "d": [1, 2, 3]} {"a": 3} +SELECT c2, JSON_OBJECT_FILTER_KEYS('{"a": 13, "d": [1,2,3]}', c2) AS c2_s FROM t; +c2 c2_s +["a", "c"] {"a": 13} +["a", "b"] {"a": 13} +["a", "d"] {"a": 13, "d": [1, 2, 3]} +NULL NULL +["a", "d"] {"a": 13, "d": [1, 2, 3]} +SELECT c1,c2, JSON_OBJECT_FILTER_KEYS(c1, c2) AS c2_s FROM t; +c1 c2 c2_s +{"a":1,"b":2} ["a", "c"] {"a": 1} +NULL ["a", "b"] NULL +{"b": 3, "c": [1, 2, 3]} ["a", "d"] NULL +{"r": 1} NULL NULL +{"a": 3, "d": [1, 2, 3]} ["a", "d"] {"a": 3, "d": [1, 2, 3]} +DROP TABLE t; # End of 11.4 tests diff --git a/mysql-test/main/func_json.test b/mysql-test/main/func_json.test index 186258bb91478..9268852ecc3df 100644 --- a/mysql-test/main/func_json.test +++ b/mysql-test/main/func_json.test @@ -4373,4 +4373,21 @@ SET @value= ' SELECT JSON_SCHEMA_VALID(@schema, @value); +--echo # +--echo # MDEV-39931 JSON_OBJECT_FILTER_KEYS only handle constant +--echo # + +create table t(c1 JSON, c2 JSON); +insert into t values ('{"a":1,"b":2}', '["a", "c"]'), + (NULL, '["a", "b"]'), + ('{"b": 3, "c": [1, 2, 3]}', '["a", "d"]'), + ('{"r": 1}', NULL), + ('{"a": 3, "d": [1, 2, 3]}', '["a", "d"]'); + +SELECT c1, JSON_OBJECT_FILTER_KEYS(c1, '["a"]') AS a_s FROM t; +SELECT c2, JSON_OBJECT_FILTER_KEYS('{"a": 13, "d": [1,2,3]}', c2) AS c2_s FROM t; +SELECT c1,c2, JSON_OBJECT_FILTER_KEYS(c1, c2) AS c2_s FROM t; + +DROP TABLE t; + --echo # End of 11.4 tests diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 15bb700065f66..64aefc9f63a3c 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -325,7 +325,7 @@ static int json_nice(json_engine_t *je, String *nice_js, if (nice_js->alloc(je->s.str_end - je->s.c_str + 32)) goto error; - + DBUG_ASSERT(mode != Item_func_json_format::DETAILED || (tab_size >= 0 && tab_size <= TAB_SIZE_LIMIT)); @@ -5378,10 +5378,15 @@ static bool create_hash(json_engine_t *value, HASH *items, bool &item_hash_inite { int level= value->stack_p; json_engine_t je; - if (my_hash_init(PSI_INSTRUMENT_ME, items, value->s.cs, 0, 0, 0, - get_key_name, NULL, 0)) - return true; - item_hash_inited= true; + if (item_hash_inited) + my_hash_reset(items); + else + { + if (my_hash_init(PSI_INSTRUMENT_ME, items, value->s.cs, 0, 0, 0, + get_key_name, NULL, 0)) + return true; + item_hash_inited= true; + } while (json_scan_next(value) == 0 && value->stack_p >= level) { @@ -5695,13 +5700,12 @@ bool Item_func_json_array_intersect::fix_length_and_dec(THD *thd) } -static bool filter_keys(json_engine_t *je1, String *str, HASH items) +static bool filter_keys(json_engine_t *je1, String *res_str, HASH items) { int level= je1->stack_p; - String temp_str(0); bool res= true, has_value= false; - temp_str.append('{'); + res_str->append('{'); while (json_scan_next(je1)==0 && level <= je1->stack_p) { switch(je1->state) @@ -5730,38 +5734,29 @@ static bool filter_keys(json_engine_t *je1, String *str, HASH items) str.append((const char*)key_start, (size_t)(key_end-key_start)); str.append('"'); - str.append('\0'); - - char *curr_key= (char*)malloc((size_t)(str.length()+3)); - strncpy(curr_key, str.ptr(), str.length()); - if (my_hash_search(&items, (const uchar*)curr_key, strlen(curr_key))) + if (my_hash_search(&items, (const uchar*) str.ptr(), str.length())) { has_value= true; - temp_str.append('"'); - temp_str.append((const char*)key_start, (size_t)(key_end-key_start)); - temp_str.append('"'); - - temp_str.append(':'); - - temp_str.append((const char*)value_start, value_len); - - temp_str.append(','); + res_str->append(str); + res_str->append(':'); + res_str->append((const char*)value_start, value_len); + res_str->append(','); } - free(curr_key); } } } - res= false; + res= je1->s.error != 0; - if (has_value) + if (has_value && !res) { - temp_str.chop(); - temp_str.append('}'); - str->append(temp_str.ptr(), temp_str.length()); + res_str->chop(); + res_str->append('}'); } + else + res_str->length(0); error: return res; @@ -5771,44 +5766,96 @@ String* Item_func_json_object_filter_keys::val_str(String *str) { DBUG_ASSERT(fixed()); - json_engine_t je1, res_je; - String *js1= args[0]->val_json(&tmp_js1); + THD *thd; - if (null_value || args[0]->null_value) - goto null_return; + thd= current_thd; + if (!root_inited) + { + init_alloc_root(PSI_NOT_INSTRUMENTED, &hash_root, 1024, 0, MYF(0)); + root_inited= true; - str->set_charset(js1->charset()); - str->length(0); +new_arg2: + String *js2= args[1]->val_json(&arg2_val); + if (!js2 || args[1]->null_value) + goto null_return; + json_engine_t je2; + json_scan_start(&je2, js2->charset(),(const uchar *) js2->ptr(), + (const uchar *) js2->ptr() + js2->length()); + je2.killed_ptr= (uint32_t *) &thd->killed; + if (json_read_value(&je2)) + goto je2_error_return; - json_scan_start(&je1, js1->charset(),(const uchar *) js1->ptr(), - (const uchar *) js1->ptr() + js1->length()); + if (je2.value_type != JSON_VALUE_ARRAY) + { + je2.s.error= JE_SYN; +je2_error_return: + report_json_error(js2, &je2, 1); + goto null_return; + } + if (create_hash(&je2, &items, hash_inited, &hash_root)) + { + if (je2.s.error) + goto je2_error_return; + my_error(ER_OUTOFMEMORY, MYF(0), 64); + } + } + else if (!args[1]->const_item()) + goto new_arg2; - if (json_read_value(&je1) || je1.value_type != JSON_VALUE_OBJECT) - goto error_return; + { + json_engine_t je1; + String tmp_js1; + String *js1= args[0]->val_json(&tmp_js1); - if(filter_keys(&je1, str, items)) - goto null_return; + if (!js1 || args[0]->null_value) + goto null_return; - if (str->length()) - { - json_scan_start(&res_je, str->charset(), (const uchar *) str->ptr(), - (const uchar *) str->ptr() + str->length()); - str= &tmp_js1; - if (json_nice(&res_je, str, Item_func_json_format::LOOSE)) + JSON_DO_PAUSE_EXECUTION(thd, 0.0002); + json_scan_start(&je1, js1->charset(),(const uchar *) js1->ptr(), + (const uchar *) js1->ptr() + js1->length()); + je1.killed_ptr= (uint32_t *) &thd->killed; + + if (json_read_value(&je1)) goto error_return; - null_value= 0; - return str; - } - else - { - goto null_return; - } + if (je1.value_type != JSON_VALUE_OBJECT) + { + je1.s.error= JE_SYN; +error_return: + report_json_error(js1, &je1, 0); + goto null_return; + } + { + String fil_res; + fil_res.set_charset(js1->charset()); + if (filter_keys(&je1, &fil_res, items)) + goto error_return; + + if (fil_res.length()) + { + json_engine_t res_je; + json_scan_start(&res_je, fil_res.charset(), (const uchar *) fil_res.ptr(), + (const uchar *) fil_res.ptr() + fil_res.length()); + res_je.killed_ptr= (uint32_t *) &thd->killed; + + str->set_charset(js1->charset()); + str->length(0); + + if (json_nice(&res_je, str, Item_func_json_format::LOOSE)) + { + report_json_error(str, &res_je, 0); + goto null_return; + } + + null_value= 0; + return str; + } + else + goto null_return; + } + } -error_return: - if (je1.s.error) - report_json_error(js1, &je1, 0); null_return: null_value= 1; return NULL; @@ -5817,33 +5864,8 @@ String* Item_func_json_object_filter_keys::val_str(String *str) bool Item_func_json_object_filter_keys::fix_length_and_dec(THD *thd) { - String *js2= args[1]->val_json(&tmp_js2); - json_engine_t je2; - - if (args[1]->null_value) - { - null_value= 1; - return FALSE; - } - - json_scan_start(&je2, js2->charset(),(const uchar *) js2->ptr(), - (const uchar *) js2->ptr() + js2->length()); - if (!root_inited) - init_alloc_root(PSI_NOT_INSTRUMENTED, &hash_root, 1024, 0, MYF(0)); - root_inited= true; - - if (json_read_value(&je2) || je2.value_type != JSON_VALUE_ARRAY || - create_hash(&je2, &items, hash_inited, &hash_root)) - { - if (je2.s.error) - report_json_error(js2, &je2, 0); - null_value= 1; - return FALSE; - } - - max_length= args[0]->max_length; set_maybe_null(); - + max_length= args[0]->max_length; return FALSE; } diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index 32fc77ddef1a8..4eecdfe968727 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -971,10 +971,10 @@ class Item_func_json_array_intersect: public Item_str_func class Item_func_json_object_filter_keys: public Item_str_func { protected: - String tmp_js1, tmp_js2; - bool hash_inited, root_inited; + String arg2_val; HASH items; MEM_ROOT hash_root; + bool hash_inited, root_inited; public: Item_func_json_object_filter_keys(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) { hash_inited= root_inited= false; } From e21e99ad8533ec9d0c07a3d95d47016536193b9c Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Tue, 21 Jul 2026 16:25:22 +1000 Subject: [PATCH 2/3] MDEV-39742 11.4 JSON_OBJECT_FILTER_KEYS not interuptable The added error handing inthe previous commit made JSON_OBJECT_FILTER_KEYS interuptable with KILL QUERY and respect the max_statement_time. --- mysql-test/main/func_json_notembedded.result | 2 ++ mysql-test/main/func_json_notembedded.test | 1 + 2 files changed, 3 insertions(+) diff --git a/mysql-test/main/func_json_notembedded.result b/mysql-test/main/func_json_notembedded.result index e32442c114624..18fa2151520c2 100644 --- a/mysql-test/main/func_json_notembedded.result +++ b/mysql-test/main/func_json_notembedded.result @@ -52,6 +52,8 @@ select json_normalize(@obj); ERROR 70100: Query execution was interrupted (max_statement_time exceeded) select json_overlaps(@obj, @arr); ERROR 70100: Query execution was interrupted (max_statement_time exceeded) +select json_object_filter_keys(@obj, @arr); +ERROR 70100: Query execution was interrupted (max_statement_time exceeded) select json_remove(@obj,'$.foo'); ERROR 70100: Query execution was interrupted (max_statement_time exceeded) select json_replace(@obj,'$.foo',1); diff --git a/mysql-test/main/func_json_notembedded.test b/mysql-test/main/func_json_notembedded.test index 5c177074e8107..8ea838aa34ddc 100644 --- a/mysql-test/main/func_json_notembedded.test +++ b/mysql-test/main/func_json_notembedded.test @@ -39,6 +39,7 @@ select json_merge_patch(@obj, @obj); select json_merge_preserve(@obj, @arr); select json_normalize(@obj); select json_overlaps(@obj, @arr); +select json_object_filter_keys(@obj, @arr); select json_remove(@obj,'$.foo'); select json_replace(@obj,'$.foo',1); select json_schema_valid(@obj, '{"type":"object"}'); From 8025b2fa1e5914157aa425a72d208af7a5b23a2e Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Sun, 28 Jun 2026 21:59:51 +1000 Subject: [PATCH 3/3] MDEV-40185 JSON_ARRAY_INTERSECT handing incorrect JSON_ARRAY_INTERSECT when passed arrays out of tables returned incorrect results. Corrected the caching to all occur within val_str() so that non-constant arguments get parsed corrected. Like previous implementation, if arg1 is a constant take care not to reprocess this. When JSON_ARRAY_INTERSECT arguments are swapped to make const the first arg, errors are reported against the wrong argument. OOM errors of create_or_reset_hash aren't reported as errors. Renamed create_hash to create_or_reset_hash to better reflect its function. Non-array arguments to this function are reported as syntax warnings. Remove unnecessary memory allocation/copy in Item_func_json_array_intersect::get_intersect_between_arrays Adjust tests to show the normalization comparion that occurs. --- mysql-test/main/func_json.result | 59 +++++++- mysql-test/main/func_json.test | 25 ++++ sql/item_jsonfunc.cc | 227 ++++++++++++++----------------- sql/item_jsonfunc.h | 12 +- 4 files changed, 194 insertions(+), 129 deletions(-) diff --git a/mysql-test/main/func_json.result b/mysql-test/main/func_json.result index 9e7b7530f5c91..b212298940949 100644 --- a/mysql-test/main/func_json.result +++ b/mysql-test/main/func_json.result @@ -5404,7 +5404,7 @@ SET @obj1= '[1, 2, 3, 3, 3.0, "abc", true, true, {"key1":"val1" ]'; SET @obj2= '[3.0, 3, 5, "abc", "abc", true, {"key2":"val2"}, {"key1":"val1"}, {"key1":"val2"}]'; select json_array_intersect(@obj1, @obj2); json_array_intersect(@obj1, @obj2) -[3.0, 3, "abc", true] +NULL Warnings: Warning 4038 Syntax error in JSON text in argument 1 to function 'json_array_intersect' at position 53 # Checking incorrect type for input @@ -5416,6 +5416,8 @@ SET @bool1= 'true'; select json_array_intersect(@obj1, @arr1); json_array_intersect(@obj1, @arr1) NULL +Warnings: +Warning 4038 Syntax error in JSON text in argument 1 to function 'json_array_intersect' at position 1 select json_array_intersect(@arr1, @obj1); json_array_intersect(@arr1, @obj1) NULL @@ -5429,6 +5431,8 @@ Warning 4038 Syntax error in JSON text in argument 2 to function 'json_array_int select json_array_intersect(@num1, @bool1); json_array_intersect(@num1, @bool1) NULL +Warnings: +Warning 4038 Syntax error in JSON text in argument 1 to function 'json_array_intersect' at position 1 # JSON_OBJECT_FILTER_KEYS() SET @obj1= '{ "a": 1, "b": 2, "c": 3}'; SET @obj2= '{"b" : 10, "c": 20, "d": 30}'; @@ -5659,4 +5663,57 @@ NULL ["a", "b"] NULL {"r": 1} NULL NULL {"a": 3, "d": [1, 2, 3]} ["a", "d"] {"a": 3, "d": [1, 2, 3]} DROP TABLE t; +# +# MDEV-40185 JSON_ARRAY_INTERSECT error handing incorrect +# +create table t (j text); +insert into t values ('[1, 2, 3]'), ('[3, invalid]'); +select json_array_intersect(j, '[3, 4]') as arg1_invalid from t; +arg1_invalid +[3] +NULL +Warnings: +Warning 4038 Syntax error in JSON text in argument 1 to function 'json_array_intersect' at position 5 +select json_array_intersect('[3, 4]', j) as arg2_invalid from t; +arg2_invalid +[3] +NULL +Warnings: +Warning 4038 Syntax error in JSON text in argument 2 to function 'json_array_intersect' at position 5 +drop table t; +create table t (a1 json, a2 json); +insert into t values ('[1, 2, 3]', '[4, 5, 6]'), +('[2, 3, 4.0]', '[4.0000, 5, 7]'), +( NULL, NULL), +('[1, 2, 3]', '[{"dog": 3, "cat": 4, "fish": 5}]'), +('[1E0, 2E1, 3E2]', '[1.0, 20.0, 300.0]'); +select a1, a2, json_array_intersect(a1, a2) as jai from t; +a1 a2 jai +[1, 2, 3] [4, 5, 6] NULL +[2, 3, 4.0] [4.0000, 5, 7] [4.0000] +NULL NULL NULL +[1, 2, 3] [{"dog": 3, "cat": 4, "fish": 5}] NULL +[1E0, 2E1, 3E2] [1.0, 20.0, 300.0] [1.0, 20.0, 300.0] +select a2, json_array_intersect('[5.0]', a2) as jai from t; +a2 jai +[4, 5, 6] [5] +[4.0000, 5, 7] [5] +NULL NULL +[{"dog": 3, "cat": 4, "fish": 5}] NULL +[1.0, 20.0, 300.0] NULL +select a1, json_array_intersect(a1, '[ 1E0 ]') as jai from t; +a1 jai +[1, 2, 3] [1] +[2, 3, 4.0] NULL +NULL NULL +[1, 2, 3] [1] +[1E0, 2E1, 3E2] [1E0] +select a2, json_array_intersect('[{"dog": 3}', a2) as jai from t; +a2 jai +[4, 5, 6] NULL +[4.0000, 5, 7] NULL +NULL NULL +[{"dog": 3, "cat": 4, "fish": 5}] NULL +[1.0, 20.0, 300.0] NULL +drop table t; # End of 11.4 tests diff --git a/mysql-test/main/func_json.test b/mysql-test/main/func_json.test index 9268852ecc3df..e4cb44ca775de 100644 --- a/mysql-test/main/func_json.test +++ b/mysql-test/main/func_json.test @@ -4390,4 +4390,29 @@ SELECT c1,c2, JSON_OBJECT_FILTER_KEYS(c1, c2) AS c2_s FROM t; DROP TABLE t; +--echo # +--echo # MDEV-40185 JSON_ARRAY_INTERSECT error handing incorrect +--echo # + +create table t (j text); +insert into t values ('[1, 2, 3]'), ('[3, invalid]'); + +select json_array_intersect(j, '[3, 4]') as arg1_invalid from t; +select json_array_intersect('[3, 4]', j) as arg2_invalid from t; + +drop table t; + +create table t (a1 json, a2 json); +insert into t values ('[1, 2, 3]', '[4, 5, 6]'), + ('[2, 3, 4.0]', '[4.0000, 5, 7]'), + ( NULL, NULL), + ('[1, 2, 3]', '[{"dog": 3, "cat": 4, "fish": 5}]'), + ('[1E0, 2E1, 3E2]', '[1.0, 20.0, 300.0]'); +select a1, a2, json_array_intersect(a1, a2) as jai from t; +select a2, json_array_intersect('[5.0]', a2) as jai from t; +select a1, json_array_intersect(a1, '[ 1E0 ]') as jai from t; +select a2, json_array_intersect('[{"dog": 3}', a2) as jai from t; + +drop table t; + --echo # End of 11.4 tests diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 64aefc9f63a3c..a5212445efd0f 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -5373,8 +5373,9 @@ bool Item_func_json_key_value::fix_length_and_dec(THD *thd) } -static bool create_hash(json_engine_t *value, HASH *items, bool &item_hash_inited, - MEM_ROOT *hash_root) +static bool create_or_reset_hash(json_engine_t *value, HASH *items, + bool &item_hash_inited, + MEM_ROOT *hash_root) { int level= value->stack_p; json_engine_t je; @@ -5484,6 +5485,7 @@ bool Item_func_json_array_intersect:: int level= value->stack_p; json_engine_t je; + String temp_str; temp_str.length(0); temp_str.append('['); @@ -5509,17 +5511,6 @@ bool Item_func_json_array_intersect:: goto error; } - char *new_entry= (char*)malloc(norm_val.length+1); - if (!new_entry) - { - dynstr_free(&norm_val); - goto error; - } - strncpy(new_entry, norm_val.str, norm_val.length); - new_entry[norm_val.length]='\0'; - - dynstr_free(&norm_val); - /* If the same value is found in the hash table, add that value to str. At the same time, update the number @@ -5527,19 +5518,20 @@ bool Item_func_json_array_intersect:: */ uchar * found= NULL; if ((found= my_hash_search(items, - (const uchar *) new_entry, - strlen(new_entry)))) + (const uchar *) norm_val.str, + norm_val.length))) { + dynstr_free(&norm_val); has_value= true; temp_str.append( (const char*) value_start, value_len); temp_str.append(','); if (my_hash_delete(items, found) || my_hash_insert(seen, (const uchar *)found)) { - free(new_entry); goto error; } } - free(new_entry); + else + dynstr_free(&norm_val); } res= value->s.error != 0; @@ -5563,134 +5555,123 @@ String* Item_func_json_array_intersect::val_str(String *str) THD *thd; DBUG_ASSERT(fixed()); - json_engine_t je2, res_je, je1; - String *js2= args[1]->val_json(&tmp_js2), *js1= args[0]->val_json(&tmp_js1); - - if (!js1 || !js2) - goto null_return; - - if (parse_for_each_row) + thd= current_thd; + /* + Scan value uses the hash table to get the intersection of two arrays. + */ + if (!seen_hash_inited) { - if (args[0]->null_value) + if (my_hash_init(PSI_INSTRUMENT_ME, &seen, nullptr, 0, 0, 0, + get_key_name, NULL, 0)) + { +err_oom: + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + goto null_return; + } + seen_hash_inited= true; + init_alloc_root(PSI_NOT_INSTRUMENTED, &hash_root, 1024, 0, MYF(0)); + root_inited= true; +new_arg1: + String *js1= args[0]->val_json(&arg1_val); + json_engine_t je1; + if (!js1 || args[0]->null_value) goto null_return; - 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; - item_hash_inited= false; - prepare_json_and_create_hash(&je1, js1); - } - - if (!is_array || args[1]->null_value) - goto null_return; + json_scan_start(&je1, js1->charset(), (const uchar *) js1->ptr(), + (const uchar *) js1->ptr() + js1->length()); + je1.killed_ptr= (uint32_t *) &thd->killed; - str->set_charset(js2->charset()); - str->length(0); + if (json_read_value(&je1)) + goto je1_error_return; + if (je1.value_type != JSON_VALUE_ARRAY) + { + je1.s.error= JE_SYN; +je1_error_return: + report_json_error(js1, &je1, swapped ? 1 : 0); + goto null_return; + } + if (create_or_reset_hash(&je1, &items, item_hash_inited, &hash_root)) + { + if (je1.s.error) + goto je1_error_return; + else + goto err_oom; + } + seen.charset= je1.s.cs; + } + else if (!args[0]->const_item()) + goto new_arg1; - thd= current_thd; - JSON_DO_PAUSE_EXECUTION(thd, 0.0002); - json_scan_start(&je2, js2->charset(), (const uchar *) js2->ptr(), - (const uchar *) js2->ptr() + js2->length()); - je2.killed_ptr= (uint32_t *) &thd->killed; + { + /* arg2 */ + String tmp_js2; + String *js2= args[1]->val_json(&tmp_js2); + if (!js2 || args[1]->null_value) + goto null_return; + json_engine_t je2; + JSON_DO_PAUSE_EXECUTION(thd, 0.0002); + json_scan_start(&je2, js2->charset(), (const uchar *) js2->ptr(), + (const uchar *) js2->ptr() + js2->length()); + je2.killed_ptr= (uint32_t *) &thd->killed; - if (json_read_value(&je2)) - goto je2_error_return; + if (json_read_value(&je2)) + goto je2_error_return; - if (je2.value_type != JSON_VALUE_ARRAY) - { - je2.s.error= JE_SYN; - goto je2_error_return; - } + if (je2.value_type != JSON_VALUE_ARRAY) + { + je2.s.error= JE_SYN; +je2_error_return: + report_json_error(js2, &je2, swapped ? 0 : 1); + goto null_return; + } - if (get_intersect_between_arrays(str, &je2, &items, &seen)) - goto error_return; + { + /* result */ + String res; + res.set_charset(js2->charset()); + if (get_intersect_between_arrays(&res, &je2, &items, &seen)) + { + if (je2.s.error) + goto je2_error_return; + goto err_oom; + } - if (str->length()) - { - json_scan_start(&res_je, str->charset(), (const uchar *) str->ptr(), - (const uchar *) str->ptr() + str->length()); - res_je.killed_ptr= (uint32_t *) &thd->killed; - str= &tmp_js1; - if (json_nice(&res_je, str, Item_func_json_format::LOOSE)) - goto error_return; + json_engine_t res_je; + /* and make output nice */ + if (res.length()) + { + json_scan_start(&res_je, res.charset(), (const uchar *) res.ptr(), + (const uchar *) res.ptr() + res.length()); + res_je.killed_ptr= (uint32_t *) &thd->killed; - null_value= 0; - return str; - } - else - { - goto null_return; + str->set_charset(js2->charset()); + str->length(0); + if (json_nice(&res_je, str, Item_func_json_format::LOOSE)) + { + report_json_error(str, &res_je, 0); + goto null_return; + } + null_value= 0; + return str; + } + } } -error_return: - if (je2.s.error) -je2_error_return: - report_json_error(js2, &je2, 1); null_return: null_value= 1; return NULL; } -bool Item_func_json_array_intersect::prepare_json_and_create_hash(json_engine_t *je1, String *js) -{ - THD *thd= current_thd; - json_scan_start(je1, js->charset(), (const uchar *) js->ptr(), - (const uchar *) js->ptr() + js->length()); - je1->killed_ptr= (uint32_t *) &thd->killed; - /* - 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) - || !(is_array= (je1->value_type == JSON_VALUE_ARRAY)) || - create_hash(je1, &items, item_hash_inited, &hash_root)) - { - if (je1->s.error) - report_json_error(js, je1, 0); - } - - return false; -} bool Item_func_json_array_intersect::fix_length_and_dec(THD *thd) { - json_engine_t je1; - String *js1; - - if (!args[0]->const_item()) - { - if (args[1]->const_item()) - { - std::swap(args[0], args[1]); - } - else - { - parse_for_each_row= true; - goto end; - } - } - - js1= args[0]->val_json(&tmp_js1); - - if (js1 && prepare_json_and_create_hash(&je1, js1)) + if (!args[0]->const_item() + && args[1]->const_item()) { - return TRUE; + swapped= true; + std::swap(args[0], args[1]); } -end: collation.set(args[0]->collation); fix_char_length_ulonglong((ulonglong) 2* MY_MIN(args[0]->max_char_length(), @@ -5792,7 +5773,7 @@ String* Item_func_json_object_filter_keys::val_str(String *str) report_json_error(js2, &je2, 1); goto null_return; } - if (create_hash(&je2, &items, hash_inited, &hash_root)) + if (create_or_reset_hash(&je2, &items, hash_inited, &hash_root)) { if (je2.s.error) goto je2_error_return; diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index 4eecdfe968727..1ed09fe047fa5 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -935,15 +935,17 @@ class Item_func_json_key_value: public Item_json_func, class Item_func_json_array_intersect: public Item_str_func { protected: - String tmp_js1, tmp_js2, temp_str; - bool item_hash_inited, seen_hash_inited, root_inited; + String arg1_val; HASH items, seen; MEM_ROOT hash_root; - bool parse_for_each_row, is_array; + bool item_hash_inited, seen_hash_inited, root_inited; + bool swapped; public: Item_func_json_array_intersect(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) - { item_hash_inited= seen_hash_inited= root_inited= parse_for_each_row= is_array= false; } + { + item_hash_inited= seen_hash_inited= root_inited= swapped= false; + } String *val_str(String *) override; bool fix_length_and_dec(THD *thd) override; LEX_CSTRING func_name_cstring() const override @@ -963,7 +965,7 @@ class Item_func_json_array_intersect: public Item_str_func if (root_inited) free_root(&hash_root, MYF(0)); } - bool prepare_json_and_create_hash(json_engine_t *je1, String *js); +protected: bool get_intersect_between_arrays(String *str, json_engine_t *value, HASH *items, HASH *seen); };