Skip to content

Commit db4d19b

Browse files
authored
expression: fix the arg verification for json functions. (#54145)
close #54029, close #54044
1 parent 865b283 commit db4d19b

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"
@@ -108,7 +109,7 @@ func (b *builtinJSONTypeSig) Clone() builtinFunc {
108109
}
109110

110111
func (c *jsonTypeFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
111-
if err := c.verifyArgs(args); err != nil {
112+
if err := c.verifyArgs(ctx.GetEvalCtx(), args); err != nil {
112113
return nil, err
113114
}
114115
bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETString, types.ETJson)
@@ -125,6 +126,51 @@ func (c *jsonTypeFunctionClass) getFunction(ctx BuildContext, args []Expression)
125126
return sig, nil
126127
}
127128

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

164207
func (c *jsonExtractFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -225,10 +268,7 @@ func (c *jsonUnquoteFunctionClass) verifyArgs(ctx EvalContext, args []Expression
225268
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
226269
return err
227270
}
228-
if evalType := args[0].GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
229-
return ErrIncorrectType.GenWithStackByArgs("1", "json_unquote")
230-
}
231-
return nil
271+
return verifyJSONArgsType(ctx, c.funcName, false, args, 0)
232272
}
233273

234274
func (c *jsonUnquoteFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -469,12 +509,7 @@ func (c *jsonMergeFunctionClass) verifyArgs(ctx EvalContext, args []Expression)
469509
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
470510
return err
471511
}
472-
for i, arg := range args {
473-
if evalType := arg.GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
474-
return ErrInvalidTypeForJSON.GenWithStackByArgs(i+1, "json_merge")
475-
}
476-
}
477-
return nil
512+
return verifyJSONArgsType(ctx, c.funcName, true, args)
478513
}
479514

480515
type builtinJSONMergeSig struct {
@@ -682,10 +717,7 @@ func (c *jsonContainsPathFunctionClass) verifyArgs(ctx EvalContext, args []Expre
682717
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
683718
return err
684719
}
685-
if evalType := args[0].GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
686-
return ErrInvalidTypeForJSON.GenWithStackByArgs(1, "json_contains_path")
687-
}
688-
return nil
720+
return verifyJSONArgsType(ctx, c.funcName, true, args, 0)
689721
}
690722

691723
func (c *jsonContainsPathFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -801,10 +833,7 @@ func (c *jsonMemberOfFunctionClass) verifyArgs(ctx EvalContext, args []Expressio
801833
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
802834
return err
803835
}
804-
if evalType := args[1].GetType(ctx).EvalType(); evalType != types.ETJson && evalType != types.ETString {
805-
return ErrInvalidTypeForJSON.GenWithStackByArgs(2, "member of")
806-
}
807-
return nil
836+
return verifyJSONArgsType(ctx, "member of", true, args, 1)
808837
}
809838

810839
func (c *jsonMemberOfFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -867,13 +896,7 @@ func (c *jsonContainsFunctionClass) verifyArgs(ctx EvalContext, args []Expressio
867896
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
868897
return err
869898
}
870-
if evalType := args[0].GetType(ctx).EvalType(); evalType != types.ETJson && evalType != types.ETString {
871-
return ErrInvalidTypeForJSON.GenWithStackByArgs(1, "json_contains")
872-
}
873-
if evalType := args[1].GetType(ctx).EvalType(); evalType != types.ETJson && evalType != types.ETString {
874-
return ErrInvalidTypeForJSON.GenWithStackByArgs(2, "json_contains")
875-
}
876-
return nil
899+
return verifyJSONArgsType(ctx, c.funcName, true, args, 0, 1)
877900
}
878901

879902
func (c *jsonContainsFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -950,13 +973,7 @@ func (c *jsonOverlapsFunctionClass) verifyArgs(ctx EvalContext, args []Expressio
950973
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
951974
return err
952975
}
953-
if evalType := args[0].GetType(ctx).EvalType(); evalType != types.ETJson && evalType != types.ETString {
954-
return ErrInvalidTypeForJSON.GenWithStackByArgs(1, "json_overlaps")
955-
}
956-
if evalType := args[1].GetType(ctx).EvalType(); evalType != types.ETJson && evalType != types.ETString {
957-
return ErrInvalidTypeForJSON.GenWithStackByArgs(2, "json_overlaps")
958-
}
959-
return nil
976+
return verifyJSONArgsType(ctx, c.funcName, true, args, 0, 1)
960977
}
961978

962979
func (c *jsonOverlapsFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -1284,12 +1301,7 @@ func (c *jsonMergePatchFunctionClass) verifyArgs(ctx EvalContext, args []Express
12841301
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
12851302
return err
12861303
}
1287-
for i, arg := range args {
1288-
if evalType := arg.GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
1289-
return ErrInvalidTypeForJSON.GenWithStackByArgs(i+1, "json_merge_patch")
1290-
}
1291-
}
1292-
return nil
1304+
return verifyJSONArgsType(ctx, c.funcName, true, args)
12931305
}
12941306

12951307
func (c *jsonMergePatchFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -1356,12 +1368,7 @@ func (c *jsonMergePreserveFunctionClass) verifyArgs(ctx EvalContext, args []Expr
13561368
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
13571369
return err
13581370
}
1359-
for i, arg := range args {
1360-
if evalType := arg.GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
1361-
return ErrInvalidTypeForJSON.GenWithStackByArgs(i+1, "json_merge_preserve")
1362-
}
1363-
}
1364-
return nil
1371+
return verifyJSONArgsType(ctx, c.funcName, true, args)
13651372
}
13661373

13671374
func (c *jsonMergePreserveFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -1510,10 +1517,7 @@ func (c *jsonSearchFunctionClass) verifyArgs(ctx EvalContext, args []Expression)
15101517
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
15111518
return err
15121519
}
1513-
if evalType := args[0].GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
1514-
return ErrInvalidTypeForJSON.GenWithStackByArgs(1, "json_search")
1515-
}
1516-
return nil
1520+
return verifyJSONArgsType(ctx, c.funcName, true, args, 0)
15171521
}
15181522

15191523
func (c *jsonSearchFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -1728,10 +1732,7 @@ func (c *jsonKeysFunctionClass) verifyArgs(ctx EvalContext, args []Expression) e
17281732
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
17291733
return err
17301734
}
1731-
if evalType := args[0].GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
1732-
return ErrInvalidTypeForJSON.GenWithStackByArgs(1, "json_keys")
1733-
}
1734-
return nil
1735+
return verifyJSONArgsType(ctx, c.funcName, true, args, 0)
17351736
}
17361737

17371738
func (c *jsonKeysFunctionClass) getFunction(ctx BuildContext, args []Expression) (builtinFunc, error) {
@@ -1903,11 +1904,9 @@ func (c *jsonSchemaValidFunctionClass) verifyArgs(ctx EvalContext, args []Expres
19031904
if err := c.baseFunctionClass.verifyArgs(args); err != nil {
19041905
return err
19051906
}
1906-
if evalType := args[0].GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
1907-
return ErrInvalidTypeForJSON.GenWithStackByArgs(1, "json_schema_valid")
1908-
}
1909-
if evalType := args[1].GetType(ctx).EvalType(); evalType != types.ETString && evalType != types.ETJson {
1910-
return ErrInvalidTypeForJSON.GenWithStackByArgs(2, "json_schema_valid")
1907+
1908+
if err := verifyJSONArgsType(ctx, c.funcName, true, args, 0, 1); err != nil {
1909+
return err
19111910
}
19121911
if c, ok := args[0].(*Constant); ok {
19131912
// 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)