0002-Changes.patch
application/octet-stream
Filename: 0002-Changes.patch
Type: application/octet-stream
Part: 1
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: format-patch
Series: patch 0002
Subject: Changes.
| File | + | − |
|---|---|---|
| doc/src/sgml/config.sgml | 3 | 3 |
| src/backend/nodes/outfuncs.c | 1 | 1 |
| src/backend/optimizer/path/joinrels.c | 100 | 83 |
| src/backend/optimizer/README | 27 | 0 |
| src/backend/optimizer/util/relnode.c | 5 | 5 |
| src/backend/partitioning/partbounds.c | 56 | 30 |
| src/include/nodes/pathnodes.h | 6 | 5 |
From 11f93f75f05315404d5838ff050b0d66d1633a78 Mon Sep 17 00:00:00 2001
From: Etsuro Fujita <etsuro@EtsuronoMacBook-Air.local>
Date: Sat, 4 Apr 2020 00:05:59 +0900
Subject: [PATCH 2/2] Changes.
---
doc/src/sgml/config.sgml | 6 +-
src/backend/nodes/outfuncs.c | 2 +-
src/backend/optimizer/README | 27 +++++
src/backend/optimizer/path/joinrels.c | 183 +++++++++++++++++++---------------
src/backend/optimizer/util/relnode.c | 10 +-
src/backend/partitioning/partbounds.c | 86 ++++++++++------
src/include/nodes/pathnodes.h | 11 +-
7 files changed, 198 insertions(+), 127 deletions(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 2de21903a1..50cffb8694 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -4693,9 +4693,9 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
which allows a join between partitioned tables to be performed by
joining the matching partitions. Partitionwise join currently applies
only when the join conditions include all the partition keys, which
- must be of the same data type and have exactly matching sets of child
- partitions. Because partitionwise join planning can use significantly
- more CPU time and memory during planning, the default is
+ must be of the same data type and have one-to-one matching sets of
+ child partitions. Because partitionwise join planning can use
+ significantly more CPU time and memory during planning, the default is
<literal>off</literal>.
</para>
</listitem>
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 55dfa27157..baee36892f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2288,7 +2288,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_BOOL_FIELD(merged);
+ WRITE_BOOL_FIELD(partbounds_merged);
WRITE_BITMAPSET_FIELD(all_partrels);
WRITE_NODE_FIELD(partitioned_child_rels);
}
diff --git a/src/backend/optimizer/README b/src/backend/optimizer/README
index 89ce373d5e..13633841f3 100644
--- a/src/backend/optimizer/README
+++ b/src/backend/optimizer/README
@@ -1106,6 +1106,33 @@ into joins between their partitions is called partitionwise join. We will use
term "partitioned relation" for either a partitioned table or a join between
compatibly partitioned tables.
+The technique is extended to some cases where the joining tables don't have
+exactly the same partition bounds, by an advanced partition-matching
+algorithm: it checks to see if there is a relationship where each partition of
+one joining table matches/overlaps at most one partition of the other, and
+vice versa; in which case the join between the joining tables can be broken
+down into joins between the matching partitions (ie, the join relation is
+considerd partitioned), so the algorithm produces the pairs of the matching
+partitions, plus the partition bounds for the join relation, to allow
+partitionwise join for the join. The algorithm is implemented in
+partition_bounds_merge(). For an N-way join relation considered partitioned
+by this extension, not every pair of joining relations can use partitionwise
+join. For example:
+
+ (A leftjoin B on (Pab)) innerjoin C on (Pac)
+
+where A, B, and C are partitioned tables, and A has an extra partition
+compared to B and C. When considering partitionwise join for the join {A B},
+the extra partition of A doesn't have a matching partition on the nullable
+side, which is the case that the current implementation of partitionwise join
+can't handle. So {A B} is not considered partitioned, and thus the pair of
+{A B} and C considered for the 3-way join can't use partitionwise join. On
+the other hand, the pair of {A C} and B can use partitionwise join, because
+{A C} is considered partitioned, eliminating the extra partition (see identity
+1 on outer join reordering). The partitionwise joinability of the N-way join
+relation is determined based on the first pair of joining relations that are
+both partitioned and can use partitionwise join.
+
The partitioning properties of a partitioned relation are stored in its
RelOptInfo. The information about data types of partition keys are stored in
PartitionSchemeData structure. The planner maintains a list of canonical
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 530ebed245..2c0ce6d2f3 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -48,6 +48,10 @@ 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 compute_partition_bounds(PlannerInfo *root, RelOptInfo *rel1,
+ RelOptInfo *rel2, RelOptInfo *joinrel,
+ SpecialJoinInfo *parent_sjinfo,
+ List **parts1, List **parts2);
static void get_matching_part_pairs(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *rel1, RelOptInfo *rel2,
List **parts1, List **parts2);
@@ -1360,7 +1364,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);
- bool merged = false;
List *parts1 = NIL;
List *parts2 = NIL;
ListCell *lcr1 = NULL;
@@ -1397,89 +1400,12 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
Assert(joinrel->part_scheme == rel1->part_scheme &&
joinrel->part_scheme == rel2->part_scheme);
- /*
- * If we don't have the partition bounds for the join rel yet, try to
- * compute those along with pairs of partitions to be joined.
- */
- if (joinrel->nparts == -1)
- {
- PartitionScheme part_scheme = joinrel->part_scheme;
- PartitionBoundInfo boundinfo = NULL;
- int nparts = 0;
-
- Assert(joinrel->boundinfo == NULL);
- 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: the join rel have the same
- * partition bounds as inputs, and the partitions with the same
- * cardinal positions form the pairs.
- *
- * Note: even in cases where one or both inputs have merged bounds,
- * it would be possible for both the bounds to be exactly the same, but
- * it seems unlikely to be worth the cycles to check.
- */
- if (!rel1->merged &&
- !rel2->merged &&
- rel1->nparts == rel2->nparts &&
- partition_bounds_equal(part_scheme->partnatts,
- part_scheme->parttyplen,
- part_scheme->parttypbyval,
- rel1->boundinfo, rel2->boundinfo))
- {
- boundinfo = rel1->boundinfo;
- nparts = rel1->nparts;
- }
- else
- {
- /* Try merging the partition bounds for inputs. */
- boundinfo = partition_bounds_merge(part_scheme->partnatts,
- part_scheme->partsupfunc,
- part_scheme->partcollation,
- rel1, rel2,
- parent_sjinfo->jointype,
- &parts1, &parts2);
- if (boundinfo == NULL)
- {
- joinrel->nparts = 0;
- return;
- }
- nparts = list_length(parts1);
- merged = true;
- }
-
- Assert(nparts > 0);
- joinrel->boundinfo = boundinfo;
- joinrel->merged = merged;
- joinrel->nparts = nparts;
- joinrel->part_rels =
- (RelOptInfo **) palloc0(sizeof(RelOptInfo *) * nparts);
- }
- else
- {
- Assert(joinrel->nparts > 0);
- Assert(joinrel->boundinfo);
- Assert(joinrel->part_rels);
+ Assert(!(joinrel->partbounds_merged && (joinrel->nparts <= 0)));
- /*
- * If the join rel's merged flag is true, it means inputs are not
- * guaranteed to have the same partition bounds, therefore we can't
- * assume that the partitions at the same cardinal positions form the
- * pairs; let get_matching_part_pairs() generate the pairs. Otherwise,
- * nothing to do since we can assume that.
- */
- if (joinrel->merged)
- {
- get_matching_part_pairs(root, joinrel, rel1, rel2,
- &parts1, &parts2);
- Assert(list_length(parts1) == joinrel->nparts);
- Assert(list_length(parts2) == joinrel->nparts);
- merged = true;
- }
- }
+ compute_partition_bounds(root, rel1, rel2, joinrel, parent_sjinfo,
+ &parts1, &parts2);
- if (merged)
+ if (joinrel->partbounds_merged)
{
lcr1 = list_head(parts1);
lcr2 = list_head(parts2);
@@ -1503,7 +1429,7 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
AppendRelInfo **appinfos;
int nappinfos;
- if (merged)
+ if (joinrel->partbounds_merged)
{
child_rel1 = lfirst_node(RelOptInfo, lcr1);
child_rel2 = lfirst_node(RelOptInfo, lcr2);
@@ -1835,6 +1761,97 @@ match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel, bool strict_op)
return -1;
}
+/*
+ * compute_partition_bounds
+ * Compute the partition bounds for a join rel from those for inputs
+ */
+static void
+compute_partition_bounds(PlannerInfo *root, RelOptInfo *rel1,
+ RelOptInfo *rel2, RelOptInfo *joinrel,
+ SpecialJoinInfo *parent_sjinfo,
+ List **parts1, List **parts2)
+{
+ /*
+ * If we don't have the partition bounds for the join rel yet, try to
+ * compute those along with pairs of partitions to be joined.
+ */
+ if (joinrel->nparts == -1)
+ {
+ PartitionScheme part_scheme = joinrel->part_scheme;
+ PartitionBoundInfo boundinfo = NULL;
+ int nparts = 0;
+
+ Assert(joinrel->boundinfo == NULL);
+ 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: the join rel have the same
+ * partition bounds as inputs, and the partitions with the same
+ * cardinal positions form the pairs.
+ *
+ * Note: even in cases where one or both inputs have merged bounds,
+ * it would be possible for both the bounds to be exactly the same, but
+ * it seems unlikely to be worth the cycles to check.
+ */
+ if (!rel1->partbounds_merged &&
+ !rel2->partbounds_merged &&
+ rel1->nparts == rel2->nparts &&
+ partition_bounds_equal(part_scheme->partnatts,
+ part_scheme->parttyplen,
+ part_scheme->parttypbyval,
+ rel1->boundinfo, rel2->boundinfo))
+ {
+ boundinfo = rel1->boundinfo;
+ nparts = rel1->nparts;
+ }
+ else
+ {
+ /* Try merging the partition bounds for inputs. */
+ boundinfo = partition_bounds_merge(part_scheme->partnatts,
+ part_scheme->partsupfunc,
+ part_scheme->partcollation,
+ rel1, rel2,
+ parent_sjinfo->jointype,
+ parts1, parts2);
+ if (boundinfo == NULL)
+ {
+ joinrel->nparts = 0;
+ return;
+ }
+ nparts = list_length(*parts1);
+ joinrel->partbounds_merged = true;
+ }
+
+ Assert(nparts > 0);
+ joinrel->boundinfo = boundinfo;
+ joinrel->nparts = nparts;
+ joinrel->part_rels =
+ (RelOptInfo **) palloc0(sizeof(RelOptInfo *) * nparts);
+ }
+ else
+ {
+ Assert(joinrel->nparts > 0);
+ Assert(joinrel->boundinfo);
+ Assert(joinrel->part_rels);
+
+ /*
+ * If the join rel's partbounds_merged flag is true, it means inputs
+ * are not guaranteed to have the same partition bounds, therefore we
+ * can't assume that the partitions at the same cardinal positions form
+ * the pairs; let get_matching_part_pairs() generate the pairs.
+ * Otherwise, nothing to do since we can assume that.
+ */
+ if (joinrel->partbounds_merged)
+ {
+ get_matching_part_pairs(root, joinrel, rel1, rel2,
+ parts1, parts2);
+ Assert(list_length(*parts1) == joinrel->nparts);
+ Assert(list_length(*parts2) == joinrel->nparts);
+ }
+ }
+}
+
/*
* get_matching_part_pairs
* Generate pairs of partitions to be joined from the two inputs
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 0e4944ac8e..433f031d0f 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -242,7 +242,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
rel->part_scheme = NULL;
rel->nparts = -1;
rel->boundinfo = NULL;
- rel->merged = false;
+ rel->partbounds_merged = false;
rel->partition_qual = NIL;
rel->part_rels = NULL;
rel->all_partrels = NULL;
@@ -657,7 +657,7 @@ build_join_rel(PlannerInfo *root,
joinrel->part_scheme = NULL;
joinrel->nparts = -1;
joinrel->boundinfo = NULL;
- joinrel->merged = false;
+ joinrel->partbounds_merged = false;
joinrel->partition_qual = NIL;
joinrel->part_rels = NULL;
joinrel->all_partrels = NULL;
@@ -835,7 +835,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
joinrel->part_scheme = NULL;
joinrel->nparts = -1;
joinrel->boundinfo = NULL;
- joinrel->merged = false;
+ joinrel->partbounds_merged = false;
joinrel->partition_qual = NIL;
joinrel->part_rels = NULL;
joinrel->all_partrels = NULL;
@@ -1668,8 +1668,8 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
!joinrel->boundinfo);
/*
- * If the join relation is partitioned, it use the same partitioning scheme
- * as the joining relations.
+ * If the join relation is partitioned, it uses the same partitioning
+ * scheme as the joining relations.
*
* Note: we calculate the partition bounds, number of partitions, and
* child-join relations of the join relation in try_partitionwise_join().
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 24dbc2c8f3..e4c74d6c03 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -69,7 +69,10 @@ typedef struct PartitionRangeBound
bool lower; /* this is the lower (vs upper) bound */
} PartitionRangeBound;
-/* Per-partitioned-relation data for merge_list_bounds()/merge_range_bounds() */
+/*
+ * Mapping from partitions of a partitioned relation to partitions of a join
+ * relation supposed to be partitioned (a.k.a merged partitions)
+ */
typedef struct PartitionMap
{
int nparts; /* number of partitions */
@@ -998,16 +1001,18 @@ partition_bounds_copy(PartitionBoundInfo src,
/*
* partition_bounds_merge
+ * Check to see if there is a relationship where each partition of
+ * 'outer_rel' matches/overlaps at most one partition of 'inner_rel', and
+ * vice versa; and if so, build and return the partition bounds for a join
+ * relation between the rels, generating two lists of matching/overlapping
+ * partitions, which are returned to *outer_parts and *inner_parts
+ * respectively.
*
- * This function builds and returns the partition bounds for a join relation
- * between input relations, creating two lists of partitions, which are
- * returned to *outer_parts and *inner_parts respectively. The lists contain
- * the same number of partitions, and the partitions at the same positions in
- * the lists indicate join pairs used for partitioned join.
- *
- * This function returns NULL, setting *outer_parts and *inner_parts to NIL,
- * if a partition on one side matches multiple partitions on the other side,
- * in which case we currently don't support partitioned join.
+ * The lists contain the same number of partitions, and the partitions at the
+ * same positions in the lists indicate join pairs used for partitioned join.
+ * If a partition on one side matches/overlaps multiple partitions on the other
+ * side, this function returns NULL, setting *outer_parts and *inner_parts to
+ * NIL.
*/
PartitionBoundInfo
partition_bounds_merge(int partnatts,
@@ -1018,24 +1023,20 @@ partition_bounds_merge(int partnatts,
{
PartitionBoundInfo outer_binfo = outer_rel->boundinfo;
PartitionBoundInfo inner_binfo = inner_rel->boundinfo;
- char strategy;
/*
* Currently, this function is called only from try_partitionwise_join(),
* so the join type should be INNER, LEFT, FULL, SEMI, or ANTI.
*/
- if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
- jointype != JOIN_FULL && jointype != JOIN_SEMI &&
- jointype != JOIN_ANTI)
- elog(ERROR, "unrecognized join type: %d", (int) jointype);
+ Assert(jointype == JOIN_INNER || jointype == JOIN_LEFT ||
+ jointype == JOIN_FULL || jointype == JOIN_SEMI ||
+ jointype == JOIN_ANTI);
- /* Bail out if the partitioning strategies are different. */
- if (outer_binfo->strategy != inner_binfo->strategy)
- return NULL;
+ /* The partitioning strategies should be the same. */
+ Assert(outer_binfo->strategy == inner_binfo->strategy);
- strategy = outer_binfo->strategy;
*outer_parts = *inner_parts = NIL;
- switch (strategy)
+ switch (outer_binfo->strategy)
{
case PARTITION_STRATEGY_HASH:
@@ -1075,7 +1076,8 @@ partition_bounds_merge(int partnatts,
inner_parts);
default:
- elog(ERROR, "unexpected partition strategy: %d", (int) strategy);
+ elog(ERROR, "unexpected partition strategy: %d",
+ (int) outer_binfo->strategy);
return NULL; /* keep compiler quiet */
}
}
@@ -1084,6 +1086,18 @@ partition_bounds_merge(int partnatts,
* merge_list_bounds
* Create the partition bounds for a join relation between list
* partitioned tables, if possible
+ *
+ * In this function we try to find matching partitions from both sides by
+ * comparing list values stored in their partition bounds. Since the list
+ * values appear in the ascending order, an algorithm similar to merge join is
+ * used for that. If a partition doesn't have a matching partition on the
+ * other side, the algorithm tries to match it with the default partition on
+ * the other side if any; if not, the algorithm tries to match it with a
+ * dummy partition on the other side if it is on the non-nullable side of an
+ * outer join. Also, if both sides have the default partitions, the algorithm
+ * tries to match them with each other. We give up if the algorithm finds a
+ * partition matching multiple partitions on the other side, which is the
+ * scenario the current implementation of partitioned join can't handle.
*/
static PartitionBoundInfo
merge_list_bounds(FmgrInfo *partsupfunc, Oid *partcollation,
@@ -1379,6 +1393,18 @@ cleanup:
* merge_range_bounds
* Create the partition bounds for a join relation between range
* partitioned tables, if possible
+ *
+ * In this function we try to find overlapping partitions from both sides by
+ * comparing ranges stored in their partition bounds. Since the ranges
+ * appear in the ascending order, an algorithm similar to merge join is
+ * used for that. If a partition doesn't have an overlapping partition on the
+ * other side, the algorithm tries to match it with the default partition on
+ * the other side if any; if not, the algorithm tries to match it with a
+ * dummy partition on the other side if it is on the non-nullable side of an
+ * outer join. Also, if both sides have the default partitions, the algorithm
+ * tries to match them with each other. We give up if the algorithm finds a
+ * partition overlapping multiple partitions on the other side, which is the
+ * scenario the current implementation of partitioned join can't handle.
*/
static PartitionBoundInfo
merge_range_bounds(int partnatts, FmgrInfo *partsupfuncs,
@@ -1851,8 +1877,8 @@ merge_matching_partitions(PartitionMap *outer_map, PartitionMap *inner_map,
* index of the merged partition if successful, -1 otherwise
*
* If the partition is newly created, *next_index is incremented. Also, if it
- * is the default partition of the join relation, *default_partition is set to
- * the index if not already done.
+ * is the default partition of the join relation, *default_index is set to the
+ * index if not already done.
*/
static int
process_outer_partition(PartitionMap *outer_map,
@@ -1901,7 +1927,7 @@ process_outer_partition(PartitionMap *outer_map,
* has to be scanned all the way anyway, so the resulting partition
* will contain all key values from the default partition, which any
* other partition of the join relation will not contain. Thus the
- * resutling partition will act as the default partition of the join
+ * resulting partition will act as the default partition of the join
* relation; record the index in *default_index if not already done.
*/
if (jointype == JOIN_FULL)
@@ -1932,8 +1958,8 @@ process_outer_partition(PartitionMap *outer_map,
* index of the merged partition if successful, -1 otherwise
*
* If the partition is newly created, *next_index is incremented. Also, if it
- * is the default partition of the join relation, *default_partition is set to
- * the index if not already done.
+ * is the default partition of the join relation, *default_index is set to the
+ * index if not already done.
*/
static int
process_inner_partition(PartitionMap *outer_map,
@@ -1982,7 +2008,7 @@ process_inner_partition(PartitionMap *outer_map,
* has to be scanned all the way anyway, so the resulting partition
* will contain all key values from the default partition, which any
* other partition of the join relation will not contain. Thus the
- * resutling partition will act as the default partition of the join
+ * resulting partition will act as the default partition of the join
* relation; record the index in *default_index if not already done.
*/
if (IS_OUTER_JOIN(jointype))
@@ -2044,13 +2070,13 @@ merge_null_partitions(PartitionMap *outer_map,
{
Assert(outer_null >= 0 && outer_null < outer_map->nparts);
if (outer_map->merged_indexes[outer_null] == -1)
- consider_outer_null = true;
+ consider_outer_null = true;
}
if (inner_has_null)
{
Assert(inner_null >= 0 && inner_null < inner_map->nparts);
if (inner_map->merged_indexes[inner_null] == -1)
- consider_inner_null = true;
+ consider_inner_null = true;
}
/* If both flags are set false, we don't need to do anything. */
@@ -2235,7 +2261,7 @@ merge_default_partitions(PartitionMap *outer_map,
*
* Note: The caller assumes that the given partition doesn't have a non-dummy
* matching partition on the other side, but if the given partition finds the
- * matchig partition later, we will adjust the assignment.
+ * matching partition later, we will adjust the assignment.
*/
static int
merge_partition_with_dummy(PartitionMap *map, int index, int *next_index)
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 7f9c4ab1f1..0b1eb00223 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -579,7 +579,7 @@ typedef struct PartitionSchemeData *PartitionScheme;
* part_scheme - Partitioning scheme of the relation
* nparts - Number of partitions
* boundinfo - Partition bounds
- * merged - true if partition bounds are merged ones
+ * partbounds_merged - true if partition bounds are merged ones
* partition_qual - Partition constraint if not the root
* part_rels - RelOptInfos for each partition
* all_partrels - Relids set of all partition relids
@@ -720,11 +720,12 @@ typedef struct RelOptInfo
/* used for partitioned relations */
PartitionScheme part_scheme; /* Partitioning scheme. */
- int nparts; /* number of partitions; 0 = not partitioned;
- * -1 = not yet set */
+ int nparts; /* number of partitions; -1 if not yet set;
+ * in case of a join relation 0 means it's
+ * considered unpartitioned */
struct PartitionBoundInfoData *boundinfo; /* Partition bounds */
- bool merged; /* true if partition bounds were created by
- * partition_bounds_merge() */
+ bool partbounds_merged; /* true if partition bounds were created by
+ * partition_bounds_merge() */
List *partition_qual; /* partition constraint */
struct RelOptInfo **part_rels; /* Array of RelOptInfos of partitions,
* stored in the same order of bounds */
--
2.14.3 (Apple Git-98)