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 }