github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/uuid_test.go (about) 1 // Copyright 2021 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package function 16 17 import ( 18 "regexp" 19 "testing" 20 21 "github.com/dolthub/vitess/go/vt/proto/query" 22 "github.com/google/uuid" 23 "github.com/stretchr/testify/require" 24 25 "github.com/dolthub/go-mysql-server/sql" 26 "github.com/dolthub/go-mysql-server/sql/expression" 27 "github.com/dolthub/go-mysql-server/sql/types" 28 ) 29 30 func TestUUID(t *testing.T) { 31 ctx := sql.NewEmptyContext() 32 // Generate a UUID and validate that is a legitimate uuid 33 uuidE := NewUUIDFunc() 34 35 result, err := uuidE.Eval(ctx, sql.Row{nil}) 36 require.NoError(t, err) 37 38 myUUID := result.(string) 39 _, err = uuid.Parse(myUUID) 40 require.NoError(t, err) 41 42 // validate that generated uuid is legitimate for IsUUID 43 val := NewIsUUID(uuidE) 44 require.Equal(t, int8(1), eval(t, val, sql.Row{nil})) 45 46 // Use a UUID regex as a sanity check 47 re2 := regexp.MustCompile(`\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b`) 48 require.True(t, re2.MatchString(myUUID)) 49 } 50 51 func TestIsUUID(t *testing.T) { 52 testCases := []struct { 53 name string 54 rowType sql.Type 55 value interface{} 56 expected interface{} 57 }{ 58 {"uuid form 1", types.LongText, "{12345678-1234-5678-1234-567812345678}", int8(1)}, 59 {"uuid form 2", types.LongText, "12345678123456781234567812345678", int8(1)}, 60 {"uuid form 3", types.LongText, "12345678-1234-5678-1234-567812345678", int8(1)}, 61 {"NULL", types.Null, nil, nil}, 62 {"random int", types.Int8, 1, int8(0)}, 63 {"random bool", types.Boolean, false, int8(0)}, 64 {"random string", types.LongText, "12345678-dasd-fasdf8", int8(0)}, 65 {"swapped uuid", types.LongText, "5678-1234-12345678-1234-567812345678", int8(0)}, 66 } 67 68 for _, tt := range testCases { 69 f := NewIsUUID(expression.NewLiteral(tt.value, tt.rowType)) 70 71 t.Run(tt.name, func(t *testing.T) { 72 require.Equal(t, tt.expected, eval(t, f, sql.Row{nil})) 73 }) 74 75 req := require.New(t) 76 req.False(f.IsNullable()) 77 } 78 } 79 80 func TestUUIDToBinValid(t *testing.T) { 81 validTestCases := []struct { 82 name string 83 uuidType sql.Type 84 uuid interface{} 85 hasSwap bool 86 swapType sql.Type 87 swapValue interface{} 88 expected interface{} 89 }{ 90 {"valid uuid; swap=0", types.LongText, "6ccd780c-baba-1026-9564-5b8c656024db", true, types.Int8, int8(0), "6CCD780CBABA102695645B8C656024DB"}, 91 {"valid uuid; swap=nil", types.LongText, "6ccd780c-baba-1026-9564-5b8c656024db", true, types.Null, nil, "6CCD780CBABA102695645B8C656024DB"}, 92 {"valid uuid; swap=1", types.LongText, "6ccd780c-baba-1026-9564-5b8c656024db", true, types.Int8, int8(1), "1026BABA6CCD780C95645B8C656024DB"}, 93 {"valid uuid; no swap", types.LongText, "6ccd780c-baba-1026-9564-5b8c656024db", false, nil, nil, "6CCD780CBABA102695645B8C656024DB"}, 94 {"null uuid; no swap", types.Null, nil, false, nil, nil, nil}, 95 } 96 97 for _, tt := range validTestCases { 98 var f sql.Expression 99 var err error 100 101 if tt.hasSwap { 102 f, err = NewUUIDToBin(expression.NewLiteral(tt.uuid, tt.uuidType), expression.NewLiteral(tt.swapValue, tt.swapType)) 103 } else { 104 f, err = NewUUIDToBin(expression.NewLiteral(tt.uuid, tt.uuidType)) 105 } 106 107 require.NoError(t, err) 108 109 // Convert to hex to make testing easier 110 h := NewHex(f) 111 112 t.Run(tt.name, func(t *testing.T) { 113 require.Equal(t, tt.expected, eval(t, h, sql.Row{nil})) 114 }) 115 116 req := require.New(t) 117 req.False(f.IsNullable()) 118 } 119 } 120 121 func TestUUIDToBinFailing(t *testing.T) { 122 failingTestCases := []struct { 123 name string 124 uuidType sql.Type 125 uuid interface{} 126 swapType sql.Type 127 swapValue interface{} 128 }{ 129 {"bad swap value", types.LongText, "6ccd780c-baba-1026-9564-5b8c656024db", types.Int8, int8(2)}, 130 {"bad uuid value", types.LongText, "sdasdsad", types.Int8, int8(0)}, 131 {"bad uuid value2", types.Int8, int8(0), types.Int8, int8(0)}, 132 } 133 134 for _, tt := range failingTestCases { 135 f, err := NewUUIDToBin(expression.NewLiteral(tt.uuid, tt.uuidType), expression.NewLiteral(tt.swapValue, tt.swapType)) 136 require.NoError(t, err) 137 138 t.Run(tt.name, func(t *testing.T) { 139 ctx := sql.NewEmptyContext() 140 _, err := f.Eval(ctx, sql.Row{nil}) 141 require.Error(t, err) 142 }) 143 } 144 } 145 146 func TestBinToUUID(t *testing.T) { 147 // Test that UUID_TO_BIN to BIN_TO_UUID is reflexive 148 uuidE := eval(t, NewUUIDFunc(), sql.Row{nil}) 149 150 f, err := NewUUIDToBin(expression.NewLiteral(uuidE, types.LongText)) 151 require.NoError(t, err) 152 153 retUUID, err := NewBinToUUID(f) 154 require.NoError(t, err) 155 156 require.Equal(t, uuidE, eval(t, retUUID, sql.Row{nil})) 157 158 // Run UUID_TO_BIN through a series of test cases. 159 validTestCases := []struct { 160 name string 161 uuidType sql.Type 162 binary interface{} 163 hasSwap bool 164 swapType sql.Type 165 swapValue interface{} 166 expected interface{} 167 }{ 168 {"valid uuid; swap=0", types.MustCreateBinary(query.Type_VARBINARY, int64(16)), []byte("lxºº & d[e`$Û"), true, types.Int8, int8(0), "6c78c2ba-c2ba-2026-2064-5b656024c39b"}, 169 {"valid uuid; swap=1", types.MustCreateBinary(query.Type_VARBINARY, int64(16)), []byte("&ººlÍxd[e`$Û"), true, types.Int8, int8(1), "ba6cc38d-bac2-26c2-7864-5b656024c39b"}, 170 {"valid uuid; no swap", types.MustCreateBinary(query.Type_VARBINARY, int64(16)), []byte("lxºº & d[e`$Û"), false, nil, nil, "6c78c2ba-c2ba-2026-2064-5b656024c39b"}, 171 {"null input", types.Null, nil, false, nil, nil, nil}, 172 } 173 174 for _, tt := range validTestCases { 175 var f sql.Expression 176 var err error 177 178 if tt.hasSwap { 179 f, err = NewBinToUUID(expression.NewLiteral(tt.binary, tt.uuidType), expression.NewLiteral(tt.swapValue, tt.swapType)) 180 } else { 181 f, err = NewBinToUUID(expression.NewLiteral(tt.binary, tt.uuidType)) 182 } 183 require.NoError(t, err) 184 185 t.Run(tt.name, func(t *testing.T) { 186 require.Equal(t, tt.expected, eval(t, f, sql.Row{nil})) 187 }) 188 189 req := require.New(t) 190 req.False(f.IsNullable()) 191 } 192 } 193 194 func TestBinToUUIDFailing(t *testing.T) { 195 failingTestCases := []struct { 196 name string 197 uuidType sql.Type 198 uuid interface{} 199 swapType sql.Type 200 swapValue interface{} 201 }{ 202 {"bad swap value", types.MustCreateBinary(query.Type_VARBINARY, int64(16)), "helo", types.Int8, int8(2)}, 203 {"bad binary value", types.MustCreateBinary(query.Type_VARBINARY, int64(16)), "sdasdsad", types.Int8, int8(0)}, 204 {"bad input value", types.Int8, int8(0), types.Int8, int8(0)}, 205 } 206 207 for _, tt := range failingTestCases { 208 f, err := NewBinToUUID(expression.NewLiteral(tt.uuid, tt.uuidType), expression.NewLiteral(tt.swapValue, tt.swapType)) 209 require.NoError(t, err) 210 211 t.Run(tt.name, func(t *testing.T) { 212 ctx := sql.NewEmptyContext() 213 _, err := f.Eval(ctx, sql.Row{nil}) 214 require.Error(t, err) 215 }) 216 } 217 }