Skip to content

Commit 24902c1

Browse files
committed
Bug#21828321: JSON FUNCS CALL DBUG_ABORT OR EXIT() ON WINDOWS!
If LEAST or GREATEST is called with only JSON arguments, and the result of the call is passed as an argument to a JSON function, an error is raised in release builds and an assert failure in debug builds. The LEAST and GREATEST functions don't have any special handling of JSON values currently. JSON arguments are handled as if they were strings, and the result is returned as a string. If all the arguments have type JSON, however, the return type of the function will be set to MYSQL_TYPE_JSON. If the result is passed as an argument to a JSON function, the JSON function will attempt to call val_json() on the result, since it appears to be a JSON value. Since the corresponding Item subclass (Item_func_min_max) does not override Item::val_json(), an error or assert failure is raised when val_json() is called. The fix changes the return type of the function call from JSON to string in the case where all arguments are JSON values. This way, the receiving JSON function is not led to believe it's a JSON value and does not call val_json() on it. This patch also makes LEAST and GREATEST raise a "not supported yet" warning if one of the arguments is JSON. This is meant as a notification that these operators have not yet been updated to use the JSON comparator, and may therefore give other results than expected. The warning is raised by BETWEEN and IN as well, since those operators also haven't been updated to use the JSON comparator to compare JSON values.
1 parent 2076ad6 commit 24902c1

File tree

6 files changed

+95
-1
lines changed

6 files changed

+95
-1
lines changed

mysql-test/r/json.result

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ i c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 c17 c18 c19
280280
31 0 0 0 0 NULL NULL 0 0 0 0 0 NULL NULL 0 0 0 0 0 NULL
281281
100 0 0 0 1 NULL NULL 0 0 0 0 0 NULL NULL 0 0 0 0 0 NULL
282282
Warnings:
283+
Warning 1235 This version of MySQL doesn't yet support 'comparison of JSON in the IN operator'
284+
Warning 1235 This version of MySQL doesn't yet support 'comparison of JSON in the IN operator'
283285
Warning 3156 Invalid JSON value for CAST to DOUBLE: '{"a": 2}' from j at row 2
284286
Warning 3156 Invalid JSON value for CAST to DOUBLE: '[1, 2]' from j at row 35
285287
Warning 3156 Invalid JSON value for CAST to DOUBLE: '{"a": "b", "c": "d", "ab": "abc", "bc": ["x", "y"]}' from j at row 36
@@ -10978,6 +10980,8 @@ insert into tt(j) values (cast(1 as json)), (null);
1097810980
select sum( distinct j ) from tt group by j having j in ( avg( 1 ), 1 + j);
1097910981
sum( distinct j )
1098010982
1
10983+
Warnings:
10984+
Warning 1235 This version of MySQL doesn't yet support 'comparison of JSON in the IN operator'
1098110985
SELECT JSON_ARRAY(j), COUNT(*) FROM tt GROUP BY j, i WITH ROLLUP;
1098210986
JSON_ARRAY(j) COUNT(*)
1098310987
[null] 1
@@ -13387,3 +13391,16 @@ id select_type table partitions type possible_keys key key_len ref rows filtered
1338713391
Warnings:
1338813392
Note 1003 /* select#1 */ select json_object('c',json_extract(`test`.`t1`.`f1`,'$.b')) AS `f2` from `test`.`t1` having (json_type(json_extract(`f2`,'$.c')) <> 'NULL')
1338913393
DROP TABLE t1;
13394+
#
13395+
# Bug#21828321: JSON FUNCS CALL DBUG_ABORT OR EXIT() ON WINDOWS!
13396+
#
13397+
SELECT JSON_ARRAY(LEAST(NULL, NULL), GREATEST(NULL, NULL), LEAST(j1, NULL),
13398+
GREATEST(NULL, j2), LEAST(j1, j2), GREATEST(j1, j2)) AS j
13399+
FROM (SELECT CAST('1' AS JSON) AS j1, CAST('2' AS JSON) AS j2) t;
13400+
j
13401+
[null, null, null, null, "1", "2"]
13402+
Warnings:
13403+
Warning 1235 This version of MySQL doesn't yet support 'comparison of JSON in the LEAST and GREATEST operators'
13404+
Warning 1235 This version of MySQL doesn't yet support 'comparison of JSON in the LEAST and GREATEST operators'
13405+
Warning 1235 This version of MySQL doesn't yet support 'comparison of JSON in the LEAST and GREATEST operators'
13406+
Warning 1235 This version of MySQL doesn't yet support 'comparison of JSON in the LEAST and GREATEST operators'

mysql-test/r/json_index.result

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,13 @@ f1
100100
"vbn"
101101
"vbn"
102102
"vbn"
103+
Warnings:
104+
Warning 1235 This version of MySQL doesn't yet support 'comparison of JSON in the BETWEEN operator'
103105
explain select f1 from t1 where json_extract(f1,"$") between "v" and "z";
104106
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
105107
1 SIMPLE t1 NULL range gc_idx gc_idx 83 NULL 4 100.00 Using where
106108
Warnings:
109+
Warning 1235 This version of MySQL doesn't yet support 'comparison of JSON in the BETWEEN operator'
107110
Note 1003 /* select#1 */ select `test`.`t1`.`f1` AS `f1` from `test`.`t1` where (`test`.`t1`.`gc` between 'v' and 'z')
108111
select f1 from t1 where gc in ("asd","asasas","asdf");
109112
f1
@@ -120,10 +123,13 @@ f1
120123
"asasas"
121124
"asd"
122125
"asdf"
126+
Warnings:
127+
Warning 1235 This version of MySQL doesn't yet support 'comparison of JSON in the IN operator'
123128
explain select f1 from t1 where json_extract(f1,"$") in ("asd","asasas","asdf");
124129
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
125130
1 SIMPLE t1 NULL range gc_idx gc_idx 83 NULL 3 100.00 Using where
126131
Warnings:
132+
Warning 1235 This version of MySQL doesn't yet support 'comparison of JSON in the IN operator'
127133
Note 1003 /* select#1 */ select `test`.`t1`.`f1` AS `f1` from `test`.`t1` where (`test`.`t1`.`gc` in ('asd','asasas','asdf'))
128134
select f1 from t1 where json_unquote(json_extract(f1,"$"))="asd";
129135
f1

mysql-test/t/json.test

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6132,6 +6132,17 @@ EXPLAIN
61326132

61336133
DROP TABLE t1;
61346134

6135+
--echo #
6136+
--echo # Bug#21828321: JSON FUNCS CALL DBUG_ABORT OR EXIT() ON WINDOWS!
6137+
--echo #
6138+
# LEAST and GREATEST treat JSON arguments as strings for now. They used to hit
6139+
# an assertion if used in a JSON context and all arguments were JSON values, or
6140+
# a mix of NULLs and JSON values.
6141+
SELECT JSON_ARRAY(LEAST(NULL, NULL), GREATEST(NULL, NULL), LEAST(j1, NULL),
6142+
GREATEST(NULL, j2), LEAST(j1, j2), GREATEST(j1, j2)) AS j
6143+
FROM (SELECT CAST('1' AS JSON) AS j1, CAST('2' AS JSON) AS j2) t;
6144+
6145+
61356146
# Local Variables:
61366147
# mode: sql
61376148
# sql-product: mysql

sql/item_cmpfunc.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2962,6 +2962,14 @@ void Item_func_between::fix_length_and_dec()
29622962
*/
29632963
reject_geometry_args(arg_count, args, this);
29642964

2965+
/*
2966+
JSON values will be compared as strings, and not with the JSON
2967+
comparator as one might expect. Raise a warning if one of the
2968+
arguments is JSON.
2969+
*/
2970+
unsupported_json_comparison(arg_count, args,
2971+
"comparison of JSON in the BETWEEN operator");
2972+
29652973
/*
29662974
Detect the comparison of DATE/DATETIME items.
29672975
At least one of items should be a DATE/DATETIME item and other items
@@ -5305,6 +5313,16 @@ void Item_func_in::fix_length_and_dec()
53055313
bisection_possible= false;
53065314
}
53075315

5316+
/*
5317+
JSON values will be compared as strings, and not with the JSON
5318+
comparator as one might expect. Raise a warning if one of the
5319+
arguments is JSON. (The degenerate case x IN (y) may get rewritten
5320+
to x = y, though, and then the JSON comparator will be used if one
5321+
of the arguments is JSON.)
5322+
*/
5323+
unsupported_json_comparison(arg_count, args,
5324+
"comparison of JSON in the IN operator");
5325+
53085326
if (type_cnt == 1)
53095327
{
53105328
if (cmp_type == STRING_RESULT &&

sql/item_func.cc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,6 +1266,35 @@ void reject_geometry_args(uint arg_count, Item **args, Item_result_field *me)
12661266
}
12671267

12681268

1269+
/**
1270+
Go through the arguments of a function and check if any of them are
1271+
JSON. If a JSON argument is found, raise a warning saying that this
1272+
operation is not supported yet. This function is used to notify
1273+
users that they are comparing JSON values using a mechanism that has
1274+
not yet been updated to use the JSON comparator. JSON values are
1275+
typically handled as strings in that case.
1276+
1277+
@param arg_count the number of arguments
1278+
@param args the arguments to go through looking for JSON values
1279+
@param msg the message that explains what is not supported
1280+
*/
1281+
void unsupported_json_comparison(size_t arg_count, Item **args, const char *msg)
1282+
{
1283+
for (size_t i= 0; i < arg_count; ++i)
1284+
{
1285+
if (args[i]->result_type() == STRING_RESULT &&
1286+
args[i]->field_type() == MYSQL_TYPE_JSON)
1287+
{
1288+
push_warning_printf(current_thd, Sql_condition::SL_WARNING,
1289+
ER_NOT_SUPPORTED_YET,
1290+
ER_THD(current_thd, ER_NOT_SUPPORTED_YET),
1291+
msg);
1292+
break;
1293+
}
1294+
}
1295+
}
1296+
1297+
12691298
void Item_func_numhybrid::fix_length_and_dec()
12701299
{
12711300
fix_num_length_and_dec();
@@ -3496,6 +3525,18 @@ void Item_func_min_max::fix_length_and_dec()
34963525
else if (cmp_type == REAL_RESULT)
34973526
fix_char_length(float_length(decimals));
34983527
cached_field_type= agg_field_type(args, arg_count);
3528+
/*
3529+
LEAST and GREATEST convert JSON values to strings before they are
3530+
compared, so their JSON nature is lost. Raise a warning to
3531+
indicate to the users that the values are not compared using the
3532+
JSON comparator, as they might expect. Also update the field type
3533+
of the result to reflect that the result is a string.
3534+
*/
3535+
unsupported_json_comparison(arg_count, args,
3536+
"comparison of JSON in the "
3537+
"LEAST and GREATEST operators");
3538+
if (cached_field_type == MYSQL_TYPE_JSON)
3539+
cached_field_type= MYSQL_TYPE_VARCHAR;
34993540
reject_geometry_args(arg_count, args, this);
35003541
}
35013542

sql/item_func.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class PT_item_list;
2828

2929
extern void reject_geometry_args(uint arg_count, Item **args,
3030
Item_result_field *me);
31-
31+
void unsupported_json_comparison(size_t arg_count, Item **args,
32+
const char *msg);
3233

3334
class Item_func :public Item_result_field
3435
{

0 commit comments

Comments
 (0)