@@ -45,6 +45,13 @@ static void try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1,
45
45
static SpecialJoinInfo * build_child_join_sjinfo (PlannerInfo * root ,
46
46
SpecialJoinInfo * parent_sjinfo ,
47
47
Relids left_relids , Relids right_relids );
48
+ static void compute_partition_bounds (PlannerInfo * root , RelOptInfo * rel1 ,
49
+ RelOptInfo * rel2 , RelOptInfo * joinrel ,
50
+ SpecialJoinInfo * parent_sjinfo ,
51
+ List * * parts1 , List * * parts2 );
52
+ static void get_matching_part_pairs (PlannerInfo * root , RelOptInfo * joinrel ,
53
+ RelOptInfo * rel1 , RelOptInfo * rel2 ,
54
+ List * * parts1 , List * * parts2 );
48
55
49
56
50
57
/*
@@ -1354,25 +1361,29 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
1354
1361
{
1355
1362
bool rel1_is_simple = IS_SIMPLE_REL (rel1 );
1356
1363
bool rel2_is_simple = IS_SIMPLE_REL (rel2 );
1357
- int nparts ;
1364
+ List * parts1 = NIL ;
1365
+ List * parts2 = NIL ;
1366
+ ListCell * lcr1 = NULL ;
1367
+ ListCell * lcr2 = NULL ;
1358
1368
int cnt_parts ;
1359
1369
1360
1370
/* Guard against stack overflow due to overly deep partition hierarchy. */
1361
1371
check_stack_depth ();
1362
1372
1363
1373
/* Nothing to do, if the join relation is not partitioned. */
1364
- if (! IS_PARTITIONED_REL ( joinrel ) )
1374
+ if (joinrel -> part_scheme == NULL || joinrel -> nparts == 0 )
1365
1375
return ;
1366
1376
1367
1377
/* The join relation should have consider_partitionwise_join set. */
1368
1378
Assert (joinrel -> consider_partitionwise_join );
1369
1379
1370
1380
/*
1371
- * Since this join relation is partitioned, all the base relations
1372
- * participating in this join must be partitioned and so are all the
1373
- * intermediate join relations.
1381
+ * We can not perform partitionwise join if either of the joining relations
1382
+ * is not partitioned.
1374
1383
*/
1375
- Assert (IS_PARTITIONED_REL (rel1 ) && IS_PARTITIONED_REL (rel2 ));
1384
+ if (!IS_PARTITIONED_REL (rel1 ) || !IS_PARTITIONED_REL (rel2 ))
1385
+ return ;
1386
+
1376
1387
Assert (REL_HAS_ALL_PART_PROPS (rel1 ) && REL_HAS_ALL_PART_PROPS (rel2 ));
1377
1388
1378
1389
/* The joining relations should have consider_partitionwise_join set. */
@@ -1386,42 +1397,51 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
1386
1397
Assert (joinrel -> part_scheme == rel1 -> part_scheme &&
1387
1398
joinrel -> part_scheme == rel2 -> part_scheme );
1388
1399
1389
- /*
1390
- * Since we allow partitionwise join only when the partition bounds of the
1391
- * joining relations exactly match, the partition bounds of the join
1392
- * should match those of the joining relations.
1393
- */
1394
- Assert (partition_bounds_equal (joinrel -> part_scheme -> partnatts ,
1395
- joinrel -> part_scheme -> parttyplen ,
1396
- joinrel -> part_scheme -> parttypbyval ,
1397
- joinrel -> boundinfo , rel1 -> boundinfo ));
1398
- Assert (partition_bounds_equal (joinrel -> part_scheme -> partnatts ,
1399
- joinrel -> part_scheme -> parttyplen ,
1400
- joinrel -> part_scheme -> parttypbyval ,
1401
- joinrel -> boundinfo , rel2 -> boundinfo ));
1400
+ Assert (!(joinrel -> partbounds_merged && (joinrel -> nparts <= 0 )));
1402
1401
1403
- nparts = joinrel -> nparts ;
1402
+ compute_partition_bounds (root , rel1 , rel2 , joinrel , parent_sjinfo ,
1403
+ & parts1 , & parts2 );
1404
+
1405
+ if (joinrel -> partbounds_merged )
1406
+ {
1407
+ lcr1 = list_head (parts1 );
1408
+ lcr2 = list_head (parts2 );
1409
+ }
1404
1410
1405
1411
/*
1406
1412
* Create child-join relations for this partitioned join, if those don't
1407
1413
* exist. Add paths to child-joins for a pair of child relations
1408
1414
* corresponding to the given pair of parent relations.
1409
1415
*/
1410
- for (cnt_parts = 0 ; cnt_parts < nparts ; cnt_parts ++ )
1416
+ for (cnt_parts = 0 ; cnt_parts < joinrel -> nparts ; cnt_parts ++ )
1411
1417
{
1412
- RelOptInfo * child_rel1 = rel1 -> part_rels [cnt_parts ];
1413
- RelOptInfo * child_rel2 = rel2 -> part_rels [cnt_parts ];
1414
- bool rel1_empty = (child_rel1 == NULL ||
1415
- IS_DUMMY_REL (child_rel1 ));
1416
- bool rel2_empty = (child_rel2 == NULL ||
1417
- IS_DUMMY_REL (child_rel2 ));
1418
+ RelOptInfo * child_rel1 ;
1419
+ RelOptInfo * child_rel2 ;
1420
+ bool rel1_empty ;
1421
+ bool rel2_empty ;
1418
1422
SpecialJoinInfo * child_sjinfo ;
1419
1423
List * child_restrictlist ;
1420
1424
RelOptInfo * child_joinrel ;
1421
1425
Relids child_joinrelids ;
1422
1426
AppendRelInfo * * appinfos ;
1423
1427
int nappinfos ;
1424
1428
1429
+ if (joinrel -> partbounds_merged )
1430
+ {
1431
+ child_rel1 = lfirst_node (RelOptInfo , lcr1 );
1432
+ child_rel2 = lfirst_node (RelOptInfo , lcr2 );
1433
+ lcr1 = lnext (parts1 , lcr1 );
1434
+ lcr2 = lnext (parts2 , lcr2 );
1435
+ }
1436
+ else
1437
+ {
1438
+ child_rel1 = rel1 -> part_rels [cnt_parts ];
1439
+ child_rel2 = rel2 -> part_rels [cnt_parts ];
1440
+ }
1441
+
1442
+ rel1_empty = (child_rel1 == NULL || IS_DUMMY_REL (child_rel1 ));
1443
+ rel2_empty = (child_rel2 == NULL || IS_DUMMY_REL (child_rel2 ));
1444
+
1425
1445
/*
1426
1446
* Check for cases where we can prove that this segment of the join
1427
1447
* returns no rows, due to one or both inputs being empty (including
@@ -1519,6 +1539,8 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
1519
1539
child_sjinfo ,
1520
1540
child_sjinfo -> jointype );
1521
1541
joinrel -> part_rels [cnt_parts ] = child_joinrel ;
1542
+ joinrel -> all_partrels = bms_add_members (joinrel -> all_partrels ,
1543
+ child_joinrel -> relids );
1522
1544
}
1523
1545
1524
1546
Assert (bms_equal (child_joinrel -> relids , child_joinrelids ));
@@ -1570,3 +1592,190 @@ build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,
1570
1592
1571
1593
return sjinfo ;
1572
1594
}
1595
+
1596
+ /*
1597
+ * compute_partition_bounds
1598
+ * Compute the partition bounds for a join rel from those for inputs
1599
+ */
1600
+ static void
1601
+ compute_partition_bounds (PlannerInfo * root , RelOptInfo * rel1 ,
1602
+ RelOptInfo * rel2 , RelOptInfo * joinrel ,
1603
+ SpecialJoinInfo * parent_sjinfo ,
1604
+ List * * parts1 , List * * parts2 )
1605
+ {
1606
+ /*
1607
+ * If we don't have the partition bounds for the join rel yet, try to
1608
+ * compute those along with pairs of partitions to be joined.
1609
+ */
1610
+ if (joinrel -> nparts == -1 )
1611
+ {
1612
+ PartitionScheme part_scheme = joinrel -> part_scheme ;
1613
+ PartitionBoundInfo boundinfo = NULL ;
1614
+ int nparts = 0 ;
1615
+
1616
+ Assert (joinrel -> boundinfo == NULL );
1617
+ Assert (joinrel -> part_rels == NULL );
1618
+
1619
+ /*
1620
+ * See if the partition bounds for inputs are exactly the same, in
1621
+ * which case we don't need to work hard: the join rel have the same
1622
+ * partition bounds as inputs, and the partitions with the same
1623
+ * cardinal positions form the pairs.
1624
+ *
1625
+ * Note: even in cases where one or both inputs have merged bounds,
1626
+ * it would be possible for both the bounds to be exactly the same, but
1627
+ * it seems unlikely to be worth the cycles to check.
1628
+ */
1629
+ if (!rel1 -> partbounds_merged &&
1630
+ !rel2 -> partbounds_merged &&
1631
+ rel1 -> nparts == rel2 -> nparts &&
1632
+ partition_bounds_equal (part_scheme -> partnatts ,
1633
+ part_scheme -> parttyplen ,
1634
+ part_scheme -> parttypbyval ,
1635
+ rel1 -> boundinfo , rel2 -> boundinfo ))
1636
+ {
1637
+ boundinfo = rel1 -> boundinfo ;
1638
+ nparts = rel1 -> nparts ;
1639
+ }
1640
+ else
1641
+ {
1642
+ /* Try merging the partition bounds for inputs. */
1643
+ boundinfo = partition_bounds_merge (part_scheme -> partnatts ,
1644
+ part_scheme -> partsupfunc ,
1645
+ part_scheme -> partcollation ,
1646
+ rel1 , rel2 ,
1647
+ parent_sjinfo -> jointype ,
1648
+ parts1 , parts2 );
1649
+ if (boundinfo == NULL )
1650
+ {
1651
+ joinrel -> nparts = 0 ;
1652
+ return ;
1653
+ }
1654
+ nparts = list_length (* parts1 );
1655
+ joinrel -> partbounds_merged = true;
1656
+ }
1657
+
1658
+ Assert (nparts > 0 );
1659
+ joinrel -> boundinfo = boundinfo ;
1660
+ joinrel -> nparts = nparts ;
1661
+ joinrel -> part_rels =
1662
+ (RelOptInfo * * ) palloc0 (sizeof (RelOptInfo * ) * nparts );
1663
+ }
1664
+ else
1665
+ {
1666
+ Assert (joinrel -> nparts > 0 );
1667
+ Assert (joinrel -> boundinfo );
1668
+ Assert (joinrel -> part_rels );
1669
+
1670
+ /*
1671
+ * If the join rel's partbounds_merged flag is true, it means inputs
1672
+ * are not guaranteed to have the same partition bounds, therefore we
1673
+ * can't assume that the partitions at the same cardinal positions form
1674
+ * the pairs; let get_matching_part_pairs() generate the pairs.
1675
+ * Otherwise, nothing to do since we can assume that.
1676
+ */
1677
+ if (joinrel -> partbounds_merged )
1678
+ {
1679
+ get_matching_part_pairs (root , joinrel , rel1 , rel2 ,
1680
+ parts1 , parts2 );
1681
+ Assert (list_length (* parts1 ) == joinrel -> nparts );
1682
+ Assert (list_length (* parts2 ) == joinrel -> nparts );
1683
+ }
1684
+ }
1685
+ }
1686
+
1687
+ /*
1688
+ * get_matching_part_pairs
1689
+ * Generate pairs of partitions to be joined from inputs
1690
+ */
1691
+ static void
1692
+ get_matching_part_pairs (PlannerInfo * root , RelOptInfo * joinrel ,
1693
+ RelOptInfo * rel1 , RelOptInfo * rel2 ,
1694
+ List * * parts1 , List * * parts2 )
1695
+ {
1696
+ bool rel1_is_simple = IS_SIMPLE_REL (rel1 );
1697
+ bool rel2_is_simple = IS_SIMPLE_REL (rel2 );
1698
+ int cnt_parts ;
1699
+
1700
+ * parts1 = NIL ;
1701
+ * parts2 = NIL ;
1702
+
1703
+ for (cnt_parts = 0 ; cnt_parts < joinrel -> nparts ; cnt_parts ++ )
1704
+ {
1705
+ RelOptInfo * child_joinrel = joinrel -> part_rels [cnt_parts ];
1706
+ RelOptInfo * child_rel1 ;
1707
+ RelOptInfo * child_rel2 ;
1708
+ Relids child_relids1 ;
1709
+ Relids child_relids2 ;
1710
+
1711
+ /*
1712
+ * If this segment of the join is empty, it means that this segment
1713
+ * was ignored when previously creating child-join paths for it in
1714
+ * try_partitionwise_join() as it would not contribute to the join
1715
+ * result, due to one or both inputs being empty; add NULL to each of
1716
+ * the given lists so that this segment will be ignored again in that
1717
+ * function.
1718
+ */
1719
+ if (!child_joinrel )
1720
+ {
1721
+ * parts1 = lappend (* parts1 , NULL );
1722
+ * parts2 = lappend (* parts2 , NULL );
1723
+ continue ;
1724
+ }
1725
+
1726
+ /*
1727
+ * Get a relids set of partition(s) involved in this join segment that
1728
+ * are from the rel1 side.
1729
+ */
1730
+ child_relids1 = bms_intersect (child_joinrel -> relids ,
1731
+ rel1 -> all_partrels );
1732
+ Assert (bms_num_members (child_relids1 ) == bms_num_members (rel1 -> relids ));
1733
+
1734
+ /*
1735
+ * Get a child rel for rel1 with the relids. Note that we should have
1736
+ * the child rel even if rel1 is a join rel, because in that case the
1737
+ * partitions specified in the relids would have matching/overlapping
1738
+ * boundaries, so the specified partitions should be considered as ones
1739
+ * to be joined when planning partitionwise joins of rel1, meaning that
1740
+ * the child rel would have been built by the time we get here.
1741
+ */
1742
+ if (rel1_is_simple )
1743
+ {
1744
+ int varno = bms_singleton_member (child_relids1 );
1745
+
1746
+ child_rel1 = find_base_rel (root , varno );
1747
+ }
1748
+ else
1749
+ child_rel1 = find_join_rel (root , child_relids1 );
1750
+ Assert (child_rel1 );
1751
+
1752
+ /*
1753
+ * Get a relids set of partition(s) involved in this join segment that
1754
+ * are from the rel2 side.
1755
+ */
1756
+ child_relids2 = bms_intersect (child_joinrel -> relids ,
1757
+ rel2 -> all_partrels );
1758
+ Assert (bms_num_members (child_relids2 ) == bms_num_members (rel2 -> relids ));
1759
+
1760
+ /*
1761
+ * Get a child rel for rel2 with the relids. See above comments.
1762
+ */
1763
+ if (rel2_is_simple )
1764
+ {
1765
+ int varno = bms_singleton_member (child_relids2 );
1766
+
1767
+ child_rel2 = find_base_rel (root , varno );
1768
+ }
1769
+ else
1770
+ child_rel2 = find_join_rel (root , child_relids2 );
1771
+ Assert (child_rel2 );
1772
+
1773
+ /*
1774
+ * The join of rel1 and rel2 is legal, so is the join of the child
1775
+ * rels obtained above; add them to the given lists as a join pair
1776
+ * producing this join segment.
1777
+ */
1778
+ * parts1 = lappend (* parts1 , child_rel1 );
1779
+ * parts2 = lappend (* parts2 , child_rel2 );
1780
+ }
1781
+ }
0 commit comments