vitess.io/vitess@v0.16.2/go/sqltypes/bind_variables_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 sqltypes
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/stretchr/testify/assert"
    26  
    27  	"github.com/stretchr/testify/require"
    28  	"google.golang.org/protobuf/proto"
    29  
    30  	querypb "vitess.io/vitess/go/vt/proto/query"
    31  )
    32  
    33  func TestProtoConversions(t *testing.T) {
    34  	v := TestValue(Int64, "1")
    35  	got := ValueToProto(v)
    36  	want := &querypb.Value{Type: Int64, Value: []byte("1")}
    37  	if !proto.Equal(got, want) {
    38  		t.Errorf("ValueToProto: %v, want %v", got, want)
    39  	}
    40  	gotback := ProtoToValue(got)
    41  	if !reflect.DeepEqual(gotback, v) {
    42  		t.Errorf("ProtoToValue: %v, want %v", gotback, v)
    43  	}
    44  }
    45  
    46  func TestBuildBindVariables(t *testing.T) {
    47  	tcases := []struct {
    48  		in  map[string]any
    49  		out map[string]*querypb.BindVariable
    50  		err string
    51  	}{{
    52  		in:  nil,
    53  		out: nil,
    54  	}, {
    55  		in: map[string]any{
    56  			"k": int64(1),
    57  		},
    58  		out: map[string]*querypb.BindVariable{
    59  			"k": Int64BindVariable(1),
    60  		},
    61  	}, {
    62  		in: map[string]any{
    63  			"k": byte(1),
    64  		},
    65  		err: "k: type uint8 not supported as bind var: 1",
    66  	}}
    67  	for _, tcase := range tcases {
    68  		bindVars, err := BuildBindVariables(tcase.in)
    69  		if err != nil {
    70  			if err.Error() != tcase.err {
    71  				t.Errorf("MapToBindVars(%v) error: %v, want %s", tcase.in, err, tcase.err)
    72  			}
    73  			continue
    74  		}
    75  		if tcase.err != "" {
    76  			t.Errorf("MapToBindVars(%v) error: nil, want %s", tcase.in, tcase.err)
    77  			continue
    78  		}
    79  		if !BindVariablesEqual(bindVars, tcase.out) {
    80  			t.Errorf("MapToBindVars(%v): %v, want %s", tcase.in, bindVars, tcase.out)
    81  		}
    82  	}
    83  }
    84  
    85  func TestBuildBindVariable(t *testing.T) {
    86  	tcases := []struct {
    87  		in  any
    88  		out *querypb.BindVariable
    89  		err string
    90  	}{{
    91  		in: "aa",
    92  		out: &querypb.BindVariable{
    93  			Type:  querypb.Type_VARCHAR,
    94  			Value: []byte("aa"),
    95  		},
    96  	}, {
    97  		in: []byte("aa"),
    98  		out: &querypb.BindVariable{
    99  			Type:  querypb.Type_VARBINARY,
   100  			Value: []byte("aa"),
   101  		},
   102  	}, {
   103  		in: true,
   104  		out: &querypb.BindVariable{
   105  			Type:  querypb.Type_INT8,
   106  			Value: []byte("1"),
   107  		},
   108  	}, {
   109  		in: false,
   110  		out: &querypb.BindVariable{
   111  			Type:  querypb.Type_INT8,
   112  			Value: []byte("0"),
   113  		},
   114  	}, {
   115  		in: int(1),
   116  		out: &querypb.BindVariable{
   117  			Type:  querypb.Type_INT64,
   118  			Value: []byte("1"),
   119  		},
   120  	}, {
   121  		in: int64(1),
   122  		out: &querypb.BindVariable{
   123  			Type:  querypb.Type_INT64,
   124  			Value: []byte("1"),
   125  		},
   126  	}, {
   127  		in: uint64(1),
   128  		out: &querypb.BindVariable{
   129  			Type:  querypb.Type_UINT64,
   130  			Value: []byte("1"),
   131  		},
   132  	}, {
   133  		in: float64(1),
   134  		out: &querypb.BindVariable{
   135  			Type:  querypb.Type_FLOAT64,
   136  			Value: []byte("1"),
   137  		},
   138  	}, {
   139  		in:  nil,
   140  		out: NullBindVariable,
   141  	}, {
   142  		in: MakeTrusted(Int64, []byte("1")),
   143  		out: &querypb.BindVariable{
   144  			Type:  querypb.Type_INT64,
   145  			Value: []byte("1"),
   146  		},
   147  	}, {
   148  		in: &querypb.BindVariable{
   149  			Type:  querypb.Type_INT64,
   150  			Value: []byte("1"),
   151  		},
   152  		out: &querypb.BindVariable{
   153  			Type:  querypb.Type_INT64,
   154  			Value: []byte("1"),
   155  		},
   156  	}, {
   157  		in: []any{"aa", int64(1)},
   158  		out: &querypb.BindVariable{
   159  			Type: querypb.Type_TUPLE,
   160  			Values: []*querypb.Value{{
   161  				Type:  querypb.Type_VARCHAR,
   162  				Value: []byte("aa"),
   163  			}, {
   164  				Type:  querypb.Type_INT64,
   165  				Value: []byte("1"),
   166  			}},
   167  		},
   168  	}, {
   169  		in: []string{"aa", "bb"},
   170  		out: &querypb.BindVariable{
   171  			Type: querypb.Type_TUPLE,
   172  			Values: []*querypb.Value{{
   173  				Type:  querypb.Type_VARCHAR,
   174  				Value: []byte("aa"),
   175  			}, {
   176  				Type:  querypb.Type_VARCHAR,
   177  				Value: []byte("bb"),
   178  			}},
   179  		},
   180  	}, {
   181  		in: [][]byte{[]byte("aa"), []byte("bb")},
   182  		out: &querypb.BindVariable{
   183  			Type: querypb.Type_TUPLE,
   184  			Values: []*querypb.Value{{
   185  				Type:  querypb.Type_VARBINARY,
   186  				Value: []byte("aa"),
   187  			}, {
   188  				Type:  querypb.Type_VARBINARY,
   189  				Value: []byte("bb"),
   190  			}},
   191  		},
   192  	}, {
   193  		in: []int{1, 2},
   194  		out: &querypb.BindVariable{
   195  			Type: querypb.Type_TUPLE,
   196  			Values: []*querypb.Value{{
   197  				Type:  querypb.Type_INT64,
   198  				Value: []byte("1"),
   199  			}, {
   200  				Type:  querypb.Type_INT64,
   201  				Value: []byte("2"),
   202  			}},
   203  		},
   204  	}, {
   205  		in: []int64{1, 2},
   206  		out: &querypb.BindVariable{
   207  			Type: querypb.Type_TUPLE,
   208  			Values: []*querypb.Value{{
   209  				Type:  querypb.Type_INT64,
   210  				Value: []byte("1"),
   211  			}, {
   212  				Type:  querypb.Type_INT64,
   213  				Value: []byte("2"),
   214  			}},
   215  		},
   216  	}, {
   217  		in: []uint64{1, 2},
   218  		out: &querypb.BindVariable{
   219  			Type: querypb.Type_TUPLE,
   220  			Values: []*querypb.Value{{
   221  				Type:  querypb.Type_UINT64,
   222  				Value: []byte("1"),
   223  			}, {
   224  				Type:  querypb.Type_UINT64,
   225  				Value: []byte("2"),
   226  			}},
   227  		},
   228  	}, {
   229  		in: []float64{1, 2},
   230  		out: &querypb.BindVariable{
   231  			Type: querypb.Type_TUPLE,
   232  			Values: []*querypb.Value{{
   233  				Type:  querypb.Type_FLOAT64,
   234  				Value: []byte("1"),
   235  			}, {
   236  				Type:  querypb.Type_FLOAT64,
   237  				Value: []byte("2"),
   238  			}},
   239  		},
   240  	}, {
   241  		in:  byte(1),
   242  		err: "type uint8 not supported as bind var: 1",
   243  	}, {
   244  		in:  []any{1, byte(1)},
   245  		err: "type uint8 not supported as bind var: 1",
   246  	}}
   247  	for _, tcase := range tcases {
   248  		t.Run(fmt.Sprintf("%v", tcase.in), func(t *testing.T) {
   249  			bv, err := BuildBindVariable(tcase.in)
   250  			if tcase.err != "" {
   251  				require.EqualError(t, err, tcase.err)
   252  			} else {
   253  				require.Truef(t, proto.Equal(tcase.out, bv), "binvar output did not match")
   254  			}
   255  		})
   256  	}
   257  }
   258  
   259  func TestValidateBindVarables(t *testing.T) {
   260  	tcases := []struct {
   261  		in  map[string]*querypb.BindVariable
   262  		err string
   263  	}{{
   264  		in: map[string]*querypb.BindVariable{
   265  			"v": {
   266  				Type:  querypb.Type_INT64,
   267  				Value: []byte("1"),
   268  			},
   269  		},
   270  		err: "",
   271  	}, {
   272  		in: map[string]*querypb.BindVariable{
   273  			"v": {
   274  				Type:  querypb.Type_INT64,
   275  				Value: []byte("a"),
   276  			},
   277  		},
   278  		err: `v: strconv.ParseInt: parsing "a": invalid syntax`,
   279  	}, {
   280  		in: map[string]*querypb.BindVariable{
   281  			"v": {
   282  				Type: querypb.Type_TUPLE,
   283  				Values: []*querypb.Value{{
   284  					Type:  Int64,
   285  					Value: []byte("a"),
   286  				}},
   287  			},
   288  		},
   289  		err: `v: strconv.ParseInt: parsing "a": invalid syntax`,
   290  	}}
   291  	for _, tcase := range tcases {
   292  		err := ValidateBindVariables(tcase.in)
   293  		if tcase.err != "" {
   294  			if err == nil || err.Error() != tcase.err {
   295  				t.Errorf("ValidateBindVars(%v): %v, want %s", tcase.in, err, tcase.err)
   296  			}
   297  			continue
   298  		}
   299  		if err != nil {
   300  			t.Errorf("ValidateBindVars(%v): %v, want nil", tcase.in, err)
   301  		}
   302  	}
   303  }
   304  
   305  func TestValidateBindVariable(t *testing.T) {
   306  	testcases := []struct {
   307  		in  *querypb.BindVariable
   308  		err string
   309  	}{{
   310  		in: &querypb.BindVariable{
   311  			Type:  querypb.Type_INT8,
   312  			Value: []byte("1"),
   313  		},
   314  	}, {
   315  		in: &querypb.BindVariable{
   316  			Type:  querypb.Type_INT16,
   317  			Value: []byte("1"),
   318  		},
   319  	}, {
   320  		in: &querypb.BindVariable{
   321  			Type:  querypb.Type_INT24,
   322  			Value: []byte("1"),
   323  		},
   324  	}, {
   325  		in: &querypb.BindVariable{
   326  			Type:  querypb.Type_INT32,
   327  			Value: []byte("1"),
   328  		},
   329  	}, {
   330  		in: &querypb.BindVariable{
   331  			Type:  querypb.Type_INT64,
   332  			Value: []byte("1"),
   333  		},
   334  	}, {
   335  		in: &querypb.BindVariable{
   336  			Type:  querypb.Type_UINT8,
   337  			Value: []byte("1"),
   338  		},
   339  	}, {
   340  		in: &querypb.BindVariable{
   341  			Type:  querypb.Type_UINT16,
   342  			Value: []byte("1"),
   343  		},
   344  	}, {
   345  		in: &querypb.BindVariable{
   346  			Type:  querypb.Type_UINT24,
   347  			Value: []byte("1"),
   348  		},
   349  	}, {
   350  		in: &querypb.BindVariable{
   351  			Type:  querypb.Type_UINT32,
   352  			Value: []byte("1"),
   353  		},
   354  	}, {
   355  		in: &querypb.BindVariable{
   356  			Type:  querypb.Type_UINT64,
   357  			Value: []byte("1"),
   358  		},
   359  	}, {
   360  		in: &querypb.BindVariable{
   361  			Type:  querypb.Type_FLOAT32,
   362  			Value: []byte("1.00"),
   363  		},
   364  	}, {
   365  		in: &querypb.BindVariable{
   366  			Type:  querypb.Type_FLOAT64,
   367  			Value: []byte("1.00"),
   368  		},
   369  	}, {
   370  		in: &querypb.BindVariable{
   371  			Type:  querypb.Type_DECIMAL,
   372  			Value: []byte("1.00"),
   373  		},
   374  	}, {
   375  		in: &querypb.BindVariable{
   376  			Type:  querypb.Type_TIMESTAMP,
   377  			Value: []byte("2012-02-24 23:19:43"),
   378  		},
   379  	}, {
   380  		in: &querypb.BindVariable{
   381  			Type:  querypb.Type_DATE,
   382  			Value: []byte("2012-02-24"),
   383  		},
   384  	}, {
   385  		in: &querypb.BindVariable{
   386  			Type:  querypb.Type_TIME,
   387  			Value: []byte("23:19:43"),
   388  		},
   389  	}, {
   390  		in: &querypb.BindVariable{
   391  			Type:  querypb.Type_DATETIME,
   392  			Value: []byte("2012-02-24 23:19:43"),
   393  		},
   394  	}, {
   395  		in: &querypb.BindVariable{
   396  			Type:  querypb.Type_YEAR,
   397  			Value: []byte("1"),
   398  		},
   399  	}, {
   400  		in: &querypb.BindVariable{
   401  			Type:  querypb.Type_TEXT,
   402  			Value: []byte("a"),
   403  		},
   404  	}, {
   405  		in: &querypb.BindVariable{
   406  			Type:  querypb.Type_BLOB,
   407  			Value: []byte("a"),
   408  		},
   409  	}, {
   410  		in: &querypb.BindVariable{
   411  			Type:  querypb.Type_VARCHAR,
   412  			Value: []byte("a"),
   413  		},
   414  	}, {
   415  		in: &querypb.BindVariable{
   416  			Type:  querypb.Type_BINARY,
   417  			Value: []byte("a"),
   418  		},
   419  	}, {
   420  		in: &querypb.BindVariable{
   421  			Type:  querypb.Type_CHAR,
   422  			Value: []byte("a"),
   423  		},
   424  	}, {
   425  		in: &querypb.BindVariable{
   426  			Type:  querypb.Type_BIT,
   427  			Value: []byte("1"),
   428  		},
   429  	}, {
   430  		in: &querypb.BindVariable{
   431  			Type:  querypb.Type_ENUM,
   432  			Value: []byte("a"),
   433  		},
   434  	}, {
   435  		in: &querypb.BindVariable{
   436  			Type:  querypb.Type_SET,
   437  			Value: []byte("a"),
   438  		},
   439  	}, {
   440  		in: &querypb.BindVariable{
   441  			Type:  querypb.Type_VARBINARY,
   442  			Value: []byte("a"),
   443  		},
   444  	}, {
   445  		in: &querypb.BindVariable{
   446  			Type:  querypb.Type_INT64,
   447  			Value: []byte(InvalidNeg),
   448  		},
   449  		err: "out of range",
   450  	}, {
   451  		in: &querypb.BindVariable{
   452  			Type:  querypb.Type_INT64,
   453  			Value: []byte(InvalidPos),
   454  		},
   455  		err: "out of range",
   456  	}, {
   457  		in: &querypb.BindVariable{
   458  			Type:  querypb.Type_UINT64,
   459  			Value: []byte("-1"),
   460  		},
   461  		err: "invalid syntax",
   462  	}, {
   463  		in: &querypb.BindVariable{
   464  			Type:  querypb.Type_UINT64,
   465  			Value: []byte(InvalidPos),
   466  		},
   467  		err: "out of range",
   468  	}, {
   469  		in: &querypb.BindVariable{
   470  			Type:  querypb.Type_FLOAT64,
   471  			Value: []byte("a"),
   472  		},
   473  		err: "invalid syntax",
   474  	}, {
   475  		in: &querypb.BindVariable{
   476  			Type:  querypb.Type_EXPRESSION,
   477  			Value: []byte("a"),
   478  		},
   479  		err: "invalid type specified for MakeValue: EXPRESSION",
   480  	}, {
   481  		in: &querypb.BindVariable{
   482  			Type: querypb.Type_TUPLE,
   483  			Values: []*querypb.Value{{
   484  				Type:  querypb.Type_INT64,
   485  				Value: []byte("1"),
   486  			}},
   487  		},
   488  	}, {
   489  		in: &querypb.BindVariable{
   490  			Type: querypb.Type_TUPLE,
   491  		},
   492  		err: "empty tuple is not allowed",
   493  	}, {
   494  		in: &querypb.BindVariable{
   495  			Type: querypb.Type_TUPLE,
   496  			Values: []*querypb.Value{{
   497  				Type: querypb.Type_TUPLE,
   498  			}},
   499  		},
   500  		err: "tuple not allowed inside another tuple",
   501  	}}
   502  	for _, tcase := range testcases {
   503  		err := ValidateBindVariable(tcase.in)
   504  		if tcase.err != "" {
   505  			if err == nil || !strings.Contains(err.Error(), tcase.err) {
   506  				t.Errorf("ValidateBindVar(%v) error: %v, must contain %v", tcase.in, err, tcase.err)
   507  			}
   508  			continue
   509  		}
   510  		if err != nil {
   511  			t.Errorf("ValidateBindVar(%v) error: %v", tcase.in, err)
   512  		}
   513  	}
   514  
   515  	// Special case: nil bind var.
   516  	err := ValidateBindVariable(nil)
   517  	want := "bind variable is nil"
   518  	if err == nil || err.Error() != want {
   519  		t.Errorf("ValidateBindVar(nil) error: %v, want %s", err, want)
   520  	}
   521  }
   522  
   523  func TestBindVariableToValue(t *testing.T) {
   524  	v, err := BindVariableToValue(Int64BindVariable(1))
   525  	require.NoError(t, err)
   526  	assert.Equal(t, MakeTrusted(querypb.Type_INT64, []byte("1")), v)
   527  
   528  	_, err = BindVariableToValue(&querypb.BindVariable{Type: querypb.Type_TUPLE})
   529  	require.EqualError(t, err, "cannot convert a TUPLE bind var into a value")
   530  
   531  	v, err = BindVariableToValue(BitNumBindVariable([]byte("0b101")))
   532  	require.NoError(t, err)
   533  	assert.Equal(t, MakeTrusted(querypb.Type_BITNUM, []byte("0b101")), v)
   534  
   535  }
   536  
   537  func TestBindVariablesEqual(t *testing.T) {
   538  	bv1 := map[string]*querypb.BindVariable{
   539  		"k": {
   540  			Type:  querypb.Type_INT64,
   541  			Value: []byte("1"),
   542  		},
   543  	}
   544  	bv2 := map[string]*querypb.BindVariable{
   545  		"k": {
   546  			Type:  querypb.Type_INT64,
   547  			Value: []byte("1"),
   548  		},
   549  	}
   550  	bv3 := map[string]*querypb.BindVariable{
   551  		"k": {
   552  			Type:  querypb.Type_INT64,
   553  			Value: []byte("1"),
   554  		},
   555  	}
   556  	if !BindVariablesEqual(bv1, bv2) {
   557  		t.Errorf("%v != %v, want equal", bv1, bv2)
   558  	}
   559  	if !BindVariablesEqual(bv1, bv3) {
   560  		t.Errorf("%v = %v, want not equal", bv1, bv3)
   561  	}
   562  }
   563  
   564  func TestBindVariablesFormat(t *testing.T) {
   565  	tupleBindVar, err := BuildBindVariable([]int64{1, 2})
   566  	if err != nil {
   567  		t.Fatalf("failed to create a tuple bind var: %v", err)
   568  	}
   569  
   570  	bindVariables := map[string]*querypb.BindVariable{
   571  		"key_1": StringBindVariable("val_1"),
   572  		"key_2": Int64BindVariable(789),
   573  		"key_3": BytesBindVariable([]byte("val_3")),
   574  		"key_4": tupleBindVar,
   575  	}
   576  
   577  	formattedStr := FormatBindVariables(bindVariables, true /* full */, false /* asJSON */)
   578  	if !strings.Contains(formattedStr, "key_1") ||
   579  		!strings.Contains(formattedStr, "val_1") {
   580  		t.Fatalf("bind variable 'key_1': 'val_1' is not formatted")
   581  	}
   582  	if !strings.Contains(formattedStr, "key_2") ||
   583  		!strings.Contains(formattedStr, "789") {
   584  		t.Fatalf("bind variable 'key_2': '789' is not formatted")
   585  	}
   586  	if !strings.Contains(formattedStr, "key_3") || !strings.Contains(formattedStr, "val_3") {
   587  		t.Fatalf("bind variable 'key_3': 'val_3' is not formatted")
   588  	}
   589  	if !strings.Contains(formattedStr, "key_4:type:TUPLE") {
   590  		t.Fatalf("bind variable 'key_4': (1, 2) is not formatted")
   591  	}
   592  
   593  	formattedStr = FormatBindVariables(bindVariables, false /* full */, false /* asJSON */)
   594  	if !strings.Contains(formattedStr, "key_1") {
   595  		t.Fatalf("bind variable 'key_1' is not formatted")
   596  	}
   597  	if !strings.Contains(formattedStr, "key_2") ||
   598  		!strings.Contains(formattedStr, "789") {
   599  		t.Fatalf("bind variable 'key_2': '789' is not formatted")
   600  	}
   601  	if !strings.Contains(formattedStr, "key_3") || !strings.Contains(formattedStr, "5 bytes") {
   602  		t.Fatalf("bind variable 'key_3' is not formatted")
   603  	}
   604  	if !strings.Contains(formattedStr, "key_4") || !strings.Contains(formattedStr, "2 items") {
   605  		t.Fatalf("bind variable 'key_4' is not formatted")
   606  	}
   607  
   608  	formattedStr = FormatBindVariables(bindVariables, true /* full */, true /* asJSON */)
   609  	t.Logf("%q", formattedStr)
   610  	if !strings.Contains(formattedStr, "\"key_1\": {\"type\": \"VARCHAR\", \"value\": \"val_1\"}") {
   611  		t.Fatalf("bind variable 'key_1' is not formatted")
   612  	}
   613  
   614  	if !strings.Contains(formattedStr, "\"key_2\": {\"type\": \"INT64\", \"value\": 789}") {
   615  		t.Fatalf("bind variable 'key_2' is not formatted")
   616  	}
   617  
   618  	if !strings.Contains(formattedStr, "\"key_3\": {\"type\": \"VARBINARY\", \"value\": \"val_3\"}") {
   619  		t.Fatalf("bind variable 'key_3' is not formatted")
   620  	}
   621  
   622  	if !strings.Contains(formattedStr, "\"key_4\": {\"type\": \"TUPLE\", \"value\": \"\"}") {
   623  		t.Fatalf("bind variable 'key_4' is not formatted")
   624  	}
   625  
   626  	formattedStr = FormatBindVariables(bindVariables, false /* full */, true /* asJSON */)
   627  	if !strings.Contains(formattedStr, "\"key_1\": {\"type\": \"VARCHAR\", \"value\": \"5 bytes\"}") {
   628  		t.Fatalf("bind variable 'key_1' is not formatted")
   629  	}
   630  
   631  	if !strings.Contains(formattedStr, "\"key_2\": {\"type\": \"INT64\", \"value\": 789}") {
   632  		t.Fatalf("bind variable 'key_2' is not formatted")
   633  	}
   634  
   635  	if !strings.Contains(formattedStr, "\"key_3\": {\"type\": \"VARCHAR\", \"value\": \"5 bytes\"}") {
   636  		t.Fatalf("bind variable 'key_3' is not formatted")
   637  	}
   638  
   639  	if !strings.Contains(formattedStr, "\"key_4\": {\"type\": \"VARCHAR\", \"value\": \"2 items\"}") {
   640  		t.Fatalf("bind variable 'key_4' is not formatted")
   641  	}
   642  }