Skip to content

Commit a247e87

Browse files
committed
Bug#25516960: JSON_SEARCH AND JSON_CONTAINS_PATH REJECT ONE-OR-ALL
ARGUMENT IF IT IS UTF-16 Problem: The one-or-all argument is assumed to be utf8mb4 and is always believed to be different from "one" or "all" when it is encoded in UTF-16. Fix: Convert the one-or-all argument to utf8mb4 before checking if it is "one" or "all". Change-Id: Ic2160d1928d9afe8dcdc57aae72bb18958540946
1 parent 1a7e515 commit a247e87

File tree

3 files changed

+45
-4
lines changed

3 files changed

+45
-4
lines changed

mysql-test/suite/json/r/json_no_table.result

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3320,3 +3320,15 @@ SELECT HEX(JSON_UNQUOTE(CAST('"abc"' AS CHAR CHARSET utf16))),
33203320
HEX(JSON_UNQUOTE(CAST('abc' AS CHAR CHARSET utf16)));
33213321
HEX(JSON_UNQUOTE(CAST('"abc"' AS CHAR CHARSET utf16))) HEX(JSON_UNQUOTE(CAST('abc' AS CHAR CHARSET utf16)))
33223322
616263 616263
3323+
#
3324+
# Bug#25516960: JSON_SEARCH AND JSON_CONTAINS_PATH REJECT ONE-OR-ALL
3325+
# ARGUMENT IF IT IS UTF-16
3326+
#
3327+
SELECT JSON_SEARCH('["a", "a"]', CAST('one' AS CHAR CHARSET utf16), 'a') AS c1,
3328+
JSON_SEARCH('["a", "a"]', CAST('all' AS CHAR CHARSET utf16), 'a') AS c2,
3329+
JSON_CONTAINS_PATH('{"a":"x"}', CAST('one' AS CHAR CHARSET utf16),
3330+
'$.a', '$.b') AS c3,
3331+
JSON_CONTAINS_PATH('{"a":"x"}', CAST('all' AS CHAR CHARSET utf16),
3332+
'$.a', '$.b') AS c4;
3333+
c1 c2 c3 c4
3334+
"$[0]" ["$[0]", "$[1]"] 1 0

mysql-test/suite/json/t/json_no_table.test

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2318,3 +2318,14 @@ SELECT JSON_UNQUOTE(CAST('ABCD' AS BINARY));
23182318
# JSON_UNQUOTE should return utf8mb4 encoded results, but used to return utf16.
23192319
SELECT HEX(JSON_UNQUOTE(CAST('"abc"' AS CHAR CHARSET utf16))),
23202320
HEX(JSON_UNQUOTE(CAST('abc' AS CHAR CHARSET utf16)));
2321+
2322+
--echo #
2323+
--echo # Bug#25516960: JSON_SEARCH AND JSON_CONTAINS_PATH REJECT ONE-OR-ALL
2324+
--echo # ARGUMENT IF IT IS UTF-16
2325+
--echo #
2326+
SELECT JSON_SEARCH('["a", "a"]', CAST('one' AS CHAR CHARSET utf16), 'a') AS c1,
2327+
JSON_SEARCH('["a", "a"]', CAST('all' AS CHAR CHARSET utf16), 'a') AS c2,
2328+
JSON_CONTAINS_PATH('{"a":"x"}', CAST('one' AS CHAR CHARSET utf16),
2329+
'$.a', '$.b') AS c3,
2330+
JSON_CONTAINS_PATH('{"a":"x"}', CAST('all' AS CHAR CHARSET utf16),
2331+
'$.a', '$.b') AS c4;

sql/item_json_func.cc

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -391,13 +391,31 @@ static bool parse_path(Item * path_expression, String *value,
391391
392392
@returns ooa_one, ooa_all, or ooa_error, based on the match
393393
*/
394-
static enum_one_or_all_type parse_one_or_all(const char *candidate,
394+
static enum_one_or_all_type parse_one_or_all(const String *candidate,
395395
const char *func_name)
396396
{
397-
if (!my_strcasecmp(&my_charset_utf8mb4_general_ci, candidate, "all"))
397+
/*
398+
First convert the candidate to utf8mb4.
399+
400+
A buffer of four bytes is enough to hold the candidate in the common
401+
case ("one" or "all" + terminating NUL character).
402+
403+
We can ignore conversion errors here. If a conversion error should
404+
happen, the converted string will contain a question mark, and we will
405+
correctly raise an error later because no string with a question mark
406+
will match "one" or "all".
407+
*/
408+
StringBuffer<4> utf8str;
409+
uint errors;
410+
if (utf8str.copy(candidate->ptr(), candidate->length(), candidate->charset(),
411+
&my_charset_utf8mb4_bin, &errors))
412+
return ooa_error; /* purecov: inspected */
413+
414+
const char *str= utf8str.c_ptr_safe();
415+
if (!my_strcasecmp(&my_charset_utf8mb4_general_ci, str, "all"))
398416
return ooa_all;
399417

400-
if (!my_strcasecmp(&my_charset_utf8mb4_general_ci, candidate, "one"))
418+
if (!my_strcasecmp(&my_charset_utf8mb4_general_ci, str, "one"))
401419
return ooa_one;
402420

403421
my_error(ER_JSON_BAD_ONE_OR_ALL_ARG, MYF(0), func_name);
@@ -436,7 +454,7 @@ static enum_one_or_all_type parse_and_cache_ooa(Item *arg,
436454
}
437455
else
438456
{
439-
*cached_ooa= parse_one_or_all(one_or_all->c_ptr_safe(), func_name);
457+
*cached_ooa= parse_one_or_all(one_or_all, func_name);
440458
}
441459

442460
return *cached_ooa;

0 commit comments

Comments
 (0)