diff --git a/expression/builtin_cast.go b/expression/builtin_cast.go index 0ec4ad904dee4..ebf1903a569ba 100644 --- a/expression/builtin_cast.go +++ b/expression/builtin_cast.go @@ -285,6 +285,11 @@ func (c *castAsStringFunctionClass) getFunction(ctx sessionctx.Context, args []E } bf.tp = c.tp if args[0].GetType().Hybrid() || IsBinaryLiteral(args[0]) { + // When cast from binary to some other charsets, we should check if the binary is valid or not. + // so we build a from_binary function to do this check. + ft := args[0].GetType().Clone() + ft.Charset, ft.Collate = c.tp.Charset, c.tp.Collate + bf.args[0] = BuildFromBinaryFunction(ctx, args[0], ft) sig = &builtinCastStringAsStringSig{bf} sig.setPbCode(tipb.ScalarFuncSig_CastStringAsString) return sig, nil diff --git a/expression/builtin_cast_test.go b/expression/builtin_cast_test.go index 3333709499007..73620773f5a81 100644 --- a/expression/builtin_cast_test.go +++ b/expression/builtin_cast_test.go @@ -1398,6 +1398,37 @@ func TestWrapWithCastAsDuration(t *testing.T) { } } +func TestWrapWithCastAsString(t *testing.T) { + t.Parallel() + ctx := createContext(t) + + cases := []struct { + expr Expression + err bool + ret string + }{ + { + &Constant{RetType: types.NewFieldTypeWithCollation(mysql.TypeVarString, charset.CollationBin, 1), Value: types.NewBinaryLiteralDatum([]byte{0x91})}, + true, + "", + }, + { + &Constant{RetType: types.NewFieldTypeWithCollation(mysql.TypeVarString, charset.CollationBin, 1), Value: types.NewBinaryLiteralDatum([]byte{0x61})}, + false, + "a", + }, + } + for _, c := range cases { + expr := BuildCastFunction(ctx, c.expr, types.NewFieldType(mysql.TypeVarString)) + res, _, err := expr.EvalString(ctx, chunk.Row{}) + if c.err { + require.Error(t, err) + } else { + require.Equal(t, c.ret, res) + } + } +} + func TestWrapWithCastAsJSON(t *testing.T) { t.Parallel() ctx := createContext(t)