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  }