vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/fake_vcursor_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 engine
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"fmt"
    23  	"reflect"
    24  	"sort"
    25  	"strings"
    26  	"sync"
    27  	"testing"
    28  
    29  	"vitess.io/vitess/go/mysql/collations"
    30  	"vitess.io/vitess/go/sqltypes"
    31  	"vitess.io/vitess/go/test/utils"
    32  	"vitess.io/vitess/go/vt/key"
    33  	"vitess.io/vitess/go/vt/sqlparser"
    34  	"vitess.io/vitess/go/vt/srvtopo"
    35  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    36  
    37  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    38  	querypb "vitess.io/vitess/go/vt/proto/query"
    39  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    40  	vtgatepb "vitess.io/vitess/go/vt/proto/vtgate"
    41  )
    42  
    43  var testMaxMemoryRows = 100
    44  var testIgnoreMaxMemoryRows = false
    45  
    46  var _ VCursor = (*noopVCursor)(nil)
    47  var _ SessionActions = (*noopVCursor)(nil)
    48  
    49  // noopVCursor is used to build other vcursors.
    50  type noopVCursor struct {
    51  }
    52  
    53  func (t *noopVCursor) InTransaction() bool {
    54  	return false
    55  }
    56  
    57  func (t *noopVCursor) SetCommitOrder(co vtgatepb.CommitOrder) {
    58  	//TODO implement me
    59  	panic("implement me")
    60  }
    61  
    62  func (t *noopVCursor) StreamExecutePrimitiveStandalone(ctx context.Context, primitive Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(result *sqltypes.Result) error) error {
    63  	return primitive.TryStreamExecute(ctx, t, bindVars, wantfields, callback)
    64  }
    65  
    66  func (t *noopVCursor) AnyAdvisoryLockTaken() bool {
    67  	// TODO implement me
    68  	panic("implement me")
    69  }
    70  
    71  func (t *noopVCursor) AddAdvisoryLock(name string) {
    72  	// TODO implement me
    73  	panic("implement me")
    74  }
    75  
    76  func (t *noopVCursor) RemoveAdvisoryLock(name string) {
    77  	// TODO implement me
    78  	panic("implement me")
    79  }
    80  
    81  func (t *noopVCursor) ReleaseLock(context.Context) error {
    82  	// TODO implement me
    83  	panic("implement me")
    84  }
    85  
    86  func (t *noopVCursor) SetExec(ctx context.Context, name string, value string) error {
    87  	panic("implement me")
    88  }
    89  
    90  func (t *noopVCursor) ShowExec(ctx context.Context, command sqlparser.ShowCommandType, filter *sqlparser.ShowFilter) (*sqltypes.Result, error) {
    91  	panic("implement me")
    92  }
    93  
    94  // SetContextWithValue implements VCursor interface.
    95  func (t *noopVCursor) SetContextWithValue(key, value interface{}) func() {
    96  	return func() {}
    97  }
    98  
    99  // ConnCollation implements VCursor
   100  func (t *noopVCursor) ConnCollation() collations.ID {
   101  	return collations.CollationUtf8mb4ID
   102  }
   103  
   104  func (t *noopVCursor) ExecutePrimitive(ctx context.Context, primitive Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) {
   105  	return primitive.TryExecute(ctx, t, bindVars, wantfields)
   106  }
   107  
   108  func (t *noopVCursor) ExecutePrimitiveStandalone(ctx context.Context, primitive Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) {
   109  	return primitive.TryExecute(ctx, t, bindVars, wantfields)
   110  }
   111  
   112  func (t *noopVCursor) StreamExecutePrimitive(ctx context.Context, primitive Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error {
   113  	return primitive.TryStreamExecute(ctx, t, bindVars, wantfields, callback)
   114  }
   115  
   116  func (t *noopVCursor) HasSystemVariables() bool {
   117  	panic("implement me")
   118  }
   119  
   120  func (t *noopVCursor) GetSystemVariables(func(k string, v string)) {
   121  	panic("implement me")
   122  }
   123  
   124  func (t *noopVCursor) GetWarnings() []*querypb.QueryWarning {
   125  	panic("implement me")
   126  }
   127  
   128  func (t *noopVCursor) VStream(ctx context.Context, rss []*srvtopo.ResolvedShard, filter *binlogdatapb.Filter, gtid string, callback func(evs []*binlogdatapb.VEvent) error) error {
   129  	panic("implement me")
   130  }
   131  
   132  func (t *noopVCursor) MessageStream(ctx context.Context, rss []*srvtopo.ResolvedShard, tableName string, callback func(*sqltypes.Result) error) error {
   133  	panic("implement me")
   134  }
   135  
   136  func (t *noopVCursor) KeyspaceAvailable(ks string) bool {
   137  	panic("implement me")
   138  }
   139  
   140  func (t *noopVCursor) SetDDLStrategy(strategy string) {
   141  	panic("implement me")
   142  }
   143  
   144  func (t *noopVCursor) GetDDLStrategy() string {
   145  	panic("implement me")
   146  }
   147  
   148  func (t *noopVCursor) GetSessionUUID() string {
   149  	panic("implement me")
   150  }
   151  
   152  func (t *noopVCursor) SetReadAfterWriteGTID(s string) {
   153  	panic("implement me")
   154  }
   155  
   156  func (t *noopVCursor) SetSessionEnableSystemSettings(ctx context.Context, allow bool) error {
   157  	panic("implement me")
   158  }
   159  
   160  func (t *noopVCursor) GetSessionEnableSystemSettings() bool {
   161  	panic("implement me")
   162  }
   163  
   164  func (t *noopVCursor) CanUseSetVar() bool {
   165  	panic("implement me")
   166  }
   167  
   168  func (t *noopVCursor) SetReadAfterWriteTimeout(f float64) {
   169  	panic("implement me")
   170  }
   171  
   172  func (t *noopVCursor) SetSessionTrackGTIDs(b bool) {
   173  	panic("implement me")
   174  }
   175  
   176  func (t *noopVCursor) HasCreatedTempTable() {
   177  	panic("implement me")
   178  }
   179  
   180  func (t *noopVCursor) LookupRowLockShardSession() vtgatepb.CommitOrder {
   181  	panic("implement me")
   182  }
   183  
   184  func (t *noopVCursor) SetFoundRows(u uint64) {
   185  	panic("implement me")
   186  }
   187  
   188  func (t *noopVCursor) InTransactionAndIsDML() bool {
   189  	panic("implement me")
   190  }
   191  
   192  func (t *noopVCursor) FindRoutedTable(sqlparser.TableName) (*vindexes.Table, error) {
   193  	panic("implement me")
   194  }
   195  
   196  func (t *noopVCursor) ExecuteLock(ctx context.Context, rs *srvtopo.ResolvedShard, query *querypb.BoundQuery, lockFuncType sqlparser.LockingFuncType) (*sqltypes.Result, error) {
   197  	panic("implement me")
   198  }
   199  
   200  func (t *noopVCursor) NeedsReservedConn() {
   201  }
   202  
   203  func (t *noopVCursor) SetUDV(key string, value any) error {
   204  	panic("implement me")
   205  }
   206  
   207  func (t *noopVCursor) SetSysVar(name string, expr string) {
   208  	// panic("implement me")
   209  }
   210  
   211  func (t *noopVCursor) InReservedConn() bool {
   212  	panic("implement me")
   213  }
   214  
   215  func (t *noopVCursor) ShardSession() []*srvtopo.ResolvedShard {
   216  	panic("implement me")
   217  }
   218  
   219  func (t *noopVCursor) ExecuteVSchema(ctx context.Context, keyspace string, vschemaDDL *sqlparser.AlterVschema) error {
   220  	panic("implement me")
   221  }
   222  
   223  func (t *noopVCursor) Session() SessionActions {
   224  	return t
   225  }
   226  
   227  func (t *noopVCursor) SetAutocommit(context.Context, bool) error {
   228  	panic("implement me")
   229  }
   230  
   231  func (t *noopVCursor) SetClientFoundRows(context.Context, bool) error {
   232  	panic("implement me")
   233  }
   234  
   235  func (t *noopVCursor) SetQueryTimeout(maxExecutionTime int64) {
   236  }
   237  
   238  func (t *noopVCursor) GetQueryTimeout(queryTimeoutFromComments int) int {
   239  	return queryTimeoutFromComments
   240  }
   241  
   242  func (t *noopVCursor) SetSkipQueryPlanCache(context.Context, bool) error {
   243  	panic("implement me")
   244  }
   245  
   246  func (t *noopVCursor) SetSQLSelectLimit(int64) error {
   247  	panic("implement me")
   248  }
   249  
   250  func (t *noopVCursor) SetTransactionMode(vtgatepb.TransactionMode) {
   251  	panic("implement me")
   252  }
   253  
   254  func (t *noopVCursor) SetWorkload(querypb.ExecuteOptions_Workload) {
   255  	panic("implement me")
   256  }
   257  
   258  func (t *noopVCursor) SetPlannerVersion(querypb.ExecuteOptions_PlannerVersion) {
   259  	panic("implement me")
   260  }
   261  
   262  func (t *noopVCursor) SetConsolidator(querypb.ExecuteOptions_Consolidator) {
   263  	panic("implement me")
   264  }
   265  
   266  func (t *noopVCursor) SetTarget(string) error {
   267  	panic("implement me")
   268  }
   269  
   270  func (t *noopVCursor) MaxMemoryRows() int {
   271  	return testMaxMemoryRows
   272  }
   273  
   274  func (t *noopVCursor) ExceedsMaxMemoryRows(numRows int) bool {
   275  	return !testIgnoreMaxMemoryRows && numRows > testMaxMemoryRows
   276  }
   277  
   278  func (t *noopVCursor) GetKeyspace() string {
   279  	return ""
   280  }
   281  
   282  func (t *noopVCursor) RecordWarning(warning *querypb.QueryWarning) {
   283  }
   284  
   285  func (t *noopVCursor) Execute(ctx context.Context, method string, query string, bindvars map[string]*querypb.BindVariable, rollbackOnError bool, co vtgatepb.CommitOrder) (*sqltypes.Result, error) {
   286  	panic("unimplemented")
   287  }
   288  
   289  func (t *noopVCursor) ExecuteMultiShard(ctx context.Context, primitive Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, rollbackOnError, canAutocommit bool) (*sqltypes.Result, []error) {
   290  	panic("unimplemented")
   291  }
   292  
   293  func (t *noopVCursor) AutocommitApproval() bool {
   294  	panic("unimplemented")
   295  }
   296  
   297  func (t *noopVCursor) ExecuteStandalone(ctx context.Context, primitive Primitive, query string, bindvars map[string]*querypb.BindVariable, rs *srvtopo.ResolvedShard) (*sqltypes.Result, error) {
   298  	panic("unimplemented")
   299  }
   300  
   301  func (t *noopVCursor) StreamExecuteMulti(ctx context.Context, primitive Primitive, query string, rss []*srvtopo.ResolvedShard, bindVars []map[string]*querypb.BindVariable, rollbackOnError bool, autocommit bool, callback func(reply *sqltypes.Result) error) []error {
   302  	panic("unimplemented")
   303  }
   304  
   305  func (t *noopVCursor) ExecuteKeyspaceID(ctx context.Context, keyspace string, ksid []byte, query string, bindVars map[string]*querypb.BindVariable, rollbackOnError, autocommit bool) (*sqltypes.Result, error) {
   306  	panic("unimplemented")
   307  }
   308  
   309  func (t *noopVCursor) ResolveDestinations(ctx context.Context, keyspace string, ids []*querypb.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][]*querypb.Value, error) {
   310  	return nil, nil, nil
   311  }
   312  
   313  func (t *noopVCursor) ResolveDestinationsMultiCol(ctx context.Context, keyspace string, ids [][]sqltypes.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][][]sqltypes.Value, error) {
   314  	panic("unimplemented")
   315  }
   316  
   317  func (t *noopVCursor) GetDBDDLPluginName() string {
   318  	panic("unimplemented")
   319  }
   320  
   321  var _ VCursor = (*loggingVCursor)(nil)
   322  var _ SessionActions = (*loggingVCursor)(nil)
   323  
   324  // loggingVCursor logs requests and allows you to verify
   325  // that the correct requests were made.
   326  type loggingVCursor struct {
   327  	noopVCursor
   328  
   329  	shards          []string
   330  	shardForKsid    []string
   331  	curShardForKsid int
   332  	shardErr        error
   333  
   334  	results   []*sqltypes.Result
   335  	curResult int
   336  	resultErr error
   337  
   338  	warnings []*querypb.QueryWarning
   339  
   340  	// Optional errors that can be returned from nextResult() alongside the results for
   341  	// multi-shard queries
   342  	multiShardErrs []error
   343  
   344  	log []string
   345  	mu  sync.Mutex
   346  
   347  	resolvedTargetTabletType topodatapb.TabletType
   348  
   349  	tableRoutes     tableRoutes
   350  	dbDDLPlugin     string
   351  	ksAvailable     bool
   352  	inReservedConn  bool
   353  	systemVariables map[string]string
   354  	disableSetVar   bool
   355  
   356  	// map different shards to keyspaces in the test.
   357  	ksShardMap map[string][]string
   358  
   359  	shardSession []*srvtopo.ResolvedShard
   360  }
   361  
   362  type tableRoutes struct {
   363  	tbl *vindexes.Table
   364  }
   365  
   366  func (f *loggingVCursor) ExecutePrimitive(ctx context.Context, primitive Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) {
   367  	return primitive.TryExecute(ctx, f, bindVars, wantfields)
   368  }
   369  
   370  func (f *loggingVCursor) ExecutePrimitiveStandalone(ctx context.Context, primitive Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) {
   371  	return primitive.TryExecute(ctx, f, bindVars, wantfields)
   372  }
   373  
   374  func (f *loggingVCursor) StreamExecutePrimitive(ctx context.Context, primitive Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error {
   375  	return primitive.TryStreamExecute(ctx, f, bindVars, wantfields, callback)
   376  }
   377  
   378  func (f *loggingVCursor) StreamExecutePrimitiveStandalone(ctx context.Context, primitive Primitive, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(result *sqltypes.Result) error) error {
   379  	return primitive.TryStreamExecute(ctx, f, bindVars, wantfields, callback)
   380  }
   381  
   382  func (f *loggingVCursor) KeyspaceAvailable(ks string) bool {
   383  	return f.ksAvailable
   384  }
   385  
   386  func (f *loggingVCursor) HasSystemVariables() bool {
   387  	return len(f.systemVariables) > 0
   388  }
   389  
   390  func (f *loggingVCursor) GetSystemVariables(func(k string, v string)) {
   391  	panic("implement me")
   392  }
   393  
   394  func (f *loggingVCursor) SetFoundRows(u uint64) {
   395  	panic("implement me")
   396  }
   397  
   398  func (f *loggingVCursor) InTransactionAndIsDML() bool {
   399  	return false
   400  }
   401  
   402  func (f *loggingVCursor) LookupRowLockShardSession() vtgatepb.CommitOrder {
   403  	panic("implement me")
   404  }
   405  
   406  func (f *loggingVCursor) SetUDV(key string, value any) error {
   407  	f.log = append(f.log, fmt.Sprintf("UDV set with (%s,%v)", key, value))
   408  	return nil
   409  }
   410  
   411  func (f *loggingVCursor) SetSysVar(name string, expr string) {
   412  	f.log = append(f.log, fmt.Sprintf("SysVar set with (%s,%v)", name, expr))
   413  }
   414  
   415  func (f *loggingVCursor) NeedsReservedConn() {
   416  	f.log = append(f.log, "Needs Reserved Conn")
   417  	f.inReservedConn = true
   418  }
   419  
   420  func (f *loggingVCursor) InReservedConn() bool {
   421  	return f.inReservedConn
   422  }
   423  
   424  func (f *loggingVCursor) ShardSession() []*srvtopo.ResolvedShard {
   425  	return f.shardSession
   426  }
   427  
   428  func (f *loggingVCursor) ExecuteVSchema(context.Context, string, *sqlparser.AlterVschema) error {
   429  	panic("implement me")
   430  }
   431  
   432  func (f *loggingVCursor) Session() SessionActions {
   433  	return f
   434  }
   435  
   436  func (f *loggingVCursor) SetTarget(target string) error {
   437  	f.log = append(f.log, fmt.Sprintf("Target set to %s", target))
   438  	return nil
   439  }
   440  
   441  func (f *loggingVCursor) GetKeyspace() string {
   442  	return ""
   443  }
   444  
   445  func (f *loggingVCursor) RecordWarning(warning *querypb.QueryWarning) {
   446  	f.warnings = append(f.warnings, warning)
   447  }
   448  
   449  func (f *loggingVCursor) Execute(ctx context.Context, method string, query string, bindvars map[string]*querypb.BindVariable, rollbackOnError bool, co vtgatepb.CommitOrder) (*sqltypes.Result, error) {
   450  	name := "Unknown"
   451  	switch co {
   452  	case vtgatepb.CommitOrder_NORMAL:
   453  		name = "Execute"
   454  	case vtgatepb.CommitOrder_PRE:
   455  		name = "ExecutePre"
   456  	case vtgatepb.CommitOrder_POST:
   457  		name = "ExecutePost"
   458  	case vtgatepb.CommitOrder_AUTOCOMMIT:
   459  		name = "ExecuteAutocommit"
   460  	}
   461  	f.log = append(f.log, fmt.Sprintf("%s %s %v %v", name, query, printBindVars(bindvars), rollbackOnError))
   462  	return f.nextResult()
   463  }
   464  
   465  func (f *loggingVCursor) ExecuteMultiShard(ctx context.Context, primitive Primitive, rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery, rollbackOnError, canAutocommit bool) (*sqltypes.Result, []error) {
   466  	f.log = append(f.log, fmt.Sprintf("ExecuteMultiShard %v%v %v", printResolvedShardQueries(rss, queries), rollbackOnError, canAutocommit))
   467  	res, err := f.nextResult()
   468  	if err != nil {
   469  		return nil, []error{err}
   470  	}
   471  
   472  	return res, f.multiShardErrs
   473  }
   474  
   475  func (f *loggingVCursor) AutocommitApproval() bool {
   476  	return true
   477  }
   478  
   479  func (f *loggingVCursor) ExecuteStandalone(ctx context.Context, primitive Primitive, query string, bindvars map[string]*querypb.BindVariable, rs *srvtopo.ResolvedShard) (*sqltypes.Result, error) {
   480  	f.log = append(f.log, fmt.Sprintf("ExecuteStandalone %s %v %s %s", query, printBindVars(bindvars), rs.Target.Keyspace, rs.Target.Shard))
   481  	return f.nextResult()
   482  }
   483  
   484  func (f *loggingVCursor) StreamExecuteMulti(ctx context.Context, primitive Primitive, query string, rss []*srvtopo.ResolvedShard, bindVars []map[string]*querypb.BindVariable, rollbackOnError bool, autocommit bool, callback func(reply *sqltypes.Result) error) []error {
   485  	f.mu.Lock()
   486  	f.log = append(f.log, fmt.Sprintf("StreamExecuteMulti %s %s", query, printResolvedShardsBindVars(rss, bindVars)))
   487  	r, err := f.nextResult()
   488  	f.mu.Unlock()
   489  	if err != nil {
   490  		return []error{err}
   491  	}
   492  
   493  	return []error{callback(r)}
   494  }
   495  
   496  func (f *loggingVCursor) ResolveDestinations(ctx context.Context, keyspace string, ids []*querypb.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][]*querypb.Value, error) {
   497  	f.log = append(f.log, fmt.Sprintf("ResolveDestinations %v %v %v", keyspace, ids, key.DestinationsString(destinations)))
   498  	if f.shardErr != nil {
   499  		return nil, nil, f.shardErr
   500  	}
   501  
   502  	var rss []*srvtopo.ResolvedShard
   503  	var values [][]*querypb.Value
   504  	visited := make(map[string]int)
   505  	for i, destination := range destinations {
   506  		var shards []string
   507  
   508  		switch d := destination.(type) {
   509  		case key.DestinationAllShards:
   510  			if f.ksShardMap != nil {
   511  				if ksShards, exists := f.ksShardMap[keyspace]; exists {
   512  					shards = ksShards
   513  					break
   514  				}
   515  			}
   516  			shards = f.shards
   517  		case key.DestinationKeyRange:
   518  			shards = f.shardForKsid
   519  		case key.DestinationKeyspaceID:
   520  			if f.shardForKsid == nil || f.curShardForKsid >= len(f.shardForKsid) {
   521  				shards = []string{"-20"}
   522  			} else {
   523  				shards = []string{f.shardForKsid[f.curShardForKsid]}
   524  				f.curShardForKsid++
   525  			}
   526  		case key.DestinationKeyspaceIDs:
   527  			for _, ksid := range d {
   528  				if string(ksid) < "\x20" {
   529  					shards = append(shards, "-20")
   530  				} else {
   531  					shards = append(shards, "20-")
   532  				}
   533  			}
   534  		case key.DestinationAnyShard:
   535  			// Take the first shard.
   536  			shards = f.shards[:1]
   537  		case key.DestinationNone:
   538  			// Nothing to do here.
   539  		case key.DestinationShard:
   540  			shards = []string{destination.String()}
   541  		default:
   542  			return nil, nil, fmt.Errorf("unsupported destination: %v", destination)
   543  		}
   544  
   545  		for _, shard := range shards {
   546  			vi, ok := visited[shard]
   547  			if !ok {
   548  				vi = len(rss)
   549  				visited[shard] = vi
   550  				rss = append(rss, &srvtopo.ResolvedShard{
   551  					Target: &querypb.Target{
   552  						Keyspace:   keyspace,
   553  						Shard:      shard,
   554  						TabletType: f.resolvedTargetTabletType,
   555  					},
   556  				})
   557  				if ids != nil {
   558  					values = append(values, nil)
   559  				}
   560  			}
   561  			if ids != nil {
   562  				values[vi] = append(values[vi], ids[i])
   563  			}
   564  		}
   565  	}
   566  	return rss, values, nil
   567  }
   568  
   569  func (f *loggingVCursor) ResolveDestinationsMultiCol(ctx context.Context, keyspace string, ids [][]sqltypes.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][][]sqltypes.Value, error) {
   570  	f.log = append(f.log, fmt.Sprintf("ResolveDestinationsMultiCol %v %v %v", keyspace, ids, key.DestinationsString(destinations)))
   571  	if f.shardErr != nil {
   572  		return nil, nil, f.shardErr
   573  	}
   574  
   575  	var rss []*srvtopo.ResolvedShard
   576  	var values [][][]sqltypes.Value
   577  	visited := make(map[string]int)
   578  	for i, destination := range destinations {
   579  		var shards []string
   580  
   581  		switch d := destination.(type) {
   582  		case key.DestinationAllShards:
   583  			shards = f.shards
   584  		case key.DestinationKeyRange:
   585  			shards = f.shardForKsid
   586  		case key.DestinationKeyspaceID:
   587  			if f.shardForKsid == nil || f.curShardForKsid >= len(f.shardForKsid) {
   588  				shards = []string{"-20"}
   589  			} else {
   590  				shards = []string{f.shardForKsid[f.curShardForKsid]}
   591  				f.curShardForKsid++
   592  			}
   593  		case key.DestinationKeyspaceIDs:
   594  			for _, ksid := range d {
   595  				if string(ksid) < "\x20" {
   596  					shards = append(shards, "-20")
   597  				} else {
   598  					shards = append(shards, "20-")
   599  				}
   600  			}
   601  		case key.DestinationAnyShard:
   602  			// Take the first shard.
   603  			shards = f.shards[:1]
   604  		case key.DestinationNone:
   605  			// Nothing to do here.
   606  		case key.DestinationShard:
   607  			shards = []string{destination.String()}
   608  		default:
   609  			return nil, nil, fmt.Errorf("unsupported destination: %v", destination)
   610  		}
   611  
   612  		for _, shard := range shards {
   613  			vi, ok := visited[shard]
   614  			if !ok {
   615  				vi = len(rss)
   616  				visited[shard] = vi
   617  				rss = append(rss, &srvtopo.ResolvedShard{
   618  					Target: &querypb.Target{
   619  						Keyspace:   keyspace,
   620  						Shard:      shard,
   621  						TabletType: f.resolvedTargetTabletType,
   622  					},
   623  				})
   624  				if ids != nil {
   625  					values = append(values, nil)
   626  				}
   627  			}
   628  			if ids != nil {
   629  				values[vi] = append(values[vi], ids[i])
   630  			}
   631  		}
   632  	}
   633  	return rss, values, nil
   634  }
   635  
   636  func (f *loggingVCursor) ExpectLog(t *testing.T, want []string) {
   637  	t.Helper()
   638  	if len(f.log) == 0 && len(want) == 0 {
   639  		return
   640  	}
   641  	if !reflect.DeepEqual(f.log, want) {
   642  		t.Errorf("got:\n%s\nwant:\n%s", strings.Join(f.log, "\n"), strings.Join(want, "\n"))
   643  	}
   644  	utils.MustMatch(t, want, f.log, "")
   645  }
   646  
   647  func (f *loggingVCursor) ExpectWarnings(t *testing.T, want []*querypb.QueryWarning) {
   648  	t.Helper()
   649  	if !reflect.DeepEqual(f.warnings, want) {
   650  		t.Errorf("vc.warnings:\n%+v\nwant:\n%+v", f.warnings, want)
   651  	}
   652  }
   653  
   654  func (f *loggingVCursor) Rewind() {
   655  	f.curShardForKsid = 0
   656  	f.curResult = 0
   657  	f.log = nil
   658  	f.warnings = nil
   659  }
   660  
   661  func (f *loggingVCursor) SetAutocommit(context.Context, bool) error {
   662  	panic("implement me")
   663  }
   664  
   665  func (f *loggingVCursor) SetClientFoundRows(context.Context, bool) error {
   666  	panic("implement me")
   667  }
   668  
   669  func (f *loggingVCursor) SetSkipQueryPlanCache(context.Context, bool) error {
   670  	panic("implement me")
   671  }
   672  
   673  func (f *loggingVCursor) SetSQLSelectLimit(int64) error {
   674  	panic("implement me")
   675  }
   676  
   677  func (f *loggingVCursor) SetTransactionMode(vtgatepb.TransactionMode) {
   678  	panic("implement me")
   679  }
   680  
   681  func (f *loggingVCursor) SetWorkload(querypb.ExecuteOptions_Workload) {
   682  	panic("implement me")
   683  }
   684  
   685  func (f *loggingVCursor) SetPlannerVersion(querypb.ExecuteOptions_PlannerVersion) {
   686  	panic("implement me")
   687  }
   688  
   689  func (f *loggingVCursor) FindRoutedTable(tbl sqlparser.TableName) (*vindexes.Table, error) {
   690  	f.log = append(f.log, fmt.Sprintf("FindTable(%s)", sqlparser.String(tbl)))
   691  	return f.tableRoutes.tbl, nil
   692  }
   693  
   694  func (f *loggingVCursor) GetDBDDLPluginName() string {
   695  	return f.dbDDLPlugin
   696  }
   697  
   698  func (f *loggingVCursor) nextResult() (*sqltypes.Result, error) {
   699  	if f.results == nil || f.curResult >= len(f.results) {
   700  		return &sqltypes.Result{}, f.resultErr
   701  	}
   702  
   703  	r := f.results[f.curResult]
   704  	f.curResult++
   705  	if r == nil {
   706  		return &sqltypes.Result{}, f.resultErr
   707  	}
   708  	return r, nil
   709  }
   710  
   711  func (f *loggingVCursor) CanUseSetVar() bool {
   712  	useSetVar := sqlparser.IsMySQL80AndAbove() && !f.disableSetVar
   713  	if useSetVar {
   714  		f.log = append(f.log, "SET_VAR can be used")
   715  	}
   716  	return useSetVar
   717  }
   718  
   719  func (t *noopVCursor) VExplainLogging() {}
   720  func (t *noopVCursor) DisableLogging()  {}
   721  func (t *noopVCursor) GetVExplainLogs() []ExecuteEntry {
   722  	return nil
   723  }
   724  func (t *noopVCursor) GetLogs() ([]ExecuteEntry, error) {
   725  	return nil, nil
   726  }
   727  
   728  func expectResult(t *testing.T, msg string, result, want *sqltypes.Result) {
   729  	t.Helper()
   730  	if !reflect.DeepEqual(result, want) {
   731  		t.Errorf("%s:\n%v\nwant:\n%v", msg, result, want)
   732  	}
   733  }
   734  
   735  func printBindVars(bindvars map[string]*querypb.BindVariable) string {
   736  	var keys []string
   737  	for k := range bindvars {
   738  		keys = append(keys, k)
   739  	}
   740  	sort.Strings(keys)
   741  	buf := &bytes.Buffer{}
   742  	for i, k := range keys {
   743  		if i > 0 {
   744  			fmt.Fprintf(buf, " ")
   745  		}
   746  		fmt.Fprintf(buf, "%s: %v", k, bindvars[k])
   747  	}
   748  	return buf.String()
   749  }
   750  
   751  func printResolvedShardQueries(rss []*srvtopo.ResolvedShard, queries []*querypb.BoundQuery) string {
   752  	buf := &bytes.Buffer{}
   753  	for i, rs := range rss {
   754  		fmt.Fprintf(buf, "%s.%s: %s {%s} ", rs.Target.Keyspace, rs.Target.Shard, queries[i].Sql, printBindVars(queries[i].BindVariables))
   755  	}
   756  	return buf.String()
   757  }
   758  
   759  func printResolvedShardsBindVars(rss []*srvtopo.ResolvedShard, bvs []map[string]*querypb.BindVariable) string {
   760  	buf := &bytes.Buffer{}
   761  	for i, rs := range rss {
   762  		fmt.Fprintf(buf, "%s.%s: {%v} ", rs.Target.Keyspace, rs.Target.Shard, printBindVars(bvs[i]))
   763  	}
   764  	return buf.String()
   765  }