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

     1  package vtgate
     2  
     3  import (
     4  	"context"
     5  	"encoding/hex"
     6  	"fmt"
     7  	"strconv"
     8  	"testing"
     9  
    10  	querypb "vitess.io/vitess/go/vt/proto/query"
    11  
    12  	"vitess.io/vitess/go/vt/proto/vschema"
    13  	vschemapb "vitess.io/vitess/go/vt/proto/vschema"
    14  	"vitess.io/vitess/go/vt/srvtopo"
    15  	"vitess.io/vitess/go/vt/topo"
    16  
    17  	"vitess.io/vitess/go/vt/key"
    18  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    19  
    20  	"github.com/stretchr/testify/require"
    21  
    22  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    23  	vtgatepb "vitess.io/vitess/go/vt/proto/vtgate"
    24  	"vitess.io/vitess/go/vt/sqlparser"
    25  )
    26  
    27  var _ VSchemaOperator = (*fakeVSchemaOperator)(nil)
    28  
    29  type fakeVSchemaOperator struct {
    30  	vschema *vindexes.VSchema
    31  }
    32  
    33  func (f fakeVSchemaOperator) GetCurrentSrvVschema() *vschema.SrvVSchema {
    34  	panic("implement me")
    35  }
    36  
    37  func (f fakeVSchemaOperator) UpdateVSchema(ctx context.Context, ksName string, vschema *vschema.SrvVSchema) error {
    38  	panic("implement me")
    39  }
    40  
    41  type fakeTopoServer struct {
    42  }
    43  
    44  // GetTopoServer returns the full topo.Server instance.
    45  func (f *fakeTopoServer) GetTopoServer() (*topo.Server, error) {
    46  	return nil, nil
    47  }
    48  
    49  // GetSrvKeyspaceNames returns the list of keyspaces served in
    50  // the provided cell.
    51  func (f *fakeTopoServer) GetSrvKeyspaceNames(ctx context.Context, cell string, staleOK bool) ([]string, error) {
    52  	return []string{"ks1"}, nil
    53  }
    54  
    55  // GetSrvKeyspace returns the SrvKeyspace for a cell/keyspace.
    56  func (f *fakeTopoServer) GetSrvKeyspace(ctx context.Context, cell, keyspace string) (*topodatapb.SrvKeyspace, error) {
    57  	zeroHexBytes, _ := hex.DecodeString("")
    58  	eightyHexBytes, _ := hex.DecodeString("80")
    59  	ks := &topodatapb.SrvKeyspace{
    60  		Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{
    61  			{
    62  				ServedType: topodatapb.TabletType_PRIMARY,
    63  				ShardReferences: []*topodatapb.ShardReference{
    64  					{Name: "-80", KeyRange: &topodatapb.KeyRange{Start: zeroHexBytes, End: eightyHexBytes}},
    65  					{Name: "80-", KeyRange: &topodatapb.KeyRange{Start: eightyHexBytes, End: zeroHexBytes}},
    66  				},
    67  			},
    68  		},
    69  	}
    70  	return ks, nil
    71  }
    72  
    73  func (f *fakeTopoServer) WatchSrvKeyspace(ctx context.Context, cell, keyspace string, callback func(*topodatapb.SrvKeyspace, error) bool) {
    74  	ks, err := f.GetSrvKeyspace(ctx, cell, keyspace)
    75  	callback(ks, err)
    76  }
    77  
    78  // WatchSrvVSchema starts watching the SrvVSchema object for
    79  // the provided cell.  It will call the callback when
    80  // a new value or an error occurs.
    81  func (f *fakeTopoServer) WatchSrvVSchema(ctx context.Context, cell string, callback func(*vschemapb.SrvVSchema, error) bool) {
    82  
    83  }
    84  
    85  func TestDestinationKeyspace(t *testing.T) {
    86  	ks1 := &vindexes.Keyspace{
    87  		Name:    "ks1",
    88  		Sharded: false,
    89  	}
    90  	ks1Schema := &vindexes.KeyspaceSchema{
    91  		Keyspace: ks1,
    92  		Tables:   nil,
    93  		Vindexes: nil,
    94  		Error:    nil,
    95  	}
    96  	ks2 := &vindexes.Keyspace{
    97  		Name:    "ks2",
    98  		Sharded: false,
    99  	}
   100  	ks2Schema := &vindexes.KeyspaceSchema{
   101  		Keyspace: ks2,
   102  		Tables:   nil,
   103  		Vindexes: nil,
   104  		Error:    nil,
   105  	}
   106  	vschemaWith2KS := &vindexes.VSchema{
   107  		Keyspaces: map[string]*vindexes.KeyspaceSchema{
   108  			ks1.Name: ks1Schema,
   109  			ks2.Name: ks2Schema,
   110  		}}
   111  
   112  	vschemaWith1KS := &vindexes.VSchema{
   113  		Keyspaces: map[string]*vindexes.KeyspaceSchema{
   114  			ks1.Name: ks1Schema,
   115  		}}
   116  
   117  	type testCase struct {
   118  		vschema                 *vindexes.VSchema
   119  		targetString, qualifier string
   120  		expectedError           string
   121  		expectedKeyspace        string
   122  		expectedDest            key.Destination
   123  		expectedTabletType      topodatapb.TabletType
   124  	}
   125  
   126  	tests := []testCase{{
   127  		vschema:            vschemaWith1KS,
   128  		targetString:       "",
   129  		qualifier:          "",
   130  		expectedKeyspace:   ks1.Name,
   131  		expectedDest:       nil,
   132  		expectedTabletType: topodatapb.TabletType_PRIMARY,
   133  	}, {
   134  		vschema:            vschemaWith1KS,
   135  		targetString:       "ks1",
   136  		qualifier:          "",
   137  		expectedKeyspace:   ks1.Name,
   138  		expectedDest:       nil,
   139  		expectedTabletType: topodatapb.TabletType_PRIMARY,
   140  	}, {
   141  		vschema:            vschemaWith1KS,
   142  		targetString:       "ks1:-80",
   143  		qualifier:          "",
   144  		expectedKeyspace:   ks1.Name,
   145  		expectedDest:       key.DestinationShard("-80"),
   146  		expectedTabletType: topodatapb.TabletType_PRIMARY,
   147  	}, {
   148  		vschema:            vschemaWith1KS,
   149  		targetString:       "ks1@replica",
   150  		qualifier:          "",
   151  		expectedKeyspace:   ks1.Name,
   152  		expectedDest:       nil,
   153  		expectedTabletType: topodatapb.TabletType_REPLICA,
   154  	}, {
   155  		vschema:            vschemaWith1KS,
   156  		targetString:       "ks1:-80@replica",
   157  		qualifier:          "",
   158  		expectedKeyspace:   ks1.Name,
   159  		expectedDest:       key.DestinationShard("-80"),
   160  		expectedTabletType: topodatapb.TabletType_REPLICA,
   161  	}, {
   162  		vschema:            vschemaWith1KS,
   163  		targetString:       "",
   164  		qualifier:          "ks1",
   165  		expectedKeyspace:   ks1.Name,
   166  		expectedDest:       nil,
   167  		expectedTabletType: topodatapb.TabletType_PRIMARY,
   168  	}, {
   169  		vschema:       vschemaWith1KS,
   170  		targetString:  "ks2",
   171  		qualifier:     "",
   172  		expectedError: "VT05003: unknown database 'ks2' in vschema",
   173  	}, {
   174  		vschema:       vschemaWith1KS,
   175  		targetString:  "ks2:-80",
   176  		qualifier:     "",
   177  		expectedError: "VT05003: unknown database 'ks2' in vschema",
   178  	}, {
   179  		vschema:       vschemaWith1KS,
   180  		targetString:  "",
   181  		qualifier:     "ks2",
   182  		expectedError: "VT05003: unknown database 'ks2' in vschema",
   183  	}, {
   184  		vschema:       vschemaWith2KS,
   185  		targetString:  "",
   186  		expectedError: errNoKeyspace.Error(),
   187  	}}
   188  
   189  	for i, tc := range tests {
   190  		t.Run(strconv.Itoa(i)+tc.targetString, func(t *testing.T) {
   191  			impl, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{TargetString: tc.targetString}), sqlparser.MarginComments{}, nil, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, nil, nil, false, querypb.ExecuteOptions_Gen4)
   192  			impl.vschema = tc.vschema
   193  			dest, keyspace, tabletType, err := impl.TargetDestination(tc.qualifier)
   194  			if tc.expectedError == "" {
   195  				require.NoError(t, err)
   196  				require.Equal(t, tc.expectedDest, dest)
   197  				require.Equal(t, tc.expectedKeyspace, keyspace.Name)
   198  				require.Equal(t, tc.expectedTabletType, tabletType)
   199  			} else {
   200  				require.EqualError(t, err, tc.expectedError)
   201  			}
   202  		})
   203  	}
   204  }
   205  
   206  var ks1 = &vindexes.Keyspace{Name: "ks1"}
   207  var ks1Schema = &vindexes.KeyspaceSchema{Keyspace: ks1}
   208  var ks2 = &vindexes.Keyspace{Name: "ks2"}
   209  var ks2Schema = &vindexes.KeyspaceSchema{Keyspace: ks2}
   210  var vschemaWith1KS = &vindexes.VSchema{
   211  	Keyspaces: map[string]*vindexes.KeyspaceSchema{
   212  		ks1.Name: ks1Schema,
   213  	},
   214  }
   215  var vschemaWith2KS = &vindexes.VSchema{
   216  	Keyspaces: map[string]*vindexes.KeyspaceSchema{
   217  		ks1.Name: ks1Schema,
   218  		ks2.Name: ks2Schema,
   219  	}}
   220  
   221  func TestSetTarget(t *testing.T) {
   222  	type testCase struct {
   223  		vschema       *vindexes.VSchema
   224  		targetString  string
   225  		expectedError string
   226  	}
   227  
   228  	tests := []testCase{{
   229  		vschema:      vschemaWith2KS,
   230  		targetString: "",
   231  	}, {
   232  		vschema:      vschemaWith2KS,
   233  		targetString: "ks1",
   234  	}, {
   235  		vschema:      vschemaWith2KS,
   236  		targetString: "ks2",
   237  	}, {
   238  		vschema:       vschemaWith2KS,
   239  		targetString:  "ks3",
   240  		expectedError: "VT05003: unknown database 'ks3' in vschema",
   241  	}, {
   242  		vschema:       vschemaWith2KS,
   243  		targetString:  "ks2@replica",
   244  		expectedError: "can't execute the given command because you have an active transaction",
   245  	}}
   246  
   247  	for i, tc := range tests {
   248  		t.Run(fmt.Sprintf("%d#%s", i, tc.targetString), func(t *testing.T) {
   249  			vc, _ := newVCursorImpl(NewSafeSession(&vtgatepb.Session{InTransaction: true}), sqlparser.MarginComments{}, nil, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, nil, nil, false, querypb.ExecuteOptions_Gen4)
   250  			vc.vschema = tc.vschema
   251  			err := vc.SetTarget(tc.targetString)
   252  			if tc.expectedError == "" {
   253  				require.NoError(t, err)
   254  				require.Equal(t, vc.safeSession.TargetString, tc.targetString)
   255  			} else {
   256  				require.EqualError(t, err, tc.expectedError)
   257  			}
   258  		})
   259  	}
   260  }
   261  
   262  func TestPlanPrefixKey(t *testing.T) {
   263  	type testCase struct {
   264  		vschema               *vindexes.VSchema
   265  		targetString          string
   266  		expectedPlanPrefixKey string
   267  	}
   268  
   269  	tests := []testCase{{
   270  		vschema:               vschemaWith1KS,
   271  		targetString:          "",
   272  		expectedPlanPrefixKey: "ks1@primary",
   273  	}, {
   274  		vschema:               vschemaWith1KS,
   275  		targetString:          "ks1@replica",
   276  		expectedPlanPrefixKey: "ks1@replica",
   277  	}, {
   278  		vschema:               vschemaWith1KS,
   279  		targetString:          "ks1:-80",
   280  		expectedPlanPrefixKey: "ks1@primaryDestinationShard(-80)",
   281  	}, {
   282  		vschema:               vschemaWith1KS,
   283  		targetString:          "ks1[deadbeef]",
   284  		expectedPlanPrefixKey: "ks1@primaryKsIDsResolved(80-)",
   285  	}}
   286  
   287  	for i, tc := range tests {
   288  		t.Run(fmt.Sprintf("%d#%s", i, tc.targetString), func(t *testing.T) {
   289  			ss := NewSafeSession(&vtgatepb.Session{InTransaction: false})
   290  			ss.SetTargetString(tc.targetString)
   291  			vc, err := newVCursorImpl(ss, sqlparser.MarginComments{}, nil, nil, &fakeVSchemaOperator{vschema: tc.vschema}, tc.vschema, srvtopo.NewResolver(&fakeTopoServer{}, nil, ""), nil, false, querypb.ExecuteOptions_Gen4)
   292  			require.NoError(t, err)
   293  			vc.vschema = tc.vschema
   294  			require.Equal(t, tc.expectedPlanPrefixKey, vc.planPrefixKey(context.Background()))
   295  		})
   296  	}
   297  }
   298  
   299  func TestFirstSortedKeyspace(t *testing.T) {
   300  	ks1Schema := &vindexes.KeyspaceSchema{Keyspace: &vindexes.Keyspace{Name: "xks1"}}
   301  	ks2Schema := &vindexes.KeyspaceSchema{Keyspace: &vindexes.Keyspace{Name: "aks2"}}
   302  	ks3Schema := &vindexes.KeyspaceSchema{Keyspace: &vindexes.Keyspace{Name: "aks1"}}
   303  	vschemaWith2KS := &vindexes.VSchema{
   304  		Keyspaces: map[string]*vindexes.KeyspaceSchema{
   305  			ks1Schema.Keyspace.Name: ks1Schema,
   306  			ks2Schema.Keyspace.Name: ks2Schema,
   307  			ks3Schema.Keyspace.Name: ks3Schema,
   308  		}}
   309  
   310  	vc, err := newVCursorImpl(NewSafeSession(nil), sqlparser.MarginComments{}, nil, nil, &fakeVSchemaOperator{vschema: vschemaWith2KS}, vschemaWith2KS, srvtopo.NewResolver(&fakeTopoServer{}, nil, ""), nil, false, querypb.ExecuteOptions_Gen4)
   311  	require.NoError(t, err)
   312  	ks, err := vc.FirstSortedKeyspace()
   313  	require.NoError(t, err)
   314  	require.Equal(t, ks3Schema.Keyspace, ks)
   315  }