changes_on_top_of_v32_0001.patch
text/x-patch
Filename: changes_on_top_of_v32_0001.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v32
| File | + | − |
|---|---|---|
| src/backend/optimizer/path/joinrels.c | 22 | 21 |
| src/backend/partitioning/partbounds.c | 51 | 14 |
| src/include/nodes/pathnodes.h | 6 | 1 |
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 77e6ff5376..e14a2e51c2 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1378,7 +1378,7 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
Assert(joinrel->consider_partitionwise_join);
/*
- * We can not perform partition-wise join if either of the joining
+ * We can not perform partitionwise join if either of the joining
* relations is not partitioned.
*/
if (!IS_PARTITIONED_REL(rel1) || !IS_PARTITIONED_REL(rel2))
@@ -1399,8 +1399,9 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
/*
* If we don't have the partition bounds for the join rel yet, try to
- * create it along with pairs of partitions to be joined; else generate
- * those using the partitioning info for the join rel we already have.
+ * compute those along with pairs of partitions to be joined; else generate
+ * the pairs using the partitioning info of the join relation we already
+ * have.
*/
if (joinrel->nparts == -1)
{
@@ -1412,16 +1413,17 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
Assert(joinrel->part_rels == NULL);
/*
- * See if the partition bounds for inputs are exactly the same, in
- * which case we don't need to work hard: partitions with the same
- * partition indexes will form join pairs, and the join rel will have
- * the same partition bounds as inputs; otherwise try to merge the
- * partition bounds along with generating join pairs.
+ * See if the partition bounds of the joining relations are exactly the
+ * same, in which case we don't need to work hard: partitions with the
+ * same partition indexes will form join pairs, and the join relation
+ * will have the same partition bounds as the joining relations;
+ * otherwise try to merge the partition bounds along with generating
+ * join pairs.
*
- * Even if one or both inputs have merged partition bounds, it'd be
- * possible for the partition bounds to be exactly the same, but it
- * seems unlikely to be worth the cycles to check; do this check only
- * if both inputs have non-merged partition bounds.
+ * Even if one or both the joining relations have merged partition
+ * bounds, it'd be possible for the partition bounds to be exactly the
+ * same, but it seems unlikely to be worth the cycles to check; do this
+ * check only if both inputs have non-merged partition bounds.
*/
if (!rel1->merged &&
!rel2->merged &&
@@ -1467,10 +1469,11 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
Assert(joinrel->part_rels);
/*
- * If the partition bounds for the join rel are not merged ones,
- * inputs are guaranteed to have the same partition bounds, so
- * partitions with the same partition indexes will form join pairs;
- * else let get_matching_part_pairs() do the work.
+ * If the partition bounds of the join relation were computed by
+ * merging the bounds of the joining relations, generate the pairs of
+ * joining partitions by matching their relids. Nothing to do otherwise
+ * since the partitions at same cardinal positions form the joining
+ * pairs.
*/
if (joinrel->merged)
{
@@ -1847,8 +1850,6 @@ get_matching_part_pairs(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *rel1, RelOptInfo *rel2,
List **parts1, List **parts2)
{
- bool rel1_is_simple = IS_SIMPLE_REL(rel1);
- bool rel2_is_simple = IS_SIMPLE_REL(rel2);
int cnt_parts;
*parts1 = NIL;
@@ -1863,7 +1864,7 @@ get_matching_part_pairs(PlannerInfo *root, RelOptInfo *joinrel,
Relids child_relids2;
/*
- * If this segment of the join is empty, it means that this segment
+ * If the current partition of the join is empty, it means that this segment
* was ignored when previously creating child-join paths for it in
* try_partitionwise_join() as it would not contribute to the join
* result, due to one or both inputs being empty; add NULL to each of
@@ -1893,7 +1894,7 @@ get_matching_part_pairs(PlannerInfo *root, RelOptInfo *joinrel,
* joined even when planning partitionwise joins of rel1, meaning that
* the child rel would have been built by the time we get here.
*/
- if (rel1_is_simple)
+ if (IS_SIMPLE_REL(rel1))
{
int varno = bms_singleton_member(child_relids1);
@@ -1914,7 +1915,7 @@ get_matching_part_pairs(PlannerInfo *root, RelOptInfo *joinrel,
/*
* Get a child rel for rel2 with the relids. See above comments.
*/
- if (rel2_is_simple)
+ if (IS_SIMPLE_REL(rel2))
{
int varno = bms_singleton_member(child_relids2);
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 3c7b6030c0..f639e2c69b 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -3156,6 +3156,16 @@ partition_bounds_merge(int partnatts,
switch (strategy)
{
case PARTITION_STRATEGY_HASH:
+ /*
+ * When the modulo of used for partition mapping (i.e. maximum
+ * number of has partitions) is same, their partition bounds are
+ * same and do not need merging. If the maximum number of
+ * partitions is different for both the joining relations, there's
+ * high probability that one partition on one side will join with
+ * multiple partitions on the other side. So exact partition bounds
+ * match will work in most of the cases. The cases otherwise are
+ * not so common to spend the effort in coding and planning.
+ */
merged_bounds = NULL;
break;
@@ -3324,7 +3334,7 @@ compare_range_partitions(int partnatts, FmgrInfo *partsupfuncs,
/*
* get_merged_range_bounds
- * Given the bounds of range partitions to be join, determine the range
+ * Given the bounds of range partitions to be joined, determine the range
* bounds of the merged partition produced from the range partitions
*
* *merged_lb and *merged_ub are set to the lower and upper bounds of the
@@ -3534,7 +3544,7 @@ partition_range_bounds_merge(int partnatts, FmgrInfo *partsupfuncs,
* the side which finishes earlier has an extra partition with lower
* and upper bounds higher than any other partition of the unfinished
* side. That way we advance the partitions on that side till all of
- * them are exhausted.
+ * them are exhausted.
*/
if (outer_part == -1)
{
@@ -4093,8 +4103,13 @@ map_and_merge_partitions(PartitionMap *outer_map, PartitionMap *inner_map,
if (!outer_merged && !inner_merged)
{
/*
- * Note that we will fix the larger index that have been added to
- * the merged_indexes list so far in fix_merged_indexes().
+ * Both the inner and outer partitions have an empty partition on
+ * the other side as their joining partner. But now that each of
+ * them has found a non-empty joining partner we should re-map
+ * those to a single partition in the join. We use lower of the
+ * two indexes to avoid any holes being created by re-mapping.
+ * Also, it keeps the partition index array in partition bounds
+ * roughly sorted.
*/
if (outer_merged_index < inner_merged_index)
{
@@ -4165,6 +4180,12 @@ map_and_merge_partitions(PartitionMap *outer_map, PartitionMap *inner_map,
/*
* merge_partition_with_dummy
*
+ * The caller thinks that the partition at the given index does not have a
+ * partition in the other relation or the joining partition is empty. In such a
+ * case we assign a temporary index (indicated by merged flag in the map) for
+ * the resulting partition in the join. In case the given partition finds a
+ * non-empty partner latter we will adjust the mapping again.
+ *
* *next_index is incremented.
*/
static int
@@ -4206,8 +4227,8 @@ process_outer_partition(PartitionMap *outer_map,
/*
* If the inner side has the default partition, the outer partition has to
- * be joined with the default partition; try merging them. Otherwise, we
- * should in an outer join, in which case the outer partition has to be
+ * be joined with the default partition; try merging them. Otherwise
+ * it's an outer join, in which case the outer partition has to be
* scanned all the way anyway; if the outer partition is already mapped to
* a merged partition, get it, otherwise create a new merged partition by
* merging the outer partition with a dummy partition.
@@ -4217,9 +4238,11 @@ process_outer_partition(PartitionMap *outer_map,
Assert(inner_default >= 0);
/*
- * If the outer side has the default partition as well, we need to
- * merge the default partitions (see merge_default_partitions()); give
- * up on it.
+ * If the outer side has the default partition as well, the default
+ * partition from inner side will have two matching partitions on the
+ * outer side: the default partition on the outer side and the given
+ * outer partition. Partitionwise join doesn't handle this scenario
+ * yet.
*/
if (outer_has_default)
return -1;
@@ -4231,9 +4254,13 @@ process_outer_partition(PartitionMap *outer_map,
return -1;
/*
- * If this is a FULL join, the merged partition would act as the
- * default partition of the join; record the index in *default_index
- * if not done yet.
+ * If this is a FULL join, both the sides act as outer side. Since the
+ * inner partition is a default partition, it will have partition key
+ * values which are not covered by any other partition. In join result
+ * as well, the resulting partition will hold partition key values that
+ * no other partition holds. Thus the merged partition would act as the
+ * default partition of the join; record the index in *default_index if
+ * not done yet.
*/
if (jointype == JOIN_FULL)
{
@@ -4293,8 +4320,11 @@ process_inner_partition(PartitionMap *outer_map,
/*
* If the inner side has the default partition as well, we need to
- * merge the default partitions (see merge_default_partitions()); give
- * up on it.
+ * merge the default partitions (see merge_default_partitions()). So
+ * there will two inner partitions, given inner partition and the
+ * default inner partition, that will map to the default outer
+ * partition. Partitionwise join does not support this case, so give up
+ * on it.
*/
if (inner_has_default)
return -1;
@@ -4590,6 +4620,13 @@ merge_default_partitions(PartitionMap *outer_map,
}
else
{
+ /*
+ * We should have already given up if we found that both the inner and
+ * outer relations have default partitions and either of them had a
+ * partition without a matching non-default partition on the other
+ * side. See process_outer_partition() and process_inner_partition()
+ * for details.
+ */
Assert(outer_has_default && inner_has_default);
Assert(outer_merged_index == -1);
Assert(inner_merged_index == -1);
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 1545877d8c..bdf8ac4bce 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -720,7 +720,12 @@ typedef struct RelOptInfo
/* used for partitioned relations */
PartitionScheme part_scheme; /* Partitioning scheme. */
- int nparts; /* number of partitions */
+ int nparts; /* number of partitions.
+ * 0 for a relation with no partitions,
+ * > 0 indicates actual number of partitions
+ * -1 for a relation whose number of partitions
+ * is not yet known.
+ */
struct PartitionBoundInfoData *boundinfo; /* Partition bounds */
bool merged; /* true if partition bounds were created by
* partition_bounds_merge() */