github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/distsql_plan_join_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 sql 12 13 import ( 14 "context" 15 "fmt" 16 "reflect" 17 "strconv" 18 "strings" 19 "testing" 20 21 "github.com/cockroachdb/cockroach/pkg/base" 22 "github.com/cockroachdb/cockroach/pkg/keys" 23 "github.com/cockroachdb/cockroach/pkg/kv" 24 "github.com/cockroachdb/cockroach/pkg/roachpb" 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/encoding" 29 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 30 "github.com/cockroachdb/errors" 31 ) 32 33 func setTestEqColForSide(colName string, side *scanNode, equalityIndices *[]int) error { 34 colFound := false 35 36 for i, leftCol := range side.cols { 37 if colName == leftCol.Name { 38 *equalityIndices = append(*equalityIndices, i) 39 colFound = true 40 break 41 } 42 } 43 if !colFound { 44 return errors.Errorf("column %s not found in %s", colName, side.desc.Name) 45 } 46 return nil 47 } 48 49 func setTestEqCols(n *joinNode, colNames []string) error { 50 left := n.left.plan.(*scanNode) 51 right := n.right.plan.(*scanNode) 52 53 n.pred = &joinPredicate{} 54 n.mergeJoinOrdering = nil 55 56 for _, colName := range colNames { 57 if colName == "" { 58 continue 59 } 60 61 if err := setTestEqColForSide(colName, left, &n.pred.leftEqualityIndices); err != nil { 62 return err 63 } 64 if err := setTestEqColForSide(colName, right, &n.pred.rightEqualityIndices); err != nil { 65 return err 66 } 67 } 68 69 n.mergeJoinOrdering = computeMergeJoinOrdering( 70 left.reqOrdering, 71 right.reqOrdering, 72 n.pred.leftEqualityIndices, 73 n.pred.rightEqualityIndices, 74 ) 75 76 return nil 77 } 78 79 func genPermutations(slice []string) [][]string { 80 if len(slice) == 0 { 81 return [][]string{{}} 82 } 83 84 var out [][]string 85 for i, str := range slice { 86 recurse := append([]string{}, slice[:i]...) 87 recurse = append(recurse, slice[i+1:]...) 88 for _, subperms := range genPermutations(recurse) { 89 out = append(out, append([]string{str}, subperms...)) 90 } 91 } 92 93 return out 94 } 95 96 var tableNames = map[string]bool{ 97 "parent1": true, 98 "child1": true, 99 "grandchild1": true, 100 "child2": true, 101 "parent2": true, 102 } 103 104 // Format for any key: 105 // <table-name>/<index-id>/<index-col1>/.../#/<table-name>/<index-id>/.... 106 func encodeTestKey(kvDB *kv.DB, keyStr string) (roachpb.Key, error) { 107 key := keys.SystemSQLCodec.TenantPrefix() 108 tokens := strings.Split(keyStr, "/") 109 110 for _, tok := range tokens { 111 // Encode the table ID if the token is a table name. 112 if tableNames[tok] { 113 desc := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, tok) 114 key = encoding.EncodeUvarintAscending(key, uint64(desc.ID)) 115 continue 116 } 117 118 // Interleaved sentinel. 119 if tok == "#" { 120 key = encoding.EncodeNotNullDescending(key) 121 continue 122 } 123 124 // Assume any other value is an unsigned integer. 125 tokInt, err := strconv.ParseUint(tok, 10, 64) 126 if err != nil { 127 return nil, err 128 } 129 key = encoding.EncodeUvarintAscending(key, tokInt) 130 } 131 132 return key, nil 133 } 134 135 func decodeTestKey(kvDB *kv.DB, key roachpb.Key) (string, error) { 136 var out []byte 137 138 keyStr := roachpb.PrettyPrintKey(nil /* valDirs */, key) 139 tokens := strings.Split(keyStr, "/")[1:] 140 141 for i := 0; i < len(tokens); i++ { 142 tok := tokens[i] 143 // We know for certain the next token is the table ID. Need 144 // to convert into a table name. 145 if tok == "Table" || tok == "#" { 146 if tok == "#" { 147 out = append(out, []byte("#/")...) 148 } 149 150 descID, err := strconv.ParseUint(tokens[i+1], 10, 64) 151 if err != nil { 152 return "", err 153 } 154 155 if err := kvDB.Txn(context.Background(), func(ctx context.Context, txn *kv.Txn) error { 156 desc, err := sqlbase.GetTableDescFromID(context.Background(), txn, keys.SystemSQLCodec, sqlbase.ID(descID)) 157 if err != nil { 158 return err 159 } 160 161 out = append(out, []byte(desc.Name)...) 162 return nil 163 }); err != nil { 164 return "", err 165 } 166 167 // We read an extra token for the table ID. 168 i++ 169 } else { 170 // Encode anything else as is. 171 out = append(out, []byte(tok)...) 172 } 173 174 out = append(out, '/') 175 } 176 177 // Omit the last '/'. 178 return string(out[:len(out)-1]), nil 179 } 180 181 // See CreateTestInterleavedHierarchy for the longest chain used for the short 182 // format. 183 var shortFormTables = [3]string{"parent1", "child1", "grandchild1"} 184 185 // shortToLongKey converts the short key format preferred in test cases 186 // /1/#/3/4 187 // to its long form required by parseTestkey 188 // parent1/1/1/#/child1/1/3/4 189 func shortToLongKey(short string) string { 190 tableOrder := shortFormTables 191 curTableIdx := 0 192 193 var long []byte 194 tokens := strings.Split(short, "/") 195 // Verify short format starts with '/'. 196 if tokens[0] != "" { 197 panic("missing '/' token at the beginning of short format") 198 } 199 // Skip the first element since short format has starting '/'. 200 tokens = tokens[1:] 201 202 // Always append parent1. 203 long = append(long, []byte(fmt.Sprintf("%s/1/", tableOrder[curTableIdx]))...) 204 curTableIdx++ 205 206 for _, tok := range tokens { 207 // New interleaved table and primary keys follow. 208 if tok == "#" { 209 if curTableIdx >= len(tableOrder) { 210 panic("too many '#' tokens specified in short format (max 2 for child1 and grandchild1)") 211 } 212 213 long = append(long, []byte(fmt.Sprintf("#/%s/1/", tableOrder[curTableIdx]))...) 214 curTableIdx++ 215 216 continue 217 } 218 219 long = append(long, []byte(fmt.Sprintf("%s/", tok))...) 220 } 221 222 // Remove the last '/'. 223 return string(long[:len(long)-1]) 224 } 225 226 func TestUseInterleavedJoin(t *testing.T) { 227 defer leaktest.AfterTest(t)() 228 229 s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 230 defer s.Stopper().Stop(context.Background()) 231 232 sqlutils.CreateTestInterleavedHierarchy(t, sqlDB) 233 234 // Only test cases on the full interleave prefix between the two 235 // tables should return true. 236 for _, tc := range []struct { 237 table1 string 238 table2 string 239 eqCols string 240 expected bool 241 }{ 242 // Refer to comment above CreateTestInterleavedHierarchy for 243 // table schemas. 244 245 // Simple parent-child case. 246 // parent1-child1 share interleave prefix (pid1). 247 {"parent1", "child1", "pid1", true}, 248 {"parent1", "child1", "pid1,v", false}, 249 {"parent1", "child1", "", false}, 250 {"parent1", "child1", "v", false}, 251 // Parent-grandchild case. 252 // parent1-grandchild1 share interleave prefix (pid1). 253 {"parent1", "grandchild1", "pid1", true}, 254 {"parent1", "grandchild1", "pid1,v", false}, 255 {"parent1", "grandchild1", "", false}, 256 {"parent1", "grandchild1", "v", false}, 257 // Multiple-column interleave prefix. 258 // child1-grandchild1 share interleave prefix (pid1, cid1, 259 // cid2). 260 {"child1", "grandchild1", "pid1,cid1,cid2", true}, 261 {"child1", "grandchild1", "pid1,cid1,cid2,v", false}, 262 {"child1", "grandchild1", "", false}, 263 {"child1", "grandchild1", "v", false}, 264 // TODO(richardwu): update these once prefix/subset of 265 // interleave prefixes are permitted. 266 {"child1", "grandchild1", "cid1", false}, 267 {"child1", "grandchild1", "cid2", false}, 268 {"child1", "grandchild1", "cid1,v", false}, 269 {"child1", "grandchild1", "cid2,v", false}, 270 {"child1", "grandchild1", "cid1,cid2", false}, 271 {"child1", "grandchild1", "cid1,cid2,v", false}, 272 {"child1", "grandchild1", "pid1,cid1", false}, 273 {"child1", "grandchild1", "pid1,cid2", false}, 274 {"child1", "grandchild1", "pid1,cid1,v", false}, 275 {"child1", "grandchild1", "pid1,cid2,v", false}, 276 // Common ancestor example. 277 {"child1", "child2", "", false}, 278 // TODO(richardwu): update this when common ancestor 279 // interleaved joins are possible. 280 {"child1", "child2", "pid1", false}, 281 } { 282 // Run the subtests with the tables in both positions (left and 283 // right). 284 for i := 0; i < 2; i++ { 285 // Run every permutation of the equality columns (just 286 // to ensure mergeJoinOrdering is invariant since we 287 // rely on it to correspond with the primary index of 288 // the ancestor). 289 eqCols := strings.Split(tc.eqCols, ",") 290 for _, colNames := range genPermutations(eqCols) { 291 testName := fmt.Sprintf("%s-%s-%s", tc.table1, tc.table2, strings.Join(colNames, ",")) 292 t.Run(testName, func(t *testing.T) { 293 join, err := newTestJoinNode(kvDB, tc.table1, tc.table2) 294 if err != nil { 295 t.Fatal(err) 296 } 297 join.joinType = sqlbase.InnerJoin 298 299 if err := setTestEqCols(join, colNames); err != nil { 300 t.Fatal(err) 301 } 302 join.mergeJoinOrdering = computeMergeJoinOrdering( 303 planReqOrdering(join.left.plan), 304 planReqOrdering(join.right.plan), 305 join.pred.leftEqualityIndices, 306 join.pred.rightEqualityIndices, 307 ) 308 309 actual := useInterleavedJoin(join) 310 311 if tc.expected != actual { 312 t.Errorf("expected useInterleaveJoin to return %t, actual %t", tc.expected, actual) 313 } 314 }) 315 } 316 // Rerun the same subtests but flip the tables 317 tc.table1, tc.table2 = tc.table2, tc.table1 318 } 319 } 320 321 // Test that a join from an interleaved column to a non-interleaved column 322 // doesn't get planned as an interleaved table join, even if the 323 // non-interleaved column is constant and is given a merge join ordering. 324 t.Run("MismatchedJoin", func(t *testing.T) { 325 join, err := newTestJoinNode(kvDB, "parent1", "child1") 326 if err != nil { 327 t.Fatal(err) 328 } 329 join.joinType = sqlbase.InnerJoin 330 331 join.pred = &joinPredicate{} 332 join.mergeJoinOrdering = nil 333 if err := setTestEqColForSide("pid1", join.left.plan.(*scanNode), &join.pred.leftEqualityIndices); err != nil { 334 t.Fatal(err) 335 } 336 if err := setTestEqColForSide("v", join.right.plan.(*scanNode), &join.pred.rightEqualityIndices); err != nil { 337 t.Fatal(err) 338 } 339 // Set the merge join ordering to idx 0 - this says that the column `pid1` 340 // and `v` have the same ordering. This can be true if `v` has been 341 // constrained to a constant value. We shouldn't plan an interleaved table 342 // join in this case, even though the left equality columns are a prefix 343 // of the interleaved columns, because the right equality columns are not 344 // part of the interleaved columns. 345 // See issue #25838 for a case where this could happen. 346 join.mergeJoinOrdering = sqlbase.ColumnOrdering{ 347 sqlbase.ColumnOrderInfo{ 348 ColIdx: 0, 349 Direction: encoding.Ascending, 350 }, 351 } 352 353 actual := useInterleavedJoin(join) 354 355 if actual { 356 t.Errorf("expected useInterleaveJoin to return %t, actual %t", false, actual) 357 } 358 }) 359 } 360 361 func TestMaximalJoinPrefix(t *testing.T) { 362 defer leaktest.AfterTest(t)() 363 364 s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 365 defer s.Stopper().Stop(context.Background()) 366 367 sqlutils.CreateTestInterleavedHierarchy(t, sqlDB) 368 369 testCases := []struct { 370 table1 string 371 table2 string 372 input string 373 expected string 374 truncated bool 375 }{ 376 // Key is already an ancestor prefix. 377 {"parent1", "child1", "/2", "/2", false}, 378 379 // Key of descendant child1. 380 {"parent1", "child1", "/2/#/3/4", "/2", true}, 381 382 // Partial key of descendant child1 (only cid1, missing cid2). 383 {"parent1", "child1", "/2/#/1/3", "/2", true}, 384 385 // Key of descendant grandchild1. 386 {"parent1", "grandchild1", "/2/#/3/4/#/5", "/2", true}, 387 388 // Key of some descendant child1 is still a descendant key 389 // of parent1. 390 {"parent1", "grandchild1", "/2/#/3/4", "/2", true}, 391 392 // Key is already an ancestor prefix of child1. 393 {"child1", "grandchild1", "/2/#/3/4", "/2/#/3/4", false}, 394 395 // Key of descendant grandchild1 with ancestor child1: 396 // prefix of parent1 retained. 397 {"child1", "grandchild1", "/2/#/3/4/#/5", "/2/#/3/4", true}, 398 399 // TODO(richardwu): prefix/subset joins and sibiling joins. 400 } 401 402 for testIdx, tc := range testCases { 403 t.Run(strconv.Itoa(testIdx), func(t *testing.T) { 404 join, err := newTestJoinNode(kvDB, tc.table1, tc.table2) 405 if err != nil { 406 t.Fatal(err) 407 } 408 409 input, err := encodeTestKey(kvDB, shortToLongKey(tc.input)) 410 if err != nil { 411 t.Fatal(err) 412 } 413 414 ancestor, descendant := join.interleavedNodes() 415 416 // Compute maximal join prefix. 417 actualKey, truncated, err := maximalJoinPrefix(ancestor, descendant, input) 418 if err != nil { 419 t.Fatal(err) 420 } 421 422 actual, err := decodeTestKey(kvDB, actualKey) 423 if err != nil { 424 t.Fatal(err) 425 } 426 427 expected := shortToLongKey(tc.expected) 428 429 if expected != actual { 430 t.Errorf("unexpected maximal join prefix.\nexpected:\t%s\nactual:\t%s", expected, actual) 431 } 432 433 if tc.truncated != truncated { 434 t.Errorf("expected maximalJoinPrefix to return %t for truncated, got %t", tc.truncated, truncated) 435 } 436 }) 437 } 438 } 439 440 type testPartition struct { 441 node roachpb.NodeID 442 spans [][2]string 443 } 444 445 func makeSpanPartitions(kvDB *kv.DB, testParts []testPartition) ([]SpanPartition, error) { 446 spanParts := make([]SpanPartition, len(testParts)) 447 448 for i, testPart := range testParts { 449 spanParts[i].Node = testPart.node 450 for _, span := range testPart.spans { 451 start, err := encodeTestKey(kvDB, shortToLongKey(span[0])) 452 if err != nil { 453 return nil, err 454 } 455 456 end, err := encodeTestKey(kvDB, shortToLongKey(span[1])) 457 if err != nil { 458 return nil, err 459 } 460 461 spanParts[i].Spans = append( 462 spanParts[i].Spans, 463 roachpb.Span{Key: start, EndKey: end}, 464 ) 465 } 466 } 467 468 return spanParts, nil 469 } 470 471 func TestAlignInterleavedSpans(t *testing.T) { 472 defer leaktest.AfterTest(t)() 473 474 s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 475 defer s.Stopper().Stop(context.Background()) 476 477 sqlutils.CreateTestInterleavedHierarchy(t, sqlDB) 478 479 testCases := []struct { 480 table1 string 481 table2 string 482 483 ancsParts []testPartition 484 descParts []testPartition 485 expected []testPartition 486 }{ 487 // Test that child1 spans get mapped to their corresponding 488 // parent1 spans and the descendant span is recursively split 489 // to satisfaction. 490 { 491 table1: "parent1", table2: "child1", 492 493 ancsParts: []testPartition{ 494 // Test that the next parent row after the 495 // last is computed properly if the end key 496 // is not a parent1 key. 497 {1, [][2]string{{"/1", "/2/#/5"}}}, 498 // End key is a parent1 key. 499 {2, [][2]string{{"/3", "/4"}}}, 500 {3, [][2]string{{"/4", "/5"}}}, 501 }, 502 503 descParts: []testPartition{ 504 {4, [][2]string{{"/1/#/7", "/4/#/8"}}}, 505 }, 506 507 expected: []testPartition{ 508 {1, [][2]string{{"/1/#/7", "/3"}}}, 509 {2, [][2]string{{"/3", "/4"}}}, 510 {3, [][2]string{{"/4", "/4/#/8"}}}, 511 }, 512 }, 513 514 // Test that child spans do not get remapped if they're already 515 // on the correct node. 516 { 517 table1: "parent1", table2: "child1", 518 519 ancsParts: []testPartition{ 520 {1, [][2]string{{"/1", "/3"}}}, 521 }, 522 523 descParts: []testPartition{ 524 {1, [][2]string{{"/1/#/7", "/2/#/8"}}}, 525 }, 526 527 expected: []testPartition{ 528 {1, [][2]string{{"/1/#/7", "/2/#/8"}}}, 529 }, 530 }, 531 532 // Test that even if the parent1 span does not entirely contain 533 // the child1 span, it gets mapped to the relevant parent row 534 // correctly. 535 { 536 table1: "parent1", table2: "child1", 537 538 ancsParts: []testPartition{ 539 {1, [][2]string{{"/1", "/1/#/5"}}}, 540 }, 541 542 descParts: []testPartition{ 543 {2, [][2]string{{"/1/#/7", "/1/#/8"}}}, 544 }, 545 546 expected: []testPartition{ 547 {1, [][2]string{{"/1/#/7", "/1/#/8"}}}, 548 }, 549 }, 550 551 // Test that multiple child spans mapped to the same nodes 552 // are merged and properly ordered. 553 { 554 table1: "parent1", table2: "child1", 555 556 ancsParts: []testPartition{ 557 // Multiple spans within each partition. 558 {1, [][2]string{ 559 {"/1", "/1/#/1"}, 560 {"/1/#/1", "/2"}, 561 }}, 562 {2, [][2]string{ 563 {"/2", "/2/#/1/1/#/8"}, 564 {"/2/#/1/1/#/8", "/2/#/3/5"}, 565 {"/2/#/3/5", "/3"}, 566 }}, 567 }, 568 569 descParts: []testPartition{ 570 {1, [][2]string{ 571 // pid1=1 rows should map to node 1. 572 {"/1/#/1", "/1/#/2"}, 573 {"/1/#/7", "/1/#/9"}, 574 // pid1=2 rows should map to node 2. 575 {"/2/#/1", "/2/#/2"}, 576 {"/2/#/5", "/2/#/8"}, 577 }}, 578 {2, [][2]string{ 579 // pid1=1 rows should map to node 1. 580 {"/1/#/2", "/1/#/7"}, 581 // pid1=2 rows should map to node 2. 582 // Overlaps with previous spans in node 583 // 1. 584 {"/2/#/2", "/2/#/6"}, 585 }}, 586 {3, [][2]string{ 587 // pid1=1 rows should map to node 1. 588 {"/1/#/11", "/1/#/13"}, 589 // pid1=2 rows should map to node 2. 590 {"/2/#/11", "/2/#/15"}, 591 }}, 592 // pid1=1 and pid=2 rows in a span. 593 {4, [][2]string{{"/1/#/15", "/2/#/0/7/#/1"}}}, 594 }, 595 596 expected: []testPartition{ 597 {1, [][2]string{ 598 {"/1/#/1", "/1/#/9"}, 599 {"/1/#/11", "/1/#/13"}, 600 {"/1/#/15", "/2"}, 601 }}, 602 {2, [][2]string{ 603 {"/2", "/2/#/0/7/#/1"}, 604 {"/2/#/1", "/2/#/8"}, 605 {"/2/#/11", "/2/#/15"}, 606 }}, 607 }, 608 }, 609 610 // Test with child1 spans having parent1 keys split points. 611 { 612 table1: "parent1", table2: "child1", 613 614 ancsParts: []testPartition{ 615 {1, [][2]string{{"/1", "/2"}}}, 616 {2, [][2]string{{"/2", "/3"}}}, 617 {3, [][2]string{{"/3", "/4"}}}, 618 }, 619 620 descParts: []testPartition{ 621 {1, [][2]string{{"/2", "/3"}}}, 622 // Technically not possible for two partitions 623 // to have the same span. 624 {3, [][2]string{{"/1", "/2"}}}, 625 {6, [][2]string{{"/1", "/2"}}}, 626 }, 627 628 expected: []testPartition{ 629 {1, [][2]string{{"/1", "/2"}}}, 630 {2, [][2]string{{"/2", "/3"}}}, 631 }, 632 }, 633 634 // Test child1 span that do not need to be remapped are still 635 // split by the next parent1 row after the last. 636 { 637 table1: "parent1", table2: "child1", 638 639 ancsParts: []testPartition{ 640 {1, [][2]string{{"/1", "/2"}}}, 641 {2, [][2]string{{"/2", "/3"}}}, 642 }, 643 644 descParts: []testPartition{ 645 {1, [][2]string{{"/1", "/3"}}}, 646 }, 647 648 expected: []testPartition{ 649 {1, [][2]string{{"/1", "/2"}}}, 650 {2, [][2]string{{"/2", "/3"}}}, 651 }, 652 }, 653 654 // Test that child1 spans that have no corresponding parent1 655 // span are not remapped. 656 { 657 table1: "parent1", table2: "child1", 658 659 ancsParts: []testPartition{ 660 {1, [][2]string{{"/1", "/2"}}}, 661 {2, [][2]string{{"/2", "/3"}}}, 662 }, 663 664 descParts: []testPartition{ 665 // No corresponding parent span: not remapped. 666 {1, [][2]string{{"/4", "/5"}}}, 667 // Partially no corresponding parent span. 668 {2, [][2]string{{"/2", "/4"}}}, 669 }, 670 671 expected: []testPartition{ 672 {1, [][2]string{{"/4", "/5"}}}, 673 {2, [][2]string{{"/2", "/4"}}}, 674 }, 675 }, 676 677 // Test parent-grandchild example. 678 { 679 table1: "parent1", table2: "grandchild1", 680 681 ancsParts: []testPartition{ 682 {1, [][2]string{{"/1", "/2/#/1/1/#/5"}}}, 683 {2, [][2]string{{"/3", "/4"}}}, 684 {3, [][2]string{{"/4", "/5"}}}, 685 }, 686 687 descParts: []testPartition{ 688 {4, [][2]string{{"/1/#/42/37/#/5", "/2/#/1/1/#/5"}}}, 689 // Partial child1 key (instead of grandchild1). 690 {5, [][2]string{{"/3/#/1", "/4/#/1/1/#/5"}}}, 691 }, 692 693 expected: []testPartition{ 694 {1, [][2]string{{"/1/#/42/37/#/5", "/2/#/1/1/#/5"}}}, 695 {2, [][2]string{{"/3/#/1", "/4"}}}, 696 {3, [][2]string{{"/4", "/4/#/1/1/#/5"}}}, 697 }, 698 }, 699 700 // Test child-grandchild example. 701 { 702 table1: "child1", table2: "grandchild1", 703 704 ancsParts: []testPartition{ 705 {1, [][2]string{{"/1/#/2/3", "/2"}}}, 706 {2, [][2]string{{"/2/#/2/3", "/2/#/5/2"}}}, 707 {3, [][2]string{{"/4", "/5"}}}, 708 {4, [][2]string{{"/2/#/5/2", "/2/#/6"}}}, 709 }, 710 711 descParts: []testPartition{ 712 // Starts before any of the child1 spans. 713 {5, [][2]string{{"/1", "/1/#/5/6"}}}, 714 // Starts in between the first and second 715 // child1 spans. 716 {6, [][2]string{{"/2/#/1/2", "/2/#/5/2/#/7"}}}, 717 }, 718 719 expected: []testPartition{ 720 {1, [][2]string{{"/1/#/2/3", "/1/#/5/6"}}}, 721 {2, [][2]string{{"/2/#/2/3", "/2/#/5/2"}}}, 722 {4, [][2]string{{"/2/#/5/2", "/2/#/5/2/#/7"}}}, 723 {5, [][2]string{{"/1", "/1/#/2/3"}}}, 724 {6, [][2]string{{"/2/#/1/2", "/2/#/2/3"}}}, 725 }, 726 }, 727 } 728 729 for testIdx, tc := range testCases { 730 t.Run(strconv.Itoa(testIdx), func(t *testing.T) { 731 join, err := newTestJoinNode(kvDB, tc.table1, tc.table2) 732 if err != nil { 733 t.Fatal(err) 734 } 735 736 ancsParts, err := makeSpanPartitions(kvDB, tc.ancsParts) 737 if err != nil { 738 t.Fatal(err) 739 } 740 741 descParts, err := makeSpanPartitions(kvDB, tc.descParts) 742 if err != nil { 743 t.Fatal(err) 744 } 745 746 actual, err := alignInterleavedSpans(join, ancsParts, descParts) 747 if err != nil { 748 t.Fatal(err) 749 } 750 751 expected, err := makeSpanPartitions(kvDB, tc.expected) 752 if err != nil { 753 t.Fatal(err) 754 } 755 756 if !reflect.DeepEqual(expected, actual) { 757 t.Errorf("unexpected partition results after aligning.\nexpected:\t%v\nactual:\t%v", expected, actual) 758 } 759 }) 760 } 761 } 762 763 // computeMergeJoinOrdering determines if merge-join can be used to perform a join. 764 // 765 // It takes the orderings of the two data sources that are to be joined on a set 766 // of equality columns (the join condition is that the value for the column 767 // colA[i] equals the value for column colB[i]). 768 // 769 // If merge-join can be used, the function returns a ColumnOrdering that refers 770 // to the equality columns by their index in colA/colB. Specifically column i in 771 // the returned ordering refers to column colA[i] for A and colB[i] for B. This 772 // is the ordering that must be used by the merge-join. 773 // 774 // The returned ordering can be partial, i.e. only contains a subset of the 775 // equality columns. 776 func computeMergeJoinOrdering( 777 a, b sqlbase.ColumnOrdering, colA, colB []int, 778 ) sqlbase.ColumnOrdering { 779 if len(colA) != len(colB) { 780 panic(fmt.Sprintf("invalid column lists %v; %v", colA, colB)) 781 } 782 var result sqlbase.ColumnOrdering 783 for i := 0; i < len(a) && i < len(b); i++ { 784 found := false 785 if a[i].Direction != b[i].Direction { 786 break 787 } 788 for j := range colA { 789 if colA[j] == a[i].ColIdx && colB[j] == b[i].ColIdx { 790 result = append(result, sqlbase.ColumnOrderInfo{ 791 ColIdx: j, 792 Direction: a[i].Direction, 793 }) 794 found = true 795 break 796 } 797 } 798 if !found { 799 break 800 } 801 } 802 return result 803 } 804 805 func TestInterleavedNodes(t *testing.T) { 806 defer leaktest.AfterTest(t)() 807 808 s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 809 defer s.Stopper().Stop(context.Background()) 810 811 sqlutils.CreateTestInterleavedHierarchy(t, sqlDB) 812 813 for _, tc := range []struct { 814 table1 string 815 table2 string 816 ancestor string 817 descendant string 818 }{ 819 // Refer to comment above CreateTestInterleavedHierarchy for 820 // table schemas. 821 822 {"parent1", "child1", "parent1", "child1"}, 823 {"parent1", "child2", "parent1", "child2"}, 824 {"parent1", "grandchild1", "parent1", "grandchild1"}, 825 {"child1", "child2", "", ""}, 826 {"child1", "grandchild1", "child1", "grandchild1"}, 827 {"child2", "grandchild1", "", ""}, 828 {"parent1", "parent2", "", ""}, 829 {"parent2", "child1", "", ""}, 830 {"parent2", "grandchild1", "", ""}, 831 {"parent2", "child2", "", ""}, 832 } { 833 // Run the subtests with the tables in both positions (left 834 // and right). 835 for i := 0; i < 2; i++ { 836 testName := fmt.Sprintf("%s-%s", tc.table1, tc.table2) 837 t.Run(testName, func(t *testing.T) { 838 join, err := newTestJoinNode(kvDB, tc.table1, tc.table2) 839 if err != nil { 840 t.Fatal(err) 841 } 842 843 ancestor, descendant := join.interleavedNodes() 844 845 if tc.ancestor == tc.descendant && tc.ancestor == "" { 846 if ancestor != nil || descendant != nil { 847 t.Errorf("expected ancestor and descendant to both be nil") 848 } 849 return 850 } 851 852 if ancestor == nil || descendant == nil { 853 t.Fatalf("expected ancestor and descendant to not be nil") 854 } 855 856 if tc.ancestor != ancestor.desc.Name || tc.descendant != descendant.desc.Name { 857 t.Errorf( 858 "unexpected ancestor and descendant nodes.\nexpected: %s (ancestor), %s (descendant)\nactual: %s (ancestor), %s (descendant)", 859 tc.ancestor, tc.descendant, 860 ancestor.desc.Name, descendant.desc.Name, 861 ) 862 } 863 }) 864 // Rerun the same subtests but flip the tables 865 tc.table1, tc.table2 = tc.table2, tc.table1 866 } 867 } 868 }