vitess.io/vitess@v0.16.2/go/vt/vtgate/evalengine/arithmetic_test.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package evalengine
    18  
    19  import (
    20  	"encoding/binary"
    21  	"fmt"
    22  	"math"
    23  	"reflect"
    24  	"strconv"
    25  	"testing"
    26  
    27  	"vitess.io/vitess/go/mysql/collations"
    28  	"vitess.io/vitess/go/test/utils"
    29  
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  
    33  	"vitess.io/vitess/go/sqltypes"
    34  
    35  	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
    36  	"vitess.io/vitess/go/vt/vterrors"
    37  )
    38  
    39  var (
    40  	NULL       = sqltypes.NULL
    41  	NewInt32   = sqltypes.NewInt32
    42  	NewInt64   = sqltypes.NewInt64
    43  	NewUint64  = sqltypes.NewUint64
    44  	NewFloat64 = sqltypes.NewFloat64
    45  	TestValue  = sqltypes.TestValue
    46  	NewDecimal = sqltypes.NewDecimal
    47  
    48  	maxUint64 uint64 = math.MaxUint64
    49  )
    50  
    51  func TestArithmetics(t *testing.T) {
    52  	type tcase struct {
    53  		v1, v2, out sqltypes.Value
    54  		err         string
    55  	}
    56  
    57  	tests := []struct {
    58  		operator string
    59  		f        func(a, b sqltypes.Value) (sqltypes.Value, error)
    60  		cases    []tcase
    61  	}{{
    62  		operator: "-",
    63  		f:        Subtract,
    64  		cases: []tcase{{
    65  			// All Nulls
    66  			v1:  NULL,
    67  			v2:  NULL,
    68  			out: NULL,
    69  		}, {
    70  			// First value null.
    71  			v1:  NewInt32(1),
    72  			v2:  NULL,
    73  			out: NULL,
    74  		}, {
    75  			// Second value null.
    76  			v1:  NULL,
    77  			v2:  NewInt32(1),
    78  			out: NULL,
    79  		}, {
    80  			// case with negative value
    81  			v1:  NewInt64(-1),
    82  			v2:  NewInt64(-2),
    83  			out: NewInt64(1),
    84  		}, {
    85  			// testing for int64 overflow with min negative value
    86  			v1:  NewInt64(math.MinInt64),
    87  			v2:  NewInt64(1),
    88  			err: dataOutOfRangeError(math.MinInt64, 1, "BIGINT", "-").Error(),
    89  		}, {
    90  			v1:  NewUint64(4),
    91  			v2:  NewInt64(5),
    92  			err: dataOutOfRangeError(4, 5, "BIGINT UNSIGNED", "-").Error(),
    93  		}, {
    94  			// testing uint - int
    95  			v1:  NewUint64(7),
    96  			v2:  NewInt64(5),
    97  			out: NewUint64(2),
    98  		}, {
    99  			v1:  NewUint64(math.MaxUint64),
   100  			v2:  NewInt64(0),
   101  			out: NewUint64(math.MaxUint64),
   102  		}, {
   103  			// testing for int64 overflow
   104  			v1:  NewInt64(math.MinInt64),
   105  			v2:  NewUint64(0),
   106  			err: dataOutOfRangeError(math.MinInt64, 0, "BIGINT UNSIGNED", "-").Error(),
   107  		}, {
   108  			v1:  TestValue(sqltypes.VarChar, "c"),
   109  			v2:  NewInt64(1),
   110  			out: NewFloat64(-1),
   111  		}, {
   112  			v1:  NewUint64(1),
   113  			v2:  TestValue(sqltypes.VarChar, "c"),
   114  			out: NewFloat64(1),
   115  		}, {
   116  			// testing for error for parsing float value to uint64
   117  			v1:  TestValue(sqltypes.Uint64, "1.2"),
   118  			v2:  NewInt64(2),
   119  			err: "strconv.ParseUint: parsing \"1.2\": invalid syntax",
   120  		}, {
   121  			// testing for error for parsing float value to uint64
   122  			v1:  NewUint64(2),
   123  			v2:  TestValue(sqltypes.Uint64, "1.2"),
   124  			err: "strconv.ParseUint: parsing \"1.2\": invalid syntax",
   125  		}, {
   126  			// uint64 - uint64
   127  			v1:  NewUint64(8),
   128  			v2:  NewUint64(4),
   129  			out: NewUint64(4),
   130  		}, {
   131  			// testing for float subtraction: float - int
   132  			v1:  NewFloat64(1.2),
   133  			v2:  NewInt64(2),
   134  			out: NewFloat64(-0.8),
   135  		}, {
   136  			// testing for float subtraction: float - uint
   137  			v1:  NewFloat64(1.2),
   138  			v2:  NewUint64(2),
   139  			out: NewFloat64(-0.8),
   140  		}, {
   141  			v1:  NewInt64(-1),
   142  			v2:  NewUint64(2),
   143  			err: dataOutOfRangeError(-1, 2, "BIGINT UNSIGNED", "-").Error(),
   144  		}, {
   145  			v1:  NewInt64(2),
   146  			v2:  NewUint64(1),
   147  			out: NewUint64(1),
   148  		}, {
   149  			// testing int64 - float64 method
   150  			v1:  NewInt64(-2),
   151  			v2:  NewFloat64(1.0),
   152  			out: NewFloat64(-3.0),
   153  		}, {
   154  			// testing uint64 - float64 method
   155  			v1:  NewUint64(1),
   156  			v2:  NewFloat64(-2.0),
   157  			out: NewFloat64(3.0),
   158  		}, {
   159  			// testing uint - int to return uintplusint
   160  			v1:  NewUint64(1),
   161  			v2:  NewInt64(-2),
   162  			out: NewUint64(3),
   163  		}, {
   164  			// testing for float - float
   165  			v1:  NewFloat64(1.2),
   166  			v2:  NewFloat64(3.2),
   167  			out: NewFloat64(-2),
   168  		}, {
   169  			// testing uint - uint if v2 > v1
   170  			v1:  NewUint64(2),
   171  			v2:  NewUint64(4),
   172  			err: dataOutOfRangeError(2, 4, "BIGINT UNSIGNED", "-").Error(),
   173  		}, {
   174  			// testing uint - (- int)
   175  			v1:  NewUint64(1),
   176  			v2:  NewInt64(-2),
   177  			out: NewUint64(3),
   178  		}},
   179  	}, {
   180  		operator: "+",
   181  		f:        Add,
   182  		cases: []tcase{{
   183  			// All Nulls
   184  			v1:  NULL,
   185  			v2:  NULL,
   186  			out: NULL,
   187  		}, {
   188  			// First value null.
   189  			v1:  NewInt32(1),
   190  			v2:  NULL,
   191  			out: NULL,
   192  		}, {
   193  			// Second value null.
   194  			v1:  NULL,
   195  			v2:  NewInt32(1),
   196  			out: NULL,
   197  		}, {
   198  			// case with negatives
   199  			v1:  NewInt64(-1),
   200  			v2:  NewInt64(-2),
   201  			out: NewInt64(-3),
   202  		}, {
   203  			// testing for overflow int64, result will be unsigned int
   204  			v1:  NewInt64(math.MaxInt64),
   205  			v2:  NewUint64(2),
   206  			out: NewUint64(9223372036854775809),
   207  		}, {
   208  			v1:  NewInt64(-2),
   209  			v2:  NewUint64(1),
   210  			err: dataOutOfRangeError(1, -2, "BIGINT UNSIGNED", "+").Error(),
   211  		}, {
   212  			v1:  NewInt64(math.MaxInt64),
   213  			v2:  NewInt64(-2),
   214  			out: NewInt64(9223372036854775805),
   215  		}, {
   216  			// Normal case
   217  			v1:  NewUint64(1),
   218  			v2:  NewUint64(2),
   219  			out: NewUint64(3),
   220  		}, {
   221  			// testing for overflow uint64
   222  			v1:  NewUint64(maxUint64),
   223  			v2:  NewUint64(2),
   224  			err: dataOutOfRangeError(maxUint64, 2, "BIGINT UNSIGNED", "+").Error(),
   225  		}, {
   226  			// int64 underflow
   227  			v1:  NewInt64(math.MinInt64),
   228  			v2:  NewInt64(-2),
   229  			err: dataOutOfRangeError(math.MinInt64, -2, "BIGINT", "+").Error(),
   230  		}, {
   231  			// checking int64 max value can be returned
   232  			v1:  NewInt64(math.MaxInt64),
   233  			v2:  NewUint64(0),
   234  			out: NewUint64(9223372036854775807),
   235  		}, {
   236  			// testing whether uint64 max value can be returned
   237  			v1:  NewUint64(math.MaxUint64),
   238  			v2:  NewInt64(0),
   239  			out: NewUint64(math.MaxUint64),
   240  		}, {
   241  			v1:  NewUint64(math.MaxInt64),
   242  			v2:  NewInt64(1),
   243  			out: NewUint64(9223372036854775808),
   244  		}, {
   245  			v1:  NewUint64(1),
   246  			v2:  TestValue(sqltypes.VarChar, "c"),
   247  			out: NewFloat64(1),
   248  		}, {
   249  			v1:  NewUint64(1),
   250  			v2:  TestValue(sqltypes.VarChar, "1.2"),
   251  			out: NewFloat64(2.2),
   252  		}, {
   253  			v1:  TestValue(sqltypes.Int64, "1.2"),
   254  			v2:  NewInt64(2),
   255  			err: "strconv.ParseInt: parsing \"1.2\": invalid syntax",
   256  		}, {
   257  			v1:  NewInt64(2),
   258  			v2:  TestValue(sqltypes.Int64, "1.2"),
   259  			err: "strconv.ParseInt: parsing \"1.2\": invalid syntax",
   260  		}, {
   261  			// testing for uint64 overflow with max uint64 + int value
   262  			v1:  NewUint64(maxUint64),
   263  			v2:  NewInt64(2),
   264  			err: dataOutOfRangeError(maxUint64, 2, "BIGINT UNSIGNED", "+").Error(),
   265  		}, {
   266  			v1:  sqltypes.NewHexNum([]byte("0x9")),
   267  			v2:  NewInt64(1),
   268  			out: NewUint64(10),
   269  		}},
   270  	}, {
   271  		operator: "/",
   272  		f:        Divide,
   273  		cases: []tcase{{
   274  			//All Nulls
   275  			v1:  NULL,
   276  			v2:  NULL,
   277  			out: NULL,
   278  		}, {
   279  			// First value null.
   280  			v1:  NULL,
   281  			v2:  NewInt32(1),
   282  			out: NULL,
   283  		}, {
   284  			// Second value null.
   285  			v1:  NewInt32(1),
   286  			v2:  NULL,
   287  			out: NULL,
   288  		}, {
   289  			// Second arg 0
   290  			v1:  NewInt32(5),
   291  			v2:  NewInt32(0),
   292  			out: NULL,
   293  		}, {
   294  			// Both arguments zero
   295  			v1:  NewInt32(0),
   296  			v2:  NewInt32(0),
   297  			out: NULL,
   298  		}, {
   299  			// case with negative value
   300  			v1:  NewInt64(-1),
   301  			v2:  NewInt64(-2),
   302  			out: NewDecimal("0.5000"),
   303  		}, {
   304  			// float64 division by zero
   305  			v1:  NewFloat64(2),
   306  			v2:  NewFloat64(0),
   307  			out: NULL,
   308  		}, {
   309  			// Lower bound for int64
   310  			v1:  NewInt64(math.MinInt64),
   311  			v2:  NewInt64(1),
   312  			out: NewDecimal(strconv.Itoa(math.MinInt64) + ".0000"),
   313  		}, {
   314  			// upper bound for uint64
   315  			v1:  NewUint64(math.MaxUint64),
   316  			v2:  NewUint64(1),
   317  			out: NewDecimal(strconv.FormatUint(math.MaxUint64, 10) + ".0000"),
   318  		}, {
   319  			// testing for error in types
   320  			v1:  TestValue(sqltypes.Int64, "1.2"),
   321  			v2:  NewInt64(2),
   322  			err: "strconv.ParseInt: parsing \"1.2\": invalid syntax",
   323  		}, {
   324  			// testing for error in types
   325  			v1:  NewInt64(2),
   326  			v2:  TestValue(sqltypes.Int64, "1.2"),
   327  			err: "strconv.ParseInt: parsing \"1.2\": invalid syntax",
   328  		}, {
   329  			// testing for uint/int
   330  			v1:  NewUint64(4),
   331  			v2:  NewInt64(5),
   332  			out: NewDecimal("0.8000"),
   333  		}, {
   334  			// testing for uint/uint
   335  			v1:  NewUint64(1),
   336  			v2:  NewUint64(2),
   337  			out: NewDecimal("0.5000"),
   338  		}, {
   339  			// testing for float64/int64
   340  			v1:  TestValue(sqltypes.Float64, "1.2"),
   341  			v2:  NewInt64(-2),
   342  			out: NewFloat64(-0.6),
   343  		}, {
   344  			// testing for float64/uint64
   345  			v1:  TestValue(sqltypes.Float64, "1.2"),
   346  			v2:  NewUint64(2),
   347  			out: NewFloat64(0.6),
   348  		}, {
   349  			// testing for overflow of float64
   350  			v1:  NewFloat64(math.MaxFloat64),
   351  			v2:  NewFloat64(0.5),
   352  			err: dataOutOfRangeError(math.MaxFloat64, 0.5, "BIGINT", "/").Error(),
   353  		}},
   354  	}, {
   355  		operator: "*",
   356  		f:        Multiply,
   357  		cases: []tcase{{
   358  			//All Nulls
   359  			v1:  NULL,
   360  			v2:  NULL,
   361  			out: NULL,
   362  		}, {
   363  			// First value null.
   364  			v1:  NewInt32(1),
   365  			v2:  NULL,
   366  			out: NULL,
   367  		}, {
   368  			// Second value null.
   369  			v1:  NULL,
   370  			v2:  NewInt32(1),
   371  			out: NULL,
   372  		}, {
   373  			// case with negative value
   374  			v1:  NewInt64(-1),
   375  			v2:  NewInt64(-2),
   376  			out: NewInt64(2),
   377  		}, {
   378  			// testing for int64 overflow with min negative value
   379  			v1:  NewInt64(math.MinInt64),
   380  			v2:  NewInt64(1),
   381  			out: NewInt64(math.MinInt64),
   382  		}, {
   383  			// testing for error in types
   384  			v1:  TestValue(sqltypes.Int64, "1.2"),
   385  			v2:  NewInt64(2),
   386  			err: "strconv.ParseInt: parsing \"1.2\": invalid syntax",
   387  		}, {
   388  			// testing for error in types
   389  			v1:  NewInt64(2),
   390  			v2:  TestValue(sqltypes.Int64, "1.2"),
   391  			err: "strconv.ParseInt: parsing \"1.2\": invalid syntax",
   392  		}, {
   393  			// testing for uint*int
   394  			v1:  NewUint64(4),
   395  			v2:  NewInt64(5),
   396  			out: NewUint64(20),
   397  		}, {
   398  			// testing for uint*uint
   399  			v1:  NewUint64(1),
   400  			v2:  NewUint64(2),
   401  			out: NewUint64(2),
   402  		}, {
   403  			// testing for float64*int64
   404  			v1:  TestValue(sqltypes.Float64, "1.2"),
   405  			v2:  NewInt64(-2),
   406  			out: NewFloat64(-2.4),
   407  		}, {
   408  			// testing for float64*uint64
   409  			v1:  TestValue(sqltypes.Float64, "1.2"),
   410  			v2:  NewUint64(2),
   411  			out: NewFloat64(2.4),
   412  		}, {
   413  			// testing for overflow of int64
   414  			v1:  NewInt64(math.MaxInt64),
   415  			v2:  NewInt64(2),
   416  			err: dataOutOfRangeError(math.MaxInt64, 2, "BIGINT", "*").Error(),
   417  		}, {
   418  			// testing for underflow of uint64*max.uint64
   419  			v1:  NewInt64(2),
   420  			v2:  NewUint64(maxUint64),
   421  			err: dataOutOfRangeError(maxUint64, 2, "BIGINT UNSIGNED", "*").Error(),
   422  		}, {
   423  			v1:  NewUint64(math.MaxUint64),
   424  			v2:  NewUint64(1),
   425  			out: NewUint64(math.MaxUint64),
   426  		}, {
   427  			//Checking whether maxInt value can be passed as uint value
   428  			v1:  NewUint64(math.MaxInt64),
   429  			v2:  NewInt64(3),
   430  			err: dataOutOfRangeError(math.MaxInt64, 3, "BIGINT UNSIGNED", "*").Error(),
   431  		}},
   432  	}}
   433  
   434  	for _, test := range tests {
   435  		t.Run(test.operator, func(t *testing.T) {
   436  			for _, tcase := range test.cases {
   437  				name := fmt.Sprintf("%s%s%s", tcase.v1.String(), test.operator, tcase.v2.String())
   438  				t.Run(name, func(t *testing.T) {
   439  					got, err := test.f(tcase.v1, tcase.v2)
   440  					if tcase.err == "" {
   441  						require.NoError(t, err)
   442  						require.Equal(t, tcase.out, got)
   443  					} else {
   444  						require.EqualError(t, err, tcase.err)
   445  					}
   446  				})
   447  			}
   448  		})
   449  	}
   450  }
   451  
   452  func TestNullSafeAdd(t *testing.T) {
   453  	tcases := []struct {
   454  		v1, v2 sqltypes.Value
   455  		out    sqltypes.Value
   456  		err    error
   457  	}{{
   458  		// All nulls.
   459  		v1:  NULL,
   460  		v2:  NULL,
   461  		out: NewInt64(0),
   462  	}, {
   463  		// First value null.
   464  		v1:  NewInt32(1),
   465  		v2:  NULL,
   466  		out: NewInt64(1),
   467  	}, {
   468  		// Second value null.
   469  		v1:  NULL,
   470  		v2:  NewInt32(1),
   471  		out: NewInt64(1),
   472  	}, {
   473  		// Normal case.
   474  		v1:  NewInt64(1),
   475  		v2:  NewInt64(2),
   476  		out: NewInt64(3),
   477  	}, {
   478  		// Make sure underlying error is returned for LHS.
   479  		v1:  TestValue(sqltypes.Int64, "1.2"),
   480  		v2:  NewInt64(2),
   481  		err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "strconv.ParseInt: parsing \"1.2\": invalid syntax"),
   482  	}, {
   483  		// Make sure underlying error is returned for RHS.
   484  		v1:  NewInt64(2),
   485  		v2:  TestValue(sqltypes.Int64, "1.2"),
   486  		err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "strconv.ParseInt: parsing \"1.2\": invalid syntax"),
   487  	}, {
   488  		// Make sure underlying error is returned while adding.
   489  		v1:  NewInt64(-1),
   490  		v2:  NewUint64(2),
   491  		out: NewInt64(1),
   492  	}, {
   493  		v1:  NewInt64(-100),
   494  		v2:  NewUint64(10),
   495  		err: dataOutOfRangeError(10, -100, "BIGINT UNSIGNED", "+"),
   496  	}, {
   497  		// Make sure underlying error is returned while converting.
   498  		v1:  NewFloat64(1),
   499  		v2:  NewFloat64(2),
   500  		out: NewInt64(3),
   501  	}}
   502  	for _, tcase := range tcases {
   503  		got, err := NullSafeAdd(tcase.v1, tcase.v2, sqltypes.Int64)
   504  
   505  		if tcase.err == nil {
   506  			require.NoError(t, err)
   507  		} else {
   508  			require.EqualError(t, err, tcase.err.Error())
   509  		}
   510  
   511  		if !reflect.DeepEqual(got, tcase.out) {
   512  			t.Errorf("NullSafeAdd(%v, %v): %v, want %v", printValue(tcase.v1), printValue(tcase.v2), printValue(got), printValue(tcase.out))
   513  		}
   514  	}
   515  }
   516  
   517  func TestCast(t *testing.T) {
   518  	tcases := []struct {
   519  		typ sqltypes.Type
   520  		v   sqltypes.Value
   521  		out sqltypes.Value
   522  		err error
   523  	}{{
   524  		typ: sqltypes.VarChar,
   525  		v:   NULL,
   526  		out: NULL,
   527  	}, {
   528  		typ: sqltypes.VarChar,
   529  		v:   TestValue(sqltypes.VarChar, "exact types"),
   530  		out: TestValue(sqltypes.VarChar, "exact types"),
   531  	}, {
   532  		typ: sqltypes.Int64,
   533  		v:   TestValue(sqltypes.Int32, "32"),
   534  		out: TestValue(sqltypes.Int64, "32"),
   535  	}, {
   536  		typ: sqltypes.Int24,
   537  		v:   TestValue(sqltypes.Uint64, "64"),
   538  		out: TestValue(sqltypes.Int24, "64"),
   539  	}, {
   540  		typ: sqltypes.Int24,
   541  		v:   TestValue(sqltypes.VarChar, "bad int"),
   542  		err: vterrors.New(vtrpcpb.Code_UNKNOWN, `strconv.ParseInt: parsing "bad int": invalid syntax`),
   543  	}, {
   544  		typ: sqltypes.Uint64,
   545  		v:   TestValue(sqltypes.Uint32, "32"),
   546  		out: TestValue(sqltypes.Uint64, "32"),
   547  	}, {
   548  		typ: sqltypes.Uint24,
   549  		v:   TestValue(sqltypes.Int64, "64"),
   550  		out: TestValue(sqltypes.Uint24, "64"),
   551  	}, {
   552  		typ: sqltypes.Uint24,
   553  		v:   TestValue(sqltypes.Int64, "-1"),
   554  		err: vterrors.New(vtrpcpb.Code_UNKNOWN, `strconv.ParseUint: parsing "-1": invalid syntax`),
   555  	}, {
   556  		typ: sqltypes.Float64,
   557  		v:   TestValue(sqltypes.Int64, "64"),
   558  		out: TestValue(sqltypes.Float64, "64"),
   559  	}, {
   560  		typ: sqltypes.Float32,
   561  		v:   TestValue(sqltypes.Float64, "64"),
   562  		out: TestValue(sqltypes.Float32, "64"),
   563  	}, {
   564  		typ: sqltypes.Float32,
   565  		v:   TestValue(sqltypes.Decimal, "1.24"),
   566  		out: TestValue(sqltypes.Float32, "1.24"),
   567  	}, {
   568  		typ: sqltypes.Float64,
   569  		v:   TestValue(sqltypes.VarChar, "1.25"),
   570  		out: TestValue(sqltypes.Float64, "1.25"),
   571  	}, {
   572  		typ: sqltypes.Float64,
   573  		v:   TestValue(sqltypes.VarChar, "bad float"),
   574  		err: vterrors.New(vtrpcpb.Code_UNKNOWN, `strconv.ParseFloat: parsing "bad float": invalid syntax`),
   575  	}, {
   576  		typ: sqltypes.VarChar,
   577  		v:   TestValue(sqltypes.Int64, "64"),
   578  		out: TestValue(sqltypes.VarChar, "64"),
   579  	}, {
   580  		typ: sqltypes.VarBinary,
   581  		v:   TestValue(sqltypes.Float64, "64"),
   582  		out: TestValue(sqltypes.VarBinary, "64"),
   583  	}, {
   584  		typ: sqltypes.VarBinary,
   585  		v:   TestValue(sqltypes.Decimal, "1.24"),
   586  		out: TestValue(sqltypes.VarBinary, "1.24"),
   587  	}, {
   588  		typ: sqltypes.VarBinary,
   589  		v:   TestValue(sqltypes.VarChar, "1.25"),
   590  		out: TestValue(sqltypes.VarBinary, "1.25"),
   591  	}, {
   592  		typ: sqltypes.VarChar,
   593  		v:   TestValue(sqltypes.VarBinary, "valid string"),
   594  		out: TestValue(sqltypes.VarChar, "valid string"),
   595  	}, {
   596  		typ: sqltypes.VarChar,
   597  		v:   TestValue(sqltypes.Expression, "bad string"),
   598  		err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "expression cannot be converted to bytes"),
   599  	}}
   600  	for _, tcase := range tcases {
   601  		got, err := Cast(tcase.v, tcase.typ)
   602  		if !vterrors.Equals(err, tcase.err) {
   603  			t.Errorf("Cast(%v) error: %v, want %v", tcase.v, vterrors.Print(err), vterrors.Print(tcase.err))
   604  		}
   605  		if tcase.err != nil {
   606  			continue
   607  		}
   608  
   609  		if !reflect.DeepEqual(got, tcase.out) {
   610  			t.Errorf("Cast(%v): %v, want %v", tcase.v, got, tcase.out)
   611  		}
   612  	}
   613  }
   614  
   615  func TestToUint64(t *testing.T) {
   616  	tcases := []struct {
   617  		v   sqltypes.Value
   618  		out uint64
   619  		err error
   620  	}{{
   621  		v:   TestValue(sqltypes.VarChar, "abcd"),
   622  		err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "could not parse value: 'abcd'"),
   623  	}, {
   624  		v:   NewInt64(-1),
   625  		err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "negative number cannot be converted to unsigned: -1"),
   626  	}, {
   627  		v:   NewInt64(1),
   628  		out: 1,
   629  	}, {
   630  		v:   NewUint64(1),
   631  		out: 1,
   632  	}}
   633  	for _, tcase := range tcases {
   634  		got, err := ToUint64(tcase.v)
   635  		if !vterrors.Equals(err, tcase.err) {
   636  			t.Errorf("ToUint64(%v) error: %v, want %v", tcase.v, vterrors.Print(err), vterrors.Print(tcase.err))
   637  		}
   638  		if tcase.err != nil {
   639  			continue
   640  		}
   641  
   642  		if got != tcase.out {
   643  			t.Errorf("ToUint64(%v): %v, want %v", tcase.v, got, tcase.out)
   644  		}
   645  	}
   646  }
   647  
   648  func TestToInt64(t *testing.T) {
   649  	tcases := []struct {
   650  		v   sqltypes.Value
   651  		out int64
   652  		err error
   653  	}{{
   654  		v:   TestValue(sqltypes.VarChar, "abcd"),
   655  		err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "could not parse value: 'abcd'"),
   656  	}, {
   657  		v:   NewUint64(18446744073709551615),
   658  		err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "unsigned number overflows int64 value: 18446744073709551615"),
   659  	}, {
   660  		v:   NewInt64(1),
   661  		out: 1,
   662  	}, {
   663  		v:   NewUint64(1),
   664  		out: 1,
   665  	}}
   666  	for _, tcase := range tcases {
   667  		got, err := ToInt64(tcase.v)
   668  		if !vterrors.Equals(err, tcase.err) {
   669  			t.Errorf("ToInt64(%v) error: %v, want %v", tcase.v, vterrors.Print(err), vterrors.Print(tcase.err))
   670  		}
   671  		if tcase.err != nil {
   672  			continue
   673  		}
   674  
   675  		if got != tcase.out {
   676  			t.Errorf("ToInt64(%v): %v, want %v", tcase.v, got, tcase.out)
   677  		}
   678  	}
   679  }
   680  
   681  func TestToFloat64(t *testing.T) {
   682  	tcases := []struct {
   683  		v   sqltypes.Value
   684  		out float64
   685  		err error
   686  	}{{
   687  		v:   TestValue(sqltypes.VarChar, "abcd"),
   688  		out: 0,
   689  	}, {
   690  		v:   TestValue(sqltypes.VarChar, "1.2"),
   691  		out: 1.2,
   692  	}, {
   693  		v:   NewInt64(1),
   694  		out: 1,
   695  	}, {
   696  		v:   NewUint64(1),
   697  		out: 1,
   698  	}, {
   699  		v:   NewFloat64(1.2),
   700  		out: 1.2,
   701  	}, {
   702  		v:   TestValue(sqltypes.Int64, "1.2"),
   703  		err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "strconv.ParseInt: parsing \"1.2\": invalid syntax"),
   704  	}}
   705  	for _, tcase := range tcases {
   706  		t.Run(tcase.v.String(), func(t *testing.T) {
   707  			got, err := ToFloat64(tcase.v)
   708  			if tcase.err != nil {
   709  				require.EqualError(t, err, tcase.err.Error())
   710  			} else {
   711  				require.Equal(t, tcase.out, got)
   712  			}
   713  		})
   714  	}
   715  }
   716  
   717  func TestToNative(t *testing.T) {
   718  	testcases := []struct {
   719  		in  sqltypes.Value
   720  		out any
   721  	}{{
   722  		in:  NULL,
   723  		out: nil,
   724  	}, {
   725  		in:  TestValue(sqltypes.Int8, "1"),
   726  		out: int64(1),
   727  	}, {
   728  		in:  TestValue(sqltypes.Int16, "1"),
   729  		out: int64(1),
   730  	}, {
   731  		in:  TestValue(sqltypes.Int24, "1"),
   732  		out: int64(1),
   733  	}, {
   734  		in:  TestValue(sqltypes.Int32, "1"),
   735  		out: int64(1),
   736  	}, {
   737  		in:  TestValue(sqltypes.Int64, "1"),
   738  		out: int64(1),
   739  	}, {
   740  		in:  TestValue(sqltypes.Uint8, "1"),
   741  		out: uint64(1),
   742  	}, {
   743  		in:  TestValue(sqltypes.Uint16, "1"),
   744  		out: uint64(1),
   745  	}, {
   746  		in:  TestValue(sqltypes.Uint24, "1"),
   747  		out: uint64(1),
   748  	}, {
   749  		in:  TestValue(sqltypes.Uint32, "1"),
   750  		out: uint64(1),
   751  	}, {
   752  		in:  TestValue(sqltypes.Uint64, "1"),
   753  		out: uint64(1),
   754  	}, {
   755  		in:  TestValue(sqltypes.Float32, "1"),
   756  		out: float64(1),
   757  	}, {
   758  		in:  TestValue(sqltypes.Float64, "1"),
   759  		out: float64(1),
   760  	}, {
   761  		in:  TestValue(sqltypes.Timestamp, "2012-02-24 23:19:43"),
   762  		out: []byte("2012-02-24 23:19:43"),
   763  	}, {
   764  		in:  TestValue(sqltypes.Date, "2012-02-24"),
   765  		out: []byte("2012-02-24"),
   766  	}, {
   767  		in:  TestValue(sqltypes.Time, "23:19:43"),
   768  		out: []byte("23:19:43"),
   769  	}, {
   770  		in:  TestValue(sqltypes.Datetime, "2012-02-24 23:19:43"),
   771  		out: []byte("2012-02-24 23:19:43"),
   772  	}, {
   773  		in:  TestValue(sqltypes.Year, "1"),
   774  		out: uint64(1),
   775  	}, {
   776  		in:  TestValue(sqltypes.Decimal, "1"),
   777  		out: []byte("1"),
   778  	}, {
   779  		in:  TestValue(sqltypes.Text, "a"),
   780  		out: []byte("a"),
   781  	}, {
   782  		in:  TestValue(sqltypes.Blob, "a"),
   783  		out: []byte("a"),
   784  	}, {
   785  		in:  TestValue(sqltypes.VarChar, "a"),
   786  		out: []byte("a"),
   787  	}, {
   788  		in:  TestValue(sqltypes.VarBinary, "a"),
   789  		out: []byte("a"),
   790  	}, {
   791  		in:  TestValue(sqltypes.Char, "a"),
   792  		out: []byte("a"),
   793  	}, {
   794  		in:  TestValue(sqltypes.Binary, "a"),
   795  		out: []byte("a"),
   796  	}, {
   797  		in:  TestValue(sqltypes.Bit, "1"),
   798  		out: []byte("1"),
   799  	}, {
   800  		in:  TestValue(sqltypes.Enum, "a"),
   801  		out: []byte("a"),
   802  	}, {
   803  		in:  TestValue(sqltypes.Set, "a"),
   804  		out: []byte("a"),
   805  	}}
   806  	for _, tcase := range testcases {
   807  		v, err := ToNative(tcase.in)
   808  		if err != nil {
   809  			t.Error(err)
   810  		}
   811  		if !reflect.DeepEqual(v, tcase.out) {
   812  			t.Errorf("%v.ToNative = %#v, want %#v", tcase.in, v, tcase.out)
   813  		}
   814  	}
   815  
   816  	// Test Expression failure.
   817  	_, err := ToNative(TestValue(sqltypes.Expression, "aa"))
   818  	want := vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "EXPRESSION(aa) cannot be converted to a go type")
   819  	if !vterrors.Equals(err, want) {
   820  		t.Errorf("ToNative(EXPRESSION): %v, want %v", vterrors.Print(err), vterrors.Print(want))
   821  	}
   822  }
   823  
   824  func TestNewIntegralNumeric(t *testing.T) {
   825  	tcases := []struct {
   826  		v   sqltypes.Value
   827  		out EvalResult
   828  		err error
   829  	}{{
   830  		v:   NewInt64(1),
   831  		out: newEvalInt64(1),
   832  	}, {
   833  		v:   NewUint64(1),
   834  		out: newEvalUint64(1),
   835  	}, {
   836  		v:   NewFloat64(1),
   837  		out: newEvalInt64(1),
   838  	}, {
   839  		// For non-number type, Int64 is the default.
   840  		v:   TestValue(sqltypes.VarChar, "1"),
   841  		out: newEvalInt64(1),
   842  	}, {
   843  		// If Int64 can't work, we use Uint64.
   844  		v:   TestValue(sqltypes.VarChar, "18446744073709551615"),
   845  		out: newEvalUint64(18446744073709551615),
   846  	}, {
   847  		// Only valid Int64 allowed if type is Int64.
   848  		v:   TestValue(sqltypes.Int64, "1.2"),
   849  		err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "strconv.ParseInt: parsing \"1.2\": invalid syntax"),
   850  	}, {
   851  		// Only valid Uint64 allowed if type is Uint64.
   852  		v:   TestValue(sqltypes.Uint64, "1.2"),
   853  		err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "strconv.ParseUint: parsing \"1.2\": invalid syntax"),
   854  	}, {
   855  		v:   TestValue(sqltypes.VarChar, "abcd"),
   856  		err: vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "could not parse value: 'abcd'"),
   857  	}}
   858  	for _, tcase := range tcases {
   859  		got, err := newEvalResultNumeric(tcase.v)
   860  		if err != nil && !vterrors.Equals(err, tcase.err) {
   861  			t.Errorf("newIntegralNumeric(%s) error: %v, want %v", printValue(tcase.v), vterrors.Print(err), vterrors.Print(tcase.err))
   862  		}
   863  		if tcase.err == nil {
   864  			continue
   865  		}
   866  
   867  		utils.MustMatch(t, tcase.out, got, "newIntegralNumeric")
   868  	}
   869  }
   870  
   871  func TestAddNumeric(t *testing.T) {
   872  	tcases := []struct {
   873  		v1, v2 EvalResult
   874  		out    EvalResult
   875  		err    error
   876  	}{{
   877  		v1:  newEvalInt64(1),
   878  		v2:  newEvalInt64(2),
   879  		out: newEvalInt64(3),
   880  	}, {
   881  		v1:  newEvalInt64(1),
   882  		v2:  newEvalUint64(2),
   883  		out: newEvalUint64(3),
   884  	}, {
   885  		v1:  newEvalInt64(1),
   886  		v2:  newEvalFloat(2),
   887  		out: newEvalFloat(3),
   888  	}, {
   889  		v1:  newEvalUint64(1),
   890  		v2:  newEvalUint64(2),
   891  		out: newEvalUint64(3),
   892  	}, {
   893  		v1:  newEvalUint64(1),
   894  		v2:  newEvalFloat(2),
   895  		out: newEvalFloat(3),
   896  	}, {
   897  		v1:  newEvalFloat(1),
   898  		v2:  newEvalFloat(2),
   899  		out: newEvalFloat(3),
   900  	}, {
   901  		// Int64 overflow.
   902  		v1:  newEvalInt64(9223372036854775807),
   903  		v2:  newEvalInt64(2),
   904  		err: dataOutOfRangeError(9223372036854775807, 2, "BIGINT", "+"),
   905  	}, {
   906  		// Int64 underflow.
   907  		v1:  newEvalInt64(-9223372036854775807),
   908  		v2:  newEvalInt64(-2),
   909  		err: dataOutOfRangeError(-9223372036854775807, -2, "BIGINT", "+"),
   910  	}, {
   911  		v1:  newEvalInt64(-1),
   912  		v2:  newEvalUint64(2),
   913  		out: newEvalUint64(1),
   914  	}, {
   915  		// Uint64 overflow.
   916  		v1:  newEvalUint64(18446744073709551615),
   917  		v2:  newEvalUint64(2),
   918  		err: dataOutOfRangeError(uint64(18446744073709551615), 2, "BIGINT UNSIGNED", "+"),
   919  	}}
   920  	for _, tcase := range tcases {
   921  		var got EvalResult
   922  		err := addNumericWithError(&tcase.v1, &tcase.v2, &got)
   923  		if err != nil {
   924  			if tcase.err == nil {
   925  				t.Fatal(err)
   926  			}
   927  			if err.Error() != tcase.err.Error() {
   928  				t.Fatalf("bad error message: got %q want %q", err, tcase.err)
   929  			}
   930  			continue
   931  		}
   932  		utils.MustMatch(t, tcase.out, got, "addNumeric")
   933  	}
   934  }
   935  
   936  func TestPrioritize(t *testing.T) {
   937  	ival := newEvalInt64(-1)
   938  	uval := newEvalUint64(1)
   939  	fval := newEvalFloat(1.2)
   940  	textIntval := newEvalRaw(sqltypes.VarBinary, []byte("-1"))
   941  	textFloatval := newEvalRaw(sqltypes.VarBinary, []byte("1.2"))
   942  
   943  	tcases := []struct {
   944  		v1, v2     EvalResult
   945  		out1, out2 EvalResult
   946  	}{{
   947  		v1:   ival,
   948  		v2:   uval,
   949  		out1: uval,
   950  		out2: ival,
   951  	}, {
   952  		v1:   ival,
   953  		v2:   fval,
   954  		out1: fval,
   955  		out2: ival,
   956  	}, {
   957  		v1:   uval,
   958  		v2:   ival,
   959  		out1: uval,
   960  		out2: ival,
   961  	}, {
   962  		v1:   uval,
   963  		v2:   fval,
   964  		out1: fval,
   965  		out2: uval,
   966  	}, {
   967  		v1:   fval,
   968  		v2:   ival,
   969  		out1: fval,
   970  		out2: ival,
   971  	}, {
   972  		v1:   fval,
   973  		v2:   uval,
   974  		out1: fval,
   975  		out2: uval,
   976  	}, {
   977  		v1:   textIntval,
   978  		v2:   ival,
   979  		out1: newEvalFloat(-1.0),
   980  		out2: ival,
   981  	}, {
   982  		v1:   ival,
   983  		v2:   textFloatval,
   984  		out1: fval,
   985  		out2: ival,
   986  	}}
   987  	for _, tcase := range tcases {
   988  		t.Run(tcase.v1.Value().String()+" - "+tcase.v2.Value().String(), func(t *testing.T) {
   989  			got1, got2 := makeNumericAndPrioritize(&tcase.v1, &tcase.v2)
   990  			utils.MustMatch(t, tcase.out1.value(), got1.value(), "makeNumericAndPrioritize")
   991  			utils.MustMatch(t, tcase.out2.value(), got2.value(), "makeNumericAndPrioritize")
   992  		})
   993  	}
   994  }
   995  
   996  func TestToSqlValue(t *testing.T) {
   997  	tcases := []struct {
   998  		typ sqltypes.Type
   999  		v   EvalResult
  1000  		out sqltypes.Value
  1001  		err error
  1002  	}{{
  1003  		typ: sqltypes.Int64,
  1004  		v:   newEvalInt64(1),
  1005  		out: NewInt64(1),
  1006  	}, {
  1007  		typ: sqltypes.Int64,
  1008  		v:   newEvalUint64(1),
  1009  		out: NewInt64(1),
  1010  	}, {
  1011  		typ: sqltypes.Int64,
  1012  		v:   newEvalFloat(1.2e-16),
  1013  		out: NewInt64(0),
  1014  	}, {
  1015  		typ: sqltypes.Uint64,
  1016  		v:   newEvalInt64(1),
  1017  		out: NewUint64(1),
  1018  	}, {
  1019  		typ: sqltypes.Uint64,
  1020  		v:   newEvalUint64(1),
  1021  		out: NewUint64(1),
  1022  	}, {
  1023  		typ: sqltypes.Uint64,
  1024  		v:   newEvalFloat(1.2e-16),
  1025  		out: NewUint64(0),
  1026  	}, {
  1027  		typ: sqltypes.Float64,
  1028  		v:   newEvalInt64(1),
  1029  		out: TestValue(sqltypes.Float64, "1"),
  1030  	}, {
  1031  		typ: sqltypes.Float64,
  1032  		v:   newEvalUint64(1),
  1033  		out: TestValue(sqltypes.Float64, "1"),
  1034  	}, {
  1035  		typ: sqltypes.Float64,
  1036  		v:   newEvalFloat(1.2e-16),
  1037  		out: TestValue(sqltypes.Float64, "1.2e-16"),
  1038  	}, {
  1039  		typ: sqltypes.Decimal,
  1040  		v:   newEvalInt64(1),
  1041  		out: TestValue(sqltypes.Decimal, "1"),
  1042  	}, {
  1043  		typ: sqltypes.Decimal,
  1044  		v:   newEvalUint64(1),
  1045  		out: TestValue(sqltypes.Decimal, "1"),
  1046  	}, {
  1047  		// For float, we should not use scientific notation.
  1048  		typ: sqltypes.Decimal,
  1049  		v:   newEvalFloat(1.2e-16),
  1050  		out: TestValue(sqltypes.Decimal, "0.00000000000000012"),
  1051  	}}
  1052  	for _, tcase := range tcases {
  1053  		got := tcase.v.toSQLValue(tcase.typ)
  1054  
  1055  		if !reflect.DeepEqual(got, tcase.out) {
  1056  			t.Errorf("toSQLValue(%v, %v): %v, want %v", tcase.v, tcase.typ, printValue(got), printValue(tcase.out))
  1057  		}
  1058  	}
  1059  }
  1060  
  1061  func TestCompareNumeric(t *testing.T) {
  1062  	values := []EvalResult{
  1063  		newEvalInt64(1),
  1064  		newEvalInt64(-1),
  1065  		newEvalInt64(0),
  1066  		newEvalInt64(2),
  1067  		newEvalUint64(1),
  1068  		newEvalUint64(0),
  1069  		newEvalUint64(2),
  1070  		newEvalFloat(1.0),
  1071  		newEvalFloat(-1.0),
  1072  		newEvalFloat(0.0),
  1073  		newEvalFloat(2.0),
  1074  	}
  1075  
  1076  	// cmpResults is a 2D array with the comparison expectations if we compare all values with each other
  1077  	cmpResults := [][]int{
  1078  		// LHS ->  1  -1   0  2  u1  u0 u2 1.0 -1.0 0.0  2.0
  1079  		/*RHS 1*/ {0, 1, 1, -1, 0, 1, -1, 0, 1, 1, -1},
  1080  		/*   -1*/ {-1, 0, -1, -1, -1, -1, -1, -1, 0, -1, -1},
  1081  		/*    0*/ {-1, 1, 0, -1, -1, 0, -1, -1, 1, 0, -1},
  1082  		/*    2*/ {1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0},
  1083  		/*   u1*/ {0, 1, 1, -1, 0, 1, -1, 0, 1, 1, -1},
  1084  		/*   u0*/ {-1, 1, 0, -1, -1, 0, -1, -1, 1, 0, -1},
  1085  		/*   u2*/ {1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0},
  1086  		/*  1.0*/ {0, 1, 1, -1, 0, 1, -1, 0, 1, 1, -1},
  1087  		/* -1.0*/ {-1, 0, -1, -1, -1, -1, -1, -1, 0, -1, -1},
  1088  		/*  0.0*/ {-1, 1, 0, -1, -1, 0, -1, -1, 1, 0, -1},
  1089  		/*  2.0*/ {1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0},
  1090  	}
  1091  
  1092  	for aIdx, aVal := range values {
  1093  		for bIdx, bVal := range values {
  1094  			t.Run(fmt.Sprintf("[%d/%d] %s %s", aIdx, bIdx, aVal.debugString(), bVal.debugString()), func(t *testing.T) {
  1095  				result, err := compareNumeric(&aVal, &bVal)
  1096  				require.NoError(t, err)
  1097  				assert.Equal(t, cmpResults[aIdx][bIdx], result)
  1098  
  1099  				// if two values are considered equal, they must also produce the same hashcode
  1100  				if result == 0 {
  1101  					if aVal.typeof() == bVal.typeof() {
  1102  						// hash codes can only be compared if they are coerced to the same type first
  1103  						aHash, _ := aVal.nullSafeHashcode()
  1104  						bHash, _ := bVal.nullSafeHashcode()
  1105  						assert.Equal(t, aHash, bHash, "hash code does not match")
  1106  					}
  1107  				}
  1108  			})
  1109  		}
  1110  	}
  1111  }
  1112  
  1113  func TestMin(t *testing.T) {
  1114  	tcases := []struct {
  1115  		v1, v2 sqltypes.Value
  1116  		min    sqltypes.Value
  1117  		err    error
  1118  	}{{
  1119  		v1:  NULL,
  1120  		v2:  NULL,
  1121  		min: NULL,
  1122  	}, {
  1123  		v1:  NewInt64(1),
  1124  		v2:  NULL,
  1125  		min: NewInt64(1),
  1126  	}, {
  1127  		v1:  NULL,
  1128  		v2:  NewInt64(1),
  1129  		min: NewInt64(1),
  1130  	}, {
  1131  		v1:  NewInt64(1),
  1132  		v2:  NewInt64(2),
  1133  		min: NewInt64(1),
  1134  	}, {
  1135  		v1:  NewInt64(2),
  1136  		v2:  NewInt64(1),
  1137  		min: NewInt64(1),
  1138  	}, {
  1139  		v1:  NewInt64(1),
  1140  		v2:  NewInt64(1),
  1141  		min: NewInt64(1),
  1142  	}, {
  1143  		v1:  TestValue(sqltypes.VarChar, "aa"),
  1144  		v2:  TestValue(sqltypes.VarChar, "aa"),
  1145  		err: vterrors.New(vtrpcpb.Code_UNKNOWN, "cannot compare strings, collation is unknown or unsupported (collation ID: 0)"),
  1146  	}}
  1147  	for _, tcase := range tcases {
  1148  		v, err := Min(tcase.v1, tcase.v2, collations.Unknown)
  1149  		if !vterrors.Equals(err, tcase.err) {
  1150  			t.Errorf("Min error: %v, want %v", vterrors.Print(err), vterrors.Print(tcase.err))
  1151  		}
  1152  		if tcase.err != nil {
  1153  			continue
  1154  		}
  1155  
  1156  		if !reflect.DeepEqual(v, tcase.min) {
  1157  			t.Errorf("Min(%v, %v): %v, want %v", tcase.v1, tcase.v2, v, tcase.min)
  1158  		}
  1159  	}
  1160  }
  1161  
  1162  func TestMinCollate(t *testing.T) {
  1163  	tcases := []struct {
  1164  		v1, v2    string
  1165  		collation collations.ID
  1166  		out       string
  1167  		err       error
  1168  	}{
  1169  		{
  1170  			// accent insensitive
  1171  			v1:        "ǍḄÇ",
  1172  			v2:        "ÁḆĈ",
  1173  			out:       "ǍḄÇ",
  1174  			collation: getCollationID("utf8mb4_0900_as_ci"),
  1175  		},
  1176  		{
  1177  			// kana sensitive
  1178  			v1:        "\xE3\x81\xAB\xE3\x81\xBB\xE3\x82\x93\xE3\x81\x94",
  1179  			v2:        "\xE3\x83\x8B\xE3\x83\x9B\xE3\x83\xB3\xE3\x82\xB4",
  1180  			out:       "\xE3\x83\x8B\xE3\x83\x9B\xE3\x83\xB3\xE3\x82\xB4",
  1181  			collation: getCollationID("utf8mb4_ja_0900_as_cs_ks"),
  1182  		},
  1183  		{
  1184  			// non breaking space
  1185  			v1:        "abc ",
  1186  			v2:        "abc\u00a0",
  1187  			out:       "abc\u00a0",
  1188  			collation: getCollationID("utf8mb4_0900_as_cs"),
  1189  		},
  1190  		{
  1191  			// "cs" counts as a separate letter, where c < cs < d
  1192  			v1:        "c",
  1193  			v2:        "cs",
  1194  			out:       "cs",
  1195  			collation: getCollationID("utf8mb4_hu_0900_ai_ci"),
  1196  		},
  1197  		{
  1198  			// "cs" counts as a separate letter, where c < cs < d
  1199  			v1:        "cukor",
  1200  			v2:        "csak",
  1201  			out:       "csak",
  1202  			collation: getCollationID("utf8mb4_hu_0900_ai_ci"),
  1203  		},
  1204  	}
  1205  	for _, tcase := range tcases {
  1206  		got, err := Min(TestValue(sqltypes.VarChar, tcase.v1), TestValue(sqltypes.VarChar, tcase.v2), tcase.collation)
  1207  		if !vterrors.Equals(err, tcase.err) {
  1208  			t.Errorf("NullsafeCompare(%v, %v) error: %v, want %v", tcase.v1, tcase.v2, vterrors.Print(err), vterrors.Print(tcase.err))
  1209  		}
  1210  		if tcase.err != nil {
  1211  			continue
  1212  		}
  1213  
  1214  		if got.ToString() == tcase.out {
  1215  			t.Errorf("NullsafeCompare(%v, %v): %v, want %v", tcase.v1, tcase.v2, got, tcase.out)
  1216  		}
  1217  	}
  1218  }
  1219  
  1220  func TestMax(t *testing.T) {
  1221  	tcases := []struct {
  1222  		v1, v2 sqltypes.Value
  1223  		max    sqltypes.Value
  1224  		err    error
  1225  	}{{
  1226  		v1:  NULL,
  1227  		v2:  NULL,
  1228  		max: NULL,
  1229  	}, {
  1230  		v1:  NewInt64(1),
  1231  		v2:  NULL,
  1232  		max: NewInt64(1),
  1233  	}, {
  1234  		v1:  NULL,
  1235  		v2:  NewInt64(1),
  1236  		max: NewInt64(1),
  1237  	}, {
  1238  		v1:  NewInt64(1),
  1239  		v2:  NewInt64(2),
  1240  		max: NewInt64(2),
  1241  	}, {
  1242  		v1:  NewInt64(2),
  1243  		v2:  NewInt64(1),
  1244  		max: NewInt64(2),
  1245  	}, {
  1246  		v1:  NewInt64(1),
  1247  		v2:  NewInt64(1),
  1248  		max: NewInt64(1),
  1249  	}, {
  1250  		v1:  TestValue(sqltypes.VarChar, "aa"),
  1251  		v2:  TestValue(sqltypes.VarChar, "aa"),
  1252  		err: vterrors.New(vtrpcpb.Code_UNKNOWN, "cannot compare strings, collation is unknown or unsupported (collation ID: 0)"),
  1253  	}}
  1254  	for _, tcase := range tcases {
  1255  		v, err := Max(tcase.v1, tcase.v2, collations.Unknown)
  1256  		if !vterrors.Equals(err, tcase.err) {
  1257  			t.Errorf("Max error: %v, want %v", vterrors.Print(err), vterrors.Print(tcase.err))
  1258  		}
  1259  		if tcase.err != nil {
  1260  			continue
  1261  		}
  1262  
  1263  		if !reflect.DeepEqual(v, tcase.max) {
  1264  			t.Errorf("Max(%v, %v): %v, want %v", tcase.v1, tcase.v2, v, tcase.max)
  1265  		}
  1266  	}
  1267  }
  1268  
  1269  func TestMaxCollate(t *testing.T) {
  1270  	tcases := []struct {
  1271  		v1, v2    string
  1272  		collation collations.ID
  1273  		out       string
  1274  		err       error
  1275  	}{
  1276  		{
  1277  			// accent insensitive
  1278  			v1:        "ǍḄÇ",
  1279  			v2:        "ÁḆĈ",
  1280  			out:       "ǍḄÇ",
  1281  			collation: getCollationID("utf8mb4_0900_as_ci"),
  1282  		},
  1283  		{
  1284  			// kana sensitive
  1285  			v1:        "\xE3\x81\xAB\xE3\x81\xBB\xE3\x82\x93\xE3\x81\x94",
  1286  			v2:        "\xE3\x83\x8B\xE3\x83\x9B\xE3\x83\xB3\xE3\x82\xB4",
  1287  			out:       "\xE3\x83\x8B\xE3\x83\x9B\xE3\x83\xB3\xE3\x82\xB4",
  1288  			collation: getCollationID("utf8mb4_ja_0900_as_cs_ks"),
  1289  		},
  1290  		{
  1291  			// non breaking space
  1292  			v1:        "abc ",
  1293  			v2:        "abc\u00a0",
  1294  			out:       "abc\u00a0",
  1295  			collation: getCollationID("utf8mb4_0900_as_cs"),
  1296  		},
  1297  		{
  1298  			// "cs" counts as a separate letter, where c < cs < d
  1299  			v1:        "c",
  1300  			v2:        "cs",
  1301  			out:       "cs",
  1302  			collation: getCollationID("utf8mb4_hu_0900_ai_ci"),
  1303  		},
  1304  		{
  1305  			// "cs" counts as a separate letter, where c < cs < d
  1306  			v1:        "cukor",
  1307  			v2:        "csak",
  1308  			out:       "csak",
  1309  			collation: getCollationID("utf8mb4_hu_0900_ai_ci"),
  1310  		},
  1311  	}
  1312  	for _, tcase := range tcases {
  1313  		got, err := Max(TestValue(sqltypes.VarChar, tcase.v1), TestValue(sqltypes.VarChar, tcase.v2), tcase.collation)
  1314  		if !vterrors.Equals(err, tcase.err) {
  1315  			t.Errorf("NullsafeCompare(%v, %v) error: %v, want %v", tcase.v1, tcase.v2, vterrors.Print(err), vterrors.Print(tcase.err))
  1316  		}
  1317  		if tcase.err != nil {
  1318  			continue
  1319  		}
  1320  
  1321  		if got.ToString() != tcase.out {
  1322  			t.Errorf("NullsafeCompare(%v, %v): %v, want %v", tcase.v1, tcase.v2, got, tcase.out)
  1323  		}
  1324  	}
  1325  }
  1326  
  1327  func printValue(v sqltypes.Value) string {
  1328  	vBytes, _ := v.ToBytes()
  1329  	return fmt.Sprintf("%v:%q", v.Type(), vBytes)
  1330  }
  1331  
  1332  // These benchmarks show that using existing ASCII representations
  1333  // for numbers is about 6x slower than using native representations.
  1334  // However, 229ns is still a negligible time compared to the cost of
  1335  // other operations. The additional complexity of introducing native
  1336  // types is currently not worth it. So, we'll stay with the existing
  1337  // ASCII representation for now. Using interfaces is more expensive
  1338  // than native representation of values. This is probably because
  1339  // interfaces also allocate memory, and also perform type assertions.
  1340  // Actual benchmark is based on NoNative. So, the numbers are similar.
  1341  // Date: 6/4/17
  1342  // Version: go1.8
  1343  // BenchmarkAddActual-8            10000000               263 ns/op
  1344  // BenchmarkAddNoNative-8          10000000               228 ns/op
  1345  // BenchmarkAddNative-8            50000000                40.0 ns/op
  1346  // BenchmarkAddGoInterface-8       30000000                52.4 ns/op
  1347  // BenchmarkAddGoNonInterface-8    2000000000               1.00 ns/op
  1348  // BenchmarkAddGo-8                2000000000               1.00 ns/op
  1349  func BenchmarkAddActual(b *testing.B) {
  1350  	v1 := sqltypes.MakeTrusted(sqltypes.Int64, []byte("1"))
  1351  	v2 := sqltypes.MakeTrusted(sqltypes.Int64, []byte("12"))
  1352  	for i := 0; i < b.N; i++ {
  1353  		v1, _ = NullSafeAdd(v1, v2, sqltypes.Int64)
  1354  	}
  1355  }
  1356  
  1357  func BenchmarkAddNoNative(b *testing.B) {
  1358  	v1 := sqltypes.MakeTrusted(sqltypes.Int64, []byte("1"))
  1359  	v2 := sqltypes.MakeTrusted(sqltypes.Int64, []byte("12"))
  1360  	for i := 0; i < b.N; i++ {
  1361  		iv1, _ := ToInt64(v1)
  1362  		iv2, _ := ToInt64(v2)
  1363  		v1 = sqltypes.MakeTrusted(sqltypes.Int64, strconv.AppendInt(nil, iv1+iv2, 10))
  1364  	}
  1365  }
  1366  
  1367  func BenchmarkAddNative(b *testing.B) {
  1368  	v1 := makeNativeInt64(1)
  1369  	v2 := makeNativeInt64(12)
  1370  	for i := 0; i < b.N; i++ {
  1371  		iv1 := int64(binary.BigEndian.Uint64(v1.Raw()))
  1372  		iv2 := int64(binary.BigEndian.Uint64(v2.Raw()))
  1373  		v1 = makeNativeInt64(iv1 + iv2)
  1374  	}
  1375  }
  1376  
  1377  func makeNativeInt64(v int64) sqltypes.Value {
  1378  	buf := make([]byte, 8)
  1379  	binary.BigEndian.PutUint64(buf, uint64(v))
  1380  	return sqltypes.MakeTrusted(sqltypes.Int64, buf)
  1381  }
  1382  
  1383  func BenchmarkAddGoInterface(b *testing.B) {
  1384  	var v1, v2 any
  1385  	v1 = int64(1)
  1386  	v2 = int64(2)
  1387  	for i := 0; i < b.N; i++ {
  1388  		v1 = v1.(int64) + v2.(int64)
  1389  	}
  1390  }
  1391  
  1392  func BenchmarkAddGoNonInterface(b *testing.B) {
  1393  	v1 := newEvalInt64(1)
  1394  	v2 := newEvalInt64(12)
  1395  	for i := 0; i < b.N; i++ {
  1396  		if v1.typeof() != sqltypes.Int64 {
  1397  			b.Error("type assertion failed")
  1398  		}
  1399  		if v2.typeof() != sqltypes.Int64 {
  1400  			b.Error("type assertion failed")
  1401  		}
  1402  		v1 = newEvalInt64(v1.int64() + v2.int64())
  1403  	}
  1404  }
  1405  
  1406  func BenchmarkAddGo(b *testing.B) {
  1407  	v1 := int64(1)
  1408  	v2 := int64(2)
  1409  	for i := 0; i < b.N; i++ {
  1410  		v1 += v2
  1411  	}
  1412  }
  1413  
  1414  func TestParseStringToFloat(t *testing.T) {
  1415  	tcs := []struct {
  1416  		str string
  1417  		val float64
  1418  	}{
  1419  		{str: ""},
  1420  		{str: " "},
  1421  		{str: "1", val: 1},
  1422  		{str: "1.10", val: 1.10},
  1423  		{str: "    6.87", val: 6.87},
  1424  		{str: "93.66  ", val: 93.66},
  1425  		{str: "\t 42.10 \n ", val: 42.10},
  1426  		{str: "1.10aa", val: 1.10},
  1427  		{str: ".", val: 0.00},
  1428  		{str: ".99", val: 0.99},
  1429  		{str: "..99", val: 0},
  1430  		{str: "1.", val: 1},
  1431  		{str: "0.1.99", val: 0.1},
  1432  		{str: "0.", val: 0},
  1433  		{str: "8794354", val: 8794354},
  1434  		{str: "    10  ", val: 10},
  1435  		{str: "2266951196291479516", val: 2266951196291479516},
  1436  		{str: "abcd123", val: 0},
  1437  	}
  1438  
  1439  	for _, tc := range tcs {
  1440  		t.Run(tc.str, func(t *testing.T) {
  1441  			got := parseStringToFloat(tc.str)
  1442  			require.EqualValues(t, tc.val, got)
  1443  		})
  1444  	}
  1445  }