github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/row/fetcher_test.go (about) 1 // Copyright 2017 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package row 12 13 import ( 14 "context" 15 "fmt" 16 "reflect" 17 "strings" 18 "testing" 19 20 "github.com/cockroachdb/cockroach/pkg/base" 21 "github.com/cockroachdb/cockroach/pkg/keys" 22 "github.com/cockroachdb/cockroach/pkg/kv" 23 "github.com/cockroachdb/cockroach/pkg/roachpb" 24 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 25 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 26 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" 27 "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" 28 "github.com/cockroachdb/cockroach/pkg/util" 29 "github.com/cockroachdb/cockroach/pkg/util/encoding" 30 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 31 "github.com/stretchr/testify/assert" 32 ) 33 34 type initFetcherArgs struct { 35 tableDesc *sqlbase.ImmutableTableDescriptor 36 indexIdx int 37 valNeededForCol util.FastIntSet 38 spans roachpb.Spans 39 } 40 41 func makeFetcherArgs(entries []initFetcherArgs) []FetcherTableArgs { 42 fetcherArgs := make([]FetcherTableArgs, len(entries)) 43 44 for i, entry := range entries { 45 var index *sqlbase.IndexDescriptor 46 var isSecondaryIndex bool 47 48 if entry.indexIdx > 0 { 49 index = &entry.tableDesc.Indexes[entry.indexIdx-1] 50 isSecondaryIndex = true 51 } else { 52 index = &entry.tableDesc.PrimaryIndex 53 } 54 55 fetcherArgs[i] = FetcherTableArgs{ 56 Spans: entry.spans, 57 Desc: entry.tableDesc, 58 Index: index, 59 ColIdxMap: entry.tableDesc.ColumnIdxMap(), 60 IsSecondaryIndex: isSecondaryIndex, 61 Cols: entry.tableDesc.Columns, 62 ValNeededForCol: entry.valNeededForCol, 63 } 64 } 65 return fetcherArgs 66 } 67 68 func initFetcher( 69 entries []initFetcherArgs, reverseScan bool, alloc *sqlbase.DatumAlloc, 70 ) (fetcher *Fetcher, err error) { 71 fetcher = &Fetcher{} 72 73 fetcherCodec := keys.SystemSQLCodec 74 fetcherArgs := makeFetcherArgs(entries) 75 76 if err := fetcher.Init( 77 fetcherCodec, 78 reverseScan, 79 sqlbase.ScanLockingStrength_FOR_NONE, 80 false, /* returnRangeInfo */ 81 false, /* isCheck */ 82 alloc, 83 fetcherArgs..., 84 ); err != nil { 85 return nil, err 86 } 87 88 return fetcher, nil 89 } 90 91 type fetcherEntryArgs struct { 92 tableName string 93 indexName string // Specify if this entry is an index 94 indexIdx int // 0 for primary index (default) 95 modFactor int // Useful modulo to apply for value columns 96 schema string 97 interleaveSchema string // Specify if this entry is to be interleaved into another table 98 indexSchema string // Specify if this entry is to be created as an index 99 nRows int 100 nCols int // Number of columns in the table 101 nVals int // Number of values requested from scan 102 valNeededForCol util.FastIntSet 103 genValue sqlutils.GenRowFn 104 } 105 106 func TestNextRowSingle(t *testing.T) { 107 defer leaktest.AfterTest(t)() 108 ctx := context.Background() 109 110 s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 111 defer s.Stopper().Stop(ctx) 112 113 tables := map[string]fetcherEntryArgs{ 114 "t1": { 115 modFactor: 42, 116 nRows: 1337, 117 nCols: 2, 118 }, 119 "t2": { 120 modFactor: 13, 121 nRows: 2014, 122 nCols: 2, 123 }, 124 "norows": { 125 modFactor: 10, 126 nRows: 0, 127 nCols: 2, 128 }, 129 "onerow": { 130 modFactor: 10, 131 nRows: 1, 132 nCols: 2, 133 }, 134 } 135 136 // Initialize tables first. 137 for tableName, table := range tables { 138 sqlutils.CreateTable( 139 t, sqlDB, tableName, 140 "k INT PRIMARY KEY, v INT", 141 table.nRows, 142 sqlutils.ToRowFn(sqlutils.RowIdxFn, sqlutils.RowModuloFn(table.modFactor)), 143 ) 144 } 145 146 alloc := &sqlbase.DatumAlloc{} 147 148 // We try to read rows from each table. 149 for tableName, table := range tables { 150 t.Run(tableName, func(t *testing.T) { 151 tableDesc := sqlbase.GetImmutableTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, tableName) 152 153 var valNeededForCol util.FastIntSet 154 valNeededForCol.AddRange(0, table.nCols-1) 155 156 args := []initFetcherArgs{ 157 { 158 tableDesc: tableDesc, 159 indexIdx: 0, 160 valNeededForCol: valNeededForCol, 161 }, 162 } 163 164 rf, err := initFetcher(args, false /*reverseScan*/, alloc) 165 if err != nil { 166 t.Fatal(err) 167 } 168 169 if err := rf.StartScan( 170 context.Background(), 171 kv.NewTxn(ctx, kvDB, 0), 172 roachpb.Spans{tableDesc.IndexSpan(keys.SystemSQLCodec, tableDesc.PrimaryIndex.ID)}, 173 false, /*limitBatches*/ 174 0, /*limitHint*/ 175 false, /*traceKV*/ 176 ); err != nil { 177 t.Fatal(err) 178 } 179 180 count := 0 181 182 expectedVals := [2]int64{1, 1} 183 for { 184 datums, desc, index, err := rf.NextRowDecoded(context.Background()) 185 if err != nil { 186 t.Fatal(err) 187 } 188 if datums == nil { 189 break 190 } 191 192 count++ 193 194 if desc.ID != tableDesc.ID || index.ID != tableDesc.PrimaryIndex.ID { 195 t.Fatalf( 196 "unexpected row retrieved from fetcher.\nnexpected: table %s - index %s\nactual: table %s - index %s", 197 tableDesc.Name, tableDesc.PrimaryIndex.Name, 198 desc.Name, index.Name, 199 ) 200 } 201 202 if table.nCols != len(datums) { 203 t.Fatalf("expected %d columns, got %d columns", table.nCols, len(datums)) 204 } 205 206 for i, expected := range expectedVals { 207 actual := int64(*datums[i].(*tree.DInt)) 208 if expected != actual { 209 t.Fatalf("unexpected value for row %d, col %d.\nexpected: %d\nactual: %d", count, i, expected, actual) 210 } 211 } 212 213 expectedVals[0]++ 214 expectedVals[1]++ 215 // Value column is in terms of a modulo. 216 expectedVals[1] %= int64(table.modFactor) 217 } 218 219 if table.nRows != count { 220 t.Fatalf("expected %d rows, got %d rows", table.nRows, count) 221 } 222 }) 223 } 224 } 225 226 func TestNextRowBatchLimiting(t *testing.T) { 227 defer leaktest.AfterTest(t)() 228 ctx := context.Background() 229 230 s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 231 defer s.Stopper().Stop(ctx) 232 233 tables := map[string]fetcherEntryArgs{ 234 "t1": { 235 modFactor: 42, 236 nRows: 1337, 237 nCols: 2, 238 }, 239 "t2": { 240 modFactor: 13, 241 nRows: 2014, 242 nCols: 2, 243 }, 244 "norows": { 245 modFactor: 10, 246 nRows: 0, 247 nCols: 2, 248 }, 249 "onerow": { 250 modFactor: 10, 251 nRows: 1, 252 nCols: 2, 253 }, 254 } 255 256 // Initialize tables first. 257 for tableName, table := range tables { 258 sqlutils.CreateTable( 259 t, sqlDB, tableName, 260 "k INT PRIMARY KEY, v INT, FAMILY f1 (k), FAMILY f2(v)", 261 table.nRows, 262 sqlutils.ToRowFn(sqlutils.RowIdxFn, sqlutils.RowModuloFn(table.modFactor)), 263 ) 264 } 265 266 alloc := &sqlbase.DatumAlloc{} 267 268 // We try to read rows from each table. 269 for tableName, table := range tables { 270 t.Run(tableName, func(t *testing.T) { 271 tableDesc := sqlbase.GetImmutableTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, tableName) 272 273 var valNeededForCol util.FastIntSet 274 valNeededForCol.AddRange(0, table.nCols-1) 275 276 args := []initFetcherArgs{ 277 { 278 tableDesc: tableDesc, 279 indexIdx: 0, 280 valNeededForCol: valNeededForCol, 281 }, 282 } 283 284 rf, err := initFetcher(args, false /*reverseScan*/, alloc) 285 if err != nil { 286 t.Fatal(err) 287 } 288 289 if err := rf.StartScan( 290 context.Background(), 291 kv.NewTxn(ctx, kvDB, 0), 292 roachpb.Spans{tableDesc.IndexSpan(keys.SystemSQLCodec, tableDesc.PrimaryIndex.ID)}, 293 true, /*limitBatches*/ 294 10, /*limitHint*/ 295 false, /*traceKV*/ 296 ); err != nil { 297 t.Fatal(err) 298 } 299 300 count := 0 301 302 expectedVals := [2]int64{1, 1} 303 for { 304 datums, desc, index, err := rf.NextRowDecoded(context.Background()) 305 if err != nil { 306 t.Fatal(err) 307 } 308 if datums == nil { 309 break 310 } 311 312 count++ 313 314 if desc.ID != tableDesc.ID || index.ID != tableDesc.PrimaryIndex.ID { 315 t.Fatalf( 316 "unexpected row retrieved from fetcher.\nnexpected: table %s - index %s\nactual: table %s - index %s", 317 tableDesc.Name, tableDesc.PrimaryIndex.Name, 318 desc.Name, index.Name, 319 ) 320 } 321 322 if table.nCols != len(datums) { 323 t.Fatalf("expected %d columns, got %d columns", table.nCols, len(datums)) 324 } 325 326 for i, expected := range expectedVals { 327 actual := int64(*datums[i].(*tree.DInt)) 328 if expected != actual { 329 t.Fatalf("unexpected value for row %d, col %d.\nexpected: %d\nactual: %d", count, i, expected, actual) 330 } 331 } 332 333 expectedVals[0]++ 334 expectedVals[1]++ 335 // Value column is in terms of a modulo. 336 expectedVals[1] %= int64(table.modFactor) 337 } 338 339 if table.nRows != count { 340 t.Fatalf("expected %d rows, got %d rows", table.nRows, count) 341 } 342 }) 343 } 344 } 345 346 // Regression test for #29374. Ensure that RowFetcher can handle multi-span 347 // fetches where individual batches end in the middle of a multi-column family 348 // row with not-null columns. 349 func TestNextRowPartialColumnFamily(t *testing.T) { 350 defer leaktest.AfterTest(t)() 351 ctx := context.Background() 352 353 s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 354 defer s.Stopper().Stop(ctx) 355 356 tableName := "t1" 357 table := fetcherEntryArgs{ 358 modFactor: 42, 359 nRows: 2, 360 nCols: 4, 361 } 362 363 // Initialize a table with multiple column families with some not null ones. 364 // We'll insert rows that contain nulls for the nullable column families, to 365 // trick the rowfetcher heuristic that multiplies the input batch size by the 366 // number of columns in the table. 367 sqlutils.CreateTable( 368 t, sqlDB, tableName, 369 ` 370 k INT PRIMARY KEY, a INT NOT NULL, b INT NOT NULL, c INT NULL, 371 FAMILY f1 (k), FAMILY f2(a), FAMILY f3(b), FAMILY f4(c), 372 INDEX(c) 373 `, 374 table.nRows, 375 sqlutils.ToRowFn(sqlutils.RowIdxFn, 376 sqlutils.RowModuloFn(table.modFactor), 377 sqlutils.RowModuloFn(table.modFactor), 378 ), 379 ) 380 381 alloc := &sqlbase.DatumAlloc{} 382 383 tableDesc := sqlbase.GetImmutableTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, tableName) 384 385 var valNeededForCol util.FastIntSet 386 valNeededForCol.AddRange(0, table.nCols-1) 387 388 args := []initFetcherArgs{ 389 { 390 tableDesc: tableDesc, 391 indexIdx: 0, 392 valNeededForCol: valNeededForCol, 393 }, 394 } 395 396 rf, err := initFetcher(args, false /*reverseScan*/, alloc) 397 if err != nil { 398 t.Fatal(err) 399 } 400 401 // Start a scan that has multiple input spans, to tickle the codepath that 402 // sees an "empty batch". When we have multiple input spans, the kv server 403 // will always return one response per input span. Make sure that the 404 // empty response that will be produced in the case where the first span 405 // does not end before the limit is satisfied doesn't cause the rowfetcher 406 // to think that a row has ended, and therefore have issues when it sees 407 // the next kvs from that row in isolation in the next batch. 408 409 // We'll make the first span go to some random key in the middle of the 410 // key space (by appending a number to the index's start key) and the 411 // second span go from that key to the end of the index. 412 indexSpan := tableDesc.IndexSpan(keys.SystemSQLCodec, tableDesc.PrimaryIndex.ID) 413 endKey := indexSpan.EndKey 414 midKey := encoding.EncodeUvarintAscending(indexSpan.Key, uint64(100)) 415 indexSpan.EndKey = midKey 416 417 if err := rf.StartScan( 418 context.Background(), 419 kv.NewTxn(ctx, kvDB, 0), 420 roachpb.Spans{indexSpan, 421 roachpb.Span{Key: midKey, EndKey: endKey}, 422 }, 423 true, /*limitBatches*/ 424 // Set a limitHint of 1 to more quickly end the first batch, causing a 425 // batch that ends between rows. 426 1, /*limitHint*/ 427 false, /*traceKV*/ 428 ); err != nil { 429 t.Fatal(err) 430 } 431 432 var count int 433 for { 434 // Just try to grab the row - we don't need to validate the contents 435 // in this test. 436 datums, _, _, err := rf.NextRowDecoded(context.Background()) 437 if err != nil { 438 t.Fatal(err) 439 } 440 if datums == nil { 441 break 442 } 443 count++ 444 } 445 446 if table.nRows != count { 447 t.Fatalf("expected %d rows, got %d rows", table.nRows, count) 448 } 449 } 450 451 // Secondary indexes contain extra values (the primary key of the primary index 452 // as well as STORING columns). 453 func TestNextRowSecondaryIndex(t *testing.T) { 454 defer leaktest.AfterTest(t)() 455 ctx := context.Background() 456 457 s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 458 defer s.Stopper().Stop(ctx) 459 460 // Modulo to use for s1, s2 storing columns. 461 storingMods := [2]int{7, 13} 462 // Number of NULL secondary index values. 463 nNulls := 20 464 465 tables := map[string]*fetcherEntryArgs{ 466 "nonunique": { 467 modFactor: 20, 468 schema: "p INT PRIMARY KEY, idx INT, s1 INT, s2 INT, INDEX i1 (idx)", 469 nRows: 422, 470 nCols: 4, 471 nVals: 2, 472 }, 473 "unique": { 474 // Must be > nRows since this value must be unique. 475 modFactor: 1000, 476 schema: "p INT PRIMARY KEY, idx INT, s1 INT, s2 INT, UNIQUE INDEX i1 (idx)", 477 nRows: 123, 478 nCols: 4, 479 nVals: 2, 480 }, 481 "nonuniquestoring": { 482 modFactor: 42, 483 schema: "p INT PRIMARY KEY, idx INT, s1 INT, s2 INT, INDEX i1 (idx) STORING (s1, s2)", 484 nRows: 654, 485 nCols: 4, 486 nVals: 4, 487 }, 488 "uniquestoring": { 489 // Must be > nRows since this value must be unique. 490 modFactor: 1000, 491 nRows: 555, 492 schema: "p INT PRIMARY KEY, idx INT, s1 INT, s2 INT, UNIQUE INDEX i1 (idx) STORING (s1, s2)", 493 nCols: 4, 494 nVals: 4, 495 }, 496 } 497 498 // Initialize the generate value functions. 499 tables["nonunique"].genValue = sqlutils.ToRowFn( 500 sqlutils.RowIdxFn, 501 sqlutils.RowModuloFn(tables["nonunique"].modFactor), 502 ) 503 tables["unique"].genValue = sqlutils.ToRowFn( 504 sqlutils.RowIdxFn, 505 sqlutils.RowModuloFn(tables["unique"].modFactor), 506 ) 507 tables["nonuniquestoring"].genValue = sqlutils.ToRowFn( 508 sqlutils.RowIdxFn, 509 sqlutils.RowModuloFn(tables["nonuniquestoring"].modFactor), 510 sqlutils.RowModuloFn(storingMods[0]), 511 sqlutils.RowModuloFn(storingMods[1]), 512 ) 513 tables["uniquestoring"].genValue = sqlutils.ToRowFn( 514 sqlutils.RowIdxFn, 515 sqlutils.RowModuloFn(tables["uniquestoring"].modFactor), 516 sqlutils.RowModuloFn(storingMods[0]), 517 sqlutils.RowModuloFn(storingMods[1]), 518 ) 519 520 // Add family definitions to each table. 521 tablesWithFamilies := make(map[string]*fetcherEntryArgs) 522 for tableName, table := range tables { 523 argCopy := *table 524 argCopy.schema = argCopy.schema + ", FAMILY (p), FAMILY (idx), FAMILY (s1), FAMILY (s2)" 525 familyName := tableName + "_with_families" 526 tablesWithFamilies[familyName] = &argCopy 527 } 528 for tableName, args := range tablesWithFamilies { 529 tables[tableName] = args 530 } 531 532 r := sqlutils.MakeSQLRunner(sqlDB) 533 // Initialize tables first. 534 for tableName, table := range tables { 535 sqlutils.CreateTable( 536 t, sqlDB, tableName, 537 table.schema, 538 table.nRows, 539 table.genValue, 540 ) 541 542 // Insert nNulls NULL secondary index values (this tests if 543 // we're properly decoding (UNIQUE) secondary index keys 544 // properly). 545 for i := 1; i <= nNulls; i++ { 546 r.Exec(t, fmt.Sprintf( 547 `INSERT INTO %s.%s VALUES (%d, NULL, %d, %d);`, 548 sqlutils.TestDB, 549 tableName, 550 table.nRows+i, 551 (table.nRows+i)%storingMods[0], 552 (table.nRows+i)%storingMods[1], 553 )) 554 } 555 table.nRows += nNulls 556 } 557 558 alloc := &sqlbase.DatumAlloc{} 559 // We try to read rows from each index. 560 for tableName, table := range tables { 561 t.Run(tableName, func(t *testing.T) { 562 tableDesc := sqlbase.GetImmutableTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, tableName) 563 564 var valNeededForCol util.FastIntSet 565 valNeededForCol.AddRange(0, table.nVals-1) 566 567 args := []initFetcherArgs{ 568 { 569 tableDesc: tableDesc, 570 // We scan from the first secondary index. 571 indexIdx: 1, 572 valNeededForCol: valNeededForCol, 573 }, 574 } 575 576 rf, err := initFetcher(args, false /*reverseScan*/, alloc) 577 if err != nil { 578 t.Fatal(err) 579 } 580 581 if err := rf.StartScan( 582 context.Background(), 583 kv.NewTxn(ctx, kvDB, 0), 584 roachpb.Spans{tableDesc.IndexSpan(keys.SystemSQLCodec, tableDesc.Indexes[0].ID)}, 585 false, /*limitBatches*/ 586 0, /*limitHint*/ 587 false, /*traceKV*/ 588 ); err != nil { 589 t.Fatal(err) 590 } 591 592 count := 0 593 nullCount := 0 594 var prevIdxVal int64 595 for { 596 datums, desc, index, err := rf.NextRowDecoded(context.Background()) 597 if err != nil { 598 t.Fatal(err) 599 } 600 if datums == nil { 601 break 602 } 603 604 count++ 605 606 if desc.ID != tableDesc.ID || index.ID != tableDesc.Indexes[0].ID { 607 t.Fatalf( 608 "unexpected row retrieved from fetcher.\nnexpected: table %s - index %s\nactual: table %s - index %s", 609 tableDesc.Name, tableDesc.Indexes[0].Name, 610 desc.Name, index.Name, 611 ) 612 } 613 614 if table.nCols != len(datums) { 615 t.Fatalf("expected %d columns, got %d columns", table.nCols, len(datums)) 616 } 617 618 // Verify that the correct # of values are returned. 619 numVals := 0 620 for _, datum := range datums { 621 if datum != tree.DNull { 622 numVals++ 623 } 624 } 625 626 // Some secondary index values can be NULL. We keep track 627 // of how many we encounter. 628 idxNull := datums[1] == tree.DNull 629 if idxNull { 630 nullCount++ 631 // It is okay to bump this up by one since we know 632 // this index value is suppose to be NULL. 633 numVals++ 634 } 635 636 if table.nVals != numVals { 637 t.Fatalf("expected %d non-NULL values, got %d", table.nVals, numVals) 638 } 639 640 id := int64(*datums[0].(*tree.DInt)) 641 // Verify the value in the value column is 642 // correct (if it is not NULL). 643 if !idxNull { 644 idx := int64(*datums[1].(*tree.DInt)) 645 if id%int64(table.modFactor) != idx { 646 t.Fatalf("for row id %d, expected %d value, got %d", id, id%int64(table.modFactor), idx) 647 } 648 649 // Index values must be fetched in 650 // non-decreasing order. 651 if prevIdxVal > idx { 652 t.Fatalf("index value unexpectedly decreased from %d to %d", prevIdxVal, idx) 653 } 654 prevIdxVal = idx 655 } 656 657 // We verify that the storing values are 658 // decoded correctly. 659 if tableName == "nonuniquestoring" || tableName == "uniquestoring" { 660 s1 := int64(*datums[2].(*tree.DInt)) 661 s2 := int64(*datums[3].(*tree.DInt)) 662 663 if id%int64(storingMods[0]) != s1 { 664 t.Fatalf("for row id %d, expected %d for s1 value, got %d", id, id%int64(storingMods[0]), s1) 665 } 666 if id%int64(storingMods[1]) != s2 { 667 t.Fatalf("for row id %d, expected %d for s2 value, got %d", id, id%int64(storingMods[1]), s2) 668 } 669 } 670 } 671 672 if table.nRows != count { 673 t.Fatalf("expected %d rows, got %d rows", table.nRows, count) 674 } 675 }) 676 } 677 } 678 679 // Appends all non-empty subsets of indices in [0, maxIdx). 680 func generateIdxSubsets(maxIdx int, subsets [][]int) [][]int { 681 if maxIdx < 0 { 682 return subsets 683 } 684 subsets = generateIdxSubsets(maxIdx-1, subsets) 685 curLength := len(subsets) 686 for i := 0; i < curLength; i++ { 687 // Keep original subsets by duplicating them. 688 dupe := make([]int, len(subsets[i])) 689 copy(dupe, subsets[i]) 690 subsets = append(subsets, dupe) 691 // Generate new subsets with the current index. 692 subsets[i] = append(subsets[i], maxIdx) 693 } 694 return append(subsets, []int{maxIdx}) 695 } 696 697 // We test reading rows from six tables in a database that contains two 698 // interleave hierarchies. 699 // The tables are structured as follows: 700 // parent1 701 // parent2 702 // child1 703 // grandchild1 704 // grandgrandchild1 705 // child2 706 // grandgrandchild1@ggc1_unique_idx 707 // parent3 708 // We test reading rows from every non-empty subset for completeness. 709 func TestNextRowInterleaved(t *testing.T) { 710 defer leaktest.AfterTest(t)() 711 ctx := context.Background() 712 713 s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 714 defer s.Stopper().Stop(ctx) 715 716 tableArgs := map[string]*fetcherEntryArgs{ 717 "parent1": { 718 tableName: "parent1", 719 modFactor: 12, 720 schema: "p1 INT PRIMARY KEY, v INT", 721 nRows: 100, 722 nCols: 2, 723 nVals: 2, 724 }, 725 "parent2": { 726 tableName: "parent2", 727 modFactor: 3, 728 schema: "p2 INT PRIMARY KEY, v INT", 729 nRows: 400, 730 nCols: 2, 731 nVals: 2, 732 }, 733 "child1": { 734 tableName: "child1", 735 modFactor: 5, 736 schema: "p2 INT, c1 INT, v INT, PRIMARY KEY (p2, c1)", 737 interleaveSchema: "parent2 (p2)", 738 // child1 has more rows than parent2, thus some parent2 739 // rows will have multiple child1. 740 nRows: 500, 741 nCols: 3, 742 nVals: 3, 743 }, 744 "grandchild1": { 745 tableName: "grandchild1", 746 modFactor: 7, 747 schema: "p2 INT, c1 INT, gc1 INT, v INT, PRIMARY KEY (p2, c1, gc1)", 748 interleaveSchema: "child1 (p2, c1)", 749 nRows: 2000, 750 nCols: 4, 751 nVals: 4, 752 }, 753 "grandgrandchild1": { 754 tableName: "grandgrandchild1", 755 modFactor: 12, 756 schema: "p2 INT, c1 INT, gc1 INT, ggc1 INT, v INT, PRIMARY KEY (p2, c1, gc1, ggc1)", 757 interleaveSchema: "grandchild1 (p2, c1, gc1)", 758 nRows: 350, 759 nCols: 5, 760 nVals: 5, 761 }, 762 "child2": { 763 tableName: "child2", 764 modFactor: 42, 765 schema: "p2 INT, c2 INT, v INT, PRIMARY KEY (p2, c2)", 766 interleaveSchema: "parent2 (p2)", 767 // child2 has less rows than parent2, thus not all 768 // parent2 rows will have a nested child2 row. 769 nRows: 100, 770 nCols: 3, 771 nVals: 3, 772 }, 773 "parent3": { 774 tableName: "parent3", 775 modFactor: 42, 776 schema: "p3 INT PRIMARY KEY, v INT", 777 nRows: 1, 778 nCols: 2, 779 nVals: 2, 780 }, 781 } 782 783 for _, table := range tableArgs { 784 table.valNeededForCol.AddRange(0, table.nVals-1) 785 } 786 787 // Initialize generating value functions for each table. 788 tableArgs["parent1"].genValue = sqlutils.ToRowFn( 789 sqlutils.RowIdxFn, 790 sqlutils.RowModuloFn(tableArgs["parent1"].modFactor), 791 ) 792 793 tableArgs["parent2"].genValue = sqlutils.ToRowFn( 794 sqlutils.RowIdxFn, 795 sqlutils.RowModuloFn(tableArgs["parent2"].modFactor), 796 ) 797 798 tableArgs["child1"].genValue = sqlutils.ToRowFn( 799 // Foreign key needs a shifted modulo. 800 sqlutils.RowModuloShiftedFn(tableArgs["parent2"].nRows), 801 sqlutils.RowIdxFn, 802 sqlutils.RowModuloFn(tableArgs["child1"].modFactor), 803 ) 804 805 tableArgs["grandchild1"].genValue = sqlutils.ToRowFn( 806 // Foreign keys need a shifted modulo. 807 sqlutils.RowModuloShiftedFn( 808 tableArgs["child1"].nRows, 809 tableArgs["parent2"].nRows, 810 ), 811 sqlutils.RowModuloShiftedFn(tableArgs["child1"].nRows), 812 sqlutils.RowIdxFn, 813 sqlutils.RowModuloFn(tableArgs["grandchild1"].modFactor), 814 ) 815 816 tableArgs["grandgrandchild1"].genValue = sqlutils.ToRowFn( 817 // Foreign keys need a shifted modulo. 818 sqlutils.RowModuloShiftedFn( 819 tableArgs["grandchild1"].nRows, 820 tableArgs["child1"].nRows, 821 tableArgs["parent2"].nRows, 822 ), 823 sqlutils.RowModuloShiftedFn( 824 tableArgs["grandchild1"].nRows, 825 tableArgs["child1"].nRows, 826 ), 827 sqlutils.RowModuloShiftedFn(tableArgs["grandchild1"].nRows), 828 sqlutils.RowIdxFn, 829 sqlutils.RowModuloFn(tableArgs["grandgrandchild1"].modFactor), 830 ) 831 832 tableArgs["child2"].genValue = sqlutils.ToRowFn( 833 // Foreign key needs a shifted modulo. 834 sqlutils.RowModuloShiftedFn(tableArgs["parent2"].nRows), 835 sqlutils.RowIdxFn, 836 sqlutils.RowModuloFn(tableArgs["child2"].modFactor), 837 ) 838 839 tableArgs["parent3"].genValue = sqlutils.ToRowFn( 840 sqlutils.RowIdxFn, 841 sqlutils.RowModuloFn(tableArgs["parent3"].modFactor), 842 ) 843 844 ggc1idx := *tableArgs["grandgrandchild1"] 845 // This is only possible since nrows(ggc1) < nrows(p2) thus c1 is 846 // unique. 847 ggc1idx.indexSchema = fmt.Sprintf( 848 `CREATE UNIQUE INDEX ggc1_unique_idx ON %s.grandgrandchild1 (p2) INTERLEAVE IN PARENT %s.parent2 (p2);`, 849 sqlutils.TestDB, 850 sqlutils.TestDB, 851 ) 852 ggc1idx.indexName = "ggc1_unique_idx" 853 ggc1idx.indexIdx = 1 854 // Last column v (idx 4) is not stored in this index. 855 ggc1idx.valNeededForCol = ggc1idx.valNeededForCol.Copy() 856 ggc1idx.valNeededForCol.Remove(4) 857 ggc1idx.nVals = 4 858 859 // We need an ordering of the tables in order to execute the interleave 860 // DDL statements. 861 interleaveEntries := []fetcherEntryArgs{ 862 *tableArgs["parent1"], 863 *tableArgs["parent2"], 864 *tableArgs["child1"], 865 *tableArgs["grandchild1"], 866 *tableArgs["grandgrandchild1"], 867 *tableArgs["child2"], 868 ggc1idx, 869 *tableArgs["parent3"], 870 } 871 872 for _, table := range interleaveEntries { 873 if table.indexSchema != "" { 874 // Create interleaved secondary indexes. 875 r := sqlutils.MakeSQLRunner(sqlDB) 876 r.Exec(t, table.indexSchema) 877 } else { 878 // Create tables (primary indexes). 879 sqlutils.CreateTableInterleaved( 880 t, sqlDB, table.tableName, 881 table.schema, 882 table.interleaveSchema, 883 table.nRows, 884 table.genValue, 885 ) 886 } 887 } 888 889 alloc := &sqlbase.DatumAlloc{} 890 // Retrieve rows from every non-empty subset of the tables/indexes. 891 for _, idxs := range generateIdxSubsets(len(interleaveEntries)-1, nil) { 892 // Initialize our subset of tables/indexes. 893 entries := make([]*fetcherEntryArgs, len(idxs)) 894 testNames := make([]string, len(entries)) 895 for i, idx := range idxs { 896 entries[i] = &interleaveEntries[idx] 897 testNames[i] = entries[i].tableName 898 // Use the index name instead if we're scanning an index. 899 if entries[i].indexName != "" { 900 testNames[i] = entries[i].indexName 901 } 902 } 903 904 testName := strings.Join(testNames, "-") 905 906 t.Run(testName, func(t *testing.T) { 907 // Initialize the RowFetcher. 908 args := make([]initFetcherArgs, len(entries)) 909 lookupSpans := make([]roachpb.Span, len(entries)) 910 // Used during NextRow to see if tableID << 32 | 911 // indexID (key) are with what we initialize 912 // RowFetcher. 913 idLookups := make(map[uint64]*fetcherEntryArgs, len(entries)) 914 for i, entry := range entries { 915 tableDesc := sqlbase.GetImmutableTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, entry.tableName) 916 var indexID sqlbase.IndexID 917 if entry.indexIdx == 0 { 918 indexID = tableDesc.PrimaryIndex.ID 919 } else { 920 indexID = tableDesc.Indexes[entry.indexIdx-1].ID 921 } 922 idLookups[idLookupKey(tableDesc.ID, indexID)] = entry 923 924 // We take every entry's index span (primary or 925 // secondary) and use it to start our scan. 926 lookupSpans[i] = tableDesc.IndexSpan(keys.SystemSQLCodec, indexID) 927 928 args[i] = initFetcherArgs{ 929 tableDesc: tableDesc, 930 indexIdx: entry.indexIdx, 931 valNeededForCol: entry.valNeededForCol, 932 spans: roachpb.Spans{lookupSpans[i]}, 933 } 934 } 935 936 lookupSpans, _ = roachpb.MergeSpans(lookupSpans) 937 938 rf, err := initFetcher(args, false /*reverseScan*/, alloc) 939 if err != nil { 940 t.Fatal(err) 941 } 942 943 if err := rf.StartScan( 944 context.Background(), 945 kv.NewTxn(ctx, kvDB, 0), 946 lookupSpans, 947 false, /*limitBatches*/ 948 0, /*limitHint*/ 949 false, /*traceKV*/ 950 ); err != nil { 951 t.Fatal(err) 952 } 953 954 // Running count of rows processed for each table-index. 955 count := make(map[string]int, len(entries)) 956 957 for { 958 datums, desc, index, err := rf.NextRowDecoded(context.Background()) 959 if err != nil { 960 t.Fatal(err) 961 } 962 if datums == nil { 963 break 964 } 965 966 entry, found := idLookups[idLookupKey(desc.ID, index.ID)] 967 if !found { 968 t.Fatalf( 969 "unexpected row from table %s - index %s", 970 desc.Name, index.Name, 971 ) 972 } 973 974 tableIdxName := fmt.Sprintf("%s@%s", entry.tableName, entry.indexName) 975 count[tableIdxName]++ 976 977 // Check that the correct # of columns is returned. 978 if entry.nCols != len(datums) { 979 t.Fatalf("for table %s expected %d columns, got %d columns", tableIdxName, entry.nCols, len(datums)) 980 } 981 982 // Verify that the correct # of values are returned. 983 numVals := 0 984 for _, datum := range datums { 985 if datum != tree.DNull { 986 numVals++ 987 } 988 } 989 if entry.nVals != numVals { 990 t.Fatalf("for table %s expected %d non-NULL values, got %d", tableIdxName, entry.nVals, numVals) 991 } 992 993 // Verify the value in the value column is 994 // correct if it is requested. 995 if entry.nVals == entry.nCols { 996 id := int64(*datums[entry.nCols-2].(*tree.DInt)) 997 val := int64(*datums[entry.nCols-1].(*tree.DInt)) 998 999 if id%int64(entry.modFactor) != val { 1000 t.Fatalf("for table %s row id %d, expected %d value, got %d", tableIdxName, id, id%int64(entry.modFactor), val) 1001 } 1002 } 1003 } 1004 1005 for _, entry := range entries { 1006 lookup := fmt.Sprintf("%s@%s", entry.tableName, entry.indexName) 1007 1008 actual, ok := count[lookup] 1009 if !ok { 1010 t.Errorf("no rows were retrieved for table %s, expected %d rows", entry.tableName, entry.nRows) 1011 continue 1012 } 1013 1014 if entry.nRows != actual { 1015 t.Errorf("for table %s expected %d rows, got %d rows", entry.tableName, entry.nRows, actual) 1016 } 1017 } 1018 }) 1019 } 1020 } 1021 1022 func TestRowFetcherReset(t *testing.T) { 1023 defer leaktest.AfterTest(t)() 1024 ctx := context.Background() 1025 1026 s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 1027 defer s.Stopper().Stop(ctx) 1028 sqlutils.CreateTable( 1029 t, sqlDB, "foo", 1030 "k INT PRIMARY KEY, v INT", 1031 0, 1032 sqlutils.ToRowFn(sqlutils.RowIdxFn, sqlutils.RowModuloFn(1)), 1033 ) 1034 tableDesc := sqlbase.GetImmutableTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, "foo") 1035 var valNeededForCol util.FastIntSet 1036 valNeededForCol.AddRange(0, 1) 1037 args := []initFetcherArgs{ 1038 { 1039 tableDesc: tableDesc, 1040 indexIdx: 0, 1041 valNeededForCol: valNeededForCol, 1042 }, 1043 } 1044 da := sqlbase.DatumAlloc{} 1045 fetcher, err := initFetcher(args, false, &da) 1046 if err != nil { 1047 t.Fatal(err) 1048 } 1049 1050 resetFetcher, err := initFetcher(args, false, &da) 1051 if err != nil { 1052 t.Fatal(err) 1053 } 1054 1055 resetFetcher.Reset() 1056 if len(resetFetcher.tables) != 0 || cap(resetFetcher.tables) != 1 { 1057 t.Fatal("Didn't find saved slice:", resetFetcher.tables) 1058 } 1059 1060 // Now re-init the reset fetcher and make sure its the same as the fetcher we 1061 // didn't reset. 1062 1063 fetcherArgs := makeFetcherArgs(args) 1064 if err := resetFetcher.Init( 1065 keys.SystemSQLCodec, false /*reverse*/, 0 /* todo */, false /* returnRangeInfo */, false /* isCheck */, &da, fetcherArgs..., 1066 ); err != nil { 1067 t.Fatal(err) 1068 } 1069 1070 if !reflect.DeepEqual(resetFetcher, fetcher) { 1071 t.Fatal("unequal before and after reset", resetFetcher, fetcher) 1072 } 1073 1074 } 1075 1076 func idLookupKey(tableID TableID, indexID sqlbase.IndexID) uint64 { 1077 return (uint64(tableID) << 32) | uint64(indexID) 1078 } 1079 1080 func TestFetcherUninitialized(t *testing.T) { 1081 // Regression test for #39013: make sure it's okay to call GetRangesInfo and 1082 // GetBytesReader even before the fetcher was fully initialized. 1083 var fetcher Fetcher 1084 1085 assert.Nil(t, fetcher.GetRangesInfo()) 1086 assert.Zero(t, fetcher.GetBytesRead()) 1087 }