diff --git a/expression/integration_test.go b/expression/integration_test.go index a5021ab2e21fd..11c5743a4a000 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -7435,3 +7435,15 @@ func TestIssue36358(t *testing.T) { tk.MustQuery("select extract(day_microsecond from cast('2001-01-01 02:03:04.050607' as datetime(6))) from t").Check(testkit.Rows("1020304050607")) tk.MustQuery("select extract(day_microsecond from c) from t").Check(testkit.Rows("1020304050607")) } + +func TestCastJSONOpaqueValueToNumeric(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustQuery("select cast(cast(b'010101' as json) as signed);").Check(testkit.Rows("0")) + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect INTEGER value: '\"base64:type253:FQ==\"'")) + tk.MustQuery("select cast(json_extract(json_objectagg('a', b'010101'), '$.a') as signed);").Check(testkit.Rows("0")) + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect INTEGER value: '\"base64:type253:FQ==\"'")) + tk.MustQuery("select cast(json_extract(json_objectagg('a', b'010101'), '$.a') as double);").Check(testkit.Rows("0")) + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect FLOAT value: '\"base64:type253:FQ==\"'")) +} diff --git a/types/convert.go b/types/convert.go index a6e33d5b2c51b..f97eed3d24178 100644 --- a/types/convert.go +++ b/types/convert.go @@ -555,7 +555,7 @@ func ConvertJSONToInt64(sc *stmtctx.StatementContext, j json.BinaryJSON, unsigne // ConvertJSONToInt casts JSON into int by type. func ConvertJSONToInt(sc *stmtctx.StatementContext, j json.BinaryJSON, unsigned bool, tp byte) (int64, error) { switch j.TypeCode { - case json.TypeCodeObject, json.TypeCodeArray: + case json.TypeCodeObject, json.TypeCodeArray, json.TypeCodeOpaque: return 0, sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("INTEGER", j.String())) case json.TypeCodeLiteral: switch j.Value[0] { @@ -615,7 +615,7 @@ func ConvertJSONToInt(sc *stmtctx.StatementContext, j json.BinaryJSON, unsigned // ConvertJSONToFloat casts JSON into float64. func ConvertJSONToFloat(sc *stmtctx.StatementContext, j json.BinaryJSON) (float64, error) { switch j.TypeCode { - case json.TypeCodeObject, json.TypeCodeArray: + case json.TypeCodeObject, json.TypeCodeArray, json.TypeCodeOpaque: return 0, sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("FLOAT", j.String())) case json.TypeCodeLiteral: switch j.Value[0] { @@ -644,7 +644,7 @@ func ConvertJSONToDecimal(sc *stmtctx.StatementContext, j json.BinaryJSON) (*MyD var err error = nil res := new(MyDecimal) switch j.TypeCode { - case json.TypeCodeObject, json.TypeCodeArray: + case json.TypeCodeObject, json.TypeCodeArray, json.TypeCodeOpaque: err = ErrTruncatedWrongVal.GenWithStackByArgs("DECIMAL", j.String()) case json.TypeCodeLiteral: switch j.Value[0] { diff --git a/types/json/binary.go b/types/json/binary.go index 1a2767fd0c89a..d03932722ba65 100644 --- a/types/json/binary.go +++ b/types/json/binary.go @@ -173,6 +173,9 @@ func (bj BinaryJSON) IsZero() bool { isZero = false case TypeCodeObject: isZero = false + // FIXME: TiDB always casts the json to double BINARY so this function will never be called. + case TypeCodeOpaque: + isZero = false } return isZero } diff --git a/types/json/binary_functions.go b/types/json/binary_functions.go index 53b4fe6a08363..49ec801d794bf 100644 --- a/types/json/binary_functions.go +++ b/types/json/binary_functions.go @@ -627,7 +627,7 @@ func (bm *binaryModifier) rebuildTo(buf []byte) ([]byte, TypeCode) { } bj := bm.bj switch bj.TypeCode { - case TypeCodeLiteral, TypeCodeInt64, TypeCodeUint64, TypeCodeFloat64, TypeCodeString: + case TypeCodeLiteral, TypeCodeInt64, TypeCodeUint64, TypeCodeFloat64, TypeCodeString, TypeCodeOpaque: return append(buf, bj.Value...), bj.TypeCode } docOff := len(buf) @@ -1008,6 +1008,9 @@ func PeekBytesAsJSON(b []byte) (n int, err error) { case TypeCodeLiteral: n = valTypeSize + 1 return + case TypeCodeOpaque: + bufLen, lenLen := binary.Uvarint(b[valTypeSize+1:]) + return valTypeSize + 1 + int(bufLen) + lenLen, nil } err = errors.New("Invalid JSON bytes") return diff --git a/util/codec/codec_test.go b/util/codec/codec_test.go index a1a5461dcd5fa..0d74b2a189364 100644 --- a/util/codec/codec_test.go +++ b/util/codec/codec_test.go @@ -872,6 +872,10 @@ func TestCut(t *testing.T) { types.MakeDatums(json.CreateBinary("abc")), types.MakeDatums(json.CreateBinary("abc")), }, + { + types.MakeDatums(json.CreateBinary(json.Opaque{TypeCode: mysql.TypeString, Buf: []byte("abc")})), + types.MakeDatums(json.CreateBinary(json.Opaque{TypeCode: mysql.TypeString, Buf: []byte("abc")})), + }, } sc := &stmtctx.StatementContext{TimeZone: time.Local} for i, datums := range table {