diff --git a/executor/aggfuncs/func_json_objectagg.go b/executor/aggfuncs/func_json_objectagg.go index 9382016582..38f815d572 100644 --- a/executor/aggfuncs/func_json_objectagg.go +++ b/executor/aggfuncs/func_json_objectagg.go @@ -79,6 +79,10 @@ func (e *jsonObjectAgg) UpdatePartialResult(sctx sessionctx.Context, rowsInGroup return 0, json.ErrJSONDocumentNULLKey } + if e.args[0].GetType().GetCharset() == charset.CharsetBin { + return 0, json.ErrInvalidJSONCharset.GenWithStackByArgs(e.args[0].GetType().GetCharset()) + } + value, err := e.args[1].Eval(row) if err != nil { return 0, errors.Trace(err) diff --git a/executor/aggfuncs/func_json_objectagg_test.go b/executor/aggfuncs/func_json_objectagg_test.go index 7670d48597..ce30ec48bf 100644 --- a/executor/aggfuncs/func_json_objectagg_test.go +++ b/executor/aggfuncs/func_json_objectagg_test.go @@ -56,6 +56,10 @@ func TestMergePartialResult4JsonObjectagg(t *testing.T) { } var argCombines [][]*types.FieldType for i := 0; i < len(typeList); i++ { + if typeList[i].GetCharset() == charset.CharsetBin { + // skip because binary charset cannot be used as key. + continue + } for j := 0; j < len(typeList); j++ { argTypes := []*types.FieldType{typeList[i], typeList[j]} argCombines = append(argCombines, argTypes) @@ -112,6 +116,10 @@ func TestJsonObjectagg(t *testing.T) { } var argCombines [][]*types.FieldType for i := 0; i < len(typeList); i++ { + if typeList[i].GetCharset() == charset.CharsetBin { + // skip because binary charset cannot be used as key. + continue + } for j := 0; j < len(typeList); j++ { argTypes := []*types.FieldType{typeList[i], typeList[j]} argCombines = append(argCombines, argTypes) diff --git a/expression/aggregation/base_func.go b/expression/aggregation/base_func.go index 982c48cb2d..b1820ee2fb 100644 --- a/expression/aggregation/base_func.go +++ b/expression/aggregation/base_func.go @@ -119,7 +119,7 @@ func (a *baseFuncDesc) TypeInfer(ctx sessionctx.Context) error { case ast.AggFuncJsonArrayagg: a.typeInfer4JsonArrayAgg(ctx) case ast.AggFuncJsonObjectAgg: - a.typeInfer4JsonObjectAgg(ctx) + return a.typeInfer4JsonObjectAgg(ctx) default: return errors.Errorf("unsupported agg function: %s", a.Name) } @@ -294,10 +294,11 @@ func (a *baseFuncDesc) typeInfer4JsonArrayAgg(ctx sessionctx.Context) { types.SetBinChsClnFlag(a.RetTp) } -func (a *baseFuncDesc) typeInfer4JsonObjectAgg(ctx sessionctx.Context) { +func (a *baseFuncDesc) typeInfer4JsonObjectAgg(ctx sessionctx.Context) error { a.RetTp = types.NewFieldType(mysql.TypeJSON) types.SetBinChsClnFlag(a.RetTp) a.Args[0] = expression.WrapWithCastAsString(ctx, a.Args[0]) + return nil } func (a *baseFuncDesc) typeInfer4NumberFuncs() { diff --git a/expression/builtin_json.go b/expression/builtin_json.go index aa0d3f8fcc..d7af9b5c07 100644 --- a/expression/builtin_json.go +++ b/expression/builtin_json.go @@ -22,6 +22,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/parser/ast" + "github.com/pingcap/tidb/parser/charset" "github.com/pingcap/tidb/parser/mysql" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/types" @@ -496,6 +497,9 @@ func (c *jsonObjectFunctionClass) getFunction(ctx sessionctx.Context, args []Exp } argTps := make([]types.EvalType, 0, len(args)) for i := 0; i < len(args)-1; i += 2 { + if args[i].GetType().EvalType() == types.ETString && args[i].GetType().GetCharset() == charset.CharsetBin { + return nil, json.ErrInvalidJSONCharset.GenWithStackByArgs(args[i].GetType().GetCharset()) + } argTps = append(argTps, types.ETString, types.ETJson) } bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETJson, argTps...) diff --git a/expression/integration_test.go b/expression/integration_test.go index 0cda433f9a..137783a0d9 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -7449,6 +7449,20 @@ func TestIssue36358(t *testing.T) { tk.MustQuery("select extract(day_microsecond from c) from t").Check(testkit.Rows("1020304050607")) } +func TestJSONObjectWithBinaryCharset(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a char(20), b blob);") + tk.MustExec("insert into t values ('a string', 'a binary string');") + tk.MustExec("select json_object(a, b) from t;") + tk.MustExec("select json_objectagg(a, b) from t;") + tk.MustGetErrCode("select json_object(b, a) from t;", errno.ErrInvalidJSONCharset) + err := tk.QueryToErr("select json_objectagg(b, a) from t;") + require.Error(t, err) + require.Equal(t, "[json:3144]Cannot create a JSON value from a string with CHARACTER SET 'binary'.", err.Error()) +} + func TestCastJSONOpaqueValueToNumeric(t *testing.T) { store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store)