mirror of
https://github.com/MariaDB/server.git
synced 2025-11-08 00:28:29 +03:00
MDEV-36809: json_array_intersect crashs when unused table ref provided
Analysis: So, there were two problems that needed to be fixed. 1) To fix the crash. 2) After fixing the crash, the result was wrong. Reason for crash: When we pass the hash to get_intersect_between_arrays(), We were initialially not passing it value, so the operations were not performed correctly. Reason for wrong result: The number of rows that it was returning were same as that in the table, but, only the first row had correct ouput, rest of them were NULL (it should also be the result of interection). This was because we modified the "items" HASH by deleting the "seen" elements. So for next rows, it did not have the elements it should have in the hash. Fix: 1) To fix the crash: pass the HASH by reference 2) To fix incorrect result: Maintain a separate "seen" hash, if an item is found the the "items" hash, delete it ony temporarily and put it in the seen hash. At then end, put the items from "seen" back into "items" and reset "seen".
This commit is contained in:
committed by
Daniel Black
parent
44dc149c78
commit
6c56c92a6c
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user