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 }