vitess.io/vitess@v0.16.2/go/vt/vtgate/executor_framework_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 vtgate 18 19 import ( 20 "bytes" 21 "context" 22 "fmt" 23 "strconv" 24 "strings" 25 "testing" 26 27 "vitess.io/vitess/go/vt/vtgate/logstats" 28 29 vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" 30 31 "github.com/stretchr/testify/require" 32 33 "github.com/stretchr/testify/assert" 34 35 "vitess.io/vitess/go/cache" 36 "vitess.io/vitess/go/sqltypes" 37 "vitess.io/vitess/go/streamlog" 38 "vitess.io/vitess/go/vt/discovery" 39 "vitess.io/vitess/go/vt/key" 40 "vitess.io/vitess/go/vt/srvtopo" 41 "vitess.io/vitess/go/vt/vtgate/vindexes" 42 "vitess.io/vitess/go/vt/vttablet/sandboxconn" 43 44 querypb "vitess.io/vitess/go/vt/proto/query" 45 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 46 ) 47 48 var executorVSchema = ` 49 { 50 "sharded": true, 51 "vindexes": { 52 "hash_index": { 53 "type": "hash" 54 }, 55 "music_user_map": { 56 "type": "lookup_hash_unique", 57 "owner": "music", 58 "params": { 59 "table": "music_user_map", 60 "from": "music_id", 61 "to": "user_id" 62 } 63 }, 64 "name_user_map": { 65 "type": "lookup_hash", 66 "owner": "user", 67 "params": { 68 "table": "name_user_map", 69 "from": "name", 70 "to": "user_id" 71 } 72 }, 73 "name_lastname_keyspace_id_map": { 74 "type": "lookup", 75 "owner": "user2", 76 "params": { 77 "table": "name_lastname_keyspace_id_map", 78 "from": "name,lastname", 79 "to": "keyspace_id" 80 } 81 }, 82 "insert_ignore_idx": { 83 "type": "lookup_hash", 84 "owner": "insert_ignore_test", 85 "params": { 86 "table": "ins_lookup", 87 "from": "fromcol", 88 "to": "tocol" 89 } 90 }, 91 "idx1": { 92 "type": "hash" 93 }, 94 "idx_noauto": { 95 "type": "hash", 96 "owner": "noauto_table" 97 }, 98 "keyspace_id": { 99 "type": "numeric" 100 }, 101 "krcol_unique_vdx": { 102 "type": "keyrange_lookuper_unique" 103 }, 104 "krcol_vdx": { 105 "type": "keyrange_lookuper" 106 }, 107 "t1_lkp_vdx": { 108 "type": "consistent_lookup_unique", 109 "params": { 110 "table": "t1_lkp_idx", 111 "from": "unq_col", 112 "to": "keyspace_id" 113 }, 114 "owner": "t1" 115 }, 116 "t2_wo_lu_vdx": { 117 "type": "lookup_unique", 118 "params": { 119 "table": "TestUnsharded.wo_lu_idx", 120 "from": "wo_lu_col", 121 "to": "keyspace_id", 122 "write_only": "true" 123 }, 124 "owner": "t2_lookup" 125 }, 126 "t2_erl_lu_vdx": { 127 "type": "lookup_unique", 128 "params": { 129 "table": "TestUnsharded.erl_lu_idx", 130 "from": "erl_lu_col", 131 "to": "keyspace_id", 132 "read_lock": "exclusive" 133 }, 134 "owner": "t2_lookup" 135 }, 136 "t2_srl_lu_vdx": { 137 "type": "lookup_unique", 138 "params": { 139 "table": "TestUnsharded.srl_lu_idx", 140 "from": "srl_lu_col", 141 "to": "keyspace_id", 142 "read_lock": "shared" 143 }, 144 "owner": "t2_lookup" 145 }, 146 "t2_nrl_lu_vdx": { 147 "type": "lookup_unique", 148 "params": { 149 "table": "TestUnsharded.nrl_lu_idx", 150 "from": "nrl_lu_col", 151 "to": "keyspace_id", 152 "read_lock": "none" 153 }, 154 "owner": "t2_lookup" 155 }, 156 "t2_nv_lu_vdx": { 157 "type": "lookup_unique", 158 "params": { 159 "table": "TestUnsharded.nv_lu_idx", 160 "from": "nv_lu_col", 161 "to": "keyspace_id", 162 "no_verify": "true" 163 }, 164 "owner": "t2_lookup" 165 }, 166 "t2_lu_vdx": { 167 "type": "lookup_hash_unique", 168 "params": { 169 "table": "TestUnsharded.lu_idx", 170 "from": "lu_col", 171 "to": "keyspace_id" 172 }, 173 "owner": "t2_lookup" 174 }, 175 "regional_vdx": { 176 "type": "region_experimental", 177 "params": { 178 "region_bytes": "1" 179 } 180 }, 181 "cfc": { 182 "type": "cfc" 183 } 184 }, 185 "tables": { 186 "user": { 187 "column_vindexes": [ 188 { 189 "column": "Id", 190 "name": "hash_index" 191 }, 192 { 193 "column": "name", 194 "name": "name_user_map" 195 } 196 ], 197 "auto_increment": { 198 "column": "id", 199 "sequence": "user_seq" 200 }, 201 "columns": [ 202 { 203 "name": "textcol", 204 "type": "VARCHAR" 205 } 206 ] 207 }, 208 "user2": { 209 "column_vindexes": [ 210 { 211 "column": "id", 212 "name": "hash_index" 213 }, 214 { 215 "columns": ["name", "lastname"], 216 "name": "name_lastname_keyspace_id_map" 217 } 218 ] 219 }, 220 "user_extra": { 221 "column_vindexes": [ 222 { 223 "column": "user_id", 224 "name": "hash_index" 225 } 226 ] 227 }, 228 "sharded_user_msgs": { 229 "column_vindexes": [ 230 { 231 "column": "user_id", 232 "name": "hash_index" 233 } 234 ] 235 }, 236 "music": { 237 "column_vindexes": [ 238 { 239 "column": "user_id", 240 "name": "hash_index" 241 }, 242 { 243 "column": "id", 244 "name": "music_user_map" 245 } 246 ], 247 "auto_increment": { 248 "column": "id", 249 "sequence": "user_seq" 250 } 251 }, 252 "music_extra": { 253 "column_vindexes": [ 254 { 255 "column": "user_id", 256 "name": "hash_index" 257 }, 258 { 259 "column": "music_id", 260 "name": "music_user_map" 261 } 262 ] 263 }, 264 "music_extra_reversed": { 265 "column_vindexes": [ 266 { 267 "column": "music_id", 268 "name": "music_user_map" 269 }, 270 { 271 "column": "user_id", 272 "name": "hash_index" 273 } 274 ] 275 }, 276 "insert_ignore_test": { 277 "column_vindexes": [ 278 { 279 "column": "pv", 280 "name": "music_user_map" 281 }, 282 { 283 "column": "owned", 284 "name": "insert_ignore_idx" 285 }, 286 { 287 "column": "verify", 288 "name": "hash_index" 289 } 290 ] 291 }, 292 "noauto_table": { 293 "column_vindexes": [ 294 { 295 "column": "id", 296 "name": "idx_noauto" 297 } 298 ] 299 }, 300 "keyrange_table": { 301 "column_vindexes": [ 302 { 303 "column": "krcol_unique", 304 "name": "krcol_unique_vdx" 305 }, 306 { 307 "column": "krcol", 308 "name": "krcol_vdx" 309 } 310 ] 311 }, 312 "ksid_table": { 313 "column_vindexes": [ 314 { 315 "column": "keyspace_id", 316 "name": "keyspace_id" 317 } 318 ] 319 }, 320 "t1": { 321 "column_vindexes": [ 322 { 323 "column": "id", 324 "name": "hash_index" 325 }, 326 { 327 "column": "unq_col", 328 "name": "t1_lkp_vdx" 329 } 330 ] 331 }, 332 "t1_lkp_idx": { 333 "column_vindexes": [ 334 { 335 "column": "unq_col", 336 "name": "hash_index" 337 } 338 ] 339 }, 340 "t2_lookup": { 341 "column_vindexes": [ 342 { 343 "column": "id", 344 "name": "hash_index" 345 }, 346 { 347 "column": "wo_lu_col", 348 "name": "t2_wo_lu_vdx" 349 }, 350 { 351 "column": "erl_lu_col", 352 "name": "t2_erl_lu_vdx" 353 }, 354 { 355 "column": "srl_lu_col", 356 "name": "t2_srl_lu_vdx" 357 }, 358 { 359 "column": "nrl_lu_col", 360 "name": "t2_nrl_lu_vdx" 361 }, 362 { 363 "column": "nv_lu_col", 364 "name": "t2_nv_lu_vdx" 365 }, 366 { 367 "column": "lu_col", 368 "name": "t2_lu_vdx" 369 } 370 ] 371 }, 372 "user_region": { 373 "column_vindexes": [ 374 { 375 "columns": ["cola","colb"], 376 "name": "regional_vdx" 377 } 378 ] 379 }, 380 "tbl_cfc": { 381 "column_vindexes": [ 382 { 383 "column": "c1", 384 "name": "cfc" 385 } 386 ], 387 "columns": [ 388 { 389 "name": "c2", 390 "type": "VARCHAR" 391 } 392 ] 393 }, 394 "zip_detail": { 395 "type": "reference", 396 "source": "TestUnsharded.zip_detail" 397 } 398 } 399 } 400 ` 401 402 var badVSchema = ` 403 { 404 "sharded": false, 405 "tables": { 406 "sharded_table": {} 407 } 408 } 409 ` 410 411 var unshardedVSchema = ` 412 { 413 "sharded": false, 414 "tables": { 415 "user_seq": { 416 "type": "sequence" 417 }, 418 "music_user_map": {}, 419 "name_user_map": {}, 420 "name_lastname_keyspace_id_map": {}, 421 "user_msgs": {}, 422 "ins_lookup": {}, 423 "main1": { 424 "auto_increment": { 425 "column": "id", 426 "sequence": "user_seq" 427 } 428 }, 429 "wo_lu_idx": {}, 430 "erl_lu_idx": {}, 431 "srl_lu_idx": {}, 432 "nrl_lu_idx": {}, 433 "nv_lu_idx": {}, 434 "lu_idx": {}, 435 "simple": {}, 436 "zip_detail": {} 437 } 438 } 439 ` 440 441 const ( 442 testBufferSize = 10 443 ) 444 445 type DestinationAnyShardPickerFirstShard struct{} 446 447 func (dp DestinationAnyShardPickerFirstShard) PickShard(shardCount int) int { 448 return 0 449 } 450 451 // keyRangeLookuper is for testing a lookup that returns a keyrange. 452 type keyRangeLookuper struct { 453 } 454 455 func (v *keyRangeLookuper) String() string { return "keyrange_lookuper" } 456 func (*keyRangeLookuper) Cost() int { return 0 } 457 func (*keyRangeLookuper) IsUnique() bool { return false } 458 func (*keyRangeLookuper) NeedsVCursor() bool { return false } 459 func (*keyRangeLookuper) Verify(context.Context, vindexes.VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { 460 return []bool{}, nil 461 } 462 func (*keyRangeLookuper) Map(ctx context.Context, vcursor vindexes.VCursor, ids []sqltypes.Value) ([]key.Destination, error) { 463 return []key.Destination{ 464 key.DestinationKeyRange{ 465 KeyRange: &topodatapb.KeyRange{ 466 End: []byte{0x10}, 467 }, 468 }, 469 }, nil 470 } 471 472 func newKeyRangeLookuper(name string, params map[string]string) (vindexes.Vindex, error) { 473 return &keyRangeLookuper{}, nil 474 } 475 476 // keyRangeLookuperUnique is for testing a unique lookup that returns a keyrange. 477 type keyRangeLookuperUnique struct { 478 } 479 480 func (v *keyRangeLookuperUnique) String() string { return "keyrange_lookuper" } 481 func (*keyRangeLookuperUnique) Cost() int { return 0 } 482 func (*keyRangeLookuperUnique) IsUnique() bool { return true } 483 func (*keyRangeLookuperUnique) NeedsVCursor() bool { return false } 484 func (*keyRangeLookuperUnique) Verify(context.Context, vindexes.VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { 485 return []bool{}, nil 486 } 487 func (*keyRangeLookuperUnique) Map(ctx context.Context, vcursor vindexes.VCursor, ids []sqltypes.Value) ([]key.Destination, error) { 488 return []key.Destination{ 489 key.DestinationKeyRange{ 490 KeyRange: &topodatapb.KeyRange{ 491 End: []byte{0x10}, 492 }, 493 }, 494 }, nil 495 } 496 497 func newKeyRangeLookuperUnique(name string, params map[string]string) (vindexes.Vindex, error) { 498 return &keyRangeLookuperUnique{}, nil 499 } 500 501 func init() { 502 vindexes.Register("keyrange_lookuper", newKeyRangeLookuper) 503 vindexes.Register("keyrange_lookuper_unique", newKeyRangeLookuperUnique) 504 } 505 506 func createExecutorEnv() (executor *Executor, sbc1, sbc2, sbclookup *sandboxconn.SandboxConn) { 507 cell := "aa" 508 hc := discovery.NewFakeHealthCheck(nil) 509 s := createSandbox(KsTestSharded) 510 s.VSchema = executorVSchema 511 serv := newSandboxForCells([]string{cell}) 512 resolver := newTestResolver(hc, serv, cell) 513 sbc1 = hc.AddTestTablet(cell, "-20", 1, "TestExecutor", "-20", topodatapb.TabletType_PRIMARY, true, 1, nil) 514 sbc2 = hc.AddTestTablet(cell, "40-60", 1, "TestExecutor", "40-60", topodatapb.TabletType_PRIMARY, true, 1, nil) 515 // Create these connections so scatter queries don't fail. 516 _ = hc.AddTestTablet(cell, "20-40", 1, "TestExecutor", "20-40", topodatapb.TabletType_PRIMARY, true, 1, nil) 517 _ = hc.AddTestTablet(cell, "60-60", 1, "TestExecutor", "60-80", topodatapb.TabletType_PRIMARY, true, 1, nil) 518 _ = hc.AddTestTablet(cell, "80-a0", 1, "TestExecutor", "80-a0", topodatapb.TabletType_PRIMARY, true, 1, nil) 519 _ = hc.AddTestTablet(cell, "a0-c0", 1, "TestExecutor", "a0-c0", topodatapb.TabletType_PRIMARY, true, 1, nil) 520 _ = hc.AddTestTablet(cell, "c0-e0", 1, "TestExecutor", "c0-e0", topodatapb.TabletType_PRIMARY, true, 1, nil) 521 _ = hc.AddTestTablet(cell, "e0-", 1, "TestExecutor", "e0-", topodatapb.TabletType_PRIMARY, true, 1, nil) 522 // Below is needed so that SendAnyWherePlan doesn't fail 523 _ = hc.AddTestTablet(cell, "random", 1, "TestXBadVSchema", "-20", topodatapb.TabletType_PRIMARY, true, 1, nil) 524 525 createSandbox(KsTestUnsharded) 526 sbclookup = hc.AddTestTablet(cell, "0", 1, KsTestUnsharded, "0", topodatapb.TabletType_PRIMARY, true, 1, nil) 527 528 // Ues the 'X' in the name to ensure it's not alphabetically first. 529 // Otherwise, it would become the default keyspace for the dual table. 530 bad := createSandbox("TestXBadSharding") 531 bad.VSchema = badVSchema 532 533 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 534 executor = NewExecutor(context.Background(), serv, cell, resolver, false, false, testBufferSize, cache.DefaultConfig, nil, false, querypb.ExecuteOptions_V3) 535 536 key.AnyShardPicker = DestinationAnyShardPickerFirstShard{} 537 // create a new session each time so that ShardSessions don't get re-used across tests 538 primarySession = &vtgatepb.Session{ 539 TargetString: "@primary", 540 } 541 return executor, sbc1, sbc2, sbclookup 542 } 543 544 func createCustomExecutor(vschema string) (executor *Executor, sbc1, sbc2, sbclookup *sandboxconn.SandboxConn) { 545 cell := "aa" 546 hc := discovery.NewFakeHealthCheck(nil) 547 s := createSandbox(KsTestSharded) 548 s.VSchema = vschema 549 serv := newSandboxForCells([]string{cell}) 550 resolver := newTestResolver(hc, serv, cell) 551 sbc1 = hc.AddTestTablet(cell, "-20", 1, "TestExecutor", "-20", topodatapb.TabletType_PRIMARY, true, 1, nil) 552 sbc2 = hc.AddTestTablet(cell, "40-60", 1, "TestExecutor", "40-60", topodatapb.TabletType_PRIMARY, true, 1, nil) 553 554 createSandbox(KsTestUnsharded) 555 sbclookup = hc.AddTestTablet(cell, "0", 1, KsTestUnsharded, "0", topodatapb.TabletType_PRIMARY, true, 1, nil) 556 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 557 558 executor = NewExecutor(context.Background(), serv, cell, resolver, false, false, testBufferSize, cache.DefaultConfig, nil, false, querypb.ExecuteOptions_V3) 559 // create a new session each time so that ShardSessions don't get re-used across tests 560 primarySession = &vtgatepb.Session{ 561 TargetString: "@primary", 562 } 563 return executor, sbc1, sbc2, sbclookup 564 } 565 566 func createCustomExecutorSetValues(vschema string, values []*sqltypes.Result) (executor *Executor, sbc1, sbc2, sbclookup *sandboxconn.SandboxConn) { 567 cell := "aa" 568 hc := discovery.NewFakeHealthCheck(nil) 569 s := createSandbox(KsTestSharded) 570 s.VSchema = vschema 571 serv := newSandboxForCells([]string{cell}) 572 resolver := newTestResolver(hc, serv, cell) 573 shards := []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"} 574 sbcs := []*sandboxconn.SandboxConn{} 575 for _, shard := range shards { 576 sbc := hc.AddTestTablet(cell, shard, 1, "TestExecutor", shard, topodatapb.TabletType_PRIMARY, true, 1, nil) 577 if values != nil { 578 sbc.SetResults(values) 579 } 580 sbcs = append(sbcs, sbc) 581 } 582 583 createSandbox(KsTestUnsharded) 584 sbclookup = hc.AddTestTablet(cell, "0", 1, KsTestUnsharded, "0", topodatapb.TabletType_PRIMARY, true, 1, nil) 585 getSandbox(KsTestUnsharded).VSchema = unshardedVSchema 586 587 executor = NewExecutor(context.Background(), serv, cell, resolver, false, false, testBufferSize, cache.DefaultConfig, nil, false, querypb.ExecuteOptions_V3) 588 // create a new session each time so that ShardSessions don't get re-used across tests 589 primarySession = &vtgatepb.Session{ 590 TargetString: "@primary", 591 } 592 return executor, sbcs[0], sbcs[1], sbclookup 593 } 594 595 func executorExecSession(executor *Executor, sql string, bv map[string]*querypb.BindVariable, session *vtgatepb.Session) (*sqltypes.Result, error) { 596 return executor.Execute( 597 context.Background(), 598 "TestExecute", 599 NewSafeSession(session), 600 sql, 601 bv) 602 } 603 604 func executorExec(executor *Executor, sql string, bv map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 605 return executorExecSession(executor, sql, bv, primarySession) 606 } 607 608 func executorPrepare(executor *Executor, sql string, bv map[string]*querypb.BindVariable) ([]*querypb.Field, error) { 609 return executor.Prepare( 610 context.Background(), 611 "TestExecute", 612 NewSafeSession(primarySession), 613 sql, 614 bv) 615 } 616 617 func executorStream(executor *Executor, sql string) (qr *sqltypes.Result, err error) { 618 results := make(chan *sqltypes.Result, 100) 619 err = executor.StreamExecute( 620 context.Background(), 621 "TestExecuteStream", 622 NewSafeSession(nil), 623 sql, 624 nil, 625 func(qr *sqltypes.Result) error { 626 results <- qr 627 return nil 628 }, 629 ) 630 close(results) 631 if err != nil { 632 return nil, err 633 } 634 first := true 635 for r := range results { 636 if first { 637 qr = &sqltypes.Result{Fields: r.Fields} 638 first = false 639 } 640 qr.Rows = append(qr.Rows, r.Rows...) 641 } 642 return qr, nil 643 } 644 645 func assertQueries(t *testing.T, sbc *sandboxconn.SandboxConn, wantQueries []*querypb.BoundQuery) { 646 t.Helper() 647 idx := 0 648 for _, query := range sbc.Queries { 649 if strings.HasPrefix(query.Sql, "savepoint") || strings.HasPrefix(query.Sql, "rollback to") { 650 continue 651 } 652 if len(wantQueries) < idx { 653 t.Errorf("got more queries than expected") 654 } 655 got := query.Sql 656 expected := wantQueries[idx].Sql 657 assert.Equal(t, expected, got) 658 assert.Equal(t, wantQueries[idx].BindVariables, query.BindVariables) 659 idx++ 660 } 661 } 662 663 func assertQueriesWithSavepoint(t *testing.T, sbc *sandboxconn.SandboxConn, wantQueries []*querypb.BoundQuery) { 664 t.Helper() 665 require.Equal(t, len(wantQueries), len(sbc.Queries), sbc.Queries) 666 savepointStore := make(map[string]string) 667 for idx, query := range sbc.Queries { 668 require.Equal(t, wantQueries[idx].BindVariables, query.BindVariables) 669 got := query.Sql 670 expected := wantQueries[idx].Sql 671 if strings.HasPrefix(got, "savepoint") { 672 if !strings.HasPrefix(expected, "savepoint") { 673 t.Fatal("savepoint expected") 674 } 675 if sp, exists := savepointStore[expected[10:]]; exists { 676 assert.Equal(t, sp, got[10:]) 677 } else { 678 savepointStore[expected[10:]] = got[10:] 679 } 680 continue 681 } 682 if strings.HasPrefix(got, "rollback to") { 683 if !strings.HasPrefix(expected, "rollback to") { 684 t.Fatal("rollback to expected") 685 } 686 assert.Equal(t, savepointStore[expected[12:]], got[12:]) 687 continue 688 } 689 assert.Equal(t, expected, got) 690 } 691 } 692 693 func testCommitCount(t *testing.T, sbcName string, sbc *sandboxconn.SandboxConn, want int) { 694 t.Helper() 695 if got, want := sbc.CommitCount.Get(), int64(want); got != want { 696 t.Errorf("%s.CommitCount: %d, want %d\n", sbcName, got, want) 697 } 698 } 699 700 func testNonZeroDuration(t *testing.T, what, d string) { 701 t.Helper() 702 time, _ := strconv.ParseFloat(d, 64) 703 if time == 0 { 704 t.Errorf("querylog %s want non-zero duration got %s (%v)", what, d, time) 705 } 706 } 707 708 func getQueryLog(logChan chan any) *logstats.LogStats { 709 var log any 710 711 select { 712 case log = <-logChan: 713 return log.(*logstats.LogStats) 714 default: 715 return nil 716 } 717 } 718 719 // Queries can hit the plan cache in less than a microsecond, which makes them 720 // appear to take 0.000000 time in the query log. To mitigate this in tests, 721 // keep an in-memory record of queries that we know have been planned during 722 // the current test execution and skip testing for non-zero plan time if this 723 // is a repeat query. 724 var testPlannedQueries = map[string]bool{} 725 726 func testQueryLog(t *testing.T, logChan chan any, method, stmtType, sql string, shardQueries int) *logstats.LogStats { 727 t.Helper() 728 729 logStats := getQueryLog(logChan) 730 require.NotNil(t, logStats) 731 732 var log bytes.Buffer 733 streamlog.GetFormatter(QueryLogger)(&log, nil, logStats) 734 fields := strings.Split(log.String(), "\t") 735 736 // fields[0] is the method 737 assert.Equal(t, method, fields[0], "logstats: method") 738 739 // fields[1] - fields[6] are the caller id, start/end times, etc 740 741 checkEqualQuery := true 742 // The internal savepoints are created with uuids so the value of it not known to assert. 743 // Therefore, the equal query check is ignored. 744 switch stmtType { 745 case "SAVEPOINT", "SAVEPOINT_ROLLBACK", "RELEASE": 746 checkEqualQuery = false 747 } 748 // only test the durations if there is no error (fields[16]) 749 if fields[16] == "\"\"" { 750 // fields[7] is the total execution time 751 testNonZeroDuration(t, "TotalTime", fields[7]) 752 753 // fields[8] is the planner time. keep track of the planned queries to 754 // avoid the case where we hit the plan in cache and it takes less than 755 // a microsecond to plan it 756 if testPlannedQueries[sql] == false { 757 testNonZeroDuration(t, "PlanTime", fields[8]) 758 } 759 testPlannedQueries[sql] = true 760 761 // fields[9] is ExecuteTime which is not set for certain statements SET, 762 // BEGIN, COMMIT, ROLLBACK, etc 763 switch stmtType { 764 case "BEGIN", "COMMIT", "SET", "ROLLBACK", "SAVEPOINT", "SAVEPOINT_ROLLBACK", "RELEASE": 765 default: 766 testNonZeroDuration(t, "ExecuteTime", fields[9]) 767 } 768 769 // fields[10] is CommitTime which is set only in autocommit mode and 770 // tested separately 771 } 772 773 // fields[11] is the statement type 774 assert.Equal(t, stmtType, fields[11], "logstats: stmtType") 775 776 if checkEqualQuery { 777 // fields[12] is the original sql 778 wantSQL := fmt.Sprintf("%q", sql) 779 assert.Equal(t, wantSQL, fields[12], "logstats: SQL") 780 } 781 // fields[13] contains the formatted bind vars 782 783 // fields[14] is the count of shard queries 784 assert.Equal(t, fmt.Sprintf("%v", shardQueries), fields[14], "logstats: ShardQueries") 785 786 return logStats 787 } 788 789 func newTestResolver(hc discovery.HealthCheck, serv srvtopo.Server, cell string) *Resolver { 790 sc := newTestScatterConn(hc, serv, cell) 791 srvResolver := srvtopo.NewResolver(serv, sc.gateway, cell) 792 return NewResolver(srvResolver, serv, cell, sc) 793 }