Skip to content

Commit 2ed426a

Browse files
authored
expression: fix the arg verification for json functions. (#54145) (#57567)
close #54029, close #54044
1 parent 33c06b6 commit 2ed426a

File tree

7 files changed

+165
-65
lines changed

7 files changed

+165
-65
lines changed

pkg/expression/builtin_json.go

Lines changed: 61 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"bytes"
1919
"context"
2020
goJSON "encoding/json"
21+
"strconv"
2122
"strings"
2223

2324
"github.com/pingcap/errors"
@@ -105,7 +106,7 @@ func (b *builtinJSONTypeSig) Clone() builtinFunc {
105106
}
106107

107108
func (c *jsonTypeFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
108-
if err := c.verifyArgs(args); err != nil {
109+
if err := c.verifyArgs(ctx.GetEvalCtx(), args); err != nil {
109110
return nil, err
110111
}
111112
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETJson)
@@ -122,6 +123,51 @@ func (c *jsonTypeFunctionClass) getFunction(ctx BuildContext, args []Expression)
122123
return sig, nil
123124
}
124125

126+
func (c *jsonTypeFunctionClass) verifyArgs(ctx EvalContext, args []Expression) error {
127+
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
128+
return err
129+
}
130+
return verifyJSONArgsType(ctx, c.funcName, true, args, 0)
131+
}
132+
133+
// verifyJSONArgsType verifies that all args specified in `jsonArgsIndex` are JSON or non-binary string or NULL.
134+
// the `useJSONErr` specifies to use `ErrIncorrectType` or `ErrInvalidTypeForJSON`. If it's true, the error will be `ErrInvalidTypeForJSON`
135+
func verifyJSONArgsType(ctx EvalContext, funcName string, useJSONErr bool, args []Expression, jsonArgsIndex ...int) error {
136+
if jsonArgsIndex == nil {
137+
// if no index is specified, verify all args
138+
jsonArgsIndex = make([]int, len(args))
139+
for i := 0; i < len(args); i++ {
140+
jsonArgsIndex[i] = i
141+
}
142+
}
143+
for _, argIndex := range jsonArgsIndex {
144+
arg := args[argIndex]
145+
146+
typ := arg.GetType(ctx)
147+
if typ.GetType() == mysql.TypeNull {
148+
continue
149+
}
150+
151+
evalType := typ.EvalType()
152+
switch evalType {
153+
case types.ETString:
154+
cs := typ.GetCharset()
155+
if cs == charset.CharsetBin {
156+
return types.ErrInvalidJSONCharset.GenWithStackByArgs(cs)
157+
}
158+
continue
159+
case types.ETJson:
160+
continue
161+
default:
162+
if useJSONErr {
163+
return ErrInvalidTypeForJSON.GenWithStackByArgs(argIndex+1, funcName)
164+
}
165+
return ErrIncorrectType.GenWithStackByArgs(strconv.Itoa(argIndex+1), funcName)
166+
}
167+
}
168+
return nil
169+
}
170+
125171
func (b *builtinJSONTypeSig) evalString(ctx EvalContext, row chunk.Row) (val string, isNull bool, err error) {
126172
var j types.BinaryJSON
127173
j, isNull, err = b.args[0].EvalJSON(ctx, row)
@@ -149,10 +195,7 @@ func (c *jsonExtractFunctionClass) verifyArgs(ctx EvalContext, args []Expression
149195
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
150196
return err
151197
}
152-
if evalType := args[0].GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
153-
return ErrInvalidTypeForJSON.GenWithStackByArgs(1, "json_extract")
154-
}
155-
return nil
198+
return verifyJSONArgsType(ctx, c.funcName, true, args, 0)
156199
}
157200

158201
func (c *jsonExtractFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -216,10 +259,7 @@ func (c *jsonUnquoteFunctionClass) verifyArgs(ctx EvalContext, args []Expression
216259
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
217260
return err
218261
}
219-
if evalType := args[0].GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
220-
return ErrIncorrectType.GenWithStackByArgs("1", "json_unquote")
221-
}
222-
return nil
262+
return verifyJSONArgsType(ctx, c.funcName, false, args, 0)
223263
}
224264

225265
func (c *jsonUnquoteFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -448,12 +488,7 @@ func (c *jsonMergeFunctionClass) verifyArgs(ctx EvalContext, args []Expression)
448488
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
449489
return err
450490
}
451-
for i, arg := range args {
452-
if evalType := arg.GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
453-
return ErrInvalidTypeForJSON.GenWithStackByArgs(i+1, "json_merge")
454-
}
455-
}
456-
return nil
491+
return verifyJSONArgsType(ctx, c.funcName, true, args)
457492
}
458493

459494
type builtinJSONMergeSig struct {
@@ -649,10 +684,7 @@ func (c *jsonContainsPathFunctionClass) verifyArgs(ctx EvalContext, args []Expre
649684
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
650685
return err
651686
}
652-
if evalType := args[0].GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
653-
return ErrInvalidTypeForJSON.GenWithStackByArgs(1, "json_contains_path")
654-
}
655-
return nil
687+
return verifyJSONArgsType(ctx, c.funcName, true, args, 0)
656688
}
657689

658690
func (c *jsonContainsPathFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -765,10 +797,7 @@ func (c *jsonMemberOfFunctionClass) verifyArgs(ctx EvalContext, args []Expressio
765797
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
766798
return err
767799
}
768-
if evalType := args[1].GetType(ctx).EvalType(); evalType != types.ETJson && evalType != types.ETString {
769-
return ErrInvalidTypeForJSON.GenWithStackByArgs(2, "member of")
770-
}
771-
return nil
800+
return verifyJSONArgsType(ctx, "member of", true, args, 1)
772801
}
773802

774803
func (c *jsonMemberOfFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -828,13 +857,7 @@ func (c *jsonContainsFunctionClass) verifyArgs(ctx EvalContext, args []Expressio
828857
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
829858
return err
830859
}
831-
if evalType := args[0].GetType(ctx).EvalType(); evalType != types.ETJson && evalType != types.ETString {
832-
return ErrInvalidTypeForJSON.GenWithStackByArgs(1, "json_contains")
833-
}
834-
if evalType := args[1].GetType(ctx).EvalType(); evalType != types.ETJson && evalType != types.ETString {
835-
return ErrInvalidTypeForJSON.GenWithStackByArgs(2, "json_contains")
836-
}
837-
return nil
860+
return verifyJSONArgsType(ctx, c.funcName, true, args, 0, 1)
838861
}
839862

840863
func (c *jsonContainsFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -908,13 +931,7 @@ func (c *jsonOverlapsFunctionClass) verifyArgs(ctx EvalContext, args []Expressio
908931
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
909932
return err
910933
}
911-
if evalType := args[0].GetType(ctx).EvalType(); evalType != types.ETJson && evalType != types.ETString {
912-
return ErrInvalidTypeForJSON.GenWithStackByArgs(1, "json_overlaps")
913-
}
914-
if evalType := args[1].GetType(ctx).EvalType(); evalType != types.ETJson && evalType != types.ETString {
915-
return ErrInvalidTypeForJSON.GenWithStackByArgs(2, "json_overlaps")
916-
}
917-
return nil
934+
return verifyJSONArgsType(ctx, c.funcName, true, args, 0, 1)
918935
}
919936

920937
func (c *jsonOverlapsFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -1227,12 +1244,7 @@ func (c *jsonMergePatchFunctionClass) verifyArgs(ctx EvalContext, args []Express
12271244
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
12281245
return err
12291246
}
1230-
for i, arg := range args {
1231-
if evalType := arg.GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
1232-
return ErrInvalidTypeForJSON.GenWithStackByArgs(i+1, "json_merge_patch")
1233-
}
1234-
}
1235-
return nil
1247+
return verifyJSONArgsType(ctx, c.funcName, true, args)
12361248
}
12371249

12381250
func (c *jsonMergePatchFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -1296,12 +1308,7 @@ func (c *jsonMergePreserveFunctionClass) verifyArgs(ctx EvalContext, args []Expr
12961308
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
12971309
return err
12981310
}
1299-
for i, arg := range args {
1300-
if evalType := arg.GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
1301-
return ErrInvalidTypeForJSON.GenWithStackByArgs(i+1, "json_merge_preserve")
1302-
}
1303-
}
1304-
return nil
1311+
return verifyJSONArgsType(ctx, c.funcName, true, args)
13051312
}
13061313

13071314
func (c *jsonMergePreserveFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -1441,10 +1448,7 @@ func (c *jsonSearchFunctionClass) verifyArgs(ctx EvalContext, args []Expression)
14411448
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
14421449
return err
14431450
}
1444-
if evalType := args[0].GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
1445-
return ErrInvalidTypeForJSON.GenWithStackByArgs(1, "json_search")
1446-
}
1447-
return nil
1451+
return verifyJSONArgsType(ctx, c.funcName, true, args, 0)
14481452
}
14491453

14501454
func (c *jsonSearchFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -1650,10 +1654,7 @@ func (c *jsonKeysFunctionClass) verifyArgs(ctx EvalContext, args []Expression) e
16501654
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
16511655
return err
16521656
}
1653-
if evalType := args[0].GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
1654-
return ErrInvalidTypeForJSON.GenWithStackByArgs(1, "json_keys")
1655-
}
1656-
return nil
1657+
return verifyJSONArgsType(ctx, c.funcName, true, args, 0)
16571658
}
16581659

16591660
func (c *jsonKeysFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -1816,11 +1817,9 @@ func (c *jsonSchemaValidFunctionClass) verifyArgs(ctx EvalContext, args []Expres
18161817
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
18171818
return err
18181819
}
1819-
if evalType := args[0].GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
1820-
return ErrInvalidTypeForJSON.GenWithStackByArgs(1, "json_schema_valid")
1821-
}
1822-
if evalType := args[1].GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
1823-
return ErrInvalidTypeForJSON.GenWithStackByArgs(2, "json_schema_valid")
1820+
1821+
if err := verifyJSONArgsType(ctx, c.funcName, true, args, 0, 1); err != nil {
1822+
return err
18241823
}
18251824
if c, ok := args[0].(*Constant); ok {
18261825
// If args[0] is NULL, then don't check the length of *both* arguments.

pkg/parser/mysql/errcode.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,7 @@ const (
898898
ErrInvalidJSONText = 3140
899899
ErrInvalidJSONTextInParam = 3141
900900
ErrInvalidJSONPath = 3143
901+
ErrInvalidJSONCharset = 3144
901902
ErrInvalidTypeForJSON = 3146
902903
ErrInvalidJSONPathWildcard = 3149
903904
ErrInvalidJSONContainsPathType = 3150

pkg/parser/mysql/errname.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,7 @@ var MySQLErrName = map[uint16]*ErrMessage{
907907
ErrInvalidJSONText: Message("Invalid JSON text: %-.192s", nil),
908908
ErrInvalidJSONTextInParam: Message("Invalid JSON text in argument %d to function %s: \"%s\" at position %d.", nil),
909909
ErrInvalidJSONPath: Message("Invalid JSON path expression %s.", nil),
910+
ErrInvalidJSONCharset: Message("Cannot create a JSON value from a string with CHARACTER SET '%s'.", nil),
910911
ErrInvalidTypeForJSON: Message("Invalid data type for JSON data in argument %d to function %s; a JSON string or JSON type is required.", nil),
911912
ErrInvalidJSONPathWildcard: Message("In this situation, path expressions may not contain the * and ** tokens or an array range.", nil),
912913
ErrInvalidJSONContainsPathType: Message("The second argument can only be either 'one' or 'all'.", nil),

pkg/parser/mysql/state.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ var MySQLState = map[uint16]string{
254254
ErrInvalidJSONText: "22032",
255255
ErrInvalidJSONTextInParam: "22032",
256256
ErrInvalidJSONPath: "42000",
257+
ErrInvalidJSONCharset: "22032",
257258
ErrInvalidJSONData: "22032",
258259
ErrInvalidJSONPathWildcard: "42000",
259260
ErrJSONUsedAsKey: "42000",

pkg/types/field_type.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,10 @@ func InferParamTypeFromUnderlyingValue(value any, tp *FieldType) {
197197
tp.SetType(mysql.TypeVarString)
198198
tp.SetFlen(UnspecifiedLength)
199199
tp.SetDecimal(UnspecifiedLength)
200+
// Also set the `charset` and `collation` for it, because some function (e.g. `json_object`) will return error
201+
// if the argument collation is `binary`.
202+
tp.SetCharset(mysql.DefaultCharset)
203+
tp.SetCollate(mysql.DefaultCollationName)
200204
default:
201205
DefaultTypeForValue(value, tp, mysql.DefaultCharset, mysql.DefaultCollationName)
202206
if hasVariantFieldLength(tp) {

tests/integrationtest/r/expression/json.result

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,9 +518,9 @@ select json_objectagg(a, b) from t;
518518
json_objectagg(a, b)
519519
{"a string": "base64:type252:YSBiaW5hcnkgc3RyaW5n"}
520520
select json_object(b, a) from t;
521-
Error 3144 (HY000): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
521+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
522522
select json_objectagg(b, a) from t;
523-
Error 3144 (HY000): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
523+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
524524
select cast(cast(b'010101' as json) as signed);
525525
cast(cast(b'010101' as json) as signed)
526526
0
@@ -771,3 +771,49 @@ insert into t values (NULL, NULL, NULL);
771771
select json_valid(j), json_valid(str), json_valid(other) from t;
772772
json_valid(j) json_valid(str) json_valid(other)
773773
NULL NULL NULL
774+
DROP TABLE IF EXISTS t1;
775+
CREATE TABLE t1(id INT PRIMARY KEY, d1 DATE, d2 DATETIME, t1 TIME, t2 TIMESTAMP, b1 BIT, b2 BINARY);
776+
INSERT INTO t1 VALUES (1, '2024-06-14', '2024-06-14 09:37:00', '09:37:00', '2024-06-14 09:37:00', b'0', 0x41);
777+
SELECT JSON_TYPE(d1) FROM t1;
778+
Error 3146 (22032): Invalid data type for JSON data in argument 1 to function json_type; a JSON string or JSON type is required.
779+
SELECT JSON_TYPE(d2) FROM t1;
780+
Error 3146 (22032): Invalid data type for JSON data in argument 1 to function json_type; a JSON string or JSON type is required.
781+
SELECT JSON_TYPE(t1) FROM t1;
782+
Error 3146 (22032): Invalid data type for JSON data in argument 1 to function json_type; a JSON string or JSON type is required.
783+
SELECT JSON_TYPE(t2) FROM t1;
784+
Error 3146 (22032): Invalid data type for JSON data in argument 1 to function json_type; a JSON string or JSON type is required.
785+
SELECT JSON_TYPE(b1) FROM t1;
786+
Error 3146 (22032): Invalid data type for JSON data in argument 1 to function json_type; a JSON string or JSON type is required.
787+
SELECT JSON_TYPE(b2) FROM t1;
788+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
789+
SELECT JSON_EXTRACT(b2, '$') FROM t1;
790+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
791+
SELECT JSON_MERGE(b2, '{a:"b"}') FROM t1;
792+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
793+
SELECT JSON_CONTAINS_PATH(b2, 'one', '$.a') FROM t1;
794+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
795+
SELECT '1' member of(b2) FROM t1;
796+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
797+
SELECT JSON_CONTAINS(b2, '{a:"b"}') FROM t1;
798+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
799+
SELECT JSON_OVERLAPS(b2, '{a:"b"}') FROM t1;
800+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
801+
SELECT JSON_MERGE_PATCH(b2, '{a:"b"}') FROM t1;
802+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
803+
SELECT JSON_MERGE_PATCH('{a:"b"}', b2) FROM t1;
804+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
805+
SELECT JSON_MERGE_PRESERVE(b2, '{a:"b"}') FROM t1;
806+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
807+
SELECT JSON_MERGE_PRESERVE('{a:"b"}', b2) FROM t1;
808+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
809+
SELECT JSON_SEARCH(b2, 'one', '1') FROM t1;
810+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
811+
SELECT JSON_KEYS(b2) FROM t1;
812+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
813+
SELECT JSON_SCHEMA_VALID(b2, '{}') FROM t1;
814+
Error 3144 (22032): Cannot create a JSON value from a string with CHARACTER SET 'binary'.
815+
prepare stmt from 'select json_object(?, ?)';
816+
set @a=1;
817+
execute stmt using @a, @a;
818+
json_object(?, ?)
819+
{"1": 1}

tests/integrationtest/t/expression/json.test

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,4 +489,52 @@ select json_type(cast(cast('2024' as year) as json));
489489
drop table if exists t;
490490
create table t(j json, str varchar(255), other int);
491491
insert into t values (NULL, NULL, NULL);
492-
select json_valid(j), json_valid(str), json_valid(other) from t;
492+
select json_valid(j), json_valid(str), json_valid(other) from t;
493+
494+
# TestIssue54029
495+
DROP TABLE IF EXISTS t1;
496+
CREATE TABLE t1(id INT PRIMARY KEY, d1 DATE, d2 DATETIME, t1 TIME, t2 TIMESTAMP, b1 BIT, b2 BINARY);
497+
INSERT INTO t1 VALUES (1, '2024-06-14', '2024-06-14 09:37:00', '09:37:00', '2024-06-14 09:37:00', b'0', 0x41);
498+
-- error 3146
499+
SELECT JSON_TYPE(d1) FROM t1;
500+
-- error 3146
501+
SELECT JSON_TYPE(d2) FROM t1;
502+
-- error 3146
503+
SELECT JSON_TYPE(t1) FROM t1;
504+
-- error 3146
505+
SELECT JSON_TYPE(t2) FROM t1;
506+
-- error 3146
507+
SELECT JSON_TYPE(b1) FROM t1;
508+
-- error 3144
509+
SELECT JSON_TYPE(b2) FROM t1;
510+
-- error 3144
511+
SELECT JSON_EXTRACT(b2, '$') FROM t1;
512+
-- error 3144
513+
SELECT JSON_MERGE(b2, '{a:"b"}') FROM t1;
514+
-- error 3144
515+
SELECT JSON_CONTAINS_PATH(b2, 'one', '$.a') FROM t1;
516+
-- error 3144
517+
SELECT '1' member of(b2) FROM t1;
518+
-- error 3144
519+
SELECT JSON_CONTAINS(b2, '{a:"b"}') FROM t1;
520+
-- error 3144
521+
SELECT JSON_OVERLAPS(b2, '{a:"b"}') FROM t1;
522+
-- error 3144
523+
SELECT JSON_MERGE_PATCH(b2, '{a:"b"}') FROM t1;
524+
-- error 3144
525+
SELECT JSON_MERGE_PATCH('{a:"b"}', b2) FROM t1;
526+
-- error 3144
527+
SELECT JSON_MERGE_PRESERVE(b2, '{a:"b"}') FROM t1;
528+
-- error 3144
529+
SELECT JSON_MERGE_PRESERVE('{a:"b"}', b2) FROM t1;
530+
-- error 3144
531+
SELECT JSON_SEARCH(b2, 'one', '1') FROM t1;
532+
-- error 3144
533+
SELECT JSON_KEYS(b2) FROM t1;
534+
-- error 3144
535+
SELECT JSON_SCHEMA_VALID(b2, '{}') FROM t1;
536+
537+
# TestIssue54044
538+
prepare stmt from 'select json_object(?, ?)';
539+
set @a=1;
540+
execute stmt using @a, @a;

0 commit comments

Comments
 (0)