Modify-partition-matching-algorithm-WIP.patch
application/octet-stream
Filename: Modify-partition-matching-algorithm-WIP.patch
Type: application/octet-stream
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
| File | + | − |
|---|---|---|
| src/backend/nodes/outfuncs.c | 1 | 0 |
| src/backend/optimizer/path/joinrels.c | 117 | 60 |
| src/backend/optimizer/util/inherit.c | 2 | 0 |
| src/backend/optimizer/util/relnode.c | 6 | 3 |
| src/backend/partitioning/partbounds.c | 23 | 67 |
| src/include/nodes/pathnodes.h | 2 | 0 |
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e6ce8e2110..a61b1741ec 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2281,6 +2281,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
WRITE_BOOL_FIELD(has_eclass_joins);
WRITE_BOOL_FIELD(consider_partitionwise_join);
WRITE_BITMAPSET_FIELD(top_parent_relids);
+ WRITE_BITMAPSET_FIELD(all_partrels);
WRITE_NODE_FIELD(partitioned_child_rels);
}
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 52abd8a12f..6607fae57b 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -48,6 +48,9 @@ static SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,
Relids left_relids, Relids right_relids);
static int match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel,
bool strict_op);
+static void get_matching_part_pairs(PlannerInfo *root, RelOptInfo *joinrel,
+ RelOptInfo *rel1, RelOptInfo *rel2,
+ List **parts1, List **parts2);
/*
@@ -1357,8 +1360,6 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
{
bool rel1_is_simple = IS_SIMPLE_REL(rel1);
bool rel2_is_simple = IS_SIMPLE_REL(rel2);
- PartitionScheme part_scheme;
- PartitionBoundInfo join_boundinfo;
List *parts1;
List *parts2;
ListCell *lc1;
@@ -1369,7 +1370,7 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
check_stack_depth();
/* Nothing to do, if the join relation is not partitioned. */
- if (joinrel->part_scheme == NULL)
+ if (joinrel->part_scheme == NULL || joinrel->nparts == 0)
return;
/* The join relation should have consider_partitionwise_join set. */
@@ -1394,51 +1395,51 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
*/
Assert(joinrel->part_scheme == rel1->part_scheme &&
joinrel->part_scheme == rel2->part_scheme);
- part_scheme = joinrel->part_scheme;
/*
- * Get the list of matching partitions to be joined along with the
- * partition bounds of the join relation. Because of the restrictions
- * imposed by partition matching algorithm, not every pair of joining
- * relations for this join will be able to use partition-wise join. But all
- * those pairs which can use partition-wise join will produce the same
- * partition bounds for the join relation.
+ * If we haven't created the partition bounds for the join relation yet,
+ * create it along with the the list of matching partitions to be joined;
+ * else generate the list of such partitions from the partitioning info
+ * for the join relation we already have.
*/
- join_boundinfo = partition_bounds_merge(part_scheme->partnatts,
- part_scheme->parttyplen,
- part_scheme->parttypbyval,
- part_scheme->partsupfunc,
- part_scheme->partcollation,
- rel1, rel2,
- parent_sjinfo->jointype,
- &parts1, &parts2);
-
- if (join_boundinfo == NULL)
- return;
-
- if (joinrel->boundinfo == NULL)
+ if (joinrel->nparts == -1)
{
- Assert(joinrel->nparts == 0 && joinrel->part_rels == NULL);
- joinrel->boundinfo = join_boundinfo;
- joinrel->nparts = list_length(parts1);
- Assert(joinrel->nparts == list_length(parts2));
+ PartitionBoundInfo boundinfo;
+ int nparts;
+
+ Assert(joinrel->boundinfo == NULL);
+ boundinfo = partition_bounds_merge(joinrel->part_scheme->partnatts,
+ joinrel->part_scheme->parttyplen,
+ joinrel->part_scheme->parttypbyval,
+ joinrel->part_scheme->partsupfunc,
+ joinrel->part_scheme->partcollation,
+ rel1, rel2,
+ parent_sjinfo->jointype,
+ &parts1, &parts2);
+ if (boundinfo == NULL)
+ {
+ joinrel->nparts = 0;
+ return;
+ }
+ joinrel->boundinfo = boundinfo;
+
+ Assert(list_length(parts1) == list_length(parts2));
+ nparts = list_length(parts1);
+ Assert(nparts > 0);
+ joinrel->nparts = nparts;
+ Assert(joinrel->part_rels == NULL);
joinrel->part_rels =
- (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *
- joinrel->nparts);
+ (RelOptInfo **) palloc0(sizeof(RelOptInfo *) * nparts);
}
else
{
- Assert(partition_bounds_equal(part_scheme->partnatts,
- part_scheme->parttyplen,
- part_scheme->parttypbyval,
- join_boundinfo, joinrel->boundinfo));
- /*
- * Every pair of joining relations should result in the same number
- * of child-joins.
- */
- Assert(joinrel->nparts == list_length(parts1));
- Assert(joinrel->nparts == list_length(parts2));
+ Assert(joinrel->nparts > 0);
+ Assert(joinrel->boundinfo);
Assert(joinrel->part_rels);
+
+ get_matching_part_pairs(root, joinrel, rel1, rel2, &parts1, &parts2);
+ Assert(list_length(parts1) == joinrel->nparts);
+ Assert(list_length(parts2) == joinrel->nparts);
}
/*
@@ -1449,12 +1450,12 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
cnt_parts = -1;
forboth(lc1, parts1, lc2, parts2)
{
- int part1 = lfirst_int(lc1);
- int part2 = lfirst_int(lc2);
- RelOptInfo *child_rel1;
- RelOptInfo *child_rel2;
- bool rel1_empty;
- bool rel2_empty;
+ RelOptInfo *child_rel1 = (RelOptInfo *) lfirst(lc1);
+ RelOptInfo *child_rel2 = (RelOptInfo *) lfirst(lc2);
+ bool rel1_empty = (child_rel1 == NULL ||
+ IS_DUMMY_REL(child_rel1));
+ bool rel2_empty = (child_rel2 == NULL ||
+ IS_DUMMY_REL(child_rel2));
SpecialJoinInfo *child_sjinfo;
List *child_restrictlist;
RelOptInfo *child_joinrel;
@@ -1462,11 +1463,6 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
AppendRelInfo **appinfos;
int nappinfos;
- Assert(part1 >= 0 && part2 >= 0);
- child_rel1 = rel1->part_rels[part1];
- child_rel2 = rel2->part_rels[part2];
- rel1_empty = (child_rel1 == NULL || IS_DUMMY_REL(child_rel1));
- rel2_empty = (child_rel2 == NULL || IS_DUMMY_REL(child_rel2));
cnt_parts++;
/*
@@ -1566,25 +1562,19 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
child_sjinfo,
child_sjinfo->jointype);
joinrel->part_rels[cnt_parts] = child_joinrel;
+ joinrel->all_partrels = bms_add_members(joinrel->all_partrels,
+ child_joinrel->relids);
}
- /*
- * For every pair of joining relations, the set of matching partitions
- * would change. However, the base relation partitions constituting
- * the given child should remain same for all the joining pairs. Since
- * the order in which children are stored in the array of child-joins,
- * depends upon partition bounds of the join, which are same for all
- * the joining pairs, every joining pair yields the child-joins in the
- * same order.
- */
Assert(bms_equal(child_joinrel->relids, child_joinrelids));
populate_joinrel_with_paths(root, child_rel1, child_rel2,
child_joinrel, child_sjinfo,
child_restrictlist);
+
}
- Assert(cnt_parts == (joinrel->nparts - 1));
+ Assert(cnt_parts == joinrel->nparts - 1);
}
/*
@@ -1793,3 +1783,70 @@ match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel, bool strict_op)
return -1;
}
+
+static void
+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;
+ *parts2 = NIL;
+
+ for (cnt_parts = 0; cnt_parts < joinrel->nparts; cnt_parts++)
+ {
+ RelOptInfo *child_joinrel = joinrel->part_rels[cnt_parts];
+ RelOptInfo *child_rel1;
+ RelOptInfo *child_rel2;
+ Relids child_relids1;
+ Relids child_relids2;
+
+ /*
+ * If the segment of the join is empty, it means that the segment was
+ * ignored in the child-join creation step in try_partitionwise_join()
+ * since it would not contribute to the join result, due to one or
+ * both inputs being empty; add NULL for each list so that it will be
+ * ignored again in that step.
+ */
+ if (!child_joinrel)
+ {
+ *parts1 = lappend(*parts1, NULL);
+ *parts2 = lappend(*parts2, NULL);
+ continue;
+ }
+
+ child_relids1 = bms_intersect(child_joinrel->relids,
+ rel1->all_partrels);
+ Assert(bms_num_members(child_relids1) == bms_num_members(rel1->relids));
+ if (rel1_is_simple)
+ {
+ int varno = bms_singleton_member(child_relids1);
+
+ child_rel1 = find_base_rel(root, varno);
+ }
+ else
+ child_rel1 = find_join_rel(root, child_relids1);
+ /* We should have a rel for child_relids1 */
+ Assert(child_rel1);
+
+ child_relids2 = bms_intersect(child_joinrel->relids,
+ rel2->all_partrels);
+ Assert(bms_num_members(child_relids2) == bms_num_members(rel2->relids));
+ if (rel2_is_simple)
+ {
+ int varno = bms_singleton_member(child_relids2);
+
+ child_rel2 = find_base_rel(root, varno);
+ }
+ else
+ child_rel2 = find_join_rel(root, child_relids2);
+ /* We should have a rel for child_relids2 */
+ Assert(child_rel2);
+
+ *parts1 = lappend(*parts1, child_rel1);
+ *parts2 = lappend(*parts2, child_rel2);
+ }
+}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 38bc61e687..caf6039c10 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -376,6 +376,8 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
/* Create the otherrel RelOptInfo too. */
childrelinfo = build_simple_rel(root, childRTindex, relinfo);
relinfo->part_rels[i] = childrelinfo;
+ relinfo->all_partrels = bms_add_members(relinfo->all_partrels,
+ childrelinfo->relids);
/* If this child is itself partitioned, recurse */
if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 1e883bc994..f9ac8ab82a 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -237,10 +237,11 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
rel->has_eclass_joins = false;
rel->consider_partitionwise_join = false; /* might get changed later */
rel->part_scheme = NULL;
- rel->nparts = 0;
+ rel->nparts = -1;
rel->boundinfo = NULL;
rel->partition_qual = NIL;
rel->part_rels = NULL;
+ rel->all_partrels = NULL;
rel->partexprs = NULL;
rel->nullable_partexprs = NULL;
rel->partitioned_child_rels = NIL;
@@ -650,10 +651,11 @@ build_join_rel(PlannerInfo *root,
joinrel->consider_partitionwise_join = false; /* might get changed later */
joinrel->top_parent_relids = NULL;
joinrel->part_scheme = NULL;
- joinrel->nparts = 0;
+ joinrel->nparts = -1;
joinrel->boundinfo = NULL;
joinrel->partition_qual = NIL;
joinrel->part_rels = NULL;
+ joinrel->all_partrels = NULL;
joinrel->partexprs = NULL;
joinrel->nullable_partexprs = NULL;
joinrel->partitioned_child_rels = NIL;
@@ -826,10 +828,11 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
joinrel->consider_partitionwise_join = false; /* might get changed later */
joinrel->top_parent_relids = NULL;
joinrel->part_scheme = NULL;
- joinrel->nparts = 0;
+ joinrel->nparts = -1;
joinrel->boundinfo = NULL;
joinrel->partition_qual = NIL;
joinrel->part_rels = NULL;
+ joinrel->all_partrels = NULL;
joinrel->partexprs = NULL;
joinrel->nullable_partexprs = NULL;
joinrel->partitioned_child_rels = NIL;
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 88afc05a60..a5b3072c0f 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -128,11 +128,14 @@ static PartitionBoundInfo partition_hash_bounds_merge(int partnatts,
int16 *parttyplen, bool *parttypbyval,
RelOptInfo *outer_rel, RelOptInfo *inner_rel,
List **outer_parts, List **inner_parts);
-static void generate_matching_part_pairs(PartitionMap *outer_maps,
- PartitionMap *inner_maps,
- int nparts1, int nparts2,
- JoinType jointype, int nparts,
- List **parts1, List **parts2);
+static void generate_matching_part_pairs(RelOptInfo *rel1,
+ RelOptInfo *rel2,
+ PartitionMap *partmaps1,
+ PartitionMap *partmaps2,
+ int nparts1, int nparts2,
+ int nparts,
+ List **matched_parts1,
+ List **matched_parts2);
static PartitionBoundInfo build_merged_partition_bounds(char strategy,
List *merged_datums, List *merged_indexes,
List *merged_contents, int null_index,
@@ -3839,9 +3842,10 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
return NULL;
/* Use maps to match partition from the joining relations. */
- generate_matching_part_pairs(outer_maps, inner_maps,
+ generate_matching_part_pairs(outer_rel, inner_rel,
+ outer_maps, inner_maps,
outer_nparts, inner_nparts,
- jointype, next_index,
+ next_index,
outer_parts, inner_parts);
/* Craft a PartitionBoundInfo to return. */
@@ -4099,9 +4103,10 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation,
return NULL;
/* Use maps to match partition from the joining relations. */
- generate_matching_part_pairs(outer_maps, inner_maps,
+ generate_matching_part_pairs(outer_rel, inner_rel,
+ outer_maps, inner_maps,
outer_nparts, inner_nparts,
- jointype, next_index,
+ next_index,
outer_parts, inner_parts);
/* Craft a PartitionBoundInfo to return. */
@@ -4170,8 +4175,8 @@ partition_hash_bounds_merge(int partnatts,
*/
for (cnt = 0; cnt < outer_rel->nparts; cnt++)
{
- *outer_parts = lappend_int(*outer_parts, cnt);
- *inner_parts = lappend_int(*inner_parts, cnt);
+ *outer_parts = lappend(*outer_parts, outer_rel->part_rels[cnt]);
+ *inner_parts = lappend(*inner_parts, inner_rel->part_rels[cnt]);
}
return outer_bi;
@@ -4312,12 +4317,11 @@ map_and_merge_partitions(PartitionMap *partmaps1, PartitionMap *partmaps2,
* set to NIL.
*/
static void
-generate_matching_part_pairs(PartitionMap *partmaps1, PartitionMap *partmaps2,
- int nparts1, int nparts2,
- JoinType jointype, int nparts,
+generate_matching_part_pairs(RelOptInfo *rel1, RelOptInfo *rel2,
+ PartitionMap *partmaps1, PartitionMap *partmaps2,
+ int nparts1, int nparts2, int nparts,
List **matched_parts1, List **matched_parts2)
{
- bool merged = true;
int *matching1,
*matching2;
int i;
@@ -4358,16 +4362,6 @@ generate_matching_part_pairs(PartitionMap *partmaps1, PartitionMap *partmaps2,
}
}
- /*
- * If we have a partition missing on an inner side, we need to add a dummy
- * relation which joins with the outer partition. If the inner relation
- * happens to be a base relation, it will require adding a dummy child
- * base relation during join processing. Right now, we freeze the base
- * relation arrays like PlannerInfo::simple_rte_array after planning for
- * base relations. Adding a new (dummy) base relation would require some
- * changes to that. So, right now, we do not implement partition-wise join
- * in such cases.
- */
for (i = 0; i < nparts; i++)
{
int part1 = matching1[i];
@@ -4376,52 +4370,14 @@ generate_matching_part_pairs(PartitionMap *partmaps1, PartitionMap *partmaps2,
/* At least one of the partitions should exist. */
Assert(part1 >= 0 || part2 >= 0);
- switch (jointype)
- {
- case JOIN_INNER:
- case JOIN_SEMI:
-
- /*
- * An inner or semi join can not return any row when the
- * matching partition on either side is missing. We should
- * have eliminated all such cases while merging the bounds.
- */
- Assert(part1 >= 0 && part2 >= 0);
- break;
-
- case JOIN_LEFT:
- case JOIN_ANTI:
- Assert(part1 >= 0);
- if (part2 < 0)
- merged = false;
- break;
-
- case JOIN_FULL:
- if (part1 < 0 || part2 < 0)
- merged = false;
- break;
-
- default:
- elog(ERROR, "unrecognized join type: %d", (int) jointype);
- }
-
- if (!merged)
- break;
-
- *matched_parts1 = lappend_int(*matched_parts1, part1);
- *matched_parts2 = lappend_int(*matched_parts2, part2);
+ *matched_parts1 = lappend(*matched_parts1,
+ part1 >= 0 ? rel1->part_rels[part1] : NULL);
+ *matched_parts2 = lappend(*matched_parts2,
+ part2 >= 0 ? rel2->part_rels[part2] : NULL);
}
pfree(matching1);
pfree(matching2);
-
- if (!merged)
- {
- list_free(*matched_parts1);
- list_free(*matched_parts2);
- *matched_parts1 = NIL;
- *matched_parts2 = NIL;
- }
}
static PartitionBoundInfo
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index e3c579ee44..1d2dc68201 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -581,6 +581,7 @@ typedef struct PartitionSchemeData *PartitionScheme;
* boundinfo - Partition bounds
* partition_qual - Partition constraint if not the root
* part_rels - RelOptInfos for each partition
+ * all_partrels - Relids set of all partition relids
* partexprs, nullable_partexprs - Partition key expressions
* partitioned_child_rels - RT indexes of unpruned partitions of
* this relation that are partitioned tables
@@ -723,6 +724,7 @@ typedef struct RelOptInfo
List *partition_qual; /* partition constraint */
struct RelOptInfo **part_rels; /* Array of RelOptInfos of partitions,
* stored in the same order of bounds */
+ Relids all_partrels; /* Relids set of all partition relids */
List **partexprs; /* Non-nullable partition key expressions. */
List **nullable_partexprs; /* Nullable partition key expressions. */
List *partitioned_child_rels; /* List of RT indexes. */