vitess.io/vitess@v0.16.2/go/vt/vtgate/vindexes/lookup_hash_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 vindexes
    18  
    19  import (
    20  	"context"
    21  	"reflect"
    22  	"testing"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  
    27  	"vitess.io/vitess/go/sqltypes"
    28  	"vitess.io/vitess/go/vt/key"
    29  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    30  )
    31  
    32  func TestLookupHashNew(t *testing.T) {
    33  	l := createLookup(t, "lookup_hash", false /* writeOnly */)
    34  	if want, got := l.(*LookupHash).writeOnly, false; got != want {
    35  		t.Errorf("Create(lookup, false): %v, want %v", got, want)
    36  	}
    37  
    38  	l = createLookup(t, "lookup_hash", true)
    39  	if want, got := l.(*LookupHash).writeOnly, true; got != want {
    40  		t.Errorf("Create(lookup, false): %v, want %v", got, want)
    41  	}
    42  
    43  	_, err := CreateVindex("lookup_hash", "lookup_hash", map[string]string{
    44  		"table":      "t",
    45  		"from":       "fromc",
    46  		"to":         "toc",
    47  		"write_only": "invalid",
    48  	})
    49  	want := "write_only value must be 'true' or 'false': 'invalid'"
    50  	if err == nil || err.Error() != want {
    51  		t.Errorf("Create(bad_scatter): %v, want %s", err, want)
    52  	}
    53  }
    54  
    55  func TestLookupHashInfo(t *testing.T) {
    56  	lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */)
    57  	assert.Equal(t, 20, lookuphash.Cost())
    58  	assert.Equal(t, "lookup_hash", lookuphash.String())
    59  	assert.False(t, lookuphash.IsUnique())
    60  	assert.True(t, lookuphash.NeedsVCursor())
    61  
    62  	lookuphashunique := createLookup(t, "lookup_hash_unique", false /* writeOnly */)
    63  	assert.Equal(t, 10, lookuphashunique.Cost())
    64  	assert.Equal(t, "lookup_hash_unique", lookuphashunique.String())
    65  	assert.True(t, lookuphashunique.IsUnique())
    66  	assert.True(t, lookuphashunique.NeedsVCursor())
    67  }
    68  
    69  func TestLookupHashMap(t *testing.T) {
    70  	lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */)
    71  	vc := &vcursor{numRows: 2}
    72  
    73  	got, err := lookuphash.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)})
    74  	require.NoError(t, err)
    75  	want := []key.Destination{
    76  		key.DestinationKeyspaceIDs([][]byte{
    77  			[]byte("\x16k@\xb4J\xbaK\xd6"),
    78  			[]byte("\x06\xe7\xea\"Βp\x8f"),
    79  		}),
    80  		key.DestinationKeyspaceIDs([][]byte{
    81  			[]byte("\x16k@\xb4J\xbaK\xd6"),
    82  			[]byte("\x06\xe7\xea\"Βp\x8f"),
    83  		}),
    84  	}
    85  	if !reflect.DeepEqual(got, want) {
    86  		t.Errorf("Map(): %#v, want %+v", got, want)
    87  	}
    88  
    89  	// Test conversion fail.
    90  	vc.result = sqltypes.MakeTestResult(
    91  		sqltypes.MakeTestFields("b|a", "int64|varbinary"),
    92  		"1|notint",
    93  	)
    94  	got, err = lookuphash.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)})
    95  	require.NoError(t, err)
    96  	want = []key.Destination{key.DestinationKeyspaceIDs([][]byte{})}
    97  	if !reflect.DeepEqual(got, want) {
    98  		t.Errorf("Map(): %#v, want %#v", got, want)
    99  	}
   100  
   101  	// Test query fail.
   102  	vc.mustFail = true
   103  	_, err = lookuphash.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)})
   104  	wantErr := "lookup.Map: execute failed"
   105  	if err == nil || err.Error() != wantErr {
   106  		t.Errorf("lookuphash(query fail) err: %v, want %s", err, wantErr)
   107  	}
   108  	vc.mustFail = false
   109  }
   110  
   111  func TestLookupHashMapAbsent(t *testing.T) {
   112  	lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */)
   113  	vc := &vcursor{numRows: 0}
   114  
   115  	got, err := lookuphash.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)})
   116  	require.NoError(t, err)
   117  	want := []key.Destination{
   118  		key.DestinationNone{},
   119  		key.DestinationNone{},
   120  	}
   121  	if !reflect.DeepEqual(got, want) {
   122  		t.Errorf("Map(): %#v, want %+v", got, want)
   123  	}
   124  
   125  	// writeOnly true should return full keyranges.
   126  	lookuphash = createLookup(t, "lookup_hash", true)
   127  	got, err = lookuphash.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)})
   128  	require.NoError(t, err)
   129  	want = []key.Destination{
   130  		key.DestinationKeyRange{
   131  			KeyRange: &topodatapb.KeyRange{},
   132  		},
   133  		key.DestinationKeyRange{
   134  			KeyRange: &topodatapb.KeyRange{},
   135  		},
   136  	}
   137  	if !reflect.DeepEqual(got, want) {
   138  		t.Errorf("Map(): %#v, want %+v", got, want)
   139  	}
   140  }
   141  
   142  func TestLookupHashMapNull(t *testing.T) {
   143  	lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */)
   144  	vc := &vcursor{numRows: 1, keys: []sqltypes.Value{sqltypes.NULL}}
   145  
   146  	got, err := lookuphash.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NULL})
   147  	require.NoError(t, err)
   148  	want := []key.Destination{
   149  		key.DestinationKeyspaceIDs([][]byte{
   150  			[]byte("\x16k@\xb4J\xbaK\xd6"),
   151  		}),
   152  	}
   153  	if !reflect.DeepEqual(got, want) {
   154  		t.Errorf("Map(): %#v, want %+v", got, want)
   155  	}
   156  
   157  	// writeOnly true should return full keyranges.
   158  	lookuphash = createLookup(t, "lookup_hash", true)
   159  	got, err = lookuphash.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NULL})
   160  	require.NoError(t, err)
   161  	want = []key.Destination{
   162  		key.DestinationKeyRange{
   163  			KeyRange: &topodatapb.KeyRange{},
   164  		},
   165  	}
   166  	if !reflect.DeepEqual(got, want) {
   167  		t.Errorf("Map(): %#v, want %+v", got, want)
   168  	}
   169  }
   170  
   171  func TestLookupHashVerify(t *testing.T) {
   172  	lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */)
   173  	vc := &vcursor{numRows: 1}
   174  
   175  	// The check doesn't actually happen. But we give correct values
   176  	// to avoid confusion.
   177  	got, err := lookuphash.Verify(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6"), []byte("\x06\xe7\xea\"Βp\x8f")})
   178  	require.NoError(t, err)
   179  	want := []bool{true, true}
   180  	if !reflect.DeepEqual(got, want) {
   181  		t.Errorf("lookuphash.Verify(match): %v, want %v", got, want)
   182  	}
   183  
   184  	vc.numRows = 0
   185  	got, err = lookuphash.Verify(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")})
   186  	require.NoError(t, err)
   187  	want = []bool{false}
   188  	if !reflect.DeepEqual(got, want) {
   189  		t.Errorf("lookuphash.Verify(mismatch): %v, want %v", got, want)
   190  	}
   191  
   192  	_, err = lookuphash.Verify(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)}, [][]byte{[]byte("bogus")})
   193  	wantErr := "lookup.Verify.vunhash: invalid keyspace id: 626f677573"
   194  	if err == nil || err.Error() != wantErr {
   195  		t.Errorf("lookuphash.Verify(bogus) err: %v, want %s", err, wantErr)
   196  	}
   197  
   198  	// writeOnly true should always yield true.
   199  	lookuphash = createLookup(t, "lookup_hash", true)
   200  	vc.queries = nil
   201  
   202  	got, err = lookuphash.Verify(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte(""), []byte("")})
   203  	require.NoError(t, err)
   204  	if vc.queries != nil {
   205  		t.Errorf("lookuphash.Verify(scatter), queries: %v, want nil", vc.queries)
   206  	}
   207  	wantBools := []bool{true, true}
   208  	if !reflect.DeepEqual(got, wantBools) {
   209  		t.Errorf("lookuphash.Verify(scatter): %v, want %v", got, wantBools)
   210  	}
   211  }
   212  
   213  func TestLookupHashCreate(t *testing.T) {
   214  	lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */)
   215  	vc := &vcursor{}
   216  
   217  	err := lookuphash.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}, false /* ignoreMode */)
   218  	require.NoError(t, err)
   219  	if got, want := len(vc.queries), 1; got != want {
   220  		t.Errorf("vc.queries length: %v, want %v", got, want)
   221  	}
   222  
   223  	err = lookuphash.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NULL}}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}, false /* ignoreMode */)
   224  	want := "lookup.Create: input has null values: row: 0, col: 0"
   225  	if err == nil || err.Error() != want {
   226  		t.Errorf("lookuphash.Create(NULL) err: %v, want %s", err, want)
   227  	}
   228  
   229  	vc.queries = nil
   230  	lookuphash.(*LookupHash).lkp.IgnoreNulls = true
   231  	err = lookuphash.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NULL}}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}, false /* ignoreMode */)
   232  	require.NoError(t, err)
   233  	if got, want := len(vc.queries), 0; got != want {
   234  		t.Errorf("vc.queries length: %v, want %v", got, want)
   235  	}
   236  
   237  	err = lookuphash.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, [][]byte{[]byte("bogus")}, false /* ignoreMode */)
   238  	want = "lookup.Create.vunhash: invalid keyspace id: 626f677573"
   239  	if err == nil || err.Error() != want {
   240  		t.Errorf("lookuphash.Create(bogus) err: %v, want %s", err, want)
   241  	}
   242  }
   243  
   244  func TestLookupHashDelete(t *testing.T) {
   245  	lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */)
   246  	vc := &vcursor{}
   247  
   248  	err := lookuphash.(Lookup).Delete(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, []byte("\x16k@\xb4J\xbaK\xd6"))
   249  	require.NoError(t, err)
   250  	if got, want := len(vc.queries), 1; got != want {
   251  		t.Errorf("vc.queries length: %v, want %v", got, want)
   252  	}
   253  
   254  	vc.queries = nil
   255  	err = lookuphash.(Lookup).Delete(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NULL}}, []byte("\x16k@\xb4J\xbaK\xd6"))
   256  	require.NoError(t, err)
   257  	if got, want := len(vc.queries), 1; got != want {
   258  		t.Errorf("vc.queries length: %v, want %v", got, want)
   259  	}
   260  
   261  	err = lookuphash.(Lookup).Delete(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, []byte("bogus"))
   262  	want := "lookup.Delete.vunhash: invalid keyspace id: 626f677573"
   263  	if err == nil || err.Error() != want {
   264  		t.Errorf("lookuphash.Delete(bogus) err: %v, want %s", err, want)
   265  	}
   266  }
   267  
   268  func TestLookupHashUpdate(t *testing.T) {
   269  	lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */)
   270  	vc := &vcursor{}
   271  
   272  	err := lookuphash.(Lookup).Update(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)}, []byte("\x16k@\xb4J\xbaK\xd6"), []sqltypes.Value{sqltypes.NewInt64(2)})
   273  	require.NoError(t, err)
   274  	if got, want := len(vc.queries), 2; got != want {
   275  		t.Errorf("vc.queries length: %v, want %v", got, want)
   276  	}
   277  
   278  	vc.queries = nil
   279  	err = lookuphash.(Lookup).Update(context.Background(), vc, []sqltypes.Value{sqltypes.NULL}, []byte("\x16k@\xb4J\xbaK\xd6"), []sqltypes.Value{sqltypes.NewInt64(2)})
   280  	require.NoError(t, err)
   281  	if got, want := len(vc.queries), 2; got != want {
   282  		t.Errorf("vc.queries length: %v, want %v", got, want)
   283  	}
   284  }