diff --git a/executor/show_test.go b/executor/show_test.go index 7373e1b67910e..34db4d54ae962 100644 --- a/executor/show_test.go +++ b/executor/show_test.go @@ -1394,7 +1394,7 @@ func (s *testSuite5) TestShowBuiltin(c *C) { res := tk.MustQuery("show builtins;") c.Assert(res, NotNil) rows := res.Rows() - const builtinFuncNum = 273 + const builtinFuncNum = 274 c.Assert(builtinFuncNum, Equals, len(rows)) c.Assert("abs", Equals, rows[0][0].(string)) c.Assert("yearweek", Equals, rows[builtinFuncNum-1][0].(string)) diff --git a/expression/builtin.go b/expression/builtin.go index 316e3a2ecb462..1b67239613498 100644 --- a/expression/builtin.go +++ b/expression/builtin.go @@ -772,6 +772,7 @@ var funcs = map[string]functionClass{ ast.IsIPv4Mapped: &isIPv4MappedFunctionClass{baseFunctionClass{ast.IsIPv4Mapped, 1, 1}}, ast.IsIPv6: &isIPv6FunctionClass{baseFunctionClass{ast.IsIPv6, 1, 1}}, ast.IsUsedLock: &isUsedLockFunctionClass{baseFunctionClass{ast.IsUsedLock, 1, 1}}, + ast.IsUUID: &isUUIDFunctionClass{baseFunctionClass{ast.IsUUID, 1, 1}}, ast.MasterPosWait: &masterPosWaitFunctionClass{baseFunctionClass{ast.MasterPosWait, 2, 4}}, ast.NameConst: &nameConstFunctionClass{baseFunctionClass{ast.NameConst, 2, 2}}, ast.ReleaseAllLocks: &releaseAllLocksFunctionClass{baseFunctionClass{ast.ReleaseAllLocks, 0, 0}}, diff --git a/expression/builtin_miscellaneous.go b/expression/builtin_miscellaneous.go index ca9803ea97b9f..82b43d192c84c 100644 --- a/expression/builtin_miscellaneous.go +++ b/expression/builtin_miscellaneous.go @@ -57,6 +57,7 @@ var ( _ functionClass = &vitessHashFunctionClass{} _ functionClass = &uuidToBinFunctionClass{} _ functionClass = &binToUUIDFunctionClass{} + _ functionClass = &isUUIDFunctionClass{} ) var ( @@ -78,6 +79,7 @@ var ( _ builtinFunc = &builtinIsIPv4CompatSig{} _ builtinFunc = &builtinIsIPv4MappedSig{} _ builtinFunc = &builtinIsIPv6Sig{} + _ builtinFunc = &builtinIsUUIDSig{} _ builtinFunc = &builtinUUIDSig{} _ builtinFunc = &builtinVitessHashSig{} _ builtinFunc = &builtinUUIDToBinSig{} @@ -862,6 +864,47 @@ func (c *isUsedLockFunctionClass) getFunction(ctx sessionctx.Context, args []Exp return nil, errFunctionNotExists.GenWithStackByArgs("FUNCTION", "IS_USED_LOCK") } +type isUUIDFunctionClass struct { + baseFunctionClass +} + +func (c *isUUIDFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { + if err := c.verifyArgs(args); err != nil { + return nil, err + } + bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, types.ETInt, types.ETString) + if err != nil { + return nil, err + } + bf.tp.Flen = 1 + sig := &builtinIsUUIDSig{bf} + sig.setPbCode(tipb.ScalarFuncSig_IsUUID) + return sig, nil +} + +type builtinIsUUIDSig struct { + baseBuiltinFunc +} + +func (b *builtinIsUUIDSig) Clone() builtinFunc { + newSig := &builtinIsUUIDSig{} + newSig.cloneFrom(&b.baseBuiltinFunc) + return newSig +} + +// evalInt evals a builtinIsUUIDSig. +// See https://dev.mysql.com/doc/refman/8.0/en/miscellaneous-functions.html#function_is-uuid +func (b *builtinIsUUIDSig) evalInt(row chunk.Row) (int64, bool, error) { + val, isNull, err := b.args[0].EvalString(b.ctx, row) + if err != nil || isNull { + return 0, isNull, err + } + if _, err = uuid.Parse(val); err != nil { + return 0, false, nil + } + return 1, false, nil +} + type masterPosWaitFunctionClass struct { baseFunctionClass } diff --git a/expression/builtin_miscellaneous_test.go b/expression/builtin_miscellaneous_test.go index 9a1c7fd45e689..4de7136b29d14 100644 --- a/expression/builtin_miscellaneous_test.go +++ b/expression/builtin_miscellaneous_test.go @@ -97,6 +97,42 @@ func TestIsIPv4(t *testing.T) { trequire.DatumEqual(t, types.NewDatum(0), r) } +func TestIsUUID(t *testing.T) { + t.Parallel() + ctx := createContext(t) + tests := []struct { + uuid string + expect interface{} + }{ + {"6ccd780c-baba-1026-9564-5b8c656024db", 1}, + {"6CCD780C-BABA-1026-9564-5B8C656024DB", 1}, + {"6ccd780cbaba102695645b8c656024db", 1}, + {"{6ccd780c-baba-1026-9564-5b8c656024db}", 1}, + {"6ccd780c-baba-1026-9564-5b8c6560", 0}, + {"6CCD780C-BABA-1026-9564-5B8C656024DQ", 0}, + // This is a bug in google/uuid#60 + {"{99a9ad03-5298-11ec-8f5c-00ff90147ac3*", 1}, + // This is a format google/uuid support, while mysql doesn't + {"urn:uuid:99a9ad03-5298-11ec-8f5c-00ff90147ac3", 1}, + } + + fc := funcs[ast.IsUUID] + for _, test := range tests { + uuid := types.NewStringDatum(test.uuid) + f, err := fc.getFunction(ctx, datumsToConstants([]types.Datum{uuid})) + require.NoError(t, err) + result, err := evalBuiltinFunc(f, chunk.Row{}) + require.NoError(t, err) + trequire.DatumEqual(t, types.NewDatum(test.expect), result) + } + + var argNull types.Datum + f, _ := fc.getFunction(ctx, datumsToConstants([]types.Datum{argNull})) + r, err := evalBuiltinFunc(f, chunk.Row{}) + require.NoError(t, err) + require.True(t, r.IsNull()) +} + func TestUUID(t *testing.T) { t.Parallel() ctx := createContext(t) diff --git a/expression/builtin_miscellaneous_vec.go b/expression/builtin_miscellaneous_vec.go index 4ffad69ad8660..6e4b16c4cb7dd 100644 --- a/expression/builtin_miscellaneous_vec.go +++ b/expression/builtin_miscellaneous_vec.go @@ -154,6 +154,36 @@ func (b *builtinIsIPv6Sig) vecEvalInt(input *chunk.Chunk, result *chunk.Column) return nil } +func (b *builtinIsUUIDSig) vectorized() bool { + return true +} + +func (b *builtinIsUUIDSig) vecEvalInt(input *chunk.Chunk, result *chunk.Column) error { + n := input.NumRows() + buf, err := b.bufAllocator.get() + if err != nil { + return err + } + defer b.bufAllocator.put(buf) + if err := b.args[0].VecEvalString(b.ctx, input, buf); err != nil { + return err + } + result.ResizeInt64(n, false) + i64s := result.Int64s() + result.MergeNulls(buf) + for i := 0; i < n; i++ { + if result.IsNull(i) { + continue + } + if _, err = uuid.Parse(buf.GetString(i)); err != nil { + i64s[i] = 0 + } else { + i64s[i] = 1 + } + } + return nil +} + func (b *builtinNameConstStringSig) vectorized() bool { return true } diff --git a/expression/builtin_miscellaneous_vec_test.go b/expression/builtin_miscellaneous_vec_test.go index 50c4508fe41be..386f7bc423878 100644 --- a/expression/builtin_miscellaneous_vec_test.go +++ b/expression/builtin_miscellaneous_vec_test.go @@ -108,6 +108,9 @@ var vecBuiltinMiscellaneousCases = map[string][]vecExprBenchCase{ {retEvalType: types.ETString, childrenTypes: []types.EvalType{types.ETString}, geners: []dataGenerator{&uuidBinGener{newDefaultRandGen()}}}, {retEvalType: types.ETString, childrenTypes: []types.EvalType{types.ETString, types.ETInt}, geners: []dataGenerator{&uuidBinGener{newDefaultRandGen()}}}, }, + ast.IsUUID: { + {retEvalType: types.ETInt, childrenTypes: []types.EvalType{types.ETString}, geners: []dataGenerator{&uuidStrGener{newDefaultRandGen()}}}, + }, } func TestVectorizedBuiltinMiscellaneousEvalOneVec(t *testing.T) { diff --git a/go.mod b/go.mod index 54ed673133c77..b047aabfad63b 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,7 @@ require ( github.com/pingcap/sysutil v0.0.0-20210730114356-fcd8a63f68c5 github.com/pingcap/tidb-tools v5.2.2-0.20211019062242-37a8bef2fa17+incompatible github.com/pingcap/tidb/parser v0.0.0-20211011031125-9b13dc409c5e - github.com/pingcap/tipb v0.0.0-20211116093845-e9b045a0bdf8 + github.com/pingcap/tipb v0.0.0-20211201080053-bd104bb270ba github.com/prometheus/client_golang v1.5.1 github.com/prometheus/client_model v0.2.0 github.com/prometheus/common v0.9.1 diff --git a/go.sum b/go.sum index 57c2e38d04ccc..1ab204f14388d 100644 --- a/go.sum +++ b/go.sum @@ -597,8 +597,8 @@ github.com/pingcap/tidb-dashboard v0.0.0-20211008050453-a25c25809529/go.mod h1:O github.com/pingcap/tidb-dashboard v0.0.0-20211107164327-80363dfbe884/go.mod h1:OCXbZTBTIMRcIt0jFsuCakZP+goYRv6IjawKbwLS2TQ= github.com/pingcap/tidb-tools v5.2.2-0.20211019062242-37a8bef2fa17+incompatible h1:c7+izmker91NkjkZ6FgTlmD4k1A5FLOAq+li6Ki2/GY= github.com/pingcap/tidb-tools v5.2.2-0.20211019062242-37a8bef2fa17+incompatible/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM= -github.com/pingcap/tipb v0.0.0-20211116093845-e9b045a0bdf8 h1:Vu/6oq8EFNWgyXRHiclNzTKIu+YKHPCSI/Ba5oVrLtM= -github.com/pingcap/tipb v0.0.0-20211116093845-e9b045a0bdf8/go.mod h1:A7mrd7WHBl1o63LE2bIBGEJMTNWXqhgmYiOvMLxozfs= +github.com/pingcap/tipb v0.0.0-20211201080053-bd104bb270ba h1:Tt5W/maVBUbG+wxg2nfc88Cqj/HiWYb0TJQ2Rfi0UOQ= +github.com/pingcap/tipb v0.0.0-20211201080053-bd104bb270ba/go.mod h1:A7mrd7WHBl1o63LE2bIBGEJMTNWXqhgmYiOvMLxozfs= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= diff --git a/parser/ast/functions.go b/parser/ast/functions.go index e463598838fee..7775f3dbc2029 100644 --- a/parser/ast/functions.go +++ b/parser/ast/functions.go @@ -279,6 +279,7 @@ const ( IsIPv4Mapped = "is_ipv4_mapped" IsIPv6 = "is_ipv6" IsUsedLock = "is_used_lock" + IsUUID = "is_uuid" MasterPosWait = "master_pos_wait" NameConst = "name_const" ReleaseAllLocks = "release_all_locks"