github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/convert_test.go (about)

     1  // Copyright 2020-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 expression
    16  
    17  import (
    18  	"math"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/shopspring/decimal"
    23  	"github.com/stretchr/testify/require"
    24  
    25  	"github.com/dolthub/go-mysql-server/sql"
    26  	"github.com/dolthub/go-mysql-server/sql/types"
    27  )
    28  
    29  func TestConvert(t *testing.T) {
    30  	tests := []struct {
    31  		name        string
    32  		row         sql.Row
    33  		expression  sql.Expression
    34  		castTo      string
    35  		typeLength  int
    36  		typeScale   int
    37  		expected    interface{}
    38  		expectedErr bool
    39  	}{
    40  		{
    41  			name:        "convert int32 to signed",
    42  			row:         nil,
    43  			expression:  NewLiteral(int32(1), types.Int32),
    44  			castTo:      ConvertToSigned,
    45  			expected:    int64(1),
    46  			expectedErr: false,
    47  		},
    48  		{
    49  			name:        "convert int32 to unsigned",
    50  			row:         nil,
    51  			expression:  NewLiteral(int32(-5), types.Int32),
    52  			castTo:      ConvertToUnsigned,
    53  			expected:    uint64(math.MaxUint64 - 4),
    54  			expectedErr: false,
    55  		},
    56  		{
    57  			name:        "convert int32 to float",
    58  			row:         nil,
    59  			expression:  NewLiteral(int32(-5), types.Int32),
    60  			castTo:      ConvertToFloat,
    61  			expected:    float32(-5.0),
    62  			expectedErr: false,
    63  		},
    64  		{
    65  			name:        "convert int32 to double",
    66  			row:         nil,
    67  			expression:  NewLiteral(int32(-5), types.Int32),
    68  			castTo:      ConvertToDouble,
    69  			expected:    -5.0,
    70  			expectedErr: false,
    71  		},
    72  		{
    73  			name:        "convert string to signed",
    74  			row:         nil,
    75  			expression:  NewLiteral("-3", types.LongText),
    76  			castTo:      ConvertToSigned,
    77  			expected:    int64(-3),
    78  			expectedErr: false,
    79  		},
    80  		{
    81  			name:        "convert string to unsigned",
    82  			row:         nil,
    83  			expression:  NewLiteral("-3", types.LongText),
    84  			castTo:      ConvertToUnsigned,
    85  			expected:    uint64(18446744073709551613),
    86  			expectedErr: false,
    87  		},
    88  		{
    89  			name:        "convert string to double",
    90  			row:         nil,
    91  			expression:  NewLiteral("-3.123", types.LongText),
    92  			castTo:      ConvertToDouble,
    93  			expected:    -3.123,
    94  			expectedErr: false,
    95  		},
    96  		{
    97  			name:        "convert string to decimal with precision/scale constraints",
    98  			row:         nil,
    99  			expression:  NewLiteral("10.123456", types.LongText),
   100  			typeLength:  4,
   101  			typeScale:   2,
   102  			castTo:      ConvertToDecimal,
   103  			expected:    "10.12",
   104  			expectedErr: false,
   105  		},
   106  		{
   107  			name:        "convert int to string",
   108  			row:         nil,
   109  			expression:  NewLiteral(-3, types.Int32),
   110  			castTo:      ConvertToChar,
   111  			expected:    "-3",
   112  			expectedErr: false,
   113  		},
   114  		{
   115  			name:        "convert int to string with length constraint",
   116  			row:         nil,
   117  			expression:  NewLiteral(-3, types.Int32),
   118  			castTo:      ConvertToChar,
   119  			typeLength:  1,
   120  			expected:    "-",
   121  			expectedErr: false,
   122  		},
   123  		{
   124  			name:        "convert int to string with length constraint larger than value",
   125  			row:         nil,
   126  			expression:  NewLiteral(-3, types.Int32),
   127  			castTo:      ConvertToChar,
   128  			typeLength:  10,
   129  			expected:    "-3",
   130  			expectedErr: false,
   131  		},
   132  		{
   133  			name:        "impossible conversion string to unsigned",
   134  			row:         nil,
   135  			expression:  NewLiteral("hello", types.LongText),
   136  			castTo:      ConvertToUnsigned,
   137  			expected:    uint64(0),
   138  			expectedErr: false,
   139  		},
   140  		{
   141  			name:        "impossible conversion string to signed",
   142  			row:         nil,
   143  			castTo:      ConvertToSigned,
   144  			expression:  NewLiteral("A", types.LongText),
   145  			expected:    int64(0),
   146  			expectedErr: false,
   147  		},
   148  		{
   149  			name:        "impossible conversion string to double",
   150  			row:         nil,
   151  			castTo:      ConvertToDouble,
   152  			expression:  NewLiteral("A", types.LongText),
   153  			expected:    0.0,
   154  			expectedErr: false,
   155  		},
   156  		{
   157  			name:        "string to datetime",
   158  			row:         nil,
   159  			expression:  NewLiteral("2017-12-12", types.LongText),
   160  			castTo:      ConvertToDatetime,
   161  			expected:    time.Date(2017, time.December, 12, 0, 0, 0, 0, time.UTC),
   162  			expectedErr: false,
   163  		},
   164  		{
   165  			name:        "impossible conversion string to datetime",
   166  			row:         nil,
   167  			castTo:      ConvertToDatetime,
   168  			expression:  NewLiteral(1, types.Int32),
   169  			expected:    nil,
   170  			expectedErr: false,
   171  		},
   172  		{
   173  			name:        "string to date",
   174  			row:         nil,
   175  			castTo:      ConvertToDate,
   176  			expression:  NewLiteral("2017-12-12 11:12:13", types.Int32),
   177  			expected:    time.Date(2017, time.December, 12, 0, 0, 0, 0, time.UTC),
   178  			expectedErr: false,
   179  		},
   180  		{
   181  			name:        "impossible conversion string to date",
   182  			row:         nil,
   183  			castTo:      ConvertToDate,
   184  			expression:  NewLiteral(1, types.Int32),
   185  			expected:    nil,
   186  			expectedErr: false,
   187  		},
   188  		{
   189  			name:        "float to binary",
   190  			row:         nil,
   191  			castTo:      ConvertToBinary,
   192  			expression:  NewLiteral(float64(-2.3), types.Float64),
   193  			expected:    []byte("-2.3"),
   194  			expectedErr: false,
   195  		},
   196  		{
   197  			name:        "float to binary with length restriction",
   198  			row:         nil,
   199  			castTo:      ConvertToBinary,
   200  			typeLength:  3,
   201  			expression:  NewLiteral(float64(-2.3), types.Float64),
   202  			expected:    []byte("-2."),
   203  			expectedErr: false,
   204  		},
   205  		{
   206  			name:        "float to char with length restriction",
   207  			row:         nil,
   208  			castTo:      ConvertToChar,
   209  			typeLength:  4,
   210  			expression:  NewLiteral(10.56789, types.Float32),
   211  			expected:    "10.5",
   212  			expectedErr: false,
   213  		},
   214  		{
   215  			name:        "float to char with length restriction larger than value",
   216  			row:         nil,
   217  			castTo:      ConvertToChar,
   218  			typeLength:  40,
   219  			expression:  NewLiteral(10.56789, types.Float32),
   220  			expected:    "10.56789",
   221  			expectedErr: false,
   222  		},
   223  		{
   224  			name:        "string to json",
   225  			row:         nil,
   226  			castTo:      ConvertToJSON,
   227  			expression:  NewLiteral(`{"a":2}`, types.LongText),
   228  			expected:    types.MustJSON(`{"a":2}`),
   229  			expectedErr: false,
   230  		},
   231  		{
   232  			name:        "int to json",
   233  			row:         nil,
   234  			castTo:      ConvertToJSON,
   235  			expression:  NewLiteral(2, types.Int32),
   236  			expected:    types.JSONDocument{Val: float64(2)},
   237  			expectedErr: false,
   238  		},
   239  		{
   240  			name:        "impossible conversion string to json",
   241  			row:         nil,
   242  			castTo:      ConvertToJSON,
   243  			expression:  NewLiteral("3>2", types.LongText),
   244  			expected:    nil,
   245  			expectedErr: true,
   246  		},
   247  		{
   248  			name:        "bool to signed",
   249  			row:         nil,
   250  			castTo:      ConvertToSigned,
   251  			expression:  NewLiteral(true, types.Boolean),
   252  			expected:    int64(1),
   253  			expectedErr: false,
   254  		},
   255  		{
   256  			name:        "bool to datetime",
   257  			row:         nil,
   258  			castTo:      ConvertToDatetime,
   259  			expression:  NewLiteral(true, types.Boolean),
   260  			expected:    nil,
   261  			expectedErr: false,
   262  		},
   263  	}
   264  
   265  	for _, test := range tests {
   266  		t.Run(test.name, func(t *testing.T) {
   267  			require := require.New(t)
   268  			convert := NewConvertWithLengthAndScale(test.expression, test.castTo, test.typeLength, test.typeScale)
   269  			val, err := convert.Eval(sql.NewEmptyContext(), test.row)
   270  			if test.expectedErr {
   271  				require.Error(err)
   272  			} else {
   273  				require.NoError(err)
   274  			}
   275  
   276  			// Convert any Decimal values to strings for easier comparison (same as we do for engine tests)
   277  			if d, ok := val.(decimal.Decimal); ok {
   278  				val = d.String()
   279  			}
   280  
   281  			require.Equal(test.expected, val)
   282  		})
   283  	}
   284  }