vitess.io/vitess@v0.16.2/go/vt/vtgate/vindexes/consistent_lookup_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  	"errors"
    22  	"fmt"
    23  	"reflect"
    24  	"sort"
    25  	"strconv"
    26  	"strings"
    27  	"testing"
    28  
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  
    32  	"vitess.io/vitess/go/mysql"
    33  	"vitess.io/vitess/go/sqltypes"
    34  	"vitess.io/vitess/go/vt/key"
    35  	querypb "vitess.io/vitess/go/vt/proto/query"
    36  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    37  	vtgatepb "vitess.io/vitess/go/vt/proto/vtgate"
    38  	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
    39  	"vitess.io/vitess/go/vt/sqlparser"
    40  	"vitess.io/vitess/go/vt/vterrors"
    41  )
    42  
    43  func TestConsistentLookupInit(t *testing.T) {
    44  	lookup := createConsistentLookup(t, "consistent_lookup", true)
    45  	cols := []sqlparser.IdentifierCI{
    46  		sqlparser.NewIdentifierCI("fc"),
    47  	}
    48  	err := lookup.(WantOwnerInfo).SetOwnerInfo("ks", "t1", cols)
    49  	want := "does not match"
    50  	if err == nil || !strings.Contains(err.Error(), want) {
    51  		t.Errorf("SetOwnerInfo: %v, want %v", err, want)
    52  	}
    53  	if got := lookup.(*ConsistentLookup).writeOnly; !got {
    54  		t.Errorf("lookup.writeOnly: false, want true")
    55  	}
    56  }
    57  
    58  func TestConsistentLookupInfo(t *testing.T) {
    59  	lookup := createConsistentLookup(t, "consistent_lookup", false)
    60  	assert.Equal(t, 20, lookup.Cost())
    61  	assert.Equal(t, "consistent_lookup", lookup.String())
    62  	assert.False(t, lookup.IsUnique())
    63  	assert.True(t, lookup.NeedsVCursor())
    64  }
    65  
    66  func TestConsistentLookupUniqueInfo(t *testing.T) {
    67  	lookup := createConsistentLookup(t, "consistent_lookup_unique", false)
    68  	assert.Equal(t, 10, lookup.Cost())
    69  	assert.Equal(t, "consistent_lookup_unique", lookup.String())
    70  	assert.True(t, lookup.IsUnique())
    71  	assert.True(t, lookup.NeedsVCursor())
    72  }
    73  
    74  func TestConsistentLookupMap(t *testing.T) {
    75  	lookup := createConsistentLookup(t, "consistent_lookup", false)
    76  	vc := &loggingVCursor{}
    77  	vc.AddResult(makeTestResultLookup([]int{2, 2}), nil)
    78  
    79  	got, err := lookup.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)})
    80  	require.NoError(t, err)
    81  	want := []key.Destination{
    82  		key.DestinationKeyspaceIDs([][]byte{
    83  			[]byte("1"),
    84  			[]byte("2"),
    85  		}),
    86  		key.DestinationKeyspaceIDs([][]byte{
    87  			[]byte("1"),
    88  			[]byte("2"),
    89  		}),
    90  	}
    91  	if !reflect.DeepEqual(got, want) {
    92  		t.Errorf("Map(): %#v, want %+v", got, want)
    93  	}
    94  	vc.verifyLog(t, []string{
    95  		"ExecutePre select fromc1, toc from t where fromc1 in ::fromc1 [{fromc1 }] false",
    96  	})
    97  
    98  	// Test query fail.
    99  	vc.AddResult(nil, fmt.Errorf("execute failed"))
   100  	_, err = lookup.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)})
   101  	wantErr := "lookup.Map: execute failed"
   102  	if err == nil || err.Error() != wantErr {
   103  		t.Errorf("lookup(query fail) err: %v, want %s", err, wantErr)
   104  	}
   105  }
   106  
   107  func TestConsistentLookupMapWriteOnly(t *testing.T) {
   108  	lookup := createConsistentLookup(t, "consistent_lookup", true)
   109  
   110  	got, err := lookup.Map(context.Background(), nil, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)})
   111  	require.NoError(t, err)
   112  	want := []key.Destination{
   113  		key.DestinationKeyRange{
   114  			KeyRange: &topodatapb.KeyRange{},
   115  		},
   116  		key.DestinationKeyRange{
   117  			KeyRange: &topodatapb.KeyRange{},
   118  		},
   119  	}
   120  	if !reflect.DeepEqual(got, want) {
   121  		t.Errorf("Map(): %#v, want %+v", got, want)
   122  	}
   123  }
   124  
   125  func TestConsistentLookupUniqueMap(t *testing.T) {
   126  	lookup := createConsistentLookup(t, "consistent_lookup_unique", false)
   127  	vc := &loggingVCursor{}
   128  	vc.AddResult(makeTestResultLookup([]int{0, 1}), nil)
   129  
   130  	got, err := lookup.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)})
   131  	require.NoError(t, err)
   132  	want := []key.Destination{
   133  		key.DestinationNone{},
   134  		key.DestinationKeyspaceID([]byte("1")),
   135  	}
   136  	if !reflect.DeepEqual(got, want) {
   137  		t.Errorf("Map(): %#v, want %+v", got, want)
   138  	}
   139  	vc.verifyLog(t, []string{
   140  		"ExecutePre select fromc1, toc from t where fromc1 in ::fromc1 [{fromc1 }] false",
   141  	})
   142  
   143  	// More than one result is invalid
   144  	vc.AddResult(makeTestResultLookup([]int{2}), nil)
   145  	_, err = lookup.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)})
   146  	wanterr := "Lookup.Map: unexpected multiple results from vindex t: INT64(1)"
   147  	if err == nil || err.Error() != wanterr {
   148  		t.Errorf("lookup(query fail) err: %v, want %s", err, wanterr)
   149  	}
   150  }
   151  
   152  func TestConsistentLookupUniqueMapWriteOnly(t *testing.T) {
   153  	lookup := createConsistentLookup(t, "consistent_lookup_unique", true)
   154  
   155  	got, err := lookup.Map(context.Background(), nil, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)})
   156  	require.NoError(t, err)
   157  	want := []key.Destination{
   158  		key.DestinationKeyRange{
   159  			KeyRange: &topodatapb.KeyRange{},
   160  		},
   161  		key.DestinationKeyRange{
   162  			KeyRange: &topodatapb.KeyRange{},
   163  		},
   164  	}
   165  	if !reflect.DeepEqual(got, want) {
   166  		t.Errorf("Map(): %#v, want %+v", got, want)
   167  	}
   168  }
   169  
   170  func TestConsistentLookupMapAbsent(t *testing.T) {
   171  	lookup := createConsistentLookup(t, "consistent_lookup", false)
   172  	vc := &loggingVCursor{}
   173  	vc.AddResult(makeTestResultLookup([]int{0, 0}), nil)
   174  
   175  	got, err := lookup.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)})
   176  	require.NoError(t, err)
   177  	want := []key.Destination{
   178  		key.DestinationNone{},
   179  		key.DestinationNone{},
   180  	}
   181  	if !reflect.DeepEqual(got, want) {
   182  		t.Errorf("Map(): %#v, want %+v", got, want)
   183  	}
   184  	vc.verifyLog(t, []string{
   185  		"ExecutePre select fromc1, toc from t where fromc1 in ::fromc1 [{fromc1 }] false",
   186  	})
   187  }
   188  
   189  func TestConsistentLookupVerify(t *testing.T) {
   190  	lookup := createConsistentLookup(t, "consistent_lookup", false)
   191  	vc := &loggingVCursor{}
   192  	vc.AddResult(makeTestResult(1), nil)
   193  	vc.AddResult(makeTestResult(1), nil)
   194  
   195  	_, err := lookup.Verify(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte("test1"), []byte("test2")})
   196  	require.NoError(t, err)
   197  	vc.verifyLog(t, []string{
   198  		"ExecutePre select fromc1 from t where fromc1 = :fromc1 and toc = :toc [{fromc1 1} {toc test1}] false",
   199  		"ExecutePre select fromc1 from t where fromc1 = :fromc1 and toc = :toc [{fromc1 2} {toc test2}] false",
   200  	})
   201  
   202  	// Test query fail.
   203  	vc.AddResult(nil, fmt.Errorf("execute failed"))
   204  	_, err = lookup.Verify(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")})
   205  	want := "lookup.Verify: execute failed"
   206  	if err == nil || err.Error() != want {
   207  		t.Errorf("lookup(query fail) err: %v, want %s", err, want)
   208  	}
   209  
   210  	// Test write_only.
   211  	lookup = createConsistentLookup(t, "consistent_lookup", true)
   212  	got, err := lookup.Verify(context.Background(), nil, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte(""), []byte("")})
   213  	require.NoError(t, err)
   214  	wantBools := []bool{true, true}
   215  	if !reflect.DeepEqual(got, wantBools) {
   216  		t.Errorf("lookup.Verify(writeOnly): %v, want %v", got, wantBools)
   217  	}
   218  }
   219  
   220  func TestConsistentLookupCreateSimple(t *testing.T) {
   221  	lookup := createConsistentLookup(t, "consistent_lookup", false)
   222  	vc := &loggingVCursor{}
   223  	vc.AddResult(&sqltypes.Result{}, nil)
   224  
   225  	if err := lookup.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{
   226  		sqltypes.NewInt64(1),
   227  		sqltypes.NewInt64(2),
   228  	}, {
   229  		sqltypes.NewInt64(3),
   230  		sqltypes.NewInt64(4),
   231  	}}, [][]byte{[]byte("test1"), []byte("test2")}, false); err != nil {
   232  		t.Error(err)
   233  	}
   234  	vc.verifyLog(t, []string{
   235  		"ExecutePre insert into t(fromc1, fromc2, toc) values(:fromc1_0, :fromc2_0, :toc_0), (:fromc1_1, :fromc2_1, :toc_1) [{fromc1_0 1} {fromc1_1 3} {fromc2_0 2} {fromc2_1 4} {toc_0 test1} {toc_1 test2}] true",
   236  	})
   237  }
   238  
   239  func TestConsistentLookupCreateThenRecreate(t *testing.T) {
   240  	lookup := createConsistentLookup(t, "consistent_lookup", false)
   241  	vc := &loggingVCursor{}
   242  	vc.AddResult(nil, mysql.NewSQLError(mysql.ERDupEntry, mysql.SSConstraintViolation, "Duplicate entry"))
   243  	vc.AddResult(&sqltypes.Result{}, nil)
   244  	vc.AddResult(&sqltypes.Result{}, nil)
   245  
   246  	if err := lookup.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{
   247  		sqltypes.NewInt64(1),
   248  		sqltypes.NewInt64(2),
   249  	}}, [][]byte{[]byte("test1")}, false); err != nil {
   250  		t.Error(err)
   251  	}
   252  	vc.verifyLog(t, []string{
   253  		"ExecutePre insert into t(fromc1, fromc2, toc) values(:fromc1_0, :fromc2_0, :toc_0) [{fromc1_0 1} {fromc2_0 2} {toc_0 test1}] true",
   254  		"ExecutePre select toc from t where fromc1 = :fromc1 and fromc2 = :fromc2 for update [{fromc1 1} {fromc2 2} {toc test1}] false",
   255  		"ExecutePre insert into t(fromc1, fromc2, toc) values(:fromc1, :fromc2, :toc) [{fromc1 1} {fromc2 2} {toc test1}] true",
   256  	})
   257  }
   258  
   259  func TestConsistentLookupCreateThenUpdate(t *testing.T) {
   260  	lookup := createConsistentLookup(t, "consistent_lookup", false)
   261  	vc := &loggingVCursor{}
   262  	vc.AddResult(nil, vterrors.New(vtrpcpb.Code_ALREADY_EXISTS, "(errno 1062) (sqlstate 23000) Duplicate entry"))
   263  	vc.AddResult(makeTestResult(1), nil)
   264  	vc.AddResult(&sqltypes.Result{}, nil)
   265  	vc.AddResult(&sqltypes.Result{}, nil)
   266  
   267  	if err := lookup.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{
   268  		sqltypes.NewInt64(1),
   269  		sqltypes.NewInt64(2),
   270  	}}, [][]byte{[]byte("test1")}, false); err != nil {
   271  		t.Error(err)
   272  	}
   273  	vc.verifyLog(t, []string{
   274  		"ExecutePre insert into t(fromc1, fromc2, toc) values(:fromc1_0, :fromc2_0, :toc_0) [{fromc1_0 1} {fromc2_0 2} {toc_0 test1}] true",
   275  		"ExecutePre select toc from t where fromc1 = :fromc1 and fromc2 = :fromc2 for update [{fromc1 1} {fromc2 2} {toc test1}] false",
   276  		"ExecuteKeyspaceID select fc1 from `dot.t1` where fc1 = :fromc1 and fc2 = :fromc2 lock in share mode [{fromc1 1} {fromc2 2} {toc test1}] false",
   277  		"ExecutePre update t set toc=:toc where fromc1 = :fromc1 and fromc2 = :fromc2 [{fromc1 1} {fromc2 2} {toc test1}] true",
   278  	})
   279  }
   280  
   281  func TestConsistentLookupCreateThenSkipUpdate(t *testing.T) {
   282  	lookup := createConsistentLookup(t, "consistent_lookup", false)
   283  	vc := &loggingVCursor{}
   284  	vc.AddResult(nil, vterrors.New(vtrpcpb.Code_ALREADY_EXISTS, "(errno 1062) (sqlstate 23000) Duplicate entry"))
   285  	vc.AddResult(makeTestResult(1), nil)
   286  	vc.AddResult(&sqltypes.Result{}, nil)
   287  	vc.AddResult(&sqltypes.Result{}, nil)
   288  
   289  	if err := lookup.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{
   290  		sqltypes.NewInt64(1),
   291  		sqltypes.NewInt64(2),
   292  	}}, [][]byte{[]byte("1")}, false); err != nil {
   293  		t.Error(err)
   294  	}
   295  	vc.verifyLog(t, []string{
   296  		"ExecutePre insert into t(fromc1, fromc2, toc) values(:fromc1_0, :fromc2_0, :toc_0) [{fromc1_0 1} {fromc2_0 2} {toc_0 1}] true",
   297  		"ExecutePre select toc from t where fromc1 = :fromc1 and fromc2 = :fromc2 for update [{fromc1 1} {fromc2 2} {toc 1}] false",
   298  		"ExecuteKeyspaceID select fc1 from `dot.t1` where fc1 = :fromc1 and fc2 = :fromc2 lock in share mode [{fromc1 1} {fromc2 2} {toc 1}] false",
   299  	})
   300  }
   301  
   302  func TestConsistentLookupCreateThenDupkey(t *testing.T) {
   303  	lookup := createConsistentLookup(t, "consistent_lookup", false)
   304  	vc := &loggingVCursor{}
   305  	vc.AddResult(nil, vterrors.New(vtrpcpb.Code_ALREADY_EXISTS, "(errno 1062) (sqlstate 23000) Duplicate entry, pass mysql error as it is"))
   306  	vc.AddResult(makeTestResult(1), nil)
   307  	vc.AddResult(makeTestResult(1), nil)
   308  	vc.AddResult(&sqltypes.Result{}, nil)
   309  
   310  	err := lookup.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{
   311  		sqltypes.NewInt64(1),
   312  		sqltypes.NewInt64(2),
   313  	}}, [][]byte{[]byte("test1")}, false)
   314  	require.Error(t, err)
   315  	assert.Contains(t, err.Error(), "Duplicate entry, pass mysql error as it is")
   316  	vc.verifyLog(t, []string{
   317  		"ExecutePre insert into t(fromc1, fromc2, toc) values(:fromc1_0, :fromc2_0, :toc_0) [{fromc1_0 1} {fromc2_0 2} {toc_0 test1}] true",
   318  		"ExecutePre select toc from t where fromc1 = :fromc1 and fromc2 = :fromc2 for update [{fromc1 1} {fromc2 2} {toc test1}] false",
   319  		"ExecuteKeyspaceID select fc1 from `dot.t1` where fc1 = :fromc1 and fc2 = :fromc2 lock in share mode [{fromc1 1} {fromc2 2} {toc test1}] false",
   320  	})
   321  }
   322  
   323  func TestConsistentLookupCreateNonDupError(t *testing.T) {
   324  	lookup := createConsistentLookup(t, "consistent_lookup", false)
   325  	vc := &loggingVCursor{}
   326  	vc.AddResult(nil, errors.New("general error"))
   327  
   328  	err := lookup.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{
   329  		sqltypes.NewInt64(1),
   330  		sqltypes.NewInt64(2),
   331  	}}, [][]byte{[]byte("test1")}, false)
   332  	want := "general error"
   333  	if err == nil || !strings.Contains(err.Error(), want) {
   334  		t.Errorf("lookup(query fail) err: %v, must contain %s", err, want)
   335  	}
   336  	vc.verifyLog(t, []string{
   337  		"ExecutePre insert into t(fromc1, fromc2, toc) values(:fromc1_0, :fromc2_0, :toc_0) [{fromc1_0 1} {fromc2_0 2} {toc_0 test1}] true",
   338  	})
   339  }
   340  
   341  func TestConsistentLookupCreateThenBadRows(t *testing.T) {
   342  	lookup := createConsistentLookup(t, "consistent_lookup", false)
   343  	vc := &loggingVCursor{}
   344  	vc.AddResult(nil, vterrors.New(vtrpcpb.Code_ALREADY_EXISTS, "(errno 1062) (sqlstate 23000) Duplicate entry"))
   345  	vc.AddResult(makeTestResult(2), nil)
   346  
   347  	err := lookup.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{
   348  		sqltypes.NewInt64(1),
   349  		sqltypes.NewInt64(2),
   350  	}}, [][]byte{[]byte("test1")}, false)
   351  	want := "unexpected rows"
   352  	if err == nil || !strings.Contains(err.Error(), want) {
   353  		t.Errorf("lookup(query fail) err: %v, must contain %s", err, want)
   354  	}
   355  	vc.verifyLog(t, []string{
   356  		"ExecutePre insert into t(fromc1, fromc2, toc) values(:fromc1_0, :fromc2_0, :toc_0) [{fromc1_0 1} {fromc2_0 2} {toc_0 test1}] true",
   357  		"ExecutePre select toc from t where fromc1 = :fromc1 and fromc2 = :fromc2 for update [{fromc1 1} {fromc2 2} {toc test1}] false",
   358  	})
   359  }
   360  
   361  func TestConsistentLookupDelete(t *testing.T) {
   362  	lookup := createConsistentLookup(t, "consistent_lookup", false)
   363  	vc := &loggingVCursor{}
   364  	vc.AddResult(&sqltypes.Result{}, nil)
   365  
   366  	if err := lookup.(Lookup).Delete(context.Background(), vc, [][]sqltypes.Value{{
   367  		sqltypes.NewInt64(1),
   368  		sqltypes.NewInt64(2),
   369  	}}, []byte("test")); err != nil {
   370  		t.Error(err)
   371  	}
   372  	vc.verifyLog(t, []string{
   373  		"ExecutePost delete from t where fromc1 = :fromc1 and fromc2 = :fromc2 and toc = :toc [{fromc1 1} {fromc2 2} {toc test}] true",
   374  	})
   375  }
   376  
   377  func TestConsistentLookupUpdate(t *testing.T) {
   378  	lookup := createConsistentLookup(t, "consistent_lookup", false)
   379  	vc := &loggingVCursor{}
   380  	vc.AddResult(&sqltypes.Result{}, nil)
   381  	vc.AddResult(&sqltypes.Result{}, nil)
   382  
   383  	if err := lookup.(Lookup).Update(context.Background(), vc, []sqltypes.Value{
   384  		sqltypes.NewInt64(1),
   385  		sqltypes.NewInt64(2),
   386  	}, []byte("test"), []sqltypes.Value{
   387  		sqltypes.NewInt64(3),
   388  		sqltypes.NewInt64(4),
   389  	}); err != nil {
   390  		t.Error(err)
   391  	}
   392  	vc.verifyLog(t, []string{
   393  		"ExecutePost delete from t where fromc1 = :fromc1 and fromc2 = :fromc2 and toc = :toc [{fromc1 1} {fromc2 2} {toc test}] true",
   394  		"ExecutePre insert into t(fromc1, fromc2, toc) values(:fromc1_0, :fromc2_0, :toc_0) [{fromc1_0 3} {fromc2_0 4} {toc_0 test}] true",
   395  	})
   396  }
   397  
   398  func TestConsistentLookupNoUpdate(t *testing.T) {
   399  	lookup := createConsistentLookup(t, "consistent_lookup", false)
   400  	vc := &loggingVCursor{}
   401  	vc.AddResult(&sqltypes.Result{}, nil)
   402  	vc.AddResult(&sqltypes.Result{}, nil)
   403  
   404  	if err := lookup.(Lookup).Update(context.Background(), vc, []sqltypes.Value{
   405  		sqltypes.NewInt64(1),
   406  		sqltypes.NewInt64(2),
   407  	}, []byte("test"), []sqltypes.Value{
   408  		sqltypes.NewInt64(1),
   409  		sqltypes.NewInt64(2),
   410  	}); err != nil {
   411  		t.Error(err)
   412  	}
   413  	vc.verifyLog(t, []string{})
   414  }
   415  
   416  func TestConsistentLookupUpdateBecauseUncomparableTypes(t *testing.T) {
   417  	lookup := createConsistentLookup(t, "consistent_lookup", false)
   418  	vc := &loggingVCursor{}
   419  
   420  	type test struct {
   421  		typ querypb.Type
   422  		val string
   423  	}
   424  
   425  	tests := []test{
   426  		{querypb.Type_TEXT, "some string"},
   427  		{querypb.Type_VARCHAR, "some string"},
   428  		{querypb.Type_CHAR, "some string"},
   429  		{querypb.Type_GEOMETRY, "some string"},
   430  	}
   431  
   432  	for _, val := range tests {
   433  		t.Run(val.typ.String(), func(t *testing.T) {
   434  			vc.AddResult(&sqltypes.Result{}, nil)
   435  			vc.AddResult(&sqltypes.Result{}, nil)
   436  			literal, err := sqltypes.NewValue(val.typ, []byte(val.val))
   437  			require.NoError(t, err)
   438  
   439  			err = lookup.(Lookup).Update(context.Background(), vc, []sqltypes.Value{literal, literal}, []byte("test"), []sqltypes.Value{literal, literal})
   440  			require.NoError(t, err)
   441  			require.NotEmpty(t, vc.log)
   442  			vc.log = nil
   443  		})
   444  	}
   445  }
   446  
   447  func createConsistentLookup(t *testing.T, name string, writeOnly bool) SingleColumn {
   448  	t.Helper()
   449  	write := "false"
   450  	if writeOnly {
   451  		write = "true"
   452  	}
   453  	l, err := CreateVindex(name, name, map[string]string{
   454  		"table":      "t",
   455  		"from":       "fromc1,fromc2",
   456  		"to":         "toc",
   457  		"write_only": write,
   458  	})
   459  	if err != nil {
   460  		t.Fatal(err)
   461  	}
   462  	cols := []sqlparser.IdentifierCI{
   463  		sqlparser.NewIdentifierCI("fc1"),
   464  		sqlparser.NewIdentifierCI("fc2"),
   465  	}
   466  	if err := l.(WantOwnerInfo).SetOwnerInfo("ks", "dot.t1", cols); err != nil {
   467  		t.Fatal(err)
   468  	}
   469  	return l.(SingleColumn)
   470  }
   471  
   472  var _ VCursor = (*loggingVCursor)(nil)
   473  
   474  type loggingVCursor struct {
   475  	results []*sqltypes.Result
   476  	errors  []error
   477  	index   int
   478  	log     []string
   479  }
   480  
   481  func (vc *loggingVCursor) LookupRowLockShardSession() vtgatepb.CommitOrder {
   482  	return vtgatepb.CommitOrder_PRE
   483  }
   484  
   485  func (vc *loggingVCursor) InTransactionAndIsDML() bool {
   486  	return false
   487  }
   488  
   489  type bv struct {
   490  	Name string
   491  	Bv   string
   492  }
   493  
   494  func (vc *loggingVCursor) AddResult(qr *sqltypes.Result, err error) {
   495  	vc.results = append(vc.results, qr)
   496  	vc.errors = append(vc.errors, err)
   497  }
   498  
   499  func (vc *loggingVCursor) Execute(ctx context.Context, method string, query string, bindvars map[string]*querypb.BindVariable, rollbackOnError bool, co vtgatepb.CommitOrder) (*sqltypes.Result, error) {
   500  	name := "Unknown"
   501  	switch co {
   502  	case vtgatepb.CommitOrder_NORMAL:
   503  		name = "Execute"
   504  	case vtgatepb.CommitOrder_PRE:
   505  		name = "ExecutePre"
   506  	case vtgatepb.CommitOrder_POST:
   507  		name = "ExecutePost"
   508  	case vtgatepb.CommitOrder_AUTOCOMMIT:
   509  		name = "ExecuteAutocommit"
   510  	}
   511  	return vc.execute(name, query, bindvars, rollbackOnError)
   512  }
   513  
   514  func (vc *loggingVCursor) ExecuteKeyspaceID(ctx context.Context, keyspace string, ksid []byte, query string, bindVars map[string]*querypb.BindVariable, rollbackOnError, autocommit bool) (*sqltypes.Result, error) {
   515  	return vc.execute("ExecuteKeyspaceID", query, bindVars, rollbackOnError)
   516  }
   517  
   518  func (vc *loggingVCursor) execute(method string, query string, bindvars map[string]*querypb.BindVariable, rollbackOnError bool) (*sqltypes.Result, error) {
   519  	if vc.index >= len(vc.results) {
   520  		return nil, fmt.Errorf("ran out of results to return: %s", query)
   521  	}
   522  	bvl := make([]bv, 0, len(bindvars))
   523  	for k, v := range bindvars {
   524  		bvl = append(bvl, bv{Name: k, Bv: string(v.Value)})
   525  	}
   526  	sort.Slice(bvl, func(i, j int) bool { return bvl[i].Name < bvl[j].Name })
   527  	vc.log = append(vc.log, fmt.Sprintf("%s %s %v %v", method, query, bvl, rollbackOnError))
   528  	idx := vc.index
   529  	vc.index++
   530  	if vc.errors[idx] != nil {
   531  		return nil, vc.errors[idx]
   532  	}
   533  	return vc.results[idx], nil
   534  }
   535  
   536  func (vc *loggingVCursor) verifyLog(t *testing.T, want []string) {
   537  	t.Helper()
   538  	for i, got := range vc.log {
   539  		if i >= len(want) {
   540  			t.Fatalf("index exceeded: %v", vc.log[i:])
   541  		}
   542  		if got != want[i] {
   543  			t.Errorf("log(%d):\n%q, want\n%q", i, got, want[i])
   544  		}
   545  	}
   546  	if len(want) > len(vc.log) {
   547  		t.Errorf("expecting queries: %v", want[len(vc.log):])
   548  	}
   549  }
   550  
   551  // create lookup result with one to one mapping
   552  func makeTestResult(numRows int) *sqltypes.Result {
   553  	result := &sqltypes.Result{
   554  		Fields:       sqltypes.MakeTestFields("id|keyspace_id", "bigint|varbinary"),
   555  		RowsAffected: uint64(numRows),
   556  	}
   557  	for i := 0; i < numRows; i++ {
   558  		result.Rows = append(result.Rows, []sqltypes.Value{
   559  			sqltypes.NewInt64(int64(i + 1)),
   560  			sqltypes.NewVarBinary(strconv.Itoa(i + 1)),
   561  		})
   562  	}
   563  	return result
   564  }
   565  
   566  // create lookup result with many to many mapping
   567  func makeTestResultLookup(numRows []int) *sqltypes.Result {
   568  	total := 0
   569  	for _, t := range numRows {
   570  		total += t
   571  	}
   572  	result := &sqltypes.Result{
   573  		Fields:       sqltypes.MakeTestFields("id|keyspace_id", "bigint|varbinary"),
   574  		RowsAffected: uint64(total),
   575  	}
   576  	for i, row := range numRows {
   577  		for j := 0; j < row; j++ {
   578  			result.Rows = append(result.Rows, []sqltypes.Value{
   579  				sqltypes.NewInt64(int64(i + 1)),
   580  				sqltypes.NewVarBinary(strconv.Itoa(j + 1)),
   581  			})
   582  		}
   583  	}
   584  	return result
   585  }