From e13113a14ae8db940c397966eef1ea69046bed12 Mon Sep 17 00:00:00 2001 From: YangKeao Date: Thu, 26 Oct 2023 20:12:34 +0800 Subject: [PATCH] types: fix the behavior of casting json string to integers (#48010) close pingcap/tidb#47864 --- pkg/types/convert.go | 15 ++++++++++----- tests/integrationtest/r/expression/json.result | 15 +++++++++++++++ tests/integrationtest/t/expression/json.test | 10 ++++++++++ 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/pkg/types/convert.go b/pkg/types/convert.go index fc98f1fb5a1e2..d1de90bdb1000 100644 --- a/pkg/types/convert.go +++ b/pkg/types/convert.go @@ -630,12 +630,17 @@ func ConvertJSONToInt(sc *stmtctx.StatementContext, j BinaryJSON, unsigned bool, return int64(u), sc.HandleOverflow(err, err) case JSONTypeCodeString: str := string(hack.String(j.GetString())) - if !unsigned { - r, e := StrToInt(sc.TypeCtxOrDefault(), str, false) - return r, sc.HandleOverflow(e, e) + // The behavior of casting json string as an integer is consistent with casting a string as an integer. + // See the `builtinCastStringAsIntSig` in `expression` pkg. The only difference is that this function + // doesn't append any warning. This behavior is compatible with MySQL. + isNegative := len(str) > 1 && str[0] == '-' + if !isNegative { + r, err := StrToUint(sc.TypeCtxOrDefault(), str, false) + return int64(r), sc.HandleOverflow(err, err) } - u, err := StrToUint(sc.TypeCtxOrDefault(), str, false) - return int64(u), sc.HandleOverflow(err, err) + + r, err := StrToInt(sc.TypeCtxOrDefault(), str, false) + return r, sc.HandleOverflow(err, err) } return 0, errors.New("Unknown type code in JSON") } diff --git a/tests/integrationtest/r/expression/json.result b/tests/integrationtest/r/expression/json.result index 8a1472456f31d..243a8388b0ab9 100644 --- a/tests/integrationtest/r/expression/json.result +++ b/tests/integrationtest/r/expression/json.result @@ -602,3 +602,18 @@ json_extract('[{"a": [1,2,3,4]}]', '$[0].a[0 to last]') select json_extract('[{"a": [1,2,3,4]}]', '$[0].a[0 to 2]'); json_extract('[{"a": [1,2,3,4]}]', '$[0].a[0 to 2]') [1, 2, 3] +drop table if exists t; +create table t (a json); +insert into t values ('"-1"'); +insert into t values ('"18446744073709551615"'); +insert into t values ('"18446744073709552000"'); +select a, cast(a as unsigned) from t; +a cast(a as unsigned) +"-1" 18446744073709551615 +"18446744073709551615" 18446744073709551615 +"18446744073709552000" 18446744073709551615 +select a, cast(a as signed) from t; +a cast(a as signed) +"-1" -1 +"18446744073709551615" -1 +"18446744073709552000" -1 diff --git a/tests/integrationtest/t/expression/json.test b/tests/integrationtest/t/expression/json.test index ca41e0b701fea..142b46288ce18 100644 --- a/tests/integrationtest/t/expression/json.test +++ b/tests/integrationtest/t/expression/json.test @@ -359,3 +359,13 @@ select json_extract('[{"a": [1,2,3,4]}]', '$[0].a[1 to 100]'); select json_extract('[{"a": [1,2,3,4]}]', '$[0].a[0 to last]'); select json_extract('[{"a": [1,2,3,4]}]', '$[0].a[0 to 2]'); +# TestCastJSONStringToInteger +drop table if exists t; +create table t (a json); +insert into t values ('"-1"'); +insert into t values ('"18446744073709551615"'); +insert into t values ('"18446744073709552000"'); +-- sorted_result +select a, cast(a as unsigned) from t; +-- sorted_result +select a, cast(a as signed) from t;