advanced partition matching algorithm for partition-wise join
The patch-set in [1]/messages/by-id/CAFjFpRd9Vqh_=-Ldv-XqWY006d07TJ+VXuhXCbdj=P1jukYBrw@mail.gmail.com supports partition-wise join when the partition bounds and
partition keys of the joining tables exactly match. The last two patches in the
last few patch-sets in that thread implement more
advanced partition matching code. In order to avoid mixing reviews for advanced
partition matching and the basic partition-wise join implementation, I am
starting a new thread to discuss the same. I am attaching the last two
patches from that patch set here.
The new partition matching algorithm handles following cases when a
given partition
on one side has at most one matching partition matching on the other side.
1. When the ranges of the joining tables do not match exactly E.g. partition
table t1 has partitions t1p1 (0 - 100), t1p2 (150 - 200) and partition table t2
has partitions t2p1 (0 - 50), t2p2 (100 - 175). In this case (t1p1, t2p1) and
(t1p2, t2p2) form the matching partition pairs, which can be joined. While
matching the pairs, we also compute the partition bounds for the resulting
join. An INNER join between t1 and t2 will have ranges (0 - 50) since no row
with 50 <= key < 100 from t1p1 is going to find a matching row in t2p1 and (150
- 175) since no row with 100 <= key < 150 from t2p2 is going to find a matching
row in t1p2 and no row with 175 <= key < 200 in t1p2 is going to find a
matching row in t1p2. A t1 LEFT join t2 on the other hand will have ranges same
as the outer relation i.e. t1, (0 - 100), (150 - 200) since all rows from t1
will be part of the join. Thus depending upon the type of join the partition
bounds of the resultant join relation change. Similarly for list partitioned
table, when the lists do not match exactly, the algorithm finds matching pairs
of partitions and the lists of resultant join relation. E.g. t1 has
partitions t1p1 ('a',
'b', 'c'), t1p2 ('e', 'f') and t2 has partitions t2p1 ('a', 'b'), t2p2 ('d',
'e', 'f'). In this case (t1p1, t2p1) and (t2p1, t2p2) form the matching
pairs which are joined. Inner join will have bounds ('a','b'), ('e', 'f') and
t1 LEFT JOIN t2 will have bounds same as t1.
2. When one or both side have at least one partition that does not have
matching partition on the other side. E.g. t1 has partitions t1p1 ('a','b'),
t1p2 ('c','d') and t2 has only one partition t2p1 ('a','b') OR t1 has
partitions t1p1 (0 - 100), t1p2 (100 - 200) and t2 has only one partition t2p1
(0 - 100). In this case as well different types of joins will have different
partition bounds for the result using similar rules described above.
3. A combination of 1 and 2 e.g. t1 has partitions t1p1('a','b','c'),
t1p2('d','e','f') and t2 has a single partition t2p1 ('a','b', 'z').
Algorithm
---------
The pairs of matching partitions and the partition bounds of the join are
calculated by an algorithm similar to merge join.
In such a join, it can be observed that every partition on either side,
contributes to at most one partition of the resultant join relation. Thus for
every partition on either side, we keep track of the partition of resultant
join (if any), which it contributes to. If multiple partitions from any of the
joining relations map to a single partition of the resultant join, we need to
gang those partitions together before joining the partition/s from the other
side. Since we do not have infrastructure for ganging multiple arbitrary
RelOptInfos together in a parent RelOptInfo, we do not support such a
partitionw-wise join right now. We stop merging the bounds immediately when we
detect such a case.
For list partitioned tables, we compare list values from both the sides,
starting with the lowest. If the two list values being compared match,
corresponding partitions from both sides form a pair of partitions to be
joined. We record this mapping and also include the list value in join bounds.
If the two list values do not match and the lower of those two comes from the
outer side of the join, we include it in the join bounds. We advance to the
next list value on side with the lower list value continuing the process of
merging till list values on at least one side are exhausted. If the remaining
values are from the outer side, we include those in the join partition bounds.
Every list value included in the join bounds, and its originating partition/s
are associated with appropriate partition of the resultant join. For more
details please see partition_list_bounds_merge() in the attached patch.
In case of range partitioned tables, we compare the ranges of the partitions in
increasing order of their bounds. If two ranges being compared overlap,
corresponding partitions from both sides form a pair of partitions to be
joined. We record this mapping and also include the merged range in the bounds
of resultant join. The overlapping ranges are merged based on the type of join
as described above. If either of the ranges completely precedes the other, and
it's on the outer side, we include that range in the bounds of resultant join.
We advance to the next range on the side with lower upper bound till ranges on
at least one side are exhausted. If the remaining ranges are from the outer
side, we include those in the partition bounds of resultant join. While
including a range in the partition bounds of the resultant join if its lower
bound precedes the upper bound of the last included range, it indicates that
multiple partitions on that side map to one partition on the other side, so we
bail out. Notice that in this method, we always include the ranges in the
partition bounds of the resultant join in the increasing order of their bounds.
Every range included in the join's partition bounds and it's corresponding
partition/s from joining relations are associated with appropriate partition of
the resultant join. For more details please see partition_range_bounds_merge()
in the attached patch.
The partitions from both sides (one partition from each side) which map to the
same partition of the resultant join are joined to form child-joins. The case
when an outer partition may not have a matching partition from the inner side
will be discussed in the next section. Except for the above algorithm to find
the pairs of matching partitions and calculating bounds of the resultant join,
the rest of the partition-wise join algorithm remains the same.
Unsupported case: When a partition from outer side doesn't have matching
partition on the inner side.
--------------------------------------------------------------------------
Consider a join t1 LEFT JOIN t2 where t1 has partitions t1p1 (0 - 100), t1p2
(100 - 200) and t2 has a single partition t2p1(0 - 100). The rows in t1p2 won't
have a matching row in t2 since there is no partition matching t1p2. The result
of the join will have rows in t1p2 with columns from t2 NULLed. In order to
execute this join as a partition-wise join, we need a dummy relation in place
of the missing partition, which we can join with t1p2. We need this placeholder
dummy relation (its targetlist, relids etc.), so that rest of the planner can
work with the resulting child-join.
We notice the missing partitions only while planning the join (during the
execution of make_one_rel()), by which time we have frozen the number of base
relations. Introducing a base relation during join planning is not supported
in current planner. Similarly, a partition can be missing from a partitioned
join relation, in which case we have to add a dummy join relation. This might
need adding corresponding base relations as well. I have not spent time looking
for what it takes to support these cases. For now the patch does not support
partition-wise join in such cases.
TODOs
-----------
1. Add tests for advanced partition matching algorithm
2. Improve code quality, commenting, function names etc.
[1]: /messages/by-id/CAFjFpRd9Vqh_=-Ldv-XqWY006d07TJ+VXuhXCbdj=P1jukYBrw@mail.gmail.com
--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
Attachments:
0010-Modify-bound-comparision-functions-to-accept-members.patchtext/x-patch; charset=US-ASCII; name=0010-Modify-bound-comparision-functions-to-accept-members.patchDownload
From 7a9d903deb0475b4f8b0742fbe904a7cf0ce69c1 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>
Date: Thu, 6 Jul 2017 14:15:22 +0530
Subject: [PATCH 10/11] Modify bound comparision functions to accept members
of PartitionKey
Functions partition_bound_cmp(), partition_rbound_cmp() and
partition_rbound_datum_cmp() are required to merge partition bounds
from joining relations. While doing so, we do not have access to the
PartitionKey of either relations. So, modify these functions to accept
only required members of PartitionKey so that the functions can be
reused for merging bounds.
Ashutosh Bapat.
---
src/backend/catalog/partition.c | 76 ++++++++++++++++++++++-----------------
1 file changed, 44 insertions(+), 32 deletions(-)
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 96a64ce..d42e1b5 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -126,15 +126,17 @@ static List *generate_partition_qual(Relation rel);
static PartitionRangeBound *make_one_range_bound(PartitionKey key, int index,
List *datums, bool lower);
-static int32 partition_rbound_cmp(PartitionKey key,
- Datum *datums1, PartitionRangeDatumKind *kind1,
- bool lower1, PartitionRangeBound *b2);
-static int32 partition_rbound_datum_cmp(PartitionKey key,
- Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
+static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, Datum *datums1,
+ PartitionRangeDatumKind *kind1, bool lower1,
+ PartitionRangeBound *b2);
+static int32 partition_rbound_datum_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, Datum *rb_datums,
+ PartitionRangeDatumKind *rb_kind,
Datum *tuple_datums);
-static int32 partition_bound_cmp(PartitionKey key,
- PartitionBoundInfo boundinfo,
+static int32 partition_bound_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, PartitionBoundInfo boundinfo,
int offset, void *probe, bool probe_is_bound);
static int partition_bound_bsearch(PartitionKey key,
PartitionBoundInfo boundinfo,
@@ -719,8 +721,9 @@ check_new_partition_bound(char *relname, Relation parent,
* First check if the resulting range would be empty with
* specified lower and upper bounds
*/
- if (partition_rbound_cmp(key, lower->datums, lower->kind, true,
- upper) >= 0)
+ if (partition_rbound_cmp(key->partnatts, key->partsupfunc,
+ key->partcollation, lower->datums,
+ lower->kind, true, upper) >= 0)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -771,9 +774,11 @@ check_new_partition_bound(char *relname, Relation parent,
{
int32 cmpval;
- cmpval = partition_bound_cmp(key, boundinfo,
- offset + 1, upper,
- true);
+ cmpval = partition_bound_cmp(key->partnatts,
+ key->partsupfunc,
+ key->partcollation,
+ boundinfo, offset + 1,
+ upper, true);
if (cmpval < 0)
{
/*
@@ -2138,7 +2143,9 @@ qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
PartitionKey key = (PartitionKey) arg;
- return partition_rbound_cmp(key, b1->datums, b1->kind, b1->lower, b2);
+ return partition_rbound_cmp(key->partnatts, key->partsupfunc,
+ key->partcollation, b1->datums, b1->kind,
+ b1->lower, b2);
}
/*
@@ -2155,7 +2162,7 @@ qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
* two contiguous partitions.
*/
static int32
-partition_rbound_cmp(PartitionKey key,
+partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation,
Datum *datums1, PartitionRangeDatumKind *kind1,
bool lower1, PartitionRangeBound *b2)
{
@@ -2165,7 +2172,7 @@ partition_rbound_cmp(PartitionKey key,
PartitionRangeDatumKind *kind2 = b2->kind;
bool lower2 = b2->lower;
- for (i = 0; i < key->partnatts; i++)
+ for (i = 0; i < partnatts; i++)
{
/*
* First, handle cases where the column is unbounded, which should not
@@ -2186,8 +2193,8 @@ partition_rbound_cmp(PartitionKey key,
*/
break;
- cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
- key->partcollation[i],
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
+ partcollation[i],
datums1[i],
datums2[i]));
if (cmpval != 0)
@@ -2213,22 +2220,23 @@ partition_rbound_cmp(PartitionKey key,
* is <, =, or > partition key of tuple (tuple_datums)
*/
static int32
-partition_rbound_datum_cmp(PartitionKey key,
- Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
+partition_rbound_datum_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, Datum *rb_datums,
+ PartitionRangeDatumKind *rb_kind,
Datum *tuple_datums)
{
int i;
int32 cmpval = -1;
- for (i = 0; i < key->partnatts; i++)
+ for (i = 0; i < partnatts; i++)
{
if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
return -1;
else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
return 1;
- cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
- key->partcollation[i],
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
+ partcollation[i],
rb_datums[i],
tuple_datums[i]));
if (cmpval != 0)
@@ -2245,17 +2253,18 @@ partition_rbound_datum_cmp(PartitionKey key,
* specified in *probe.
*/
static int32
-partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo,
- int offset, void *probe, bool probe_is_bound)
+partition_bound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation,
+ PartitionBoundInfo boundinfo, int offset, void *probe,
+ bool probe_is_bound)
{
Datum *bound_datums = boundinfo->datums[offset];
int32 cmpval = -1;
- switch (key->strategy)
+ switch (boundinfo->strategy)
{
case PARTITION_STRATEGY_LIST:
- cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
- key->partcollation[0],
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
+ partcollation[0],
bound_datums[0],
*(Datum *) probe));
break;
@@ -2273,12 +2282,14 @@ partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo,
*/
bool lower = boundinfo->indexes[offset] < 0;
- cmpval = partition_rbound_cmp(key,
- bound_datums, kind, lower,
+ cmpval = partition_rbound_cmp(partnatts, partsupfunc,
+ partcollation, bound_datums,
+ kind, lower,
(PartitionRangeBound *) probe);
}
else
- cmpval = partition_rbound_datum_cmp(key,
+ cmpval = partition_rbound_datum_cmp(partnatts, partsupfunc,
+ partcollation,
bound_datums, kind,
(Datum *) probe);
break;
@@ -2286,7 +2297,7 @@ partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo,
default:
elog(ERROR, "unexpected partition strategy: %d",
- (int) key->strategy);
+ (int) boundinfo->strategy);
}
return cmpval;
@@ -2320,7 +2331,8 @@ partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo,
int32 cmpval;
mid = (lo + hi + 1) / 2;
- cmpval = partition_bound_cmp(key, boundinfo, mid, probe,
+ cmpval = partition_bound_cmp(key->partnatts, key->partsupfunc,
+ key->partcollation, boundinfo, mid, probe,
probe_is_bound);
if (cmpval <= 0)
{
--
1.7.9.5
0011-WIP-Partition-wise-join-for-1-1-1-0-0-1-partition-ma.patchtext/x-patch; charset=US-ASCII; name=0011-WIP-Partition-wise-join-for-1-1-1-0-0-1-partition-ma.patchDownload
From a650bbfa5510aa8db87d36be9def50d265779a3e Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>
Date: Wed, 9 Aug 2017 12:30:34 +0530
Subject: [PATCH 11/11] WIP Partition-wise join for 1:1, 1:0, 0:1 partition
matching.
Earlier version of partition-wise join implementation allowed
partition-wise join between two relations with exactly same partition
bounds. This commit allows partition-wise join to be applied under
following conditions
1. the partition bounds of joining relations are such that rows from
given partition on one side join can join with rows from maximum one
partition on the other side i.e. bounds of a given partition on one
side match/overlap with those of maximum one partition on the other
side. If the mapping happens to be m:n where m > 1 or n > 1, we have
to gang multiple partition relations together into a single relation.
This means that we have to add simple relations during join
processing, something which is not supported right now. ALso, in such
a case, different pairs of joining relations can produce different
partition bounds for the same join relation, which again is not
supported right now.
2. For every partition on outer side that can contribute to the result
of an OUTER side, there exists at least one (taken along with item 1,
it means exactly one) matching partition on the inner side. To
support partition-wise join when the inner matching partition doesn't
exist, we have to add a dummy base relation corresponding to the
non-existent inner partition. We don't have support add base relations
during join processing.
This commit is not complete yet.
Ashutosh Bapat.
---
src/backend/catalog/partition.c | 1231 +++++++++++++++++++++++++++++++++
src/backend/optimizer/path/joinrels.c | 77 ++-
src/backend/optimizer/util/relnode.c | 42 +-
src/include/catalog/partition.h | 6 +
4 files changed, 1325 insertions(+), 31 deletions(-)
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index d42e1b5..eb35fab 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -141,6 +141,38 @@ static int32 partition_bound_cmp(int partnatts, FmgrInfo *partsupfunc,
static int partition_bound_bsearch(PartitionKey key,
PartitionBoundInfo boundinfo,
void *probe, bool probe_is_bound, bool *is_equal);
+static PartitionBoundInfo partition_range_bounds_merge(int partnatts,
+ FmgrInfo *supfuncs, Oid *collations,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2);
+static PartitionBoundInfo partition_list_bounds_merge(int partnatts,
+ FmgrInfo *partsupfunc, Oid *collations,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2);
+static void generate_matching_part_pairs(int *mergemap1, int npart1,
+ int *mergemap2, int nparts2, JoinType jointype,
+ int nparts, List **parts1, List **parts2);
+static PartitionBoundInfo build_merged_partition_bounds(char strategy,
+ List *merged_datums, List *merged_indexes,
+ List *merged_contents, int null_index);
+static int map_and_merge_partitions(int *partmap1, int *mergemap1, int index1,
+ int *partmap2, int *mergemap2, int index2,
+ int *next_index);
+static int32 partition_range_bound_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *collations, PartitionRangeBound *bound1,
+ PartitionRangeBound *bound2);
+static int partition_range_cmp(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, PartitionRangeBound *lower_bound1,
+ PartitionRangeBound *upper_bound1,
+ PartitionRangeBound *lower_bound2,
+ PartitionRangeBound *upper_bound2, bool *overlap);
+static bool partition_range_merge_next_lb(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, Datum *next_lb_datums,
+ PartitionRangeDatumKind *next_lb_kind,
+ List **merged_datums, List **merged_kinds,
+ List **merged_indexes);
/*
* RelationBuildPartitionDesc
@@ -2348,3 +2380,1202 @@ partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo,
return lo;
}
+
+/*
+ * Merge the given partition bounds.
+ *
+ * If given partition bounds can not be merged, return NULL.
+ *
+ * The function also returns two lists of partition indexes one for each of the
+ * joining relations. Both the lists contain the same number of elements. The
+ * partition indexes at the same positions in the list indicate partitions from
+ * each side to be joined and their position corresponds to the index of
+ * partition to which the results of the child-join belong in the partitioned
+ * join.
+ */
+extern PartitionBoundInfo
+partition_bounds_merge(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2)
+{
+ PartitionBoundInfo merged_bounds;
+ char strategy;
+
+ /* Bail out if partitioning strategies are different. */
+ if (boundinfo1->strategy != boundinfo2->strategy)
+ return NULL;
+
+ *parts1 = NIL;
+ *parts2 = NIL;
+ strategy = boundinfo1->strategy;
+ if (strategy == PARTITION_STRATEGY_LIST)
+ merged_bounds = partition_list_bounds_merge(partnatts, partsupfunc,
+ partcollation, boundinfo1,
+ nparts1, boundinfo2,
+ nparts2, jointype, parts1,
+ parts2);
+ else if (strategy == PARTITION_STRATEGY_RANGE)
+ merged_bounds = partition_range_bounds_merge(partnatts, partsupfunc,
+ partcollation, boundinfo1,
+ nparts1, boundinfo2,
+ nparts2, jointype, parts1,
+ parts2);
+ else
+ elog(ERROR, "unexpected partition strategy: %d", strategy);
+
+ Assert(merged_bounds || (*parts1 == NIL && *parts2 == NIL));
+ return merged_bounds;
+}
+
+/*
+ * partition_get_range_bounds
+ *
+ * Given the index of lower bound in datums array, return lower and upper
+ * bounds and the index of the partition with that lower bound.
+ */
+static int
+partition_get_range_bounds(PartitionBoundInfo bi, int lb_index,
+ PartitionRangeBound *lower,
+ PartitionRangeBound *upper)
+{
+ int part_index;
+
+ /* A lower bound should have at least one more bound after it. */
+ Assert(lb_index < bi->ndatums - 1);
+
+ /* The lower bound should correspond to a valid partition. */
+ part_index = bi->indexes[lb_index + 1];
+ Assert(part_index >= 0);
+
+ lower->kind = bi->kind[lb_index];
+ lower->datums = bi->datums[lb_index];
+ lower->lower = true;
+ upper->kind = bi->kind[lb_index + 1];
+ upper->datums = bi->datums[lb_index + 1];
+ upper->lower = false;
+
+ return part_index;
+}
+
+/*
+ * partition_range_get_next_lb_index
+ *
+ * Given the index of lower bound in datums array return the
+ * index of lower bound of the next partition. When the given index corresponds
+ * to the last partition, return -1.
+ */
+static int
+partition_range_get_next_lb_index(PartitionBoundInfo bi, int lb_index)
+{
+ /* A lower bound should have at least one more bound after it. */
+ Assert(lb_index < bi->ndatums - 1);
+
+ /* The lower bound should correspond to a valid partition. */
+ Assert(bi->indexes[lb_index + 1] >= 0);
+
+ /*
+ * If there are no bounds left beyond the upper bound, we have reached the
+ * last partition.
+ */
+ if (lb_index + 2 < bi->ndatums)
+ {
+ /*
+ * If the bound next to the upper bound corresponds to no partition,
+ * that's the next lower bound of the next partition. Otherwise, the
+ * current upper bound is the lower bound of the next partition.
+ */
+ if (bi->indexes[lb_index + 2] < 0)
+ return lb_index + 2;
+ else
+ return lb_index + 1;
+ }
+ else
+ return -1;
+}
+
+static int32
+partition_range_bound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *collations,
+ PartitionRangeBound *bound1,
+ PartitionRangeBound *bound2)
+{
+ return partition_rbound_cmp(partnatts, partsupfunc, collations,
+ bound1->datums, bound1->kind, bound1->lower,
+ bound2);
+}
+
+/*
+ * partition_range_cmp
+ *
+ * Compare the bounds of two range partitions and return <, = or > 0, if the
+ * first partition's upper bound is lower than, equal to or higher than the
+ * second partition's upper bound resp.
+ *
+ * Also, set overlaps to true, if the ranges overlap, otherwise set it to
+ * false.
+ */
+static int
+partition_range_cmp(int partnatts, FmgrInfo *supfuncs, Oid *collations,
+ PartitionRangeBound *lower_bound1,
+ PartitionRangeBound *upper_bound1,
+ PartitionRangeBound *lower_bound2,
+ PartitionRangeBound *upper_bound2, bool *overlap)
+{
+ /*
+ * Compare upper bound of the first partition with the lower bound of the
+ * second and vice-versa. If lower bound is higher than the upper bound,
+ * the partitions are not overlapping. All other cases indicate overlapping
+ * partitions.
+ * TODO: Add a testcase which has lower and upper bound matching exactly.
+ * Lower bound is inclusive and upper bound is exclusive, so even if the
+ * datums match, the bounds do not match exactly.
+ */
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ lower_bound1, upper_bound2) > 0)
+ {
+ *overlap = false;
+ return 1;
+ }
+ else if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ lower_bound2, upper_bound1) > 0)
+ {
+ *overlap = false;
+ return -1;
+ }
+ else
+ {
+ *overlap = true;
+ return partition_range_bound_cmp(partnatts, supfuncs, collations,
+ upper_bound1, upper_bound2);
+ }
+}
+
+/*
+ * partition_range_merge
+ *
+ * Merge the partition bounds of given two partitions such that the join
+ * between the given two partitions fits merged bounds.
+ *
+ * "merged_upper" will be set to one of the given upper bounds and
+ * "merged_lower" will be set to one of the given lower bounds.
+ */
+static void
+partition_range_merge(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, JoinType jointype,
+ PartitionRangeBound *left_lb,
+ PartitionRangeBound *left_ub,
+ PartitionRangeBound *right_lb,
+ PartitionRangeBound *right_ub,
+ PartitionRangeBound **merged_lb,
+ PartitionRangeBound **merged_ub)
+{
+ /*
+ * An outer join will have all the rows from the outer side, so merged
+ * bounds will be same as the outer bounds. An inner join will have rows
+ * that fit both the bounds, thus lower merged bound will be higher of two
+ * lower bounds and upper merged bound will be lower of the two upper
+ * bounds.
+ */
+ switch (jointype)
+ {
+ case JOIN_LEFT:
+ case JOIN_ANTI:
+ *merged_ub = left_ub;
+ *merged_lb = left_lb;
+ break;
+
+ case JOIN_RIGHT:
+ *merged_ub = right_ub;
+ *merged_lb = right_lb;
+ break;
+
+ case JOIN_INNER:
+ case JOIN_SEMI:
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_ub, right_ub) < 0)
+ *merged_ub = left_ub;
+ else
+ *merged_ub = right_ub;
+
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_lb, right_lb) > 0)
+ *merged_lb = left_lb;
+ else
+ *merged_lb = right_lb;
+ break;
+
+ case JOIN_FULL:
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_ub, right_ub) > 0)
+ *merged_ub = left_ub;
+ else
+ *merged_ub = right_ub;
+
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_lb, right_lb) < 0)
+ *merged_lb = left_lb;
+ else
+ *merged_lb = right_lb;
+ break;
+
+ default:
+ elog(ERROR, "Unexpected join type %d", jointype);
+ }
+
+ return;
+}
+
+/*
+ * Add the lower bound of the next range to the list of bounds, if the lower
+ * bound is higher or equal to the previous upper bound. If successful return
+ * true, otherwise false.
+ */
+static bool
+partition_range_merge_next_lb(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, Datum *next_lb_datums,
+ PartitionRangeDatumKind *next_lb_kind,
+ List **merged_datums, List **merged_kinds,
+ List **merged_indexes)
+{
+ int cmpval;
+
+ if (!*merged_datums)
+ {
+ Assert(!*merged_kinds && !*merged_indexes);
+ cmpval = 1;
+ }
+ else
+ {
+ PartitionRangeBound prev_ub;
+
+ prev_ub.datums = llast(*merged_datums);
+ prev_ub.kind = llast(*merged_kinds);
+ prev_ub.lower = false;
+
+ /*
+ * TODO: explain why do we pass lower to be false for the next lower
+ * bound.
+ */
+ cmpval = partition_rbound_cmp(partnatts, supfuncs, collations,
+ next_lb_datums, next_lb_kind, false,
+ &prev_ub);
+ }
+
+ /*
+ * The lower bound is lower than the last upper bound, thus does not fit
+ * the bounds created so far and hence can not be merged with the existing
+ * bounds.
+ */
+ if (cmpval < 0)
+ return false;
+
+ /*
+ * Add bounds of the new merged partition. If the next lower bound is
+ * higher than the last upper bound, add new range with index
+ * corresponding to the lower bound as -1. If the merged lower bound
+ * is same as the last merged upper bound, the last upper bound will be
+ * reused as the lower bound of the next range.
+ */
+ if (cmpval > 0)
+ {
+ *merged_datums = lappend(*merged_datums, next_lb_datums);
+ *merged_kinds = lappend(*merged_kinds, next_lb_kind);
+ *merged_indexes = lappend_int(*merged_indexes, -1);
+ }
+
+ return true;
+}
+
+/*
+ * Merge given two range partition bounds.
+ *
+ * Work horse function for partition_bounds_merge() for range partitioned
+ * tables.
+ *
+ * TODO: for an anti-join, the caller is supposed to send the outer relation as
+ * left relation. May be we should rename left and right as inner and outer. We
+ * don't need to handle RIGHT joins in this function, so renaming them as outer
+ * and inner is fine.
+ */
+static PartitionBoundInfo
+partition_range_bounds_merge(int partnatts, FmgrInfo *supfuncs, Oid *collations,
+ PartitionBoundInfo left_bi, int left_nparts,
+ PartitionBoundInfo right_bi, int right_nparts,
+ JoinType jointype, List **left_parts, List **right_parts)
+{
+ int *left_pmap;
+ int *left_mmap;
+ int *right_pmap;
+ int *right_mmap;
+ int cnt1;
+ int cnt2;
+ int left_part;
+ int right_part;
+ PartitionBoundInfo merged_bounds = NULL;
+ bool merged = true; /* By default we ranges are merge-able. */
+ int left_lb_index;
+ int right_lb_index;
+ int next_index;
+ int cmpval;
+ List *merged_datums = NIL;
+ List *merged_indexes = NIL;
+ List *merged_kinds = NIL;
+
+ Assert(left_bi->strategy == right_bi->strategy &&
+ left_bi->strategy == PARTITION_STRATEGY_RANGE);
+
+ *left_parts = NIL;
+ *right_parts = NIL;
+
+ /* Allocate and initialize partition maps. */
+ left_pmap = (int *) palloc(sizeof(int) * left_nparts);
+ left_mmap = (int *) palloc(sizeof(int) * left_nparts);
+ right_pmap = (int *) palloc(sizeof(int) * right_nparts);
+ right_mmap = (int *) palloc(sizeof(int) * right_nparts);
+
+ for (cnt1 = 0; cnt1 < left_nparts; cnt1++)
+ {
+ left_pmap[cnt1] = -1;
+ left_mmap[cnt1] = -1;
+ }
+ for (cnt2 = 0; cnt2 < right_nparts; cnt2++)
+ {
+ right_pmap[cnt2] = -1;
+ right_mmap[cnt2] = -1;
+ }
+
+ left_lb_index = 0;
+ right_lb_index = 0;
+ next_index = 0;
+ while (left_lb_index >= 0 && right_lb_index >= 0)
+ {
+ PartitionRangeBound left_lb;
+ PartitionRangeBound left_ub;
+ PartitionRangeBound right_lb;
+ PartitionRangeBound right_ub;
+ PartitionRangeBound *merged_lb = NULL;
+ PartitionRangeBound *merged_ub = NULL;
+ int merged_index = -1;
+ bool overlap;
+
+ /* Get the range bounds of the next partition. */
+ left_part = partition_get_range_bounds(left_bi, left_lb_index,
+ &left_lb, &left_ub);
+ right_part = partition_get_range_bounds(right_bi, right_lb_index,
+ &right_lb, &right_ub);
+
+ cmpval = partition_range_cmp(partnatts, supfuncs, collations,
+ &left_lb, &left_ub, &right_lb, &right_ub,
+ &overlap);
+
+ if (overlap)
+ {
+ /* Overlapping ranges, try merging. */
+ partition_range_merge(partnatts, supfuncs, collations, jointype,
+ &left_lb, &left_ub, &right_lb, &right_ub,
+ &merged_lb, &merged_ub);
+ merged_index = map_and_merge_partitions(left_pmap, left_mmap,
+ left_part, right_pmap,
+ right_mmap, right_part,
+ &next_index);
+
+ if (merged_index < 0)
+ {
+ merged = false;
+ break;
+ }
+ }
+
+ if (cmpval == 0)
+ {
+ Assert(overlap);
+
+ /* Move to the next pair of partitions. */
+ left_lb_index = partition_range_get_next_lb_index(left_bi,
+ left_lb_index);
+ right_lb_index = partition_range_get_next_lb_index(right_bi,
+ right_lb_index);
+ }
+ else if (cmpval < 0)
+ {
+ /*
+ * If the partition on the left was not mapped to any partition on
+ * the right. Any row from that partition will not have a matching
+ * row from the other relation. So the partition will be part of
+ * the join if it's an anti-join or the left side is the outer
+ * side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_SEMI ||
+ jointype == JOIN_RIGHT)
+ {
+ /* Nothing to do. */
+ }
+ else if (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI)
+ {
+ if (left_mmap[left_part] < 0)
+ {
+ left_mmap[left_part] = next_index++;
+ merged_index = left_mmap[left_part];
+ merged_lb = &left_lb;
+ merged_ub = &left_ub;
+ }
+ }
+ else
+ {
+ /* Don't know what to do with other join types. Bail out. */
+ merged = false;
+ }
+
+ /* Move to the next partition on the left side. */
+ left_lb_index = partition_range_get_next_lb_index(left_bi,
+ left_lb_index);
+ }
+ else
+ {
+ Assert(cmpval > 0);
+
+ /*
+ * If the partition on the right was not mapped to any partition on
+ * the left. Any row from that partition will not have a matching
+ * row from the other relation. So the partition will be part of
+ * the join if the right side is the outer side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_SEMI ||
+ jointype == JOIN_LEFT || jointype == JOIN_ANTI)
+ merged_index = -1;
+ else if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ if (right_mmap[right_part] < 0)
+ {
+ right_mmap[right_part] = next_index++;
+ merged_index = right_mmap[right_part];
+ merged_lb = &right_lb;
+ merged_ub = &right_ub;
+ }
+ }
+ else
+ {
+ /* Don't know what to do with other join types. Bail out. */
+ merged = false;
+ }
+
+ /* Move to the next partition on the right side. */
+ right_lb_index = partition_range_get_next_lb_index(right_bi,
+ right_lb_index);
+ }
+
+ if (!merged)
+ break;
+
+ /* A skipped partition is not added to merged bounds. */
+ if (merged_index < 0)
+ continue;
+
+ /*
+ * We have a valid partition index for the next partition of join. The
+ * partition should have valid range.
+ */
+ Assert(merged_lb && merged_ub);
+
+ /* Try merging merged lower bound with the last upper bound. */
+ merged = partition_range_merge_next_lb(partnatts, supfuncs,
+ collations, merged_lb->datums,
+ merged_lb->kind, &merged_datums,
+ &merged_kinds, &merged_indexes);
+ if (merged)
+ {
+ /* Add upper bound with the merged partition index. */
+ merged_datums = lappend(merged_datums, merged_ub->datums);
+ merged_kinds = lappend(merged_kinds, merged_ub->kind);
+ merged_indexes = lappend_int(merged_indexes, merged_index);
+ }
+ else
+ break;
+ }
+
+ /*
+ * We will run the above loop till we exhaust ranges of at least one side
+ * unless we failed to merge the ranges.
+ */
+ Assert (!merged || (left_lb_index < 0 || right_lb_index < 0));
+
+ /*
+ * Handle any remaining partition bounds. If remaining partitions fall on
+ * the inner side of the join, none of the rows in those partition are
+ * going to be joined with any row on the outer side and hence those
+ * partitions will not be part of the join result. Hence only consider the
+ * remaining partitions on the outer side of the join.
+ */
+ if (merged &&
+ ((left_lb_index >= 0 &&
+ (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_LEFT)) ||
+ (right_lb_index >= 0 &&
+ (jointype == JOIN_RIGHT || jointype == JOIN_FULL))))
+ {
+ int bound_index = -1;
+ PartitionBoundInfo rem_bi = NULL;
+ int *mmap = NULL;
+
+ if (left_lb_index >= 0)
+ {
+ Assert(jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI);
+ bound_index = left_lb_index;
+ rem_bi = left_bi;
+ mmap = left_mmap;
+ }
+ else if (right_lb_index >= 0)
+ {
+ Assert(jointype == JOIN_RIGHT || jointype == JOIN_FULL);
+ bound_index = right_lb_index;
+ rem_bi = right_bi;
+ mmap = right_mmap;
+ }
+
+ Assert(bound_index >= 0 && rem_bi && mmap);
+
+ /*
+ * Merge lower bound of the next range with the upper bound of last
+ * range.
+ */
+ merged = partition_range_merge_next_lb(partnatts, supfuncs, collations,
+ rem_bi->datums[bound_index],
+ rem_bi->kind[bound_index],
+ &merged_datums, &merged_kinds,
+ &merged_indexes);
+
+ /*
+ * Rest of the bounds correspond to valid ranges so add them after
+ * remapping their partitions as required.
+ */
+ for (bound_index++; merged && bound_index < rem_bi->ndatums;
+ bound_index++)
+ {
+ Datum *datums = rem_bi->datums[bound_index];
+ int index = rem_bi->indexes[bound_index];
+ int part_index;
+
+ if (index < 0)
+ part_index = index;
+ else
+ {
+ if (mmap[index] < 0)
+ mmap[index] = next_index++;
+ part_index = mmap[index];
+ }
+
+ merged_indexes = lappend_int(merged_indexes, part_index);
+ merged_datums = lappend(merged_datums, datums);
+ merged_kinds = lappend(merged_kinds,
+ rem_bi->kind[bound_index]);
+ }
+ }
+
+ /* Create PartitionBoundInfo */
+ if (merged)
+ {
+ /* Use maps to match partition from the joining relations. */
+ generate_matching_part_pairs(left_mmap, left_nparts, right_mmap,
+ right_nparts, jointype, next_index,
+ left_parts, right_parts);
+
+ /* Craft a PartitionBoundInfo to return. */
+ if (*left_parts && *right_parts)
+ {
+ Assert(list_length(*left_parts) == list_length(*right_parts));
+ Assert(list_length(*left_parts) == next_index);
+ merged_bounds = build_merged_partition_bounds(left_bi->strategy,
+ merged_datums,
+ merged_indexes,
+ merged_kinds,
+ -1);
+ }
+ }
+
+ list_free(merged_datums);
+ list_free(merged_indexes);
+ pfree(left_pmap);
+ pfree(right_pmap);
+ pfree(left_mmap);
+ pfree(right_mmap);
+
+ /* Free any memory we used in this function. */
+ return merged_bounds;
+}
+
+/*
+ * partition_bounds_merge()'s arm for list partitioned tables.
+ *
+ * The function builds the maps of matching partitions from either relation. It
+ * builds the list of partition key values that may appear in the join result
+ * alongwith the list of indexes of partitions of join to which those values
+ * belong. It then crafts a PartitionBoundInfo structure representing the
+ * partition bounds of the join result.
+ */
+static PartitionBoundInfo
+partition_list_bounds_merge(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, PartitionBoundInfo left_bi,
+ int left_nparts, PartitionBoundInfo right_bi,
+ int right_nparts, JoinType jointype, List **left_parts,
+ List **right_parts)
+{
+ int *left_pmap; /* left to right partition map */
+ int *left_mmap; /* left to merged partition map */
+ int *right_pmap; /* right to left partition map */
+ int *right_mmap; /* right to merged partition map */
+ int cntl;
+ int cntr;
+ bool merged = true;
+ List *merged_datums = NIL;
+ List *merged_indexes = NIL;
+ int next_index = 0;
+ int null_index;
+ PartitionBoundInfo merged_bounds = NULL;
+ int *left_indexes = left_bi->indexes;
+ int *right_indexes = right_bi->indexes;
+ int left_ni = left_bi->null_index;
+ int right_ni = right_bi->null_index;
+
+ Assert(left_bi->strategy == right_bi->strategy &&
+ left_bi->strategy == PARTITION_STRATEGY_LIST);
+
+ /* List partitions do not require unbounded ranges. */
+ Assert(!left_bi->kind && !right_bi->kind);
+
+ /* Allocate and initialize partition maps. */
+ left_pmap = (int *) palloc(sizeof(int) * left_nparts);
+ left_mmap = (int *) palloc(sizeof(int) * left_nparts);
+ right_pmap = (int *) palloc(sizeof(int) * right_nparts);
+ right_mmap = (int *) palloc(sizeof(int) * right_nparts);
+
+ /* Initialize partition maps. */
+ for (cntl = 0; cntl < left_nparts; cntl++)
+ {
+ left_pmap[cntl] = -1;
+ left_mmap[cntl] = -1;
+ }
+ for (cntr = 0; cntr < right_nparts; cntr++)
+ {
+ right_pmap[cntr] = -1;
+ right_mmap[cntr] = -1;
+ }
+
+ cntl = cntr = 0;
+ while (cntl < left_bi->ndatums && cntr < right_bi->ndatums)
+ {
+ Datum *ldatums = left_bi->datums[cntl];
+ Datum *rdatums = right_bi->datums[cntr];
+ int l_index = left_indexes[cntl];
+ int r_index = right_indexes[cntr];
+ int cmpval;
+ int merged_index;
+ Datum *merged_datum;
+
+ /* Every list datum should map to a valid partition index. */
+ Assert(l_index >= 0 && r_index >= 0);
+
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
+ partcollation[0], ldatums[0],
+ rdatums[0]));
+ if (cmpval == 0)
+ {
+ /*
+ * Try matching partitions containing the matching datums. If
+ * successful, add the datum to the merged bounds with index of
+ * merged partition containing it.
+ */
+ merged_datum = ldatums;
+ merged_index = map_and_merge_partitions(left_pmap, left_mmap, l_index,
+ right_pmap, right_mmap, r_index,
+ &next_index);
+
+ if (merged_index < 0)
+ {
+ merged = false;
+ break;
+ }
+
+ /* Move to the next pair of bounds. */
+ cntl++;
+ cntr++;
+ }
+ else if (cmpval < 0)
+ {
+ /*
+ * This list datum is present in the left side but not the right
+ * side. So it will appear in the join when the left side is outer
+ * side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_RIGHT ||
+ jointype == JOIN_SEMI)
+ merged_index = -1;
+ else if (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI)
+ {
+ if (left_mmap[l_index] < 0)
+ left_mmap[l_index] = next_index++;
+ merged_index = left_mmap[l_index];
+ merged_datum = ldatums;
+ }
+ else
+ {
+ /* Don't know what to do with other join types. */
+ merged = false;
+ break;
+ }
+
+ /* Move to the next datum on the left side. */
+ cntl++;
+ }
+ else
+ {
+ Assert(cmpval > 0);
+
+ /*
+ * This list datum is present in the right side but not the left
+ * side. So it will appear in the join when the right side is outer
+ * side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_LEFT ||
+ jointype == JOIN_SEMI || jointype == JOIN_ANTI)
+ merged_index = -1;
+ else if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ /*
+ * Every list value on the outer side will appear in the
+ * join. Find the merged partition to which this value
+ * belongs.
+ */
+ if (right_mmap[r_index] < 0)
+ right_mmap[r_index] = next_index++;
+ merged_index = right_mmap[r_index];
+ merged_datum = rdatums;
+ }
+ else
+ {
+ /* Don't know what to do with other join types. */
+ merged = false;
+ break;
+ }
+
+ /* Move to the next datum on the right side. */
+ cntr++;
+ }
+
+ /*
+ * Add the datum with appropriate index in the list of datums, if the
+ * rows containing that datum are deemed to be part of the join.
+ */
+ if (merged_index >= 0)
+ {
+ merged_indexes = lappend_int(merged_indexes, merged_index);
+ merged_datums = lappend(merged_datums, merged_datum);
+ }
+ }
+
+ /*
+ * If merge is unsuccessful, bail out without any further processing.
+ * That leaks the memory allocated in this function. So, try not to leak
+ * memory.
+ */
+ if (!merged)
+ goto merge_failed;
+
+ /* We should have exhausted datums on at least one side. */
+ Assert(cntr >= right_bi->ndatums || cntl >= left_bi->ndatums);
+
+ /*
+ * Add any remaining list values on the outer side, assigning partition
+ * indexes if required.
+ */
+ if (jointype == JOIN_LEFT || jointype == JOIN_FULL || jointype == JOIN_ANTI)
+ {
+ for (;cntl < left_bi->ndatums; cntl++)
+ {
+ Datum *ldatums = left_bi->datums[cntl];
+ int l_index = left_indexes[cntl];
+
+ if (left_mmap[l_index] < 0)
+ left_mmap[l_index] = next_index++;
+ merged_indexes = lappend_int(merged_indexes, left_mmap[l_index]);
+ merged_datums = lappend(merged_datums, ldatums);
+ }
+ }
+
+ if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ for (;cntr < right_bi->ndatums; cntr++)
+ {
+ Datum *rdatums = right_bi->datums[cntr];
+ int r_index = right_indexes[cntr];
+
+ if (right_mmap[r_index] < 0)
+ right_mmap[r_index] = next_index++;
+ merged_indexes = lappend_int(merged_indexes, right_mmap[r_index]);
+ merged_datums = lappend(merged_datums, rdatums);
+ }
+ }
+
+ /*
+ * Merge NULL partitions if any. Find the index of merged partition to
+ * which the NULL values belong in the join result. We can eliminate a NULL
+ * partition when it appears only in the inner relation.
+ */
+ if (!partition_bound_accepts_nulls(left_bi) &&
+ !partition_bound_accepts_nulls(right_bi))
+ null_index = -1;
+ else if (partition_bound_accepts_nulls(left_bi) &&
+ !partition_bound_accepts_nulls(right_bi))
+ {
+ if (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI)
+ {
+ if (left_mmap[left_ni] < 0)
+ left_mmap[left_ni] = next_index++;
+ null_index = left_mmap[left_ni];
+ }
+ else
+ null_index = -1;
+ }
+ else if (!partition_bound_accepts_nulls(left_bi) &&
+ partition_bound_accepts_nulls(right_bi))
+ {
+ if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ if (right_mmap[right_ni] < 0)
+ right_mmap[right_ni] = next_index++;
+ null_index = right_mmap[right_ni];
+ }
+ else
+ null_index = -1;
+ }
+ else
+ {
+ /* Both the relations have NULL partitions, try merging them. */
+ null_index = map_and_merge_partitions(left_pmap, left_mmap,
+ left_ni, right_pmap,
+ right_mmap, right_ni,
+ &next_index);
+ if (null_index < 0)
+ merged = false;
+ }
+
+ /* If successful build the output structures. */
+ if (merged)
+ {
+
+ /* Use maps to match partition from the joining relations. */
+ generate_matching_part_pairs(left_mmap, left_nparts, right_mmap,
+ right_nparts, jointype, next_index,
+ left_parts, right_parts);
+ /* Craft a PartitionBoundInfo to return. */
+ if (*left_parts && *right_parts)
+ {
+ Assert(list_length(*left_parts) == list_length(*right_parts));
+ Assert(list_length(*left_parts) == next_index);
+ merged_bounds = build_merged_partition_bounds(left_bi->strategy,
+ merged_datums,
+ merged_indexes, NIL,
+ null_index);
+ }
+ }
+
+merge_failed:
+ list_free(merged_datums);
+ list_free(merged_indexes);
+ pfree(left_pmap);
+ pfree(right_pmap);
+ pfree(left_mmap);
+ pfree(right_mmap);
+
+ return merged_bounds;
+}
+
+/*
+ * map_and_merge_partitions
+ *
+ * If the two given partitions (given by index1 and index2 resp.) are already
+ * mapped to each other return the index of corresponding partition in the
+ * merged set of partitions. If they do not have a merged partition associated
+ * with them, assign a new merged partition index. If the partitions are
+ * already mapped and their mapped partitions are different from each other,
+ * they can not be merged, so return -1.
+ *
+ * partmap1[i] gives the partition of relation 2 which matches ith partition of
+ * relation 1. Similarly for partmap2.
+ *
+ * mergemap1[i] gives the partition in the merged set to which ith partition of
+ * relation 1 maps to. Similarly for mergemap2.
+ *
+ * index1 and index2 are the indexes of matching partition from respective
+ * relations.
+ *
+ * *next_index is used and incremented when the given partitions require a new
+ * merged partition.
+ */
+static int
+map_and_merge_partitions(int *partmap1, int *mergemap1, int index1,
+ int *partmap2, int *mergemap2, int index2,
+ int *next_index)
+{
+ int merged_index;
+
+ /*
+ * If both the partitions are not mapped to each other, update the
+ * maps.
+ */
+ if (partmap1[index1] < 0 && partmap2[index2] < 0)
+ {
+ partmap1[index1] = index2;
+ partmap2[index2] = index1;
+ }
+
+ /*
+ * If the given to partitions map to each other, find the corresponding
+ * merged partition index .
+ */
+ if (partmap1[index1] == index2 && partmap2[index2] == index1)
+ {
+ /*
+ * If both the partitions are mapped to the same merged partition, get
+ * the index of merged partition.
+ */
+ if (mergemap1[index1] == mergemap2[index2])
+ {
+ merged_index = mergemap1[index1];
+
+ /*
+ * If the given two partitions do not have a merged partition
+ * associated with them, allocate a new merged partition.
+ */
+ if (merged_index < 0)
+ {
+ merged_index = *next_index;
+ *next_index = *next_index + 1;
+ mergemap1[index1] = merged_index;
+ mergemap2[index2] = merged_index;
+ }
+ }
+
+ /*
+ * If partition from one relation was mapped to a merged partition but
+ * not the partition from the other relation, map the same merged
+ * partition to the partition from other relation, since matching
+ * partitions map to the same merged partition.
+ */
+ else if (mergemap1[index1] >= 0 && mergemap2[index2] < 0)
+ {
+ mergemap2[index2] = mergemap1[index1];
+ merged_index = mergemap1[index1];
+ }
+ else if (mergemap1[index1] < 0 && mergemap2[index2] >= 0)
+ {
+ mergemap1[index1] = mergemap2[index2];
+ merged_index = mergemap2[index2];
+ }
+ else
+ {
+ Assert(mergemap1[index1] != mergemap2[index2] &&
+ mergemap1[index1] >= 0 && mergemap2[index2] >= 0);
+
+ /*
+ * Both the partitions map to different merged partitions. This
+ * means that multiple partitions from one relation matches to one
+ * partition from the other relation. Partition-wise join does not
+ * handle this case right now, since it requires ganging multiple
+ * partitions together (into one RelOptInfo).
+ */
+ merged_index = -1;
+ }
+ }
+ else
+ {
+ /*
+ * Multiple partitions from one relation map to one partition from the
+ * other relation. Partition-wise join does not handle this case right
+ * now, since it requires ganging multiple partitions together (into
+ * one RelOptInfo).
+ */
+ merged_index = -1;
+ }
+
+ return merged_index;
+}
+
+/*
+ * generate_matching_part_pairs
+ *
+ * Given the merged partition to which partition on either side of join map,
+ * produce the list pairs of partitions which when joined produce the merged
+ * partitions in the order of merged partition indexes.
+ *
+ * If successful, the pairs of partitions are returned as two separate lists
+ * one for each side. Otherwise, those lists will be set to NIL.
+ *
+ * TODO: rename the sides as outer and inner. You may not need to support
+ * JOIN_RIGHT, since we won't see that type here.
+ */
+static void
+generate_matching_part_pairs(int *mergemap1, int nparts1, int *mergemap2,
+ int nparts2, JoinType jointype, int nparts,
+ List **parts1, List **parts2)
+{
+ bool merged = true;
+ int **matching_parts;
+ int cnt1;
+ int cnt2;
+
+ matching_parts = (int **) palloc(sizeof(int *) * 2);
+ matching_parts[0] = (int *) palloc(sizeof(int) * nparts);
+ matching_parts[1] = (int *) palloc(sizeof(int) * nparts);
+
+ *parts1 = NIL;
+ *parts2 = NIL;
+
+ for (cnt1 = 0; cnt1 < nparts; cnt1++)
+ {
+ matching_parts[0][cnt1] = -1;
+ matching_parts[1][cnt1] = -1;
+ }
+
+ /* Set pairs of matching partitions. */
+ for (cnt1 = 0; cnt1 < nparts1; cnt1++)
+ {
+ if (mergemap1[cnt1] >= 0)
+ {
+ Assert(mergemap1[cnt1] < nparts);
+ matching_parts[0][mergemap1[cnt1]] = cnt1;
+ }
+ }
+ for (cnt2 = 0; cnt2 < nparts2; cnt2++)
+ {
+ if (mergemap2[cnt2] >= 0)
+ {
+ Assert(mergemap2[cnt2] < nparts);
+ matching_parts[1][mergemap2[cnt2]] = cnt2;
+ }
+ }
+
+ /*
+ * 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 (cnt1 = 0; cnt1 < nparts; cnt1++)
+ {
+ int part1 = matching_parts[0][cnt1];
+ int part2 = matching_parts[1][cnt1];
+
+ /* 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_RIGHT:
+ Assert(part2 >= 0);
+ if (part1 < 0)
+ merged = false;
+ break;
+
+ case JOIN_FULL:
+ if (part1 < 0 || part2 < 0)
+ merged = false;
+ break;
+
+ default:
+ /* We do not know what to do in this case. Bail out. */
+ merged = false;
+ }
+
+ if (!merged)
+ break;
+
+ *parts1 = lappend_int(*parts1, part1);
+ *parts2 = lappend_int(*parts2, part2);
+ }
+
+ pfree(matching_parts[0]);
+ pfree(matching_parts[1]);
+ pfree(matching_parts);
+
+ if (!merged)
+ {
+ list_free(*parts1);
+ list_free(*parts2);
+ *parts1 = NIL;
+ *parts2 = NIL;
+ }
+}
+
+static PartitionBoundInfo
+build_merged_partition_bounds(char strategy, List *merged_datums,
+ List *merged_indexes, List *merged_kinds,
+ int null_index)
+{
+ int cnt;
+ PartitionBoundInfo merged_bounds;
+ ListCell *lc;
+
+ /* We expect the same number of elements in datums and indexes lists. */
+ Assert(list_length(merged_datums) == list_length(merged_indexes));
+
+ merged_bounds = (PartitionBoundInfo) palloc(sizeof(PartitionBoundInfoData));
+ merged_bounds->strategy = strategy;
+ merged_bounds->ndatums = list_length(merged_datums);
+
+ if (strategy == PARTITION_STRATEGY_RANGE)
+ {
+ Assert(list_length(merged_datums) == list_length(merged_kinds));
+ merged_bounds->kind = (PartitionRangeDatumKind **) palloc(sizeof(PartitionRangeDatumKind *) *
+ list_length(merged_kinds));
+ cnt = 0;
+ foreach(lc, merged_kinds)
+ merged_bounds->kind[cnt++] = lfirst(lc);
+
+ /* There are ndatums+1 indexes in case of range partitions */
+ merged_indexes = lappend_int(merged_indexes, -1);
+ }
+ else
+ merged_bounds->kind = NULL;
+
+ cnt = 0;
+ merged_bounds->datums = (Datum **) palloc(sizeof(Datum *) *
+ list_length(merged_datums));
+ foreach(lc, merged_datums)
+ merged_bounds->datums[cnt++] = lfirst(lc);
+
+ merged_bounds->indexes = (int *) palloc(sizeof(int) *
+ list_length(merged_indexes));
+ cnt = 0;
+ foreach(lc, merged_indexes)
+ merged_bounds->indexes[cnt++] = lfirst_int(lc);
+
+ merged_bounds->null_index = null_index;
+
+ return merged_bounds;
+}
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index a97a895..fb8d752 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1308,8 +1308,13 @@ try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
RelOptInfo *joinrel, SpecialJoinInfo *parent_sjinfo,
List *parent_restrictlist)
{
- int nparts;
int cnt_parts;
+ PartitionScheme part_scheme;
+ PartitionBoundInfo join_boundinfo;
+ List *parts1;
+ List *parts2;
+ ListCell *lc1;
+ ListCell *lc2;
/* Guard against stack overflow due to overly deep partition hierarchy. */
check_stack_depth();
@@ -1353,39 +1358,54 @@ try_partition_wise_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;
/*
- * Since we allow partition-wise join only when the partition bounds of
- * the joining relations exactly match, the partition bounds of the join
- * should match those of the joining relations.
+ * Get the list of matching partitions from both sides of the join. While
+ * doing so, we also build the partition bounds of the join relation,
+ * which should match the bounds calculated for other pairs. TODO: why
+ * should every pair result in the same partition bounds?
*/
- Assert(partition_bounds_equal(joinrel->part_scheme->partnatts,
- joinrel->part_scheme->parttyplen,
- joinrel->part_scheme->parttypbyval,
- joinrel->boundinfo, rel1->boundinfo));
- Assert(partition_bounds_equal(joinrel->part_scheme->partnatts,
- joinrel->part_scheme->parttyplen,
- joinrel->part_scheme->parttypbyval,
- joinrel->boundinfo, rel2->boundinfo));
-
- nparts = joinrel->nparts;
-
+ join_boundinfo = partition_bounds_merge(part_scheme->partnatts,
+ part_scheme->partsupfunc,
+ part_scheme->parttypcoll,
+ rel1->boundinfo, rel1->nparts,
+ rel2->boundinfo, rel2->nparts,
+ parent_sjinfo->jointype,
+ &parts1, &parts2);
+
+ Assert(join_boundinfo);
+ Assert(partition_bounds_equal(part_scheme->partnatts,
+ part_scheme->parttyplen,
+ part_scheme->parttypbyval, join_boundinfo,
+ joinrel->boundinfo));
elog(DEBUG3, "join between relations %s and %s is considered for partition-wise join.",
bmsToString(rel1->relids), bmsToString(rel2->relids));
- /* Allocate space to hold child-joins RelOptInfos, if not already done. */
+ /*
+ * 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));
+
+ /* Allocate space for hold child-joins RelOptInfos, if not already done. */
if (!joinrel->part_rels)
- joinrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) * nparts);
+ joinrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *
+ joinrel->nparts);
/*
* Create child-join relations for this partitioned join, if those don't
* exist. Add paths to child-joins for a pair of child relations
* corresponding to the given pair of parent relations.
*/
- for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+ cnt_parts = 0;
+ forboth(lc1, parts1, lc2, parts2)
{
- RelOptInfo *child_rel1 = rel1->part_rels[cnt_parts];
- RelOptInfo *child_rel2 = rel2->part_rels[cnt_parts];
+ int part1 = lfirst_int(lc1);
+ int part2 = lfirst_int(lc2);
+ RelOptInfo *child_rel1;
+ RelOptInfo *child_rel2;
SpecialJoinInfo *child_sjinfo;
List *child_restrictlist;
RelOptInfo *child_joinrel;
@@ -1393,6 +1413,10 @@ try_partition_wise_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];
+
/* We should never try to join two overlapping sets of rels. */
Assert(!bms_overlap(child_rel1->relids, child_rel2->relids));
child_joinrelids = bms_union(child_rel1->relids, child_rel2->relids);
@@ -1425,6 +1449,15 @@ try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
joinrel->part_rels[cnt_parts] = child_joinrel;
}
+ /*
+ * 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,
@@ -1437,7 +1470,11 @@ try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
*/
try_partition_wise_join(root, child_rel1, child_rel2, child_joinrel,
child_sjinfo, child_restrictlist);
+
+ cnt_parts++;
}
+
+ Assert(cnt_parts == joinrel->nparts);
}
/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 72b6832..c58b00c 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -1623,6 +1623,9 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
int partnatts;
int cnt;
PartitionScheme part_scheme;
+ PartitionBoundInfo join_boundinfo;
+ List *parts1;
+ List *parts2;
/* Nothing to do if partition-wise join technique is disabled. */
if (!enable_partition_wise_join)
@@ -1663,17 +1666,26 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
REL_HAS_ALL_PART_PROPS(inner_rel));
/*
- * For now, our partition matching algorithm can match partitions only
- * when the partition bounds of the joining relations are exactly same.
- * So, bail out otherwise.
+ * Every pair of joining relations would yield the same partition bounds
+ * for a given join (TODO: why?) so we compute the bounds only the first
+ * time. Then for every pair we find the pairs of matching partitions from
+ * the joining relations and join those. TODO: Needs a better explanation
+ * of why is this true. TODO: Also there is no reason to have
+ * part_indexes1 and part_indexes2 pulled here just to be freed up later.
+ * So, we might want to do something better.
*/
- if (outer_rel->nparts != inner_rel->nparts ||
- !partition_bounds_equal(part_scheme->partnatts,
- part_scheme->parttyplen,
- part_scheme->parttypbyval,
- outer_rel->boundinfo, inner_rel->boundinfo))
+ join_boundinfo = partition_bounds_merge(part_scheme->partnatts,
+ part_scheme->partsupfunc,
+ part_scheme->parttypcoll,
+ outer_rel->boundinfo,
+ outer_rel->nparts,
+ inner_rel->boundinfo,
+ inner_rel->nparts,
+ jointype, &parts1, &parts2);
+ if (!join_boundinfo)
{
Assert(!IS_PARTITIONED_REL(joinrel));
+ Assert(!parts1 && !parts2);
return;
}
@@ -1686,13 +1698,16 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
!joinrel->nullable_partexprs && !joinrel->part_rels &&
!joinrel->boundinfo);
+ Assert(list_length(parts1) == list_length(parts2));
+
/*
* Join relation is partitioned using same partitioning scheme as the
- * joining relations and has same bounds.
+ * joining relations. It will have as many partitions as the pairs of
+ * matching partitions we found.
*/
joinrel->part_scheme = part_scheme;
- joinrel->boundinfo = outer_rel->boundinfo;
- joinrel->nparts = outer_rel->nparts;
+ joinrel->nparts = list_length(parts1);
+ joinrel->boundinfo = join_boundinfo;
partnatts = joinrel->part_scheme->partnatts;
/*
@@ -1813,4 +1828,9 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
joinrel->nullable_partexprs[cnt] = nullable_partexprs;
}
}
+
+ /* TODO: OR we could actually create the child-join relations here.*/
+ list_free(parts1);
+ list_free(parts2);
+
}
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index 2283c67..056a4f9 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -99,4 +99,10 @@ extern int get_partition_for_tuple(PartitionDispatch *pd,
EState *estate,
PartitionDispatchData **failed_at,
TupleTableSlot **failed_slot);
+extern PartitionBoundInfo partition_bounds_merge(int partnatts,
+ FmgrInfo *supfuncs, Oid *collations,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2);
+
#endif /* PARTITION_H */
--
1.7.9.5
On Mon, Aug 21, 2017 at 12:43 PM, Ashutosh Bapat <
ashutosh.bapat@enterprisedb.com> wrote:
TODOs
-----------
1. Add tests for advanced partition matching algorithm
Hi Ashutosh,
I have applied all partition-wise-join patches (v26) and tested feature. I
have modified partition_join.sql file and added extra test cases to test
partition matching.
Attaching WIP test case patch which as of now have some server crashes and
a data corruptions issue which is commented in the file itself and need to
be removed once issue got solved. Also some of queries is not picking or
picking partition-wise-join as per expectation which may need some
adjustment.
Attachments:
advanced_partition_matching_test.patchtext/x-patch; charset=US-ASCII; name=advanced_partition_matching_test.patchDownload
diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out
index a246d87..e9dd539 100644
--- a/src/test/regress/expected/partition_join.out
+++ b/src/test/regress/expected/partition_join.out
@@ -5,6 +5,9 @@
-- Enable partition-wise join, which by default is disabled.
SET enable_partition_wise_join to true;
--
+-- tests for range partitioned tables.
+--
+--
-- partitioned by a single column
--
CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
@@ -16,15 +19,20 @@ CREATE INDEX iprt1_p1_a on prt1_p1(a);
CREATE INDEX iprt1_p2_a on prt1_p2(a);
CREATE INDEX iprt1_p3_a on prt1_p3(a);
ANALYZE prt1;
+-- prt2 have missing starting 0-50 range and missing ending 550-600
+-- range bounds
CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
-CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250);
+CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (50) TO (250);
CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500);
-CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600);
-INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (550);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0+50, 599-50, 3) i;
CREATE INDEX iprt2_p1_b on prt2_p1(b);
CREATE INDEX iprt2_p2_b on prt2_p2(b);
CREATE INDEX iprt2_p3_b on prt2_p3(b);
ANALYZE prt2;
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
-- inner join
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
@@ -56,10 +64,10 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b =
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
a | c | b | c
-----+------+-----+------
- 0 | 0000 | 0 | 0000
- 150 | 0150 | 150 | 0150
- 300 | 0300 | 300 | 0300
- 450 | 0450 | 450 | 0450
+ 50 | 0050 | 50 | 0050
+ 200 | 0200 | 200 | 0200
+ 350 | 0350 | 350 | 0350
+ 500 | 0500 | 500 | 0500
(4 rows)
-- left outer join, with whole-row reference
@@ -94,25 +102,25 @@ SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER
SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
t1 | t2
--------------+--------------
- (0,0,0000) | (0,0,0000)
- (50,0,0050) |
+ (0,0,0000) |
+ (50,0,0050) | (0,50,0050)
(100,0,0100) |
- (150,0,0150) | (0,150,0150)
- (200,0,0200) |
+ (150,0,0150) |
+ (200,0,0200) | (0,200,0200)
(250,0,0250) |
- (300,0,0300) | (0,300,0300)
- (350,0,0350) |
+ (300,0,0300) |
+ (350,0,0350) | (0,350,0350)
(400,0,0400) |
- (450,0,0450) | (0,450,0450)
- (500,0,0500) |
+ (450,0,0450) |
+ (500,0,0500) | (0,500,0500)
(550,0,0550) |
(12 rows)
-- right outer join
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b;
- QUERY PLAN
----------------------------------------------------------------------
+ QUERY PLAN
+--------------------------------------------------------
Sort
Sort Key: t1.a, t2.b
-> Result
@@ -129,171 +137,150 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHE
-> Hash
-> Seq Scan on prt2_p2 t2_1
Filter: (a = 0)
- -> Nested Loop Left Join
- -> Seq Scan on prt2_p3 t2_2
- Filter: (a = 0)
- -> Index Scan using iprt1_p3_a on prt1_p3 t1_1
- Index Cond: (a = t2_2.b)
-(21 rows)
+ -> Hash Right Join
+ Hash Cond: (t1_1.a = t2_2.b)
+ -> Seq Scan on prt1_p3 t1_1
+ -> Hash
+ -> Seq Scan on prt2_p3 t2_2
+ Filter: (a = 0)
+(22 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b;
a | c | b | c
-----+------+-----+------
- 0 | 0000 | 0 | 0000
- 150 | 0150 | 150 | 0150
- 300 | 0300 | 300 | 0300
- 450 | 0450 | 450 | 0450
- | | 75 | 0075
- | | 225 | 0225
- | | 375 | 0375
- | | 525 | 0525
-(8 rows)
+ 50 | 0050 | 50 | 0050
+ 200 | 0200 | 200 | 0200
+ 350 | 0350 | 350 | 0350
+ 500 | 0500 | 500 | 0500
+ | | 125 | 0125
+ | | 275 | 0275
+ | | 425 | 0425
+(7 rows)
-- full outer join
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
- QUERY PLAN
---------------------------------------------------
+ QUERY PLAN
+---------------------------------------------
Sort
Sort Key: prt1_p1.a, prt2_p1.b
- -> Append
- -> Hash Full Join
- Hash Cond: (prt1_p1.a = prt2_p1.b)
+ -> Hash Full Join
+ Hash Cond: (prt1_p1.a = prt2_p1.b)
+ -> Append
-> Seq Scan on prt1_p1
Filter: (b = 0)
- -> Hash
- -> Seq Scan on prt2_p1
- Filter: (a = 0)
- -> Hash Full Join
- Hash Cond: (prt1_p2.a = prt2_p2.b)
+ -> Seq Scan on prt1_p3
+ Filter: (b = 0)
-> Seq Scan on prt1_p2
Filter: (b = 0)
- -> Hash
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p1
+ Filter: (a = 0)
-> Seq Scan on prt2_p2
Filter: (a = 0)
- -> Hash Full Join
- Hash Cond: (prt1_p3.a = prt2_p3.b)
- -> Seq Scan on prt1_p3
- Filter: (b = 0)
- -> Hash
-> Seq Scan on prt2_p3
Filter: (a = 0)
-(24 rows)
+(19 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
a | c | b | c
-----+------+-----+------
- 0 | 0000 | 0 | 0000
- 50 | 0050 | |
+ 0 | 0000 | |
+ 50 | 0050 | 50 | 0050
100 | 0100 | |
- 150 | 0150 | 150 | 0150
- 200 | 0200 | |
+ 150 | 0150 | |
+ 200 | 0200 | 200 | 0200
250 | 0250 | |
- 300 | 0300 | 300 | 0300
- 350 | 0350 | |
+ 300 | 0300 | |
+ 350 | 0350 | 350 | 0350
400 | 0400 | |
- 450 | 0450 | 450 | 0450
- 500 | 0500 | |
+ 450 | 0450 | |
+ 500 | 0500 | 500 | 0500
550 | 0550 | |
- | | 75 | 0075
- | | 225 | 0225
- | | 375 | 0375
- | | 525 | 0525
-(16 rows)
+ | | 125 | 0125
+ | | 275 | 0275
+ | | 425 | 0425
+(15 rows)
-- Cases with non-nullable expressions in subquery results;
-- make sure these go to null as expected
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b;
- QUERY PLAN
-------------------------------------------------------------------
+ QUERY PLAN
+------------------------------------------------------------
Sort
Sort Key: prt1_p1.a, prt2_p1.b
- -> Append
- -> Hash Full Join
- Hash Cond: (prt1_p1.a = prt2_p1.b)
- Filter: (((50) = prt1_p1.a) OR ((75) = prt2_p1.b))
+ -> Hash Full Join
+ Hash Cond: (prt1_p1.a = prt2_p1.b)
+ Filter: (((50) = prt1_p1.a) OR ((75) = prt2_p1.b))
+ -> Append
-> Seq Scan on prt1_p1
Filter: (b = 0)
- -> Hash
- -> Seq Scan on prt2_p1
- Filter: (a = 0)
- -> Hash Full Join
- Hash Cond: (prt1_p2.a = prt2_p2.b)
- Filter: (((50) = prt1_p2.a) OR ((75) = prt2_p2.b))
+ -> Seq Scan on prt1_p3
+ Filter: (b = 0)
-> Seq Scan on prt1_p2
Filter: (b = 0)
- -> Hash
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p1
+ Filter: (a = 0)
-> Seq Scan on prt2_p2
Filter: (a = 0)
- -> Hash Full Join
- Hash Cond: (prt1_p3.a = prt2_p3.b)
- Filter: (((50) = prt1_p3.a) OR ((75) = prt2_p3.b))
- -> Seq Scan on prt1_p3
- Filter: (b = 0)
- -> Hash
-> Seq Scan on prt2_p3
Filter: (a = 0)
-(27 rows)
+(20 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b;
a | c | b | c
----+------+----+------
- 50 | 0050 | |
- | | 75 | 0075
-(2 rows)
+ 50 | 0050 | 50 | 0050
+(1 row)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
- QUERY PLAN
---------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------
Sort
Sort Key: prt1_p1.a, prt2_p1.b
- -> Result
+ -> Hash Full Join
+ Hash Cond: (prt1_p1.a = prt2_p1.b)
-> Append
- -> Hash Full Join
- Hash Cond: (prt1_p1.a = prt2_p1.b)
- -> Seq Scan on prt1_p1
- Filter: (b = 0)
- -> Hash
- -> Seq Scan on prt2_p1
- Filter: (a = 0)
- -> Hash Full Join
- Hash Cond: (prt1_p2.a = prt2_p2.b)
- -> Seq Scan on prt1_p2
- Filter: (b = 0)
- -> Hash
- -> Seq Scan on prt2_p2
- Filter: (a = 0)
- -> Hash Full Join
- Hash Cond: (prt1_p3.a = prt2_p3.b)
- -> Seq Scan on prt1_p3
- Filter: (b = 0)
- -> Hash
- -> Seq Scan on prt2_p3
- Filter: (a = 0)
-(25 rows)
+ -> Seq Scan on prt1_p1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3
+ Filter: (a = 0)
+(19 rows)
SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
a | c | phv | b | c | phv
-----+------+-----+-----+------+-----
- 0 | 0000 | 25 | 0 | 0000 | 50
- 50 | 0050 | 25 | | |
+ 0 | 0000 | 25 | | |
+ 50 | 0050 | 25 | 50 | 0050 | 50
100 | 0100 | 25 | | |
- 150 | 0150 | 25 | 150 | 0150 | 50
- 200 | 0200 | 25 | | |
+ 150 | 0150 | 25 | | |
+ 200 | 0200 | 25 | 200 | 0200 | 50
250 | 0250 | 25 | | |
- 300 | 0300 | 25 | 300 | 0300 | 50
- 350 | 0350 | 25 | | |
+ 300 | 0300 | 25 | | |
+ 350 | 0350 | 25 | 350 | 0350 | 50
400 | 0400 | 25 | | |
- 450 | 0450 | 25 | 450 | 0450 | 50
- 500 | 0500 | 25 | | |
+ 450 | 0450 | 25 | | |
+ 500 | 0500 | 25 | 500 | 0500 | 50
550 | 0550 | 25 | | |
- | | | 75 | 0075 | 50
- | | | 225 | 0225 | 50
- | | | 375 | 0375 | 50
- | | | 525 | 0525 | 50
-(16 rows)
+ | | | 125 | 0125 | 50
+ | | | 275 | 0275 | 50
+ | | | 425 | 0425 | 50
+(15 rows)
-- Join with pruned partitions from joining relations
EXPLAIN (COSTS OFF)
@@ -315,7 +302,7 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a <
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.b = 0 ORDER BY t1.a, t2.b;
a | c | b | c
-----+------+-----+------
- 300 | 0300 | 300 | 0300
+ 350 | 0350 | 350 | 0350
(1 row)
EXPLAIN (COSTS OFF)
@@ -350,43 +337,32 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JO
150 | 0150 | |
200 | 0200 | |
250 | 0250 | |
- 300 | 0300 | 300 | 0300
- 350 | 0350 | |
+ 300 | 0300 | |
+ 350 | 0350 | 350 | 0350
400 | 0400 | |
(9 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b;
- QUERY PLAN
-------------------------------------------------------------
+ QUERY PLAN
+------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, b
- -> Append
- -> Hash Full Join
- Hash Cond: (prt1_p1.a = b)
- Filter: ((prt1_p1.b = 0) OR (a = 0))
+ Sort Key: prt1_p1.a, prt2_p2.b
+ -> Hash Full Join
+ Hash Cond: (prt1_p1.a = prt2_p2.b)
+ Filter: ((prt1_p1.b = 0) OR (prt2_p2.a = 0))
+ -> Append
-> Seq Scan on prt1_p1
Filter: (a < 450)
- -> Hash
- -> Result
- One-Time Filter: false
- -> Hash Full Join
- Hash Cond: (prt1_p2.a = prt2_p2.b)
- Filter: ((prt1_p2.b = 0) OR (prt2_p2.a = 0))
-> Seq Scan on prt1_p2
Filter: (a < 450)
- -> Hash
+ -> Hash
+ -> Append
-> Seq Scan on prt2_p2
Filter: (b > 250)
- -> Hash Full Join
- Hash Cond: (prt2_p3.b = a)
- Filter: ((b = 0) OR (prt2_p3.a = 0))
- -> Seq Scan on prt2_p3
- Filter: (b > 250)
- -> Hash
- -> Result
- One-Time Filter: false
-(27 rows)
+ -> Seq Scan on prt2_p3
+ Filter: (b > 250)
+(16 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b;
a | c | b | c
@@ -397,12 +373,12 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JO
150 | 0150 | |
200 | 0200 | |
250 | 0250 | |
- 300 | 0300 | 300 | 0300
- 350 | 0350 | |
+ 300 | 0300 | |
+ 350 | 0350 | 350 | 0350
400 | 0400 | |
- | | 375 | 0375
- | | 450 | 0450
- | | 525 | 0525
+ | | 275 | 0275
+ | | 425 | 0425
+ | | 500 | 0500
(12 rows)
-- Semi-join
@@ -439,10 +415,46 @@ SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
a | b | c
-----+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
+ 50 | 0 | 0050
+ 200 | 0 | 0200
+ 350 | 0 | 0350
+ 500 | 0 | 0500
+(4 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------
+ Append
+ -> Hash Semi Join
+ Hash Cond: (t1.b = t2.a)
+ -> Seq Scan on prt2_p1 t1
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p1 t2
+ Filter: (b = 0)
+ -> Hash Semi Join
+ Hash Cond: (t1_1.b = t2_2.a)
+ -> Seq Scan on prt2_p2 t1_1
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p2 t2_2
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: (t1_2.b = t2_1.a)
+ -> Seq Scan on prt2_p3 t1_2
+ Filter: (a = 0)
+ -> Seq Scan on prt1_p3 t2_1
+ Filter: (b = 0)
+(21 rows)
+
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.a;
+ a | b | c
+---+-----+------
+ 0 | 50 | 0050
+ 0 | 200 | 0200
+ 0 | 350 | 0350
+ 0 | 500 | 0500
(4 rows)
-- Anti-join with aggregates
@@ -472,7 +484,31 @@ SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS
SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b);
sum | avg | sum | avg
-------+----------------------+------+---------------------
- 60000 | 300.0000000000000000 | 2400 | 12.0000000000000000
+ 64584 | 299.0000000000000000 | 2584 | 11.9629629629629630
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a);
+ QUERY PLAN
+--------------------------------------------------
+ Aggregate
+ -> Hash Anti Join
+ Hash Cond: (t1.b = t2.a)
+ -> Append
+ -> Seq Scan on prt2_p1 t1
+ -> Seq Scan on prt2_p2 t1_1
+ -> Seq Scan on prt2_p3 t1_2
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p1 t2
+ -> Seq Scan on prt1_p3 t2_1
+ -> Seq Scan on prt1_p2 t2_2
+(12 rows)
+
+SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a);
+ sum | avg | sum | avg
+-----+---------------------+-------+----------------------
+ 992 | 11.9518072289156627 | 24817 | 299.0000000000000000
(1 row)
-- lateral reference
@@ -484,50 +520,46 @@ SELECT * FROM prt1 t1 LEFT JOIN LATERAL
--------------------------------------------------------------------------------
Sort
Sort Key: t1.a
- -> Result
+ -> Nested Loop Left Join
-> Append
- -> Nested Loop Left Join
- -> Seq Scan on prt1_p1 t1
- Filter: (b = 0)
- -> Nested Loop
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Hash Join
+ Hash Cond: (t3.b = t2.a)
+ -> Append
+ -> Seq Scan on prt2_p1 t3
+ -> Seq Scan on prt2_p2 t3_1
+ -> Seq Scan on prt2_p3 t3_2
+ -> Hash
+ -> Append
-> Index Only Scan using iprt1_p1_a on prt1_p1 t2
Index Cond: (a = t1.a)
- -> Index Scan using iprt2_p1_b on prt2_p1 t3
- Index Cond: (b = t2.a)
- -> Nested Loop Left Join
- -> Seq Scan on prt1_p2 t1_2
- Filter: (b = 0)
- -> Nested Loop
- -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2
- Index Cond: (a = t1_2.a)
- -> Index Scan using iprt2_p2_b on prt2_p2 t3_1
- Index Cond: (b = t2_2.a)
- -> Nested Loop Left Join
- -> Seq Scan on prt1_p3 t1_1
- Filter: (b = 0)
- -> Nested Loop
-> Index Only Scan using iprt1_p3_a on prt1_p3 t2_1
- Index Cond: (a = t1_1.a)
- -> Index Scan using iprt2_p3_b on prt2_p3 t3_2
- Index Cond: (b = t2_1.a)
-(28 rows)
+ Index Cond: (a = t1.a)
+ -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2
+ Index Cond: (a = t1.a)
+(24 rows)
SELECT * FROM prt1 t1 LEFT JOIN LATERAL
(SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
ON t1.a = ss.t2a WHERE t1.b = 0 ORDER BY t1.a;
a | b | c | t2a | t3a | least
-----+---+------+-----+-----+-------
- 0 | 0 | 0000 | 0 | 0 | 0
- 50 | 0 | 0050 | | |
+ 0 | 0 | 0000 | | |
+ 50 | 0 | 0050 | 50 | 0 | 50
100 | 0 | 0100 | | |
- 150 | 0 | 0150 | 150 | 0 | 150
- 200 | 0 | 0200 | | |
+ 150 | 0 | 0150 | | |
+ 200 | 0 | 0200 | 200 | 0 | 200
250 | 0 | 0250 | | |
- 300 | 0 | 0300 | 300 | 0 | 300
- 350 | 0 | 0350 | | |
+ 300 | 0 | 0300 | | |
+ 350 | 0 | 0350 | 350 | 0 | 350
400 | 0 | 0400 | | |
- 450 | 0 | 0450 | 450 | 0 | 450
- 500 | 0 | 0500 | | |
+ 450 | 0 | 0450 | | |
+ 500 | 0 | 0500 | 500 | 0 | 500
550 | 0 | 0550 | | |
(12 rows)
@@ -570,17 +602,17 @@ SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
ON t1.c = ss.t2c WHERE (t1.b + coalesce(ss.t2b, 0)) = 0 ORDER BY t1.a;
a | t2a | t2c
-----+-----+------
- 0 | 0 | 0000
- 50 | |
+ 0 | |
+ 50 | 50 | 0050
100 | |
- 150 | 150 | 0150
- 200 | |
+ 150 | |
+ 200 | 200 | 0200
250 | |
- 300 | 300 | 0300
- 350 | |
+ 300 | |
+ 350 | 350 | 0350
400 | |
- 450 | 450 | 0450
- 500 | |
+ 450 | |
+ 500 | 500 | 0500
550 | |
(12 rows)
@@ -641,55 +673,50 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 =
--
-- N-way join
--
+--TODO: remove below comments and enable partition_wise_join
+--one issue got fixed
+--Server crashed with below queries
+--setting partition-wise-join to off
+SET enable_partition_wise_join to false;
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.b = 0 ORDER BY t1.a, t2.b;
- QUERY PLAN
----------------------------------------------------------------------------
+ QUERY PLAN
+--------------------------------------------------------------
Sort
Sort Key: t1.a
- -> Result
+ -> Hash Join
+ Hash Cond: (((t3.a + t3.b) / 2) = t1.a)
-> Append
- -> Nested Loop
- Join Filter: (t1.a = ((t3.a + t3.b) / 2))
- -> Hash Join
- Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on prt1_e_p1 t3
+ -> Seq Scan on prt1_e_p2 t3_1
+ -> Seq Scan on prt1_e_p3 t3_2
+ -> Hash
+ -> Hash Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
-> Seq Scan on prt2_p1 t2
- -> Hash
- -> Seq Scan on prt1_p1 t1
- Filter: (b = 0)
- -> Index Scan using iprt1_e_p1_ab2 on prt1_e_p1 t3
- Index Cond: (((a + b) / 2) = t2.b)
- -> Nested Loop
- Join Filter: (t1_2.a = ((t3_1.a + t3_1.b) / 2))
- -> Hash Join
- Hash Cond: (t2_1.b = t1_2.a)
-> Seq Scan on prt2_p2 t2_1
- -> Hash
- -> Seq Scan on prt1_p2 t1_2
- Filter: (b = 0)
- -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t3_1
- Index Cond: (((a + b) / 2) = t2_1.b)
- -> Nested Loop
- Join Filter: (t1_1.a = ((t3_2.a + t3_2.b) / 2))
- -> Hash Join
- Hash Cond: (t2_2.b = t1_1.a)
-> Seq Scan on prt2_p3 t2_2
- -> Hash
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
-> Seq Scan on prt1_p3 t1_1
Filter: (b = 0)
- -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_2
- Index Cond: (((a + b) / 2) = t2_2.b)
-(34 rows)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+(23 rows)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.b = 0 ORDER BY t1.a, t2.b;
a | c | b | c | ?column? | c
-----+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
+ 50 | 0050 | 50 | 0050 | 100 | 0
+ 200 | 0200 | 200 | 0200 | 400 | 0
+ 350 | 0350 | 350 | 0350 | 700 | 0
+ 500 | 0500 | 500 | 0500 | 1000 | 0
(4 rows)
+SET enable_partition_wise_join to true;
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
QUERY PLAN
@@ -733,17 +760,17 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
a | c | b | c | ?column? | c
-----+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 50 | 0050 | | | 100 | 0
+ 0 | 0000 | | | 0 | 0
+ 50 | 0050 | 50 | 0050 | 100 | 0
100 | 0100 | | | 200 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 200 | 0200 | | | 400 | 0
+ 150 | 0150 | | | 300 | 0
+ 200 | 0200 | 200 | 0200 | 400 | 0
250 | 0250 | | | 500 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 350 | 0350 | | | 700 | 0
+ 300 | 0300 | | | 600 | 0
+ 350 | 0350 | 350 | 0350 | 700 | 0
400 | 0400 | | | 800 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
- 500 | 0500 | | | 1000 | 0
+ 450 | 0450 | | | 900 | 0
+ 500 | 0500 | 500 | 0500 | 1000 | 0
550 | 0550 | | | 1100 | 0
(12 rows)
@@ -787,17 +814,17 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
a | c | b | c | ?column? | c
-----+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 50 | 0050 | | | 100 | 0
+ 0 | 0000 | | | 0 | 0
+ 50 | 0050 | 50 | 0050 | 100 | 0
100 | 0100 | | | 200 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 200 | 0200 | | | 400 | 0
+ 150 | 0150 | | | 300 | 0
+ 200 | 0200 | 200 | 0200 | 400 | 0
250 | 0250 | | | 500 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 350 | 0350 | | | 700 | 0
+ 300 | 0300 | | | 600 | 0
+ 350 | 0350 | 350 | 0350 | 700 | 0
400 | 0400 | | | 800 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
- 500 | 0500 | | | 1000 | 0
+ 450 | 0450 | | | 900 | 0
+ 500 | 0500 | 500 | 0500 | 1000 | 0
550 | 0550 | | | 1100 | 0
(12 rows)
@@ -805,65 +832,51 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
-- make sure these go to null as expected
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.c = 0) t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a = t1.phv OR t2.b = t2.phv OR (t3.a + t3.b)/2 = t3.phv ORDER BY t1.a, t2.b, t3.a + t3.b;
- QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
+ QUERY PLAN
+----------------------------------------------------------------------------------------------------------
Sort
Sort Key: prt1_p1.a, prt2_p1.b, ((prt1_e_p1.a + prt1_e_p1.b))
- -> Result
- -> Append
- -> Hash Full Join
- Hash Cond: (prt1_p1.a = ((prt1_e_p1.a + prt1_e_p1.b) / 2))
- Filter: ((prt1_p1.a = (50)) OR (prt2_p1.b = (75)) OR (((prt1_e_p1.a + prt1_e_p1.b) / 2) = (50)))
- -> Hash Full Join
- Hash Cond: (prt1_p1.a = prt2_p1.b)
- -> Seq Scan on prt1_p1
- Filter: (b = 0)
- -> Hash
- -> Seq Scan on prt2_p1
- Filter: (a = 0)
- -> Hash
- -> Seq Scan on prt1_e_p1
- Filter: (c = 0)
- -> Hash Full Join
- Hash Cond: (prt1_p2.a = ((prt1_e_p2.a + prt1_e_p2.b) / 2))
- Filter: ((prt1_p2.a = (50)) OR (prt2_p2.b = (75)) OR (((prt1_e_p2.a + prt1_e_p2.b) / 2) = (50)))
- -> Hash Full Join
- Hash Cond: (prt1_p2.a = prt2_p2.b)
- -> Seq Scan on prt1_p2
- Filter: (b = 0)
- -> Hash
- -> Seq Scan on prt2_p2
- Filter: (a = 0)
- -> Hash
- -> Seq Scan on prt1_e_p2
- Filter: (c = 0)
- -> Hash Full Join
- Hash Cond: (prt1_p3.a = ((prt1_e_p3.a + prt1_e_p3.b) / 2))
- Filter: ((prt1_p3.a = (50)) OR (prt2_p3.b = (75)) OR (((prt1_e_p3.a + prt1_e_p3.b) / 2) = (50)))
- -> Hash Full Join
- Hash Cond: (prt1_p3.a = prt2_p3.b)
- -> Seq Scan on prt1_p3
- Filter: (b = 0)
- -> Hash
- -> Seq Scan on prt2_p3
- Filter: (a = 0)
- -> Hash
- -> Seq Scan on prt1_e_p3
- Filter: (c = 0)
-(43 rows)
+ -> Hash Full Join
+ Hash Cond: (prt1_p1.a = ((prt1_e_p1.a + prt1_e_p1.b) / 2))
+ Filter: ((prt1_p1.a = (50)) OR (prt2_p1.b = (75)) OR (((prt1_e_p1.a + prt1_e_p1.b) / 2) = (50)))
+ -> Hash Full Join
+ Hash Cond: (prt1_p1.a = prt2_p1.b)
+ -> Append
+ -> Seq Scan on prt1_p1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3
+ Filter: (a = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_e_p1
+ Filter: (c = 0)
+ -> Seq Scan on prt1_e_p2
+ Filter: (c = 0)
+ -> Seq Scan on prt1_e_p3
+ Filter: (c = 0)
+(30 rows)
SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.c = 0) t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a = t1.phv OR t2.b = t2.phv OR (t3.a + t3.b)/2 = t3.phv ORDER BY t1.a, t2.b, t3.a + t3.b;
a | phv | b | phv | ?column? | phv
----+-----+----+-----+----------+-----
- 50 | 50 | | | 100 | 50
- | | 75 | 75 | |
-(2 rows)
+ 50 | 50 | 50 | 75 | 100 | 50
+(1 row)
-- Semi-join
EXPLAIN (COSTS OFF)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.a = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.b = 0 ORDER BY t1.a;
- QUERY PLAN
----------------------------------------------------------------------------------
+ QUERY PLAN
+-------------------------------------------------------------------------
Sort
Sort Key: t1.a
-> Append
@@ -897,23 +910,24 @@ SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHER
Join Filter: (t1_1.a = t1_5.b)
-> HashAggregate
Group Key: t1_5.b
- -> Nested Loop
- -> Seq Scan on prt2_p3 t1_5
- Filter: (a = 0)
- -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t2_2
- Index Cond: (((a + b) / 2) = t1_5.b)
+ -> Hash Join
+ Hash Cond: (((t2_2.a + t2_2.b) / 2) = t1_5.b)
+ -> Seq Scan on prt1_e_p3 t2_2
+ -> Hash
+ -> Seq Scan on prt2_p3 t1_5
+ Filter: (a = 0)
-> Index Scan using iprt1_p3_a on prt1_p3 t1_1
Index Cond: (a = ((t2_2.a + t2_2.b) / 2))
Filter: (b = 0)
-(41 rows)
+(42 rows)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.a = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.b = 0 ORDER BY t1.a;
a | b | c
-----+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
+ 50 | 0 | 0050
+ 200 | 0 | 0200
+ 350 | 0 | 0350
+ 500 | 0 | 0500
(4 rows)
EXPLAIN (COSTS OFF)
@@ -965,10 +979,10 @@ SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
a | b | c
-----+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
+ 50 | 0 | 0050
+ 200 | 0 | 0200
+ 350 | 0 | 0350
+ 500 | 0 | 0500
(4 rows)
-- test merge joins
@@ -1030,10 +1044,10 @@ SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
a | b | c
-----+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
+ 50 | 0 | 0050
+ 200 | 0 | 0200
+ 350 | 0 | 0350
+ 500 | 0 | 0500
(4 rows)
EXPLAIN (COSTS OFF)
@@ -1097,22 +1111,899 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
a | c | b | c | ?column? | c
-----+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 50 | 0050 | | | 100 | 0
+ 0 | 0000 | | | 0 | 0
+ 50 | 0050 | 50 | 0050 | 100 | 0
100 | 0100 | | | 200 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 200 | 0200 | | | 400 | 0
+ 150 | 0150 | | | 300 | 0
+ 200 | 0200 | 200 | 0200 | 400 | 0
250 | 0250 | | | 500 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 350 | 0350 | | | 700 | 0
+ 300 | 0300 | | | 600 | 0
+ 350 | 0350 | 350 | 0350 | 700 | 0
400 | 0400 | | | 800 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
- 500 | 0500 | | | 1000 | 0
+ 450 | 0450 | | | 900 | 0
+ 500 | 0500 | 500 | 0500 | 1000 | 0
550 | 0550 | | | 1100 | 0
(12 rows)
RESET enable_hashjoin;
RESET enable_nestloop;
+-- Add an extra partition to prt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (700);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 699, 3) i;
+ANALYZE prt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on prt2_p1 t2
+ -> Hash
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Hash Join
+ Hash Cond: (t2_1.b = t1_2.a)
+ -> Seq Scan on prt2_p2 t2_1
+ -> Hash
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Hash Join
+ Hash Cond: (t2_2.b = t1_1.a)
+ -> Seq Scan on prt2_p3 t2_2
+ -> Hash
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+(21 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+---+------
+ 50 | 0050 | 0 | 0050
+ 200 | 0200 | 0 | 0200
+ 350 | 0350 | 0 | 0350
+ 500 | 0500 | 0 | 0500
+(4 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Right Join
+ Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on prt2_p1 t2
+ -> Hash
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Hash Right Join
+ Hash Cond: (t2_1.b = t1_2.a)
+ -> Seq Scan on prt2_p2 t2_1
+ -> Hash
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Hash Right Join
+ Hash Cond: (t2_2.b = t1_1.a)
+ -> Seq Scan on prt2_p3 t2_2
+ -> Hash
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+(21 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+---+------
+ 0 | 0000 | |
+ 50 | 0050 | 0 | 0050
+ 100 | 0100 | |
+ 150 | 0150 | |
+ 200 | 0200 | 0 | 0200
+ 250 | 0250 | |
+ 300 | 0300 | |
+ 350 | 0350 | 0 | 0350
+ 400 | 0400 | |
+ 450 | 0450 | |
+ 500 | 0500 | 0 | 0500
+ 550 | 0550 | |
+(12 rows)
+
+-- right join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------
+ Hash Right Join
+ Hash Cond: (t1.a = t2.b)
+ -> Append
+ -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p3 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p1 t2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t2_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t2_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t2_3
+ Filter: (a = 0)
+(16 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+---+------
+ 50 | 0050 | 0 | 0050
+ 200 | 0200 | 0 | 0200
+ 350 | 0350 | 0 | 0350
+ 500 | 0500 | 0 | 0500
+ | | 0 | 0275
+ | | 0 | 0425
+ | | 0 | 0125
+ | | 0 | 0600
+ | | 0 | 0675
+(9 rows)
+
+-- full join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+----------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Append
+ -> Hash Join
+ Hash Cond: (t1.a = t2.b)
+ Join Filter: ((t1.b + t2.a) = 0)
+ -> Seq Scan on prt1_p1 t1
+ -> Hash
+ -> Seq Scan on prt2_p1 t2
+ -> Hash Join
+ Hash Cond: (t1_2.a = t2_1.b)
+ Join Filter: ((t1_2.b + t2_1.a) = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on prt2_p2 t2_1
+ -> Hash Join
+ Hash Cond: (t1_1.a = t2_2.b)
+ Join Filter: ((t1_1.b + t2_2.a) = 0)
+ -> Seq Scan on prt1_p3 t1_1
+ -> Hash
+ -> Seq Scan on prt2_p3 t2_2
+(21 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+ a | c | a | c
+-----+------+---+------
+ 50 | 0050 | 0 | 0050
+ 200 | 0200 | 0 | 0200
+ 350 | 0350 | 0 | 0350
+ 500 | 0500 | 0 | 0500
+(4 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Semi Join
+ Hash Cond: (t1.a = t2.b)
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p1 t2
+ -> Hash Semi Join
+ Hash Cond: (t1_2.a = t2_1.b)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p2 t2_1
+ -> Hash Semi Join
+ Hash Cond: (t1_1.a = t2_2.b)
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p3 t2_2
+(21 rows)
+
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 50 | 0 | 0050
+ 200 | 0 | 0200
+ 350 | 0 | 0350
+ 500 | 0 | 0500
+(4 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Append
+ -> Hash Semi Join
+ Hash Cond: (t1.b = t2.a)
+ -> Seq Scan on prt2_p1 t1
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p1 t2
+ -> Hash Semi Join
+ Hash Cond: (t1_1.b = t2_2.a)
+ -> Seq Scan on prt2_p2 t1_1
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p2 t2_2
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p3 t1_2
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_1
+ Index Cond: (a = t1_2.b)
+(20 rows)
+
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+---+-----+------
+ 0 | 50 | 0050
+ 0 | 200 | 0200
+ 0 | 350 | 0350
+ 0 | 500 | 0500
+(4 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Anti Join
+ Hash Cond: (t1.a = t2.b)
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p1 t2
+ -> Hash Anti Join
+ Hash Cond: (t1_2.a = t2_1.b)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p2 t2_1
+ -> Hash Anti Join
+ Hash Cond: (t1_1.a = t2_2.b)
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p3 t2_2
+(21 rows)
+
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 100 | 0 | 0100
+ 150 | 0 | 0150
+ 250 | 0 | 0250
+ 300 | 0 | 0300
+ 400 | 0 | 0400
+ 450 | 0 | 0450
+ 550 | 0 | 0550
+(8 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Append
+ -> Hash Anti Join
+ Hash Cond: (t1.b = t2.a)
+ -> Seq Scan on prt2_p1 t1
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p1 t2
+ -> Hash Anti Join
+ Hash Cond: (t1_1.b = t2_2.a)
+ -> Seq Scan on prt2_p2 t1_1
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p2 t2_2
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt2_p3 t1_2
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_1
+ Index Cond: (a = t1_2.b)
+(20 rows)
+
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+---+-----+------
+ 0 | 125 | 0125
+ 0 | 275 | 0275
+ 0 | 425 | 0425
+(3 rows)
+
+--TODO: remove below comments and enable partition_wise_join
+--one issue got fixed
+--Getting wrong output with partition wise join
+--setting partition-wise-join to off to get correct output
+SET enable_partition_wise_join to false;
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+---+-----+------
+ 0 | 125 | 0125
+ 0 | 275 | 0275
+ 0 | 425 | 0425
+ 0 | 600 | 0600
+ 0 | 675 | 0675
+(5 rows)
+
+SET enable_partition_wise_join to true;
+-- N-Way joins
+--TODO: remove below comments and enable partition_wise_join
+--one issue got fixed
+--Server crashed with below queries
+--setting partition-wise-join to off
+SET enable_partition_wise_join to false;
+-- t1 join t2 qualify partition-wise-join and t2 join t3 qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c, t2.a, t2.c, t3.a, t3.c
+ -> Hash Right Join
+ Hash Cond: (t3.b = t2.b)
+ -> Append
+ -> Seq Scan on prt2_p1 t3
+ -> Seq Scan on prt2_p2 t3_1
+ -> Seq Scan on prt2_p3 t3_2
+ -> Seq Scan on prt2_p4 t3_3
+ -> Hash
+ -> Hash Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p4 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+(25 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+ a | c | a | c | a | c
+-----+------+---+------+---+------
+ 50 | 0050 | 0 | 0050 | 0 | 0050
+ 200 | 0200 | 0 | 0200 | 0 | 0200
+ 350 | 0350 | 0 | 0350 | 0 | 0350
+ 500 | 0500 | 0 | 0500 | 0 | 0500
+(4 rows)
+
+-- t1 join t2 qualify partition-wise-join and t2 join t3 not qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c, t2.a, t2.c, t3.a, t3.c
+ -> Hash Join
+ Hash Cond: (t3.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p1 t3
+ -> Seq Scan on prt2_p2 t3_1
+ -> Seq Scan on prt2_p3 t3_2
+ -> Seq Scan on prt2_p4 t3_3
+ -> Hash
+ -> Hash Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p4 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+(25 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+ a | c | a | c | a | c
+-----+------+---+------+---+------
+ 50 | 0050 | 0 | 0050 | 0 | 0050
+ 200 | 0200 | 0 | 0200 | 0 | 0200
+ 350 | 0350 | 0 | 0350 | 0 | 0350
+ 500 | 0500 | 0 | 0500 | 0 | 0500
+(4 rows)
+
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) INNER JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c, t2.a, t2.c, t3.a, t3.c
+ -> Hash Join
+ Hash Cond: (t3.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p1 t3
+ -> Seq Scan on prt2_p2 t3_1
+ -> Seq Scan on prt2_p3 t3_2
+ -> Seq Scan on prt2_p4 t3_3
+ -> Hash
+ -> Hash Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p4 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+(25 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) INNER JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+ a | c | a | c | a | c
+-----+------+---+------+---+------
+ 50 | 0050 | 0 | 0050 | 0 | 0050
+ 200 | 0200 | 0 | 0200 | 0 | 0200
+ 350 | 0350 | 0 | 0350 | 0 | 0350
+ 500 | 0500 | 0 | 0500 | 0 | 0500
+(4 rows)
+
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 not qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) FULL JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c, t2.a, t2.c, t3.a, t3.c
+ -> Hash Right Join
+ Hash Cond: (t3.b = t2.b)
+ -> Append
+ -> Seq Scan on prt2_p1 t3
+ -> Seq Scan on prt2_p2 t3_1
+ -> Seq Scan on prt2_p3 t3_2
+ -> Seq Scan on prt2_p4 t3_3
+ -> Hash
+ -> Hash Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p4 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+(25 rows)
+
+SET enable_partition_wise_join to true;
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE prt2_p4;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (550) TO (700);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(550, 699, 3) i;
+ANALYZE prt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p4 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+(17 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Right Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p4 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+(17 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------
+ Hash Right Join
+ Hash Cond: (t1.a = t2.b)
+ -> Append
+ -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p3 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p1 t2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t2_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t2_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t2_3
+ Filter: (a = 0)
+(16 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Join
+ Hash Cond: (t1.a = t2.b)
+ Join Filter: ((t1.b + t2.a) = 0)
+ -> Append
+ -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p3 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p4 t2_3
+(15 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Semi Join
+ Hash Cond: (t1.a = t2.b)
+ -> Append
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p4 t2_3
+(17 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Hash Semi Join
+ Hash Cond: (t1.b = t2.a)
+ -> Append
+ -> Seq Scan on prt2_p1 t1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t1_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t1_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t1_3
+ Filter: (a = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p1 t2
+ -> Seq Scan on prt1_p3 t2_1
+ -> Seq Scan on prt1_p2 t2_2
+(18 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: (t1.a = t2.b)
+ -> Append
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p4 t2_3
+(17 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Hash Anti Join
+ Hash Cond: (t1.b = t2.a)
+ -> Append
+ -> Seq Scan on prt2_p1 t1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t1_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t1_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t1_3
+ Filter: (a = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p1 t2
+ -> Seq Scan on prt1_p3 t2_1
+ -> Seq Scan on prt1_p2 t2_2
+(18 rows)
+
+-- N-Way joins
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b) INNER JOIN prt1 t3 ON t2.b = t3.a WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c, t2.a, t2.c, t3.c
+ -> Hash Join
+ Hash Cond: (t3.a = t1.a)
+ -> Append
+ -> Seq Scan on prt1_p1 t3
+ -> Seq Scan on prt1_p3 t3_1
+ -> Seq Scan on prt1_p2 t3_2
+ -> Hash
+ -> Hash Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p4 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+(24 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b) INNER JOIN prt1 t3 ON t2.b = t3.a WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+ a | c | a | c | a | c
+-----+------+---+------+-----+------
+ 50 | 0050 | 0 | 0050 | 50 | 0050
+ 200 | 0200 | 0 | 0200 | 200 | 0200
+ 350 | 0350 | 0 | 0350 | 350 | 0350
+ 500 | 0500 | 0 | 0500 | 500 | 0500
+ 550 | 0550 | 0 | 0550 | 550 | 0550
+(5 rows)
+
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 not qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c, t2.a, t2.c, t3.a, t3.c
+ -> Hash Join
+ Hash Cond: (t3.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p1 t3
+ -> Seq Scan on prt2_p2 t3_1
+ -> Seq Scan on prt2_p3 t3_2
+ -> Seq Scan on prt2_p4 t3_3
+ -> Hash
+ -> Hash Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p4 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+(25 rows)
+
+-- Partition-wise join with minvalue/maxvalue bounds
+DROP TABLE prt2_p1;
+DROP TABLE prt2_p3;
+DROP TABLE prt2_p4;
+CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (MINVALUE) TO (250);
+CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (MAXVALUE);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 250, 3) i;
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(500, 600, 3) i;
+ANALYZE prt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Hash Cond: (t2_1.b = t1.a)
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Hash Join
+ Hash Cond: (t2.b = t1_2.a)
+ -> Seq Scan on prt2_p2 t2
+ -> Hash
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Hash Join
+ Hash Cond: (t2_2.b = t1_1.a)
+ -> Seq Scan on prt2_p3 t2_2
+ -> Hash
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+(21 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+---+------
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 0 | 0150
+ 350 | 0350 | 0 | 0350
+ 500 | 0500 | 0 | 0500
+(4 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+----------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2_1.a
+ -> Append
+ -> Hash Join
+ Hash Cond: (t1.a = t2_1.b)
+ Join Filter: ((t1.b + t2_1.a) = 0)
+ -> Seq Scan on prt1_p1 t1
+ -> Hash
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash Join
+ Hash Cond: (t1_2.a = t2.b)
+ Join Filter: ((t1_2.b + t2.a) = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on prt2_p2 t2
+ -> Hash Join
+ Hash Cond: (t1_1.a = t2_2.b)
+ Join Filter: ((t1_1.b + t2_2.a) = 0)
+ -> Seq Scan on prt1_p3 t1_1
+ -> Hash
+ -> Seq Scan on prt2_p3 t2_2
+(21 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+ a | c | a | c
+-----+------+---+------
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 0 | 0150
+ 350 | 0350 | 0 | 0350
+ 500 | 0500 | 0 | 0500
+(4 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Semi Join
+ Hash Cond: (t1.a = t2_1.b)
+ -> Seq Scan on prt1_p1 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash Semi Join
+ Hash Cond: (t1_2.a = t2.b)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p2 t2
+ -> Hash Semi Join
+ Hash Cond: (t1_1.a = t2_2.b)
+ -> Seq Scan on prt1_p3 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p3 t2_2
+(21 rows)
+
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 350 | 0 | 0350
+ 500 | 0 | 0500
+(4 rows)
+
--
-- partitioned by multiple columns
--
@@ -1182,83 +2073,2174 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1
--
-- tests for list partitioned tables.
--
-CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
-ANALYZE plt1;
-CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i;
-ANALYZE plt2;
+\set part_mod 17
+\set cond_mod 47
+\set num_rows 500
+CREATE TABLE plt1 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0001','0002','0003');
+CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0008','0009');
+CREATE TABLE plt1_p4 PARTITION OF plt1 FOR VALUES IN ('0000','0010');
+INSERT INTO plt1 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (7, 11, 12, 13, 14, 15, 16);
+ANALYSE plt1;
+-- plt2 have missing starting 0001, additional 0007, missing ending 0010
+-- and additional 0011 and 0012 bounds
+CREATE TABLE plt2 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002','0003');
+CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0007','0008','0009');
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000','0011','0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 10, 13, 14, 15, 16);
+ANALYSE plt2;
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t1_3.b + t2_3.b) = 0)
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt1_p1 t1
+ -> Hash Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ Join Filter: ((t1_1.b + t2_1.b) = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + t2_2.b) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a
+ -> Append
+ -> Hash Left Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash Right Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt1_p1 t1
+ -> Hash Left Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash Left Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(7 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+------------------------------------------------------------------
+ Sort
+ Sort Key: t2_3.a
+ -> Result
+ -> Append
+ -> Hash Right Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + t2_3.b) = 0)
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash Left Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt1_p1 t1
+ -> Hash Left Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + t2_1.b) = 0)
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash Right Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + t2_2.b) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(28 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 | 0011
+(6 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t2_3.a
+ -> Append
+ -> Hash Full Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash Full Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt1_p1 t1
+ -> Hash Full Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash Full Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 | 0011
+(8 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t1_3.c
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p4 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2_3.c)::text
+ -> Seq Scan on plt2_p4 t2_3
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt2_p1 t2
+ -> Materialize
+ -> Seq Scan on plt1_p1 t1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt1_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_1
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_2
+(29 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t1_3.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> HashAggregate
+ Group Key: (t2_3.c)::text
+ -> Seq Scan on plt1_p4 t2_3
+ -> Materialize
+ -> Seq Scan on plt2_p4 t1_3
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt2_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_1
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_2
+(26 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t1_3.c
+ -> Append
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p4 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p1 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt2_p1 t2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt1_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_2
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 188 | 0 | 0001
+ 282 | 0 | 0010
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t1_3.c
+ -> Append
+ -> Hash Anti Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p4 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt1_p4 t2_3
+ -> Nested Loop Anti Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt2_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_2
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 470 | 0 | 0011
+(1 row)
+
+-- N-Way joins
+-- inner-inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1,plt2 t2,plt1 t3 WHERE t1.c = t2.c AND t2.c = t3.c AND t1.b + t2.b = 0 AND t2.b + t3.b = 0 ORDER BY t1.a,t2.b;
+ QUERY PLAN
+------------------------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t2_3.b
+ -> Result
+ -> Append
+ -> Merge Join
+ Merge Cond: ((t2_3.c)::text = (t1_3.c)::text)
+ Join Filter: ((t1_3.b + t2_3.b) = 0)
+ -> Merge Join
+ Merge Cond: ((t3_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t2_3.b + t3_3.b) = 0)
+ -> Sort
+ Sort Key: t3_3.c
+ -> Seq Scan on plt1_p4 t3_3
+ -> Sort
+ Sort Key: t2_3.c
+ -> Seq Scan on plt2_p4 t2_3
+ -> Sort
+ Sort Key: t1_3.c
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash Join
+ Hash Cond: ((t3.c)::text = (t1.c)::text)
+ Join Filter: ((t2.b + t3.b) = 0)
+ -> Seq Scan on plt1_p1 t3
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt1_p1 t1
+ -> Hash Join
+ Hash Cond: ((t3_1.c)::text = (t1_1.c)::text)
+ Join Filter: ((t2_1.b + t3_1.b) = 0)
+ -> Seq Scan on plt1_p2 t3_1
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ Join Filter: ((t1_1.b + t2_1.b) = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + t2_2.b) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t3_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t2_2.b + t3_2.b) = 0)
+ -> Seq Scan on plt1_p3 t3_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(52 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1,plt2 t2,plt1 t3 WHERE t1.c = t2.c AND t2.c = t3.c AND t1.b + t2.b = 0 AND t2.b + t3.b = 0 ORDER BY t1.a,t2.b;
+ a | c | a | c | a | c
+-----+------+-----+------+-----+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left-left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c LEFT JOIN plt2 t3 ON (t2.c = t3.c) WHERE t1.b + coalesce(t2.b, 0) = 0 AND t2.b + coalesce(t3.b, 0) = 0 ORDER BY t1.a, t2.b;
+ QUERY PLAN
+-----------------------------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t2_3.b
+ -> Result
+ -> Append
+ -> Hash Right Join
+ Hash Cond: ((t3_3.c)::text = (t2_3.c)::text)
+ Filter: ((t2_3.b + COALESCE(t3_3.b, 0)) = 0)
+ -> Seq Scan on plt2_p4 t3_3
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Join Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt1_p1 t1
+ -> Hash
+ -> Hash Left Join
+ Hash Cond: ((t2.c)::text = (t3.c)::text)
+ Filter: ((t2.b + COALESCE(t3.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt2_p1 t3
+ -> Hash Right Join
+ Hash Cond: ((t3_1.c)::text = (t2_1.c)::text)
+ Filter: ((t2_1.b + COALESCE(t3_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p2 t3_1
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ Join Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash Right Join
+ Hash Cond: ((t3_2.c)::text = (t2_2.c)::text)
+ Filter: ((t2_2.b + COALESCE(t3_2.b, 0)) = 0)
+ -> Seq Scan on plt2_p3 t3_2
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(48 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c LEFT JOIN plt2 t3 ON (t2.c = t3.c) WHERE t1.b + coalesce(t2.b, 0) = 0 AND t2.b + coalesce(t3.b, 0) = 0 ORDER BY t1.a, t2.b;
+ a | c | a | c | a | c
+-----+------+-----+------+-----+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left-right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c RIGHT JOIN plt1 t3 ON (t2.c = t3.c) WHERE t1.b + coalesce(t2.b,0) = 0 AND coalesce(t1.b, 0) + t3.b = 0 ORDER BY t1.a, t2.b;
+ QUERY PLAN
+-----------------------------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t2_3.b
+ -> Result
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t3_3.c)::text = (t1_3.c)::text)
+ Join Filter: ((COALESCE(t1_3.b, 0) + t3_3.b) = 0)
+ -> Seq Scan on plt1_p4 t3_3
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash Join
+ Hash Cond: ((t3.c)::text = (t1.c)::text)
+ Join Filter: ((COALESCE(t1.b, 0) + t3.b) = 0)
+ -> Seq Scan on plt1_p1 t3
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Join Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt1_p1 t1
+ -> Hash Join
+ Hash Cond: ((t3_1.c)::text = (t1_1.c)::text)
+ Join Filter: ((COALESCE(t1_1.b, 0) + t3_1.b) = 0)
+ -> Seq Scan on plt1_p2 t3_1
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ Join Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash Join
+ Hash Cond: ((t2_2.c)::text = (t1_2.c)::text)
+ Join Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt2_p3 t2_2
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t3_2.c)::text)
+ Join Filter: ((COALESCE(t1_2.b, 0) + t3_2.b) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt1_p3 t3_2
+(48 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c RIGHT JOIN plt1 t3 ON (t2.c = t3.c) WHERE t1.b + coalesce(t2.b,0) = 0 AND coalesce(t1.b, 0) + t3.b = 0 ORDER BY t1.a, t2.b;
+ a | c | a | c | a | c
+-----+------+-----+------+-----+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- right-full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c FULL JOIN plt1 t3 ON (t2.c = t3.c) WHERE coalesce(t1.b,0) + t2.b = 0 AND coalesce(t2.b,0) + coalesce(t3.b,0) = 0 ORDER BY t1.a, t2.b;
+ QUERY PLAN
+------------------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t2_3.b
+ -> Hash Right Join
+ Hash Cond: ((t3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t2_3.b, 0) + COALESCE(t3.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt1_p1 t3
+ -> Seq Scan on plt1_p2 t3_1
+ -> Seq Scan on plt1_p3 t3_2
+ -> Seq Scan on plt1_p4 t3_3
+ -> Hash
+ -> Append
+ -> Hash Right Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + t2_3.b) = 0)
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash Left Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt1_p1 t1
+ -> Hash Left Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + t2_1.b) = 0)
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash Right Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + t2_2.b) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(36 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c FULL JOIN plt1 t3 ON (t2.c = t3.c) WHERE coalesce(t1.b,0) + t2.b = 0 AND coalesce(t2.b,0) + coalesce(t3.b,0) = 0 ORDER BY t1.a, t2.b;
+ a | c | a | c | a | c
+-----+------+-----+------+-----+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002 | 376 | 0002
+ | | 470 | 0011 | |
+(6 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT t3.c FROM plt1 t3 WHERE t3.b = 0)) AND t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1_3.c)::text = (t1_7.c)::text)
+ -> HashAggregate
+ Group Key: (t1_7.c)::text
+ -> Hash Join
+ Hash Cond: ((t1_7.c)::text = (t3_3.c)::text)
+ -> Seq Scan on plt2_p4 t1_7
+ -> Hash
+ -> HashAggregate
+ Group Key: (t3_3.c)::text
+ -> Seq Scan on plt1_p4 t3_3
+ Filter: (b = 0)
+ -> Materialize
+ -> Seq Scan on plt1_p4 t1_3
+ Filter: (b = 0)
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t1_4.c)::text)
+ -> HashAggregate
+ Group Key: (t1_4.c)::text
+ -> Hash Join
+ Hash Cond: ((t1_4.c)::text = (t3.c)::text)
+ -> Seq Scan on plt2_p1 t1_4
+ -> Hash
+ -> HashAggregate
+ Group Key: (t3.c)::text
+ -> Seq Scan on plt1_p1 t3
+ Filter: (b = 0)
+ -> Materialize
+ -> Seq Scan on plt1_p1 t1
+ Filter: (b = 0)
+ -> Nested Loop
+ Join Filter: ((t1_1.c)::text = (t1_5.c)::text)
+ -> HashAggregate
+ Group Key: (t1_5.c)::text
+ -> Hash Join
+ Hash Cond: ((t1_5.c)::text = (t3_1.c)::text)
+ -> Seq Scan on plt2_p2 t1_5
+ -> Hash
+ -> HashAggregate
+ Group Key: (t3_1.c)::text
+ -> Seq Scan on plt1_p2 t3_1
+ Filter: (b = 0)
+ -> Materialize
+ -> Seq Scan on plt1_p2 t1_1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t1_6.c)::text)
+ -> Seq Scan on plt1_p3 t1_2
+ Filter: (b = 0)
+ -> Hash Semi Join
+ Hash Cond: ((t1_6.c)::text = (t3_2.c)::text)
+ -> Seq Scan on plt2_p3 t1_6
+ -> Hash
+ -> Seq Scan on plt1_p3 t3_2
+ Filter: (b = 0)
+(58 rows)
+
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT t3.c FROM plt1 t3 WHERE t3.b = 0)) AND t1.b = 0 ORDER BY t1.a;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
--
-- list partitioned by expression
--
CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
-CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0002', '0003');
+CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0004', '0005', '0006');
+CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0008', '0009');
+CREATE TABLE plt1_e_p4 PARTITION OF plt1_e FOR VALUES IN ('0000');
+INSERT INTO plt1_e SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 7, 10, 11, 12, 13, 14, 15, 16);
ANALYZE plt1_e;
--- test partition matching with N-way join
+-- test partition matching with N-way join with expression
EXPLAIN (COSTS OFF)
SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
- QUERY PLAN
---------------------------------------------------------------------------------------
+ QUERY PLAN
+----------------------------------------------------------------------------------------
Sort
- Sort Key: t1.c, t3.c
+ Sort Key: t1_3.c, t3_3.c
-> HashAggregate
- Group Key: t1.c, t2.c, t3.c
+ Group Key: t1_3.c, t2_3.c, t3_3.c
-> Result
-> Append
-> Hash Join
- Hash Cond: (t1.c = t2.c)
- -> Seq Scan on plt1_p1 t1
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = ltrim(t3_3.c, 'A'::text))
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Seq Scan on plt1_e_p4 t3_3
-> Hash
- -> Hash Join
- Hash Cond: (t2.c = ltrim(t3.c, 'A'::text))
- -> Seq Scan on plt2_p1 t2
- -> Hash
- -> Seq Scan on plt1_e_p1 t3
+ -> Seq Scan on plt2_p4 t2_3
-> Hash Join
- Hash Cond: (t1_1.c = t2_1.c)
- -> Seq Scan on plt1_p2 t1_1
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ -> Hash Join
+ Hash Cond: ((t2.c)::text = ltrim(t3.c, 'A'::text))
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt1_e_p1 t3
-> Hash
- -> Hash Join
- Hash Cond: (t2_1.c = ltrim(t3_1.c, 'A'::text))
- -> Seq Scan on plt2_p2 t2_1
- -> Hash
- -> Seq Scan on plt1_e_p2 t3_1
+ -> Seq Scan on plt1_p1 t1
-> Hash Join
- Hash Cond: (t1_2.c = t2_2.c)
- -> Seq Scan on plt1_p3 t1_2
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Hash Join
+ Hash Cond: ((t1_1.c)::text = ltrim(t3_1.c, 'A'::text))
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash
+ -> Seq Scan on plt1_e_p2 t3_1
-> Hash
- -> Hash Join
- Hash Cond: (t2_2.c = ltrim(t3_2.c, 'A'::text))
- -> Seq Scan on plt2_p3 t2_2
- -> Hash
- -> Seq Scan on plt1_e_p3 t3_2
-(33 rows)
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = ltrim(t3_2.c, 'A'::text))
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt1_e_p3 t3_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(42 rows)
SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
- avg | avg | avg | c | c | c
-----------------------+----------------------+-----------------------+------+------+-------
- 24.0000000000000000 | 24.0000000000000000 | 48.0000000000000000 | 0000 | 0000 | A0000
- 74.0000000000000000 | 75.0000000000000000 | 148.0000000000000000 | 0001 | 0001 | A0001
- 124.0000000000000000 | 124.5000000000000000 | 248.0000000000000000 | 0002 | 0002 | A0002
- 174.0000000000000000 | 174.0000000000000000 | 348.0000000000000000 | 0003 | 0003 | A0003
- 224.0000000000000000 | 225.0000000000000000 | 448.0000000000000000 | 0004 | 0004 | A0004
- 274.0000000000000000 | 274.5000000000000000 | 548.0000000000000000 | 0005 | 0005 | A0005
- 324.0000000000000000 | 324.0000000000000000 | 648.0000000000000000 | 0006 | 0006 | A0006
- 374.0000000000000000 | 375.0000000000000000 | 748.0000000000000000 | 0007 | 0007 | A0007
- 424.0000000000000000 | 424.5000000000000000 | 848.0000000000000000 | 0008 | 0008 | A0008
- 474.0000000000000000 | 474.0000000000000000 | 948.0000000000000000 | 0009 | 0009 | A0009
- 524.0000000000000000 | 525.0000000000000000 | 1048.0000000000000000 | 0010 | 0010 | A0010
- 574.0000000000000000 | 574.5000000000000000 | 1148.0000000000000000 | 0011 | 0011 | A0011
-(12 rows)
+ avg | avg | avg | c | c | c
+----------------------+---------------------+----------------------+------+------+------
+ 246.5000000000000000 | 22.4666666666666667 | 268.9666666666666667 | 0000 | 0000 | 0000
+ 248.5000000000000000 | 21.3333333333333333 | 269.8333333333333333 | 0002 | 0002 | 0002
+ 249.5000000000000000 | 22.3333333333333333 | 271.8333333333333333 | 0003 | 0003 | 0003
+ 250.5000000000000000 | 23.3333333333333333 | 273.8333333333333333 | 0004 | 0004 | 0004
+ 251.5000000000000000 | 22.7666666666666667 | 274.2666666666666667 | 0005 | 0005 | 0005
+ 252.5000000000000000 | 22.2000000000000000 | 274.7000000000000000 | 0006 | 0006 | 0006
+ 246.0000000000000000 | 23.9655172413793103 | 269.9655172413793103 | 0008 | 0008 | 0008
+ 247.0000000000000000 | 23.3448275862068966 | 270.3448275862068966 | 0009 | 0009 | 0009
+(8 rows)
+
+-- Add an extra partition to plt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (13, 14);
+ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t1_3.b + t2_3.b) = 0)
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt1_p1 t1
+ -> Hash Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ Join Filter: ((t1_1.b + t2_1.b) = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + t2_2.b) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a
+ -> Append
+ -> Hash Left Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash Right Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt1_p1 t1
+ -> Hash Left Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash Left Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(7 rows)
+
+-- right join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Hash Right Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ -> Seq Scan on plt1_p2 t1_1
+ -> Seq Scan on plt1_p3 t1_2
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(17 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 47 | 0013
+ | | 470 | 0011
+ | | 235 | 0014
+(8 rows)
+
+-- full join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Full Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ -> Seq Scan on plt1_p2 t1_1
+ -> Seq Scan on plt1_p3 t1_2
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(17 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 47 | 0013
+ | | 235 | 0014
+ | | 470 | 0011
+(10 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t1_3.c
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p4 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2_3.c)::text
+ -> Seq Scan on plt2_p4 t2_3
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt2_p1 t2
+ -> Materialize
+ -> Seq Scan on plt1_p1 t1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt1_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_1
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_2
+(29 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t1_3.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> HashAggregate
+ Group Key: (t2_3.c)::text
+ -> Seq Scan on plt1_p4 t2_3
+ -> Materialize
+ -> Seq Scan on plt2_p4 t1_3
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt2_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_1
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_2
+(26 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t1_3.c
+ -> Append
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p4 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p1 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt2_p1 t2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt1_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_2
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 188 | 0 | 0001
+ 282 | 0 | 0010
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p5 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p1 t2
+ -> Seq Scan on plt1_p2 t2_1
+ -> Seq Scan on plt1_p3 t2_2
+ -> Seq Scan on plt1_p4 t2_3
+(21 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 47 | 0 | 0013
+ 235 | 0 | 0014
+ 470 | 0 | 0011
+(3 rows)
+
+-- N-Way joins
+-- t1 join t2 qualify partition-wise-join and t2 join t3 qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c) LEFT JOIN plt2 t3 ON t2.c = t3.c WHERE t1.b + t2.b = 0 AND t2.b + coalesce(t3.b,0) = 0 ORDER BY t1.a,t2.b;
+ QUERY PLAN
+------------------------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t2_3.b
+ -> Result
+ -> Append
+ -> Hash Right Join
+ Hash Cond: ((t3_3.c)::text = (t2_3.c)::text)
+ Filter: ((t2_3.b + COALESCE(t3_3.b, 0)) = 0)
+ -> Seq Scan on plt2_p4 t3_3
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t1_3.b + t2_3.b) = 0)
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Seq Scan on plt1_p1 t1
+ -> Hash
+ -> Hash Left Join
+ Hash Cond: ((t2.c)::text = (t3.c)::text)
+ Filter: ((t2.b + COALESCE(t3.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt2_p1 t3
+ -> Hash Right Join
+ Hash Cond: ((t3_1.c)::text = (t2_1.c)::text)
+ Filter: ((t2_1.b + COALESCE(t3_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p2 t3_1
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ Join Filter: ((t1_1.b + t2_1.b) = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash Right Join
+ Hash Cond: ((t3_2.c)::text = (t2_2.c)::text)
+ Filter: ((t2_2.b + COALESCE(t3_2.b, 0)) = 0)
+ -> Seq Scan on plt2_p3 t3_2
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + t2_2.b) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(48 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c) LEFT JOIN plt2 t3 ON t2.c = t3.c WHERE t1.b + t2.b = 0 AND t2.b + coalesce(t3.b,0) = 0 ORDER BY t1.a,t2.b;
+ a | c | a | c | a | c
+-----+------+-----+------+-----+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- t1 join t2 qualify partition-wise-join and t2 join t3 not qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c) RIGHT JOIN plt2 t3 ON (t2.c = t3.c) WHERE t1.b + coalesce(t2.b,0) = 0 AND coalesce(t2.b,0) + t3.b = 0 ORDER BY t1.a, t2.b;
+ QUERY PLAN
+-----------------------------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t2_3.b
+ -> Result
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t3_3.c)::text = (t1_3.c)::text)
+ Join Filter: ((COALESCE(t2_3.b, 0) + t3_3.b) = 0)
+ -> Seq Scan on plt2_p4 t3_3
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash Join
+ Hash Cond: ((t3.c)::text = (t1.c)::text)
+ Join Filter: ((COALESCE(t2.b, 0) + t3.b) = 0)
+ -> Seq Scan on plt2_p1 t3
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Join Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt1_p1 t1
+ -> Hash Join
+ Hash Cond: ((t3_1.c)::text = (t1_1.c)::text)
+ Join Filter: ((COALESCE(t2_1.b, 0) + t3_1.b) = 0)
+ -> Seq Scan on plt2_p2 t3_1
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ Join Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash Join
+ Hash Cond: ((t3_2.c)::text = (t1_2.c)::text)
+ Join Filter: ((COALESCE(t2_2.b, 0) + t3_2.b) = 0)
+ -> Seq Scan on plt2_p3 t3_2
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(48 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c) RIGHT JOIN plt2 t3 ON (t2.c = t3.c) WHERE t1.b + coalesce(t2.b,0) = 0 AND coalesce(t2.b,0) + t3.b = 0 ORDER BY t1.a, t2.b;
+ a | c | a | c | a | c
+-----+------+-----+------+-----+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c) INNER JOIN plt2 t3 ON (t2.c = t3.c) WHERE coalesce(t1.b, 0) + t2.b = 0 AND t2.b + t3.b = 0 ORDER BY t1.a, t2.b;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.b
+ -> Hash Join
+ Hash Cond: ((t3.c)::text = (t2.c)::text)
+ Join Filter: ((t2.b + t3.b) = 0)
+ -> Append
+ -> Seq Scan on plt2_p1 t3
+ -> Seq Scan on plt2_p2 t3_1
+ -> Seq Scan on plt2_p3 t3_2
+ -> Seq Scan on plt2_p4 t3_3
+ -> Seq Scan on plt2_p5 t3_4
+ -> Hash
+ -> Hash Right Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ -> Seq Scan on plt1_p2 t1_1
+ -> Seq Scan on plt1_p3 t1_2
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c) INNER JOIN plt2 t3 ON (t2.c = t3.c) WHERE coalesce(t1.b, 0) + t2.b = 0 AND t2.b + t3.b = 0 ORDER BY t1.a, t2.b;
+ a | c | a | c | a | c
+-----+------+-----+------+-----+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002 | 376 | 0002
+ | | 470 | 0011 | 470 | 0011
+ | | 47 | 0013 | 47 | 0013
+ | | 235 | 0014 | 235 | 0014
+(8 rows)
+
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 not qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c) FULL JOIN plt1 t3 ON (t2.c = t3.c) WHERE coalesce(t1.b, 0) + t2.b = 0 AND coalesce(t2.b,0) + coalesce(t3.b,0) = 0 ORDER BY t1.a, t2.b;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.b
+ -> Hash Right Join
+ Hash Cond: ((t3.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t2.b, 0) + COALESCE(t3.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt1_p1 t3
+ -> Seq Scan on plt1_p2 t3_1
+ -> Seq Scan on plt1_p3 t3_2
+ -> Seq Scan on plt1_p4 t3_3
+ -> Hash
+ -> Hash Right Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ -> Seq Scan on plt1_p2 t1_1
+ -> Seq Scan on plt1_p3 t1_2
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(26 rows)
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt2_p5;
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0001','0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (1, 13, 14);
+ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ -> Seq Scan on plt1_p2 t1_1
+ -> Seq Scan on plt1_p3 t1_2
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(17 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Left Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ -> Seq Scan on plt1_p2 t1_1
+ -> Seq Scan on plt1_p3 t1_2
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(17 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Hash Right Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ -> Seq Scan on plt1_p2 t1_1
+ -> Seq Scan on plt1_p3 t1_2
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(17 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Full Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ -> Seq Scan on plt1_p2 t1_1
+ -> Seq Scan on plt1_p3 t1_2
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(17 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p4 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(23 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p5 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt1_p1 t2
+ -> Seq Scan on plt1_p2 t2_1
+ -> Seq Scan on plt1_p3 t2_2
+ -> Seq Scan on plt1_p4 t2_3
+(24 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+----------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Nested Loop Anti Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p4 t1_3
+ Filter: (b = 0)
+ -> Materialize
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(20 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p5 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p1 t2
+ -> Seq Scan on plt1_p2 t2_1
+ -> Seq Scan on plt1_p3 t2_2
+ -> Seq Scan on plt1_p4 t2_3
+(21 rows)
+
+-- N-Way joins
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c) INNER JOIN plt1 t3 ON (t2.c = t3.c) WHERE t1.b + t2.b = 0 AND t2.b + t3.b = 0 ORDER BY t1.a, t2.b;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.b
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ -> Seq Scan on plt1_p2 t1_1
+ -> Seq Scan on plt1_p3 t1_2
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t3.c)::text = (t2.c)::text)
+ Join Filter: ((t2.b + t3.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p1 t3
+ -> Seq Scan on plt1_p2 t3_1
+ -> Seq Scan on plt1_p3 t3_2
+ -> Seq Scan on plt1_p4 t3_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(26 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c) INNER JOIN plt1 t3 ON (t2.c = t3.c) WHERE t1.b + t2.b = 0 AND t2.b + t3.b = 0 ORDER BY t1.a, t2.b;
+ a | c | a | c | a | c
+-----+------+-----+------+-----+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005 | 141 | 0005
+ 188 | 0001 | 188 | 0001 | 188 | 0001
+ 329 | 0006 | 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002 | 376 | 0002
+(6 rows)
+
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 not qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c) RIGHT JOIN plt2 t3 ON (t2.c = t3.c) WHERE t1.b + coalesce(t2.b, 0) = 0 AND coalesce(t2.b,0) + t3.b = 0 ORDER BY t1.a, t2.b;
+ QUERY PLAN
+-------------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.b
+ -> Hash Join
+ Hash Cond: ((t3.c)::text = (t1.c)::text)
+ Join Filter: ((COALESCE(t2.b, 0) + t3.b) = 0)
+ -> Append
+ -> Seq Scan on plt2_p1 t3
+ -> Seq Scan on plt2_p2 t3_1
+ -> Seq Scan on plt2_p3 t3_2
+ -> Seq Scan on plt2_p4 t3_3
+ -> Seq Scan on plt2_p5 t3_4
+ -> Hash
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Join Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ -> Seq Scan on plt1_p2 t1_1
+ -> Seq Scan on plt1_p3 t1_2
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(27 rows)
+
+-- partition have a NULL on one side, Partition-wise join is possible with
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- in this case NULL will be treated as addition partition bounds.
+DROP TABLE plt2_p5;
+DROP TABLE plt2_p4;
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000',NULL,'0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, case when i % :part_mod = 11 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (0,11,12);
+ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t2_3.c)::text = (t1_3.c)::text)
+ Join Filter: ((t1_3.b + t2_3.b) = 0)
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt1_p1 t1
+ -> Hash Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ Join Filter: ((t1_1.b + t2_1.b) = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + t2_2.b) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a
+ -> Append
+ -> Hash Right Join
+ Hash Cond: ((t2_3.c)::text = (t1_3.c)::text)
+ Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash Right Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt1_p1 t1
+ -> Hash Left Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash Left Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(7 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+------------------------------------------------------------------
+ Sort
+ Sort Key: t2_3.a
+ -> Result
+ -> Append
+ -> Hash Left Join
+ Hash Cond: ((t2_3.c)::text = (t1_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + t2_3.b) = 0)
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash Left Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt1_p1 t1
+ -> Hash Left Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + t2_1.b) = 0)
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash Right Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + t2_2.b) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(28 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 |
+(6 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t2_3.a
+ -> Append
+ -> Hash Full Join
+ Hash Cond: ((t2_3.c)::text = (t1_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash
+ -> Seq Scan on plt1_p4 t1_3
+ -> Hash Full Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2
+ -> Hash
+ -> Seq Scan on plt1_p1 t1
+ -> Hash Full Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_1
+ -> Hash Full Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_2
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 |
+(8 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t1_3.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> HashAggregate
+ Group Key: (t2_3.c)::text
+ -> Seq Scan on plt2_p4 t2_3
+ -> Materialize
+ -> Seq Scan on plt1_p4 t1_3
+ Filter: (b = 0)
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt2_p1 t2
+ -> Materialize
+ -> Seq Scan on plt1_p1 t1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt1_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_1
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_2
+(29 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t1_3.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> HashAggregate
+ Group Key: (t2_3.c)::text
+ -> Seq Scan on plt1_p4 t2_3
+ -> Materialize
+ -> Seq Scan on plt2_p4 t1_3
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt2_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_1
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_2
+(26 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t1_3.c
+ -> Append
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p4 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p1 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt2_p1 t2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt1_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_2
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 188 | 0 | 0001
+ 282 | 0 | 0010
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1_3.a, t1_3.c
+ -> Append
+ -> Hash Anti Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p4 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt1_p4 t2_3
+ -> Nested Loop Anti Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt2_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_2
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+---
+ 470 | 0 |
+(1 row)
+
+-- partition have a NULL on both side with different partition bounds w.r.t other side
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt1_p3;
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN (NULL,'0008','0009');
+INSERT INTO plt1 SELECT i, i % :cond_mod, case when i % :part_mod = 7 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (7,8,9);
+ANALYZE plt1;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ -> Seq Scan on plt1_p2 t1_1
+ -> Seq Scan on plt1_p4 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Right Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ -> Seq Scan on plt1_p2 t1_1
+ -> Seq Scan on plt1_p4 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Hash Left Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ -> Seq Scan on plt1_p2 t1_1
+ -> Seq Scan on plt1_p4 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Full Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ -> Seq Scan on plt1_p2 t1_1
+ -> Seq Scan on plt1_p4 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p4 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+(22 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt1_p1 t2
+ -> Seq Scan on plt1_p2 t2_1
+ -> Seq Scan on plt1_p4 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(22 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p4 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p1 t2
+ -> Seq Scan on plt2_p2 t2_1
+ -> Seq Scan on plt2_p3 t2_2
+ -> Seq Scan on plt2_p4 t2_3
+(19 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p1 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p1 t2
+ -> Seq Scan on plt1_p2 t2_1
+ -> Seq Scan on plt1_p4 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(19 rows)
-- joins where one of the relations is proven empty
EXPLAIN (COSTS OFF)
@@ -1286,9 +4268,9 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1
-> Hash Left Join
Hash Cond: (t2.b = a)
-> Append
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p2 t2
Filter: (a = 0)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
Filter: (a = 0)
-> Seq Scan on prt2_p3 t2_2
Filter: (a = 0)
@@ -1306,9 +4288,9 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1
-> Hash Left Join
Hash Cond: (t2.b = a)
-> Append
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p2 t2
Filter: (a = 0)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
Filter: (a = 0)
-> Seq Scan on prt2_p3 t2_2
Filter: (a = 0)
@@ -1341,7 +4323,7 @@ CREATE TABLE prt2_l_p3_p2 PARTITION OF prt2_l_p3 FOR VALUES FROM (13) TO (25);
INSERT INTO prt2_l SELECT i % 25, i, to_char(i % 4, 'FM0000') FROM generate_series(0, 599, 3) i;
ANALYZE prt2_l;
-- inner join, qual covering only top-level partitions
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
QUERY PLAN
-------------------------------------------------------------
@@ -1386,7 +4368,7 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1
(4 rows)
-- left join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t1.b = 0 ORDER BY t1.a, t2.b;
QUERY PLAN
------------------------------------------------------------------------------------
@@ -1440,7 +4422,7 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b
(12 rows)
-- right join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.a = 0 ORDER BY t1.a, t2.b;
QUERY PLAN
------------------------------------------------------------------------------------------
@@ -1491,7 +4473,7 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b
(8 rows)
-- full join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.b = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.a = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------
@@ -1552,7 +4534,7 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.b = 0) t1
(16 rows)
-- lateral partition-wise join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT * FROM prt1_l t1 LEFT JOIN LATERAL
(SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.b AS t3b, least(t1.a,t2.a,t3.b) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.c = t3.c)) ss
ON t1.a = ss.t2a AND t1.c = ss.t2c WHERE t1.b = 0 ORDER BY t1.a;
@@ -1626,7 +4608,7 @@ SELECT * FROM prt1_l t1 LEFT JOIN LATERAL
(12 rows)
-- join with one side empty
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c;
QUERY PLAN
-------------------------------------------------------------------------
@@ -1699,15 +4681,15 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2, prt2 t3 WHERE t1.a = t2.a
-> Hash
-> Append
-> Hash Join
- Hash Cond: (t1.a = t3.b)
+ Hash Cond: (t1.a = t3_1.b)
-> Seq Scan on prt1_p1 t1
-> Hash
- -> Seq Scan on prt2_p1 t3
+ -> Seq Scan on prt2_p1 t3_1
-> Hash Join
- Hash Cond: (t1_2.a = t3_1.b)
+ Hash Cond: (t1_2.a = t3.b)
-> Seq Scan on prt1_p2 t1_2
-> Hash
- -> Seq Scan on prt2_p2 t3_1
+ -> Seq Scan on prt2_p2 t3
-> Hash Join
Hash Cond: (t1_1.a = t3_2.b)
-> Seq Scan on prt1_p3 t1_1
@@ -1719,21 +4701,20 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2, prt2 t3 WHERE t1.a = t2.a
-- between partition keys
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON (t1.a < t2.b);
- QUERY PLAN
----------------------------------------------------------
+ QUERY PLAN
+--------------------------------------------
Nested Loop Left Join
+ Join Filter: (t1.a < t2.b)
-> Append
-> Seq Scan on prt1_p1 t1
-> Seq Scan on prt1_p3 t1_1
-> Seq Scan on prt1_p2 t1_2
- -> Append
- -> Index Scan using iprt2_p1_b on prt2_p1 t2
- Index Cond: (t1.a < b)
- -> Index Scan using iprt2_p2_b on prt2_p2 t2_1
- Index Cond: (t1.a < b)
- -> Index Scan using iprt2_p3_b on prt2_p3 t2_2
- Index Cond: (t1.a < b)
-(12 rows)
+ -> Materialize
+ -> Append
+ -> Seq Scan on prt2_p2 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p3 t2_2
+(11 rows)
-- equi-join with join condition on partial keys does not qualify for
-- partition-wise join
@@ -1819,16 +4800,17 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 JOIN prt2_n t2 ON (t1.c = t2.c) JOI
-> Seq Scan on prt2_n_p2 t2_1
-> Hash
-> Hash Join
- Hash Cond: (t3.c = (t1.c)::text)
+ Hash Cond: ((t3.c)::text = (t1.c)::text)
-> Append
-> Seq Scan on plt1_p1 t3
-> Seq Scan on plt1_p2 t3_1
- -> Seq Scan on plt1_p3 t3_2
+ -> Seq Scan on plt1_p4 t3_2
+ -> Seq Scan on plt1_p3 t3_3
-> Hash
-> Append
-> Seq Scan on prt1_n_p1 t1
-> Seq Scan on prt1_n_p2 t1_1
-(16 rows)
+(17 rows)
-- partition-wise join can not be applied for a join between list and range
-- partitioned table
diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql
index cd54ea0..7aa53c0 100644
--- a/src/test/regress/sql/partition_join.sql
+++ b/src/test/regress/sql/partition_join.sql
@@ -7,6 +7,10 @@
SET enable_partition_wise_join to true;
--
+-- tests for range partitioned tables.
+--
+
+--
-- partitioned by a single column
--
CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
@@ -19,16 +23,22 @@ CREATE INDEX iprt1_p2_a on prt1_p2(a);
CREATE INDEX iprt1_p3_a on prt1_p3(a);
ANALYZE prt1;
+-- prt2 have missing starting 0-50 range and missing ending 550-600
+-- range bounds
CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
-CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250);
+CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (50) TO (250);
CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500);
-CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600);
-INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (550);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0+50, 599-50, 3) i;
CREATE INDEX iprt2_p1_b on prt2_p1(b);
CREATE INDEX iprt2_p2_b on prt2_p2(b);
CREATE INDEX iprt2_p3_b on prt2_p3(b);
ANALYZE prt2;
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
+
-- inner join
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
@@ -77,11 +87,19 @@ EXPLAIN (COSTS OFF)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
+EXPLAIN (COSTS OFF)
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.a;
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.a;
+
-- Anti-join with aggregates
EXPLAIN (COSTS OFF)
SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b);
SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b);
+EXPLAIN (COSTS OFF)
+SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a);
+SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a);
+
-- lateral reference
EXPLAIN (COSTS OFF)
SELECT * FROM prt1 t1 LEFT JOIN LATERAL
@@ -126,9 +144,15 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 =
--
-- N-way join
--
+--TODO: remove below comments and enable partition_wise_join
+--one issue got fixed
+--Server crashed with below queries
+--setting partition-wise-join to off
+SET enable_partition_wise_join to false;
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.b = 0 ORDER BY t1.a, t2.b;
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.b = 0 ORDER BY t1.a, t2.b;
+SET enable_partition_wise_join to true;
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
@@ -168,6 +192,156 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
RESET enable_hashjoin;
RESET enable_nestloop;
+-- Add an extra partition to prt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (700);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 699, 3) i;
+ANALYZE prt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- right join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a;
+
+-- full join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+--TODO: remove below comments and enable partition_wise_join
+--one issue got fixed
+--Getting wrong output with partition wise join
+--setting partition-wise-join to off to get correct output
+SET enable_partition_wise_join to false;
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+SET enable_partition_wise_join to true;
+
+-- N-Way joins
+--TODO: remove below comments and enable partition_wise_join
+--one issue got fixed
+--Server crashed with below queries
+--setting partition-wise-join to off
+SET enable_partition_wise_join to false;
+-- t1 join t2 qualify partition-wise-join and t2 join t3 qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+
+-- t1 join t2 qualify partition-wise-join and t2 join t3 not qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) INNER JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) INNER JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 not qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) FULL JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+SET enable_partition_wise_join to true;
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE prt2_p4;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (550) TO (700);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(550, 699, 3) i;
+ANALYZE prt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
+-- N-Way joins
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b) INNER JOIN prt1 t3 ON t2.b = t3.a WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b) INNER JOIN prt1 t3 ON t2.b = t3.a WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 not qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt2 t3 ON t2.b = t3.b WHERE t1.b = 0 ORDER BY 1,2,3,4,5,6;
+
+-- Partition-wise join with minvalue/maxvalue bounds
+DROP TABLE prt2_p1;
+DROP TABLE prt2_p3;
+DROP TABLE prt2_p4;
+CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (MINVALUE) TO (250);
+CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (MAXVALUE);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 250, 3) i;
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(500, 600, 3) i;
+ANALYZE prt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
--
-- partitioned by multiple columns
--
@@ -192,35 +366,311 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1
--
-- tests for list partitioned tables.
--
-CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
-ANALYZE plt1;
+\set part_mod 17
+\set cond_mod 47
+\set num_rows 500
+
+CREATE TABLE plt1 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0001','0002','0003');
+CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0008','0009');
+CREATE TABLE plt1_p4 PARTITION OF plt1 FOR VALUES IN ('0000','0010');
+INSERT INTO plt1 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (7, 11, 12, 13, 14, 15, 16);
+ANALYSE plt1;
+
+-- plt2 have missing starting 0001, additional 0007, missing ending 0010
+-- and additional 0011 and 0012 bounds
+CREATE TABLE plt2 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002','0003');
+CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0007','0008','0009');
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000','0011','0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 10, 13, 14, 15, 16);
+ANALYSE plt2;
+
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
-CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i;
-ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- N-Way joins
+-- inner-inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1,plt2 t2,plt1 t3 WHERE t1.c = t2.c AND t2.c = t3.c AND t1.b + t2.b = 0 AND t2.b + t3.b = 0 ORDER BY t1.a,t2.b;
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1,plt2 t2,plt1 t3 WHERE t1.c = t2.c AND t2.c = t3.c AND t1.b + t2.b = 0 AND t2.b + t3.b = 0 ORDER BY t1.a,t2.b;
+
+-- left-left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c LEFT JOIN plt2 t3 ON (t2.c = t3.c) WHERE t1.b + coalesce(t2.b, 0) = 0 AND t2.b + coalesce(t3.b, 0) = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c LEFT JOIN plt2 t3 ON (t2.c = t3.c) WHERE t1.b + coalesce(t2.b, 0) = 0 AND t2.b + coalesce(t3.b, 0) = 0 ORDER BY t1.a, t2.b;
+
+-- left-right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c RIGHT JOIN plt1 t3 ON (t2.c = t3.c) WHERE t1.b + coalesce(t2.b,0) = 0 AND coalesce(t1.b, 0) + t3.b = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c RIGHT JOIN plt1 t3 ON (t2.c = t3.c) WHERE t1.b + coalesce(t2.b,0) = 0 AND coalesce(t1.b, 0) + t3.b = 0 ORDER BY t1.a, t2.b;
+
+-- right-full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c FULL JOIN plt1 t3 ON (t2.c = t3.c) WHERE coalesce(t1.b,0) + t2.b = 0 AND coalesce(t2.b,0) + coalesce(t3.b,0) = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c FULL JOIN plt1 t3 ON (t2.c = t3.c) WHERE coalesce(t1.b,0) + t2.b = 0 AND coalesce(t2.b,0) + coalesce(t3.b,0) = 0 ORDER BY t1.a, t2.b;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT t3.c FROM plt1 t3 WHERE t3.b = 0)) AND t1.b = 0 ORDER BY t1.a;
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT t3.c FROM plt1 t3 WHERE t3.b = 0)) AND t1.b = 0 ORDER BY t1.a;
--
-- list partitioned by expression
--
CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
-CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0002', '0003');
+CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0004', '0005', '0006');
+CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0008', '0009');
+CREATE TABLE plt1_e_p4 PARTITION OF plt1_e FOR VALUES IN ('0000');
+INSERT INTO plt1_e SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 7, 10, 11, 12, 13, 14, 15, 16);
ANALYZE plt1_e;
--- test partition matching with N-way join
+-- test partition matching with N-way join with expression
EXPLAIN (COSTS OFF)
SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
+-- Add an extra partition to plt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (13, 14);
+ANALYZE plt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+
+-- full join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- N-Way joins
+-- t1 join t2 qualify partition-wise-join and t2 join t3 qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c) LEFT JOIN plt2 t3 ON t2.c = t3.c WHERE t1.b + t2.b = 0 AND t2.b + coalesce(t3.b,0) = 0 ORDER BY t1.a,t2.b;
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c) LEFT JOIN plt2 t3 ON t2.c = t3.c WHERE t1.b + t2.b = 0 AND t2.b + coalesce(t3.b,0) = 0 ORDER BY t1.a,t2.b;
+
+-- t1 join t2 qualify partition-wise-join and t2 join t3 not qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c) RIGHT JOIN plt2 t3 ON (t2.c = t3.c) WHERE t1.b + coalesce(t2.b,0) = 0 AND coalesce(t2.b,0) + t3.b = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c) RIGHT JOIN plt2 t3 ON (t2.c = t3.c) WHERE t1.b + coalesce(t2.b,0) = 0 AND coalesce(t2.b,0) + t3.b = 0 ORDER BY t1.a, t2.b;
+
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c) INNER JOIN plt2 t3 ON (t2.c = t3.c) WHERE coalesce(t1.b, 0) + t2.b = 0 AND t2.b + t3.b = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c) INNER JOIN plt2 t3 ON (t2.c = t3.c) WHERE coalesce(t1.b, 0) + t2.b = 0 AND t2.b + t3.b = 0 ORDER BY t1.a, t2.b;
+
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 not qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c) FULL JOIN plt1 t3 ON (t2.c = t3.c) WHERE coalesce(t1.b, 0) + t2.b = 0 AND coalesce(t2.b,0) + coalesce(t3.b,0) = 0 ORDER BY t1.a, t2.b;
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt2_p5;
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0001','0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (1, 13, 14);
+ANALYZE plt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- N-Way joins
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c) INNER JOIN plt1 t3 ON (t2.c = t3.c) WHERE t1.b + t2.b = 0 AND t2.b + t3.b = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c) INNER JOIN plt1 t3 ON (t2.c = t3.c) WHERE t1.b + t2.b = 0 AND t2.b + t3.b = 0 ORDER BY t1.a, t2.b;
+
+-- t1 join t2 not qualify partition-wise-join and t2 join t3 not qualify partition-wise-join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c) RIGHT JOIN plt2 t3 ON (t2.c = t3.c) WHERE t1.b + coalesce(t2.b, 0) = 0 AND coalesce(t2.b,0) + t3.b = 0 ORDER BY t1.a, t2.b;
+
+-- partition have a NULL on one side, Partition-wise join is possible with
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- in this case NULL will be treated as addition partition bounds.
+DROP TABLE plt2_p5;
+DROP TABLE plt2_p4;
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000',NULL,'0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, case when i % :part_mod = 11 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (0,11,12);
+ANALYZE plt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- partition have a NULL on both side with different partition bounds w.r.t other side
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt1_p3;
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN (NULL,'0008','0009');
+INSERT INTO plt1 SELECT i, i % :cond_mod, case when i % :part_mod = 7 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (7,8,9);
+ANALYZE plt1;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
-- joins where one of the relations is proven empty
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a = 1 AND t1.a = 2;
@@ -260,27 +710,27 @@ INSERT INTO prt2_l SELECT i % 25, i, to_char(i % 4, 'FM0000') FROM generate_seri
ANALYZE prt2_l;
-- inner join, qual covering only top-level partitions
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
-- left join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t1.b = 0 ORDER BY t1.a, t2.b;
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t1.b = 0 ORDER BY t1.a, t2.b;
-- right join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.a = 0 ORDER BY t1.a, t2.b;
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.a = 0 ORDER BY t1.a, t2.b;
-- full join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.b = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.a = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.b = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.a = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
-- lateral partition-wise join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT * FROM prt1_l t1 LEFT JOIN LATERAL
(SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.b AS t3b, least(t1.a,t2.a,t3.b) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.c = t3.c)) ss
ON t1.a = ss.t2a AND t1.c = ss.t2c WHERE t1.b = 0 ORDER BY t1.a;
@@ -289,7 +739,7 @@ SELECT * FROM prt1_l t1 LEFT JOIN LATERAL
ON t1.a = ss.t2a AND t1.c = ss.t2c WHERE t1.b = 0 ORDER BY t1.a;
-- join with one side empty
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c;
--
PFA the patches rebased on the latest sources. There are also fixes
for some of the crashes and bugs reported. I haven't yet included the
testcase patch in the main patchset.
On Mon, Aug 28, 2017 at 12:44 PM, Rajkumar Raghuwanshi
<rajkumar.raghuwanshi@enterprisedb.com> wrote:
On Mon, Aug 21, 2017 at 12:43 PM, Ashutosh Bapat
<ashutosh.bapat@enterprisedb.com> wrote:TODOs
-----------
1. Add tests for advanced partition matching algorithmHi Ashutosh,
I have applied all partition-wise-join patches (v26) and tested feature. I
have modified partition_join.sql file and added extra test cases to test
partition matching.Attaching WIP test case patch which as of now have some server crashes and a
data corruptions issue which is commented in the file itself and need to be
removed once issue got solved. Also some of queries is not picking or
picking partition-wise-join as per expectation which may need some
adjustment.
--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
Attachments:
0011-Modify-bound-comparision-functions-to-accept-members.patchtext/x-patch; charset=US-ASCII; name=0011-Modify-bound-comparision-functions-to-accept-members.patchDownload
From 865242c79b56f021dc619bc028480097d11bb69a Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>
Date: Thu, 6 Jul 2017 14:15:22 +0530
Subject: [PATCH 11/12] Modify bound comparision functions to accept members
of PartitionKey
Functions partition_bound_cmp(), partition_rbound_cmp() and
partition_rbound_datum_cmp() are required to merge partition bounds
from joining relations. While doing so, we do not have access to the
PartitionKey of either relations. So, modify these functions to accept
only required members of PartitionKey so that the functions can be
reused for merging bounds.
Ashutosh Bapat.
---
src/backend/catalog/partition.c | 76 ++++++++++++++++++++++-----------------
1 file changed, 44 insertions(+), 32 deletions(-)
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 96a64ce..d42e1b5 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -126,15 +126,17 @@ static List *generate_partition_qual(Relation rel);
static PartitionRangeBound *make_one_range_bound(PartitionKey key, int index,
List *datums, bool lower);
-static int32 partition_rbound_cmp(PartitionKey key,
- Datum *datums1, PartitionRangeDatumKind *kind1,
- bool lower1, PartitionRangeBound *b2);
-static int32 partition_rbound_datum_cmp(PartitionKey key,
- Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
+static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, Datum *datums1,
+ PartitionRangeDatumKind *kind1, bool lower1,
+ PartitionRangeBound *b2);
+static int32 partition_rbound_datum_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, Datum *rb_datums,
+ PartitionRangeDatumKind *rb_kind,
Datum *tuple_datums);
-static int32 partition_bound_cmp(PartitionKey key,
- PartitionBoundInfo boundinfo,
+static int32 partition_bound_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, PartitionBoundInfo boundinfo,
int offset, void *probe, bool probe_is_bound);
static int partition_bound_bsearch(PartitionKey key,
PartitionBoundInfo boundinfo,
@@ -719,8 +721,9 @@ check_new_partition_bound(char *relname, Relation parent,
* First check if the resulting range would be empty with
* specified lower and upper bounds
*/
- if (partition_rbound_cmp(key, lower->datums, lower->kind, true,
- upper) >= 0)
+ if (partition_rbound_cmp(key->partnatts, key->partsupfunc,
+ key->partcollation, lower->datums,
+ lower->kind, true, upper) >= 0)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -771,9 +774,11 @@ check_new_partition_bound(char *relname, Relation parent,
{
int32 cmpval;
- cmpval = partition_bound_cmp(key, boundinfo,
- offset + 1, upper,
- true);
+ cmpval = partition_bound_cmp(key->partnatts,
+ key->partsupfunc,
+ key->partcollation,
+ boundinfo, offset + 1,
+ upper, true);
if (cmpval < 0)
{
/*
@@ -2138,7 +2143,9 @@ qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
PartitionKey key = (PartitionKey) arg;
- return partition_rbound_cmp(key, b1->datums, b1->kind, b1->lower, b2);
+ return partition_rbound_cmp(key->partnatts, key->partsupfunc,
+ key->partcollation, b1->datums, b1->kind,
+ b1->lower, b2);
}
/*
@@ -2155,7 +2162,7 @@ qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
* two contiguous partitions.
*/
static int32
-partition_rbound_cmp(PartitionKey key,
+partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation,
Datum *datums1, PartitionRangeDatumKind *kind1,
bool lower1, PartitionRangeBound *b2)
{
@@ -2165,7 +2172,7 @@ partition_rbound_cmp(PartitionKey key,
PartitionRangeDatumKind *kind2 = b2->kind;
bool lower2 = b2->lower;
- for (i = 0; i < key->partnatts; i++)
+ for (i = 0; i < partnatts; i++)
{
/*
* First, handle cases where the column is unbounded, which should not
@@ -2186,8 +2193,8 @@ partition_rbound_cmp(PartitionKey key,
*/
break;
- cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
- key->partcollation[i],
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
+ partcollation[i],
datums1[i],
datums2[i]));
if (cmpval != 0)
@@ -2213,22 +2220,23 @@ partition_rbound_cmp(PartitionKey key,
* is <, =, or > partition key of tuple (tuple_datums)
*/
static int32
-partition_rbound_datum_cmp(PartitionKey key,
- Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
+partition_rbound_datum_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, Datum *rb_datums,
+ PartitionRangeDatumKind *rb_kind,
Datum *tuple_datums)
{
int i;
int32 cmpval = -1;
- for (i = 0; i < key->partnatts; i++)
+ for (i = 0; i < partnatts; i++)
{
if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
return -1;
else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
return 1;
- cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
- key->partcollation[i],
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
+ partcollation[i],
rb_datums[i],
tuple_datums[i]));
if (cmpval != 0)
@@ -2245,17 +2253,18 @@ partition_rbound_datum_cmp(PartitionKey key,
* specified in *probe.
*/
static int32
-partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo,
- int offset, void *probe, bool probe_is_bound)
+partition_bound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation,
+ PartitionBoundInfo boundinfo, int offset, void *probe,
+ bool probe_is_bound)
{
Datum *bound_datums = boundinfo->datums[offset];
int32 cmpval = -1;
- switch (key->strategy)
+ switch (boundinfo->strategy)
{
case PARTITION_STRATEGY_LIST:
- cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
- key->partcollation[0],
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
+ partcollation[0],
bound_datums[0],
*(Datum *) probe));
break;
@@ -2273,12 +2282,14 @@ partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo,
*/
bool lower = boundinfo->indexes[offset] < 0;
- cmpval = partition_rbound_cmp(key,
- bound_datums, kind, lower,
+ cmpval = partition_rbound_cmp(partnatts, partsupfunc,
+ partcollation, bound_datums,
+ kind, lower,
(PartitionRangeBound *) probe);
}
else
- cmpval = partition_rbound_datum_cmp(key,
+ cmpval = partition_rbound_datum_cmp(partnatts, partsupfunc,
+ partcollation,
bound_datums, kind,
(Datum *) probe);
break;
@@ -2286,7 +2297,7 @@ partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo,
default:
elog(ERROR, "unexpected partition strategy: %d",
- (int) key->strategy);
+ (int) boundinfo->strategy);
}
return cmpval;
@@ -2320,7 +2331,8 @@ partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo,
int32 cmpval;
mid = (lo + hi + 1) / 2;
- cmpval = partition_bound_cmp(key, boundinfo, mid, probe,
+ cmpval = partition_bound_cmp(key->partnatts, key->partsupfunc,
+ key->partcollation, boundinfo, mid, probe,
probe_is_bound);
if (cmpval <= 0)
{
--
1.7.9.5
0012-WIP-Partition-wise-join-for-1-1-1-0-0-1-partition-ma.patchtext/x-patch; charset=US-ASCII; name=0012-WIP-Partition-wise-join-for-1-1-1-0-0-1-partition-ma.patchDownload
From 737299aa79cc5d8e9fbf825ef6396696c485031d Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>
Date: Wed, 9 Aug 2017 12:30:34 +0530
Subject: [PATCH 12/12] WIP Partition-wise join for 1:1, 1:0, 0:1 partition
matching.
Earlier version of partition-wise join implementation allowed
partition-wise join between two relations with exactly same partition
bounds. This commit allows partition-wise join to be applied under
following conditions
1. the partition bounds of joining relations are such that rows from
given partition on one side join can join with rows from maximum one
partition on the other side i.e. bounds of a given partition on one
side match/overlap with those of maximum one partition on the other
side. If the mapping happens to be m:n where m > 1 or n > 1, we have
to gang multiple partition relations together into a single relation.
This means that we have to add simple relations during join
processing, something which is not supported right now. ALso, in such
a case, different pairs of joining relations can produce different
partition bounds for the same join relation, which again is not
supported right now.
2. For every partition on outer side that can contribute to the result
of an OUTER side, there exists at least one (taken along with item 1,
it means exactly one) matching partition on the inner side. To
support partition-wise join when the inner matching partition doesn't
exist, we have to add a dummy base relation corresponding to the
non-existent inner partition. We don't have support add base relations
during join processing.
This commit is not complete yet.
Ashutosh Bapat.
---
src/backend/catalog/partition.c | 1255 +++++++++++++++++++++++++++++++++
src/backend/optimizer/path/joinrels.c | 77 +-
src/backend/optimizer/util/relnode.c | 42 +-
src/include/catalog/partition.h | 6 +
4 files changed, 1349 insertions(+), 31 deletions(-)
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index d42e1b5..2d1a905 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -141,6 +141,38 @@ static int32 partition_bound_cmp(int partnatts, FmgrInfo *partsupfunc,
static int partition_bound_bsearch(PartitionKey key,
PartitionBoundInfo boundinfo,
void *probe, bool probe_is_bound, bool *is_equal);
+static PartitionBoundInfo partition_range_bounds_merge(int partnatts,
+ FmgrInfo *supfuncs, Oid *collations,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2);
+static PartitionBoundInfo partition_list_bounds_merge(int partnatts,
+ FmgrInfo *partsupfunc, Oid *collations,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2);
+static void generate_matching_part_pairs(int *mergemap1, int npart1,
+ int *mergemap2, int nparts2, JoinType jointype,
+ int nparts, List **parts1, List **parts2);
+static PartitionBoundInfo build_merged_partition_bounds(char strategy,
+ List *merged_datums, List *merged_indexes,
+ List *merged_contents, int null_index);
+static int map_and_merge_partitions(int *partmap1, int *mergemap1, int index1,
+ int *partmap2, int *mergemap2, int index2,
+ int *next_index);
+static int32 partition_range_bound_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *collations, PartitionRangeBound *bound1,
+ PartitionRangeBound *bound2);
+static int partition_range_cmp(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, PartitionRangeBound *lower_bound1,
+ PartitionRangeBound *upper_bound1,
+ PartitionRangeBound *lower_bound2,
+ PartitionRangeBound *upper_bound2, bool *overlap);
+static bool partition_range_merge_next_lb(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, Datum *next_lb_datums,
+ PartitionRangeDatumKind *next_lb_kind,
+ List **merged_datums, List **merged_kinds,
+ List **merged_indexes);
/*
* RelationBuildPartitionDesc
@@ -2348,3 +2380,1226 @@ partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo,
return lo;
}
+
+/*
+ * Merge the given partition bounds.
+ *
+ * If given partition bounds can not be merged, return NULL.
+ *
+ * The function also returns two lists of partition indexes one for each of the
+ * joining relations. Both the lists contain the same number of elements. The
+ * partition indexes at the same positions in the list indicate partitions from
+ * each side to be joined and their position corresponds to the index of
+ * partition to which the results of the child-join belong in the partitioned
+ * join.
+ */
+extern PartitionBoundInfo
+partition_bounds_merge(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2)
+{
+ PartitionBoundInfo merged_bounds;
+ char strategy;
+
+ /* Bail out if partitioning strategies are different. */
+ if (boundinfo1->strategy != boundinfo2->strategy)
+ return NULL;
+
+ *parts1 = NIL;
+ *parts2 = NIL;
+ strategy = boundinfo1->strategy;
+ if (strategy == PARTITION_STRATEGY_LIST)
+ merged_bounds = partition_list_bounds_merge(partnatts, partsupfunc,
+ partcollation, boundinfo1,
+ nparts1, boundinfo2,
+ nparts2, jointype, parts1,
+ parts2);
+ else if (strategy == PARTITION_STRATEGY_RANGE)
+ merged_bounds = partition_range_bounds_merge(partnatts, partsupfunc,
+ partcollation, boundinfo1,
+ nparts1, boundinfo2,
+ nparts2, jointype, parts1,
+ parts2);
+ else
+ elog(ERROR, "unexpected partition strategy: %d", strategy);
+
+ Assert(merged_bounds || (*parts1 == NIL && *parts2 == NIL));
+ return merged_bounds;
+}
+
+/*
+ * partition_get_range_bounds
+ *
+ * Given the index of lower bound in datums array, return lower and upper
+ * bounds and the index of the partition with that lower bound.
+ */
+static int
+partition_get_range_bounds(PartitionBoundInfo bi, int lb_index,
+ PartitionRangeBound *lower,
+ PartitionRangeBound *upper)
+{
+ int part_index;
+
+ /* A lower bound should have at least one more bound after it. */
+ Assert(lb_index < bi->ndatums - 1);
+
+ /* The lower bound should correspond to a valid partition. */
+ part_index = bi->indexes[lb_index + 1];
+ Assert(part_index >= 0);
+
+ lower->kind = bi->kind[lb_index];
+ lower->datums = bi->datums[lb_index];
+ lower->lower = true;
+ upper->kind = bi->kind[lb_index + 1];
+ upper->datums = bi->datums[lb_index + 1];
+ upper->lower = false;
+
+ return part_index;
+}
+
+/*
+ * partition_range_get_next_lb_index
+ *
+ * Given the index of lower bound in datums array return the
+ * index of lower bound of the next partition. When the given index corresponds
+ * to the last partition, return number of datums (ndatums).
+ */
+static int
+partition_range_get_next_lb_index(PartitionBoundInfo bi, int lb_index)
+{
+ /* A lower bound should have at least one more bound after it. */
+ Assert(lb_index < bi->ndatums - 1);
+
+ /* The partition index corresponding to the upper bound should be valid. */
+ Assert(bi->indexes[lb_index + 1] >= 0);
+
+ /*
+ * If there are no bounds left beyond the upper bound, we have reached the
+ * last partition.
+ */
+ if (lb_index + 2 < bi->ndatums)
+ {
+ /*
+ * If the bound next to the upper bound corresponds to no partition,
+ * that's the next lower bound of the next partition. Otherwise, the
+ * current upper bound is the lower bound of the next partition.
+ */
+ if (bi->indexes[lb_index + 2] < 0)
+ return lb_index + 2;
+ else
+ return lb_index + 1;
+ }
+ else
+ return bi->ndatums;
+}
+
+static int32
+partition_range_bound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *collations,
+ PartitionRangeBound *bound1,
+ PartitionRangeBound *bound2)
+{
+ return partition_rbound_cmp(partnatts, partsupfunc, collations,
+ bound1->datums, bound1->kind, bound1->lower,
+ bound2);
+}
+
+/*
+ * partition_range_cmp
+ *
+ * Compare the bounds of two range partitions and return <, = or > 0, if the
+ * first partition's upper bound is lower than, equal to or higher than the
+ * second partition's upper bound resp.
+ *
+ * Also, set overlaps to true, if the ranges overlap, otherwise set it to
+ * false.
+ */
+static int
+partition_range_cmp(int partnatts, FmgrInfo *supfuncs, Oid *collations,
+ PartitionRangeBound *lower_bound1,
+ PartitionRangeBound *upper_bound1,
+ PartitionRangeBound *lower_bound2,
+ PartitionRangeBound *upper_bound2, bool *overlap)
+{
+ /*
+ * Compare upper bound of the first partition with the lower bound of the
+ * second and vice-versa. If lower bound is higher than the upper bound,
+ * the partitions are not overlapping. All other cases indicate overlapping
+ * partitions.
+ * TODO: Add a testcase which has lower and upper bound matching exactly.
+ * Lower bound is inclusive and upper bound is exclusive, so even if the
+ * datums match, the bounds do not match exactly.
+ */
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ lower_bound1, upper_bound2) > 0)
+ {
+ *overlap = false;
+ return 1;
+ }
+ else if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ lower_bound2, upper_bound1) > 0)
+ {
+ *overlap = false;
+ return -1;
+ }
+ else
+ {
+ *overlap = true;
+ return partition_range_bound_cmp(partnatts, supfuncs, collations,
+ upper_bound1, upper_bound2);
+ }
+}
+
+/*
+ * partition_range_merge
+ *
+ * Merge the partition bounds of given two partitions such that the join
+ * between the given two partitions fits merged bounds.
+ *
+ * "merged_upper" will be set to one of the given upper bounds and
+ * "merged_lower" will be set to one of the given lower bounds.
+ */
+static void
+partition_range_merge(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, JoinType jointype,
+ PartitionRangeBound *left_lb,
+ PartitionRangeBound *left_ub,
+ PartitionRangeBound *right_lb,
+ PartitionRangeBound *right_ub,
+ PartitionRangeBound **merged_lb,
+ PartitionRangeBound **merged_ub)
+{
+ /*
+ * An outer join will have all the rows from the outer side, so merged
+ * bounds will be same as the outer bounds. An inner join will have rows
+ * that fit both the bounds, thus lower merged bound will be higher of two
+ * lower bounds and upper merged bound will be lower of the two upper
+ * bounds.
+ */
+ switch (jointype)
+ {
+ case JOIN_LEFT:
+ case JOIN_ANTI:
+ *merged_ub = left_ub;
+ *merged_lb = left_lb;
+ break;
+
+ case JOIN_RIGHT:
+ *merged_ub = right_ub;
+ *merged_lb = right_lb;
+ break;
+
+ case JOIN_INNER:
+ case JOIN_SEMI:
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_ub, right_ub) < 0)
+ *merged_ub = left_ub;
+ else
+ *merged_ub = right_ub;
+
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_lb, right_lb) > 0)
+ *merged_lb = left_lb;
+ else
+ *merged_lb = right_lb;
+ break;
+
+ case JOIN_FULL:
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_ub, right_ub) > 0)
+ *merged_ub = left_ub;
+ else
+ *merged_ub = right_ub;
+
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_lb, right_lb) < 0)
+ *merged_lb = left_lb;
+ else
+ *merged_lb = right_lb;
+ break;
+
+ default:
+ elog(ERROR, "Unexpected join type %d", jointype);
+ }
+
+ return;
+}
+
+/*
+ * Add the lower bound of the next range to the list of bounds, if the lower
+ * bound is higher or equal to the previous upper bound. If successful return
+ * true, otherwise false.
+ */
+static bool
+partition_range_merge_next_lb(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, Datum *next_lb_datums,
+ PartitionRangeDatumKind *next_lb_kind,
+ List **merged_datums, List **merged_kinds,
+ List **merged_indexes)
+{
+ int cmpval;
+
+ if (!*merged_datums)
+ {
+ Assert(!*merged_kinds && !*merged_indexes);
+ cmpval = 1;
+ }
+ else
+ {
+ PartitionRangeBound prev_ub;
+
+ prev_ub.datums = llast(*merged_datums);
+ prev_ub.kind = llast(*merged_kinds);
+ prev_ub.lower = false;
+
+ /*
+ * TODO: explain why do we pass lower to be false for the next lower
+ * bound.
+ */
+ cmpval = partition_rbound_cmp(partnatts, supfuncs, collations,
+ next_lb_datums, next_lb_kind, false,
+ &prev_ub);
+ }
+
+ /*
+ * The lower bound is lower than the last upper bound, thus does not fit
+ * the bounds created so far and hence can not be merged with the existing
+ * bounds.
+ */
+ if (cmpval < 0)
+ return false;
+
+ /*
+ * Add bounds of the new merged partition. If the next lower bound is
+ * higher than the last upper bound, add new range with index
+ * corresponding to the lower bound as -1. If the merged lower bound
+ * is same as the last merged upper bound, the last upper bound will be
+ * reused as the lower bound of the next range.
+ */
+ if (cmpval > 0)
+ {
+ *merged_datums = lappend(*merged_datums, next_lb_datums);
+ *merged_kinds = lappend(*merged_kinds, next_lb_kind);
+ *merged_indexes = lappend_int(*merged_indexes, -1);
+ }
+
+ return true;
+}
+
+/*
+ * Merge given two range partition bounds.
+ *
+ * Work horse function for partition_bounds_merge() for range partitioned
+ * tables.
+ *
+ * TODO: for an anti-join, the caller is supposed to send the outer relation as
+ * left relation. May be we should rename left and right as inner and outer. We
+ * don't need to handle RIGHT joins in this function, so renaming them as outer
+ * and inner is fine.
+ */
+static PartitionBoundInfo
+partition_range_bounds_merge(int partnatts, FmgrInfo *supfuncs, Oid *collations,
+ PartitionBoundInfo left_bi, int left_nparts,
+ PartitionBoundInfo right_bi, int right_nparts,
+ JoinType jointype, List **left_parts, List **right_parts)
+{
+ int *left_pmap;
+ int *left_mmap;
+ int *right_pmap;
+ int *right_mmap;
+ int cnt1;
+ int cnt2;
+ int left_part;
+ int right_part;
+ PartitionBoundInfo merged_bounds = NULL;
+ bool merged = true; /* By default we ranges are merge-able. */
+ int left_lb_index;
+ int right_lb_index;
+ int next_index;
+ int cmpval;
+ List *merged_datums = NIL;
+ List *merged_indexes = NIL;
+ List *merged_kinds = NIL;
+
+ Assert(left_bi->strategy == right_bi->strategy &&
+ left_bi->strategy == PARTITION_STRATEGY_RANGE);
+
+ *left_parts = NIL;
+ *right_parts = NIL;
+
+ /* Allocate and initialize partition maps. */
+ left_pmap = (int *) palloc(sizeof(int) * left_nparts);
+ left_mmap = (int *) palloc(sizeof(int) * left_nparts);
+ right_pmap = (int *) palloc(sizeof(int) * right_nparts);
+ right_mmap = (int *) palloc(sizeof(int) * right_nparts);
+
+ for (cnt1 = 0; cnt1 < left_nparts; cnt1++)
+ {
+ left_pmap[cnt1] = -1;
+ left_mmap[cnt1] = -1;
+ }
+ for (cnt2 = 0; cnt2 < right_nparts; cnt2++)
+ {
+ right_pmap[cnt2] = -1;
+ right_mmap[cnt2] = -1;
+ }
+
+ left_lb_index = 0;
+ right_lb_index = 0;
+ next_index = 0;
+ while (left_lb_index < left_bi->ndatums &&
+ right_lb_index < right_bi->ndatums)
+ {
+ PartitionRangeBound left_lb;
+ PartitionRangeBound left_ub;
+ PartitionRangeBound right_lb;
+ PartitionRangeBound right_ub;
+ PartitionRangeBound *merged_lb = NULL;
+ PartitionRangeBound *merged_ub = NULL;
+ int merged_index = -1;
+ bool overlap;
+
+ /* Get the range bounds of the next partition. */
+ left_part = partition_get_range_bounds(left_bi, left_lb_index,
+ &left_lb, &left_ub);
+ right_part = partition_get_range_bounds(right_bi, right_lb_index,
+ &right_lb, &right_ub);
+
+ cmpval = partition_range_cmp(partnatts, supfuncs, collations,
+ &left_lb, &left_ub, &right_lb, &right_ub,
+ &overlap);
+
+ if (overlap)
+ {
+ /* Overlapping ranges, try merging. */
+ partition_range_merge(partnatts, supfuncs, collations, jointype,
+ &left_lb, &left_ub, &right_lb, &right_ub,
+ &merged_lb, &merged_ub);
+ merged_index = map_and_merge_partitions(left_pmap, left_mmap,
+ left_part, right_pmap,
+ right_mmap, right_part,
+ &next_index);
+
+ if (merged_index < 0)
+ {
+ merged = false;
+ break;
+ }
+ }
+
+ if (cmpval == 0)
+ {
+ Assert(overlap);
+
+ /* Move to the next pair of partitions. */
+ left_lb_index = partition_range_get_next_lb_index(left_bi,
+ left_lb_index);
+ right_lb_index = partition_range_get_next_lb_index(right_bi,
+ right_lb_index);
+ }
+ else if (cmpval < 0)
+ {
+ /*
+ * If the partition on the left was not mapped to any partition on
+ * the right. Any row from that partition will not have a matching
+ * row from the other relation. So the partition will be part of
+ * the join if it's an anti-join or the left side is the outer
+ * side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_SEMI ||
+ jointype == JOIN_RIGHT)
+ {
+ /* Nothing to do. */
+ }
+ else if (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI)
+ {
+ if (left_mmap[left_part] < 0)
+ {
+ left_mmap[left_part] = next_index++;
+ merged_index = left_mmap[left_part];
+ merged_lb = &left_lb;
+ merged_ub = &left_ub;
+ }
+ }
+ else
+ {
+ /* Don't know what to do with other join types. Bail out. */
+ merged = false;
+ }
+
+ /* Move to the next partition on the left side. */
+ left_lb_index = partition_range_get_next_lb_index(left_bi,
+ left_lb_index);
+ }
+ else
+ {
+ Assert(cmpval > 0);
+
+ /*
+ * If the partition on the right was not mapped to any partition on
+ * the left. Any row from that partition will not have a matching
+ * row from the other relation. So the partition will be part of
+ * the join if the right side is the outer side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_SEMI ||
+ jointype == JOIN_LEFT || jointype == JOIN_ANTI)
+ {
+ /* Nothing to do. */
+ }
+ else if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ if (right_mmap[right_part] < 0)
+ {
+ right_mmap[right_part] = next_index++;
+ merged_index = right_mmap[right_part];
+ merged_lb = &right_lb;
+ merged_ub = &right_ub;
+ }
+ }
+ else
+ {
+ /* Don't know what to do with other join types. Bail out. */
+ merged = false;
+ }
+
+ /* Move to the next partition on the right side. */
+ right_lb_index = partition_range_get_next_lb_index(right_bi,
+ right_lb_index);
+ }
+
+ if (!merged)
+ break;
+
+ /* A skipped partition is not added to merged bounds. */
+ if (merged_index < 0)
+ continue;
+
+ /*
+ * We have a valid partition index for the next partition of join. The
+ * partition should have valid range.
+ */
+ Assert(merged_lb && merged_ub);
+
+ /* Try merging merged lower bound with the last upper bound. */
+ merged = partition_range_merge_next_lb(partnatts, supfuncs,
+ collations, merged_lb->datums,
+ merged_lb->kind, &merged_datums,
+ &merged_kinds, &merged_indexes);
+ if (merged)
+ {
+ /* Add upper bound with the merged partition index. */
+ merged_datums = lappend(merged_datums, merged_ub->datums);
+ merged_kinds = lappend(merged_kinds, merged_ub->kind);
+ merged_indexes = lappend_int(merged_indexes, merged_index);
+ }
+ else
+ break;
+ }
+
+ /*
+ * We will run the above loop till we exhaust ranges of at least one side
+ * unless we failed to merge the ranges.
+ */
+ Assert (!merged || (left_lb_index >= left_bi->ndatums ||
+ right_lb_index >= right_bi->ndatums));
+
+ /*
+ * Handle any remaining partition bounds. If remaining partitions fall on
+ * the inner side of the join, none of the rows in those partition are
+ * going to be joined with any row on the outer side and hence those
+ * partitions will not be part of the join result. Hence only consider the
+ * remaining partitions on the outer side of the join.
+ */
+ if (merged &&
+ ((left_lb_index < left_bi->ndatums &&
+ (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI)) ||
+ (right_lb_index < right_bi->ndatums &&
+ (jointype == JOIN_RIGHT || jointype == JOIN_FULL))))
+ {
+ int bound_index = -1;
+ PartitionBoundInfo rem_bi = NULL;
+ int *mmap = NULL;
+ int part_index;
+
+ if (left_lb_index < left_bi->ndatums)
+ {
+ Assert(jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI);
+ bound_index = left_lb_index;
+ rem_bi = left_bi;
+ mmap = left_mmap;
+ part_index = left_part;
+ }
+ else if (right_lb_index < right_bi->ndatums)
+ {
+ Assert(jointype == JOIN_RIGHT || jointype == JOIN_FULL);
+ bound_index = right_lb_index;
+ rem_bi = right_bi;
+ mmap = right_mmap;
+ part_index = right_part;
+ }
+ Assert((bound_index >= 0 && bound_index < rem_bi->ndatums) &&
+ rem_bi && mmap && part_index >= 0);
+
+ /*
+ * If the partition corresponding to this lower bound has been already
+ * mapped to a merged partition, don't need to add it again. This may
+ * happen if the range of the last partition on the inner side overlaps
+ * with this partition's range and has upper bound lesser than upper
+ * bound of this partition's range.
+ */
+ if (mmap[part_index] >= 0)
+ bound_index = partition_range_get_next_lb_index(rem_bi, bound_index);
+
+ /*
+ * Merge lower bound of the next range with the upper bound of last
+ * range.
+ */
+ if (bound_index < rem_bi->ndatums)
+ merged = partition_range_merge_next_lb(partnatts, supfuncs,
+ collations,
+ rem_bi->datums[bound_index],
+ rem_bi->kind[bound_index],
+ &merged_datums,
+ &merged_kinds,
+ &merged_indexes);
+
+ /*
+ * Rest of the bounds correspond to valid ranges so add them after
+ * remapping their partitions as required.
+ */
+ for (bound_index++; merged && bound_index < rem_bi->ndatums;
+ bound_index++)
+ {
+ Datum *datums = rem_bi->datums[bound_index];
+ int index = rem_bi->indexes[bound_index];
+ int part_index;
+
+ /*
+ * Add lower bounds with partition index -1 and assign a new
+ * partition index to the upper bounds.
+ */
+ if (index < 0)
+ part_index = index;
+ else
+ {
+ if (mmap[index] < 0)
+ mmap[index] = next_index++;
+ part_index = mmap[index];
+ }
+
+ merged_indexes = lappend_int(merged_indexes, part_index);
+ merged_datums = lappend(merged_datums, datums);
+ merged_kinds = lappend(merged_kinds,
+ rem_bi->kind[bound_index]);
+ }
+ }
+
+ /* Create PartitionBoundInfo */
+ if (merged)
+ {
+ /* Use maps to match partition from the joining relations. */
+ generate_matching_part_pairs(left_mmap, left_nparts, right_mmap,
+ right_nparts, jointype, next_index,
+ left_parts, right_parts);
+
+ /* Craft a PartitionBoundInfo to return. */
+ if (*left_parts && *right_parts)
+ {
+ Assert(list_length(*left_parts) == list_length(*right_parts));
+ Assert(list_length(*left_parts) == next_index);
+ merged_bounds = build_merged_partition_bounds(left_bi->strategy,
+ merged_datums,
+ merged_indexes,
+ merged_kinds,
+ -1);
+ }
+ }
+
+ list_free(merged_datums);
+ list_free(merged_indexes);
+ pfree(left_pmap);
+ pfree(right_pmap);
+ pfree(left_mmap);
+ pfree(right_mmap);
+
+ /* Free any memory we used in this function. */
+ return merged_bounds;
+}
+
+/*
+ * partition_bounds_merge()'s arm for list partitioned tables.
+ *
+ * The function builds the maps of matching partitions from either relation. It
+ * builds the list of partition key values that may appear in the join result
+ * alongwith the list of indexes of partitions of join to which those values
+ * belong. It then crafts a PartitionBoundInfo structure representing the
+ * partition bounds of the join result.
+ */
+static PartitionBoundInfo
+partition_list_bounds_merge(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, PartitionBoundInfo left_bi,
+ int left_nparts, PartitionBoundInfo right_bi,
+ int right_nparts, JoinType jointype, List **left_parts,
+ List **right_parts)
+{
+ int *left_pmap; /* left to right partition map */
+ int *left_mmap; /* left to merged partition map */
+ int *right_pmap; /* right to left partition map */
+ int *right_mmap; /* right to merged partition map */
+ int cntl;
+ int cntr;
+ bool merged = true;
+ List *merged_datums = NIL;
+ List *merged_indexes = NIL;
+ int next_index = 0;
+ int null_index;
+ PartitionBoundInfo merged_bounds = NULL;
+ int *left_indexes = left_bi->indexes;
+ int *right_indexes = right_bi->indexes;
+ int left_ni = left_bi->null_index;
+ int right_ni = right_bi->null_index;
+
+ Assert(left_bi->strategy == right_bi->strategy &&
+ left_bi->strategy == PARTITION_STRATEGY_LIST);
+
+ /* List partitions do not require unbounded ranges. */
+ Assert(!left_bi->kind && !right_bi->kind);
+
+ /* Allocate and initialize partition maps. */
+ left_pmap = (int *) palloc(sizeof(int) * left_nparts);
+ left_mmap = (int *) palloc(sizeof(int) * left_nparts);
+ right_pmap = (int *) palloc(sizeof(int) * right_nparts);
+ right_mmap = (int *) palloc(sizeof(int) * right_nparts);
+
+ /* Initialize partition maps. */
+ for (cntl = 0; cntl < left_nparts; cntl++)
+ {
+ left_pmap[cntl] = -1;
+ left_mmap[cntl] = -1;
+ }
+ for (cntr = 0; cntr < right_nparts; cntr++)
+ {
+ right_pmap[cntr] = -1;
+ right_mmap[cntr] = -1;
+ }
+
+ cntl = cntr = 0;
+ while (cntl < left_bi->ndatums && cntr < right_bi->ndatums)
+ {
+ Datum *ldatums = left_bi->datums[cntl];
+ Datum *rdatums = right_bi->datums[cntr];
+ int l_index = left_indexes[cntl];
+ int r_index = right_indexes[cntr];
+ int cmpval;
+ int merged_index;
+ Datum *merged_datum;
+
+ /* Every list datum should map to a valid partition index. */
+ Assert(l_index >= 0 && r_index >= 0);
+
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
+ partcollation[0], ldatums[0],
+ rdatums[0]));
+ if (cmpval == 0)
+ {
+ /*
+ * Try matching partitions containing the matching datums. If
+ * successful, add the datum to the merged bounds with index of
+ * merged partition containing it.
+ */
+ merged_datum = ldatums;
+ merged_index = map_and_merge_partitions(left_pmap, left_mmap, l_index,
+ right_pmap, right_mmap, r_index,
+ &next_index);
+
+ if (merged_index < 0)
+ {
+ merged = false;
+ break;
+ }
+
+ /* Move to the next pair of bounds. */
+ cntl++;
+ cntr++;
+ }
+ else if (cmpval < 0)
+ {
+ /*
+ * This list datum is present in the left side but not the right
+ * side. So it will appear in the join when the left side is outer
+ * side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_RIGHT ||
+ jointype == JOIN_SEMI)
+ merged_index = -1;
+ else if (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI)
+ {
+ if (left_mmap[l_index] < 0)
+ left_mmap[l_index] = next_index++;
+ merged_index = left_mmap[l_index];
+ merged_datum = ldatums;
+ }
+ else
+ {
+ /* Don't know what to do with other join types. */
+ merged = false;
+ break;
+ }
+
+ /* Move to the next datum on the left side. */
+ cntl++;
+ }
+ else
+ {
+ Assert(cmpval > 0);
+
+ /*
+ * This list datum is present in the right side but not the left
+ * side. So it will appear in the join when the right side is outer
+ * side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_LEFT ||
+ jointype == JOIN_SEMI || jointype == JOIN_ANTI)
+ merged_index = -1;
+ else if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ /*
+ * Every list value on the outer side will appear in the
+ * join. Find the merged partition to which this value
+ * belongs.
+ */
+ if (right_mmap[r_index] < 0)
+ right_mmap[r_index] = next_index++;
+ merged_index = right_mmap[r_index];
+ merged_datum = rdatums;
+ }
+ else
+ {
+ /* Don't know what to do with other join types. */
+ merged = false;
+ break;
+ }
+
+ /* Move to the next datum on the right side. */
+ cntr++;
+ }
+
+ /*
+ * Add the datum with appropriate index in the list of datums, if the
+ * rows containing that datum are deemed to be part of the join.
+ */
+ if (merged_index >= 0)
+ {
+ merged_indexes = lappend_int(merged_indexes, merged_index);
+ merged_datums = lappend(merged_datums, merged_datum);
+ }
+ }
+
+ /*
+ * If merge is unsuccessful, bail out without any further processing.
+ * That leaks the memory allocated in this function. So, try not to leak
+ * memory.
+ */
+ if (!merged)
+ goto merge_failed;
+
+ /* We should have exhausted datums on at least one side. */
+ Assert(cntr >= right_bi->ndatums || cntl >= left_bi->ndatums);
+
+ /*
+ * Add any remaining list values on the outer side, assigning partition
+ * indexes if required.
+ */
+ if (jointype == JOIN_LEFT || jointype == JOIN_FULL || jointype == JOIN_ANTI)
+ {
+ for (;cntl < left_bi->ndatums; cntl++)
+ {
+ Datum *ldatums = left_bi->datums[cntl];
+ int l_index = left_indexes[cntl];
+
+ if (left_mmap[l_index] < 0)
+ left_mmap[l_index] = next_index++;
+ merged_indexes = lappend_int(merged_indexes, left_mmap[l_index]);
+ merged_datums = lappend(merged_datums, ldatums);
+ }
+ }
+
+ if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ for (;cntr < right_bi->ndatums; cntr++)
+ {
+ Datum *rdatums = right_bi->datums[cntr];
+ int r_index = right_indexes[cntr];
+
+ if (right_mmap[r_index] < 0)
+ right_mmap[r_index] = next_index++;
+ merged_indexes = lappend_int(merged_indexes, right_mmap[r_index]);
+ merged_datums = lappend(merged_datums, rdatums);
+ }
+ }
+
+ /*
+ * Merge NULL partitions if any. Find the index of merged partition to
+ * which the NULL values belong in the join result. We can eliminate a NULL
+ * partition when it appears only in the inner relation.
+ */
+ if (!partition_bound_accepts_nulls(left_bi) &&
+ !partition_bound_accepts_nulls(right_bi))
+ null_index = -1;
+ else if (partition_bound_accepts_nulls(left_bi) &&
+ !partition_bound_accepts_nulls(right_bi))
+ {
+ if (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI)
+ {
+ if (left_mmap[left_ni] < 0)
+ left_mmap[left_ni] = next_index++;
+ null_index = left_mmap[left_ni];
+ }
+ else
+ null_index = -1;
+ }
+ else if (!partition_bound_accepts_nulls(left_bi) &&
+ partition_bound_accepts_nulls(right_bi))
+ {
+ if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ if (right_mmap[right_ni] < 0)
+ right_mmap[right_ni] = next_index++;
+ null_index = right_mmap[right_ni];
+ }
+ else
+ null_index = -1;
+ }
+ else
+ {
+ /* Both the relations have NULL partitions, try merging them. */
+ null_index = map_and_merge_partitions(left_pmap, left_mmap,
+ left_ni, right_pmap,
+ right_mmap, right_ni,
+ &next_index);
+ if (null_index < 0)
+ merged = false;
+ }
+
+ /* If successful build the output structures. */
+ if (merged)
+ {
+
+ /* Use maps to match partition from the joining relations. */
+ generate_matching_part_pairs(left_mmap, left_nparts, right_mmap,
+ right_nparts, jointype, next_index,
+ left_parts, right_parts);
+ /* Craft a PartitionBoundInfo to return. */
+ if (*left_parts && *right_parts)
+ {
+ Assert(list_length(*left_parts) == list_length(*right_parts));
+ Assert(list_length(*left_parts) == next_index);
+ merged_bounds = build_merged_partition_bounds(left_bi->strategy,
+ merged_datums,
+ merged_indexes, NIL,
+ null_index);
+ }
+ }
+
+merge_failed:
+ list_free(merged_datums);
+ list_free(merged_indexes);
+ pfree(left_pmap);
+ pfree(right_pmap);
+ pfree(left_mmap);
+ pfree(right_mmap);
+
+ return merged_bounds;
+}
+
+/*
+ * map_and_merge_partitions
+ *
+ * If the two given partitions (given by index1 and index2 resp.) are already
+ * mapped to each other return the index of corresponding partition in the
+ * merged set of partitions. If they do not have a merged partition associated
+ * with them, assign a new merged partition index. If the partitions are
+ * already mapped and their mapped partitions are different from each other,
+ * they can not be merged, so return -1.
+ *
+ * partmap1[i] gives the partition of relation 2 which matches ith partition of
+ * relation 1. Similarly for partmap2.
+ *
+ * mergemap1[i] gives the partition in the merged set to which ith partition of
+ * relation 1 maps to. Similarly for mergemap2.
+ *
+ * index1 and index2 are the indexes of matching partition from respective
+ * relations.
+ *
+ * *next_index is used and incremented when the given partitions require a new
+ * merged partition.
+ */
+static int
+map_and_merge_partitions(int *partmap1, int *mergemap1, int index1,
+ int *partmap2, int *mergemap2, int index2,
+ int *next_index)
+{
+ int merged_index;
+
+ /*
+ * If both the partitions are not mapped to each other, update the
+ * maps.
+ */
+ if (partmap1[index1] < 0 && partmap2[index2] < 0)
+ {
+ partmap1[index1] = index2;
+ partmap2[index2] = index1;
+ }
+
+ /*
+ * If the given to partitions map to each other, find the corresponding
+ * merged partition index .
+ */
+ if (partmap1[index1] == index2 && partmap2[index2] == index1)
+ {
+ /*
+ * If both the partitions are mapped to the same merged partition, get
+ * the index of merged partition.
+ */
+ if (mergemap1[index1] == mergemap2[index2])
+ {
+ merged_index = mergemap1[index1];
+
+ /*
+ * If the given two partitions do not have a merged partition
+ * associated with them, allocate a new merged partition.
+ */
+ if (merged_index < 0)
+ {
+ merged_index = *next_index;
+ *next_index = *next_index + 1;
+ mergemap1[index1] = merged_index;
+ mergemap2[index2] = merged_index;
+ }
+ }
+
+ /*
+ * If partition from one relation was mapped to a merged partition but
+ * not the partition from the other relation, map the same merged
+ * partition to the partition from other relation, since matching
+ * partitions map to the same merged partition.
+ */
+ else if (mergemap1[index1] >= 0 && mergemap2[index2] < 0)
+ {
+ mergemap2[index2] = mergemap1[index1];
+ merged_index = mergemap1[index1];
+ }
+ else if (mergemap1[index1] < 0 && mergemap2[index2] >= 0)
+ {
+ mergemap1[index1] = mergemap2[index2];
+ merged_index = mergemap2[index2];
+ }
+ else
+ {
+ Assert(mergemap1[index1] != mergemap2[index2] &&
+ mergemap1[index1] >= 0 && mergemap2[index2] >= 0);
+
+ /*
+ * Both the partitions map to different merged partitions. This
+ * means that multiple partitions from one relation matches to one
+ * partition from the other relation. Partition-wise join does not
+ * handle this case right now, since it requires ganging multiple
+ * partitions together (into one RelOptInfo).
+ */
+ merged_index = -1;
+ }
+ }
+ else
+ {
+ /*
+ * Multiple partitions from one relation map to one partition from the
+ * other relation. Partition-wise join does not handle this case right
+ * now, since it requires ganging multiple partitions together (into
+ * one RelOptInfo).
+ */
+ merged_index = -1;
+ }
+
+ return merged_index;
+}
+
+/*
+ * generate_matching_part_pairs
+ *
+ * Given the merged partition to which partition on either side of join map,
+ * produce the list pairs of partitions which when joined produce the merged
+ * partitions in the order of merged partition indexes.
+ *
+ * If successful, the pairs of partitions are returned as two separate lists
+ * one for each side. Otherwise, those lists will be set to NIL.
+ *
+ * TODO: rename the sides as outer and inner. You may not need to support
+ * JOIN_RIGHT, since we won't see that type here.
+ */
+static void
+generate_matching_part_pairs(int *mergemap1, int nparts1, int *mergemap2,
+ int nparts2, JoinType jointype, int nparts,
+ List **parts1, List **parts2)
+{
+ bool merged = true;
+ int **matching_parts;
+ int cnt1;
+ int cnt2;
+
+ matching_parts = (int **) palloc(sizeof(int *) * 2);
+ matching_parts[0] = (int *) palloc(sizeof(int) * nparts);
+ matching_parts[1] = (int *) palloc(sizeof(int) * nparts);
+
+ *parts1 = NIL;
+ *parts2 = NIL;
+
+ for (cnt1 = 0; cnt1 < nparts; cnt1++)
+ {
+ matching_parts[0][cnt1] = -1;
+ matching_parts[1][cnt1] = -1;
+ }
+
+ /* Set pairs of matching partitions. */
+ for (cnt1 = 0; cnt1 < nparts1; cnt1++)
+ {
+ if (mergemap1[cnt1] >= 0)
+ {
+ Assert(mergemap1[cnt1] < nparts);
+ matching_parts[0][mergemap1[cnt1]] = cnt1;
+ }
+ }
+ for (cnt2 = 0; cnt2 < nparts2; cnt2++)
+ {
+ if (mergemap2[cnt2] >= 0)
+ {
+ Assert(mergemap2[cnt2] < nparts);
+ matching_parts[1][mergemap2[cnt2]] = cnt2;
+ }
+ }
+
+ /*
+ * 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 (cnt1 = 0; cnt1 < nparts; cnt1++)
+ {
+ int part1 = matching_parts[0][cnt1];
+ int part2 = matching_parts[1][cnt1];
+
+ /* 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_RIGHT:
+ Assert(part2 >= 0);
+ if (part1 < 0)
+ merged = false;
+ break;
+
+ case JOIN_FULL:
+ if (part1 < 0 || part2 < 0)
+ merged = false;
+ break;
+
+ default:
+ /* We do not know what to do in this case. Bail out. */
+ merged = false;
+ }
+
+ if (!merged)
+ break;
+
+ *parts1 = lappend_int(*parts1, part1);
+ *parts2 = lappend_int(*parts2, part2);
+ }
+
+ pfree(matching_parts[0]);
+ pfree(matching_parts[1]);
+ pfree(matching_parts);
+
+ if (!merged)
+ {
+ list_free(*parts1);
+ list_free(*parts2);
+ *parts1 = NIL;
+ *parts2 = NIL;
+ }
+}
+
+static PartitionBoundInfo
+build_merged_partition_bounds(char strategy, List *merged_datums,
+ List *merged_indexes, List *merged_kinds,
+ int null_index)
+{
+ int cnt;
+ PartitionBoundInfo merged_bounds;
+ ListCell *lc;
+
+ /* We expect the same number of elements in datums and indexes lists. */
+ Assert(list_length(merged_datums) == list_length(merged_indexes));
+
+ merged_bounds = (PartitionBoundInfo) palloc(sizeof(PartitionBoundInfoData));
+ merged_bounds->strategy = strategy;
+ merged_bounds->ndatums = list_length(merged_datums);
+
+ if (strategy == PARTITION_STRATEGY_RANGE)
+ {
+ Assert(list_length(merged_datums) == list_length(merged_kinds));
+ merged_bounds->kind = (PartitionRangeDatumKind **) palloc(sizeof(PartitionRangeDatumKind *) *
+ list_length(merged_kinds));
+ cnt = 0;
+ foreach(lc, merged_kinds)
+ merged_bounds->kind[cnt++] = lfirst(lc);
+
+ /* There are ndatums+1 indexes in case of range partitions */
+ merged_indexes = lappend_int(merged_indexes, -1);
+ }
+ else
+ merged_bounds->kind = NULL;
+
+ cnt = 0;
+ merged_bounds->datums = (Datum **) palloc(sizeof(Datum *) *
+ list_length(merged_datums));
+ foreach(lc, merged_datums)
+ merged_bounds->datums[cnt++] = lfirst(lc);
+
+ merged_bounds->indexes = (int *) palloc(sizeof(int) *
+ list_length(merged_indexes));
+ cnt = 0;
+ foreach(lc, merged_indexes)
+ merged_bounds->indexes[cnt++] = lfirst_int(lc);
+
+ merged_bounds->null_index = null_index;
+
+ return merged_bounds;
+}
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 79468d2..d4e3047 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1308,8 +1308,13 @@ try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
RelOptInfo *joinrel, SpecialJoinInfo *parent_sjinfo,
List *parent_restrictlist)
{
- int nparts;
int cnt_parts;
+ PartitionScheme part_scheme;
+ PartitionBoundInfo join_boundinfo;
+ List *parts1;
+ List *parts2;
+ ListCell *lc1;
+ ListCell *lc2;
/* Guard against stack overflow due to overly deep partition hierarchy. */
check_stack_depth();
@@ -1359,39 +1364,54 @@ try_partition_wise_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;
/*
- * Since we allow partition-wise join only when the partition bounds of
- * the joining relations exactly match, the partition bounds of the join
- * should match those of the joining relations.
+ * Get the list of matching partitions from both sides of the join. While
+ * doing so, we also build the partition bounds of the join relation,
+ * which should match the bounds calculated for other pairs. TODO: why
+ * should every pair result in the same partition bounds?
*/
- Assert(partition_bounds_equal(joinrel->part_scheme->partnatts,
- joinrel->part_scheme->parttyplen,
- joinrel->part_scheme->parttypbyval,
- joinrel->boundinfo, rel1->boundinfo));
- Assert(partition_bounds_equal(joinrel->part_scheme->partnatts,
- joinrel->part_scheme->parttyplen,
- joinrel->part_scheme->parttypbyval,
- joinrel->boundinfo, rel2->boundinfo));
-
- nparts = joinrel->nparts;
-
+ join_boundinfo = partition_bounds_merge(part_scheme->partnatts,
+ part_scheme->partsupfunc,
+ part_scheme->parttypcoll,
+ rel1->boundinfo, rel1->nparts,
+ rel2->boundinfo, rel2->nparts,
+ parent_sjinfo->jointype,
+ &parts1, &parts2);
+
+ Assert(join_boundinfo);
+ Assert(partition_bounds_equal(part_scheme->partnatts,
+ part_scheme->parttyplen,
+ part_scheme->parttypbyval, join_boundinfo,
+ joinrel->boundinfo));
elog(DEBUG3, "join between relations %s and %s is considered for partition-wise join.",
bmsToString(rel1->relids), bmsToString(rel2->relids));
- /* Allocate space to hold child-joins RelOptInfos, if not already done. */
+ /*
+ * 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));
+
+ /* Allocate space for hold child-joins RelOptInfos, if not already done. */
if (!joinrel->part_rels)
- joinrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) * nparts);
+ joinrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *
+ joinrel->nparts);
/*
* Create child-join relations for this partitioned join, if those don't
* exist. Add paths to child-joins for a pair of child relations
* corresponding to the given pair of parent relations.
*/
- for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+ cnt_parts = 0;
+ forboth(lc1, parts1, lc2, parts2)
{
- RelOptInfo *child_rel1 = rel1->part_rels[cnt_parts];
- RelOptInfo *child_rel2 = rel2->part_rels[cnt_parts];
+ int part1 = lfirst_int(lc1);
+ int part2 = lfirst_int(lc2);
+ RelOptInfo *child_rel1;
+ RelOptInfo *child_rel2;
SpecialJoinInfo *child_sjinfo;
List *child_restrictlist;
RelOptInfo *child_joinrel;
@@ -1399,6 +1419,10 @@ try_partition_wise_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];
+
/* We should never try to join two overlapping sets of rels. */
Assert(!bms_overlap(child_rel1->relids, child_rel2->relids));
child_joinrelids = bms_union(child_rel1->relids, child_rel2->relids);
@@ -1431,6 +1455,15 @@ try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
joinrel->part_rels[cnt_parts] = child_joinrel;
}
+ /*
+ * 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,
@@ -1443,7 +1476,11 @@ try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
*/
try_partition_wise_join(root, child_rel1, child_rel2, child_joinrel,
child_sjinfo, child_restrictlist);
+
+ cnt_parts++;
}
+
+ Assert(cnt_parts == joinrel->nparts);
}
/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 81bc2b1..5d1992e 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -1613,6 +1613,9 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
int partnatts;
int cnt;
PartitionScheme part_scheme;
+ PartitionBoundInfo join_boundinfo;
+ List *parts1;
+ List *parts2;
/* Nothing to do if partition-wise join technique is disabled. */
if (!enable_partition_wise_join)
@@ -1653,17 +1656,26 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
REL_HAS_ALL_PART_PROPS(inner_rel));
/*
- * For now, our partition matching algorithm can match partitions only
- * when the partition bounds of the joining relations are exactly same.
- * So, bail out otherwise.
+ * Every pair of joining relations would yield the same partition bounds
+ * for a given join (TODO: why?) so we compute the bounds only the first
+ * time. Then for every pair we find the pairs of matching partitions from
+ * the joining relations and join those. TODO: Needs a better explanation
+ * of why is this true. TODO: Also there is no reason to have
+ * part_indexes1 and part_indexes2 pulled here just to be freed up later.
+ * So, we might want to do something better.
*/
- if (outer_rel->nparts != inner_rel->nparts ||
- !partition_bounds_equal(part_scheme->partnatts,
- part_scheme->parttyplen,
- part_scheme->parttypbyval,
- outer_rel->boundinfo, inner_rel->boundinfo))
+ join_boundinfo = partition_bounds_merge(part_scheme->partnatts,
+ part_scheme->partsupfunc,
+ part_scheme->parttypcoll,
+ outer_rel->boundinfo,
+ outer_rel->nparts,
+ inner_rel->boundinfo,
+ inner_rel->nparts,
+ jointype, &parts1, &parts2);
+ if (!join_boundinfo)
{
Assert(!IS_PARTITIONED_REL(joinrel));
+ Assert(!parts1 && !parts2);
return;
}
@@ -1676,13 +1688,16 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
!joinrel->nullable_partexprs && !joinrel->part_rels &&
!joinrel->boundinfo);
+ Assert(list_length(parts1) == list_length(parts2));
+
/*
* Join relation is partitioned using same partitioning scheme as the
- * joining relations and has same bounds.
+ * joining relations. It will have as many partitions as the pairs of
+ * matching partitions we found.
*/
joinrel->part_scheme = part_scheme;
- joinrel->boundinfo = outer_rel->boundinfo;
- joinrel->nparts = outer_rel->nparts;
+ joinrel->nparts = list_length(parts1);
+ joinrel->boundinfo = join_boundinfo;
partnatts = joinrel->part_scheme->partnatts;
/*
@@ -1803,4 +1818,9 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
joinrel->nullable_partexprs[cnt] = nullable_partexprs;
}
}
+
+ /* TODO: OR we could actually create the child-join relations here.*/
+ list_free(parts1);
+ list_free(parts2);
+
}
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index 2283c67..056a4f9 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -99,4 +99,10 @@ extern int get_partition_for_tuple(PartitionDispatch *pd,
EState *estate,
PartitionDispatchData **failed_at,
TupleTableSlot **failed_slot);
+extern PartitionBoundInfo partition_bounds_merge(int partnatts,
+ FmgrInfo *supfuncs, Oid *collations,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2);
+
#endif /* PARTITION_H */
--
1.7.9.5
On Sat, Sep 2, 2017 at 12:42 AM, Ashutosh Bapat <
ashutosh.bapat@enterprisedb.com> wrote:
PFA the patches rebased on the latest sources. There are also fixes
for some of the crashes and bugs reported. I haven't yet included the
testcase patch in the main patchset.On Mon, Aug 28, 2017 at 12:44 PM, Rajkumar Raghuwanshi
<rajkumar.raghuwanshi@enterprisedb.com> wrote:On Mon, Aug 21, 2017 at 12:43 PM, Ashutosh Bapat
<ashutosh.bapat@enterprisedb.com> wrote:TODOs
-----------
1. Add tests for advanced partition matching algorithmHi Ashutosh,
I have applied all partition-wise-join patches (v26) and tested feature.
I
have modified partition_join.sql file and added extra test cases to test
partition matching.Attaching WIP test case patch which as of now have some server crashes
and a
data corruptions issue which is commented in the file itself and need to
be
removed once issue got solved. Also some of queries is not picking or
picking partition-wise-join as per expectation which may need some
adjustment.
I have applied v27 patches and tested feature. Also tried to reduce
regression diff with the
existing partition_join.sql by adding new partition instead of changing
original partition bounds.
Attached WIP patch have a server crash and some wrong output which need to
be fixed. I have
commented these issue in patch itself, Please take a look and let me know
if it need more
changes.
Attachments:
advance_partition_matching_test_v1.patchtext/x-patch; charset=US-ASCII; name=advance_partition_matching_test_v1.patchDownload
diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out
index b92fbb9..0dd0b59 100644
--- a/src/test/regress/expected/partition_join.out
+++ b/src/test/regress/expected/partition_join.out
@@ -8,105 +8,159 @@ SET enable_partition_wise_join to true;
-- partitioned by a single column
--
CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_p0 PARTITION OF prt1 FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250);
CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600);
CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500);
+CREATE TABLE prt1_p4 PARTITION OF prt1 FOR VALUES FROM (600) TO (800);
INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(-250, 0, 2) i;
+INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(600, 799, 2) i;
+CREATE INDEX iprt1_p0_a on prt1_p0(a);
CREATE INDEX iprt1_p1_a on prt1_p1(a);
CREATE INDEX iprt1_p2_a on prt1_p2(a);
CREATE INDEX iprt1_p3_a on prt1_p3(a);
+CREATE INDEX iprt1_p4_a on prt1_p4(a);
ANALYZE prt1;
+-- prt2 have missing starting MINVALUE to -250 range and
+-- extra bounds from 800 to MAXVALUE
CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_p0 PARTITION OF prt2 FOR VALUES FROM (-250) TO (0);
CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250);
CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500);
CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (MAXVALUE);
INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(-250, 0, 3) i;
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 799, 3) i;
+CREATE INDEX iprt2_p0_b on prt2_p0(b);
CREATE INDEX iprt2_p1_b on prt2_p1(b);
CREATE INDEX iprt2_p2_b on prt2_p2(b);
CREATE INDEX iprt2_p3_b on prt2_p3(b);
+CREATE INDEX iprt2_p4_b on prt2_p4(b);
ANALYZE prt2;
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
-- inner join
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
- QUERY PLAN
---------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------
Sort
Sort Key: t1.a
-> Append
-> Hash Join
Hash Cond: (t2.b = t1.a)
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
-> Hash
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Hash Join
Hash Cond: (t2_1.b = t1_1.a)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Hash
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Hash Join
Hash Cond: (t2_2.b = t1_2.a)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p2 t2_2
-> Hash
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
-(21 rows)
+ -> Nested Loop
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ -> Hash Join
+ Hash Cond: (t2_4.b = t1_4.a)
+ -> Seq Scan on prt2_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(32 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
- a | c | b | c
------+------+-----+------
- 0 | 0000 | 0 | 0000
- 150 | 0150 | 150 | 0150
- 300 | 0300 | 300 | 0300
- 450 | 0450 | 450 | 0450
-(4 rows)
+ a | c | b | c
+------+-------+------+-------
+ -250 | -0250 | -250 | -0250
+ -100 | -0100 | -100 | -0100
+ 0 | 0000 | 0 | 0000
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150
+ 300 | 0300 | 300 | 0300
+ 450 | 0450 | 450 | 0450
+ 600 | 0600 | 600 | 0600
+ 750 | 0750 | 750 | 0750
+(9 rows)
-- left outer join, with whole-row reference
EXPLAIN (COSTS OFF)
SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
- QUERY PLAN
---------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------
Sort
Sort Key: t1.a, t2.b
-> Result
-> Append
-> Hash Right Join
Hash Cond: (t2.b = t1.a)
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
-> Hash
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Hash Right Join
Hash Cond: (t2_1.b = t1_1.a)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Hash
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Hash Right Join
Hash Cond: (t2_2.b = t1_2.a)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p2 t2_2
-> Hash
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
-(22 rows)
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (t1_3.a = b)
+ -> Hash Right Join
+ Hash Cond: (t2_4.b = t1_4.a)
+ -> Seq Scan on prt2_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(33 rows)
SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
- t1 | t2
---------------+--------------
- (0,0,0000) | (0,0,0000)
- (50,0,0050) |
- (100,0,0100) |
- (150,0,0150) | (0,150,0150)
- (200,0,0200) |
- (250,0,0250) |
- (300,0,0300) | (0,300,0300)
- (350,0,0350) |
- (400,0,0400) |
- (450,0,0450) | (0,450,0450)
- (500,0,0500) |
- (550,0,0550) |
-(12 rows)
+ t1 | t2
+----------------+----------------
+ (-250,0,-0250) | (0,-250,-0250)
+ (-200,0,-0200) |
+ (-150,0,-0150) |
+ (-100,0,-0100) | (0,-100,-0100)
+ (-50,0,-0050) |
+ (0,0,0000) | (0,0,0000)
+ (0,0,0000) | (0,0,0000)
+ (50,0,0050) |
+ (100,0,0100) |
+ (150,0,0150) | (0,150,0150)
+ (200,0,0200) |
+ (250,0,0250) |
+ (300,0,0300) | (0,300,0300)
+ (350,0,0350) |
+ (400,0,0400) |
+ (450,0,0450) | (0,450,0450)
+ (500,0,0500) |
+ (550,0,0550) |
+ (600,0,0600) | (0,600,0600)
+ (650,0,0650) |
+ (700,0,0700) |
+ (750,0,0750) | (0,750,0750)
+(22 rows)
-- right outer join
EXPLAIN (COSTS OFF)
@@ -119,35 +173,55 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHE
-> Append
-> Hash Right Join
Hash Cond: (t1.a = t2.b)
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
-> Hash
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
Filter: (a = 0)
-> Hash Right Join
Hash Cond: (t1_1.a = t2_1.b)
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
-> Hash
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
+ Filter: (a = 0)
+ -> Hash Right Join
+ Hash Cond: (t1_2.a = t2_2.b)
+ -> Seq Scan on prt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on prt2_p2 t2_2
Filter: (a = 0)
-> Nested Loop Left Join
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p3 t2_3
Filter: (a = 0)
- -> Index Scan using iprt1_p3_a on prt1_p3 t1_2
- Index Cond: (a = t2_2.b)
-(21 rows)
+ -> Index Scan using iprt1_p3_a on prt1_p3 t1_3
+ Index Cond: (a = t2_3.b)
+ -> Hash Right Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+(33 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b;
- a | c | b | c
------+------+-----+------
- 0 | 0000 | 0 | 0000
- 150 | 0150 | 150 | 0150
- 300 | 0300 | 300 | 0300
- 450 | 0450 | 450 | 0450
- | | 75 | 0075
- | | 225 | 0225
- | | 375 | 0375
- | | 525 | 0525
-(8 rows)
+ a | c | b | c
+------+-------+------+-------
+ -250 | -0250 | -250 | -0250
+ -100 | -0100 | -100 | -0100
+ 0 | 0000 | 0 | 0000
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150
+ 300 | 0300 | 300 | 0300
+ 450 | 0450 | 450 | 0450
+ 600 | 0600 | 600 | 0600
+ 750 | 0750 | 750 | 0750
+ | | -175 | -0175
+ | | -25 | -0025
+ | | 75 | 0075
+ | | 225 | 0225
+ | | 375 | 0375
+ | | 525 | 0525
+ | | 675 | 0675
+(16 rows)
-- full outer join
EXPLAIN (COSTS OFF)
@@ -155,9 +229,16 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.b = 0) t1 FULL
QUERY PLAN
--------------------------------------------------
Sort
- Sort Key: prt1_p1.a, prt2_p1.b
+ Sort Key: prt1_p0.a, prt2_p0.b
-> Append
-> Hash Full Join
+ Hash Cond: (prt1_p0.a = prt2_p0.b)
+ -> Seq Scan on prt1_p0
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p0
+ Filter: (a = 0)
+ -> Hash Full Join
Hash Cond: (prt1_p1.a = prt2_p1.b)
-> Seq Scan on prt1_p1
Filter: (b = 0)
@@ -178,28 +259,48 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.b = 0) t1 FULL
-> Hash
-> Seq Scan on prt2_p3
Filter: (a = 0)
-(24 rows)
+ -> Hash Full Join
+ Hash Cond: (prt1_p4.a = prt2_p4.b)
+ -> Seq Scan on prt1_p4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4
+ Filter: (a = 0)
+(38 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
- a | c | b | c
------+------+-----+------
- 0 | 0000 | 0 | 0000
- 50 | 0050 | |
- 100 | 0100 | |
- 150 | 0150 | 150 | 0150
- 200 | 0200 | |
- 250 | 0250 | |
- 300 | 0300 | 300 | 0300
- 350 | 0350 | |
- 400 | 0400 | |
- 450 | 0450 | 450 | 0450
- 500 | 0500 | |
- 550 | 0550 | |
- | | 75 | 0075
- | | 225 | 0225
- | | 375 | 0375
- | | 525 | 0525
-(16 rows)
+ a | c | b | c
+------+-------+------+-------
+ -250 | -0250 | -250 | -0250
+ -200 | -0200 | |
+ -150 | -0150 | |
+ -100 | -0100 | -100 | -0100
+ -50 | -0050 | |
+ 0 | 0000 | 0 | 0000
+ 0 | 0000 | 0 | 0000
+ 50 | 0050 | |
+ 100 | 0100 | |
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 | |
+ 250 | 0250 | |
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 | |
+ 400 | 0400 | |
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 | |
+ 550 | 0550 | |
+ 600 | 0600 | 600 | 0600
+ 650 | 0650 | |
+ 700 | 0700 | |
+ 750 | 0750 | 750 | 0750
+ | | -175 | -0175
+ | | -25 | -0025
+ | | 75 | 0075
+ | | 225 | 0225
+ | | 375 | 0375
+ | | 525 | 0525
+ | | 675 | 0675
+(29 rows)
-- Cases with non-nullable expressions in subquery results;
-- make sure these go to null as expected
@@ -208,9 +309,17 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0)
QUERY PLAN
------------------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, prt2_p1.b
+ Sort Key: prt1_p0.a, prt2_p0.b
-> Append
-> Hash Full Join
+ Hash Cond: (prt1_p0.a = prt2_p0.b)
+ Filter: (((50) = prt1_p0.a) OR ((75) = prt2_p0.b))
+ -> Seq Scan on prt1_p0
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p0
+ Filter: (a = 0)
+ -> Hash Full Join
Hash Cond: (prt1_p1.a = prt2_p1.b)
Filter: (((50) = prt1_p1.a) OR ((75) = prt2_p1.b))
-> Seq Scan on prt1_p1
@@ -234,7 +343,15 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0)
-> Hash
-> Seq Scan on prt2_p3
Filter: (a = 0)
-(27 rows)
+ -> Hash Full Join
+ Hash Cond: (prt1_p4.a = prt2_p4.b)
+ Filter: (((50) = prt1_p4.a) OR ((75) = prt2_p4.b))
+ -> Seq Scan on prt1_p4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4
+ Filter: (a = 0)
+(43 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b;
a | c | b | c
@@ -248,10 +365,17 @@ SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 W
QUERY PLAN
--------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, prt2_p1.b
+ Sort Key: prt1_p0.a, prt2_p0.b
-> Result
-> Append
-> Hash Full Join
+ Hash Cond: (prt1_p0.a = prt2_p0.b)
+ -> Seq Scan on prt1_p0
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p0
+ Filter: (a = 0)
+ -> Hash Full Join
Hash Cond: (prt1_p1.a = prt2_p1.b)
-> Seq Scan on prt1_p1
Filter: (b = 0)
@@ -272,28 +396,48 @@ SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 W
-> Hash
-> Seq Scan on prt2_p3
Filter: (a = 0)
-(25 rows)
+ -> Hash Full Join
+ Hash Cond: (prt1_p4.a = prt2_p4.b)
+ -> Seq Scan on prt1_p4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4
+ Filter: (a = 0)
+(39 rows)
SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
- a | c | phv | b | c | phv
------+------+-----+-----+------+-----
- 0 | 0000 | 25 | 0 | 0000 | 50
- 50 | 0050 | 25 | | |
- 100 | 0100 | 25 | | |
- 150 | 0150 | 25 | 150 | 0150 | 50
- 200 | 0200 | 25 | | |
- 250 | 0250 | 25 | | |
- 300 | 0300 | 25 | 300 | 0300 | 50
- 350 | 0350 | 25 | | |
- 400 | 0400 | 25 | | |
- 450 | 0450 | 25 | 450 | 0450 | 50
- 500 | 0500 | 25 | | |
- 550 | 0550 | 25 | | |
- | | | 75 | 0075 | 50
- | | | 225 | 0225 | 50
- | | | 375 | 0375 | 50
- | | | 525 | 0525 | 50
-(16 rows)
+ a | c | phv | b | c | phv
+------+-------+-----+------+-------+-----
+ -250 | -0250 | 25 | -250 | -0250 | 50
+ -200 | -0200 | 25 | | |
+ -150 | -0150 | 25 | | |
+ -100 | -0100 | 25 | -100 | -0100 | 50
+ -50 | -0050 | 25 | | |
+ 0 | 0000 | 25 | 0 | 0000 | 50
+ 0 | 0000 | 25 | 0 | 0000 | 50
+ 50 | 0050 | 25 | | |
+ 100 | 0100 | 25 | | |
+ 150 | 0150 | 25 | 150 | 0150 | 50
+ 200 | 0200 | 25 | | |
+ 250 | 0250 | 25 | | |
+ 300 | 0300 | 25 | 300 | 0300 | 50
+ 350 | 0350 | 25 | | |
+ 400 | 0400 | 25 | | |
+ 450 | 0450 | 25 | 450 | 0450 | 50
+ 500 | 0500 | 25 | | |
+ 550 | 0550 | 25 | | |
+ 600 | 0600 | 25 | 600 | 0600 | 50
+ 650 | 0650 | 25 | | |
+ 700 | 0700 | 25 | | |
+ 750 | 0750 | 25 | 750 | 0750 | 50
+ | | | -175 | -0175 | 50
+ | | | -25 | -0025 | 50
+ | | | 75 | 0075 | 50
+ | | | 225 | 0225 | 50
+ | | | 375 | 0375 | 50
+ | | | 525 | 0525 | 50
+ | | | 675 | 0675 | 50
+(29 rows)
-- Join with pruned partitions from joining relations
EXPLAIN (COSTS OFF)
@@ -323,9 +467,16 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JO
QUERY PLAN
-----------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, b
+ Sort Key: prt1_p0.a, b
-> Append
-> Hash Left Join
+ Hash Cond: (prt1_p0.a = b)
+ -> Seq Scan on prt1_p0
+ Filter: ((a < 450) AND (b = 0))
+ -> Hash
+ -> Result
+ One-Time Filter: false
+ -> Hash Left Join
Hash Cond: (prt1_p1.a = b)
-> Seq Scan on prt1_p1
Filter: ((a < 450) AND (b = 0))
@@ -339,30 +490,44 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JO
-> Hash
-> Seq Scan on prt1_p2
Filter: ((a < 450) AND (b = 0))
-(17 rows)
+(24 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
- a | c | b | c
------+------+-----+------
- 0 | 0000 | |
- 50 | 0050 | |
- 100 | 0100 | |
- 150 | 0150 | |
- 200 | 0200 | |
- 250 | 0250 | |
- 300 | 0300 | 300 | 0300
- 350 | 0350 | |
- 400 | 0400 | |
-(9 rows)
+ a | c | b | c
+------+-------+-----+------
+ -250 | -0250 | |
+ -200 | -0200 | |
+ -150 | -0150 | |
+ -100 | -0100 | |
+ -50 | -0050 | |
+ 0 | 0000 | |
+ 0 | 0000 | |
+ 50 | 0050 | |
+ 100 | 0100 | |
+ 150 | 0150 | |
+ 200 | 0200 | |
+ 250 | 0250 | |
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 | |
+ 400 | 0400 | |
+(15 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b;
QUERY PLAN
------------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, b
+ Sort Key: prt1_p0.a, b
-> Append
-> Hash Full Join
+ Hash Cond: (prt1_p0.a = b)
+ Filter: ((prt1_p0.b = 0) OR (a = 0))
+ -> Seq Scan on prt1_p0
+ Filter: (a < 450)
+ -> Hash
+ -> Result
+ One-Time Filter: false
+ -> Hash Full Join
Hash Cond: (prt1_p1.a = b)
Filter: ((prt1_p1.b = 0) OR (a = 0))
-> Seq Scan on prt1_p1
@@ -386,64 +551,157 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JO
-> Hash
-> Result
One-Time Filter: false
-(27 rows)
+ -> Hash Full Join
+ Hash Cond: (prt2_p4.b = a)
+ Filter: ((b = 0) OR (prt2_p4.a = 0))
+ -> Seq Scan on prt2_p4
+ Filter: (b > 250)
+ -> Hash
+ -> Result
+ One-Time Filter: false
+(43 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b;
- a | c | b | c
------+------+-----+------
- 0 | 0000 | |
- 50 | 0050 | |
- 100 | 0100 | |
- 150 | 0150 | |
- 200 | 0200 | |
- 250 | 0250 | |
- 300 | 0300 | 300 | 0300
- 350 | 0350 | |
- 400 | 0400 | |
- | | 375 | 0375
- | | 450 | 0450
- | | 525 | 0525
-(12 rows)
+ a | c | b | c
+------+-------+-----+------
+ -250 | -0250 | |
+ -200 | -0200 | |
+ -150 | -0150 | |
+ -100 | -0100 | |
+ -50 | -0050 | |
+ 0 | 0000 | |
+ 0 | 0000 | |
+ 50 | 0050 | |
+ 100 | 0100 | |
+ 150 | 0150 | |
+ 200 | 0200 | |
+ 250 | 0250 | |
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 | |
+ 400 | 0400 | |
+ | | 375 | 0375
+ | | 450 | 0450
+ | | 525 | 0525
+ | | 600 | 0600
+ | | 675 | 0675
+ | | 750 | 0750
+(21 rows)
-- Semi-join
EXPLAIN (COSTS OFF)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
- QUERY PLAN
---------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------
Sort
Sort Key: t1.a
-> Append
-> Hash Semi Join
Hash Cond: (t1.a = t2.b)
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Hash
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
Filter: (a = 0)
-> Hash Semi Join
Hash Cond: (t1_1.a = t2_1.b)
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Hash
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
Filter: (a = 0)
- -> Nested Loop Semi Join
- Join Filter: (t1_2.a = t2_2.b)
- -> Seq Scan on prt1_p3 t1_2
+ -> Hash Semi Join
+ Hash Cond: (t1_2.a = t2_2.b)
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
- -> Materialize
- -> Seq Scan on prt2_p3 t2_2
+ -> Hash
+ -> Seq Scan on prt2_p2 t2_2
Filter: (a = 0)
-(24 rows)
+ -> Nested Loop
+ -> HashAggregate
+ Group Key: t2_3.b
+ -> Seq Scan on prt2_p3 t2_3
+ Filter: (a = 0)
+ -> Index Scan using iprt1_p3_a on prt1_p3 t1_3
+ Index Cond: (a = t2_3.b)
+ Filter: (b = 0)
+ -> Hash Semi Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+(39 rows)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
- a | b | c
------+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
+ a | b | c
+------+---+-------
+ -250 | 0 | -0250
+ -100 | 0 | -0100
+ 0 | 0 | 0000
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 300 | 0 | 0300
+ 450 | 0 | 0450
+ 600 | 0 | 0600
+ 750 | 0 | 0750
+(9 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.b
+ -> Append
+ -> Hash Semi Join
+ Hash Cond: (t1.b = t2.a)
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p0 t2
+ Filter: (b = 0)
+ -> Hash Semi Join
+ Hash Cond: (t1_1.b = t2_1.a)
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p1 t2_1
+ Filter: (b = 0)
+ -> Hash Semi Join
+ Hash Cond: (t1_2.b = t2_2.a)
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p2 t2_2
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: (t1_3.b = t2_3.a)
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Seq Scan on prt1_p3 t2_3
+ Filter: (b = 0)
+ -> Hash Semi Join
+ Hash Cond: (t1_4.b = t2_4.a)
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p4 t2_4
+ Filter: (b = 0)
+(37 rows)
+
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+ a | b | c
+---+------+-------
+ 0 | -250 | -0250
+ 0 | -100 | -0100
+ 0 | 0 | 0000
+ 0 | 150 | 0150
+ 0 | 300 | 0300
+ 0 | 450 | 0450
+ 0 | 600 | 0600
+ 0 | 750 | 0750
+(8 rows)
-- Anti-join with aggregates
EXPLAIN (COSTS OFF)
@@ -454,25 +712,74 @@ SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS
-> Append
-> Hash Anti Join
Hash Cond: (t1.a = t2.b)
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
-> Hash
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
-> Hash Anti Join
Hash Cond: (t1_1.a = t2_1.b)
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
-> Hash
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Hash Anti Join
Hash Cond: (t1_2.a = t2_2.b)
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
-> Hash
- -> Seq Scan on prt2_p3 t2_2
-(17 rows)
+ -> Seq Scan on prt2_p2 t2_2
+ -> Hash Anti Join
+ Hash Cond: (t1_3.a = t2_3.b)
+ -> Seq Scan on prt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on prt2_p3 t2_3
+ -> Hash Anti Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+(27 rows)
SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b);
- sum | avg | sum | avg
--------+----------------------+------+---------------------
- 60000 | 300.0000000000000000 | 2400 | 12.0000000000000000
+ sum | avg | sum | avg
+-------+----------------------+------+--------------------
+ 95718 | 274.2636103151862464 | 2168 | 6.2120343839541547
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a);
+ QUERY PLAN
+--------------------------------------------------
+ Aggregate
+ -> Append
+ -> Hash Anti Join
+ Hash Cond: (t1.b = t2.a)
+ -> Seq Scan on prt2_p0 t1
+ -> Hash
+ -> Seq Scan on prt1_p0 t2
+ -> Hash Anti Join
+ Hash Cond: (t1_1.b = t2_1.a)
+ -> Seq Scan on prt2_p1 t1_1
+ -> Hash
+ -> Seq Scan on prt1_p1 t2_1
+ -> Hash Anti Join
+ Hash Cond: (t1_2.b = t2_2.a)
+ -> Seq Scan on prt2_p2 t1_2
+ -> Hash
+ -> Seq Scan on prt1_p2 t2_2
+ -> Hash Anti Join
+ Hash Cond: (t1_3.b = t2_3.a)
+ -> Seq Scan on prt2_p3 t1_3
+ -> Hash
+ -> Seq Scan on prt1_p3 t2_3
+ -> Hash Anti Join
+ Hash Cond: (t1_4.b = t2_4.a)
+ -> Seq Scan on prt2_p4 t1_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t2_4
+(27 rows)
+
+SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a);
+ sum | avg | sum | avg
+------+--------------------+-------+----------------------
+ 1084 | 6.1942857142857143 | 47859 | 273.4800000000000000
(1 row)
-- lateral reference
@@ -487,49 +794,77 @@ SELECT * FROM prt1 t1 LEFT JOIN LATERAL
-> Result
-> Append
-> Nested Loop Left Join
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Nested Loop
- -> Index Only Scan using iprt1_p1_a on prt1_p1 t2
+ -> Index Only Scan using iprt1_p0_a on prt1_p0 t2
Index Cond: (a = t1.a)
- -> Index Scan using iprt2_p1_b on prt2_p1 t3
+ -> Index Scan using iprt2_p0_b on prt2_p0 t3
Index Cond: (b = t2.a)
-> Nested Loop Left Join
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Nested Loop
- -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_1
+ -> Index Only Scan using iprt1_p1_a on prt1_p1 t2_1
Index Cond: (a = t1_1.a)
- -> Index Scan using iprt2_p2_b on prt2_p2 t3_1
+ -> Index Scan using iprt2_p1_b on prt2_p1 t3_1
Index Cond: (b = t2_1.a)
-> Nested Loop Left Join
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
-> Nested Loop
- -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_2
+ -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2
Index Cond: (a = t1_2.a)
- -> Index Scan using iprt2_p3_b on prt2_p3 t3_2
+ -> Index Scan using iprt2_p2_b on prt2_p2 t3_2
Index Cond: (b = t2_2.a)
-(28 rows)
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Nested Loop
+ -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3
+ Index Cond: (a = t1_3.a)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t3_3
+ Index Cond: (b = t2_3.a)
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Nested Loop
+ -> Index Only Scan using iprt1_p4_a on prt1_p4 t2_4
+ Index Cond: (a = t1_4.a)
+ -> Index Scan using iprt2_p4_b on prt2_p4 t3_4
+ Index Cond: (b = t2_4.a)
+(44 rows)
SELECT * FROM prt1 t1 LEFT JOIN LATERAL
(SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
ON t1.a = ss.t2a WHERE t1.b = 0 ORDER BY t1.a;
- a | b | c | t2a | t3a | least
------+---+------+-----+-----+-------
- 0 | 0 | 0000 | 0 | 0 | 0
- 50 | 0 | 0050 | | |
- 100 | 0 | 0100 | | |
- 150 | 0 | 0150 | 150 | 0 | 150
- 200 | 0 | 0200 | | |
- 250 | 0 | 0250 | | |
- 300 | 0 | 0300 | 300 | 0 | 300
- 350 | 0 | 0350 | | |
- 400 | 0 | 0400 | | |
- 450 | 0 | 0450 | 450 | 0 | 450
- 500 | 0 | 0500 | | |
- 550 | 0 | 0550 | | |
-(12 rows)
+ a | b | c | t2a | t3a | least
+------+---+-------+------+-----+-------
+ -250 | 0 | -0250 | -250 | 0 | -250
+ -200 | 0 | -0200 | | |
+ -150 | 0 | -0150 | | |
+ -100 | 0 | -0100 | -100 | 0 | -100
+ -50 | 0 | -0050 | | |
+ 0 | 0 | 0000 | 0 | 0 | 0
+ 0 | 0 | 0000 | 0 | 0 | 0
+ 0 | 0 | 0000 | 0 | 0 | 0
+ 0 | 0 | 0000 | 0 | 0 | 0
+ 50 | 0 | 0050 | | |
+ 100 | 0 | 0100 | | |
+ 150 | 0 | 0150 | 150 | 0 | 150
+ 200 | 0 | 0200 | | |
+ 250 | 0 | 0250 | | |
+ 300 | 0 | 0300 | 300 | 0 | 300
+ 350 | 0 | 0350 | | |
+ 400 | 0 | 0400 | | |
+ 450 | 0 | 0450 | 450 | 0 | 450
+ 500 | 0 | 0500 | | |
+ 550 | 0 | 0550 | | |
+ 600 | 0 | 0600 | 600 | 0 | 600
+ 650 | 0 | 0650 | | |
+ 700 | 0 | 0700 | | |
+ 750 | 0 | 0750 | 750 | 0 | 750
+(24 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
@@ -543,64 +878,98 @@ SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
Hash Cond: ((t1.c)::text = (t2.c)::text)
Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
-> Append
- -> Seq Scan on prt1_p1 t1
- -> Seq Scan on prt1_p2 t1_1
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
-> Hash
-> Append
-> Hash Join
Hash Cond: (t2.a = t3.b)
- -> Seq Scan on prt1_p1 t2
+ -> Seq Scan on prt1_p0 t2
-> Hash
- -> Seq Scan on prt2_p1 t3
+ -> Seq Scan on prt2_p0 t3
-> Hash Join
Hash Cond: (t2_1.a = t3_1.b)
- -> Seq Scan on prt1_p2 t2_1
+ -> Seq Scan on prt1_p1 t2_1
-> Hash
- -> Seq Scan on prt2_p2 t3_1
+ -> Seq Scan on prt2_p1 t3_1
-> Hash Join
Hash Cond: (t2_2.a = t3_2.b)
- -> Seq Scan on prt1_p3 t2_2
+ -> Seq Scan on prt1_p2 t2_2
-> Hash
- -> Seq Scan on prt2_p3 t3_2
-(26 rows)
+ -> Seq Scan on prt2_p2 t3_2
+ -> Hash Join
+ Hash Cond: (t2_3.a = t3_3.b)
+ -> Seq Scan on prt1_p3 t2_3
+ -> Hash
+ -> Seq Scan on prt2_p3 t3_3
+ -> Hash Join
+ Hash Cond: (t2_4.a = t3_4.b)
+ -> Seq Scan on prt1_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt2_p4 t3_4
+(38 rows)
SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
(SELECT t2.a AS t2a, t3.a AS t3a, t2.b t2b, t2.c t2c, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
ON t1.c = ss.t2c WHERE (t1.b + coalesce(ss.t2b, 0)) = 0 ORDER BY t1.a;
- a | t2a | t2c
------+-----+------
- 0 | 0 | 0000
- 50 | |
- 100 | |
- 150 | 150 | 0150
- 200 | |
- 250 | |
- 300 | 300 | 0300
- 350 | |
- 400 | |
- 450 | 450 | 0450
- 500 | |
- 550 | |
-(12 rows)
+ a | t2a | t2c
+------+------+-------
+ -250 | -250 | -0250
+ -200 | |
+ -150 | |
+ -100 | -100 | -0100
+ -50 | |
+ 0 | 0 | 0000
+ 0 | 0 | 0000
+ 0 | 0 | 0000
+ 0 | 0 | 0000
+ 50 | |
+ 100 | |
+ 150 | 150 | 0150
+ 200 | |
+ 250 | |
+ 300 | 300 | 0300
+ 350 | |
+ 400 | |
+ 450 | 450 | 0450
+ 500 | |
+ 550 | |
+ 600 | 600 | 0600
+ 650 | |
+ 700 | |
+ 750 | 750 | 0750
+(24 rows)
--
-- partitioned by expression
--
CREATE TABLE prt1_e (a int, b int, c int) PARTITION BY RANGE(((a + b)/2));
+CREATE TABLE prt1_e_p0 PARTITION OF prt1_e FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES FROM (0) TO (250);
CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES FROM (250) TO (500);
CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt1_e_p4 PARTITION OF prt1_e FOR VALUES FROM (600) TO (MAXVALUE);
INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(600, 799, 2) i;
+CREATE INDEX iprt1_e_p0_ab2 on prt1_e_p1(((a+b)/2));
CREATE INDEX iprt1_e_p1_ab2 on prt1_e_p1(((a+b)/2));
CREATE INDEX iprt1_e_p2_ab2 on prt1_e_p2(((a+b)/2));
CREATE INDEX iprt1_e_p3_ab2 on prt1_e_p3(((a+b)/2));
+CREATE INDEX iprt1_e_p4_ab2 on prt1_e_p1(((a+b)/2));
ANALYZE prt1_e;
CREATE TABLE prt2_e (a int, b int, c int) PARTITION BY RANGE(((b + a)/2));
+CREATE TABLE prt2_e_p0 PARTITION OF prt2_e FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES FROM (0) TO (250);
CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES FROM (250) TO (500);
CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt2_e_p4 PARTITION OF prt2_e FOR VALUES FROM (600) TO (MAXVALUE);
INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(600, 799, 3) i;
ANALYZE prt2_e;
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b;
@@ -611,32 +980,49 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 =
-> Append
-> Hash Join
Hash Cond: (((t2.b + t2.a) / 2) = ((t1.a + t1.b) / 2))
- -> Seq Scan on prt2_e_p1 t2
+ -> Seq Scan on prt2_e_p0 t2
-> Hash
- -> Seq Scan on prt1_e_p1 t1
+ -> Seq Scan on prt1_e_p0 t1
Filter: (c = 0)
-> Hash Join
- Hash Cond: (((t2_1.b + t2_1.a) / 2) = ((t1_1.a + t1_1.b) / 2))
- -> Seq Scan on prt2_e_p2 t2_1
+ Hash Cond: (((t1_1.a + t1_1.b) / 2) = ((t2_1.b + t2_1.a) / 2))
+ -> Seq Scan on prt1_e_p1 t1_1
+ Filter: (c = 0)
-> Hash
- -> Seq Scan on prt1_e_p2 t1_1
- Filter: (c = 0)
+ -> Seq Scan on prt2_e_p1 t2_1
-> Hash Join
Hash Cond: (((t2_2.b + t2_2.a) / 2) = ((t1_2.a + t1_2.b) / 2))
- -> Seq Scan on prt2_e_p3 t2_2
+ -> Seq Scan on prt2_e_p2 t2_2
-> Hash
- -> Seq Scan on prt1_e_p3 t1_2
+ -> Seq Scan on prt1_e_p2 t1_2
Filter: (c = 0)
-(21 rows)
+ -> Hash Join
+ Hash Cond: (((t2_3.b + t2_3.a) / 2) = ((t1_3.a + t1_3.b) / 2))
+ -> Seq Scan on prt2_e_p3 t2_3
+ -> Hash
+ -> Seq Scan on prt1_e_p3 t1_3
+ Filter: (c = 0)
+ -> Hash Join
+ Hash Cond: (((t2_4.b + t2_4.a) / 2) = ((t1_4.a + t1_4.b) / 2))
+ -> Seq Scan on prt2_e_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt1_e_p4 t1_4
+ Filter: (c = 0)
+(33 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b;
- a | c | b | c
------+---+-----+---
- 0 | 0 | 0 | 0
- 150 | 0 | 150 | 0
- 300 | 0 | 300 | 0
- 450 | 0 | 450 | 0
-(4 rows)
+ a | c | b | c
+------+---+------+---
+ -250 | 0 | -250 | 0
+ -100 | 0 | -100 | 0
+ 0 | 0 | 0 | 0
+ 0 | 0 | 0 | 0
+ 150 | 0 | 150 | 0
+ 300 | 0 | 300 | 0
+ 450 | 0 | 450 | 0
+ 600 | 0 | 600 | 0
+ 750 | 0 | 750 | 0
+(9 rows)
--
-- N-way join
@@ -650,107 +1036,163 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t
-> Result
-> Append
-> Nested Loop
- Join Filter: (t1.a = ((t3.a + t3.b) / 2))
+ Join Filter: (t1.a = t2.b)
-> Hash Join
- Hash Cond: (t2.b = t1.a)
- -> Seq Scan on prt2_p1 t2
+ Hash Cond: (((t3.a + t3.b) / 2) = t1.a)
+ -> Seq Scan on prt1_e_p0 t3
-> Hash
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
- -> Index Scan using iprt1_e_p1_ab2 on prt1_e_p1 t3
- Index Cond: (((a + b) / 2) = t2.b)
+ -> Index Scan using iprt2_p0_b on prt2_p0 t2
+ Index Cond: (b = ((t3.a + t3.b) / 2))
-> Nested Loop
Join Filter: (t1_1.a = ((t3_1.a + t3_1.b) / 2))
-> Hash Join
Hash Cond: (t2_1.b = t1_1.a)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Hash
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
- -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t3_1
+ -> Index Scan using iprt1_e_p4_ab2 on prt1_e_p1 t3_1
Index Cond: (((a + b) / 2) = t2_1.b)
-> Nested Loop
Join Filter: (t1_2.a = ((t3_2.a + t3_2.b) / 2))
-> Hash Join
Hash Cond: (t2_2.b = t1_2.a)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p2 t2_2
-> Hash
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
- -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_2
+ -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t3_2
Index Cond: (((a + b) / 2) = t2_2.b)
-(34 rows)
+ -> Nested Loop
+ Join Filter: (t1_3.a = ((t3_3.a + t3_3.b) / 2))
+ -> Nested Loop
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_3
+ Index Cond: (((a + b) / 2) = t2_3.b)
+ -> Nested Loop
+ Join Filter: (t1_4.a = t2_4.b)
+ -> Hash Join
+ Hash Cond: (((t3_4.a + t3_4.b) / 2) = t1_4.a)
+ -> Seq Scan on prt1_e_p4 t3_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p4_b on prt2_p4 t2_4
+ Index Cond: (b = ((t3_4.a + t3_4.b) / 2))
+(53 rows)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.b = 0 ORDER BY t1.a, t2.b;
- a | c | b | c | ?column? | c
------+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
-(4 rows)
+ a | c | b | c | ?column? | c
+------+-------+------+-------+----------+---
+ -250 | -0250 | -250 | -0250 | -500 | 0
+ -100 | -0100 | -100 | -0100 | -200 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 150 | 0150 | 150 | 0150 | 300 | 0
+ 300 | 0300 | 300 | 0300 | 600 | 0
+ 450 | 0450 | 450 | 0450 | 900 | 0
+ 600 | 0600 | 600 | 0600 | 1200 | 0
+ 750 | 0750 | 750 | 0750 | 1500 | 0
+(11 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- QUERY PLAN
---------------------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------------
Sort
Sort Key: t1.a, t2.b, ((t3.a + t3.b))
-> Result
-> Append
-> Hash Right Join
Hash Cond: (((t3.a + t3.b) / 2) = t1.a)
- -> Seq Scan on prt1_e_p1 t3
+ -> Seq Scan on prt1_e_p0 t3
-> Hash
-> Hash Right Join
Hash Cond: (t2.b = t1.a)
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
-> Hash
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Hash Right Join
Hash Cond: (((t3_1.a + t3_1.b) / 2) = t1_1.a)
- -> Seq Scan on prt1_e_p2 t3_1
+ -> Seq Scan on prt1_e_p1 t3_1
-> Hash
-> Hash Right Join
Hash Cond: (t2_1.b = t1_1.a)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Hash
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Hash Right Join
Hash Cond: (((t3_2.a + t3_2.b) / 2) = t1_2.a)
- -> Seq Scan on prt1_e_p3 t3_2
+ -> Seq Scan on prt1_e_p2 t3_2
-> Hash
-> Hash Right Join
Hash Cond: (t2_2.b = t1_2.a)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p2 t2_2
+ -> Hash
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Nested Loop Left Join
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (t1_3.a = b)
+ -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_3
+ Index Cond: (t1_3.a = ((a + b) / 2))
+ -> Hash Right Join
+ Hash Cond: (((t3_4.a + t3_4.b) / 2) = t1_4.a)
+ -> Seq Scan on prt1_e_p4 t3_4
+ -> Hash
+ -> Hash Right Join
+ Hash Cond: (t2_4.b = t1_4.a)
+ -> Seq Scan on prt2_p4 t2_4
-> Hash
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p4 t1_4
Filter: (b = 0)
-(34 rows)
+(52 rows)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- a | c | b | c | ?column? | c
------+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 50 | 0050 | | | 100 | 0
- 100 | 0100 | | | 200 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 200 | 0200 | | | 400 | 0
- 250 | 0250 | | | 500 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 350 | 0350 | | | 700 | 0
- 400 | 0400 | | | 800 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
- 500 | 0500 | | | 1000 | 0
- 550 | 0550 | | | 1100 | 0
-(12 rows)
+ a | c | b | c | ?column? | c
+------+-------+------+-------+----------+---
+ -250 | -0250 | -250 | -0250 | -500 | 0
+ -200 | -0200 | | | -400 | 0
+ -150 | -0150 | | | -300 | 0
+ -100 | -0100 | -100 | -0100 | -200 | 0
+ -50 | -0050 | | | -100 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 50 | 0050 | | | 100 | 0
+ 100 | 0100 | | | 200 | 0
+ 150 | 0150 | 150 | 0150 | 300 | 0
+ 200 | 0200 | | | 400 | 0
+ 250 | 0250 | | | 500 | 0
+ 300 | 0300 | 300 | 0300 | 600 | 0
+ 350 | 0350 | | | 700 | 0
+ 400 | 0400 | | | 800 | 0
+ 450 | 0450 | 450 | 0450 | 900 | 0
+ 500 | 0500 | | | 1000 | 0
+ 550 | 0550 | | | 1100 | 0
+ 600 | 0600 | 600 | 0600 | 1200 | 0
+ 650 | 0650 | | | 1300 | 0
+ 700 | 0700 | | | 1400 | 0
+ 750 | 0750 | 750 | 0750 | 1500 | 0
+(24 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- QUERY PLAN
--------------------------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------------
Sort
Sort Key: t1.a, t2.b, ((t3.a + t3.b))
-> Result
@@ -758,48 +1200,77 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
-> Nested Loop Left Join
-> Hash Right Join
Hash Cond: (t1.a = ((t3.a + t3.b) / 2))
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
-> Hash
- -> Seq Scan on prt1_e_p1 t3
+ -> Seq Scan on prt1_e_p0 t3
Filter: (c = 0)
- -> Index Scan using iprt2_p1_b on prt2_p1 t2
+ -> Index Scan using iprt2_p0_b on prt2_p0 t2
Index Cond: (t1.a = b)
-> Nested Loop Left Join
-> Hash Right Join
Hash Cond: (t1_1.a = ((t3_1.a + t3_1.b) / 2))
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
-> Hash
- -> Seq Scan on prt1_e_p2 t3_1
+ -> Seq Scan on prt1_e_p1 t3_1
Filter: (c = 0)
- -> Index Scan using iprt2_p2_b on prt2_p2 t2_1
+ -> Index Scan using iprt2_p1_b on prt2_p1 t2_1
Index Cond: (t1_1.a = b)
-> Nested Loop Left Join
-> Hash Right Join
Hash Cond: (t1_2.a = ((t3_2.a + t3_2.b) / 2))
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
-> Hash
- -> Seq Scan on prt1_e_p3 t3_2
+ -> Seq Scan on prt1_e_p2 t3_2
Filter: (c = 0)
- -> Index Scan using iprt2_p3_b on prt2_p3 t2_2
+ -> Index Scan using iprt2_p2_b on prt2_p2 t2_2
Index Cond: (t1_2.a = b)
-(31 rows)
+ -> Nested Loop Left Join
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_e_p3 t3_3
+ Filter: (c = 0)
+ -> Index Scan using iprt1_p3_a on prt1_p3 t1_3
+ Index Cond: (a = ((t3_3.a + t3_3.b) / 2))
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (t1_3.a = b)
+ -> Nested Loop Left Join
+ -> Hash Right Join
+ Hash Cond: (t1_4.a = ((t3_4.a + t3_4.b) / 2))
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Seq Scan on prt1_e_p4 t3_4
+ Filter: (c = 0)
+ -> Index Scan using iprt2_p4_b on prt2_p4 t2_4
+ Index Cond: (t1_4.a = b)
+(48 rows)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- a | c | b | c | ?column? | c
------+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 50 | 0050 | | | 100 | 0
- 100 | 0100 | | | 200 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 200 | 0200 | | | 400 | 0
- 250 | 0250 | | | 500 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 350 | 0350 | | | 700 | 0
- 400 | 0400 | | | 800 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
- 500 | 0500 | | | 1000 | 0
- 550 | 0550 | | | 1100 | 0
-(12 rows)
+ a | c | b | c | ?column? | c
+------+-------+------+-------+----------+---
+ -250 | -0250 | -250 | -0250 | -500 | 0
+ -200 | -0200 | | | -400 | 0
+ -150 | -0150 | | | -300 | 0
+ -100 | -0100 | -100 | -0100 | -200 | 0
+ -50 | -0050 | | | -100 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 50 | 0050 | | | 100 | 0
+ 100 | 0100 | | | 200 | 0
+ 150 | 0150 | 150 | 0150 | 300 | 0
+ 200 | 0200 | | | 400 | 0
+ 250 | 0250 | | | 500 | 0
+ 300 | 0300 | 300 | 0300 | 600 | 0
+ 350 | 0350 | | | 700 | 0
+ 400 | 0400 | | | 800 | 0
+ 450 | 0450 | 450 | 0450 | 900 | 0
+ 500 | 0500 | | | 1000 | 0
+ 550 | 0550 | | | 1100 | 0
+ 600 | 0600 | 600 | 0600 | 1200 | 0
+ 650 | 0650 | | | 1300 | 0
+ 700 | 0700 | | | 1400 | 0
+ 750 | 0750 | 750 | 0750 | 1500 | 0
+(24 rows)
-- Cases with non-nullable expressions in subquery results;
-- make sure these go to null as expected
@@ -808,10 +1279,23 @@ SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * F
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, prt2_p1.b, ((prt1_e_p1.a + prt1_e_p1.b))
+ Sort Key: prt1_p0.a, prt2_p0.b, ((prt1_e_p0.a + prt1_e_p0.b))
-> Result
-> Append
-> Hash Full Join
+ Hash Cond: (prt1_p0.a = ((prt1_e_p0.a + prt1_e_p0.b) / 2))
+ Filter: ((prt1_p0.a = (50)) OR (prt2_p0.b = (75)) OR (((prt1_e_p0.a + prt1_e_p0.b) / 2) = (50)))
+ -> Hash Full Join
+ Hash Cond: (prt1_p0.a = prt2_p0.b)
+ -> Seq Scan on prt1_p0
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p0
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_e_p0
+ Filter: (c = 0)
+ -> Hash Full Join
Hash Cond: (prt1_p1.a = ((prt1_e_p1.a + prt1_e_p1.b) / 2))
Filter: ((prt1_p1.a = (50)) OR (prt2_p1.b = (75)) OR (((prt1_e_p1.a + prt1_e_p1.b) / 2) = (50)))
-> Hash Full Join
@@ -850,7 +1334,20 @@ SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * F
-> Hash
-> Seq Scan on prt1_e_p3
Filter: (c = 0)
-(43 rows)
+ -> Hash Full Join
+ Hash Cond: (prt1_p4.a = ((prt1_e_p4.a + prt1_e_p4.b) / 2))
+ Filter: ((prt1_p4.a = (50)) OR (prt2_p4.b = (75)) OR (((prt1_e_p4.a + prt1_e_p4.b) / 2) = (50)))
+ -> Hash Full Join
+ Hash Cond: (prt1_p4.a = prt2_p4.b)
+ -> Seq Scan on prt1_p4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_e_p4
+ Filter: (c = 0)
+(69 rows)
SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.c = 0) t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a = t1.phv OR t2.b = t2.phv OR (t3.a + t3.b)/2 = t3.phv ORDER BY t1.a, t2.b, t3.a + t3.b;
a | phv | b | phv | ?column? | phv
@@ -868,173 +1365,263 @@ SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHER
Sort Key: t1.a
-> Append
-> Nested Loop
- Join Filter: (t1.a = t1_3.b)
+ Join Filter: (t1.a = t1_5.b)
-> HashAggregate
- Group Key: t1_3.b
+ Group Key: t1_5.b
-> Hash Join
- Hash Cond: (((t2.a + t2.b) / 2) = t1_3.b)
- -> Seq Scan on prt1_e_p1 t2
+ Hash Cond: (((t2.a + t2.b) / 2) = t1_5.b)
+ -> Seq Scan on prt1_e_p0 t2
-> Hash
- -> Seq Scan on prt2_p1 t1_3
+ -> Seq Scan on prt2_p0 t1_5
Filter: (a = 0)
- -> Index Scan using iprt1_p1_a on prt1_p1 t1
+ -> Index Scan using iprt1_p0_a on prt1_p0 t1
Index Cond: (a = ((t2.a + t2.b) / 2))
Filter: (b = 0)
-> Nested Loop
- Join Filter: (t1_1.a = t1_4.b)
+ Join Filter: (t1_1.a = t1_6.b)
-> HashAggregate
- Group Key: t1_4.b
+ Group Key: t1_6.b
-> Hash Join
- Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_4.b)
- -> Seq Scan on prt1_e_p2 t2_1
+ Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_6.b)
+ -> Seq Scan on prt1_e_p1 t2_1
-> Hash
- -> Seq Scan on prt2_p2 t1_4
+ -> Seq Scan on prt2_p1 t1_6
Filter: (a = 0)
- -> Index Scan using iprt1_p2_a on prt1_p2 t1_1
+ -> Index Scan using iprt1_p1_a on prt1_p1 t1_1
Index Cond: (a = ((t2_1.a + t2_1.b) / 2))
Filter: (b = 0)
-> Nested Loop
- Join Filter: (t1_2.a = t1_5.b)
+ Join Filter: (t1_2.a = t1_7.b)
-> HashAggregate
- Group Key: t1_5.b
+ Group Key: t1_7.b
-> Nested Loop
- -> Seq Scan on prt2_p3 t1_5
+ -> Seq Scan on prt2_p2 t1_7
Filter: (a = 0)
- -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t2_2
- Index Cond: (((a + b) / 2) = t1_5.b)
- -> Index Scan using iprt1_p3_a on prt1_p3 t1_2
+ -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t2_2
+ Index Cond: (((a + b) / 2) = t1_7.b)
+ -> Index Scan using iprt1_p2_a on prt1_p2 t1_2
Index Cond: (a = ((t2_2.a + t2_2.b) / 2))
Filter: (b = 0)
-(41 rows)
+ -> Nested Loop
+ Join Filter: (t1_3.a = t1_8.b)
+ -> HashAggregate
+ Group Key: t1_8.b
+ -> Nested Loop
+ -> Seq Scan on prt2_p3 t1_8
+ Filter: (a = 0)
+ -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t2_3
+ Index Cond: (((a + b) / 2) = t1_8.b)
+ -> Index Scan using iprt1_p3_a on prt1_p3 t1_3
+ Index Cond: (a = ((t2_3.a + t2_3.b) / 2))
+ Filter: (b = 0)
+ -> Nested Loop
+ Join Filter: (t1_4.a = t1_9.b)
+ -> HashAggregate
+ Group Key: t1_9.b
+ -> Hash Join
+ Hash Cond: (((t2_4.a + t2_4.b) / 2) = t1_9.b)
+ -> Seq Scan on prt1_e_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt2_p4 t1_9
+ Filter: (a = 0)
+ -> Index Scan using iprt1_p4_a on prt1_p4 t1_4
+ Index Cond: (a = ((t2_4.a + t2_4.b) / 2))
+ Filter: (b = 0)
+(66 rows)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.a = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.b = 0 ORDER BY t1.a;
- a | b | c
------+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
+ a | b | c
+------+---+-------
+ -250 | 0 | -0250
+ -100 | 0 | -0100
+ 0 | 0 | 0000
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 300 | 0 | 0300
+ 450 | 0 | 0450
+ 600 | 0 | 0600
+ 750 | 0 | 0750
+(9 rows)
EXPLAIN (COSTS OFF)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
- QUERY PLAN
--------------------------------------------------------------------------------
+ QUERY PLAN
+----------------------------------------------------------------------------
Sort
Sort Key: t1.a
-> Append
-> Nested Loop
-> HashAggregate
- Group Key: t1_3.b
+ Group Key: t1_5.b
-> Hash Semi Join
- Hash Cond: (t1_3.b = ((t1_6.a + t1_6.b) / 2))
- -> Seq Scan on prt2_p1 t1_3
+ Hash Cond: (t1_5.b = ((t1_10.a + t1_10.b) / 2))
+ -> Seq Scan on prt2_p0 t1_5
-> Hash
- -> Seq Scan on prt1_e_p1 t1_6
+ -> Seq Scan on prt1_e_p0 t1_10
Filter: (c = 0)
- -> Index Scan using iprt1_p1_a on prt1_p1 t1
- Index Cond: (a = t1_3.b)
+ -> Index Scan using iprt1_p0_a on prt1_p0 t1
+ Index Cond: (a = t1_5.b)
Filter: (b = 0)
-> Nested Loop
-> HashAggregate
- Group Key: t1_4.b
+ Group Key: t1_6.b
-> Hash Semi Join
- Hash Cond: (t1_4.b = ((t1_7.a + t1_7.b) / 2))
- -> Seq Scan on prt2_p2 t1_4
+ Hash Cond: (t1_6.b = ((t1_11.a + t1_11.b) / 2))
+ -> Seq Scan on prt2_p1 t1_6
-> Hash
- -> Seq Scan on prt1_e_p2 t1_7
+ -> Seq Scan on prt1_e_p1 t1_11
Filter: (c = 0)
- -> Index Scan using iprt1_p2_a on prt1_p2 t1_1
- Index Cond: (a = t1_4.b)
+ -> Index Scan using iprt1_p1_a on prt1_p1 t1_1
+ Index Cond: (a = t1_6.b)
Filter: (b = 0)
-> Nested Loop
- -> Unique
- -> Sort
- Sort Key: t1_5.b
- -> Hash Semi Join
- Hash Cond: (t1_5.b = ((t1_8.a + t1_8.b) / 2))
- -> Seq Scan on prt2_p3 t1_5
- -> Hash
- -> Seq Scan on prt1_e_p3 t1_8
- Filter: (c = 0)
- -> Index Scan using iprt1_p3_a on prt1_p3 t1_2
- Index Cond: (a = t1_5.b)
+ -> HashAggregate
+ Group Key: t1_7.b
+ -> Hash Semi Join
+ Hash Cond: (t1_7.b = ((t1_12.a + t1_12.b) / 2))
+ -> Seq Scan on prt2_p2 t1_7
+ -> Hash
+ -> Seq Scan on prt1_e_p2 t1_12
+ Filter: (c = 0)
+ -> Index Scan using iprt1_p2_a on prt1_p2 t1_2
+ Index Cond: (a = t1_7.b)
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ -> Index Only Scan using iprt2_p3_b on prt2_p3 t1_8
+ Index Cond: (b = t1_3.a)
+ -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t1_13
+ Index Cond: (((a + b) / 2) = t1_8.b)
+ Filter: (c = 0)
+ -> Nested Loop
+ -> HashAggregate
+ Group Key: t1_9.b
+ -> Hash Semi Join
+ Hash Cond: (t1_9.b = ((t1_14.a + t1_14.b) / 2))
+ -> Seq Scan on prt2_p4 t1_9
+ -> Hash
+ -> Seq Scan on prt1_e_p4 t1_14
+ Filter: (c = 0)
+ -> Index Scan using iprt1_p4_a on prt1_p4 t1_4
+ Index Cond: (a = t1_9.b)
Filter: (b = 0)
-(40 rows)
+(60 rows)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
- a | b | c
------+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
+ a | b | c
+------+---+-------
+ -250 | 0 | -0250
+ -100 | 0 | -0100
+ 0 | 0 | 0000
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 300 | 0 | 0300
+ 450 | 0 | 0450
+ 600 | 0 | 0600
+ 750 | 0 | 0750
+(9 rows)
-- test merge joins
SET enable_hashjoin TO off;
SET enable_nestloop TO off;
EXPLAIN (COSTS OFF)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
- QUERY PLAN
-----------------------------------------------------------------
+ QUERY PLAN
+------------------------------------------------------------------
Merge Append
Sort Key: t1.a
-> Merge Semi Join
- Merge Cond: (t1.a = t1_3.b)
+ Merge Cond: (t1.a = t1_5.b)
-> Sort
Sort Key: t1.a
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Merge Semi Join
- Merge Cond: (t1_3.b = (((t1_6.a + t1_6.b) / 2)))
+ Merge Cond: (t1_5.b = (((t1_10.a + t1_10.b) / 2)))
-> Sort
- Sort Key: t1_3.b
- -> Seq Scan on prt2_p1 t1_3
+ Sort Key: t1_5.b
+ -> Seq Scan on prt2_p0 t1_5
-> Sort
- Sort Key: (((t1_6.a + t1_6.b) / 2))
- -> Seq Scan on prt1_e_p1 t1_6
+ Sort Key: (((t1_10.a + t1_10.b) / 2))
+ -> Seq Scan on prt1_e_p0 t1_10
Filter: (c = 0)
-> Merge Semi Join
- Merge Cond: (t1_1.a = t1_4.b)
+ Merge Cond: (t1_1.a = t1_6.b)
-> Sort
Sort Key: t1_1.a
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Merge Semi Join
- Merge Cond: (t1_4.b = (((t1_7.a + t1_7.b) / 2)))
+ Merge Cond: (t1_6.b = (((t1_11.a + t1_11.b) / 2)))
-> Sort
- Sort Key: t1_4.b
- -> Seq Scan on prt2_p2 t1_4
+ Sort Key: t1_6.b
+ -> Seq Scan on prt2_p1 t1_6
-> Sort
- Sort Key: (((t1_7.a + t1_7.b) / 2))
- -> Seq Scan on prt1_e_p2 t1_7
+ Sort Key: (((t1_11.a + t1_11.b) / 2))
+ -> Seq Scan on prt1_e_p1 t1_11
Filter: (c = 0)
-> Merge Semi Join
- Merge Cond: (t1_2.a = t1_5.b)
+ Merge Cond: (t1_2.a = t1_7.b)
-> Sort
Sort Key: t1_2.a
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
-> Merge Semi Join
- Merge Cond: (t1_5.b = (((t1_8.a + t1_8.b) / 2)))
+ Merge Cond: (t1_7.b = (((t1_12.a + t1_12.b) / 2)))
-> Sort
- Sort Key: t1_5.b
- -> Seq Scan on prt2_p3 t1_5
+ Sort Key: t1_7.b
+ -> Seq Scan on prt2_p2 t1_7
+ -> Sort
+ Sort Key: (((t1_12.a + t1_12.b) / 2))
+ -> Seq Scan on prt1_e_p2 t1_12
+ Filter: (c = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_3.a = t1_8.b)
+ -> Sort
+ Sort Key: t1_3.a
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_8.b = (((t1_13.a + t1_13.b) / 2)))
-> Sort
- Sort Key: (((t1_8.a + t1_8.b) / 2))
- -> Seq Scan on prt1_e_p3 t1_8
+ Sort Key: t1_8.b
+ -> Seq Scan on prt2_p3 t1_8
+ -> Sort
+ Sort Key: (((t1_13.a + t1_13.b) / 2))
+ -> Seq Scan on prt1_e_p3 t1_13
Filter: (c = 0)
-(47 rows)
+ -> Merge Semi Join
+ Merge Cond: (t1_4.a = t1_9.b)
+ -> Sort
+ Sort Key: t1_4.a
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_9.b = (((t1_14.a + t1_14.b) / 2)))
+ -> Sort
+ Sort Key: t1_9.b
+ -> Seq Scan on prt2_p4 t1_9
+ -> Sort
+ Sort Key: (((t1_14.a + t1_14.b) / 2))
+ -> Seq Scan on prt1_e_p4 t1_14
+ Filter: (c = 0)
+(77 rows)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
- a | b | c
------+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
+ a | b | c
+------+---+-------
+ -250 | 0 | -0250
+ -100 | 0 | -0100
+ 0 | 0 | 0000
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 300 | 0 | 0300
+ 450 | 0 | 0450
+ 600 | 0 | 0600
+ 750 | 0 | 0750
+(9 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
@@ -1052,14 +1639,14 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
Merge Cond: ((((t3.a + t3.b) / 2)) = t1.a)
-> Sort
Sort Key: (((t3.a + t3.b) / 2))
- -> Seq Scan on prt1_e_p1 t3
+ -> Seq Scan on prt1_e_p0 t3
Filter: (c = 0)
-> Sort
Sort Key: t1.a
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
-> Sort
Sort Key: t2.b
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
-> Merge Left Join
Merge Cond: (t1_1.a = t2_1.b)
-> Sort
@@ -1068,14 +1655,14 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
Merge Cond: ((((t3_1.a + t3_1.b) / 2)) = t1_1.a)
-> Sort
Sort Key: (((t3_1.a + t3_1.b) / 2))
- -> Seq Scan on prt1_e_p2 t3_1
+ -> Seq Scan on prt1_e_p1 t3_1
Filter: (c = 0)
-> Sort
Sort Key: t1_1.a
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
-> Sort
Sort Key: t2_1.b
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Merge Left Join
Merge Cond: (t1_2.a = t2_2.b)
-> Sort
@@ -1084,35 +1671,805 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
Merge Cond: ((((t3_2.a + t3_2.b) / 2)) = t1_2.a)
-> Sort
Sort Key: (((t3_2.a + t3_2.b) / 2))
- -> Seq Scan on prt1_e_p3 t3_2
+ -> Seq Scan on prt1_e_p2 t3_2
Filter: (c = 0)
-> Sort
Sort Key: t1_2.a
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
-> Sort
Sort Key: t2_2.b
- -> Seq Scan on prt2_p3 t2_2
-(52 rows)
+ -> Seq Scan on prt2_p2 t2_2
+ -> Merge Left Join
+ Merge Cond: (t1_3.a = t2_3.b)
+ -> Sort
+ Sort Key: t1_3.a
+ -> Merge Left Join
+ Merge Cond: ((((t3_3.a + t3_3.b) / 2)) = t1_3.a)
+ -> Sort
+ Sort Key: (((t3_3.a + t3_3.b) / 2))
+ -> Seq Scan on prt1_e_p3 t3_3
+ Filter: (c = 0)
+ -> Sort
+ Sort Key: t1_3.a
+ -> Seq Scan on prt1_p3 t1_3
+ -> Sort
+ Sort Key: t2_3.b
+ -> Seq Scan on prt2_p3 t2_3
+ -> Merge Left Join
+ Merge Cond: (t1_4.a = t2_4.b)
+ -> Sort
+ Sort Key: t1_4.a
+ -> Merge Left Join
+ Merge Cond: ((((t3_4.a + t3_4.b) / 2)) = t1_4.a)
+ -> Sort
+ Sort Key: (((t3_4.a + t3_4.b) / 2))
+ -> Seq Scan on prt1_e_p4 t3_4
+ Filter: (c = 0)
+ -> Sort
+ Sort Key: t1_4.a
+ -> Seq Scan on prt1_p4 t1_4
+ -> Sort
+ Sort Key: t2_4.b
+ -> Seq Scan on prt2_p4 t2_4
+(84 rows)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- a | c | b | c | ?column? | c
------+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 50 | 0050 | | | 100 | 0
- 100 | 0100 | | | 200 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 200 | 0200 | | | 400 | 0
- 250 | 0250 | | | 500 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 350 | 0350 | | | 700 | 0
- 400 | 0400 | | | 800 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
- 500 | 0500 | | | 1000 | 0
- 550 | 0550 | | | 1100 | 0
-(12 rows)
+ a | c | b | c | ?column? | c
+------+-------+------+-------+----------+---
+ -250 | -0250 | -250 | -0250 | -500 | 0
+ -200 | -0200 | | | -400 | 0
+ -150 | -0150 | | | -300 | 0
+ -100 | -0100 | -100 | -0100 | -200 | 0
+ -50 | -0050 | | | -100 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 50 | 0050 | | | 100 | 0
+ 100 | 0100 | | | 200 | 0
+ 150 | 0150 | 150 | 0150 | 300 | 0
+ 200 | 0200 | | | 400 | 0
+ 250 | 0250 | | | 500 | 0
+ 300 | 0300 | 300 | 0300 | 600 | 0
+ 350 | 0350 | | | 700 | 0
+ 400 | 0400 | | | 800 | 0
+ 450 | 0450 | 450 | 0450 | 900 | 0
+ 500 | 0500 | | | 1000 | 0
+ 550 | 0550 | | | 1100 | 0
+ 600 | 0600 | 600 | 0600 | 1200 | 0
+ 650 | 0650 | | | 1300 | 0
+ 700 | 0700 | | | 1400 | 0
+ 750 | 0750 | 750 | 0750 | 1500 | 0
+(24 rows)
RESET enable_hashjoin;
RESET enable_nestloop;
+-- Add an extra partition to prt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+DROP TABLE prt2_p4;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (800);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (800) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999, 3) i;
+ANALYZE prt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on prt2_p0 t2
+ -> Hash
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Hash Join
+ Hash Cond: (t2_1.b = t1_1.a)
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash Join
+ Hash Cond: (t2_2.b = t1_2.a)
+ -> Seq Scan on prt2_p2 t2_2
+ -> Hash
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Nested Loop
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ -> Hash Join
+ Hash Cond: (t2_4.b = t1_4.a)
+ -> Seq Scan on prt2_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(32 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ a | c | a | c
+------+-------+---+-------
+ -250 | -0250 | 0 | -0250
+ -100 | -0100 | 0 | -0100
+ 0 | 0000 | 0 | 0000
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 0 | 0150
+ 300 | 0300 | 0 | 0300
+ 450 | 0450 | 0 | 0450
+ 600 | 0600 | 0 | 0600
+ 750 | 0750 | 0 | 0750
+(9 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Right Join
+ Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on prt2_p0 t2
+ -> Hash
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Hash Right Join
+ Hash Cond: (t2_1.b = t1_1.a)
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash Right Join
+ Hash Cond: (t2_2.b = t1_2.a)
+ -> Seq Scan on prt2_p2 t2_2
+ -> Hash
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (t1_3.a = b)
+ -> Hash Right Join
+ Hash Cond: (t2_4.b = t1_4.a)
+ -> Seq Scan on prt2_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(32 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ a | c | a | c
+------+-------+---+-------
+ -250 | -0250 | 0 | -0250
+ -200 | -0200 | |
+ -150 | -0150 | |
+ -100 | -0100 | 0 | -0100
+ -50 | -0050 | |
+ 0 | 0000 | 0 | 0000
+ 0 | 0000 | 0 | 0000
+ 50 | 0050 | |
+ 100 | 0100 | |
+ 150 | 0150 | 0 | 0150
+ 200 | 0200 | |
+ 250 | 0250 | |
+ 300 | 0300 | 0 | 0300
+ 350 | 0350 | |
+ 400 | 0400 | |
+ 450 | 0450 | 0 | 0450
+ 500 | 0500 | |
+ 550 | 0550 | |
+ 600 | 0600 | 0 | 0600
+ 650 | 0650 | |
+ 700 | 0700 | |
+ 750 | 0750 | 0 | 0750
+(22 rows)
+
+-- TODO: below query is picking partition-wise-join which is not expected
+-- also generating wrong output, need to remove comment after fix
+-- right join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Result
+ -> Append
+ -> Hash Right Join
+ Hash Cond: (t1.a = t2.b)
+ -> Seq Scan on prt1_p0 t1
+ -> Hash
+ -> Seq Scan on prt2_p0 t2
+ Filter: (a = 0)
+ -> Hash Right Join
+ Hash Cond: (t1_1.a = t2_1.b)
+ -> Seq Scan on prt1_p1 t1_1
+ -> Hash
+ -> Seq Scan on prt2_p1 t2_1
+ Filter: (a = 0)
+ -> Nested Loop Left Join
+ -> Seq Scan on prt2_p2 t2_2
+ Filter: (a = 0)
+ -> Index Scan using iprt1_p2_a on prt1_p2 t1_2
+ Index Cond: (a = t2_2.b)
+ -> Nested Loop Left Join
+ -> Seq Scan on prt2_p3 t2_3
+ Filter: (a = 0)
+ -> Index Scan using iprt1_p3_a on prt1_p3 t1_3
+ Index Cond: (a = t2_3.b)
+ -> Hash Right Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+(30 rows)
+
+SET enable_partition_wise_join to false;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a;
+ a | c | a | c
+------+-------+---+-------
+ -250 | -0250 | 0 | -0250
+ -100 | -0100 | 0 | -0100
+ 0 | 0000 | 0 | 0000
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 0 | 0150
+ 300 | 0300 | 0 | 0300
+ 450 | 0450 | 0 | 0450
+ 600 | 0600 | 0 | 0600
+ 750 | 0750 | 0 | 0750
+ | | 0 | 0075
+ | | 0 | 0375
+ | | 0 | 0825
+ | | 0 | -0175
+ | | 0 | 0900
+ | | 0 | 0675
+ | | 0 | -0025
+ | | 0 | 0525
+ | | 0 | 0225
+ | | 0 | 0975
+(19 rows)
+
+SET enable_partition_wise_join to true;
+-- TODO: below query is picking partition-wise-join which is not expected
+-- also generating wrong output, need to remove comment after fix
+-- full join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE coalesce(t1.b, 0) + coalesce(t2.a, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Append
+ -> Hash Full Join
+ Hash Cond: (t1.a = t2.b)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.a, 0)) = 0)
+ -> Seq Scan on prt1_p0 t1
+ -> Hash
+ -> Seq Scan on prt2_p0 t2
+ -> Hash Full Join
+ Hash Cond: (t1_1.a = t2_1.b)
+ Filter: ((COALESCE(t1_1.b, 0) + COALESCE(t2_1.a, 0)) = 0)
+ -> Seq Scan on prt1_p1 t1_1
+ -> Hash
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash Full Join
+ Hash Cond: (t1_2.a = t2_2.b)
+ Filter: ((COALESCE(t1_2.b, 0) + COALESCE(t2_2.a, 0)) = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on prt2_p2 t2_2
+ -> Hash Full Join
+ Hash Cond: (t1_3.a = t2_3.b)
+ Filter: ((COALESCE(t1_3.b, 0) + COALESCE(t2_3.a, 0)) = 0)
+ -> Seq Scan on prt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on prt2_p3 t2_3
+ -> Hash Full Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ Filter: ((COALESCE(t1_4.b, 0) + COALESCE(t2_4.a, 0)) = 0)
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+(33 rows)
+
+SET enable_partition_wise_join to false;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE coalesce(t1.b, 0) + coalesce(t2.a, 0) = 0 ORDER BY t1.a, t2.a;
+ a | c | a | c
+------+-------+---+-------
+ -250 | -0250 | 0 | -0250
+ -200 | -0200 | |
+ -150 | -0150 | |
+ -100 | -0100 | 0 | -0100
+ -50 | -0050 | |
+ 0 | 0000 | 0 | 0000
+ 0 | 0000 | 0 | 0000
+ 50 | 0050 | |
+ 100 | 0100 | |
+ 150 | 0150 | 0 | 0150
+ 200 | 0200 | |
+ 250 | 0250 | |
+ 300 | 0300 | 0 | 0300
+ 350 | 0350 | |
+ 400 | 0400 | |
+ 450 | 0450 | 0 | 0450
+ 500 | 0500 | |
+ 550 | 0550 | |
+ 600 | 0600 | 0 | 0600
+ 650 | 0650 | |
+ 700 | 0700 | |
+ 750 | 0750 | 0 | 0750
+ | | 0 | 0825
+ | | 0 | -0175
+ | | 0 | 0900
+ | | 0 | 0675
+ | | 0 | -0025
+ | | 0 | 0525
+ | | 0 | 0225
+ | | 0 | 0975
+ | | 0 | 0075
+ | | 0 | 0375
+(32 rows)
+
+SET enable_partition_wise_join to true;
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Semi Join
+ Hash Cond: (t1.a = t2.b)
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p0 t2
+ -> Hash Semi Join
+ Hash Cond: (t1_1.a = t2_1.b)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash Semi Join
+ Hash Cond: (t1_2.a = t2_2.b)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p2 t2_2
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Only Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ -> Hash Semi Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+(32 rows)
+
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+------+---+-------
+ -250 | 0 | -0250
+ -100 | 0 | -0100
+ 0 | 0 | 0000
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 300 | 0 | 0300
+ 450 | 0 | 0450
+ 600 | 0 | 0600
+ 750 | 0 | 0750
+(9 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Append
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p0_a on prt1_p0 t2
+ Index Cond: (a = t1.b)
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p1_a on prt1_p1 t2_1
+ Index Cond: (a = t1_1.b)
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2
+ Index Cond: (a = t1_2.b)
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3
+ Index Cond: (a = t1_3.b)
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p4_a on prt1_p4 t2_4
+ Index Cond: (a = t1_4.b)
+(28 rows)
+
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+---+------+-------
+ 0 | -250 | -0250
+ 0 | -100 | -0100
+ 0 | 0 | 0000
+ 0 | 150 | 0150
+ 0 | 300 | 0300
+ 0 | 450 | 0450
+ 0 | 600 | 0600
+ 0 | 750 | 0750
+(8 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Anti Join
+ Hash Cond: (t1.a = t2.b)
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p0 t2
+ -> Hash Anti Join
+ Hash Cond: (t1_1.a = t2_1.b)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash Anti Join
+ Hash Cond: (t1_2.a = t2_2.b)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p2 t2_2
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Only Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ -> Hash Anti Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+(32 rows)
+
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+------+---+-------
+ -200 | 0 | -0200
+ -150 | 0 | -0150
+ -50 | 0 | -0050
+ 50 | 0 | 0050
+ 100 | 0 | 0100
+ 200 | 0 | 0200
+ 250 | 0 | 0250
+ 350 | 0 | 0350
+ 400 | 0 | 0400
+ 500 | 0 | 0500
+ 550 | 0 | 0550
+ 650 | 0 | 0650
+ 700 | 0 | 0700
+(13 rows)
+
+-- TODO: below query is generating wrong output, need to remove comment after fix
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Append
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p0_a on prt1_p0 t2
+ Index Cond: (a = t1.b)
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p1_a on prt1_p1 t2_1
+ Index Cond: (a = t1_1.b)
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2
+ Index Cond: (a = t1_2.b)
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3
+ Index Cond: (a = t1_3.b)
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p4_a on prt1_p4 t2_4
+ Index Cond: (a = t1_4.b)
+(28 rows)
+
+SET enable_partition_wise_join to false;
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+---+------+-------
+ 0 | -175 | -0175
+ 0 | -25 | -0025
+ 0 | 75 | 0075
+ 0 | 225 | 0225
+ 0 | 375 | 0375
+ 0 | 525 | 0525
+ 0 | 675 | 0675
+ 0 | 825 | 0825
+ 0 | 900 | 0900
+ 0 | 975 | 0975
+(10 rows)
+
+SET enable_partition_wise_join to true;
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE prt2_p4;
+DROP TABLE prt2_p5;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (700);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (700) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999, 3) i;
+ANALYZE prt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(23 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Right Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(23 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------
+ Hash Right Join
+ Hash Cond: (t1.a = t2.b)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t2_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t2_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t2_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t2_5
+ Filter: (a = 0)
+(22 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Join
+ Hash Cond: (t1.a = t2.b)
+ Join Filter: ((t1.b + t2.a) = 0)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+(19 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Semi Join
+ Hash Cond: (t1.a = t2.b)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+(23 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Hash Semi Join
+ Hash Cond: (t1.b = t2.a)
+ -> Append
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t1_5
+ Filter: (a = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p0 t2
+ -> Seq Scan on prt1_p1 t2_1
+ -> Seq Scan on prt1_p2 t2_2
+ -> Seq Scan on prt1_p3 t2_3
+ -> Seq Scan on prt1_p4 t2_4
+(24 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: (t1.a = t2.b)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+(23 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Hash Anti Join
+ Hash Cond: (t1.b = t2.a)
+ -> Append
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t1_5
+ Filter: (a = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p0 t2
+ -> Seq Scan on prt1_p1 t2_1
+ -> Seq Scan on prt1_p2 t2_2
+ -> Seq Scan on prt1_p3 t2_3
+ -> Seq Scan on prt1_p4 t2_4
+(24 rows)
+
--
-- partitioned by multiple columns
--
@@ -1182,32 +2539,388 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1
--
-- tests for list partitioned tables.
--
-CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
-ANALYZE plt1;
-CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i;
-ANALYZE plt2;
+\set part_mod 17
+\set cond_mod 47
+\set num_rows 500
+CREATE TABLE plt1 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0001','0002','0003');
+CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0008','0009');
+CREATE TABLE plt1_p4 PARTITION OF plt1 FOR VALUES IN ('0000','0010');
+INSERT INTO plt1 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (7, 11, 12, 13, 14, 15, 16);
+ANALYSE plt1;
+-- plt2 have missing starting 0001, additional 0007, missing ending 0010
+-- and additional 0011 and 0012 bounds
+CREATE TABLE plt2 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002','0003');
+CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0007','0008','0009');
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000','0011','0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 10, 13, 14, 15, 16);
+ANALYSE plt2;
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Join Filter: ((t1_1.b + t2_1.b) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + t2_2.b) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t1_3.b + t2_3.b) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Left Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Right Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Left Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Left Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(7 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+------------------------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Result
+ -> Append
+ -> Hash Right Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Left Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + t2_1.b) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Left Join
+ Hash Cond: ((t2_2.c)::text = (t1_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + t2_2.b) = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash Right Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + t2_3.b) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(28 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 | 0011
+(6 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Append
+ -> Hash Full Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Full Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Full Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Full Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 | 0011
+(8 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt2_p4 t2
+ -> Nested Loop
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> HashAggregate
+ Group Key: (t2_1.c)::text
+ -> Seq Scan on plt2_p1 t2_1
+ -> Materialize
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(29 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt1_p4 t2
+ -> Materialize
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2_1
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_3
+(26 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop Anti Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Anti Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt2_p1 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 188 | 0 | 0001
+ 282 | 0 | 0010
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt1_p4 t2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 470 | 0 | 0011
+(1 row)
+
--
-- list partitioned by expression
--
CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
-CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0002', '0003');
+CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0004', '0005', '0006');
+CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0008', '0009');
+CREATE TABLE plt1_e_p4 PARTITION OF plt1_e FOR VALUES IN ('0000');
+INSERT INTO plt1_e SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 7, 10, 11, 12, 13, 14, 15, 16);
ANALYZE plt1_e;
-- test partition matching with N-way join
EXPLAIN (COSTS OFF)
SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
- QUERY PLAN
---------------------------------------------------------------------------------------
+ QUERY PLAN
+----------------------------------------------------------------------------------------
Sort
Sort Key: t1.c, t3.c
-> HashAggregate
@@ -1215,50 +2928,1166 @@ SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, pl
-> Result
-> Append
-> Hash Join
- Hash Cond: (t1.c = t2.c)
- -> Seq Scan on plt1_p1 t1
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = ltrim(t3.c, 'A'::text))
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt1_e_p4 t3
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ -> Hash Join
+ Hash Cond: ((t2_1.c)::text = ltrim(t3_1.c, 'A'::text))
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_e_p1 t3_1
-> Hash
- -> Hash Join
- Hash Cond: (t2.c = ltrim(t3.c, 'A'::text))
- -> Seq Scan on plt2_p1 t2
- -> Hash
- -> Seq Scan on plt1_e_p1 t3
+ -> Seq Scan on plt1_p1 t1_1
-> Hash Join
- Hash Cond: (t1_1.c = t2_1.c)
- -> Seq Scan on plt1_p2 t1_1
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = ltrim(t3_2.c, 'A'::text))
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt1_e_p2 t3_2
-> Hash
- -> Hash Join
- Hash Cond: (t2_1.c = ltrim(t3_1.c, 'A'::text))
- -> Seq Scan on plt2_p2 t2_1
- -> Hash
- -> Seq Scan on plt1_e_p2 t3_1
+ -> Seq Scan on plt2_p2 t2_2
-> Hash Join
- Hash Cond: (t1_2.c = t2_2.c)
- -> Seq Scan on plt1_p3 t1_2
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = ltrim(t3_3.c, 'A'::text))
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt1_e_p3 t3_3
-> Hash
- -> Hash Join
- Hash Cond: (t2_2.c = ltrim(t3_2.c, 'A'::text))
- -> Seq Scan on plt2_p3 t2_2
- -> Hash
- -> Seq Scan on plt1_e_p3 t3_2
-(33 rows)
+ -> Seq Scan on plt2_p3 t2_3
+(42 rows)
SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
- avg | avg | avg | c | c | c
-----------------------+----------------------+-----------------------+------+------+-------
- 24.0000000000000000 | 24.0000000000000000 | 48.0000000000000000 | 0000 | 0000 | A0000
- 74.0000000000000000 | 75.0000000000000000 | 148.0000000000000000 | 0001 | 0001 | A0001
- 124.0000000000000000 | 124.5000000000000000 | 248.0000000000000000 | 0002 | 0002 | A0002
- 174.0000000000000000 | 174.0000000000000000 | 348.0000000000000000 | 0003 | 0003 | A0003
- 224.0000000000000000 | 225.0000000000000000 | 448.0000000000000000 | 0004 | 0004 | A0004
- 274.0000000000000000 | 274.5000000000000000 | 548.0000000000000000 | 0005 | 0005 | A0005
- 324.0000000000000000 | 324.0000000000000000 | 648.0000000000000000 | 0006 | 0006 | A0006
- 374.0000000000000000 | 375.0000000000000000 | 748.0000000000000000 | 0007 | 0007 | A0007
- 424.0000000000000000 | 424.5000000000000000 | 848.0000000000000000 | 0008 | 0008 | A0008
- 474.0000000000000000 | 474.0000000000000000 | 948.0000000000000000 | 0009 | 0009 | A0009
- 524.0000000000000000 | 525.0000000000000000 | 1048.0000000000000000 | 0010 | 0010 | A0010
- 574.0000000000000000 | 574.5000000000000000 | 1148.0000000000000000 | 0011 | 0011 | A0011
-(12 rows)
+ avg | avg | avg | c | c | c
+----------------------+---------------------+----------------------+------+------+------
+ 246.5000000000000000 | 22.4666666666666667 | 268.9666666666666667 | 0000 | 0000 | 0000
+ 248.5000000000000000 | 21.3333333333333333 | 269.8333333333333333 | 0002 | 0002 | 0002
+ 249.5000000000000000 | 22.3333333333333333 | 271.8333333333333333 | 0003 | 0003 | 0003
+ 250.5000000000000000 | 23.3333333333333333 | 273.8333333333333333 | 0004 | 0004 | 0004
+ 251.5000000000000000 | 22.7666666666666667 | 274.2666666666666667 | 0005 | 0005 | 0005
+ 252.5000000000000000 | 22.2000000000000000 | 274.7000000000000000 | 0006 | 0006 | 0006
+ 246.0000000000000000 | 23.9655172413793103 | 269.9655172413793103 | 0008 | 0008 | 0008
+ 247.0000000000000000 | 23.3448275862068966 | 270.3448275862068966 | 0009 | 0009 | 0009
+(8 rows)
+
+-- Add an extra partition to plt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (13, 14);
+ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Join Filter: ((t1_1.b + t2_1.b) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + t2_2.b) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t1_3.b + t2_3.b) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Left Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Right Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Left Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Left Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(7 rows)
+
+-- right join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Hash Right Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(17 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 47 | 0013
+ | | 470 | 0011
+ | | 235 | 0014
+(8 rows)
+
+-- full join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Full Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(17 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 47 | 0013
+ | | 235 | 0014
+ | | 470 | 0011
+(10 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt2_p4 t2
+ -> Nested Loop
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> HashAggregate
+ Group Key: (t2_1.c)::text
+ -> Seq Scan on plt2_p1 t2_1
+ -> Materialize
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(29 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt1_p4 t2
+ -> Materialize
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2_1
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_3
+(26 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop Anti Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Anti Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt2_p1 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 188 | 0 | 0001
+ 282 | 0 | 0010
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p5 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t2
+ -> Seq Scan on plt1_p1 t2_1
+ -> Seq Scan on plt1_p2 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(21 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 47 | 0 | 0013
+ 235 | 0 | 0014
+ 470 | 0 | 0011
+(3 rows)
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt2_p5;
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0001','0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (1, 13, 14);
+ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Left Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Hash Right Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Full Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(23 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p5 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p1 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt1_p4 t2
+ -> Seq Scan on plt1_p1 t2_1
+ -> Seq Scan on plt1_p2 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(24 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+----------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Nested Loop Anti Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Materialize
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(20 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p5 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p1 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t2
+ -> Seq Scan on plt1_p1 t2_1
+ -> Seq Scan on plt1_p2 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(21 rows)
+
+-- partition have a NULL on one side, Partition-wise join is possible with
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- in this case NULL will be treated as addition partition bounds.
+DROP TABLE plt2_p5;
+DROP TABLE plt2_p4;
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000',NULL,'0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, case when i % :part_mod = 11 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (0,11,12);
+ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash
+ -> Seq Scan on plt1_p4 t1
+ -> Hash Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Join Filter: ((t1_1.b + t2_1.b) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + t2_2.b) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t1_3.b + t2_3.b) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Right Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash
+ -> Seq Scan on plt1_p4 t1
+ -> Hash Right Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Left Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Left Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(7 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+------------------------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Result
+ -> Append
+ -> Hash Left Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash
+ -> Seq Scan on plt1_p4 t1
+ -> Hash Left Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + t2_1.b) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Left Join
+ Hash Cond: ((t2_2.c)::text = (t1_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + t2_2.b) = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash Right Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + t2_3.b) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(28 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 |
+(6 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Append
+ -> Hash Full Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash
+ -> Seq Scan on plt1_p4 t1
+ -> Hash Full Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Full Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Full Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 |
+(8 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt2_p4 t2
+ -> Materialize
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Nested Loop
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> HashAggregate
+ Group Key: (t2_1.c)::text
+ -> Seq Scan on plt2_p1 t2_1
+ -> Materialize
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(29 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt1_p4 t2
+ -> Materialize
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2_1
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_3
+(26 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop Anti Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Anti Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt2_p1 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 188 | 0 | 0001
+ 282 | 0 | 0010
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt1_p4 t2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+---
+ 470 | 0 |
+(1 row)
+
+-- partition have a NULL on both side with different partition bounds w.r.t other side
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt1_p3;
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN (NULL,'0008','0009');
+INSERT INTO plt1 SELECT i, i % :cond_mod, case when i % :part_mod = 7 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (7,8,9);
+ANALYZE plt1;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Right Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Hash Left Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Full Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+(22 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt1_p4 t2
+ -> Seq Scan on plt1_p1 t2_1
+ -> Seq Scan on plt1_p2 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(22 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+(19 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t2
+ -> Seq Scan on plt1_p1 t2_1
+ -> Seq Scan on plt1_p2 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(19 rows)
-- joins where one of the relations is proven empty
EXPLAIN (COSTS OFF)
@@ -1286,16 +4115,22 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1
-> Hash Left Join
Hash Cond: (t2.b = a)
-> Append
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t2_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t2_2
Filter: (a = 0)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p3 t2_3
Filter: (a = 0)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t2_5
Filter: (a = 0)
-> Hash
-> Result
One-Time Filter: false
-(14 rows)
+(20 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b;
@@ -1306,16 +4141,22 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1
-> Hash Left Join
Hash Cond: (t2.b = a)
-> Append
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t2_1
Filter: (a = 0)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p2 t2_2
Filter: (a = 0)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t2_5
Filter: (a = 0)
-> Hash
-> Result
One-Time Filter: false
-(14 rows)
+(20 rows)
--
-- multi-leveled partitions
@@ -1341,7 +4182,7 @@ CREATE TABLE prt2_l_p3_p2 PARTITION OF prt2_l_p3 FOR VALUES FROM (13) TO (25);
INSERT INTO prt2_l SELECT i % 25, i, to_char(i % 4, 'FM0000') FROM generate_series(0, 599, 3) i;
ANALYZE prt2_l;
-- inner join, qual covering only top-level partitions
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
QUERY PLAN
-------------------------------------------------------------
@@ -1386,7 +4227,7 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1
(4 rows)
-- left join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t1.b = 0 ORDER BY t1.a, t2.b;
QUERY PLAN
------------------------------------------------------------------------------------
@@ -1440,7 +4281,7 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b
(12 rows)
-- right join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.a = 0 ORDER BY t1.a, t2.b;
QUERY PLAN
------------------------------------------------------------------------------------------
@@ -1491,7 +4332,7 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b
(8 rows)
-- full join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.b = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.a = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------
@@ -1552,7 +4393,7 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.b = 0) t1
(16 rows)
-- lateral partition-wise join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT * FROM prt1_l t1 LEFT JOIN LATERAL
(SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.b AS t3b, least(t1.a,t2.a,t3.b) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.c = t3.c)) ss
ON t1.a = ss.t2a AND t1.c = ss.t2c WHERE t1.b = 0 ORDER BY t1.a;
@@ -1626,7 +4467,7 @@ SELECT * FROM prt1_l t1 LEFT JOIN LATERAL
(12 rows)
-- join with one side empty
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c;
QUERY PLAN
-------------------------------------------------------------------------
@@ -1676,64 +4517,70 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2 WHERE t1.a = t2.a;
Hash Join
Hash Cond: (t1.a = t2.a)
-> Append
- -> Seq Scan on prt1_p1 t1
- -> Seq Scan on prt1_p2 t1_1
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
-> Hash
-> Append
-> Seq Scan on prt4_n_p1 t2
-> Seq Scan on prt4_n_p2 t2_1
-> Seq Scan on prt4_n_p3 t2_2
-(11 rows)
+(13 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2, prt2 t3 WHERE t1.a = t2.a and t1.a = t3.b;
- QUERY PLAN
---------------------------------------------------------
+ QUERY PLAN
+----------------------------------------------------------
Hash Join
- Hash Cond: (t2.a = t1.a)
+ Hash Cond: (t3.b = t1.a)
-> Append
- -> Seq Scan on prt4_n_p1 t2
- -> Seq Scan on prt4_n_p2 t2_1
- -> Seq Scan on prt4_n_p3 t2_2
+ -> Seq Scan on prt2_p0 t3
+ -> Seq Scan on prt2_p1 t3_1
+ -> Seq Scan on prt2_p2 t3_2
+ -> Seq Scan on prt2_p3 t3_3
+ -> Seq Scan on prt2_p4 t3_4
+ -> Seq Scan on prt2_p5 t3_5
-> Hash
- -> Append
- -> Hash Join
- Hash Cond: (t1.a = t3.b)
- -> Seq Scan on prt1_p1 t1
- -> Hash
- -> Seq Scan on prt2_p1 t3
- -> Hash Join
- Hash Cond: (t1_1.a = t3_1.b)
- -> Seq Scan on prt1_p2 t1_1
- -> Hash
- -> Seq Scan on prt2_p2 t3_1
- -> Hash Join
- Hash Cond: (t1_2.a = t3_2.b)
- -> Seq Scan on prt1_p3 t1_2
- -> Hash
- -> Seq Scan on prt2_p3 t3_2
+ -> Hash Join
+ Hash Cond: (t1.a = t2.a)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Append
+ -> Seq Scan on prt4_n_p1 t2
+ -> Seq Scan on prt4_n_p2 t2_1
+ -> Seq Scan on prt4_n_p3 t2_2
(23 rows)
-- partition-wise join can not be applied if there are no equi-join conditions
-- between partition keys
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON (t1.a < t2.b);
- QUERY PLAN
----------------------------------------------------------
+ QUERY PLAN
+--------------------------------------------
Nested Loop Left Join
+ Join Filter: (t1.a < t2.b)
-> Append
- -> Seq Scan on prt1_p1 t1
- -> Seq Scan on prt1_p2 t1_1
- -> Seq Scan on prt1_p3 t1_2
- -> Append
- -> Index Scan using iprt2_p1_b on prt2_p1 t2
- Index Cond: (t1.a < b)
- -> Index Scan using iprt2_p2_b on prt2_p2 t2_1
- Index Cond: (t1.a < b)
- -> Index Scan using iprt2_p3_b on prt2_p3 t2_2
- Index Cond: (t1.a < b)
-(12 rows)
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Materialize
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+(16 rows)
-- equi-join with join condition on partial keys does not qualify for
-- partition-wise join
@@ -1819,16 +4666,17 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 JOIN prt2_n t2 ON (t1.c = t2.c) JOI
-> Seq Scan on prt2_n_p2 t2_1
-> Hash
-> Hash Join
- Hash Cond: (t3.c = (t1.c)::text)
+ Hash Cond: ((t3.c)::text = (t1.c)::text)
-> Append
- -> Seq Scan on plt1_p1 t3
- -> Seq Scan on plt1_p2 t3_1
- -> Seq Scan on plt1_p3 t3_2
+ -> Seq Scan on plt1_p4 t3
+ -> Seq Scan on plt1_p1 t3_1
+ -> Seq Scan on plt1_p2 t3_2
+ -> Seq Scan on plt1_p3 t3_3
-> Hash
-> Append
-> Seq Scan on prt1_n_p1 t1
-> Seq Scan on prt1_n_p2 t1_1
-(16 rows)
+(17 rows)
-- partition-wise join can not be applied for a join between list and range
-- partitioned table
@@ -1839,12 +4687,14 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 FULL JOIN prt1 t2 ON (t1.c = t2.c);
Hash Full Join
Hash Cond: ((t2.c)::text = (t1.c)::text)
-> Append
- -> Seq Scan on prt1_p1 t2
- -> Seq Scan on prt1_p2 t2_1
- -> Seq Scan on prt1_p3 t2_2
+ -> Seq Scan on prt1_p0 t2
+ -> Seq Scan on prt1_p1 t2_1
+ -> Seq Scan on prt1_p2 t2_2
+ -> Seq Scan on prt1_p3 t2_3
+ -> Seq Scan on prt1_p4 t2_4
-> Hash
-> Append
-> Seq Scan on prt1_n_p1 t1
-> Seq Scan on prt1_n_p2 t1_1
-(10 rows)
+(12 rows)
diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql
index cd54ea0..8f099a8 100644
--- a/src/test/regress/sql/partition_join.sql
+++ b/src/test/regress/sql/partition_join.sql
@@ -10,25 +10,43 @@ SET enable_partition_wise_join to true;
-- partitioned by a single column
--
CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_p0 PARTITION OF prt1 FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250);
CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600);
CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500);
+CREATE TABLE prt1_p4 PARTITION OF prt1 FOR VALUES FROM (600) TO (800);
INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(-250, 0, 2) i;
+INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(600, 799, 2) i;
+CREATE INDEX iprt1_p0_a on prt1_p0(a);
CREATE INDEX iprt1_p1_a on prt1_p1(a);
CREATE INDEX iprt1_p2_a on prt1_p2(a);
CREATE INDEX iprt1_p3_a on prt1_p3(a);
+CREATE INDEX iprt1_p4_a on prt1_p4(a);
ANALYZE prt1;
+-- prt2 have missing starting MINVALUE to -250 range and
+-- extra bounds from 800 to MAXVALUE
CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_p0 PARTITION OF prt2 FOR VALUES FROM (-250) TO (0);
CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250);
CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500);
CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (MAXVALUE);
INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(-250, 0, 3) i;
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 799, 3) i;
+CREATE INDEX iprt2_p0_b on prt2_p0(b);
CREATE INDEX iprt2_p1_b on prt2_p1(b);
CREATE INDEX iprt2_p2_b on prt2_p2(b);
CREATE INDEX iprt2_p3_b on prt2_p3(b);
+CREATE INDEX iprt2_p4_b on prt2_p4(b);
ANALYZE prt2;
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
+
-- inner join
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
@@ -77,11 +95,19 @@ EXPLAIN (COSTS OFF)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
+EXPLAIN (COSTS OFF)
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+
-- Anti-join with aggregates
EXPLAIN (COSTS OFF)
SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b);
SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b);
+EXPLAIN (COSTS OFF)
+SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a);
+SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a);
+
-- lateral reference
EXPLAIN (COSTS OFF)
SELECT * FROM prt1 t1 LEFT JOIN LATERAL
@@ -103,20 +129,30 @@ SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
-- partitioned by expression
--
CREATE TABLE prt1_e (a int, b int, c int) PARTITION BY RANGE(((a + b)/2));
+CREATE TABLE prt1_e_p0 PARTITION OF prt1_e FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES FROM (0) TO (250);
CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES FROM (250) TO (500);
CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt1_e_p4 PARTITION OF prt1_e FOR VALUES FROM (600) TO (MAXVALUE);
INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(600, 799, 2) i;
+CREATE INDEX iprt1_e_p0_ab2 on prt1_e_p1(((a+b)/2));
CREATE INDEX iprt1_e_p1_ab2 on prt1_e_p1(((a+b)/2));
CREATE INDEX iprt1_e_p2_ab2 on prt1_e_p2(((a+b)/2));
CREATE INDEX iprt1_e_p3_ab2 on prt1_e_p3(((a+b)/2));
+CREATE INDEX iprt1_e_p4_ab2 on prt1_e_p1(((a+b)/2));
ANALYZE prt1_e;
CREATE TABLE prt2_e (a int, b int, c int) PARTITION BY RANGE(((b + a)/2));
+CREATE TABLE prt2_e_p0 PARTITION OF prt2_e FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES FROM (0) TO (250);
CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES FROM (250) TO (500);
CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt2_e_p4 PARTITION OF prt2_e FOR VALUES FROM (600) TO (MAXVALUE);
INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(600, 799, 3) i;
ANALYZE prt2_e;
EXPLAIN (COSTS OFF)
@@ -168,6 +204,104 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
RESET enable_hashjoin;
RESET enable_nestloop;
+-- Add an extra partition to prt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+DROP TABLE prt2_p4;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (800);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (800) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999, 3) i;
+ANALYZE prt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- TODO: below query is picking partition-wise-join which is not expected
+-- also generating wrong output, need to remove comment after fix
+-- right join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a;
+SET enable_partition_wise_join to false;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a;
+SET enable_partition_wise_join to true;
+
+-- TODO: below query is picking partition-wise-join which is not expected
+-- also generating wrong output, need to remove comment after fix
+-- full join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE coalesce(t1.b, 0) + coalesce(t2.a, 0) = 0 ORDER BY t1.a, t2.a;
+SET enable_partition_wise_join to false;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE coalesce(t1.b, 0) + coalesce(t2.a, 0) = 0 ORDER BY t1.a, t2.a;
+SET enable_partition_wise_join to true;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- TODO: below query is generating wrong output, need to remove comment after fix
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+SET enable_partition_wise_join to false;
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+SET enable_partition_wise_join to true;
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE prt2_p4;
+DROP TABLE prt2_p5;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (700);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (700) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999, 3) i;
+ANALYZE prt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
--
-- partitioned by multiple columns
--
@@ -192,28 +326,79 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1
--
-- tests for list partitioned tables.
--
-CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
-ANALYZE plt1;
+\set part_mod 17
+\set cond_mod 47
+\set num_rows 500
+
+CREATE TABLE plt1 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0001','0002','0003');
+CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0008','0009');
+CREATE TABLE plt1_p4 PARTITION OF plt1 FOR VALUES IN ('0000','0010');
+INSERT INTO plt1 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (7, 11, 12, 13, 14, 15, 16);
+ANALYSE plt1;
+
+-- plt2 have missing starting 0001, additional 0007, missing ending 0010
+-- and additional 0011 and 0012 bounds
+CREATE TABLE plt2 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002','0003');
+CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0007','0008','0009');
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000','0011','0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 10, 13, 14, 15, 16);
+ANALYSE plt2;
+
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
-CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i;
-ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
--
-- list partitioned by expression
--
CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
-CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0002', '0003');
+CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0004', '0005', '0006');
+CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0008', '0009');
+CREATE TABLE plt1_e_p4 PARTITION OF plt1_e FOR VALUES IN ('0000');
+INSERT INTO plt1_e SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 7, 10, 11, 12, 13, 14, 15, 16);
ANALYZE plt1_e;
-- test partition matching with N-way join
@@ -221,6 +406,175 @@ EXPLAIN (COSTS OFF)
SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
+-- Add an extra partition to plt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (13, 14);
+ANALYZE plt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+
+-- full join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt2_p5;
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0001','0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (1, 13, 14);
+ANALYZE plt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- partition have a NULL on one side, Partition-wise join is possible with
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- in this case NULL will be treated as addition partition bounds.
+DROP TABLE plt2_p5;
+DROP TABLE plt2_p4;
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000',NULL,'0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, case when i % :part_mod = 11 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (0,11,12);
+ANALYZE plt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- partition have a NULL on both side with different partition bounds w.r.t other side
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt1_p3;
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN (NULL,'0008','0009');
+INSERT INTO plt1 SELECT i, i % :cond_mod, case when i % :part_mod = 7 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (7,8,9);
+ANALYZE plt1;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
-- joins where one of the relations is proven empty
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a = 1 AND t1.a = 2;
@@ -260,27 +614,27 @@ INSERT INTO prt2_l SELECT i % 25, i, to_char(i % 4, 'FM0000') FROM generate_seri
ANALYZE prt2_l;
-- inner join, qual covering only top-level partitions
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
-- left join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t1.b = 0 ORDER BY t1.a, t2.b;
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t1.b = 0 ORDER BY t1.a, t2.b;
-- right join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.a = 0 ORDER BY t1.a, t2.b;
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.a = 0 ORDER BY t1.a, t2.b;
-- full join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.b = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.a = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.b = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.a = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
-- lateral partition-wise join
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT * FROM prt1_l t1 LEFT JOIN LATERAL
(SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.b AS t3b, least(t1.a,t2.a,t3.b) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.c = t3.c)) ss
ON t1.a = ss.t2a AND t1.c = ss.t2c WHERE t1.b = 0 ORDER BY t1.a;
@@ -289,7 +643,7 @@ SELECT * FROM prt1_l t1 LEFT JOIN LATERAL
ON t1.a = ss.t2a AND t1.c = ss.t2c WHERE t1.b = 0 ORDER BY t1.a;
-- join with one side empty
-EXPLAIN(COSTS OFF)
+EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c;
--
I have applied v27 patches and tested feature. Also tried to reduce
regression diff with the
existing partition_join.sql by adding new partition instead of changing
original partition bounds.Attached WIP patch have a server crash and some wrong output which need to
be fixed. I have
commented these issue in patch itself, Please take a look and let me know if
it need more
changes.
I have fixed the issues which were marked as TODOs in the attached
patches. Also, I have included your test change patch in my series of
patches. Are there any other issues you have commented out?
--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
Attachments:
0011-Modify-bound-comparision-functions-to-accept-members.patchtext/x-patch; charset=US-ASCII; name=0011-Modify-bound-comparision-functions-to-accept-members.patchDownload
From b1888b037362acbc83fa00feba58624ecf62a6b9 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>
Date: Thu, 6 Jul 2017 14:15:22 +0530
Subject: [PATCH 11/13] Modify bound comparision functions to accept members
of PartitionKey
Functions partition_bound_cmp(), partition_rbound_cmp() and
partition_rbound_datum_cmp() are required to merge partition bounds
from joining relations. While doing so, we do not have access to the
PartitionKey of either relations. So, modify these functions to accept
only required members of PartitionKey so that the functions can be
reused for merging bounds.
Ashutosh Bapat.
---
src/backend/catalog/partition.c | 76 ++++++++++++++++++++++-----------------
1 file changed, 44 insertions(+), 32 deletions(-)
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 96a64ce..d42e1b5 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -126,15 +126,17 @@ static List *generate_partition_qual(Relation rel);
static PartitionRangeBound *make_one_range_bound(PartitionKey key, int index,
List *datums, bool lower);
-static int32 partition_rbound_cmp(PartitionKey key,
- Datum *datums1, PartitionRangeDatumKind *kind1,
- bool lower1, PartitionRangeBound *b2);
-static int32 partition_rbound_datum_cmp(PartitionKey key,
- Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
+static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, Datum *datums1,
+ PartitionRangeDatumKind *kind1, bool lower1,
+ PartitionRangeBound *b2);
+static int32 partition_rbound_datum_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, Datum *rb_datums,
+ PartitionRangeDatumKind *rb_kind,
Datum *tuple_datums);
-static int32 partition_bound_cmp(PartitionKey key,
- PartitionBoundInfo boundinfo,
+static int32 partition_bound_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, PartitionBoundInfo boundinfo,
int offset, void *probe, bool probe_is_bound);
static int partition_bound_bsearch(PartitionKey key,
PartitionBoundInfo boundinfo,
@@ -719,8 +721,9 @@ check_new_partition_bound(char *relname, Relation parent,
* First check if the resulting range would be empty with
* specified lower and upper bounds
*/
- if (partition_rbound_cmp(key, lower->datums, lower->kind, true,
- upper) >= 0)
+ if (partition_rbound_cmp(key->partnatts, key->partsupfunc,
+ key->partcollation, lower->datums,
+ lower->kind, true, upper) >= 0)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -771,9 +774,11 @@ check_new_partition_bound(char *relname, Relation parent,
{
int32 cmpval;
- cmpval = partition_bound_cmp(key, boundinfo,
- offset + 1, upper,
- true);
+ cmpval = partition_bound_cmp(key->partnatts,
+ key->partsupfunc,
+ key->partcollation,
+ boundinfo, offset + 1,
+ upper, true);
if (cmpval < 0)
{
/*
@@ -2138,7 +2143,9 @@ qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
PartitionKey key = (PartitionKey) arg;
- return partition_rbound_cmp(key, b1->datums, b1->kind, b1->lower, b2);
+ return partition_rbound_cmp(key->partnatts, key->partsupfunc,
+ key->partcollation, b1->datums, b1->kind,
+ b1->lower, b2);
}
/*
@@ -2155,7 +2162,7 @@ qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
* two contiguous partitions.
*/
static int32
-partition_rbound_cmp(PartitionKey key,
+partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation,
Datum *datums1, PartitionRangeDatumKind *kind1,
bool lower1, PartitionRangeBound *b2)
{
@@ -2165,7 +2172,7 @@ partition_rbound_cmp(PartitionKey key,
PartitionRangeDatumKind *kind2 = b2->kind;
bool lower2 = b2->lower;
- for (i = 0; i < key->partnatts; i++)
+ for (i = 0; i < partnatts; i++)
{
/*
* First, handle cases where the column is unbounded, which should not
@@ -2186,8 +2193,8 @@ partition_rbound_cmp(PartitionKey key,
*/
break;
- cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
- key->partcollation[i],
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
+ partcollation[i],
datums1[i],
datums2[i]));
if (cmpval != 0)
@@ -2213,22 +2220,23 @@ partition_rbound_cmp(PartitionKey key,
* is <, =, or > partition key of tuple (tuple_datums)
*/
static int32
-partition_rbound_datum_cmp(PartitionKey key,
- Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
+partition_rbound_datum_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, Datum *rb_datums,
+ PartitionRangeDatumKind *rb_kind,
Datum *tuple_datums)
{
int i;
int32 cmpval = -1;
- for (i = 0; i < key->partnatts; i++)
+ for (i = 0; i < partnatts; i++)
{
if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
return -1;
else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
return 1;
- cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
- key->partcollation[i],
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
+ partcollation[i],
rb_datums[i],
tuple_datums[i]));
if (cmpval != 0)
@@ -2245,17 +2253,18 @@ partition_rbound_datum_cmp(PartitionKey key,
* specified in *probe.
*/
static int32
-partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo,
- int offset, void *probe, bool probe_is_bound)
+partition_bound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation,
+ PartitionBoundInfo boundinfo, int offset, void *probe,
+ bool probe_is_bound)
{
Datum *bound_datums = boundinfo->datums[offset];
int32 cmpval = -1;
- switch (key->strategy)
+ switch (boundinfo->strategy)
{
case PARTITION_STRATEGY_LIST:
- cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
- key->partcollation[0],
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
+ partcollation[0],
bound_datums[0],
*(Datum *) probe));
break;
@@ -2273,12 +2282,14 @@ partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo,
*/
bool lower = boundinfo->indexes[offset] < 0;
- cmpval = partition_rbound_cmp(key,
- bound_datums, kind, lower,
+ cmpval = partition_rbound_cmp(partnatts, partsupfunc,
+ partcollation, bound_datums,
+ kind, lower,
(PartitionRangeBound *) probe);
}
else
- cmpval = partition_rbound_datum_cmp(key,
+ cmpval = partition_rbound_datum_cmp(partnatts, partsupfunc,
+ partcollation,
bound_datums, kind,
(Datum *) probe);
break;
@@ -2286,7 +2297,7 @@ partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo,
default:
elog(ERROR, "unexpected partition strategy: %d",
- (int) key->strategy);
+ (int) boundinfo->strategy);
}
return cmpval;
@@ -2320,7 +2331,8 @@ partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo,
int32 cmpval;
mid = (lo + hi + 1) / 2;
- cmpval = partition_bound_cmp(key, boundinfo, mid, probe,
+ cmpval = partition_bound_cmp(key->partnatts, key->partsupfunc,
+ key->partcollation, boundinfo, mid, probe,
probe_is_bound);
if (cmpval <= 0)
{
--
1.7.9.5
0012-WIP-Partition-wise-join-for-1-1-1-0-0-1-partition-ma.patchtext/x-patch; charset=US-ASCII; name=0012-WIP-Partition-wise-join-for-1-1-1-0-0-1-partition-ma.patchDownload
From f7c7b3817d733a78dad03ee89f86179f686d7c06 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>
Date: Wed, 9 Aug 2017 12:30:34 +0530
Subject: [PATCH 12/13] WIP Partition-wise join for 1:1, 1:0, 0:1 partition
matching.
Earlier version of partition-wise join implementation allowed
partition-wise join between two relations with exactly same partition
bounds. This commit allows partition-wise join to be applied under
following conditions
1. the partition bounds of joining relations are such that rows from
given partition on one side join can join with rows from maximum one
partition on the other side i.e. bounds of a given partition on one
side match/overlap with those of maximum one partition on the other
side. If the mapping happens to be m:n where m > 1 or n > 1, we have
to gang multiple partition relations together into a single relation.
This means that we have to add simple relations during join
processing, something which is not supported right now. ALso, in such
a case, different pairs of joining relations can produce different
partition bounds for the same join relation, which again is not
supported right now.
2. For every partition on outer side that can contribute to the result
of an OUTER side, there exists at least one (taken along with item 1,
it means exactly one) matching partition on the inner side. To
support partition-wise join when the inner matching partition doesn't
exist, we have to add a dummy base relation corresponding to the
non-existent inner partition. We don't have support add base relations
during join processing.
This commit is not complete yet.
Ashutosh Bapat.
---
src/backend/catalog/partition.c | 1258 +++++++++++++++++++++++++++++++++
src/backend/optimizer/path/joinrels.c | 77 +-
src/backend/optimizer/util/relnode.c | 42 +-
src/include/catalog/partition.h | 6 +
4 files changed, 1352 insertions(+), 31 deletions(-)
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index d42e1b5..94e48bd 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -141,6 +141,38 @@ static int32 partition_bound_cmp(int partnatts, FmgrInfo *partsupfunc,
static int partition_bound_bsearch(PartitionKey key,
PartitionBoundInfo boundinfo,
void *probe, bool probe_is_bound, bool *is_equal);
+static PartitionBoundInfo partition_range_bounds_merge(int partnatts,
+ FmgrInfo *supfuncs, Oid *collations,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2);
+static PartitionBoundInfo partition_list_bounds_merge(int partnatts,
+ FmgrInfo *partsupfunc, Oid *collations,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2);
+static void generate_matching_part_pairs(int *mergemap1, int npart1,
+ int *mergemap2, int nparts2, JoinType jointype,
+ int nparts, List **parts1, List **parts2);
+static PartitionBoundInfo build_merged_partition_bounds(char strategy,
+ List *merged_datums, List *merged_indexes,
+ List *merged_contents, int null_index);
+static int map_and_merge_partitions(int *partmap1, int *mergemap1, int index1,
+ int *partmap2, int *mergemap2, int index2,
+ int *next_index);
+static int32 partition_range_bound_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *collations, PartitionRangeBound *bound1,
+ PartitionRangeBound *bound2);
+static int partition_range_cmp(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, PartitionRangeBound *lower_bound1,
+ PartitionRangeBound *upper_bound1,
+ PartitionRangeBound *lower_bound2,
+ PartitionRangeBound *upper_bound2, bool *overlap);
+static bool partition_range_merge_next_lb(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, Datum *next_lb_datums,
+ PartitionRangeDatumKind *next_lb_kind,
+ List **merged_datums, List **merged_kinds,
+ List **merged_indexes);
/*
* RelationBuildPartitionDesc
@@ -2348,3 +2380,1229 @@ partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo,
return lo;
}
+
+/*
+ * Merge the given partition bounds.
+ *
+ * If given partition bounds can not be merged, return NULL.
+ *
+ * The function also returns two lists of partition indexes one for each of the
+ * joining relations. Both the lists contain the same number of elements. The
+ * partition indexes at the same positions in the list indicate partitions from
+ * each side to be joined and their position corresponds to the index of
+ * partition to which the results of the child-join belong in the partitioned
+ * join.
+ */
+extern PartitionBoundInfo
+partition_bounds_merge(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2)
+{
+ PartitionBoundInfo merged_bounds;
+ char strategy;
+
+ /* Bail out if partitioning strategies are different. */
+ if (boundinfo1->strategy != boundinfo2->strategy)
+ return NULL;
+
+ *parts1 = NIL;
+ *parts2 = NIL;
+ strategy = boundinfo1->strategy;
+ if (strategy == PARTITION_STRATEGY_LIST)
+ merged_bounds = partition_list_bounds_merge(partnatts, partsupfunc,
+ partcollation, boundinfo1,
+ nparts1, boundinfo2,
+ nparts2, jointype, parts1,
+ parts2);
+ else if (strategy == PARTITION_STRATEGY_RANGE)
+ merged_bounds = partition_range_bounds_merge(partnatts, partsupfunc,
+ partcollation, boundinfo1,
+ nparts1, boundinfo2,
+ nparts2, jointype, parts1,
+ parts2);
+ else
+ elog(ERROR, "unexpected partition strategy: %d", strategy);
+
+ Assert(merged_bounds || (*parts1 == NIL && *parts2 == NIL));
+ return merged_bounds;
+}
+
+/*
+ * partition_get_range_bounds
+ *
+ * Given the index of lower bound in datums array, return lower and upper
+ * bounds and the index of the partition with that lower bound.
+ */
+static int
+partition_get_range_bounds(PartitionBoundInfo bi, int lb_index,
+ PartitionRangeBound *lower,
+ PartitionRangeBound *upper)
+{
+ int part_index;
+
+ /* A lower bound should have at least one more bound after it. */
+ Assert(lb_index < bi->ndatums - 1);
+
+ /* The lower bound should correspond to a valid partition. */
+ part_index = bi->indexes[lb_index + 1];
+ Assert(part_index >= 0);
+
+ lower->kind = bi->kind[lb_index];
+ lower->datums = bi->datums[lb_index];
+ lower->lower = true;
+ upper->kind = bi->kind[lb_index + 1];
+ upper->datums = bi->datums[lb_index + 1];
+ upper->lower = false;
+
+ return part_index;
+}
+
+/*
+ * partition_range_get_next_lb_index
+ *
+ * Given the index of lower bound in datums array return the
+ * index of lower bound of the next partition. When the given index corresponds
+ * to the last partition, return number of datums (ndatums).
+ */
+static int
+partition_range_get_next_lb_index(PartitionBoundInfo bi, int lb_index)
+{
+ /* A lower bound should have at least one more bound after it. */
+ Assert(lb_index < bi->ndatums - 1);
+
+ /* The partition index corresponding to the upper bound should be valid. */
+ Assert(bi->indexes[lb_index + 1] >= 0);
+
+ /*
+ * If there are no bounds left beyond the upper bound, we have reached the
+ * last partition.
+ */
+ if (lb_index + 2 < bi->ndatums)
+ {
+ /*
+ * If the bound next to the upper bound corresponds to no partition,
+ * that's the next lower bound of the next partition. Otherwise, the
+ * current upper bound is the lower bound of the next partition.
+ */
+ if (bi->indexes[lb_index + 2] < 0)
+ return lb_index + 2;
+ else
+ return lb_index + 1;
+ }
+ else
+ return bi->ndatums;
+}
+
+static int32
+partition_range_bound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *collations,
+ PartitionRangeBound *bound1,
+ PartitionRangeBound *bound2)
+{
+ return partition_rbound_cmp(partnatts, partsupfunc, collations,
+ bound1->datums, bound1->kind, bound1->lower,
+ bound2);
+}
+
+/*
+ * partition_range_cmp
+ *
+ * Compare the bounds of two range partitions and return <, = or > 0, if the
+ * first partition's upper bound is lower than, equal to or higher than the
+ * second partition's upper bound resp.
+ *
+ * Also, set overlaps to true, if the ranges overlap, otherwise set it to
+ * false.
+ */
+static int
+partition_range_cmp(int partnatts, FmgrInfo *supfuncs, Oid *collations,
+ PartitionRangeBound *lower_bound1,
+ PartitionRangeBound *upper_bound1,
+ PartitionRangeBound *lower_bound2,
+ PartitionRangeBound *upper_bound2, bool *overlap)
+{
+ /*
+ * Compare upper bound of the first partition with the lower bound of the
+ * second and vice-versa. If lower bound is higher than the upper bound,
+ * the partitions are not overlapping. All other cases indicate overlapping
+ * partitions.
+ * TODO: Add a testcase which has lower and upper bound matching exactly.
+ * Lower bound is inclusive and upper bound is exclusive, so even if the
+ * datums match, the bounds do not match exactly.
+ */
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ lower_bound1, upper_bound2) > 0)
+ {
+ *overlap = false;
+ return 1;
+ }
+ else if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ lower_bound2, upper_bound1) > 0)
+ {
+ *overlap = false;
+ return -1;
+ }
+ else
+ {
+ *overlap = true;
+ return partition_range_bound_cmp(partnatts, supfuncs, collations,
+ upper_bound1, upper_bound2);
+ }
+}
+
+/*
+ * partition_range_merge
+ *
+ * Merge the partition bounds of given two partitions such that the join
+ * between the given two partitions fits merged bounds.
+ *
+ * "merged_upper" will be set to one of the given upper bounds and
+ * "merged_lower" will be set to one of the given lower bounds.
+ */
+static void
+partition_range_merge(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, JoinType jointype,
+ PartitionRangeBound *left_lb,
+ PartitionRangeBound *left_ub,
+ PartitionRangeBound *right_lb,
+ PartitionRangeBound *right_ub,
+ PartitionRangeBound **merged_lb,
+ PartitionRangeBound **merged_ub)
+{
+ /*
+ * An outer join will have all the rows from the outer side, so merged
+ * bounds will be same as the outer bounds. An inner join will have rows
+ * that fit both the bounds, thus lower merged bound will be higher of two
+ * lower bounds and upper merged bound will be lower of the two upper
+ * bounds.
+ */
+ switch (jointype)
+ {
+ case JOIN_LEFT:
+ case JOIN_ANTI:
+ *merged_ub = left_ub;
+ *merged_lb = left_lb;
+ break;
+
+ case JOIN_RIGHT:
+ *merged_ub = right_ub;
+ *merged_lb = right_lb;
+ break;
+
+ case JOIN_INNER:
+ case JOIN_SEMI:
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_ub, right_ub) < 0)
+ *merged_ub = left_ub;
+ else
+ *merged_ub = right_ub;
+
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_lb, right_lb) > 0)
+ *merged_lb = left_lb;
+ else
+ *merged_lb = right_lb;
+ break;
+
+ case JOIN_FULL:
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_ub, right_ub) > 0)
+ *merged_ub = left_ub;
+ else
+ *merged_ub = right_ub;
+
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_lb, right_lb) < 0)
+ *merged_lb = left_lb;
+ else
+ *merged_lb = right_lb;
+ break;
+
+ default:
+ elog(ERROR, "Unexpected join type %d", jointype);
+ }
+
+ return;
+}
+
+/*
+ * Add the lower bound of the next range to the list of bounds, if the lower
+ * bound is higher or equal to the previous upper bound. If successful return
+ * true, otherwise false.
+ */
+static bool
+partition_range_merge_next_lb(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, Datum *next_lb_datums,
+ PartitionRangeDatumKind *next_lb_kind,
+ List **merged_datums, List **merged_kinds,
+ List **merged_indexes)
+{
+ int cmpval;
+
+ if (!*merged_datums)
+ {
+ Assert(!*merged_kinds && !*merged_indexes);
+ cmpval = 1;
+ }
+ else
+ {
+ PartitionRangeBound prev_ub;
+
+ prev_ub.datums = llast(*merged_datums);
+ prev_ub.kind = llast(*merged_kinds);
+ prev_ub.lower = false;
+
+ /*
+ * TODO: explain why do we pass lower to be false for the next lower
+ * bound.
+ */
+ cmpval = partition_rbound_cmp(partnatts, supfuncs, collations,
+ next_lb_datums, next_lb_kind, false,
+ &prev_ub);
+ }
+
+ /*
+ * The lower bound is lower than the last upper bound, thus does not fit
+ * the bounds created so far and hence can not be merged with the existing
+ * bounds.
+ */
+ if (cmpval < 0)
+ return false;
+
+ /*
+ * Add bounds of the new merged partition. If the next lower bound is
+ * higher than the last upper bound, add new range with index
+ * corresponding to the lower bound as -1. If the merged lower bound
+ * is same as the last merged upper bound, the last upper bound will be
+ * reused as the lower bound of the next range.
+ */
+ if (cmpval > 0)
+ {
+ *merged_datums = lappend(*merged_datums, next_lb_datums);
+ *merged_kinds = lappend(*merged_kinds, next_lb_kind);
+ *merged_indexes = lappend_int(*merged_indexes, -1);
+ }
+
+ return true;
+}
+
+/*
+ * Merge given two range partition bounds.
+ *
+ * Work horse function for partition_bounds_merge() for range partitioned
+ * tables.
+ *
+ * TODO: for an anti-join, the caller is supposed to send the outer relation as
+ * left relation. May be we should rename left and right as inner and outer. We
+ * don't need to handle RIGHT joins in this function, so renaming them as outer
+ * and inner is fine.
+ */
+static PartitionBoundInfo
+partition_range_bounds_merge(int partnatts, FmgrInfo *supfuncs, Oid *collations,
+ PartitionBoundInfo left_bi, int left_nparts,
+ PartitionBoundInfo right_bi, int right_nparts,
+ JoinType jointype, List **left_parts, List **right_parts)
+{
+ int *left_pmap;
+ int *left_mmap;
+ int *right_pmap;
+ int *right_mmap;
+ int cnt1;
+ int cnt2;
+ int left_part;
+ int right_part;
+ PartitionBoundInfo merged_bounds = NULL;
+ bool merged = true; /* By default we ranges are merge-able. */
+ int left_lb_index;
+ int right_lb_index;
+ int next_index;
+ int cmpval;
+ List *merged_datums = NIL;
+ List *merged_indexes = NIL;
+ List *merged_kinds = NIL;
+
+ Assert(left_bi->strategy == right_bi->strategy &&
+ left_bi->strategy == PARTITION_STRATEGY_RANGE);
+
+ *left_parts = NIL;
+ *right_parts = NIL;
+
+ /* Allocate and initialize partition maps. */
+ left_pmap = (int *) palloc(sizeof(int) * left_nparts);
+ left_mmap = (int *) palloc(sizeof(int) * left_nparts);
+ right_pmap = (int *) palloc(sizeof(int) * right_nparts);
+ right_mmap = (int *) palloc(sizeof(int) * right_nparts);
+
+ for (cnt1 = 0; cnt1 < left_nparts; cnt1++)
+ {
+ left_pmap[cnt1] = -1;
+ left_mmap[cnt1] = -1;
+ }
+ for (cnt2 = 0; cnt2 < right_nparts; cnt2++)
+ {
+ right_pmap[cnt2] = -1;
+ right_mmap[cnt2] = -1;
+ }
+
+ left_lb_index = 0;
+ right_lb_index = 0;
+ next_index = 0;
+ while (left_lb_index < left_bi->ndatums &&
+ right_lb_index < right_bi->ndatums)
+ {
+ PartitionRangeBound left_lb;
+ PartitionRangeBound left_ub;
+ PartitionRangeBound right_lb;
+ PartitionRangeBound right_ub;
+ PartitionRangeBound *merged_lb = NULL;
+ PartitionRangeBound *merged_ub = NULL;
+ int merged_index = -1;
+ bool overlap;
+
+ /* Get the range bounds of the next partition. */
+ left_part = partition_get_range_bounds(left_bi, left_lb_index,
+ &left_lb, &left_ub);
+ right_part = partition_get_range_bounds(right_bi, right_lb_index,
+ &right_lb, &right_ub);
+
+ cmpval = partition_range_cmp(partnatts, supfuncs, collations,
+ &left_lb, &left_ub, &right_lb, &right_ub,
+ &overlap);
+
+ if (overlap)
+ {
+ /* Overlapping ranges, try merging. */
+ partition_range_merge(partnatts, supfuncs, collations, jointype,
+ &left_lb, &left_ub, &right_lb, &right_ub,
+ &merged_lb, &merged_ub);
+ merged_index = map_and_merge_partitions(left_pmap, left_mmap,
+ left_part, right_pmap,
+ right_mmap, right_part,
+ &next_index);
+
+ if (merged_index < 0)
+ {
+ merged = false;
+ break;
+ }
+ }
+
+ if (cmpval == 0)
+ {
+ Assert(overlap);
+
+ /* Move to the next pair of partitions. */
+ left_lb_index = partition_range_get_next_lb_index(left_bi,
+ left_lb_index);
+ right_lb_index = partition_range_get_next_lb_index(right_bi,
+ right_lb_index);
+ }
+ else if (cmpval < 0)
+ {
+ /*
+ * If the partition on the left was not mapped to any partition on
+ * the right. Any row from that partition will not have a matching
+ * row from the other relation. So the partition will be part of
+ * the join if it's an anti-join or the left side is the outer
+ * side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_SEMI ||
+ jointype == JOIN_RIGHT)
+ {
+ /* Nothing to do. */
+ }
+ else if (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI)
+ {
+ if (left_mmap[left_part] < 0)
+ {
+ left_mmap[left_part] = next_index++;
+ merged_index = left_mmap[left_part];
+ merged_lb = &left_lb;
+ merged_ub = &left_ub;
+ }
+ }
+ else
+ {
+ /* Don't know what to do with other join types. Bail out. */
+ merged = false;
+ }
+
+ /* Move to the next partition on the left side. */
+ left_lb_index = partition_range_get_next_lb_index(left_bi,
+ left_lb_index);
+ }
+ else
+ {
+ Assert(cmpval > 0);
+
+ /*
+ * If the partition on the right was not mapped to any partition on
+ * the left. Any row from that partition will not have a matching
+ * row from the other relation. So the partition will be part of
+ * the join if the right side is the outer side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_SEMI ||
+ jointype == JOIN_LEFT || jointype == JOIN_ANTI)
+ {
+ /* Nothing to do. */
+ }
+ else if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ if (right_mmap[right_part] < 0)
+ {
+ right_mmap[right_part] = next_index++;
+ merged_index = right_mmap[right_part];
+ merged_lb = &right_lb;
+ merged_ub = &right_ub;
+ }
+ }
+ else
+ {
+ /* Don't know what to do with other join types. Bail out. */
+ merged = false;
+ }
+
+ /* Move to the next partition on the right side. */
+ right_lb_index = partition_range_get_next_lb_index(right_bi,
+ right_lb_index);
+ }
+
+ if (!merged)
+ break;
+
+ /* A skipped partition is not added to merged bounds. */
+ if (merged_index < 0)
+ continue;
+
+ /*
+ * We have a valid partition index for the next partition of join. The
+ * partition should have valid range.
+ */
+ Assert(merged_lb && merged_ub);
+
+ /* Try merging merged lower bound with the last upper bound. */
+ merged = partition_range_merge_next_lb(partnatts, supfuncs,
+ collations, merged_lb->datums,
+ merged_lb->kind, &merged_datums,
+ &merged_kinds, &merged_indexes);
+ if (merged)
+ {
+ /* Add upper bound with the merged partition index. */
+ merged_datums = lappend(merged_datums, merged_ub->datums);
+ merged_kinds = lappend(merged_kinds, merged_ub->kind);
+ merged_indexes = lappend_int(merged_indexes, merged_index);
+ }
+ else
+ break;
+ }
+
+ /*
+ * We will run the above loop till we exhaust ranges of at least one side
+ * unless we failed to merge the ranges.
+ */
+ Assert (!merged || (left_lb_index >= left_bi->ndatums ||
+ right_lb_index >= right_bi->ndatums));
+
+ /*
+ * Handle any remaining partition bounds. If remaining partitions fall on
+ * the inner side of the join, none of the rows in those partition are
+ * going to be joined with any row on the outer side and hence those
+ * partitions will not be part of the join result. Hence only consider the
+ * remaining partitions on the outer side of the join.
+ */
+ if (merged &&
+ ((left_lb_index < left_bi->ndatums &&
+ (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI)) ||
+ (right_lb_index < right_bi->ndatums &&
+ (jointype == JOIN_RIGHT || jointype == JOIN_FULL))))
+ {
+ int bound_index = -1;
+ PartitionBoundInfo rem_bi = NULL;
+ int *mmap = NULL;
+ int part_index;
+ PartitionRangeBound rem_lb;
+ PartitionRangeBound rem_ub;
+
+ if (left_lb_index < left_bi->ndatums)
+ {
+ Assert(jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI);
+ bound_index = left_lb_index;
+ rem_bi = left_bi;
+ mmap = left_mmap;
+ }
+ else if (right_lb_index < right_bi->ndatums)
+ {
+ Assert(jointype == JOIN_RIGHT || jointype == JOIN_FULL);
+ bound_index = right_lb_index;
+ rem_bi = right_bi;
+ mmap = right_mmap;
+ }
+ Assert((bound_index >= 0 && bound_index < rem_bi->ndatums) &&
+ rem_bi && mmap);
+
+ /*
+ * If the partition corresponding to this lower bound has been already
+ * mapped to a merged partition, don't need to add it again. This may
+ * happen if the range of the last partition on the inner side overlaps
+ * with this partition's range and has upper bound lesser than upper
+ * bound of this partition's range.
+ */
+ part_index = partition_get_range_bounds(rem_bi, bound_index, &rem_lb,
+ &rem_ub);
+ Assert(part_index >= 0);
+ if (mmap[part_index] >= 0)
+ bound_index = partition_range_get_next_lb_index(rem_bi, bound_index);
+
+ /*
+ * Merge lower bound of the next range with the upper bound of last
+ * range.
+ */
+ if (bound_index < rem_bi->ndatums)
+ merged = partition_range_merge_next_lb(partnatts, supfuncs,
+ collations,
+ rem_bi->datums[bound_index],
+ rem_bi->kind[bound_index],
+ &merged_datums,
+ &merged_kinds,
+ &merged_indexes);
+
+ /*
+ * Rest of the bounds correspond to valid ranges so add them after
+ * remapping their partitions as required.
+ */
+ for (bound_index++; merged && bound_index < rem_bi->ndatums;
+ bound_index++)
+ {
+ Datum *datums = rem_bi->datums[bound_index];
+ int index = rem_bi->indexes[bound_index];
+ int part_index;
+
+ /*
+ * Add lower bounds with partition index -1 and assign a new
+ * partition index to the upper bounds.
+ */
+ if (index < 0)
+ part_index = index;
+ else
+ {
+ if (mmap[index] < 0)
+ mmap[index] = next_index++;
+ part_index = mmap[index];
+ }
+
+ merged_indexes = lappend_int(merged_indexes, part_index);
+ merged_datums = lappend(merged_datums, datums);
+ merged_kinds = lappend(merged_kinds,
+ rem_bi->kind[bound_index]);
+ }
+ }
+
+ /* Create PartitionBoundInfo */
+ if (merged)
+ {
+ /* Use maps to match partition from the joining relations. */
+ generate_matching_part_pairs(left_mmap, left_nparts, right_mmap,
+ right_nparts, jointype, next_index,
+ left_parts, right_parts);
+
+ /* Craft a PartitionBoundInfo to return. */
+ if (*left_parts && *right_parts)
+ {
+ Assert(list_length(*left_parts) == list_length(*right_parts));
+ Assert(list_length(*left_parts) == next_index);
+ merged_bounds = build_merged_partition_bounds(left_bi->strategy,
+ merged_datums,
+ merged_indexes,
+ merged_kinds,
+ -1);
+ }
+ }
+
+ list_free(merged_datums);
+ list_free(merged_indexes);
+ pfree(left_pmap);
+ pfree(right_pmap);
+ pfree(left_mmap);
+ pfree(right_mmap);
+
+ /* Free any memory we used in this function. */
+ return merged_bounds;
+}
+
+/*
+ * partition_bounds_merge()'s arm for list partitioned tables.
+ *
+ * The function builds the maps of matching partitions from either relation. It
+ * builds the list of partition key values that may appear in the join result
+ * alongwith the list of indexes of partitions of join to which those values
+ * belong. It then crafts a PartitionBoundInfo structure representing the
+ * partition bounds of the join result.
+ */
+static PartitionBoundInfo
+partition_list_bounds_merge(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, PartitionBoundInfo left_bi,
+ int left_nparts, PartitionBoundInfo right_bi,
+ int right_nparts, JoinType jointype, List **left_parts,
+ List **right_parts)
+{
+ int *left_pmap; /* left to right partition map */
+ int *left_mmap; /* left to merged partition map */
+ int *right_pmap; /* right to left partition map */
+ int *right_mmap; /* right to merged partition map */
+ int cntl;
+ int cntr;
+ bool merged = true;
+ List *merged_datums = NIL;
+ List *merged_indexes = NIL;
+ int next_index = 0;
+ int null_index;
+ PartitionBoundInfo merged_bounds = NULL;
+ int *left_indexes = left_bi->indexes;
+ int *right_indexes = right_bi->indexes;
+ int left_ni = left_bi->null_index;
+ int right_ni = right_bi->null_index;
+
+ Assert(left_bi->strategy == right_bi->strategy &&
+ left_bi->strategy == PARTITION_STRATEGY_LIST);
+
+ /* List partitions do not require unbounded ranges. */
+ Assert(!left_bi->kind && !right_bi->kind);
+
+ /* Allocate and initialize partition maps. */
+ left_pmap = (int *) palloc(sizeof(int) * left_nparts);
+ left_mmap = (int *) palloc(sizeof(int) * left_nparts);
+ right_pmap = (int *) palloc(sizeof(int) * right_nparts);
+ right_mmap = (int *) palloc(sizeof(int) * right_nparts);
+
+ /* Initialize partition maps. */
+ for (cntl = 0; cntl < left_nparts; cntl++)
+ {
+ left_pmap[cntl] = -1;
+ left_mmap[cntl] = -1;
+ }
+ for (cntr = 0; cntr < right_nparts; cntr++)
+ {
+ right_pmap[cntr] = -1;
+ right_mmap[cntr] = -1;
+ }
+
+ cntl = cntr = 0;
+ while (cntl < left_bi->ndatums && cntr < right_bi->ndatums)
+ {
+ Datum *ldatums = left_bi->datums[cntl];
+ Datum *rdatums = right_bi->datums[cntr];
+ int l_index = left_indexes[cntl];
+ int r_index = right_indexes[cntr];
+ int cmpval;
+ int merged_index;
+ Datum *merged_datum;
+
+ /* Every list datum should map to a valid partition index. */
+ Assert(l_index >= 0 && r_index >= 0);
+
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
+ partcollation[0], ldatums[0],
+ rdatums[0]));
+ if (cmpval == 0)
+ {
+ /*
+ * Try matching partitions containing the matching datums. If
+ * successful, add the datum to the merged bounds with index of
+ * merged partition containing it.
+ */
+ merged_datum = ldatums;
+ merged_index = map_and_merge_partitions(left_pmap, left_mmap, l_index,
+ right_pmap, right_mmap, r_index,
+ &next_index);
+
+ if (merged_index < 0)
+ {
+ merged = false;
+ break;
+ }
+
+ /* Move to the next pair of bounds. */
+ cntl++;
+ cntr++;
+ }
+ else if (cmpval < 0)
+ {
+ /*
+ * This list datum is present in the left side but not the right
+ * side. So it will appear in the join when the left side is outer
+ * side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_RIGHT ||
+ jointype == JOIN_SEMI)
+ merged_index = -1;
+ else if (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI)
+ {
+ if (left_mmap[l_index] < 0)
+ left_mmap[l_index] = next_index++;
+ merged_index = left_mmap[l_index];
+ merged_datum = ldatums;
+ }
+ else
+ {
+ /* Don't know what to do with other join types. */
+ merged = false;
+ break;
+ }
+
+ /* Move to the next datum on the left side. */
+ cntl++;
+ }
+ else
+ {
+ Assert(cmpval > 0);
+
+ /*
+ * This list datum is present in the right side but not the left
+ * side. So it will appear in the join when the right side is outer
+ * side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_LEFT ||
+ jointype == JOIN_SEMI || jointype == JOIN_ANTI)
+ merged_index = -1;
+ else if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ /*
+ * Every list value on the outer side will appear in the
+ * join. Find the merged partition to which this value
+ * belongs.
+ */
+ if (right_mmap[r_index] < 0)
+ right_mmap[r_index] = next_index++;
+ merged_index = right_mmap[r_index];
+ merged_datum = rdatums;
+ }
+ else
+ {
+ /* Don't know what to do with other join types. */
+ merged = false;
+ break;
+ }
+
+ /* Move to the next datum on the right side. */
+ cntr++;
+ }
+
+ /*
+ * Add the datum with appropriate index in the list of datums, if the
+ * rows containing that datum are deemed to be part of the join.
+ */
+ if (merged_index >= 0)
+ {
+ merged_indexes = lappend_int(merged_indexes, merged_index);
+ merged_datums = lappend(merged_datums, merged_datum);
+ }
+ }
+
+ /*
+ * If merge is unsuccessful, bail out without any further processing.
+ * That leaks the memory allocated in this function. So, try not to leak
+ * memory.
+ */
+ if (!merged)
+ goto merge_failed;
+
+ /* We should have exhausted datums on at least one side. */
+ Assert(cntr >= right_bi->ndatums || cntl >= left_bi->ndatums);
+
+ /*
+ * Add any remaining list values on the outer side, assigning partition
+ * indexes if required.
+ */
+ if (jointype == JOIN_LEFT || jointype == JOIN_FULL || jointype == JOIN_ANTI)
+ {
+ for (;cntl < left_bi->ndatums; cntl++)
+ {
+ Datum *ldatums = left_bi->datums[cntl];
+ int l_index = left_indexes[cntl];
+
+ if (left_mmap[l_index] < 0)
+ left_mmap[l_index] = next_index++;
+ merged_indexes = lappend_int(merged_indexes, left_mmap[l_index]);
+ merged_datums = lappend(merged_datums, ldatums);
+ }
+ }
+
+ if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ for (;cntr < right_bi->ndatums; cntr++)
+ {
+ Datum *rdatums = right_bi->datums[cntr];
+ int r_index = right_indexes[cntr];
+
+ if (right_mmap[r_index] < 0)
+ right_mmap[r_index] = next_index++;
+ merged_indexes = lappend_int(merged_indexes, right_mmap[r_index]);
+ merged_datums = lappend(merged_datums, rdatums);
+ }
+ }
+
+ /*
+ * Merge NULL partitions if any. Find the index of merged partition to
+ * which the NULL values belong in the join result. We can eliminate a NULL
+ * partition when it appears only in the inner relation.
+ */
+ if (!partition_bound_accepts_nulls(left_bi) &&
+ !partition_bound_accepts_nulls(right_bi))
+ null_index = -1;
+ else if (partition_bound_accepts_nulls(left_bi) &&
+ !partition_bound_accepts_nulls(right_bi))
+ {
+ if (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI)
+ {
+ if (left_mmap[left_ni] < 0)
+ left_mmap[left_ni] = next_index++;
+ null_index = left_mmap[left_ni];
+ }
+ else
+ null_index = -1;
+ }
+ else if (!partition_bound_accepts_nulls(left_bi) &&
+ partition_bound_accepts_nulls(right_bi))
+ {
+ if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ if (right_mmap[right_ni] < 0)
+ right_mmap[right_ni] = next_index++;
+ null_index = right_mmap[right_ni];
+ }
+ else
+ null_index = -1;
+ }
+ else
+ {
+ /* Both the relations have NULL partitions, try merging them. */
+ null_index = map_and_merge_partitions(left_pmap, left_mmap,
+ left_ni, right_pmap,
+ right_mmap, right_ni,
+ &next_index);
+ if (null_index < 0)
+ merged = false;
+ }
+
+ /* If successful build the output structures. */
+ if (merged)
+ {
+
+ /* Use maps to match partition from the joining relations. */
+ generate_matching_part_pairs(left_mmap, left_nparts, right_mmap,
+ right_nparts, jointype, next_index,
+ left_parts, right_parts);
+ /* Craft a PartitionBoundInfo to return. */
+ if (*left_parts && *right_parts)
+ {
+ Assert(list_length(*left_parts) == list_length(*right_parts));
+ Assert(list_length(*left_parts) == next_index);
+ merged_bounds = build_merged_partition_bounds(left_bi->strategy,
+ merged_datums,
+ merged_indexes, NIL,
+ null_index);
+ }
+ }
+
+merge_failed:
+ list_free(merged_datums);
+ list_free(merged_indexes);
+ pfree(left_pmap);
+ pfree(right_pmap);
+ pfree(left_mmap);
+ pfree(right_mmap);
+
+ return merged_bounds;
+}
+
+/*
+ * map_and_merge_partitions
+ *
+ * If the two given partitions (given by index1 and index2 resp.) are already
+ * mapped to each other return the index of corresponding partition in the
+ * merged set of partitions. If they do not have a merged partition associated
+ * with them, assign a new merged partition index. If the partitions are
+ * already mapped and their mapped partitions are different from each other,
+ * they can not be merged, so return -1.
+ *
+ * partmap1[i] gives the partition of relation 2 which matches ith partition of
+ * relation 1. Similarly for partmap2.
+ *
+ * mergemap1[i] gives the partition in the merged set to which ith partition of
+ * relation 1 maps to. Similarly for mergemap2.
+ *
+ * index1 and index2 are the indexes of matching partition from respective
+ * relations.
+ *
+ * *next_index is used and incremented when the given partitions require a new
+ * merged partition.
+ */
+static int
+map_and_merge_partitions(int *partmap1, int *mergemap1, int index1,
+ int *partmap2, int *mergemap2, int index2,
+ int *next_index)
+{
+ int merged_index;
+
+ /*
+ * If both the partitions are not mapped to each other, update the
+ * maps.
+ */
+ if (partmap1[index1] < 0 && partmap2[index2] < 0)
+ {
+ partmap1[index1] = index2;
+ partmap2[index2] = index1;
+ }
+
+ /*
+ * If the given to partitions map to each other, find the corresponding
+ * merged partition index .
+ */
+ if (partmap1[index1] == index2 && partmap2[index2] == index1)
+ {
+ /*
+ * If both the partitions are mapped to the same merged partition, get
+ * the index of merged partition.
+ */
+ if (mergemap1[index1] == mergemap2[index2])
+ {
+ merged_index = mergemap1[index1];
+
+ /*
+ * If the given two partitions do not have a merged partition
+ * associated with them, allocate a new merged partition.
+ */
+ if (merged_index < 0)
+ {
+ merged_index = *next_index;
+ *next_index = *next_index + 1;
+ mergemap1[index1] = merged_index;
+ mergemap2[index2] = merged_index;
+ }
+ }
+
+ /*
+ * If partition from one relation was mapped to a merged partition but
+ * not the partition from the other relation, map the same merged
+ * partition to the partition from other relation, since matching
+ * partitions map to the same merged partition.
+ */
+ else if (mergemap1[index1] >= 0 && mergemap2[index2] < 0)
+ {
+ mergemap2[index2] = mergemap1[index1];
+ merged_index = mergemap1[index1];
+ }
+ else if (mergemap1[index1] < 0 && mergemap2[index2] >= 0)
+ {
+ mergemap1[index1] = mergemap2[index2];
+ merged_index = mergemap2[index2];
+ }
+ else
+ {
+ Assert(mergemap1[index1] != mergemap2[index2] &&
+ mergemap1[index1] >= 0 && mergemap2[index2] >= 0);
+
+ /*
+ * Both the partitions map to different merged partitions. This
+ * means that multiple partitions from one relation matches to one
+ * partition from the other relation. Partition-wise join does not
+ * handle this case right now, since it requires ganging multiple
+ * partitions together (into one RelOptInfo).
+ */
+ merged_index = -1;
+ }
+ }
+ else
+ {
+ /*
+ * Multiple partitions from one relation map to one partition from the
+ * other relation. Partition-wise join does not handle this case right
+ * now, since it requires ganging multiple partitions together (into
+ * one RelOptInfo).
+ */
+ merged_index = -1;
+ }
+
+ return merged_index;
+}
+
+/*
+ * generate_matching_part_pairs
+ *
+ * Given the merged partition to which partition on either side of join map,
+ * produce the list pairs of partitions which when joined produce the merged
+ * partitions in the order of merged partition indexes.
+ *
+ * If successful, the pairs of partitions are returned as two separate lists
+ * one for each side. Otherwise, those lists will be set to NIL.
+ *
+ * TODO: rename the sides as outer and inner. You may not need to support
+ * JOIN_RIGHT, since we won't see that type here.
+ */
+static void
+generate_matching_part_pairs(int *mergemap1, int nparts1, int *mergemap2,
+ int nparts2, JoinType jointype, int nparts,
+ List **parts1, List **parts2)
+{
+ bool merged = true;
+ int **matching_parts;
+ int cnt1;
+ int cnt2;
+
+ matching_parts = (int **) palloc(sizeof(int *) * 2);
+ matching_parts[0] = (int *) palloc(sizeof(int) * nparts);
+ matching_parts[1] = (int *) palloc(sizeof(int) * nparts);
+
+ *parts1 = NIL;
+ *parts2 = NIL;
+
+ for (cnt1 = 0; cnt1 < nparts; cnt1++)
+ {
+ matching_parts[0][cnt1] = -1;
+ matching_parts[1][cnt1] = -1;
+ }
+
+ /* Set pairs of matching partitions. */
+ for (cnt1 = 0; cnt1 < nparts1; cnt1++)
+ {
+ if (mergemap1[cnt1] >= 0)
+ {
+ Assert(mergemap1[cnt1] < nparts);
+ matching_parts[0][mergemap1[cnt1]] = cnt1;
+ }
+ }
+ for (cnt2 = 0; cnt2 < nparts2; cnt2++)
+ {
+ if (mergemap2[cnt2] >= 0)
+ {
+ Assert(mergemap2[cnt2] < nparts);
+ matching_parts[1][mergemap2[cnt2]] = cnt2;
+ }
+ }
+
+ /*
+ * 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 (cnt1 = 0; cnt1 < nparts; cnt1++)
+ {
+ int part1 = matching_parts[0][cnt1];
+ int part2 = matching_parts[1][cnt1];
+
+ /* 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_RIGHT:
+ Assert(part2 >= 0);
+ if (part1 < 0)
+ merged = false;
+ break;
+
+ case JOIN_FULL:
+ if (part1 < 0 || part2 < 0)
+ merged = false;
+ break;
+
+ default:
+ /* We do not know what to do in this case. Bail out. */
+ merged = false;
+ }
+
+ if (!merged)
+ break;
+
+ *parts1 = lappend_int(*parts1, part1);
+ *parts2 = lappend_int(*parts2, part2);
+ }
+
+ pfree(matching_parts[0]);
+ pfree(matching_parts[1]);
+ pfree(matching_parts);
+
+ if (!merged)
+ {
+ list_free(*parts1);
+ list_free(*parts2);
+ *parts1 = NIL;
+ *parts2 = NIL;
+ }
+}
+
+static PartitionBoundInfo
+build_merged_partition_bounds(char strategy, List *merged_datums,
+ List *merged_indexes, List *merged_kinds,
+ int null_index)
+{
+ int cnt;
+ PartitionBoundInfo merged_bounds;
+ ListCell *lc;
+
+ /* We expect the same number of elements in datums and indexes lists. */
+ Assert(list_length(merged_datums) == list_length(merged_indexes));
+
+ merged_bounds = (PartitionBoundInfo) palloc(sizeof(PartitionBoundInfoData));
+ merged_bounds->strategy = strategy;
+ merged_bounds->ndatums = list_length(merged_datums);
+
+ if (strategy == PARTITION_STRATEGY_RANGE)
+ {
+ Assert(list_length(merged_datums) == list_length(merged_kinds));
+ merged_bounds->kind = (PartitionRangeDatumKind **) palloc(sizeof(PartitionRangeDatumKind *) *
+ list_length(merged_kinds));
+ cnt = 0;
+ foreach(lc, merged_kinds)
+ merged_bounds->kind[cnt++] = lfirst(lc);
+
+ /* There are ndatums+1 indexes in case of range partitions */
+ merged_indexes = lappend_int(merged_indexes, -1);
+ }
+ else
+ merged_bounds->kind = NULL;
+
+ cnt = 0;
+ merged_bounds->datums = (Datum **) palloc(sizeof(Datum *) *
+ list_length(merged_datums));
+ foreach(lc, merged_datums)
+ merged_bounds->datums[cnt++] = lfirst(lc);
+
+ merged_bounds->indexes = (int *) palloc(sizeof(int) *
+ list_length(merged_indexes));
+ cnt = 0;
+ foreach(lc, merged_indexes)
+ merged_bounds->indexes[cnt++] = lfirst_int(lc);
+
+ merged_bounds->null_index = null_index;
+
+ return merged_bounds;
+}
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index e3ac4de..2e39c55 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1308,8 +1308,13 @@ try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
RelOptInfo *joinrel, SpecialJoinInfo *parent_sjinfo,
List *parent_restrictlist)
{
- int nparts;
int cnt_parts;
+ PartitionScheme part_scheme;
+ PartitionBoundInfo join_boundinfo;
+ List *parts1;
+ List *parts2;
+ ListCell *lc1;
+ ListCell *lc2;
/* Guard against stack overflow due to overly deep partition hierarchy. */
check_stack_depth();
@@ -1359,39 +1364,54 @@ try_partition_wise_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;
/*
- * Since we allow partition-wise join only when the partition bounds of
- * the joining relations exactly match, the partition bounds of the join
- * should match those of the joining relations.
+ * Get the list of matching partitions from both sides of the join. While
+ * doing so, we also build the partition bounds of the join relation,
+ * which should match the bounds calculated for other pairs. TODO: why
+ * should every pair result in the same partition bounds?
*/
- Assert(partition_bounds_equal(joinrel->part_scheme->partnatts,
- joinrel->part_scheme->parttyplen,
- joinrel->part_scheme->parttypbyval,
- joinrel->boundinfo, rel1->boundinfo));
- Assert(partition_bounds_equal(joinrel->part_scheme->partnatts,
- joinrel->part_scheme->parttyplen,
- joinrel->part_scheme->parttypbyval,
- joinrel->boundinfo, rel2->boundinfo));
-
- nparts = joinrel->nparts;
-
+ join_boundinfo = partition_bounds_merge(part_scheme->partnatts,
+ part_scheme->partsupfunc,
+ part_scheme->parttypcoll,
+ rel1->boundinfo, rel1->nparts,
+ rel2->boundinfo, rel2->nparts,
+ parent_sjinfo->jointype,
+ &parts1, &parts2);
+
+ Assert(join_boundinfo);
+ Assert(partition_bounds_equal(part_scheme->partnatts,
+ part_scheme->parttyplen,
+ part_scheme->parttypbyval, join_boundinfo,
+ joinrel->boundinfo));
elog(DEBUG3, "join between relations %s and %s is considered for partition-wise join.",
bmsToString(rel1->relids), bmsToString(rel2->relids));
- /* Allocate space to hold child-joins RelOptInfos, if not already done. */
+ /*
+ * 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));
+
+ /* Allocate space for hold child-joins RelOptInfos, if not already done. */
if (!joinrel->part_rels)
- joinrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) * nparts);
+ joinrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *
+ joinrel->nparts);
/*
* Create child-join relations for this partitioned join, if those don't
* exist. Add paths to child-joins for a pair of child relations
* corresponding to the given pair of parent relations.
*/
- for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+ cnt_parts = 0;
+ forboth(lc1, parts1, lc2, parts2)
{
- RelOptInfo *child_rel1 = rel1->part_rels[cnt_parts];
- RelOptInfo *child_rel2 = rel2->part_rels[cnt_parts];
+ int part1 = lfirst_int(lc1);
+ int part2 = lfirst_int(lc2);
+ RelOptInfo *child_rel1;
+ RelOptInfo *child_rel2;
SpecialJoinInfo *child_sjinfo;
List *child_restrictlist;
RelOptInfo *child_joinrel;
@@ -1399,6 +1419,10 @@ try_partition_wise_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];
+
/* We should never try to join two overlapping sets of rels. */
Assert(!bms_overlap(child_rel1->relids, child_rel2->relids));
child_joinrelids = bms_union(child_rel1->relids, child_rel2->relids);
@@ -1431,6 +1455,15 @@ try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
joinrel->part_rels[cnt_parts] = child_joinrel;
}
+ /*
+ * 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,
@@ -1443,7 +1476,11 @@ try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
*/
try_partition_wise_join(root, child_rel1, child_rel2, child_joinrel,
child_sjinfo, child_restrictlist);
+
+ cnt_parts++;
}
+
+ Assert(cnt_parts == joinrel->nparts);
}
/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 81bc2b1..5d1992e 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -1613,6 +1613,9 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
int partnatts;
int cnt;
PartitionScheme part_scheme;
+ PartitionBoundInfo join_boundinfo;
+ List *parts1;
+ List *parts2;
/* Nothing to do if partition-wise join technique is disabled. */
if (!enable_partition_wise_join)
@@ -1653,17 +1656,26 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
REL_HAS_ALL_PART_PROPS(inner_rel));
/*
- * For now, our partition matching algorithm can match partitions only
- * when the partition bounds of the joining relations are exactly same.
- * So, bail out otherwise.
+ * Every pair of joining relations would yield the same partition bounds
+ * for a given join (TODO: why?) so we compute the bounds only the first
+ * time. Then for every pair we find the pairs of matching partitions from
+ * the joining relations and join those. TODO: Needs a better explanation
+ * of why is this true. TODO: Also there is no reason to have
+ * part_indexes1 and part_indexes2 pulled here just to be freed up later.
+ * So, we might want to do something better.
*/
- if (outer_rel->nparts != inner_rel->nparts ||
- !partition_bounds_equal(part_scheme->partnatts,
- part_scheme->parttyplen,
- part_scheme->parttypbyval,
- outer_rel->boundinfo, inner_rel->boundinfo))
+ join_boundinfo = partition_bounds_merge(part_scheme->partnatts,
+ part_scheme->partsupfunc,
+ part_scheme->parttypcoll,
+ outer_rel->boundinfo,
+ outer_rel->nparts,
+ inner_rel->boundinfo,
+ inner_rel->nparts,
+ jointype, &parts1, &parts2);
+ if (!join_boundinfo)
{
Assert(!IS_PARTITIONED_REL(joinrel));
+ Assert(!parts1 && !parts2);
return;
}
@@ -1676,13 +1688,16 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
!joinrel->nullable_partexprs && !joinrel->part_rels &&
!joinrel->boundinfo);
+ Assert(list_length(parts1) == list_length(parts2));
+
/*
* Join relation is partitioned using same partitioning scheme as the
- * joining relations and has same bounds.
+ * joining relations. It will have as many partitions as the pairs of
+ * matching partitions we found.
*/
joinrel->part_scheme = part_scheme;
- joinrel->boundinfo = outer_rel->boundinfo;
- joinrel->nparts = outer_rel->nparts;
+ joinrel->nparts = list_length(parts1);
+ joinrel->boundinfo = join_boundinfo;
partnatts = joinrel->part_scheme->partnatts;
/*
@@ -1803,4 +1818,9 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
joinrel->nullable_partexprs[cnt] = nullable_partexprs;
}
}
+
+ /* TODO: OR we could actually create the child-join relations here.*/
+ list_free(parts1);
+ list_free(parts2);
+
}
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index 2283c67..056a4f9 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -99,4 +99,10 @@ extern int get_partition_for_tuple(PartitionDispatch *pd,
EState *estate,
PartitionDispatchData **failed_at,
TupleTableSlot **failed_slot);
+extern PartitionBoundInfo partition_bounds_merge(int partnatts,
+ FmgrInfo *supfuncs, Oid *collations,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2);
+
#endif /* PARTITION_H */
--
1.7.9.5
0013-Tests-for-0-1-1-1-and-1-0-partition-matching.patchtext/x-patch; charset=US-ASCII; name=0013-Tests-for-0-1-1-1-and-1-0-partition-matching.patchDownload
From 753c8981723c2ab5d9dce57257d5e1a7c85fa7b3 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>
Date: Tue, 5 Sep 2017 09:51:41 +0530
Subject: [PATCH 13/13] Tests for 0:1, 1:1 and 1:0 partition matching
Rajkumar Raghuvanshi and Ashutosh Bapat.
---
src/test/regress/expected/partition_join.out | 4102 +++++++++++++++++++++-----
src/test/regress/sql/partition_join.sql | 367 ++-
2 files changed, 3752 insertions(+), 717 deletions(-)
diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out
index f9543c8..151d70e 100644
--- a/src/test/regress/expected/partition_join.out
+++ b/src/test/regress/expected/partition_join.out
@@ -8,105 +8,152 @@ SET enable_partition_wise_join to true;
-- partitioned by a single column
--
CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_p0 PARTITION OF prt1 FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250);
CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600);
CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500);
-INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0;
+CREATE TABLE prt1_p4 PARTITION OF prt1 FOR VALUES FROM (600) TO (800);
+INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 2 = 0;
+CREATE INDEX iprt1_p0_a on prt1_p0(a);
CREATE INDEX iprt1_p1_a on prt1_p1(a);
CREATE INDEX iprt1_p2_a on prt1_p2(a);
CREATE INDEX iprt1_p3_a on prt1_p3(a);
+CREATE INDEX iprt1_p4_a on prt1_p4(a);
ANALYZE prt1;
+-- prt2 have missing starting MINVALUE to -250 range and
+-- extra bounds from 800 to MAXVALUE
CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_p0 PARTITION OF prt2 FOR VALUES FROM (-250) TO (0);
CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250);
CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500);
CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600);
-INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (MAXVALUE);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 3 = 0;
+CREATE INDEX iprt2_p0_b on prt2_p0(b);
CREATE INDEX iprt2_p1_b on prt2_p1(b);
CREATE INDEX iprt2_p2_b on prt2_p2(b);
CREATE INDEX iprt2_p3_b on prt2_p3(b);
+CREATE INDEX iprt2_p4_b on prt2_p4(b);
ANALYZE prt2;
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
-- inner join
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
- QUERY PLAN
---------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------
Sort
Sort Key: t1.a
-> Append
-> Hash Join
Hash Cond: (t2.b = t1.a)
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
-> Hash
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Hash Join
Hash Cond: (t2_1.b = t1_1.a)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Hash
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Hash Join
Hash Cond: (t2_2.b = t1_2.a)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p2 t2_2
-> Hash
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
-(21 rows)
+ -> Nested Loop
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ -> Hash Join
+ Hash Cond: (t2_4.b = t1_4.a)
+ -> Seq Scan on prt2_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(32 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
- a | c | b | c
------+------+-----+------
- 0 | 0000 | 0 | 0000
- 150 | 0150 | 150 | 0150
- 300 | 0300 | 300 | 0300
- 450 | 0450 | 450 | 0450
-(4 rows)
+ a | c | b | c
+------+-------+------+-------
+ -150 | -0150 | -150 | -0150
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150
+ 300 | 0300 | 300 | 0300
+ 450 | 0450 | 450 | 0450
+ 600 | 0600 | 600 | 0600
+ 750 | 0750 | 750 | 0750
+(7 rows)
-- left outer join, with whole-row reference
EXPLAIN (COSTS OFF)
SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
- QUERY PLAN
---------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------
Sort
Sort Key: t1.a, t2.b
-> Result
-> Append
-> Hash Right Join
Hash Cond: (t2.b = t1.a)
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
-> Hash
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Hash Right Join
Hash Cond: (t2_1.b = t1_1.a)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Hash
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Hash Right Join
Hash Cond: (t2_2.b = t1_2.a)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p2 t2_2
-> Hash
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
-(22 rows)
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (t1_3.a = b)
+ -> Hash Right Join
+ Hash Cond: (t2_4.b = t1_4.a)
+ -> Seq Scan on prt2_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(33 rows)
SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
- t1 | t2
---------------+--------------
- (0,0,0000) | (0,0,0000)
- (50,0,0050) |
- (100,0,0100) |
- (150,0,0150) | (0,150,0150)
- (200,0,0200) |
- (250,0,0250) |
- (300,0,0300) | (0,300,0300)
- (350,0,0350) |
- (400,0,0400) |
- (450,0,0450) | (0,450,0450)
- (500,0,0500) |
- (550,0,0550) |
-(12 rows)
+ t1 | t2
+----------------+----------------
+ (-250,0,-0250) |
+ (-200,0,-0200) |
+ (-150,0,-0150) | (0,-150,-0150)
+ (-100,0,-0100) |
+ (-50,0,-0050) |
+ (0,0,0000) | (0,0,0000)
+ (50,0,0050) |
+ (100,0,0100) |
+ (150,0,0150) | (0,150,0150)
+ (200,0,0200) |
+ (250,0,0250) |
+ (300,0,0300) | (0,300,0300)
+ (350,0,0350) |
+ (400,0,0400) |
+ (450,0,0450) | (0,450,0450)
+ (500,0,0500) |
+ (550,0,0550) |
+ (600,0,0600) | (0,600,0600)
+ (650,0,0650) |
+ (700,0,0700) |
+ (750,0,0750) | (0,750,0750)
+(21 rows)
-- right outer join
EXPLAIN (COSTS OFF)
@@ -119,35 +166,53 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHE
-> Append
-> Hash Right Join
Hash Cond: (t1.a = t2.b)
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
-> Hash
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
Filter: (a = 0)
-> Hash Right Join
Hash Cond: (t1_1.a = t2_1.b)
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Hash
+ -> Seq Scan on prt2_p1 t2_1
+ Filter: (a = 0)
+ -> Hash Right Join
+ Hash Cond: (t1_2.a = t2_2.b)
+ -> Seq Scan on prt1_p2 t1_2
-> Hash
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p2 t2_2
Filter: (a = 0)
-> Nested Loop Left Join
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p3 t2_3
Filter: (a = 0)
- -> Index Scan using iprt1_p3_a on prt1_p3 t1_2
- Index Cond: (a = t2_2.b)
-(21 rows)
+ -> Index Scan using iprt1_p3_a on prt1_p3 t1_3
+ Index Cond: (a = t2_3.b)
+ -> Hash Right Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+(33 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b;
- a | c | b | c
------+------+-----+------
- 0 | 0000 | 0 | 0000
- 150 | 0150 | 150 | 0150
- 300 | 0300 | 300 | 0300
- 450 | 0450 | 450 | 0450
- | | 75 | 0075
- | | 225 | 0225
- | | 375 | 0375
- | | 525 | 0525
-(8 rows)
+ a | c | b | c
+------+-------+------+-------
+ -150 | -0150 | -150 | -0150
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150
+ 300 | 0300 | 300 | 0300
+ 450 | 0450 | 450 | 0450
+ 600 | 0600 | 600 | 0600
+ 750 | 0750 | 750 | 0750
+ | | -225 | -0225
+ | | -75 | -0075
+ | | 75 | 0075
+ | | 225 | 0225
+ | | 375 | 0375
+ | | 525 | 0525
+ | | 675 | 0675
+(14 rows)
-- full outer join
EXPLAIN (COSTS OFF)
@@ -155,9 +220,16 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.b = 0) t1 FULL
QUERY PLAN
--------------------------------------------------
Sort
- Sort Key: prt1_p1.a, prt2_p1.b
+ Sort Key: prt1_p0.a, prt2_p0.b
-> Append
-> Hash Full Join
+ Hash Cond: (prt1_p0.a = prt2_p0.b)
+ -> Seq Scan on prt1_p0
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p0
+ Filter: (a = 0)
+ -> Hash Full Join
Hash Cond: (prt1_p1.a = prt2_p1.b)
-> Seq Scan on prt1_p1
Filter: (b = 0)
@@ -178,28 +250,47 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.b = 0) t1 FULL
-> Hash
-> Seq Scan on prt2_p3
Filter: (a = 0)
-(24 rows)
+ -> Hash Full Join
+ Hash Cond: (prt1_p4.a = prt2_p4.b)
+ -> Seq Scan on prt1_p4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4
+ Filter: (a = 0)
+(38 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
- a | c | b | c
------+------+-----+------
- 0 | 0000 | 0 | 0000
- 50 | 0050 | |
- 100 | 0100 | |
- 150 | 0150 | 150 | 0150
- 200 | 0200 | |
- 250 | 0250 | |
- 300 | 0300 | 300 | 0300
- 350 | 0350 | |
- 400 | 0400 | |
- 450 | 0450 | 450 | 0450
- 500 | 0500 | |
- 550 | 0550 | |
- | | 75 | 0075
- | | 225 | 0225
- | | 375 | 0375
- | | 525 | 0525
-(16 rows)
+ a | c | b | c
+------+-------+------+-------
+ -250 | -0250 | |
+ -200 | -0200 | |
+ -150 | -0150 | -150 | -0150
+ -100 | -0100 | |
+ -50 | -0050 | |
+ 0 | 0000 | 0 | 0000
+ 50 | 0050 | |
+ 100 | 0100 | |
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 | |
+ 250 | 0250 | |
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 | |
+ 400 | 0400 | |
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 | |
+ 550 | 0550 | |
+ 600 | 0600 | 600 | 0600
+ 650 | 0650 | |
+ 700 | 0700 | |
+ 750 | 0750 | 750 | 0750
+ | | -225 | -0225
+ | | -75 | -0075
+ | | 75 | 0075
+ | | 225 | 0225
+ | | 375 | 0375
+ | | 525 | 0525
+ | | 675 | 0675
+(28 rows)
-- Cases with non-nullable expressions in subquery results;
-- make sure these go to null as expected
@@ -208,9 +299,17 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0)
QUERY PLAN
------------------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, prt2_p1.b
+ Sort Key: prt1_p0.a, prt2_p0.b
-> Append
-> Hash Full Join
+ Hash Cond: (prt1_p0.a = prt2_p0.b)
+ Filter: (((50) = prt1_p0.a) OR ((75) = prt2_p0.b))
+ -> Seq Scan on prt1_p0
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p0
+ Filter: (a = 0)
+ -> Hash Full Join
Hash Cond: (prt1_p1.a = prt2_p1.b)
Filter: (((50) = prt1_p1.a) OR ((75) = prt2_p1.b))
-> Seq Scan on prt1_p1
@@ -234,7 +333,15 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0)
-> Hash
-> Seq Scan on prt2_p3
Filter: (a = 0)
-(27 rows)
+ -> Hash Full Join
+ Hash Cond: (prt1_p4.a = prt2_p4.b)
+ Filter: (((50) = prt1_p4.a) OR ((75) = prt2_p4.b))
+ -> Seq Scan on prt1_p4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4
+ Filter: (a = 0)
+(43 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b;
a | c | b | c
@@ -248,10 +355,17 @@ SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 W
QUERY PLAN
--------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, prt2_p1.b
+ Sort Key: prt1_p0.a, prt2_p0.b
-> Result
-> Append
-> Hash Full Join
+ Hash Cond: (prt1_p0.a = prt2_p0.b)
+ -> Seq Scan on prt1_p0
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p0
+ Filter: (a = 0)
+ -> Hash Full Join
Hash Cond: (prt1_p1.a = prt2_p1.b)
-> Seq Scan on prt1_p1
Filter: (b = 0)
@@ -272,28 +386,47 @@ SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 W
-> Hash
-> Seq Scan on prt2_p3
Filter: (a = 0)
-(25 rows)
+ -> Hash Full Join
+ Hash Cond: (prt1_p4.a = prt2_p4.b)
+ -> Seq Scan on prt1_p4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4
+ Filter: (a = 0)
+(39 rows)
SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
- a | c | phv | b | c | phv
------+------+-----+-----+------+-----
- 0 | 0000 | 25 | 0 | 0000 | 50
- 50 | 0050 | 25 | | |
- 100 | 0100 | 25 | | |
- 150 | 0150 | 25 | 150 | 0150 | 50
- 200 | 0200 | 25 | | |
- 250 | 0250 | 25 | | |
- 300 | 0300 | 25 | 300 | 0300 | 50
- 350 | 0350 | 25 | | |
- 400 | 0400 | 25 | | |
- 450 | 0450 | 25 | 450 | 0450 | 50
- 500 | 0500 | 25 | | |
- 550 | 0550 | 25 | | |
- | | | 75 | 0075 | 50
- | | | 225 | 0225 | 50
- | | | 375 | 0375 | 50
- | | | 525 | 0525 | 50
-(16 rows)
+ a | c | phv | b | c | phv
+------+-------+-----+------+-------+-----
+ -250 | -0250 | 25 | | |
+ -200 | -0200 | 25 | | |
+ -150 | -0150 | 25 | -150 | -0150 | 50
+ -100 | -0100 | 25 | | |
+ -50 | -0050 | 25 | | |
+ 0 | 0000 | 25 | 0 | 0000 | 50
+ 50 | 0050 | 25 | | |
+ 100 | 0100 | 25 | | |
+ 150 | 0150 | 25 | 150 | 0150 | 50
+ 200 | 0200 | 25 | | |
+ 250 | 0250 | 25 | | |
+ 300 | 0300 | 25 | 300 | 0300 | 50
+ 350 | 0350 | 25 | | |
+ 400 | 0400 | 25 | | |
+ 450 | 0450 | 25 | 450 | 0450 | 50
+ 500 | 0500 | 25 | | |
+ 550 | 0550 | 25 | | |
+ 600 | 0600 | 25 | 600 | 0600 | 50
+ 650 | 0650 | 25 | | |
+ 700 | 0700 | 25 | | |
+ 750 | 0750 | 25 | 750 | 0750 | 50
+ | | | -225 | -0225 | 50
+ | | | -75 | -0075 | 50
+ | | | 75 | 0075 | 50
+ | | | 225 | 0225 | 50
+ | | | 375 | 0375 | 50
+ | | | 525 | 0525 | 50
+ | | | 675 | 0675 | 50
+(28 rows)
-- Join with pruned partitions from joining relations
EXPLAIN (COSTS OFF)
@@ -323,9 +456,16 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JO
QUERY PLAN
-----------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, b
+ Sort Key: prt1_p0.a, b
-> Append
-> Hash Left Join
+ Hash Cond: (prt1_p0.a = b)
+ -> Seq Scan on prt1_p0
+ Filter: ((a < 450) AND (b = 0))
+ -> Hash
+ -> Result
+ One-Time Filter: false
+ -> Hash Left Join
Hash Cond: (prt1_p1.a = b)
-> Seq Scan on prt1_p1
Filter: ((a < 450) AND (b = 0))
@@ -339,30 +479,43 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JO
-> Hash
-> Seq Scan on prt1_p2
Filter: ((a < 450) AND (b = 0))
-(17 rows)
+(24 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
- a | c | b | c
------+------+-----+------
- 0 | 0000 | |
- 50 | 0050 | |
- 100 | 0100 | |
- 150 | 0150 | |
- 200 | 0200 | |
- 250 | 0250 | |
- 300 | 0300 | 300 | 0300
- 350 | 0350 | |
- 400 | 0400 | |
-(9 rows)
+ a | c | b | c
+------+-------+-----+------
+ -250 | -0250 | |
+ -200 | -0200 | |
+ -150 | -0150 | |
+ -100 | -0100 | |
+ -50 | -0050 | |
+ 0 | 0000 | |
+ 50 | 0050 | |
+ 100 | 0100 | |
+ 150 | 0150 | |
+ 200 | 0200 | |
+ 250 | 0250 | |
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 | |
+ 400 | 0400 | |
+(14 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b;
QUERY PLAN
------------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, b
+ Sort Key: prt1_p0.a, b
-> Append
-> Hash Full Join
+ Hash Cond: (prt1_p0.a = b)
+ Filter: ((prt1_p0.b = 0) OR (a = 0))
+ -> Seq Scan on prt1_p0
+ Filter: (a < 450)
+ -> Hash
+ -> Result
+ One-Time Filter: false
+ -> Hash Full Join
Hash Cond: (prt1_p1.a = b)
Filter: ((prt1_p1.b = 0) OR (a = 0))
-> Seq Scan on prt1_p1
@@ -386,64 +539,153 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JO
-> Hash
-> Result
One-Time Filter: false
-(27 rows)
+ -> Hash Full Join
+ Hash Cond: (prt2_p4.b = a)
+ Filter: ((b = 0) OR (prt2_p4.a = 0))
+ -> Seq Scan on prt2_p4
+ Filter: (b > 250)
+ -> Hash
+ -> Result
+ One-Time Filter: false
+(43 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b;
- a | c | b | c
------+------+-----+------
- 0 | 0000 | |
- 50 | 0050 | |
- 100 | 0100 | |
- 150 | 0150 | |
- 200 | 0200 | |
- 250 | 0250 | |
- 300 | 0300 | 300 | 0300
- 350 | 0350 | |
- 400 | 0400 | |
- | | 375 | 0375
- | | 450 | 0450
- | | 525 | 0525
-(12 rows)
+ a | c | b | c
+------+-------+-----+------
+ -250 | -0250 | |
+ -200 | -0200 | |
+ -150 | -0150 | |
+ -100 | -0100 | |
+ -50 | -0050 | |
+ 0 | 0000 | |
+ 50 | 0050 | |
+ 100 | 0100 | |
+ 150 | 0150 | |
+ 200 | 0200 | |
+ 250 | 0250 | |
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 | |
+ 400 | 0400 | |
+ | | 375 | 0375
+ | | 450 | 0450
+ | | 525 | 0525
+ | | 600 | 0600
+ | | 675 | 0675
+ | | 750 | 0750
+(20 rows)
-- Semi-join
EXPLAIN (COSTS OFF)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
- QUERY PLAN
---------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------
Sort
Sort Key: t1.a
-> Append
-> Hash Semi Join
Hash Cond: (t1.a = t2.b)
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Hash
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
Filter: (a = 0)
-> Hash Semi Join
Hash Cond: (t1_1.a = t2_1.b)
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Hash
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
Filter: (a = 0)
- -> Nested Loop Semi Join
- Join Filter: (t1_2.a = t2_2.b)
- -> Seq Scan on prt1_p3 t1_2
+ -> Hash Semi Join
+ Hash Cond: (t1_2.a = t2_2.b)
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
- -> Materialize
- -> Seq Scan on prt2_p3 t2_2
+ -> Hash
+ -> Seq Scan on prt2_p2 t2_2
Filter: (a = 0)
-(24 rows)
+ -> Nested Loop
+ -> HashAggregate
+ Group Key: t2_3.b
+ -> Seq Scan on prt2_p3 t2_3
+ Filter: (a = 0)
+ -> Index Scan using iprt1_p3_a on prt1_p3 t1_3
+ Index Cond: (a = t2_3.b)
+ Filter: (b = 0)
+ -> Hash Semi Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+(39 rows)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
- a | b | c
------+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
+ a | b | c
+------+---+-------
+ -150 | 0 | -0150
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 300 | 0 | 0300
+ 450 | 0 | 0450
+ 600 | 0 | 0600
+ 750 | 0 | 0750
+(7 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.b
+ -> Append
+ -> Hash Semi Join
+ Hash Cond: (t1.b = t2.a)
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p0 t2
+ Filter: (b = 0)
+ -> Hash Semi Join
+ Hash Cond: (t1_1.b = t2_1.a)
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p1 t2_1
+ Filter: (b = 0)
+ -> Hash Semi Join
+ Hash Cond: (t1_2.b = t2_2.a)
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p2 t2_2
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: (t1_3.b = t2_3.a)
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Seq Scan on prt1_p3 t2_3
+ Filter: (b = 0)
+ -> Hash Semi Join
+ Hash Cond: (t1_4.b = t2_4.a)
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p4 t2_4
+ Filter: (b = 0)
+(37 rows)
+
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+ a | b | c
+---+------+-------
+ 0 | -150 | -0150
+ 0 | 0 | 0000
+ 0 | 150 | 0150
+ 0 | 300 | 0300
+ 0 | 450 | 0450
+ 0 | 600 | 0600
+ 0 | 750 | 0750
+(7 rows)
-- Anti-join with aggregates
EXPLAIN (COSTS OFF)
@@ -454,27 +696,82 @@ SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS
-> Append
-> Hash Anti Join
Hash Cond: (t1.a = t2.b)
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
-> Hash
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
-> Hash Anti Join
Hash Cond: (t1_1.a = t2_1.b)
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
-> Hash
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Hash Anti Join
Hash Cond: (t1_2.a = t2_2.b)
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
-> Hash
- -> Seq Scan on prt2_p3 t2_2
-(17 rows)
+ -> Seq Scan on prt2_p2 t2_2
+ -> Hash Anti Join
+ Hash Cond: (t1_3.a = t2_3.b)
+ -> Seq Scan on prt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on prt2_p3 t2_3
+ -> Hash Anti Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+(27 rows)
SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b);
- sum | avg | sum | avg
--------+----------------------+------+---------------------
- 60000 | 300.0000000000000000 | 2400 | 12.0000000000000000
+ sum | avg | sum | avg
+-------+----------------------+------+--------------------
+ 95550 | 273.0000000000000000 | 2200 | 6.2857142857142857
(1 row)
+EXPLAIN (COSTS OFF)
+SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0;
+ QUERY PLAN
+--------------------------------------------------------------
+ Append
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p0_a on prt1_p0 t2
+ Index Cond: (a = t1.b)
+ -> Hash Anti Join
+ Hash Cond: (t1_1.b = t2_1.a)
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p1 t2_1
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2
+ Index Cond: (a = t1_2.b)
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3
+ Index Cond: (a = t1_3.b)
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p4_a on prt1_p4 t2_4
+ Index Cond: (a = t1_4.b)
+(27 rows)
+
+SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0;
+ b | c
+------+-------
+ -225 | -0225
+ -75 | -0075
+ 75 | 0075
+ 225 | 0225
+ 375 | 0375
+ 525 | 0525
+ 675 | 0675
+(7 rows)
+
-- lateral reference
EXPLAIN (COSTS OFF)
SELECT * FROM prt1 t1 LEFT JOIN LATERAL
@@ -487,49 +784,74 @@ SELECT * FROM prt1 t1 LEFT JOIN LATERAL
-> Result
-> Append
-> Nested Loop Left Join
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Nested Loop
- -> Index Only Scan using iprt1_p1_a on prt1_p1 t2
+ -> Index Only Scan using iprt1_p0_a on prt1_p0 t2
Index Cond: (a = t1.a)
- -> Index Scan using iprt2_p1_b on prt2_p1 t3
+ -> Index Scan using iprt2_p0_b on prt2_p0 t3
Index Cond: (b = t2.a)
-> Nested Loop Left Join
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Nested Loop
- -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_1
+ -> Index Only Scan using iprt1_p1_a on prt1_p1 t2_1
Index Cond: (a = t1_1.a)
- -> Index Scan using iprt2_p2_b on prt2_p2 t3_1
+ -> Index Scan using iprt2_p1_b on prt2_p1 t3_1
Index Cond: (b = t2_1.a)
-> Nested Loop Left Join
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
-> Nested Loop
- -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_2
+ -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2
Index Cond: (a = t1_2.a)
- -> Index Scan using iprt2_p3_b on prt2_p3 t3_2
+ -> Index Scan using iprt2_p2_b on prt2_p2 t3_2
Index Cond: (b = t2_2.a)
-(28 rows)
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Nested Loop
+ -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3
+ Index Cond: (a = t1_3.a)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t3_3
+ Index Cond: (b = t2_3.a)
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Nested Loop
+ -> Index Only Scan using iprt1_p4_a on prt1_p4 t2_4
+ Index Cond: (a = t1_4.a)
+ -> Index Scan using iprt2_p4_b on prt2_p4 t3_4
+ Index Cond: (b = t2_4.a)
+(44 rows)
SELECT * FROM prt1 t1 LEFT JOIN LATERAL
(SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
ON t1.a = ss.t2a WHERE t1.b = 0 ORDER BY t1.a;
- a | b | c | t2a | t3a | least
------+---+------+-----+-----+-------
- 0 | 0 | 0000 | 0 | 0 | 0
- 50 | 0 | 0050 | | |
- 100 | 0 | 0100 | | |
- 150 | 0 | 0150 | 150 | 0 | 150
- 200 | 0 | 0200 | | |
- 250 | 0 | 0250 | | |
- 300 | 0 | 0300 | 300 | 0 | 300
- 350 | 0 | 0350 | | |
- 400 | 0 | 0400 | | |
- 450 | 0 | 0450 | 450 | 0 | 450
- 500 | 0 | 0500 | | |
- 550 | 0 | 0550 | | |
-(12 rows)
+ a | b | c | t2a | t3a | least
+------+---+-------+------+-----+-------
+ -250 | 0 | -0250 | | |
+ -200 | 0 | -0200 | | |
+ -150 | 0 | -0150 | -150 | 0 | -150
+ -100 | 0 | -0100 | | |
+ -50 | 0 | -0050 | | |
+ 0 | 0 | 0000 | 0 | 0 | 0
+ 50 | 0 | 0050 | | |
+ 100 | 0 | 0100 | | |
+ 150 | 0 | 0150 | 150 | 0 | 150
+ 200 | 0 | 0200 | | |
+ 250 | 0 | 0250 | | |
+ 300 | 0 | 0300 | 300 | 0 | 300
+ 350 | 0 | 0350 | | |
+ 400 | 0 | 0400 | | |
+ 450 | 0 | 0450 | 450 | 0 | 450
+ 500 | 0 | 0500 | | |
+ 550 | 0 | 0550 | | |
+ 600 | 0 | 0600 | 600 | 0 | 600
+ 650 | 0 | 0650 | | |
+ 700 | 0 | 0700 | | |
+ 750 | 0 | 0750 | 750 | 0 | 750
+(21 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
@@ -543,64 +865,95 @@ SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
Hash Cond: ((t1.c)::text = (t2.c)::text)
Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
-> Append
- -> Seq Scan on prt1_p1 t1
- -> Seq Scan on prt1_p2 t1_1
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
-> Hash
-> Append
-> Hash Join
Hash Cond: (t2.a = t3.b)
- -> Seq Scan on prt1_p1 t2
+ -> Seq Scan on prt1_p0 t2
-> Hash
- -> Seq Scan on prt2_p1 t3
+ -> Seq Scan on prt2_p0 t3
-> Hash Join
Hash Cond: (t2_1.a = t3_1.b)
- -> Seq Scan on prt1_p2 t2_1
+ -> Seq Scan on prt1_p1 t2_1
-> Hash
- -> Seq Scan on prt2_p2 t3_1
+ -> Seq Scan on prt2_p1 t3_1
-> Hash Join
Hash Cond: (t2_2.a = t3_2.b)
- -> Seq Scan on prt1_p3 t2_2
+ -> Seq Scan on prt1_p2 t2_2
-> Hash
- -> Seq Scan on prt2_p3 t3_2
-(26 rows)
+ -> Seq Scan on prt2_p2 t3_2
+ -> Hash Join
+ Hash Cond: (t2_3.a = t3_3.b)
+ -> Seq Scan on prt1_p3 t2_3
+ -> Hash
+ -> Seq Scan on prt2_p3 t3_3
+ -> Hash Join
+ Hash Cond: (t2_4.a = t3_4.b)
+ -> Seq Scan on prt1_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt2_p4 t3_4
+(38 rows)
SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
(SELECT t2.a AS t2a, t3.a AS t3a, t2.b t2b, t2.c t2c, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
ON t1.c = ss.t2c WHERE (t1.b + coalesce(ss.t2b, 0)) = 0 ORDER BY t1.a;
- a | t2a | t2c
------+-----+------
- 0 | 0 | 0000
- 50 | |
- 100 | |
- 150 | 150 | 0150
- 200 | |
- 250 | |
- 300 | 300 | 0300
- 350 | |
- 400 | |
- 450 | 450 | 0450
- 500 | |
- 550 | |
-(12 rows)
+ a | t2a | t2c
+------+------+-------
+ -250 | |
+ -200 | |
+ -150 | -150 | -0150
+ -100 | |
+ -50 | |
+ 0 | 0 | 0000
+ 50 | |
+ 100 | |
+ 150 | 150 | 0150
+ 200 | |
+ 250 | |
+ 300 | 300 | 0300
+ 350 | |
+ 400 | |
+ 450 | 450 | 0450
+ 500 | |
+ 550 | |
+ 600 | 600 | 0600
+ 650 | |
+ 700 | |
+ 750 | 750 | 0750
+(21 rows)
--
-- partitioned by expression
--
CREATE TABLE prt1_e (a int, b int, c int) PARTITION BY RANGE(((a + b)/2));
+CREATE TABLE prt1_e_p0 PARTITION OF prt1_e FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES FROM (0) TO (250);
CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES FROM (250) TO (500);
CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt1_e_p4 PARTITION OF prt1_e FOR VALUES FROM (600) TO (MAXVALUE);
INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(600, 799, 2) i;
+CREATE INDEX iprt1_e_p0_ab2 on prt1_e_p1(((a+b)/2));
CREATE INDEX iprt1_e_p1_ab2 on prt1_e_p1(((a+b)/2));
CREATE INDEX iprt1_e_p2_ab2 on prt1_e_p2(((a+b)/2));
CREATE INDEX iprt1_e_p3_ab2 on prt1_e_p3(((a+b)/2));
+CREATE INDEX iprt1_e_p4_ab2 on prt1_e_p1(((a+b)/2));
ANALYZE prt1_e;
CREATE TABLE prt2_e (a int, b int, c int) PARTITION BY RANGE(((b + a)/2));
+CREATE TABLE prt2_e_p0 PARTITION OF prt2_e FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES FROM (0) TO (250);
CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES FROM (250) TO (500);
CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt2_e_p4 PARTITION OF prt2_e FOR VALUES FROM (600) TO (MAXVALUE);
INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(600, 799, 3) i;
ANALYZE prt2_e;
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b;
@@ -611,32 +964,49 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 =
-> Append
-> Hash Join
Hash Cond: (((t2.b + t2.a) / 2) = ((t1.a + t1.b) / 2))
- -> Seq Scan on prt2_e_p1 t2
+ -> Seq Scan on prt2_e_p0 t2
-> Hash
- -> Seq Scan on prt1_e_p1 t1
+ -> Seq Scan on prt1_e_p0 t1
Filter: (c = 0)
-> Hash Join
- Hash Cond: (((t2_1.b + t2_1.a) / 2) = ((t1_1.a + t1_1.b) / 2))
- -> Seq Scan on prt2_e_p2 t2_1
+ Hash Cond: (((t1_1.a + t1_1.b) / 2) = ((t2_1.b + t2_1.a) / 2))
+ -> Seq Scan on prt1_e_p1 t1_1
+ Filter: (c = 0)
-> Hash
- -> Seq Scan on prt1_e_p2 t1_1
- Filter: (c = 0)
+ -> Seq Scan on prt2_e_p1 t2_1
-> Hash Join
Hash Cond: (((t2_2.b + t2_2.a) / 2) = ((t1_2.a + t1_2.b) / 2))
- -> Seq Scan on prt2_e_p3 t2_2
+ -> Seq Scan on prt2_e_p2 t2_2
-> Hash
- -> Seq Scan on prt1_e_p3 t1_2
+ -> Seq Scan on prt1_e_p2 t1_2
Filter: (c = 0)
-(21 rows)
+ -> Hash Join
+ Hash Cond: (((t2_3.b + t2_3.a) / 2) = ((t1_3.a + t1_3.b) / 2))
+ -> Seq Scan on prt2_e_p3 t2_3
+ -> Hash
+ -> Seq Scan on prt1_e_p3 t1_3
+ Filter: (c = 0)
+ -> Hash Join
+ Hash Cond: (((t2_4.b + t2_4.a) / 2) = ((t1_4.a + t1_4.b) / 2))
+ -> Seq Scan on prt2_e_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt1_e_p4 t1_4
+ Filter: (c = 0)
+(33 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b;
- a | c | b | c
------+---+-----+---
- 0 | 0 | 0 | 0
- 150 | 0 | 150 | 0
- 300 | 0 | 300 | 0
- 450 | 0 | 450 | 0
-(4 rows)
+ a | c | b | c
+------+---+------+---
+ -250 | 0 | -250 | 0
+ -100 | 0 | -100 | 0
+ 0 | 0 | 0 | 0
+ 0 | 0 | 0 | 0
+ 150 | 0 | 150 | 0
+ 300 | 0 | 300 | 0
+ 450 | 0 | 450 | 0
+ 600 | 0 | 600 | 0
+ 750 | 0 | 750 | 0
+(9 rows)
--
-- N-way join
@@ -650,107 +1020,158 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t
-> Result
-> Append
-> Nested Loop
- Join Filter: (t1.a = ((t3.a + t3.b) / 2))
+ Join Filter: (t1.a = t2.b)
-> Hash Join
- Hash Cond: (t2.b = t1.a)
- -> Seq Scan on prt2_p1 t2
+ Hash Cond: (((t3.a + t3.b) / 2) = t1.a)
+ -> Seq Scan on prt1_e_p0 t3
-> Hash
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
- -> Index Scan using iprt1_e_p1_ab2 on prt1_e_p1 t3
- Index Cond: (((a + b) / 2) = t2.b)
+ -> Index Scan using iprt2_p0_b on prt2_p0 t2
+ Index Cond: (b = ((t3.a + t3.b) / 2))
-> Nested Loop
Join Filter: (t1_1.a = ((t3_1.a + t3_1.b) / 2))
-> Hash Join
Hash Cond: (t2_1.b = t1_1.a)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Hash
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
- -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t3_1
+ -> Index Scan using iprt1_e_p4_ab2 on prt1_e_p1 t3_1
Index Cond: (((a + b) / 2) = t2_1.b)
-> Nested Loop
Join Filter: (t1_2.a = ((t3_2.a + t3_2.b) / 2))
-> Hash Join
Hash Cond: (t2_2.b = t1_2.a)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p2 t2_2
-> Hash
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
- -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_2
+ -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t3_2
Index Cond: (((a + b) / 2) = t2_2.b)
-(34 rows)
+ -> Nested Loop
+ Join Filter: (t1_3.a = ((t3_3.a + t3_3.b) / 2))
+ -> Nested Loop
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_3
+ Index Cond: (((a + b) / 2) = t2_3.b)
+ -> Nested Loop
+ Join Filter: (t1_4.a = t2_4.b)
+ -> Hash Join
+ Hash Cond: (((t3_4.a + t3_4.b) / 2) = t1_4.a)
+ -> Seq Scan on prt1_e_p4 t3_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p4_b on prt2_p4 t2_4
+ Index Cond: (b = ((t3_4.a + t3_4.b) / 2))
+(53 rows)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.b = 0 ORDER BY t1.a, t2.b;
- a | c | b | c | ?column? | c
------+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
-(4 rows)
+ a | c | b | c | ?column? | c
+------+-------+------+-------+----------+---
+ -150 | -0150 | -150 | -0150 | -300 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 150 | 0150 | 150 | 0150 | 300 | 0
+ 300 | 0300 | 300 | 0300 | 600 | 0
+ 450 | 0450 | 450 | 0450 | 900 | 0
+ 600 | 0600 | 600 | 0600 | 1200 | 0
+ 750 | 0750 | 750 | 0750 | 1500 | 0
+(8 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- QUERY PLAN
---------------------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------------
Sort
Sort Key: t1.a, t2.b, ((t3.a + t3.b))
-> Result
-> Append
-> Hash Right Join
Hash Cond: (((t3.a + t3.b) / 2) = t1.a)
- -> Seq Scan on prt1_e_p1 t3
+ -> Seq Scan on prt1_e_p0 t3
-> Hash
-> Hash Right Join
Hash Cond: (t2.b = t1.a)
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
-> Hash
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Hash Right Join
Hash Cond: (((t3_1.a + t3_1.b) / 2) = t1_1.a)
- -> Seq Scan on prt1_e_p2 t3_1
+ -> Seq Scan on prt1_e_p1 t3_1
-> Hash
-> Hash Right Join
Hash Cond: (t2_1.b = t1_1.a)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Hash
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Hash Right Join
Hash Cond: (((t3_2.a + t3_2.b) / 2) = t1_2.a)
- -> Seq Scan on prt1_e_p3 t3_2
+ -> Seq Scan on prt1_e_p2 t3_2
-> Hash
-> Hash Right Join
Hash Cond: (t2_2.b = t1_2.a)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p2 t2_2
+ -> Hash
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Nested Loop Left Join
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (t1_3.a = b)
+ -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_3
+ Index Cond: (t1_3.a = ((a + b) / 2))
+ -> Hash Right Join
+ Hash Cond: (((t3_4.a + t3_4.b) / 2) = t1_4.a)
+ -> Seq Scan on prt1_e_p4 t3_4
+ -> Hash
+ -> Hash Right Join
+ Hash Cond: (t2_4.b = t1_4.a)
+ -> Seq Scan on prt2_p4 t2_4
-> Hash
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p4 t1_4
Filter: (b = 0)
-(34 rows)
+(52 rows)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- a | c | b | c | ?column? | c
------+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 50 | 0050 | | | 100 | 0
- 100 | 0100 | | | 200 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 200 | 0200 | | | 400 | 0
- 250 | 0250 | | | 500 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 350 | 0350 | | | 700 | 0
- 400 | 0400 | | | 800 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
- 500 | 0500 | | | 1000 | 0
- 550 | 0550 | | | 1100 | 0
-(12 rows)
+ a | c | b | c | ?column? | c
+------+-------+------+-------+----------+---
+ -250 | -0250 | | | -500 | 0
+ -200 | -0200 | | | -400 | 0
+ -150 | -0150 | -150 | -0150 | -300 | 0
+ -100 | -0100 | | | -200 | 0
+ -50 | -0050 | | | -100 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 50 | 0050 | | | 100 | 0
+ 100 | 0100 | | | 200 | 0
+ 150 | 0150 | 150 | 0150 | 300 | 0
+ 200 | 0200 | | | 400 | 0
+ 250 | 0250 | | | 500 | 0
+ 300 | 0300 | 300 | 0300 | 600 | 0
+ 350 | 0350 | | | 700 | 0
+ 400 | 0400 | | | 800 | 0
+ 450 | 0450 | 450 | 0450 | 900 | 0
+ 500 | 0500 | | | 1000 | 0
+ 550 | 0550 | | | 1100 | 0
+ 600 | 0600 | 600 | 0600 | 1200 | 0
+ 650 | 0650 | | | 1300 | 0
+ 700 | 0700 | | | 1400 | 0
+ 750 | 0750 | 750 | 0750 | 1500 | 0
+(22 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- QUERY PLAN
--------------------------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------------
Sort
Sort Key: t1.a, t2.b, ((t3.a + t3.b))
-> Result
@@ -758,48 +1179,75 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
-> Nested Loop Left Join
-> Hash Right Join
Hash Cond: (t1.a = ((t3.a + t3.b) / 2))
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
-> Hash
- -> Seq Scan on prt1_e_p1 t3
+ -> Seq Scan on prt1_e_p0 t3
Filter: (c = 0)
- -> Index Scan using iprt2_p1_b on prt2_p1 t2
+ -> Index Scan using iprt2_p0_b on prt2_p0 t2
Index Cond: (t1.a = b)
-> Nested Loop Left Join
-> Hash Right Join
Hash Cond: (t1_1.a = ((t3_1.a + t3_1.b) / 2))
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
-> Hash
- -> Seq Scan on prt1_e_p2 t3_1
+ -> Seq Scan on prt1_e_p1 t3_1
Filter: (c = 0)
- -> Index Scan using iprt2_p2_b on prt2_p2 t2_1
+ -> Index Scan using iprt2_p1_b on prt2_p1 t2_1
Index Cond: (t1_1.a = b)
-> Nested Loop Left Join
-> Hash Right Join
Hash Cond: (t1_2.a = ((t3_2.a + t3_2.b) / 2))
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
-> Hash
- -> Seq Scan on prt1_e_p3 t3_2
+ -> Seq Scan on prt1_e_p2 t3_2
Filter: (c = 0)
- -> Index Scan using iprt2_p3_b on prt2_p3 t2_2
+ -> Index Scan using iprt2_p2_b on prt2_p2 t2_2
Index Cond: (t1_2.a = b)
-(31 rows)
+ -> Nested Loop Left Join
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_e_p3 t3_3
+ Filter: (c = 0)
+ -> Index Scan using iprt1_p3_a on prt1_p3 t1_3
+ Index Cond: (a = ((t3_3.a + t3_3.b) / 2))
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (t1_3.a = b)
+ -> Nested Loop Left Join
+ -> Hash Right Join
+ Hash Cond: (t1_4.a = ((t3_4.a + t3_4.b) / 2))
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Seq Scan on prt1_e_p4 t3_4
+ Filter: (c = 0)
+ -> Index Scan using iprt2_p4_b on prt2_p4 t2_4
+ Index Cond: (t1_4.a = b)
+(48 rows)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- a | c | b | c | ?column? | c
------+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 50 | 0050 | | | 100 | 0
- 100 | 0100 | | | 200 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 200 | 0200 | | | 400 | 0
- 250 | 0250 | | | 500 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 350 | 0350 | | | 700 | 0
- 400 | 0400 | | | 800 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
- 500 | 0500 | | | 1000 | 0
- 550 | 0550 | | | 1100 | 0
-(12 rows)
+ a | c | b | c | ?column? | c
+------+-------+------+-------+----------+---
+ -250 | -0250 | | | -500 | 0
+ -200 | -0200 | | | -400 | 0
+ -150 | -0150 | -150 | -0150 | -300 | 0
+ -100 | -0100 | | | -200 | 0
+ -50 | -0050 | | | -100 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 50 | 0050 | | | 100 | 0
+ 100 | 0100 | | | 200 | 0
+ 150 | 0150 | 150 | 0150 | 300 | 0
+ 200 | 0200 | | | 400 | 0
+ 250 | 0250 | | | 500 | 0
+ 300 | 0300 | 300 | 0300 | 600 | 0
+ 350 | 0350 | | | 700 | 0
+ 400 | 0400 | | | 800 | 0
+ 450 | 0450 | 450 | 0450 | 900 | 0
+ 500 | 0500 | | | 1000 | 0
+ 550 | 0550 | | | 1100 | 0
+ 600 | 0600 | 600 | 0600 | 1200 | 0
+ 650 | 0650 | | | 1300 | 0
+ 700 | 0700 | | | 1400 | 0
+ 750 | 0750 | 750 | 0750 | 1500 | 0
+(22 rows)
-- Cases with non-nullable expressions in subquery results;
-- make sure these go to null as expected
@@ -808,23 +1256,36 @@ SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * F
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, prt2_p1.b, ((prt1_e_p1.a + prt1_e_p1.b))
+ Sort Key: prt1_p0.a, prt2_p0.b, ((prt1_e_p0.a + prt1_e_p0.b))
-> Result
-> Append
-> Hash Full Join
- Hash Cond: (prt1_p1.a = ((prt1_e_p1.a + prt1_e_p1.b) / 2))
- Filter: ((prt1_p1.a = (50)) OR (prt2_p1.b = (75)) OR (((prt1_e_p1.a + prt1_e_p1.b) / 2) = (50)))
+ Hash Cond: (prt1_p0.a = ((prt1_e_p0.a + prt1_e_p0.b) / 2))
+ Filter: ((prt1_p0.a = (50)) OR (prt2_p0.b = (75)) OR (((prt1_e_p0.a + prt1_e_p0.b) / 2) = (50)))
-> Hash Full Join
- Hash Cond: (prt1_p1.a = prt2_p1.b)
- -> Seq Scan on prt1_p1
+ Hash Cond: (prt1_p0.a = prt2_p0.b)
+ -> Seq Scan on prt1_p0
Filter: (b = 0)
-> Hash
- -> Seq Scan on prt2_p1
+ -> Seq Scan on prt2_p0
Filter: (a = 0)
-> Hash
- -> Seq Scan on prt1_e_p1
+ -> Seq Scan on prt1_e_p0
Filter: (c = 0)
-> Hash Full Join
+ Hash Cond: (((prt1_e_p1.a + prt1_e_p1.b) / 2) = prt1_p1.a)
+ Filter: ((prt1_p1.a = (50)) OR (prt2_p1.b = (75)) OR (((prt1_e_p1.a + prt1_e_p1.b) / 2) = (50)))
+ -> Seq Scan on prt1_e_p1
+ Filter: (c = 0)
+ -> Hash
+ -> Hash Full Join
+ Hash Cond: (prt1_p1.a = prt2_p1.b)
+ -> Seq Scan on prt1_p1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p1
+ Filter: (a = 0)
+ -> Hash Full Join
Hash Cond: (prt1_p2.a = ((prt1_e_p2.a + prt1_e_p2.b) / 2))
Filter: ((prt1_p2.a = (50)) OR (prt2_p2.b = (75)) OR (((prt1_e_p2.a + prt1_e_p2.b) / 2) = (50)))
-> Hash Full Join
@@ -850,7 +1311,20 @@ SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * F
-> Hash
-> Seq Scan on prt1_e_p3
Filter: (c = 0)
-(43 rows)
+ -> Hash Full Join
+ Hash Cond: (prt1_p4.a = ((prt1_e_p4.a + prt1_e_p4.b) / 2))
+ Filter: ((prt1_p4.a = (50)) OR (prt2_p4.b = (75)) OR (((prt1_e_p4.a + prt1_e_p4.b) / 2) = (50)))
+ -> Hash Full Join
+ Hash Cond: (prt1_p4.a = prt2_p4.b)
+ -> Seq Scan on prt1_p4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_e_p4
+ Filter: (c = 0)
+(69 rows)
SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.c = 0) t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a = t1.phv OR t2.b = t2.phv OR (t3.a + t3.b)/2 = t3.phv ORDER BY t1.a, t2.b, t3.a + t3.b;
a | phv | b | phv | ?column? | phv
@@ -868,397 +1342,2606 @@ SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHER
Sort Key: t1.a
-> Append
-> Nested Loop
- Join Filter: (t1.a = t1_3.b)
+ Join Filter: (t1.a = t1_5.b)
-> HashAggregate
- Group Key: t1_3.b
+ Group Key: t1_5.b
-> Hash Join
- Hash Cond: (((t2.a + t2.b) / 2) = t1_3.b)
- -> Seq Scan on prt1_e_p1 t2
+ Hash Cond: (((t2.a + t2.b) / 2) = t1_5.b)
+ -> Seq Scan on prt1_e_p0 t2
-> Hash
- -> Seq Scan on prt2_p1 t1_3
+ -> Seq Scan on prt2_p0 t1_5
Filter: (a = 0)
- -> Index Scan using iprt1_p1_a on prt1_p1 t1
+ -> Index Scan using iprt1_p0_a on prt1_p0 t1
Index Cond: (a = ((t2.a + t2.b) / 2))
Filter: (b = 0)
-> Nested Loop
- Join Filter: (t1_1.a = t1_4.b)
+ Join Filter: (t1_1.a = t1_6.b)
-> HashAggregate
- Group Key: t1_4.b
+ Group Key: t1_6.b
-> Hash Join
- Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_4.b)
- -> Seq Scan on prt1_e_p2 t2_1
+ Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_6.b)
+ -> Seq Scan on prt1_e_p1 t2_1
-> Hash
- -> Seq Scan on prt2_p2 t1_4
+ -> Seq Scan on prt2_p1 t1_6
Filter: (a = 0)
- -> Index Scan using iprt1_p2_a on prt1_p2 t1_1
+ -> Index Scan using iprt1_p1_a on prt1_p1 t1_1
Index Cond: (a = ((t2_1.a + t2_1.b) / 2))
Filter: (b = 0)
-> Nested Loop
- Join Filter: (t1_2.a = t1_5.b)
+ Join Filter: (t1_2.a = t1_7.b)
-> HashAggregate
- Group Key: t1_5.b
+ Group Key: t1_7.b
-> Nested Loop
- -> Seq Scan on prt2_p3 t1_5
+ -> Seq Scan on prt2_p2 t1_7
Filter: (a = 0)
- -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t2_2
- Index Cond: (((a + b) / 2) = t1_5.b)
- -> Index Scan using iprt1_p3_a on prt1_p3 t1_2
+ -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t2_2
+ Index Cond: (((a + b) / 2) = t1_7.b)
+ -> Index Scan using iprt1_p2_a on prt1_p2 t1_2
Index Cond: (a = ((t2_2.a + t2_2.b) / 2))
Filter: (b = 0)
-(41 rows)
-
-SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.a = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.b = 0 ORDER BY t1.a;
- a | b | c
------+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
-
+ -> Nested Loop
+ Join Filter: (t1_3.a = t1_8.b)
+ -> HashAggregate
+ Group Key: t1_8.b
+ -> Nested Loop
+ -> Seq Scan on prt2_p3 t1_8
+ Filter: (a = 0)
+ -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t2_3
+ Index Cond: (((a + b) / 2) = t1_8.b)
+ -> Index Scan using iprt1_p3_a on prt1_p3 t1_3
+ Index Cond: (a = ((t2_3.a + t2_3.b) / 2))
+ Filter: (b = 0)
+ -> Nested Loop
+ Join Filter: (t1_4.a = t1_9.b)
+ -> HashAggregate
+ Group Key: t1_9.b
+ -> Hash Join
+ Hash Cond: (((t2_4.a + t2_4.b) / 2) = t1_9.b)
+ -> Seq Scan on prt1_e_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt2_p4 t1_9
+ Filter: (a = 0)
+ -> Index Scan using iprt1_p4_a on prt1_p4 t1_4
+ Index Cond: (a = ((t2_4.a + t2_4.b) / 2))
+ Filter: (b = 0)
+(66 rows)
+
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.a = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.b = 0 ORDER BY t1.a;
+ a | b | c
+------+---+-------
+ -150 | 0 | -0150
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 300 | 0 | 0300
+ 450 | 0 | 0450
+ 600 | 0 | 0600
+ 750 | 0 | 0750
+(7 rows)
+
EXPLAIN (COSTS OFF)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
- QUERY PLAN
--------------------------------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------------------
Sort
Sort Key: t1.a
-> Append
-> Nested Loop
-> HashAggregate
- Group Key: t1_3.b
+ Group Key: t1_5.b
-> Hash Semi Join
- Hash Cond: (t1_3.b = ((t1_6.a + t1_6.b) / 2))
- -> Seq Scan on prt2_p1 t1_3
+ Hash Cond: (t1_5.b = ((t1_10.a + t1_10.b) / 2))
+ -> Seq Scan on prt2_p0 t1_5
-> Hash
- -> Seq Scan on prt1_e_p1 t1_6
+ -> Seq Scan on prt1_e_p0 t1_10
Filter: (c = 0)
- -> Index Scan using iprt1_p1_a on prt1_p1 t1
- Index Cond: (a = t1_3.b)
+ -> Index Scan using iprt1_p0_a on prt1_p0 t1
+ Index Cond: (a = t1_5.b)
Filter: (b = 0)
-> Nested Loop
-> HashAggregate
- Group Key: t1_4.b
+ Group Key: t1_6.b
-> Hash Semi Join
- Hash Cond: (t1_4.b = ((t1_7.a + t1_7.b) / 2))
- -> Seq Scan on prt2_p2 t1_4
+ Hash Cond: (t1_6.b = ((t1_11.a + t1_11.b) / 2))
+ -> Seq Scan on prt2_p1 t1_6
-> Hash
- -> Seq Scan on prt1_e_p2 t1_7
+ -> Seq Scan on prt1_e_p1 t1_11
Filter: (c = 0)
- -> Index Scan using iprt1_p2_a on prt1_p2 t1_1
- Index Cond: (a = t1_4.b)
+ -> Index Scan using iprt1_p1_a on prt1_p1 t1_1
+ Index Cond: (a = t1_6.b)
+ Filter: (b = 0)
+ -> Nested Loop
+ -> HashAggregate
+ Group Key: t1_7.b
+ -> Hash Semi Join
+ Hash Cond: (t1_7.b = ((t1_12.a + t1_12.b) / 2))
+ -> Seq Scan on prt2_p2 t1_7
+ -> Hash
+ -> Seq Scan on prt1_e_p2 t1_12
+ Filter: (c = 0)
+ -> Index Scan using iprt1_p2_a on prt1_p2 t1_2
+ Index Cond: (a = t1_7.b)
Filter: (b = 0)
-> Nested Loop
-> Unique
-> Sort
- Sort Key: t1_5.b
+ Sort Key: t1_8.b
-> Hash Semi Join
- Hash Cond: (t1_5.b = ((t1_8.a + t1_8.b) / 2))
- -> Seq Scan on prt2_p3 t1_5
+ Hash Cond: (t1_8.b = ((t1_13.a + t1_13.b) / 2))
+ -> Seq Scan on prt2_p3 t1_8
-> Hash
- -> Seq Scan on prt1_e_p3 t1_8
+ -> Seq Scan on prt1_e_p3 t1_13
Filter: (c = 0)
- -> Index Scan using iprt1_p3_a on prt1_p3 t1_2
- Index Cond: (a = t1_5.b)
+ -> Index Scan using iprt1_p3_a on prt1_p3 t1_3
+ Index Cond: (a = t1_8.b)
+ Filter: (b = 0)
+ -> Nested Loop
+ -> HashAggregate
+ Group Key: t1_9.b
+ -> Hash Semi Join
+ Hash Cond: (t1_9.b = ((t1_14.a + t1_14.b) / 2))
+ -> Seq Scan on prt2_p4 t1_9
+ -> Hash
+ -> Seq Scan on prt1_e_p4 t1_14
+ Filter: (c = 0)
+ -> Index Scan using iprt1_p4_a on prt1_p4 t1_4
+ Index Cond: (a = t1_9.b)
Filter: (b = 0)
-(40 rows)
+(64 rows)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
- a | b | c
------+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
+ a | b | c
+------+---+-------
+ -150 | 0 | -0150
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 300 | 0 | 0300
+ 450 | 0 | 0450
+ 600 | 0 | 0600
+ 750 | 0 | 0750
+(7 rows)
+
+-- test merge joins
+SET enable_hashjoin TO off;
+SET enable_nestloop TO off;
+EXPLAIN (COSTS OFF)
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------------
+ Merge Append
+ Sort Key: t1.a
+ -> Merge Semi Join
+ Merge Cond: (t1.a = t1_5.b)
+ -> Sort
+ Sort Key: t1.a
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_5.b = (((t1_10.a + t1_10.b) / 2)))
+ -> Sort
+ Sort Key: t1_5.b
+ -> Seq Scan on prt2_p0 t1_5
+ -> Sort
+ Sort Key: (((t1_10.a + t1_10.b) / 2))
+ -> Seq Scan on prt1_e_p0 t1_10
+ Filter: (c = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_1.a = t1_6.b)
+ -> Sort
+ Sort Key: t1_1.a
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_6.b = (((t1_11.a + t1_11.b) / 2)))
+ -> Sort
+ Sort Key: t1_6.b
+ -> Seq Scan on prt2_p1 t1_6
+ -> Sort
+ Sort Key: (((t1_11.a + t1_11.b) / 2))
+ -> Seq Scan on prt1_e_p1 t1_11
+ Filter: (c = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_2.a = t1_7.b)
+ -> Sort
+ Sort Key: t1_2.a
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_7.b = (((t1_12.a + t1_12.b) / 2)))
+ -> Sort
+ Sort Key: t1_7.b
+ -> Seq Scan on prt2_p2 t1_7
+ -> Sort
+ Sort Key: (((t1_12.a + t1_12.b) / 2))
+ -> Seq Scan on prt1_e_p2 t1_12
+ Filter: (c = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_3.a = t1_8.b)
+ -> Sort
+ Sort Key: t1_3.a
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_8.b = (((t1_13.a + t1_13.b) / 2)))
+ -> Sort
+ Sort Key: t1_8.b
+ -> Seq Scan on prt2_p3 t1_8
+ -> Sort
+ Sort Key: (((t1_13.a + t1_13.b) / 2))
+ -> Seq Scan on prt1_e_p3 t1_13
+ Filter: (c = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_4.a = t1_9.b)
+ -> Sort
+ Sort Key: t1_4.a
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_9.b = (((t1_14.a + t1_14.b) / 2)))
+ -> Sort
+ Sort Key: t1_9.b
+ -> Seq Scan on prt2_p4 t1_9
+ -> Sort
+ Sort Key: (((t1_14.a + t1_14.b) / 2))
+ -> Seq Scan on prt1_e_p4 t1_14
+ Filter: (c = 0)
+(77 rows)
+
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
+ a | b | c
+------+---+-------
+ -150 | 0 | -0150
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 300 | 0 | 0300
+ 450 | 0 | 0450
+ 600 | 0 | 0600
+ 750 | 0 | 0750
+(7 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ QUERY PLAN
+----------------------------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+ -> Result
+ -> Append
+ -> Merge Left Join
+ Merge Cond: (t1.a = t2.b)
+ -> Sort
+ Sort Key: t1.a
+ -> Merge Left Join
+ Merge Cond: ((((t3.a + t3.b) / 2)) = t1.a)
+ -> Sort
+ Sort Key: (((t3.a + t3.b) / 2))
+ -> Seq Scan on prt1_e_p0 t3
+ Filter: (c = 0)
+ -> Sort
+ Sort Key: t1.a
+ -> Seq Scan on prt1_p0 t1
+ -> Sort
+ Sort Key: t2.b
+ -> Seq Scan on prt2_p0 t2
+ -> Merge Left Join
+ Merge Cond: (t1_1.a = t2_1.b)
+ -> Sort
+ Sort Key: t1_1.a
+ -> Merge Left Join
+ Merge Cond: ((((t3_1.a + t3_1.b) / 2)) = t1_1.a)
+ -> Sort
+ Sort Key: (((t3_1.a + t3_1.b) / 2))
+ -> Seq Scan on prt1_e_p1 t3_1
+ Filter: (c = 0)
+ -> Sort
+ Sort Key: t1_1.a
+ -> Seq Scan on prt1_p1 t1_1
+ -> Sort
+ Sort Key: t2_1.b
+ -> Seq Scan on prt2_p1 t2_1
+ -> Merge Left Join
+ Merge Cond: (t1_2.a = t2_2.b)
+ -> Sort
+ Sort Key: t1_2.a
+ -> Merge Left Join
+ Merge Cond: ((((t3_2.a + t3_2.b) / 2)) = t1_2.a)
+ -> Sort
+ Sort Key: (((t3_2.a + t3_2.b) / 2))
+ -> Seq Scan on prt1_e_p2 t3_2
+ Filter: (c = 0)
+ -> Sort
+ Sort Key: t1_2.a
+ -> Seq Scan on prt1_p2 t1_2
+ -> Sort
+ Sort Key: t2_2.b
+ -> Seq Scan on prt2_p2 t2_2
+ -> Merge Left Join
+ Merge Cond: (t1_3.a = t2_3.b)
+ -> Sort
+ Sort Key: t1_3.a
+ -> Merge Left Join
+ Merge Cond: ((((t3_3.a + t3_3.b) / 2)) = t1_3.a)
+ -> Sort
+ Sort Key: (((t3_3.a + t3_3.b) / 2))
+ -> Seq Scan on prt1_e_p3 t3_3
+ Filter: (c = 0)
+ -> Sort
+ Sort Key: t1_3.a
+ -> Seq Scan on prt1_p3 t1_3
+ -> Sort
+ Sort Key: t2_3.b
+ -> Seq Scan on prt2_p3 t2_3
+ -> Merge Left Join
+ Merge Cond: (t1_4.a = t2_4.b)
+ -> Sort
+ Sort Key: t1_4.a
+ -> Merge Left Join
+ Merge Cond: ((((t3_4.a + t3_4.b) / 2)) = t1_4.a)
+ -> Sort
+ Sort Key: (((t3_4.a + t3_4.b) / 2))
+ -> Seq Scan on prt1_e_p4 t3_4
+ Filter: (c = 0)
+ -> Sort
+ Sort Key: t1_4.a
+ -> Seq Scan on prt1_p4 t1_4
+ -> Sort
+ Sort Key: t2_4.b
+ -> Seq Scan on prt2_p4 t2_4
+(84 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+------+-------+------+-------+----------+---
+ -250 | -0250 | | | -500 | 0
+ -200 | -0200 | | | -400 | 0
+ -150 | -0150 | -150 | -0150 | -300 | 0
+ -100 | -0100 | | | -200 | 0
+ -50 | -0050 | | | -100 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 50 | 0050 | | | 100 | 0
+ 100 | 0100 | | | 200 | 0
+ 150 | 0150 | 150 | 0150 | 300 | 0
+ 200 | 0200 | | | 400 | 0
+ 250 | 0250 | | | 500 | 0
+ 300 | 0300 | 300 | 0300 | 600 | 0
+ 350 | 0350 | | | 700 | 0
+ 400 | 0400 | | | 800 | 0
+ 450 | 0450 | 450 | 0450 | 900 | 0
+ 500 | 0500 | | | 1000 | 0
+ 550 | 0550 | | | 1100 | 0
+ 600 | 0600 | 600 | 0600 | 1200 | 0
+ 650 | 0650 | | | 1300 | 0
+ 700 | 0700 | | | 1400 | 0
+ 750 | 0750 | 750 | 0750 | 1500 | 0
+(22 rows)
+
+RESET enable_hashjoin;
+RESET enable_nestloop;
+-- Add an extra partition to prt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+DROP TABLE prt2_p4;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (800);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (800) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999) i WHERE i % 3 = 0;
+ANALYZE prt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on prt2_p0 t2
+ -> Hash
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Hash Join
+ Hash Cond: (t2_1.b = t1_1.a)
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash Join
+ Hash Cond: (t2_2.b = t1_2.a)
+ -> Seq Scan on prt2_p2 t2_2
+ -> Hash
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Nested Loop
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ -> Hash Join
+ Hash Cond: (t2_4.b = t1_4.a)
+ -> Seq Scan on prt2_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(32 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ a | c | a | c
+------+-------+---+-------
+ -150 | -0150 | 0 | -0150
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 0 | 0150
+ 300 | 0300 | 0 | 0300
+ 450 | 0450 | 0 | 0450
+ 600 | 0600 | 0 | 0600
+ 750 | 0750 | 0 | 0750
+(7 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Right Join
+ Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on prt2_p0 t2
+ -> Hash
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Hash Right Join
+ Hash Cond: (t2_1.b = t1_1.a)
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash Right Join
+ Hash Cond: (t2_2.b = t1_2.a)
+ -> Seq Scan on prt2_p2 t2_2
+ -> Hash
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (t1_3.a = b)
+ -> Hash Right Join
+ Hash Cond: (t2_4.b = t1_4.a)
+ -> Seq Scan on prt2_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(32 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ a | c | a | c
+------+-------+---+-------
+ -250 | -0250 | |
+ -200 | -0200 | |
+ -150 | -0150 | 0 | -0150
+ -100 | -0100 | |
+ -50 | -0050 | |
+ 0 | 0000 | 0 | 0000
+ 50 | 0050 | |
+ 100 | 0100 | |
+ 150 | 0150 | 0 | 0150
+ 200 | 0200 | |
+ 250 | 0250 | |
+ 300 | 0300 | 0 | 0300
+ 350 | 0350 | |
+ 400 | 0400 | |
+ 450 | 0450 | 0 | 0450
+ 500 | 0500 | |
+ 550 | 0550 | |
+ 600 | 0600 | 0 | 0600
+ 650 | 0650 | |
+ 700 | 0700 | |
+ 750 | 0750 | 0 | 0750
+(21 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Semi Join
+ Hash Cond: (t1.a = t2.b)
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p0 t2
+ -> Hash Semi Join
+ Hash Cond: (t1_1.a = t2_1.b)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash Semi Join
+ Hash Cond: (t1_2.a = t2_2.b)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p2 t2_2
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Only Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ -> Hash Semi Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+(32 rows)
+
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+------+---+-------
+ -150 | 0 | -0150
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 300 | 0 | 0300
+ 450 | 0 | 0450
+ 600 | 0 | 0600
+ 750 | 0 | 0750
+(7 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Append
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p0_a on prt1_p0 t2
+ Index Cond: (a = t1.b)
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p1_a on prt1_p1 t2_1
+ Index Cond: (a = t1_1.b)
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2
+ Index Cond: (a = t1_2.b)
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3
+ Index Cond: (a = t1_3.b)
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p4_a on prt1_p4 t2_4
+ Index Cond: (a = t1_4.b)
+(28 rows)
+
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+---+------+-------
+ 0 | -150 | -0150
+ 0 | 0 | 0000
+ 0 | 150 | 0150
+ 0 | 300 | 0300
+ 0 | 450 | 0450
+ 0 | 600 | 0600
+ 0 | 750 | 0750
+(7 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Anti Join
+ Hash Cond: (t1.a = t2.b)
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p0 t2
+ -> Hash Anti Join
+ Hash Cond: (t1_1.a = t2_1.b)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash Anti Join
+ Hash Cond: (t1_2.a = t2_2.b)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p2 t2_2
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Only Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ -> Hash Anti Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+(32 rows)
+
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+------+---+-------
+ -250 | 0 | -0250
+ -200 | 0 | -0200
+ -100 | 0 | -0100
+ -50 | 0 | -0050
+ 50 | 0 | 0050
+ 100 | 0 | 0100
+ 200 | 0 | 0200
+ 250 | 0 | 0250
+ 350 | 0 | 0350
+ 400 | 0 | 0400
+ 500 | 0 | 0500
+ 550 | 0 | 0550
+ 650 | 0 | 0650
+ 700 | 0 | 0700
+(14 rows)
+
+-- partition-wise join can not handle missing partition on the inner side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.b;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t2.b
+ -> Hash Right Join
+ Hash Cond: (t1.a = t2.b)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t2_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t2_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t2_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t2_5
+ Filter: (a = 0)
+(24 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE coalesce(t1.b, 0) + coalesce(t2.a, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Full Join
+ Hash Cond: (t1.a = t2.b)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.a, 0)) = 0)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+(19 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Hash Anti Join
+ Hash Cond: (t1.b = t2.a)
+ -> Append
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t1_5
+ Filter: (a = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p0 t2
+ -> Seq Scan on prt1_p1 t2_1
+ -> Seq Scan on prt1_p2 t2_2
+ -> Seq Scan on prt1_p3 t2_3
+ -> Seq Scan on prt1_p4 t2_4
+(24 rows)
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE prt2_p4;
+DROP TABLE prt2_p5;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (700);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (700) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999, 3) i;
+ANALYZE prt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(23 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Right Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(23 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------
+ Hash Right Join
+ Hash Cond: (t1.a = t2.b)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t2_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t2_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t2_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t2_5
+ Filter: (a = 0)
+(22 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Join
+ Hash Cond: (t1.a = t2.b)
+ Join Filter: ((t1.b + t2.a) = 0)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+(19 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Semi Join
+ Hash Cond: (t1.a = t2.b)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+(23 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Hash Semi Join
+ Hash Cond: (t1.b = t2.a)
+ -> Append
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t1_5
+ Filter: (a = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p0 t2
+ -> Seq Scan on prt1_p1 t2_1
+ -> Seq Scan on prt1_p2 t2_2
+ -> Seq Scan on prt1_p3 t2_3
+ -> Seq Scan on prt1_p4 t2_4
+(24 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: (t1.a = t2.b)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+(23 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Hash Anti Join
+ Hash Cond: (t1.b = t2.a)
+ -> Append
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t1_5
+ Filter: (a = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p0 t2
+ -> Seq Scan on prt1_p1 t2_1
+ -> Seq Scan on prt1_p2 t2_2
+ -> Seq Scan on prt1_p3 t2_3
+ -> Seq Scan on prt1_p4 t2_4
+(24 rows)
+
+--
+-- partitioned by multiple columns
+--
+CREATE TABLE prt1_m (a int, b int, c int) PARTITION BY RANGE(a, ((a + b)/2));
+CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES FROM (0, 0) TO (250, 250);
+CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES FROM (250, 250) TO (500, 500);
+CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES FROM (500, 500) TO (600, 600);
+INSERT INTO prt1_m SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i;
+ANALYZE prt1_m;
+CREATE TABLE prt2_m (a int, b int, c int) PARTITION BY RANGE(((b + a)/2), b);
+CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES FROM (0, 0) TO (250, 250);
+CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES FROM (250, 250) TO (500, 500);
+CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES FROM (500, 500) TO (600, 600);
+INSERT INTO prt2_m SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i;
+ANALYZE prt2_m;
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Sort Key: prt1_m_p1.a, prt2_m_p1.b
+ -> Append
+ -> Hash Full Join
+ Hash Cond: ((prt1_m_p1.a = ((prt2_m_p1.b + prt2_m_p1.a) / 2)) AND (((prt1_m_p1.a + prt1_m_p1.b) / 2) = prt2_m_p1.b))
+ -> Seq Scan on prt1_m_p1
+ Filter: (c = 0)
+ -> Hash
+ -> Seq Scan on prt2_m_p1
+ Filter: (c = 0)
+ -> Hash Full Join
+ Hash Cond: ((prt1_m_p2.a = ((prt2_m_p2.b + prt2_m_p2.a) / 2)) AND (((prt1_m_p2.a + prt1_m_p2.b) / 2) = prt2_m_p2.b))
+ -> Seq Scan on prt1_m_p2
+ Filter: (c = 0)
+ -> Hash
+ -> Seq Scan on prt2_m_p2
+ Filter: (c = 0)
+ -> Hash Full Join
+ Hash Cond: ((prt1_m_p3.a = ((prt2_m_p3.b + prt2_m_p3.a) / 2)) AND (((prt1_m_p3.a + prt1_m_p3.b) / 2) = prt2_m_p3.b))
+ -> Seq Scan on prt1_m_p3
+ Filter: (c = 0)
+ -> Hash
+ -> Seq Scan on prt2_m_p3
+ Filter: (c = 0)
+(24 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+ a | c | b | c
+-----+---+-----+---
+ 0 | 0 | 0 | 0
+ 50 | 0 | |
+ 100 | 0 | |
+ 150 | 0 | 150 | 0
+ 200 | 0 | |
+ 250 | 0 | |
+ 300 | 0 | 300 | 0
+ 350 | 0 | |
+ 400 | 0 | |
+ 450 | 0 | 450 | 0
+ 500 | 0 | |
+ 550 | 0 | |
+ | | 75 | 0
+ | | 225 | 0
+ | | 375 | 0
+ | | 525 | 0
+(16 rows)
+
+--
+-- tests for list partitioned tables.
+--
+\set part_mod 17
+\set cond_mod 47
+\set num_rows 500
+CREATE TABLE plt1 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0001','0002','0003');
+CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0008','0009');
+CREATE TABLE plt1_p4 PARTITION OF plt1 FOR VALUES IN ('0000','0010');
+INSERT INTO plt1 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (7, 11, 12, 13, 14, 15, 16);
+ANALYSE plt1;
+-- plt2 have missing starting 0001, additional 0007, missing ending 0010
+-- and additional 0011 and 0012 bounds
+CREATE TABLE plt2 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002','0003');
+CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0007','0008','0009');
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000','0011','0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 10, 13, 14, 15, 16);
+ANALYSE plt2;
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Join Filter: ((t1_1.b + t2_1.b) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + t2_2.b) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t1_3.b + t2_3.b) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Left Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Right Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Left Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Left Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(7 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+------------------------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Result
+ -> Append
+ -> Hash Right Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Left Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + t2_1.b) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Left Join
+ Hash Cond: ((t2_2.c)::text = (t1_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + t2_2.b) = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash Right Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + t2_3.b) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(28 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 | 0011
+(6 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Append
+ -> Hash Full Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Full Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Full Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Full Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 | 0011
+(8 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt2_p4 t2
+ -> Nested Loop
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> HashAggregate
+ Group Key: (t2_1.c)::text
+ -> Seq Scan on plt2_p1 t2_1
+ -> Materialize
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(29 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt1_p4 t2
+ -> Materialize
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2_1
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_3
+(26 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop Anti Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Anti Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt2_p1 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 188 | 0 | 0001
+ 282 | 0 | 0010
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt1_p4 t2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 470 | 0 | 0011
+(1 row)
+
+--
+-- list partitioned by expression
+--
+CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
+CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0002', '0003');
+CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0004', '0005', '0006');
+CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0008', '0009');
+CREATE TABLE plt1_e_p4 PARTITION OF plt1_e FOR VALUES IN ('0000');
+INSERT INTO plt1_e SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 7, 10, 11, 12, 13, 14, 15, 16);
+ANALYZE plt1_e;
+-- test partition matching with N-way join
+EXPLAIN (COSTS OFF)
+SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
+ QUERY PLAN
+----------------------------------------------------------------------------------------
+ Sort
+ Sort Key: t1.c, t3.c
+ -> HashAggregate
+ Group Key: t1.c, t2.c, t3.c
+ -> Result
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = ltrim(t3.c, 'A'::text))
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt1_e_p4 t3
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ -> Hash Join
+ Hash Cond: ((t2_1.c)::text = ltrim(t3_1.c, 'A'::text))
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_e_p1 t3_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = ltrim(t3_2.c, 'A'::text))
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt1_e_p2 t3_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = ltrim(t3_3.c, 'A'::text))
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt1_e_p3 t3_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(42 rows)
+
+SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
+ avg | avg | avg | c | c | c
+----------------------+---------------------+----------------------+------+------+------
+ 246.5000000000000000 | 22.4666666666666667 | 268.9666666666666667 | 0000 | 0000 | 0000
+ 248.5000000000000000 | 21.3333333333333333 | 269.8333333333333333 | 0002 | 0002 | 0002
+ 249.5000000000000000 | 22.3333333333333333 | 271.8333333333333333 | 0003 | 0003 | 0003
+ 250.5000000000000000 | 23.3333333333333333 | 273.8333333333333333 | 0004 | 0004 | 0004
+ 251.5000000000000000 | 22.7666666666666667 | 274.2666666666666667 | 0005 | 0005 | 0005
+ 252.5000000000000000 | 22.2000000000000000 | 274.7000000000000000 | 0006 | 0006 | 0006
+ 246.0000000000000000 | 23.9655172413793103 | 269.9655172413793103 | 0008 | 0008 | 0008
+ 247.0000000000000000 | 23.3448275862068966 | 270.3448275862068966 | 0009 | 0009 | 0009
+(8 rows)
+
+-- Add an extra partition to plt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (13, 14);
+ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Join Filter: ((t1_1.b + t2_1.b) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + t2_2.b) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t1_3.b + t2_3.b) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Left Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Right Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Left Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Left Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(7 rows)
+
+-- right join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Hash Right Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(17 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 47 | 0013
+ | | 470 | 0011
+ | | 235 | 0014
+(8 rows)
+
+-- full join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Full Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(17 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 47 | 0013
+ | | 235 | 0014
+ | | 470 | 0011
+(10 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt2_p4 t2
+ -> Nested Loop
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> HashAggregate
+ Group Key: (t2_1.c)::text
+ -> Seq Scan on plt2_p1 t2_1
+ -> Materialize
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(29 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt1_p4 t2
+ -> Materialize
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2_1
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_3
+(26 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop Anti Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Anti Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt2_p1 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 188 | 0 | 0001
+ 282 | 0 | 0010
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p5 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t2
+ -> Seq Scan on plt1_p1 t2_1
+ -> Seq Scan on plt1_p2 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(21 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 47 | 0 | 0013
+ 235 | 0 | 0014
+ 470 | 0 | 0011
+(3 rows)
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt2_p5;
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0001','0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (1, 13, 14);
+ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Left Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Hash Right Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Full Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(23 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p5 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p1 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt1_p4 t2
+ -> Seq Scan on plt1_p1 t2_1
+ -> Seq Scan on plt1_p2 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(24 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+----------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Nested Loop Anti Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Materialize
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(20 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p5 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p1 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t2
+ -> Seq Scan on plt1_p1 t2_1
+ -> Seq Scan on plt1_p2 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(21 rows)
+
+-- partition have a NULL on one side, Partition-wise join is possible with
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- in this case NULL will be treated as addition partition bounds.
+DROP TABLE plt2_p5;
+DROP TABLE plt2_p4;
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000',NULL,'0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, case when i % :part_mod = 11 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (0,11,12);
+ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash
+ -> Seq Scan on plt1_p4 t1
+ -> Hash Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Join Filter: ((t1_1.b + t2_1.b) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + t2_2.b) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t1_3.b + t2_3.b) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Right Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash
+ -> Seq Scan on plt1_p4 t1
+ -> Hash Right Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Left Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Left Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(7 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+------------------------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Result
+ -> Append
+ -> Hash Left Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash
+ -> Seq Scan on plt1_p4 t1
+ -> Hash Left Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + t2_1.b) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Left Join
+ Hash Cond: ((t2_2.c)::text = (t1_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + t2_2.b) = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash Right Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + t2_3.b) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(28 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 |
+(6 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Append
+ -> Hash Full Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash
+ -> Seq Scan on plt1_p4 t1
+ -> Hash Full Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Full Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Full Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 |
+(8 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt2_p4 t2
+ -> Materialize
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Nested Loop
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> HashAggregate
+ Group Key: (t2_1.c)::text
+ -> Seq Scan on plt2_p1 t2_1
+ -> Materialize
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(29 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt1_p4 t2
+ -> Materialize
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2_1
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_3
+(26 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop Anti Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Anti Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt2_p1 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 188 | 0 | 0001
+ 282 | 0 | 0010
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt1_p4 t2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+---
+ 470 | 0 |
+(1 row)
+
+-- partition have a NULL on both side with different partition bounds w.r.t other side
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt1_p3;
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN (NULL,'0008','0009');
+INSERT INTO plt1 SELECT i, i % :cond_mod, case when i % :part_mod = 7 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (7,8,9);
+ANALYZE plt1;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Right Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Hash Left Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
--- test merge joins
-SET enable_hashjoin TO off;
-SET enable_nestloop TO off;
+-- full join
EXPLAIN (COSTS OFF)
-SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
- QUERY PLAN
-----------------------------------------------------------------
- Merge Append
- Sort Key: t1.a
- -> Merge Semi Join
- Merge Cond: (t1.a = t1_3.b)
- -> Sort
- Sort Key: t1.a
- -> Seq Scan on prt1_p1 t1
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Full Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
Filter: (b = 0)
- -> Merge Semi Join
- Merge Cond: (t1_3.b = (((t1_6.a + t1_6.b) / 2)))
- -> Sort
- Sort Key: t1_3.b
- -> Seq Scan on prt2_p1 t1_3
- -> Sort
- Sort Key: (((t1_6.a + t1_6.b) / 2))
- -> Seq Scan on prt1_e_p1 t1_6
- Filter: (c = 0)
- -> Merge Semi Join
- Merge Cond: (t1_1.a = t1_4.b)
- -> Sort
- Sort Key: t1_1.a
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on plt1_p1 t1_1
Filter: (b = 0)
- -> Merge Semi Join
- Merge Cond: (t1_4.b = (((t1_7.a + t1_7.b) / 2)))
- -> Sort
- Sort Key: t1_4.b
- -> Seq Scan on prt2_p2 t1_4
- -> Sort
- Sort Key: (((t1_7.a + t1_7.b) / 2))
- -> Seq Scan on prt1_e_p2 t1_7
- Filter: (c = 0)
- -> Merge Semi Join
- Merge Cond: (t1_2.a = t1_5.b)
- -> Sort
- Sort Key: t1_2.a
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on plt1_p2 t1_2
Filter: (b = 0)
- -> Merge Semi Join
- Merge Cond: (t1_5.b = (((t1_8.a + t1_8.b) / 2)))
- -> Sort
- Sort Key: t1_5.b
- -> Seq Scan on prt2_p3 t1_5
- -> Sort
- Sort Key: (((t1_8.a + t1_8.b) / 2))
- -> Seq Scan on prt1_e_p3 t1_8
- Filter: (c = 0)
-(47 rows)
-
-SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
- a | b | c
------+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+(22 rows)
EXPLAIN (COSTS OFF)
-SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- QUERY PLAN
-----------------------------------------------------------------------------------
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
Sort
- Sort Key: t1.a, t2.b, ((t3.a + t3.b))
- -> Result
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
-> Append
- -> Merge Left Join
- Merge Cond: (t1.a = t2.b)
- -> Sort
- Sort Key: t1.a
- -> Merge Left Join
- Merge Cond: ((((t3.a + t3.b) / 2)) = t1.a)
- -> Sort
- Sort Key: (((t3.a + t3.b) / 2))
- -> Seq Scan on prt1_e_p1 t3
- Filter: (c = 0)
- -> Sort
- Sort Key: t1.a
- -> Seq Scan on prt1_p1 t1
- -> Sort
- Sort Key: t2.b
- -> Seq Scan on prt2_p1 t2
- -> Merge Left Join
- Merge Cond: (t1_1.a = t2_1.b)
- -> Sort
- Sort Key: t1_1.a
- -> Merge Left Join
- Merge Cond: ((((t3_1.a + t3_1.b) / 2)) = t1_1.a)
- -> Sort
- Sort Key: (((t3_1.a + t3_1.b) / 2))
- -> Seq Scan on prt1_e_p2 t3_1
- Filter: (c = 0)
- -> Sort
- Sort Key: t1_1.a
- -> Seq Scan on prt1_p2 t1_1
- -> Sort
- Sort Key: t2_1.b
- -> Seq Scan on prt2_p2 t2_1
- -> Merge Left Join
- Merge Cond: (t1_2.a = t2_2.b)
- -> Sort
- Sort Key: t1_2.a
- -> Merge Left Join
- Merge Cond: ((((t3_2.a + t3_2.b) / 2)) = t1_2.a)
- -> Sort
- Sort Key: (((t3_2.a + t3_2.b) / 2))
- -> Seq Scan on prt1_e_p3 t3_2
- Filter: (c = 0)
- -> Sort
- Sort Key: t1_2.a
- -> Seq Scan on prt1_p3 t1_2
- -> Sort
- Sort Key: t2_2.b
- -> Seq Scan on prt2_p3 t2_2
-(52 rows)
-
-SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- a | c | b | c | ?column? | c
------+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 50 | 0050 | | | 100 | 0
- 100 | 0100 | | | 200 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 200 | 0200 | | | 400 | 0
- 250 | 0250 | | | 500 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 350 | 0350 | | | 700 | 0
- 400 | 0400 | | | 800 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
- 500 | 0500 | | | 1000 | 0
- 550 | 0550 | | | 1100 | 0
-(12 rows)
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt1_p4 t2
+ -> Seq Scan on plt1_p1 t2_1
+ -> Seq Scan on plt1_p2 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(22 rows)
-RESET enable_hashjoin;
-RESET enable_nestloop;
---
--- partitioned by multiple columns
---
-CREATE TABLE prt1_m (a int, b int, c int) PARTITION BY RANGE(a, ((a + b)/2));
-CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES FROM (0, 0) TO (250, 250);
-CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES FROM (250, 250) TO (500, 500);
-CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES FROM (500, 500) TO (600, 600);
-INSERT INTO prt1_m SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i;
-ANALYZE prt1_m;
-CREATE TABLE prt2_m (a int, b int, c int) PARTITION BY RANGE(((b + a)/2), b);
-CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES FROM (0, 0) TO (250, 250);
-CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES FROM (250, 250) TO (500, 500);
-CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES FROM (500, 500) TO (600, 600);
-INSERT INTO prt2_m SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i;
-ANALYZE prt2_m;
+-- anti join
EXPLAIN (COSTS OFF)
-SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
- QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
Sort
- Sort Key: prt1_m_p1.a, prt2_m_p1.b
- -> Append
- -> Hash Full Join
- Hash Cond: ((prt1_m_p1.a = ((prt2_m_p1.b + prt2_m_p1.a) / 2)) AND (((prt1_m_p1.a + prt1_m_p1.b) / 2) = prt2_m_p1.b))
- -> Seq Scan on prt1_m_p1
- Filter: (c = 0)
- -> Hash
- -> Seq Scan on prt2_m_p1
- Filter: (c = 0)
- -> Hash Full Join
- Hash Cond: ((prt1_m_p2.a = ((prt2_m_p2.b + prt2_m_p2.a) / 2)) AND (((prt1_m_p2.a + prt1_m_p2.b) / 2) = prt2_m_p2.b))
- -> Seq Scan on prt1_m_p2
- Filter: (c = 0)
- -> Hash
- -> Seq Scan on prt2_m_p2
- Filter: (c = 0)
- -> Hash Full Join
- Hash Cond: ((prt1_m_p3.a = ((prt2_m_p3.b + prt2_m_p3.a) / 2)) AND (((prt1_m_p3.a + prt1_m_p3.b) / 2) = prt2_m_p3.b))
- -> Seq Scan on prt1_m_p3
- Filter: (c = 0)
- -> Hash
- -> Seq Scan on prt2_m_p3
- Filter: (c = 0)
-(24 rows)
-
-SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
- a | c | b | c
------+---+-----+---
- 0 | 0 | 0 | 0
- 50 | 0 | |
- 100 | 0 | |
- 150 | 0 | 150 | 0
- 200 | 0 | |
- 250 | 0 | |
- 300 | 0 | 300 | 0
- 350 | 0 | |
- 400 | 0 | |
- 450 | 0 | 450 | 0
- 500 | 0 | |
- 550 | 0 | |
- | | 75 | 0
- | | 225 | 0
- | | 375 | 0
- | | 525 | 0
-(16 rows)
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+(19 rows)
---
--- tests for list partitioned tables.
---
-CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
-ANALYZE plt1;
-CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i;
-ANALYZE plt2;
---
--- list partitioned by expression
---
-CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
-CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
-ANALYZE plt1_e;
--- test partition matching with N-way join
EXPLAIN (COSTS OFF)
-SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
- QUERY PLAN
---------------------------------------------------------------------------------------
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
Sort
- Sort Key: t1.c, t3.c
- -> HashAggregate
- Group Key: t1.c, t2.c, t3.c
- -> Result
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
-> Append
- -> Hash Join
- Hash Cond: (t1.c = t2.c)
- -> Seq Scan on plt1_p1 t1
- -> Hash
- -> Hash Join
- Hash Cond: (t2.c = ltrim(t3.c, 'A'::text))
- -> Seq Scan on plt2_p1 t2
- -> Hash
- -> Seq Scan on plt1_e_p1 t3
- -> Hash Join
- Hash Cond: (t1_1.c = t2_1.c)
- -> Seq Scan on plt1_p2 t1_1
- -> Hash
- -> Hash Join
- Hash Cond: (t2_1.c = ltrim(t3_1.c, 'A'::text))
- -> Seq Scan on plt2_p2 t2_1
- -> Hash
- -> Seq Scan on plt1_e_p2 t3_1
- -> Hash Join
- Hash Cond: (t1_2.c = t2_2.c)
- -> Seq Scan on plt1_p3 t1_2
- -> Hash
- -> Hash Join
- Hash Cond: (t2_2.c = ltrim(t3_2.c, 'A'::text))
- -> Seq Scan on plt2_p3 t2_2
- -> Hash
- -> Seq Scan on plt1_e_p3 t3_2
-(33 rows)
-
-SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
- avg | avg | avg | c | c | c
-----------------------+----------------------+-----------------------+------+------+-------
- 24.0000000000000000 | 24.0000000000000000 | 48.0000000000000000 | 0000 | 0000 | A0000
- 74.0000000000000000 | 75.0000000000000000 | 148.0000000000000000 | 0001 | 0001 | A0001
- 124.0000000000000000 | 124.5000000000000000 | 248.0000000000000000 | 0002 | 0002 | A0002
- 174.0000000000000000 | 174.0000000000000000 | 348.0000000000000000 | 0003 | 0003 | A0003
- 224.0000000000000000 | 225.0000000000000000 | 448.0000000000000000 | 0004 | 0004 | A0004
- 274.0000000000000000 | 274.5000000000000000 | 548.0000000000000000 | 0005 | 0005 | A0005
- 324.0000000000000000 | 324.0000000000000000 | 648.0000000000000000 | 0006 | 0006 | A0006
- 374.0000000000000000 | 375.0000000000000000 | 748.0000000000000000 | 0007 | 0007 | A0007
- 424.0000000000000000 | 424.5000000000000000 | 848.0000000000000000 | 0008 | 0008 | A0008
- 474.0000000000000000 | 474.0000000000000000 | 948.0000000000000000 | 0009 | 0009 | A0009
- 524.0000000000000000 | 525.0000000000000000 | 1048.0000000000000000 | 0010 | 0010 | A0010
- 574.0000000000000000 | 574.5000000000000000 | 1148.0000000000000000 | 0011 | 0011 | A0011
-(12 rows)
+ -> Seq Scan on plt1_p4 t2
+ -> Seq Scan on plt1_p1 t2_1
+ -> Seq Scan on plt1_p2 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(19 rows)
-- joins where one of the relations is proven empty
EXPLAIN (COSTS OFF)
@@ -1286,16 +3969,22 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1
-> Hash Left Join
Hash Cond: (t2.b = a)
-> Append
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t2_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t2_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t2_3
Filter: (a = 0)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p4 t2_4
Filter: (a = 0)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p5 t2_5
Filter: (a = 0)
-> Hash
-> Result
One-Time Filter: false
-(14 rows)
+(20 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b;
@@ -1306,16 +3995,22 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1
-> Hash Left Join
Hash Cond: (t2.b = a)
-> Append
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t2_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t2_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t2_3
Filter: (a = 0)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p4 t2_4
Filter: (a = 0)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p5 t2_5
Filter: (a = 0)
-> Hash
-> Result
One-Time Filter: false
-(14 rows)
+(20 rows)
--
-- multi-leveled partitions
@@ -1676,64 +4371,70 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2 WHERE t1.a = t2.a;
Hash Join
Hash Cond: (t1.a = t2.a)
-> Append
- -> Seq Scan on prt1_p1 t1
- -> Seq Scan on prt1_p2 t1_1
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
-> Hash
-> Append
-> Seq Scan on prt4_n_p1 t2
-> Seq Scan on prt4_n_p2 t2_1
-> Seq Scan on prt4_n_p3 t2_2
-(11 rows)
+(13 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2, prt2 t3 WHERE t1.a = t2.a and t1.a = t3.b;
- QUERY PLAN
---------------------------------------------------------
+ QUERY PLAN
+----------------------------------------------------------
Hash Join
- Hash Cond: (t2.a = t1.a)
+ Hash Cond: (t3.b = t1.a)
-> Append
- -> Seq Scan on prt4_n_p1 t2
- -> Seq Scan on prt4_n_p2 t2_1
- -> Seq Scan on prt4_n_p3 t2_2
+ -> Seq Scan on prt2_p0 t3
+ -> Seq Scan on prt2_p1 t3_1
+ -> Seq Scan on prt2_p2 t3_2
+ -> Seq Scan on prt2_p3 t3_3
+ -> Seq Scan on prt2_p4 t3_4
+ -> Seq Scan on prt2_p5 t3_5
-> Hash
- -> Append
- -> Hash Join
- Hash Cond: (t1.a = t3.b)
- -> Seq Scan on prt1_p1 t1
- -> Hash
- -> Seq Scan on prt2_p1 t3
- -> Hash Join
- Hash Cond: (t1_1.a = t3_1.b)
- -> Seq Scan on prt1_p2 t1_1
- -> Hash
- -> Seq Scan on prt2_p2 t3_1
- -> Hash Join
- Hash Cond: (t1_2.a = t3_2.b)
- -> Seq Scan on prt1_p3 t1_2
- -> Hash
- -> Seq Scan on prt2_p3 t3_2
+ -> Hash Join
+ Hash Cond: (t1.a = t2.a)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Append
+ -> Seq Scan on prt4_n_p1 t2
+ -> Seq Scan on prt4_n_p2 t2_1
+ -> Seq Scan on prt4_n_p3 t2_2
(23 rows)
-- partition-wise join can not be applied if there are no equi-join conditions
-- between partition keys
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON (t1.a < t2.b);
- QUERY PLAN
----------------------------------------------------------
+ QUERY PLAN
+--------------------------------------------
Nested Loop Left Join
+ Join Filter: (t1.a < t2.b)
-> Append
- -> Seq Scan on prt1_p1 t1
- -> Seq Scan on prt1_p2 t1_1
- -> Seq Scan on prt1_p3 t1_2
- -> Append
- -> Index Scan using iprt2_p1_b on prt2_p1 t2
- Index Cond: (t1.a < b)
- -> Index Scan using iprt2_p2_b on prt2_p2 t2_1
- Index Cond: (t1.a < b)
- -> Index Scan using iprt2_p3_b on prt2_p3 t2_2
- Index Cond: (t1.a < b)
-(12 rows)
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Materialize
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+(16 rows)
-- equi-join with join condition on partial keys does not qualify for
-- partition-wise join
@@ -1819,16 +4520,17 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 JOIN prt2_n t2 ON (t1.c = t2.c) JOI
-> Seq Scan on prt2_n_p2 t2_1
-> Hash
-> Hash Join
- Hash Cond: (t3.c = (t1.c)::text)
+ Hash Cond: ((t3.c)::text = (t1.c)::text)
-> Append
- -> Seq Scan on plt1_p1 t3
- -> Seq Scan on plt1_p2 t3_1
- -> Seq Scan on plt1_p3 t3_2
+ -> Seq Scan on plt1_p4 t3
+ -> Seq Scan on plt1_p1 t3_1
+ -> Seq Scan on plt1_p2 t3_2
+ -> Seq Scan on plt1_p3 t3_3
-> Hash
-> Append
-> Seq Scan on prt1_n_p1 t1
-> Seq Scan on prt1_n_p2 t1_1
-(16 rows)
+(17 rows)
-- partition-wise join can not be applied for a join between list and range
-- partitioned table
@@ -1839,12 +4541,14 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 FULL JOIN prt1 t2 ON (t1.c = t2.c);
Hash Full Join
Hash Cond: ((t2.c)::text = (t1.c)::text)
-> Append
- -> Seq Scan on prt1_p1 t2
- -> Seq Scan on prt1_p2 t2_1
- -> Seq Scan on prt1_p3 t2_2
+ -> Seq Scan on prt1_p0 t2
+ -> Seq Scan on prt1_p1 t2_1
+ -> Seq Scan on prt1_p2 t2_2
+ -> Seq Scan on prt1_p3 t2_3
+ -> Seq Scan on prt1_p4 t2_4
-> Hash
-> Append
-> Seq Scan on prt1_n_p1 t1
-> Seq Scan on prt1_n_p2 t1_1
-(10 rows)
+(12 rows)
diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql
index 2bb1aff..0c7d99c 100644
--- a/src/test/regress/sql/partition_join.sql
+++ b/src/test/regress/sql/partition_join.sql
@@ -10,25 +10,39 @@ SET enable_partition_wise_join to true;
-- partitioned by a single column
--
CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_p0 PARTITION OF prt1 FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250);
CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600);
CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500);
-INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0;
+CREATE TABLE prt1_p4 PARTITION OF prt1 FOR VALUES FROM (600) TO (800);
+INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 2 = 0;
+CREATE INDEX iprt1_p0_a on prt1_p0(a);
CREATE INDEX iprt1_p1_a on prt1_p1(a);
CREATE INDEX iprt1_p2_a on prt1_p2(a);
CREATE INDEX iprt1_p3_a on prt1_p3(a);
+CREATE INDEX iprt1_p4_a on prt1_p4(a);
ANALYZE prt1;
+-- prt2 have missing starting MINVALUE to -250 range and
+-- extra bounds from 800 to MAXVALUE
CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_p0 PARTITION OF prt2 FOR VALUES FROM (-250) TO (0);
CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250);
CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500);
CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600);
-INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (MAXVALUE);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 3 = 0;
+CREATE INDEX iprt2_p0_b on prt2_p0(b);
CREATE INDEX iprt2_p1_b on prt2_p1(b);
CREATE INDEX iprt2_p2_b on prt2_p2(b);
CREATE INDEX iprt2_p3_b on prt2_p3(b);
+CREATE INDEX iprt2_p4_b on prt2_p4(b);
ANALYZE prt2;
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
+
-- inner join
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
@@ -77,11 +91,19 @@ EXPLAIN (COSTS OFF)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
+EXPLAIN (COSTS OFF)
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+
-- Anti-join with aggregates
EXPLAIN (COSTS OFF)
SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b);
SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b);
+EXPLAIN (COSTS OFF)
+SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0;
+SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0;
+
-- lateral reference
EXPLAIN (COSTS OFF)
SELECT * FROM prt1 t1 LEFT JOIN LATERAL
@@ -103,20 +125,30 @@ SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
-- partitioned by expression
--
CREATE TABLE prt1_e (a int, b int, c int) PARTITION BY RANGE(((a + b)/2));
+CREATE TABLE prt1_e_p0 PARTITION OF prt1_e FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES FROM (0) TO (250);
CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES FROM (250) TO (500);
CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt1_e_p4 PARTITION OF prt1_e FOR VALUES FROM (600) TO (MAXVALUE);
INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(600, 799, 2) i;
+CREATE INDEX iprt1_e_p0_ab2 on prt1_e_p1(((a+b)/2));
CREATE INDEX iprt1_e_p1_ab2 on prt1_e_p1(((a+b)/2));
CREATE INDEX iprt1_e_p2_ab2 on prt1_e_p2(((a+b)/2));
CREATE INDEX iprt1_e_p3_ab2 on prt1_e_p3(((a+b)/2));
+CREATE INDEX iprt1_e_p4_ab2 on prt1_e_p1(((a+b)/2));
ANALYZE prt1_e;
CREATE TABLE prt2_e (a int, b int, c int) PARTITION BY RANGE(((b + a)/2));
+CREATE TABLE prt2_e_p0 PARTITION OF prt2_e FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES FROM (0) TO (250);
CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES FROM (250) TO (500);
CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt2_e_p4 PARTITION OF prt2_e FOR VALUES FROM (600) TO (MAXVALUE);
INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(600, 799, 3) i;
ANALYZE prt2_e;
EXPLAIN (COSTS OFF)
@@ -168,6 +200,85 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
RESET enable_hashjoin;
RESET enable_nestloop;
+-- Add an extra partition to prt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+DROP TABLE prt2_p4;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (800);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (800) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999) i WHERE i % 3 = 0;
+ANALYZE prt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- partition-wise join can not handle missing partition on the inner side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.b;
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE coalesce(t1.b, 0) + coalesce(t2.a, 0) = 0 ORDER BY t1.a, t2.a;
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE prt2_p4;
+DROP TABLE prt2_p5;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (700);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (700) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999, 3) i;
+ANALYZE prt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
--
-- partitioned by multiple columns
--
@@ -192,28 +303,79 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1
--
-- tests for list partitioned tables.
--
-CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
-ANALYZE plt1;
+\set part_mod 17
+\set cond_mod 47
+\set num_rows 500
+
+CREATE TABLE plt1 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0001','0002','0003');
+CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0008','0009');
+CREATE TABLE plt1_p4 PARTITION OF plt1 FOR VALUES IN ('0000','0010');
+INSERT INTO plt1 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (7, 11, 12, 13, 14, 15, 16);
+ANALYSE plt1;
+
+-- plt2 have missing starting 0001, additional 0007, missing ending 0010
+-- and additional 0011 and 0012 bounds
+CREATE TABLE plt2 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002','0003');
+CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0007','0008','0009');
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000','0011','0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 10, 13, 14, 15, 16);
+ANALYSE plt2;
+
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
-CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i;
-ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
--
-- list partitioned by expression
--
CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
-CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0002', '0003');
+CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0004', '0005', '0006');
+CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0008', '0009');
+CREATE TABLE plt1_e_p4 PARTITION OF plt1_e FOR VALUES IN ('0000');
+INSERT INTO plt1_e SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 7, 10, 11, 12, 13, 14, 15, 16);
ANALYZE plt1_e;
-- test partition matching with N-way join
@@ -221,6 +383,175 @@ EXPLAIN (COSTS OFF)
SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
+-- Add an extra partition to plt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (13, 14);
+ANALYZE plt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+
+-- full join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt2_p5;
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0001','0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (1, 13, 14);
+ANALYZE plt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- partition have a NULL on one side, Partition-wise join is possible with
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- in this case NULL will be treated as addition partition bounds.
+DROP TABLE plt2_p5;
+DROP TABLE plt2_p4;
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000',NULL,'0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, case when i % :part_mod = 11 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (0,11,12);
+ANALYZE plt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- partition have a NULL on both side with different partition bounds w.r.t other side
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt1_p3;
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN (NULL,'0008','0009');
+INSERT INTO plt1 SELECT i, i % :cond_mod, case when i % :part_mod = 7 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (7,8,9);
+ANALYZE plt1;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
-- joins where one of the relations is proven empty
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a = 1 AND t1.a = 2;
--
1.7.9.5
On Tue, Sep 5, 2017 at 4:34 PM, Ashutosh Bapat <
ashutosh.bapat@enterprisedb.com> wrote:
I have fixed the issues which were marked as TODOs in the attached
patches. Also, I have included your test change patch in my series of
patches. Are there any other issues you have commented out?Thanks Ashutosh, All commented issue got fixed. I am working on some
combinations of N-way joins
to test partition matching, will send those as well once done.
Ashutosh Bapat <ashutosh.bapat@enterprisedb.com> wrote:
I have fixed the issues which were marked as TODOs in the attached
patches. Also, I have included your test change patch in my series of
patches.
I've noticed that partition_bounds_merge() is called twice from
make_join_rel():
* build_join_rel -> build_joinrel_partition_info -> partition_bounds_merge
* try_partition_wise_join -> partition_bounds_merge
Is this intentional, or just a thinko?
--
Antonin Houska
Cybertec Schönig & Schönig GmbH
Gröhrmühlgasse 26
A-2700 Wiener Neustadt
Web: http://www.postgresql-support.de, http://www.cybertec.at
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Sep 7, 2017 at 7:34 PM, Antonin Houska <ah@cybertec.at> wrote:
Ashutosh Bapat <ashutosh.bapat@enterprisedb.com> wrote:
I have fixed the issues which were marked as TODOs in the attached
patches. Also, I have included your test change patch in my series of
patches.I've noticed that partition_bounds_merge() is called twice from
make_join_rel():* build_join_rel -> build_joinrel_partition_info -> partition_bounds_merge
* try_partition_wise_join -> partition_bounds_merge
Is this intentional, or just a thinko?
This is expected. partition_bounds_merge() also returns the pairs of
matching partitions. So, we have to call that function for every pair
of joining relations.
--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Here's updated patch set based on the basic partition-wise join
committed. The patchset applies on top of the patch to optimize the
case of dummy partitioned tables [1]/messages/by-id/CAFjFpRcPvT5ay9_p3e-k2Cwu4bW_rypON7ceJVWhsU3Uk4Nmmg@mail.gmail.com.
Right now, the advanced partition matching algorithm bails out when
either of the joining relations has a default partition.
[1]: /messages/by-id/CAFjFpRcPvT5ay9_p3e-k2Cwu4bW_rypON7ceJVWhsU3Uk4Nmmg@mail.gmail.com
--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
Attachments:
0004-Tests-for-0-1-1-1-and-1-0-partition-matching.patchtext/x-patch; charset=US-ASCII; name=0004-Tests-for-0-1-1-1-and-1-0-partition-matching.patchDownload
From 5a450f85c3ddbc953d95188f90115d3c82b91e70 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>
Date: Tue, 5 Sep 2017 09:51:41 +0530
Subject: [PATCH 4/4] Tests for 0:1, 1:1 and 1:0 partition matching
Rajkumar Raghuvanshi and Ashutosh Bapat.
---
src/test/regress/expected/partition_join.out | 3891 ++++++++++++++++++++++----
src/test/regress/sql/partition_join.sql | 367 ++-
2 files changed, 3628 insertions(+), 630 deletions(-)
diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out
index 234b8b5..bc8aa5b 100644
--- a/src/test/regress/expected/partition_join.out
+++ b/src/test/regress/expected/partition_join.out
@@ -8,105 +8,152 @@ SET enable_partition_wise_join to true;
-- partitioned by a single column
--
CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_p0 PARTITION OF prt1 FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250);
CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600);
CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500);
-INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0;
+CREATE TABLE prt1_p4 PARTITION OF prt1 FOR VALUES FROM (600) TO (800);
+INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 2 = 0;
+CREATE INDEX iprt1_p0_a on prt1_p0(a);
CREATE INDEX iprt1_p1_a on prt1_p1(a);
CREATE INDEX iprt1_p2_a on prt1_p2(a);
CREATE INDEX iprt1_p3_a on prt1_p3(a);
+CREATE INDEX iprt1_p4_a on prt1_p4(a);
ANALYZE prt1;
+-- prt2 have missing starting MINVALUE to -250 range and
+-- extra bounds from 800 to MAXVALUE
CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_p0 PARTITION OF prt2 FOR VALUES FROM (-250) TO (0);
CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250);
CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500);
CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600);
-INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (MAXVALUE);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 3 = 0;
+CREATE INDEX iprt2_p0_b on prt2_p0(b);
CREATE INDEX iprt2_p1_b on prt2_p1(b);
CREATE INDEX iprt2_p2_b on prt2_p2(b);
CREATE INDEX iprt2_p3_b on prt2_p3(b);
+CREATE INDEX iprt2_p4_b on prt2_p4(b);
ANALYZE prt2;
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
-- inner join
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
- QUERY PLAN
---------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------
Sort
Sort Key: t1.a
-> Append
-> Hash Join
Hash Cond: (t2.b = t1.a)
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
-> Hash
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Hash Join
Hash Cond: (t2_1.b = t1_1.a)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Hash
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Hash Join
Hash Cond: (t2_2.b = t1_2.a)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p2 t2_2
-> Hash
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
-(21 rows)
+ -> Nested Loop
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ -> Hash Join
+ Hash Cond: (t2_4.b = t1_4.a)
+ -> Seq Scan on prt2_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(32 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
- a | c | b | c
------+------+-----+------
- 0 | 0000 | 0 | 0000
- 150 | 0150 | 150 | 0150
- 300 | 0300 | 300 | 0300
- 450 | 0450 | 450 | 0450
-(4 rows)
+ a | c | b | c
+------+-------+------+-------
+ -150 | -0150 | -150 | -0150
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150
+ 300 | 0300 | 300 | 0300
+ 450 | 0450 | 450 | 0450
+ 600 | 0600 | 600 | 0600
+ 750 | 0750 | 750 | 0750
+(7 rows)
-- left outer join, with whole-row reference
EXPLAIN (COSTS OFF)
SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
- QUERY PLAN
---------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------
Sort
Sort Key: t1.a, t2.b
-> Result
-> Append
-> Hash Right Join
Hash Cond: (t2.b = t1.a)
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
-> Hash
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Hash Right Join
Hash Cond: (t2_1.b = t1_1.a)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Hash
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Hash Right Join
Hash Cond: (t2_2.b = t1_2.a)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p2 t2_2
-> Hash
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
-(22 rows)
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (t1_3.a = b)
+ -> Hash Right Join
+ Hash Cond: (t2_4.b = t1_4.a)
+ -> Seq Scan on prt2_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(33 rows)
SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
- t1 | t2
---------------+--------------
- (0,0,0000) | (0,0,0000)
- (50,0,0050) |
- (100,0,0100) |
- (150,0,0150) | (0,150,0150)
- (200,0,0200) |
- (250,0,0250) |
- (300,0,0300) | (0,300,0300)
- (350,0,0350) |
- (400,0,0400) |
- (450,0,0450) | (0,450,0450)
- (500,0,0500) |
- (550,0,0550) |
-(12 rows)
+ t1 | t2
+----------------+----------------
+ (-250,0,-0250) |
+ (-200,0,-0200) |
+ (-150,0,-0150) | (0,-150,-0150)
+ (-100,0,-0100) |
+ (-50,0,-0050) |
+ (0,0,0000) | (0,0,0000)
+ (50,0,0050) |
+ (100,0,0100) |
+ (150,0,0150) | (0,150,0150)
+ (200,0,0200) |
+ (250,0,0250) |
+ (300,0,0300) | (0,300,0300)
+ (350,0,0350) |
+ (400,0,0400) |
+ (450,0,0450) | (0,450,0450)
+ (500,0,0500) |
+ (550,0,0550) |
+ (600,0,0600) | (0,600,0600)
+ (650,0,0650) |
+ (700,0,0700) |
+ (750,0,0750) | (0,750,0750)
+(21 rows)
-- right outer join
EXPLAIN (COSTS OFF)
@@ -119,35 +166,53 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHE
-> Append
-> Hash Right Join
Hash Cond: (t1.a = t2.b)
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
-> Hash
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
Filter: (a = 0)
-> Hash Right Join
Hash Cond: (t1_1.a = t2_1.b)
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
-> Hash
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
+ Filter: (a = 0)
+ -> Hash Right Join
+ Hash Cond: (t1_2.a = t2_2.b)
+ -> Seq Scan on prt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on prt2_p2 t2_2
Filter: (a = 0)
-> Nested Loop Left Join
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p3 t2_3
Filter: (a = 0)
- -> Index Scan using iprt1_p3_a on prt1_p3 t1_2
- Index Cond: (a = t2_2.b)
-(21 rows)
+ -> Index Scan using iprt1_p3_a on prt1_p3 t1_3
+ Index Cond: (a = t2_3.b)
+ -> Hash Right Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+(33 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b;
- a | c | b | c
------+------+-----+------
- 0 | 0000 | 0 | 0000
- 150 | 0150 | 150 | 0150
- 300 | 0300 | 300 | 0300
- 450 | 0450 | 450 | 0450
- | | 75 | 0075
- | | 225 | 0225
- | | 375 | 0375
- | | 525 | 0525
-(8 rows)
+ a | c | b | c
+------+-------+------+-------
+ -150 | -0150 | -150 | -0150
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150
+ 300 | 0300 | 300 | 0300
+ 450 | 0450 | 450 | 0450
+ 600 | 0600 | 600 | 0600
+ 750 | 0750 | 750 | 0750
+ | | -225 | -0225
+ | | -75 | -0075
+ | | 75 | 0075
+ | | 225 | 0225
+ | | 375 | 0375
+ | | 525 | 0525
+ | | 675 | 0675
+(14 rows)
-- full outer join, with placeholder vars
EXPLAIN (COSTS OFF)
@@ -155,9 +220,17 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0)
QUERY PLAN
------------------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, prt2_p1.b
+ Sort Key: prt1_p0.a, prt2_p0.b
-> Append
-> Hash Full Join
+ Hash Cond: (prt1_p0.a = prt2_p0.b)
+ Filter: (((50) = prt1_p0.a) OR ((75) = prt2_p0.b))
+ -> Seq Scan on prt1_p0
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p0
+ Filter: (a = 0)
+ -> Hash Full Join
Hash Cond: (prt1_p1.a = prt2_p1.b)
Filter: (((50) = prt1_p1.a) OR ((75) = prt2_p1.b))
-> Seq Scan on prt1_p1
@@ -181,7 +254,15 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0)
-> Hash
-> Seq Scan on prt2_p3
Filter: (a = 0)
-(27 rows)
+ -> Hash Full Join
+ Hash Cond: (prt1_p4.a = prt2_p4.b)
+ Filter: (((50) = prt1_p4.a) OR ((75) = prt2_p4.b))
+ -> Seq Scan on prt1_p4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4
+ Filter: (a = 0)
+(43 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b;
a | c | b | c
@@ -218,9 +299,16 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JO
QUERY PLAN
-----------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, b
+ Sort Key: prt1_p0.a, b
-> Append
-> Hash Left Join
+ Hash Cond: (prt1_p0.a = b)
+ -> Seq Scan on prt1_p0
+ Filter: ((a < 450) AND (b = 0))
+ -> Hash
+ -> Result
+ One-Time Filter: false
+ -> Hash Left Join
Hash Cond: (prt1_p1.a = b)
-> Seq Scan on prt1_p1
Filter: ((a < 450) AND (b = 0))
@@ -234,30 +322,43 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JO
-> Hash
-> Seq Scan on prt1_p2
Filter: ((a < 450) AND (b = 0))
-(17 rows)
+(24 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
- a | c | b | c
------+------+-----+------
- 0 | 0000 | |
- 50 | 0050 | |
- 100 | 0100 | |
- 150 | 0150 | |
- 200 | 0200 | |
- 250 | 0250 | |
- 300 | 0300 | 300 | 0300
- 350 | 0350 | |
- 400 | 0400 | |
-(9 rows)
+ a | c | b | c
+------+-------+-----+------
+ -250 | -0250 | |
+ -200 | -0200 | |
+ -150 | -0150 | |
+ -100 | -0100 | |
+ -50 | -0050 | |
+ 0 | 0000 | |
+ 50 | 0050 | |
+ 100 | 0100 | |
+ 150 | 0150 | |
+ 200 | 0200 | |
+ 250 | 0250 | |
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 | |
+ 400 | 0400 | |
+(14 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b;
QUERY PLAN
------------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, b
+ Sort Key: prt1_p0.a, b
-> Append
-> Hash Full Join
+ Hash Cond: (prt1_p0.a = b)
+ Filter: ((prt1_p0.b = 0) OR (a = 0))
+ -> Seq Scan on prt1_p0
+ Filter: (a < 450)
+ -> Hash
+ -> Result
+ One-Time Filter: false
+ -> Hash Full Join
Hash Cond: (prt1_p1.a = b)
Filter: ((prt1_p1.b = 0) OR (a = 0))
-> Seq Scan on prt1_p1
@@ -281,64 +382,153 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JO
-> Hash
-> Result
One-Time Filter: false
-(27 rows)
+ -> Hash Full Join
+ Hash Cond: (prt2_p4.b = a)
+ Filter: ((b = 0) OR (prt2_p4.a = 0))
+ -> Seq Scan on prt2_p4
+ Filter: (b > 250)
+ -> Hash
+ -> Result
+ One-Time Filter: false
+(43 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b;
- a | c | b | c
------+------+-----+------
- 0 | 0000 | |
- 50 | 0050 | |
- 100 | 0100 | |
- 150 | 0150 | |
- 200 | 0200 | |
- 250 | 0250 | |
- 300 | 0300 | 300 | 0300
- 350 | 0350 | |
- 400 | 0400 | |
- | | 375 | 0375
- | | 450 | 0450
- | | 525 | 0525
-(12 rows)
+ a | c | b | c
+------+-------+-----+------
+ -250 | -0250 | |
+ -200 | -0200 | |
+ -150 | -0150 | |
+ -100 | -0100 | |
+ -50 | -0050 | |
+ 0 | 0000 | |
+ 50 | 0050 | |
+ 100 | 0100 | |
+ 150 | 0150 | |
+ 200 | 0200 | |
+ 250 | 0250 | |
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 | |
+ 400 | 0400 | |
+ | | 375 | 0375
+ | | 450 | 0450
+ | | 525 | 0525
+ | | 600 | 0600
+ | | 675 | 0675
+ | | 750 | 0750
+(20 rows)
-- Semi-join
EXPLAIN (COSTS OFF)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
- QUERY PLAN
---------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------
Sort
Sort Key: t1.a
-> Append
-> Hash Semi Join
Hash Cond: (t1.a = t2.b)
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Hash
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
Filter: (a = 0)
-> Hash Semi Join
Hash Cond: (t1_1.a = t2_1.b)
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Hash
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
Filter: (a = 0)
- -> Nested Loop Semi Join
- Join Filter: (t1_2.a = t2_2.b)
- -> Seq Scan on prt1_p3 t1_2
+ -> Hash Semi Join
+ Hash Cond: (t1_2.a = t2_2.b)
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
- -> Materialize
- -> Seq Scan on prt2_p3 t2_2
+ -> Hash
+ -> Seq Scan on prt2_p2 t2_2
Filter: (a = 0)
-(24 rows)
+ -> Nested Loop
+ -> HashAggregate
+ Group Key: t2_3.b
+ -> Seq Scan on prt2_p3 t2_3
+ Filter: (a = 0)
+ -> Index Scan using iprt1_p3_a on prt1_p3 t1_3
+ Index Cond: (a = t2_3.b)
+ Filter: (b = 0)
+ -> Hash Semi Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+(39 rows)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
- a | b | c
------+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
+ a | b | c
+------+---+-------
+ -150 | 0 | -0150
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 300 | 0 | 0300
+ 450 | 0 | 0450
+ 600 | 0 | 0600
+ 750 | 0 | 0750
+(7 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.b
+ -> Append
+ -> Hash Semi Join
+ Hash Cond: (t1.b = t2.a)
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p0 t2
+ Filter: (b = 0)
+ -> Hash Semi Join
+ Hash Cond: (t1_1.b = t2_1.a)
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p1 t2_1
+ Filter: (b = 0)
+ -> Hash Semi Join
+ Hash Cond: (t1_2.b = t2_2.a)
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p2 t2_2
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: (t1_3.b = t2_3.a)
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Seq Scan on prt1_p3 t2_3
+ Filter: (b = 0)
+ -> Hash Semi Join
+ Hash Cond: (t1_4.b = t2_4.a)
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p4 t2_4
+ Filter: (b = 0)
+(37 rows)
+
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+ a | b | c
+---+------+-------
+ 0 | -150 | -0150
+ 0 | 0 | 0000
+ 0 | 150 | 0150
+ 0 | 300 | 0300
+ 0 | 450 | 0450
+ 0 | 600 | 0600
+ 0 | 750 | 0750
+(7 rows)
-- Anti-join with aggregates
EXPLAIN (COSTS OFF)
@@ -349,27 +539,82 @@ SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS
-> Append
-> Hash Anti Join
Hash Cond: (t1.a = t2.b)
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
-> Hash
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
-> Hash Anti Join
Hash Cond: (t1_1.a = t2_1.b)
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
-> Hash
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Hash Anti Join
Hash Cond: (t1_2.a = t2_2.b)
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
-> Hash
- -> Seq Scan on prt2_p3 t2_2
-(17 rows)
+ -> Seq Scan on prt2_p2 t2_2
+ -> Hash Anti Join
+ Hash Cond: (t1_3.a = t2_3.b)
+ -> Seq Scan on prt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on prt2_p3 t2_3
+ -> Hash Anti Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+(27 rows)
SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b);
- sum | avg | sum | avg
--------+----------------------+------+---------------------
- 60000 | 300.0000000000000000 | 2400 | 12.0000000000000000
+ sum | avg | sum | avg
+-------+----------------------+------+--------------------
+ 95550 | 273.0000000000000000 | 2200 | 6.2857142857142857
(1 row)
+EXPLAIN (COSTS OFF)
+SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0;
+ QUERY PLAN
+--------------------------------------------------------------
+ Append
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p0_a on prt1_p0 t2
+ Index Cond: (a = t1.b)
+ -> Hash Anti Join
+ Hash Cond: (t1_1.b = t2_1.a)
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_p1 t2_1
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2
+ Index Cond: (a = t1_2.b)
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3
+ Index Cond: (a = t1_3.b)
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p4_a on prt1_p4 t2_4
+ Index Cond: (a = t1_4.b)
+(27 rows)
+
+SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0;
+ b | c
+------+-------
+ -225 | -0225
+ -75 | -0075
+ 75 | 0075
+ 225 | 0225
+ 375 | 0375
+ 525 | 0525
+ 675 | 0675
+(7 rows)
+
-- lateral reference
EXPLAIN (COSTS OFF)
SELECT * FROM prt1 t1 LEFT JOIN LATERAL
@@ -382,49 +627,74 @@ SELECT * FROM prt1 t1 LEFT JOIN LATERAL
-> Result
-> Append
-> Nested Loop Left Join
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Nested Loop
- -> Index Only Scan using iprt1_p1_a on prt1_p1 t2
+ -> Index Only Scan using iprt1_p0_a on prt1_p0 t2
Index Cond: (a = t1.a)
- -> Index Scan using iprt2_p1_b on prt2_p1 t3
+ -> Index Scan using iprt2_p0_b on prt2_p0 t3
Index Cond: (b = t2.a)
-> Nested Loop Left Join
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Nested Loop
- -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_1
+ -> Index Only Scan using iprt1_p1_a on prt1_p1 t2_1
Index Cond: (a = t1_1.a)
- -> Index Scan using iprt2_p2_b on prt2_p2 t3_1
+ -> Index Scan using iprt2_p1_b on prt2_p1 t3_1
Index Cond: (b = t2_1.a)
-> Nested Loop Left Join
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
-> Nested Loop
- -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_2
+ -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2
Index Cond: (a = t1_2.a)
- -> Index Scan using iprt2_p3_b on prt2_p3 t3_2
+ -> Index Scan using iprt2_p2_b on prt2_p2 t3_2
Index Cond: (b = t2_2.a)
-(28 rows)
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Nested Loop
+ -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3
+ Index Cond: (a = t1_3.a)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t3_3
+ Index Cond: (b = t2_3.a)
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Nested Loop
+ -> Index Only Scan using iprt1_p4_a on prt1_p4 t2_4
+ Index Cond: (a = t1_4.a)
+ -> Index Scan using iprt2_p4_b on prt2_p4 t3_4
+ Index Cond: (b = t2_4.a)
+(44 rows)
SELECT * FROM prt1 t1 LEFT JOIN LATERAL
(SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
ON t1.a = ss.t2a WHERE t1.b = 0 ORDER BY t1.a;
- a | b | c | t2a | t3a | least
------+---+------+-----+-----+-------
- 0 | 0 | 0000 | 0 | 0 | 0
- 50 | 0 | 0050 | | |
- 100 | 0 | 0100 | | |
- 150 | 0 | 0150 | 150 | 0 | 150
- 200 | 0 | 0200 | | |
- 250 | 0 | 0250 | | |
- 300 | 0 | 0300 | 300 | 0 | 300
- 350 | 0 | 0350 | | |
- 400 | 0 | 0400 | | |
- 450 | 0 | 0450 | 450 | 0 | 450
- 500 | 0 | 0500 | | |
- 550 | 0 | 0550 | | |
-(12 rows)
+ a | b | c | t2a | t3a | least
+------+---+-------+------+-----+-------
+ -250 | 0 | -0250 | | |
+ -200 | 0 | -0200 | | |
+ -150 | 0 | -0150 | -150 | 0 | -150
+ -100 | 0 | -0100 | | |
+ -50 | 0 | -0050 | | |
+ 0 | 0 | 0000 | 0 | 0 | 0
+ 50 | 0 | 0050 | | |
+ 100 | 0 | 0100 | | |
+ 150 | 0 | 0150 | 150 | 0 | 150
+ 200 | 0 | 0200 | | |
+ 250 | 0 | 0250 | | |
+ 300 | 0 | 0300 | 300 | 0 | 300
+ 350 | 0 | 0350 | | |
+ 400 | 0 | 0400 | | |
+ 450 | 0 | 0450 | 450 | 0 | 450
+ 500 | 0 | 0500 | | |
+ 550 | 0 | 0550 | | |
+ 600 | 0 | 0600 | 600 | 0 | 600
+ 650 | 0 | 0650 | | |
+ 700 | 0 | 0700 | | |
+ 750 | 0 | 0750 | 750 | 0 | 750
+(21 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
@@ -438,64 +708,95 @@ SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
Hash Cond: ((t1.c)::text = (t2.c)::text)
Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
-> Append
- -> Seq Scan on prt1_p1 t1
- -> Seq Scan on prt1_p2 t1_1
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
-> Hash
-> Append
-> Hash Join
Hash Cond: (t2.a = t3.b)
- -> Seq Scan on prt1_p1 t2
+ -> Seq Scan on prt1_p0 t2
-> Hash
- -> Seq Scan on prt2_p1 t3
+ -> Seq Scan on prt2_p0 t3
-> Hash Join
Hash Cond: (t2_1.a = t3_1.b)
- -> Seq Scan on prt1_p2 t2_1
+ -> Seq Scan on prt1_p1 t2_1
-> Hash
- -> Seq Scan on prt2_p2 t3_1
+ -> Seq Scan on prt2_p1 t3_1
-> Hash Join
Hash Cond: (t2_2.a = t3_2.b)
- -> Seq Scan on prt1_p3 t2_2
+ -> Seq Scan on prt1_p2 t2_2
-> Hash
- -> Seq Scan on prt2_p3 t3_2
-(26 rows)
+ -> Seq Scan on prt2_p2 t3_2
+ -> Hash Join
+ Hash Cond: (t2_3.a = t3_3.b)
+ -> Seq Scan on prt1_p3 t2_3
+ -> Hash
+ -> Seq Scan on prt2_p3 t3_3
+ -> Hash Join
+ Hash Cond: (t2_4.a = t3_4.b)
+ -> Seq Scan on prt1_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt2_p4 t3_4
+(38 rows)
SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
(SELECT t2.a AS t2a, t3.a AS t3a, t2.b t2b, t2.c t2c, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
ON t1.c = ss.t2c WHERE (t1.b + coalesce(ss.t2b, 0)) = 0 ORDER BY t1.a;
- a | t2a | t2c
------+-----+------
- 0 | 0 | 0000
- 50 | |
- 100 | |
- 150 | 150 | 0150
- 200 | |
- 250 | |
- 300 | 300 | 0300
- 350 | |
- 400 | |
- 450 | 450 | 0450
- 500 | |
- 550 | |
-(12 rows)
+ a | t2a | t2c
+------+------+-------
+ -250 | |
+ -200 | |
+ -150 | -150 | -0150
+ -100 | |
+ -50 | |
+ 0 | 0 | 0000
+ 50 | |
+ 100 | |
+ 150 | 150 | 0150
+ 200 | |
+ 250 | |
+ 300 | 300 | 0300
+ 350 | |
+ 400 | |
+ 450 | 450 | 0450
+ 500 | |
+ 550 | |
+ 600 | 600 | 0600
+ 650 | |
+ 700 | |
+ 750 | 750 | 0750
+(21 rows)
--
-- partitioned by expression
--
CREATE TABLE prt1_e (a int, b int, c int) PARTITION BY RANGE(((a + b)/2));
+CREATE TABLE prt1_e_p0 PARTITION OF prt1_e FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES FROM (0) TO (250);
CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES FROM (250) TO (500);
CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt1_e_p4 PARTITION OF prt1_e FOR VALUES FROM (600) TO (MAXVALUE);
INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(600, 799, 2) i;
+CREATE INDEX iprt1_e_p0_ab2 on prt1_e_p1(((a+b)/2));
CREATE INDEX iprt1_e_p1_ab2 on prt1_e_p1(((a+b)/2));
CREATE INDEX iprt1_e_p2_ab2 on prt1_e_p2(((a+b)/2));
CREATE INDEX iprt1_e_p3_ab2 on prt1_e_p3(((a+b)/2));
+CREATE INDEX iprt1_e_p4_ab2 on prt1_e_p1(((a+b)/2));
ANALYZE prt1_e;
CREATE TABLE prt2_e (a int, b int, c int) PARTITION BY RANGE(((b + a)/2));
+CREATE TABLE prt2_e_p0 PARTITION OF prt2_e FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES FROM (0) TO (250);
CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES FROM (250) TO (500);
CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt2_e_p4 PARTITION OF prt2_e FOR VALUES FROM (600) TO (MAXVALUE);
INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(600, 799, 3) i;
ANALYZE prt2_e;
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b;
@@ -506,32 +807,49 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 =
-> Append
-> Hash Join
Hash Cond: (((t2.b + t2.a) / 2) = ((t1.a + t1.b) / 2))
- -> Seq Scan on prt2_e_p1 t2
+ -> Seq Scan on prt2_e_p0 t2
+ -> Hash
+ -> Seq Scan on prt1_e_p0 t1
+ Filter: (c = 0)
+ -> Hash Join
+ Hash Cond: (((t1_1.a + t1_1.b) / 2) = ((t2_1.b + t2_1.a) / 2))
+ -> Seq Scan on prt1_e_p1 t1_1
+ Filter: (c = 0)
+ -> Hash
+ -> Seq Scan on prt2_e_p1 t2_1
+ -> Hash Join
+ Hash Cond: (((t2_2.b + t2_2.a) / 2) = ((t1_2.a + t1_2.b) / 2))
+ -> Seq Scan on prt2_e_p2 t2_2
-> Hash
- -> Seq Scan on prt1_e_p1 t1
+ -> Seq Scan on prt1_e_p2 t1_2
Filter: (c = 0)
-> Hash Join
- Hash Cond: (((t2_1.b + t2_1.a) / 2) = ((t1_1.a + t1_1.b) / 2))
- -> Seq Scan on prt2_e_p2 t2_1
+ Hash Cond: (((t2_3.b + t2_3.a) / 2) = ((t1_3.a + t1_3.b) / 2))
+ -> Seq Scan on prt2_e_p3 t2_3
-> Hash
- -> Seq Scan on prt1_e_p2 t1_1
+ -> Seq Scan on prt1_e_p3 t1_3
Filter: (c = 0)
-> Hash Join
- Hash Cond: (((t2_2.b + t2_2.a) / 2) = ((t1_2.a + t1_2.b) / 2))
- -> Seq Scan on prt2_e_p3 t2_2
+ Hash Cond: (((t2_4.b + t2_4.a) / 2) = ((t1_4.a + t1_4.b) / 2))
+ -> Seq Scan on prt2_e_p4 t2_4
-> Hash
- -> Seq Scan on prt1_e_p3 t1_2
+ -> Seq Scan on prt1_e_p4 t1_4
Filter: (c = 0)
-(21 rows)
+(33 rows)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b;
- a | c | b | c
------+---+-----+---
- 0 | 0 | 0 | 0
- 150 | 0 | 150 | 0
- 300 | 0 | 300 | 0
- 450 | 0 | 450 | 0
-(4 rows)
+ a | c | b | c
+------+---+------+---
+ -250 | 0 | -250 | 0
+ -100 | 0 | -100 | 0
+ 0 | 0 | 0 | 0
+ 0 | 0 | 0 | 0
+ 150 | 0 | 150 | 0
+ 300 | 0 | 300 | 0
+ 450 | 0 | 450 | 0
+ 600 | 0 | 600 | 0
+ 750 | 0 | 750 | 0
+(9 rows)
--
-- N-way join
@@ -545,107 +863,158 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t
-> Result
-> Append
-> Nested Loop
- Join Filter: (t1.a = ((t3.a + t3.b) / 2))
+ Join Filter: (t1.a = t2.b)
-> Hash Join
- Hash Cond: (t2.b = t1.a)
- -> Seq Scan on prt2_p1 t2
+ Hash Cond: (((t3.a + t3.b) / 2) = t1.a)
+ -> Seq Scan on prt1_e_p0 t3
-> Hash
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
- -> Index Scan using iprt1_e_p1_ab2 on prt1_e_p1 t3
- Index Cond: (((a + b) / 2) = t2.b)
+ -> Index Scan using iprt2_p0_b on prt2_p0 t2
+ Index Cond: (b = ((t3.a + t3.b) / 2))
-> Nested Loop
Join Filter: (t1_1.a = ((t3_1.a + t3_1.b) / 2))
-> Hash Join
Hash Cond: (t2_1.b = t1_1.a)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Hash
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
- -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t3_1
+ -> Index Scan using iprt1_e_p4_ab2 on prt1_e_p1 t3_1
Index Cond: (((a + b) / 2) = t2_1.b)
-> Nested Loop
Join Filter: (t1_2.a = ((t3_2.a + t3_2.b) / 2))
-> Hash Join
Hash Cond: (t2_2.b = t1_2.a)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p2 t2_2
-> Hash
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
- -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_2
+ -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t3_2
Index Cond: (((a + b) / 2) = t2_2.b)
-(34 rows)
+ -> Nested Loop
+ Join Filter: (t1_3.a = ((t3_3.a + t3_3.b) / 2))
+ -> Nested Loop
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_3
+ Index Cond: (((a + b) / 2) = t2_3.b)
+ -> Nested Loop
+ Join Filter: (t1_4.a = t2_4.b)
+ -> Hash Join
+ Hash Cond: (((t3_4.a + t3_4.b) / 2) = t1_4.a)
+ -> Seq Scan on prt1_e_p4 t3_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p4_b on prt2_p4 t2_4
+ Index Cond: (b = ((t3_4.a + t3_4.b) / 2))
+(53 rows)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.b = 0 ORDER BY t1.a, t2.b;
- a | c | b | c | ?column? | c
------+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
-(4 rows)
+ a | c | b | c | ?column? | c
+------+-------+------+-------+----------+---
+ -150 | -0150 | -150 | -0150 | -300 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 150 | 0150 | 150 | 0150 | 300 | 0
+ 300 | 0300 | 300 | 0300 | 600 | 0
+ 450 | 0450 | 450 | 0450 | 900 | 0
+ 600 | 0600 | 600 | 0600 | 1200 | 0
+ 750 | 0750 | 750 | 0750 | 1500 | 0
+(8 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- QUERY PLAN
---------------------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------------
Sort
Sort Key: t1.a, t2.b, ((t3.a + t3.b))
-> Result
-> Append
-> Hash Right Join
Hash Cond: (((t3.a + t3.b) / 2) = t1.a)
- -> Seq Scan on prt1_e_p1 t3
+ -> Seq Scan on prt1_e_p0 t3
-> Hash
-> Hash Right Join
Hash Cond: (t2.b = t1.a)
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
-> Hash
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Hash Right Join
Hash Cond: (((t3_1.a + t3_1.b) / 2) = t1_1.a)
- -> Seq Scan on prt1_e_p2 t3_1
+ -> Seq Scan on prt1_e_p1 t3_1
-> Hash
-> Hash Right Join
Hash Cond: (t2_1.b = t1_1.a)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Hash
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Hash Right Join
Hash Cond: (((t3_2.a + t3_2.b) / 2) = t1_2.a)
- -> Seq Scan on prt1_e_p3 t3_2
+ -> Seq Scan on prt1_e_p2 t3_2
-> Hash
-> Hash Right Join
Hash Cond: (t2_2.b = t1_2.a)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p2 t2_2
+ -> Hash
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Nested Loop Left Join
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (t1_3.a = b)
+ -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_3
+ Index Cond: (t1_3.a = ((a + b) / 2))
+ -> Hash Right Join
+ Hash Cond: (((t3_4.a + t3_4.b) / 2) = t1_4.a)
+ -> Seq Scan on prt1_e_p4 t3_4
+ -> Hash
+ -> Hash Right Join
+ Hash Cond: (t2_4.b = t1_4.a)
+ -> Seq Scan on prt2_p4 t2_4
-> Hash
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p4 t1_4
Filter: (b = 0)
-(34 rows)
+(52 rows)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- a | c | b | c | ?column? | c
------+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 50 | 0050 | | | 100 | 0
- 100 | 0100 | | | 200 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 200 | 0200 | | | 400 | 0
- 250 | 0250 | | | 500 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 350 | 0350 | | | 700 | 0
- 400 | 0400 | | | 800 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
- 500 | 0500 | | | 1000 | 0
- 550 | 0550 | | | 1100 | 0
-(12 rows)
+ a | c | b | c | ?column? | c
+------+-------+------+-------+----------+---
+ -250 | -0250 | | | -500 | 0
+ -200 | -0200 | | | -400 | 0
+ -150 | -0150 | -150 | -0150 | -300 | 0
+ -100 | -0100 | | | -200 | 0
+ -50 | -0050 | | | -100 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 50 | 0050 | | | 100 | 0
+ 100 | 0100 | | | 200 | 0
+ 150 | 0150 | 150 | 0150 | 300 | 0
+ 200 | 0200 | | | 400 | 0
+ 250 | 0250 | | | 500 | 0
+ 300 | 0300 | 300 | 0300 | 600 | 0
+ 350 | 0350 | | | 700 | 0
+ 400 | 0400 | | | 800 | 0
+ 450 | 0450 | 450 | 0450 | 900 | 0
+ 500 | 0500 | | | 1000 | 0
+ 550 | 0550 | | | 1100 | 0
+ 600 | 0600 | 600 | 0600 | 1200 | 0
+ 650 | 0650 | | | 1300 | 0
+ 700 | 0700 | | | 1400 | 0
+ 750 | 0750 | 750 | 0750 | 1500 | 0
+(22 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- QUERY PLAN
--------------------------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------------
Sort
Sort Key: t1.a, t2.b, ((t3.a + t3.b))
-> Result
@@ -653,48 +1022,75 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
-> Nested Loop Left Join
-> Hash Right Join
Hash Cond: (t1.a = ((t3.a + t3.b) / 2))
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
-> Hash
- -> Seq Scan on prt1_e_p1 t3
+ -> Seq Scan on prt1_e_p0 t3
Filter: (c = 0)
- -> Index Scan using iprt2_p1_b on prt2_p1 t2
+ -> Index Scan using iprt2_p0_b on prt2_p0 t2
Index Cond: (t1.a = b)
-> Nested Loop Left Join
-> Hash Right Join
Hash Cond: (t1_1.a = ((t3_1.a + t3_1.b) / 2))
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
-> Hash
- -> Seq Scan on prt1_e_p2 t3_1
+ -> Seq Scan on prt1_e_p1 t3_1
Filter: (c = 0)
- -> Index Scan using iprt2_p2_b on prt2_p2 t2_1
+ -> Index Scan using iprt2_p1_b on prt2_p1 t2_1
Index Cond: (t1_1.a = b)
-> Nested Loop Left Join
-> Hash Right Join
Hash Cond: (t1_2.a = ((t3_2.a + t3_2.b) / 2))
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
-> Hash
- -> Seq Scan on prt1_e_p3 t3_2
+ -> Seq Scan on prt1_e_p2 t3_2
Filter: (c = 0)
- -> Index Scan using iprt2_p3_b on prt2_p3 t2_2
+ -> Index Scan using iprt2_p2_b on prt2_p2 t2_2
Index Cond: (t1_2.a = b)
-(31 rows)
+ -> Nested Loop Left Join
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_e_p3 t3_3
+ Filter: (c = 0)
+ -> Index Scan using iprt1_p3_a on prt1_p3 t1_3
+ Index Cond: (a = ((t3_3.a + t3_3.b) / 2))
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (t1_3.a = b)
+ -> Nested Loop Left Join
+ -> Hash Right Join
+ Hash Cond: (t1_4.a = ((t3_4.a + t3_4.b) / 2))
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Seq Scan on prt1_e_p4 t3_4
+ Filter: (c = 0)
+ -> Index Scan using iprt2_p4_b on prt2_p4 t2_4
+ Index Cond: (t1_4.a = b)
+(48 rows)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- a | c | b | c | ?column? | c
------+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 50 | 0050 | | | 100 | 0
- 100 | 0100 | | | 200 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 200 | 0200 | | | 400 | 0
- 250 | 0250 | | | 500 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 350 | 0350 | | | 700 | 0
- 400 | 0400 | | | 800 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
- 500 | 0500 | | | 1000 | 0
- 550 | 0550 | | | 1100 | 0
-(12 rows)
+ a | c | b | c | ?column? | c
+------+-------+------+-------+----------+---
+ -250 | -0250 | | | -500 | 0
+ -200 | -0200 | | | -400 | 0
+ -150 | -0150 | -150 | -0150 | -300 | 0
+ -100 | -0100 | | | -200 | 0
+ -50 | -0050 | | | -100 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 50 | 0050 | | | 100 | 0
+ 100 | 0100 | | | 200 | 0
+ 150 | 0150 | 150 | 0150 | 300 | 0
+ 200 | 0200 | | | 400 | 0
+ 250 | 0250 | | | 500 | 0
+ 300 | 0300 | 300 | 0300 | 600 | 0
+ 350 | 0350 | | | 700 | 0
+ 400 | 0400 | | | 800 | 0
+ 450 | 0450 | 450 | 0450 | 900 | 0
+ 500 | 0500 | | | 1000 | 0
+ 550 | 0550 | | | 1100 | 0
+ 600 | 0600 | 600 | 0600 | 1200 | 0
+ 650 | 0650 | | | 1300 | 0
+ 700 | 0700 | | | 1400 | 0
+ 750 | 0750 | 750 | 0750 | 1500 | 0
+(22 rows)
-- Cases with non-nullable expressions in subquery results;
-- make sure these go to null as expected
@@ -703,23 +1099,36 @@ SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * F
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, prt2_p1.b, ((prt1_e_p1.a + prt1_e_p1.b))
+ Sort Key: prt1_p0.a, prt2_p0.b, ((prt1_e_p0.a + prt1_e_p0.b))
-> Result
-> Append
-> Hash Full Join
- Hash Cond: (prt1_p1.a = ((prt1_e_p1.a + prt1_e_p1.b) / 2))
- Filter: ((prt1_p1.a = (50)) OR (prt2_p1.b = (75)) OR (((prt1_e_p1.a + prt1_e_p1.b) / 2) = (50)))
+ Hash Cond: (prt1_p0.a = ((prt1_e_p0.a + prt1_e_p0.b) / 2))
+ Filter: ((prt1_p0.a = (50)) OR (prt2_p0.b = (75)) OR (((prt1_e_p0.a + prt1_e_p0.b) / 2) = (50)))
-> Hash Full Join
- Hash Cond: (prt1_p1.a = prt2_p1.b)
- -> Seq Scan on prt1_p1
+ Hash Cond: (prt1_p0.a = prt2_p0.b)
+ -> Seq Scan on prt1_p0
Filter: (b = 0)
-> Hash
- -> Seq Scan on prt2_p1
+ -> Seq Scan on prt2_p0
Filter: (a = 0)
-> Hash
- -> Seq Scan on prt1_e_p1
+ -> Seq Scan on prt1_e_p0
Filter: (c = 0)
-> Hash Full Join
+ Hash Cond: (((prt1_e_p1.a + prt1_e_p1.b) / 2) = prt1_p1.a)
+ Filter: ((prt1_p1.a = (50)) OR (prt2_p1.b = (75)) OR (((prt1_e_p1.a + prt1_e_p1.b) / 2) = (50)))
+ -> Seq Scan on prt1_e_p1
+ Filter: (c = 0)
+ -> Hash
+ -> Hash Full Join
+ Hash Cond: (prt1_p1.a = prt2_p1.b)
+ -> Seq Scan on prt1_p1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p1
+ Filter: (a = 0)
+ -> Hash Full Join
Hash Cond: (prt1_p2.a = ((prt1_e_p2.a + prt1_e_p2.b) / 2))
Filter: ((prt1_p2.a = (50)) OR (prt2_p2.b = (75)) OR (((prt1_e_p2.a + prt1_e_p2.b) / 2) = (50)))
-> Hash Full Join
@@ -745,7 +1154,20 @@ SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * F
-> Hash
-> Seq Scan on prt1_e_p3
Filter: (c = 0)
-(43 rows)
+ -> Hash Full Join
+ Hash Cond: (prt1_p4.a = ((prt1_e_p4.a + prt1_e_p4.b) / 2))
+ Filter: ((prt1_p4.a = (50)) OR (prt2_p4.b = (75)) OR (((prt1_e_p4.a + prt1_e_p4.b) / 2) = (50)))
+ -> Hash Full Join
+ Hash Cond: (prt1_p4.a = prt2_p4.b)
+ -> Seq Scan on prt1_p4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4
+ Filter: (a = 0)
+ -> Hash
+ -> Seq Scan on prt1_e_p4
+ Filter: (c = 0)
+(69 rows)
SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.c = 0) t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a = t1.phv OR t2.b = t2.phv OR (t3.a + t3.b)/2 = t3.phv ORDER BY t1.a, t2.b, t3.a + t3.b;
a | phv | b | phv | ?column? | phv
@@ -763,173 +1185,261 @@ SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHER
Sort Key: t1.a
-> Append
-> Nested Loop
- Join Filter: (t1.a = t1_3.b)
+ Join Filter: (t1.a = t1_5.b)
-> HashAggregate
- Group Key: t1_3.b
+ Group Key: t1_5.b
-> Hash Join
- Hash Cond: (((t2.a + t2.b) / 2) = t1_3.b)
- -> Seq Scan on prt1_e_p1 t2
+ Hash Cond: (((t2.a + t2.b) / 2) = t1_5.b)
+ -> Seq Scan on prt1_e_p0 t2
-> Hash
- -> Seq Scan on prt2_p1 t1_3
+ -> Seq Scan on prt2_p0 t1_5
Filter: (a = 0)
- -> Index Scan using iprt1_p1_a on prt1_p1 t1
+ -> Index Scan using iprt1_p0_a on prt1_p0 t1
Index Cond: (a = ((t2.a + t2.b) / 2))
Filter: (b = 0)
-> Nested Loop
- Join Filter: (t1_1.a = t1_4.b)
+ Join Filter: (t1_1.a = t1_6.b)
-> HashAggregate
- Group Key: t1_4.b
+ Group Key: t1_6.b
-> Hash Join
- Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_4.b)
- -> Seq Scan on prt1_e_p2 t2_1
+ Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_6.b)
+ -> Seq Scan on prt1_e_p1 t2_1
-> Hash
- -> Seq Scan on prt2_p2 t1_4
+ -> Seq Scan on prt2_p1 t1_6
Filter: (a = 0)
- -> Index Scan using iprt1_p2_a on prt1_p2 t1_1
+ -> Index Scan using iprt1_p1_a on prt1_p1 t1_1
Index Cond: (a = ((t2_1.a + t2_1.b) / 2))
Filter: (b = 0)
-> Nested Loop
- Join Filter: (t1_2.a = t1_5.b)
+ Join Filter: (t1_2.a = t1_7.b)
-> HashAggregate
- Group Key: t1_5.b
+ Group Key: t1_7.b
-> Nested Loop
- -> Seq Scan on prt2_p3 t1_5
+ -> Seq Scan on prt2_p2 t1_7
Filter: (a = 0)
- -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t2_2
- Index Cond: (((a + b) / 2) = t1_5.b)
- -> Index Scan using iprt1_p3_a on prt1_p3 t1_2
+ -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t2_2
+ Index Cond: (((a + b) / 2) = t1_7.b)
+ -> Index Scan using iprt1_p2_a on prt1_p2 t1_2
Index Cond: (a = ((t2_2.a + t2_2.b) / 2))
Filter: (b = 0)
-(41 rows)
+ -> Nested Loop
+ Join Filter: (t1_3.a = t1_8.b)
+ -> HashAggregate
+ Group Key: t1_8.b
+ -> Nested Loop
+ -> Seq Scan on prt2_p3 t1_8
+ Filter: (a = 0)
+ -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t2_3
+ Index Cond: (((a + b) / 2) = t1_8.b)
+ -> Index Scan using iprt1_p3_a on prt1_p3 t1_3
+ Index Cond: (a = ((t2_3.a + t2_3.b) / 2))
+ Filter: (b = 0)
+ -> Nested Loop
+ Join Filter: (t1_4.a = t1_9.b)
+ -> HashAggregate
+ Group Key: t1_9.b
+ -> Hash Join
+ Hash Cond: (((t2_4.a + t2_4.b) / 2) = t1_9.b)
+ -> Seq Scan on prt1_e_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt2_p4 t1_9
+ Filter: (a = 0)
+ -> Index Scan using iprt1_p4_a on prt1_p4 t1_4
+ Index Cond: (a = ((t2_4.a + t2_4.b) / 2))
+ Filter: (b = 0)
+(66 rows)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.a = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.b = 0 ORDER BY t1.a;
- a | b | c
------+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
+ a | b | c
+------+---+-------
+ -150 | 0 | -0150
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 300 | 0 | 0300
+ 450 | 0 | 0450
+ 600 | 0 | 0600
+ 750 | 0 | 0750
+(7 rows)
EXPLAIN (COSTS OFF)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
- QUERY PLAN
--------------------------------------------------------------------------------
+ QUERY PLAN
+---------------------------------------------------------------------------------
Sort
Sort Key: t1.a
-> Append
-> Nested Loop
-> HashAggregate
- Group Key: t1_3.b
+ Group Key: t1_5.b
+ -> Hash Semi Join
+ Hash Cond: (t1_5.b = ((t1_10.a + t1_10.b) / 2))
+ -> Seq Scan on prt2_p0 t1_5
+ -> Hash
+ -> Seq Scan on prt1_e_p0 t1_10
+ Filter: (c = 0)
+ -> Index Scan using iprt1_p0_a on prt1_p0 t1
+ Index Cond: (a = t1_5.b)
+ Filter: (b = 0)
+ -> Nested Loop
+ -> HashAggregate
+ Group Key: t1_6.b
-> Hash Semi Join
- Hash Cond: (t1_3.b = ((t1_6.a + t1_6.b) / 2))
- -> Seq Scan on prt2_p1 t1_3
+ Hash Cond: (t1_6.b = ((t1_11.a + t1_11.b) / 2))
+ -> Seq Scan on prt2_p1 t1_6
-> Hash
- -> Seq Scan on prt1_e_p1 t1_6
+ -> Seq Scan on prt1_e_p1 t1_11
Filter: (c = 0)
- -> Index Scan using iprt1_p1_a on prt1_p1 t1
- Index Cond: (a = t1_3.b)
+ -> Index Scan using iprt1_p1_a on prt1_p1 t1_1
+ Index Cond: (a = t1_6.b)
Filter: (b = 0)
-> Nested Loop
-> HashAggregate
- Group Key: t1_4.b
+ Group Key: t1_7.b
-> Hash Semi Join
- Hash Cond: (t1_4.b = ((t1_7.a + t1_7.b) / 2))
- -> Seq Scan on prt2_p2 t1_4
+ Hash Cond: (t1_7.b = ((t1_12.a + t1_12.b) / 2))
+ -> Seq Scan on prt2_p2 t1_7
-> Hash
- -> Seq Scan on prt1_e_p2 t1_7
+ -> Seq Scan on prt1_e_p2 t1_12
Filter: (c = 0)
- -> Index Scan using iprt1_p2_a on prt1_p2 t1_1
- Index Cond: (a = t1_4.b)
+ -> Index Scan using iprt1_p2_a on prt1_p2 t1_2
+ Index Cond: (a = t1_7.b)
Filter: (b = 0)
-> Nested Loop
-> Unique
-> Sort
- Sort Key: t1_5.b
+ Sort Key: t1_8.b
-> Hash Semi Join
- Hash Cond: (t1_5.b = ((t1_8.a + t1_8.b) / 2))
- -> Seq Scan on prt2_p3 t1_5
+ Hash Cond: (t1_8.b = ((t1_13.a + t1_13.b) / 2))
+ -> Seq Scan on prt2_p3 t1_8
-> Hash
- -> Seq Scan on prt1_e_p3 t1_8
+ -> Seq Scan on prt1_e_p3 t1_13
Filter: (c = 0)
- -> Index Scan using iprt1_p3_a on prt1_p3 t1_2
- Index Cond: (a = t1_5.b)
+ -> Index Scan using iprt1_p3_a on prt1_p3 t1_3
+ Index Cond: (a = t1_8.b)
+ Filter: (b = 0)
+ -> Nested Loop
+ -> HashAggregate
+ Group Key: t1_9.b
+ -> Hash Semi Join
+ Hash Cond: (t1_9.b = ((t1_14.a + t1_14.b) / 2))
+ -> Seq Scan on prt2_p4 t1_9
+ -> Hash
+ -> Seq Scan on prt1_e_p4 t1_14
+ Filter: (c = 0)
+ -> Index Scan using iprt1_p4_a on prt1_p4 t1_4
+ Index Cond: (a = t1_9.b)
Filter: (b = 0)
-(40 rows)
+(64 rows)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
- a | b | c
------+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
+ a | b | c
+------+---+-------
+ -150 | 0 | -0150
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 300 | 0 | 0300
+ 450 | 0 | 0450
+ 600 | 0 | 0600
+ 750 | 0 | 0750
+(7 rows)
-- test merge joins
SET enable_hashjoin TO off;
SET enable_nestloop TO off;
EXPLAIN (COSTS OFF)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
- QUERY PLAN
-----------------------------------------------------------------
+ QUERY PLAN
+------------------------------------------------------------------
Merge Append
Sort Key: t1.a
-> Merge Semi Join
- Merge Cond: (t1.a = t1_3.b)
+ Merge Cond: (t1.a = t1_5.b)
-> Sort
Sort Key: t1.a
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
Filter: (b = 0)
-> Merge Semi Join
- Merge Cond: (t1_3.b = (((t1_6.a + t1_6.b) / 2)))
+ Merge Cond: (t1_5.b = (((t1_10.a + t1_10.b) / 2)))
-> Sort
- Sort Key: t1_3.b
- -> Seq Scan on prt2_p1 t1_3
+ Sort Key: t1_5.b
+ -> Seq Scan on prt2_p0 t1_5
-> Sort
- Sort Key: (((t1_6.a + t1_6.b) / 2))
- -> Seq Scan on prt1_e_p1 t1_6
+ Sort Key: (((t1_10.a + t1_10.b) / 2))
+ -> Seq Scan on prt1_e_p0 t1_10
Filter: (c = 0)
-> Merge Semi Join
- Merge Cond: (t1_1.a = t1_4.b)
+ Merge Cond: (t1_1.a = t1_6.b)
-> Sort
Sort Key: t1_1.a
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
Filter: (b = 0)
-> Merge Semi Join
- Merge Cond: (t1_4.b = (((t1_7.a + t1_7.b) / 2)))
+ Merge Cond: (t1_6.b = (((t1_11.a + t1_11.b) / 2)))
-> Sort
- Sort Key: t1_4.b
- -> Seq Scan on prt2_p2 t1_4
+ Sort Key: t1_6.b
+ -> Seq Scan on prt2_p1 t1_6
-> Sort
- Sort Key: (((t1_7.a + t1_7.b) / 2))
- -> Seq Scan on prt1_e_p2 t1_7
+ Sort Key: (((t1_11.a + t1_11.b) / 2))
+ -> Seq Scan on prt1_e_p1 t1_11
Filter: (c = 0)
-> Merge Semi Join
- Merge Cond: (t1_2.a = t1_5.b)
+ Merge Cond: (t1_2.a = t1_7.b)
-> Sort
Sort Key: t1_2.a
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
Filter: (b = 0)
-> Merge Semi Join
- Merge Cond: (t1_5.b = (((t1_8.a + t1_8.b) / 2)))
+ Merge Cond: (t1_7.b = (((t1_12.a + t1_12.b) / 2)))
-> Sort
- Sort Key: t1_5.b
- -> Seq Scan on prt2_p3 t1_5
+ Sort Key: t1_7.b
+ -> Seq Scan on prt2_p2 t1_7
+ -> Sort
+ Sort Key: (((t1_12.a + t1_12.b) / 2))
+ -> Seq Scan on prt1_e_p2 t1_12
+ Filter: (c = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_3.a = t1_8.b)
+ -> Sort
+ Sort Key: t1_3.a
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_8.b = (((t1_13.a + t1_13.b) / 2)))
+ -> Sort
+ Sort Key: t1_8.b
+ -> Seq Scan on prt2_p3 t1_8
+ -> Sort
+ Sort Key: (((t1_13.a + t1_13.b) / 2))
+ -> Seq Scan on prt1_e_p3 t1_13
+ Filter: (c = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_4.a = t1_9.b)
+ -> Sort
+ Sort Key: t1_4.a
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Merge Semi Join
+ Merge Cond: (t1_9.b = (((t1_14.a + t1_14.b) / 2)))
+ -> Sort
+ Sort Key: t1_9.b
+ -> Seq Scan on prt2_p4 t1_9
-> Sort
- Sort Key: (((t1_8.a + t1_8.b) / 2))
- -> Seq Scan on prt1_e_p3 t1_8
+ Sort Key: (((t1_14.a + t1_14.b) / 2))
+ -> Seq Scan on prt1_e_p4 t1_14
Filter: (c = 0)
-(47 rows)
+(77 rows)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
- a | b | c
------+---+------
- 0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
+ a | b | c
+------+---+-------
+ -150 | 0 | -0150
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 300 | 0 | 0300
+ 450 | 0 | 0450
+ 600 | 0 | 0600
+ 750 | 0 | 0750
+(7 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
@@ -947,14 +1457,14 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
Merge Cond: ((((t3.a + t3.b) / 2)) = t1.a)
-> Sort
Sort Key: (((t3.a + t3.b) / 2))
- -> Seq Scan on prt1_e_p1 t3
+ -> Seq Scan on prt1_e_p0 t3
Filter: (c = 0)
-> Sort
Sort Key: t1.a
- -> Seq Scan on prt1_p1 t1
+ -> Seq Scan on prt1_p0 t1
-> Sort
Sort Key: t2.b
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
-> Merge Left Join
Merge Cond: (t1_1.a = t2_1.b)
-> Sort
@@ -963,14 +1473,14 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
Merge Cond: ((((t3_1.a + t3_1.b) / 2)) = t1_1.a)
-> Sort
Sort Key: (((t3_1.a + t3_1.b) / 2))
- -> Seq Scan on prt1_e_p2 t3_1
+ -> Seq Scan on prt1_e_p1 t3_1
Filter: (c = 0)
-> Sort
Sort Key: t1_1.a
- -> Seq Scan on prt1_p2 t1_1
+ -> Seq Scan on prt1_p1 t1_1
-> Sort
Sort Key: t2_1.b
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p1 t2_1
-> Merge Left Join
Merge Cond: (t1_2.a = t2_2.b)
-> Sort
@@ -979,225 +1489,2361 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
Merge Cond: ((((t3_2.a + t3_2.b) / 2)) = t1_2.a)
-> Sort
Sort Key: (((t3_2.a + t3_2.b) / 2))
- -> Seq Scan on prt1_e_p3 t3_2
+ -> Seq Scan on prt1_e_p2 t3_2
Filter: (c = 0)
-> Sort
Sort Key: t1_2.a
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_2
-> Sort
Sort Key: t2_2.b
- -> Seq Scan on prt2_p3 t2_2
-(52 rows)
+ -> Seq Scan on prt2_p2 t2_2
+ -> Merge Left Join
+ Merge Cond: (t1_3.a = t2_3.b)
+ -> Sort
+ Sort Key: t1_3.a
+ -> Merge Left Join
+ Merge Cond: ((((t3_3.a + t3_3.b) / 2)) = t1_3.a)
+ -> Sort
+ Sort Key: (((t3_3.a + t3_3.b) / 2))
+ -> Seq Scan on prt1_e_p3 t3_3
+ Filter: (c = 0)
+ -> Sort
+ Sort Key: t1_3.a
+ -> Seq Scan on prt1_p3 t1_3
+ -> Sort
+ Sort Key: t2_3.b
+ -> Seq Scan on prt2_p3 t2_3
+ -> Merge Left Join
+ Merge Cond: (t1_4.a = t2_4.b)
+ -> Sort
+ Sort Key: t1_4.a
+ -> Merge Left Join
+ Merge Cond: ((((t3_4.a + t3_4.b) / 2)) = t1_4.a)
+ -> Sort
+ Sort Key: (((t3_4.a + t3_4.b) / 2))
+ -> Seq Scan on prt1_e_p4 t3_4
+ Filter: (c = 0)
+ -> Sort
+ Sort Key: t1_4.a
+ -> Seq Scan on prt1_p4 t1_4
+ -> Sort
+ Sort Key: t2_4.b
+ -> Seq Scan on prt2_p4 t2_4
+(84 rows)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
- a | c | b | c | ?column? | c
------+------+-----+------+----------+---
- 0 | 0000 | 0 | 0000 | 0 | 0
- 50 | 0050 | | | 100 | 0
- 100 | 0100 | | | 200 | 0
- 150 | 0150 | 150 | 0150 | 300 | 0
- 200 | 0200 | | | 400 | 0
- 250 | 0250 | | | 500 | 0
- 300 | 0300 | 300 | 0300 | 600 | 0
- 350 | 0350 | | | 700 | 0
- 400 | 0400 | | | 800 | 0
- 450 | 0450 | 450 | 0450 | 900 | 0
- 500 | 0500 | | | 1000 | 0
- 550 | 0550 | | | 1100 | 0
-(12 rows)
+ a | c | b | c | ?column? | c
+------+-------+------+-------+----------+---
+ -250 | -0250 | | | -500 | 0
+ -200 | -0200 | | | -400 | 0
+ -150 | -0150 | -150 | -0150 | -300 | 0
+ -100 | -0100 | | | -200 | 0
+ -50 | -0050 | | | -100 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 0 | 0000 | 0 | 0000 | 0 | 0
+ 50 | 0050 | | | 100 | 0
+ 100 | 0100 | | | 200 | 0
+ 150 | 0150 | 150 | 0150 | 300 | 0
+ 200 | 0200 | | | 400 | 0
+ 250 | 0250 | | | 500 | 0
+ 300 | 0300 | 300 | 0300 | 600 | 0
+ 350 | 0350 | | | 700 | 0
+ 400 | 0400 | | | 800 | 0
+ 450 | 0450 | 450 | 0450 | 900 | 0
+ 500 | 0500 | | | 1000 | 0
+ 550 | 0550 | | | 1100 | 0
+ 600 | 0600 | 600 | 0600 | 1200 | 0
+ 650 | 0650 | | | 1300 | 0
+ 700 | 0700 | | | 1400 | 0
+ 750 | 0750 | 750 | 0750 | 1500 | 0
+(22 rows)
+
+-- MergeAppend on nullable column
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
+ QUERY PLAN
+-----------------------------------------------------------
+ Sort
+ Sort Key: prt1_p0.a, b
+ -> Append
+ -> Merge Left Join
+ Merge Cond: (prt1_p0.a = b)
+ -> Sort
+ Sort Key: prt1_p0.a
+ -> Seq Scan on prt1_p0
+ Filter: ((a < 450) AND (b = 0))
+ -> Sort
+ Sort Key: b
+ -> Result
+ One-Time Filter: false
+ -> Merge Left Join
+ Merge Cond: (prt1_p1.a = b)
+ -> Sort
+ Sort Key: prt1_p1.a
+ -> Seq Scan on prt1_p1
+ Filter: ((a < 450) AND (b = 0))
+ -> Sort
+ Sort Key: b
+ -> Result
+ One-Time Filter: false
+ -> Merge Left Join
+ Merge Cond: (prt1_p2.a = prt2_p2.b)
+ -> Sort
+ Sort Key: prt1_p2.a
+ -> Seq Scan on prt1_p2
+ Filter: ((a < 450) AND (b = 0))
+ -> Sort
+ Sort Key: prt2_p2.b
+ -> Seq Scan on prt2_p2
+ Filter: (b > 250)
+(33 rows)
+
+SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
+ a | b
+------+-----
+ -250 |
+ -200 |
+ -150 |
+ -100 |
+ -50 |
+ 0 |
+ 50 |
+ 100 |
+ 150 |
+ 200 |
+ 250 |
+ 300 | 300
+ 350 |
+ 400 |
+(14 rows)
+
+RESET enable_hashjoin;
+RESET enable_nestloop;
+-- Add an extra partition to prt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+DROP TABLE prt2_p4;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (800);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (800) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999) i WHERE i % 3 = 0;
+ANALYZE prt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on prt2_p0 t2
+ -> Hash
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Hash Join
+ Hash Cond: (t2_1.b = t1_1.a)
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash Join
+ Hash Cond: (t2_2.b = t1_2.a)
+ -> Seq Scan on prt2_p2 t2_2
+ -> Hash
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Nested Loop
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ -> Hash Join
+ Hash Cond: (t2_4.b = t1_4.a)
+ -> Seq Scan on prt2_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(32 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ a | c | a | c
+------+-------+---+-------
+ -150 | -0150 | 0 | -0150
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 0 | 0150
+ 300 | 0300 | 0 | 0300
+ 450 | 0450 | 0 | 0450
+ 600 | 0600 | 0 | 0600
+ 750 | 0750 | 0 | 0750
+(7 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Right Join
+ Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on prt2_p0 t2
+ -> Hash
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Hash Right Join
+ Hash Cond: (t2_1.b = t1_1.a)
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash Right Join
+ Hash Cond: (t2_2.b = t1_2.a)
+ -> Seq Scan on prt2_p2 t2_2
+ -> Hash
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Nested Loop Left Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (t1_3.a = b)
+ -> Hash Right Join
+ Hash Cond: (t2_4.b = t1_4.a)
+ -> Seq Scan on prt2_p4 t2_4
+ -> Hash
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(32 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ a | c | a | c
+------+-------+---+-------
+ -250 | -0250 | |
+ -200 | -0200 | |
+ -150 | -0150 | 0 | -0150
+ -100 | -0100 | |
+ -50 | -0050 | |
+ 0 | 0000 | 0 | 0000
+ 50 | 0050 | |
+ 100 | 0100 | |
+ 150 | 0150 | 0 | 0150
+ 200 | 0200 | |
+ 250 | 0250 | |
+ 300 | 0300 | 0 | 0300
+ 350 | 0350 | |
+ 400 | 0400 | |
+ 450 | 0450 | 0 | 0450
+ 500 | 0500 | |
+ 550 | 0550 | |
+ 600 | 0600 | 0 | 0600
+ 650 | 0650 | |
+ 700 | 0700 | |
+ 750 | 0750 | 0 | 0750
+(21 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Semi Join
+ Hash Cond: (t1.a = t2.b)
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p0 t2
+ -> Hash Semi Join
+ Hash Cond: (t1_1.a = t2_1.b)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash Semi Join
+ Hash Cond: (t1_2.a = t2_2.b)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p2 t2_2
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Only Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ -> Hash Semi Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+(32 rows)
+
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+------+---+-------
+ -150 | 0 | -0150
+ 0 | 0 | 0000
+ 150 | 0 | 0150
+ 300 | 0 | 0300
+ 450 | 0 | 0450
+ 600 | 0 | 0600
+ 750 | 0 | 0750
+(7 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Append
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p0_a on prt1_p0 t2
+ Index Cond: (a = t1.b)
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p1_a on prt1_p1 t2_1
+ Index Cond: (a = t1_1.b)
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2
+ Index Cond: (a = t1_2.b)
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3
+ Index Cond: (a = t1_3.b)
+ -> Nested Loop Semi Join
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Index Only Scan using iprt1_p4_a on prt1_p4 t2_4
+ Index Cond: (a = t1_4.b)
+(28 rows)
+
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+---+------+-------
+ 0 | -150 | -0150
+ 0 | 0 | 0000
+ 0 | 150 | 0150
+ 0 | 300 | 0300
+ 0 | 450 | 0450
+ 0 | 600 | 0600
+ 0 | 750 | 0750
+(7 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Anti Join
+ Hash Cond: (t1.a = t2.b)
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p0 t2
+ -> Hash Anti Join
+ Hash Cond: (t1_1.a = t2_1.b)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p1 t2_1
+ -> Hash Anti Join
+ Hash Cond: (t1_2.a = t2_2.b)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p2 t2_2
+ -> Nested Loop Anti Join
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Index Only Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ -> Hash Anti Join
+ Hash Cond: (t1_4.a = t2_4.b)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on prt2_p4 t2_4
+(32 rows)
+
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+------+---+-------
+ -250 | 0 | -0250
+ -200 | 0 | -0200
+ -100 | 0 | -0100
+ -50 | 0 | -0050
+ 50 | 0 | 0050
+ 100 | 0 | 0100
+ 200 | 0 | 0200
+ 250 | 0 | 0250
+ 350 | 0 | 0350
+ 400 | 0 | 0400
+ 500 | 0 | 0500
+ 550 | 0 | 0550
+ 650 | 0 | 0650
+ 700 | 0 | 0700
+(14 rows)
+
+-- partition-wise join can not handle missing partition on the inner side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.b;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t2.b
+ -> Hash Right Join
+ Hash Cond: (t1.a = t2.b)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t2_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t2_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t2_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t2_5
+ Filter: (a = 0)
+(24 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE coalesce(t1.b, 0) + coalesce(t2.a, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Full Join
+ Hash Cond: (t1.a = t2.b)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.a, 0)) = 0)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+(19 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Hash Anti Join
+ Hash Cond: (t1.b = t2.a)
+ -> Append
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t1_5
+ Filter: (a = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p0 t2
+ -> Seq Scan on prt1_p1 t2_1
+ -> Seq Scan on prt1_p2 t2_2
+ -> Seq Scan on prt1_p3 t2_3
+ -> Seq Scan on prt1_p4 t2_4
+(24 rows)
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE prt2_p4;
+DROP TABLE prt2_p5;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (700);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (700) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999, 3) i;
+ANALYZE prt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(23 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Right Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+(23 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------
+ Hash Right Join
+ Hash Cond: (t1.a = t2.b)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t2_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t2_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t2_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t2_5
+ Filter: (a = 0)
+(22 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Join
+ Hash Cond: (t1.a = t2.b)
+ Join Filter: ((t1.b + t2.a) = 0)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+(19 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Semi Join
+ Hash Cond: (t1.a = t2.b)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+(23 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Hash Semi Join
+ Hash Cond: (t1.b = t2.a)
+ -> Append
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t1_5
+ Filter: (a = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p0 t2
+ -> Seq Scan on prt1_p1 t2_1
+ -> Seq Scan on prt1_p2 t2_2
+ -> Seq Scan on prt1_p3 t2_3
+ -> Seq Scan on prt1_p4 t2_4
+(24 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: (t1.a = t2.b)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on prt1_p4 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+(23 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.b, t1.c
+ -> Hash Anti Join
+ Hash Cond: (t1.b = t2.a)
+ -> Append
+ -> Seq Scan on prt2_p0 t1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t1_1
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p2 t1_2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p3 t1_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t1_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t1_5
+ Filter: (a = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_p0 t2
+ -> Seq Scan on prt1_p1 t2_1
+ -> Seq Scan on prt1_p2 t2_2
+ -> Seq Scan on prt1_p3 t2_3
+ -> Seq Scan on prt1_p4 t2_4
+(24 rows)
+
+--
+-- partitioned by multiple columns
+--
+CREATE TABLE prt1_m (a int, b int, c int) PARTITION BY RANGE(a, ((a + b)/2));
+CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES FROM (0, 0) TO (250, 250);
+CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES FROM (250, 250) TO (500, 500);
+CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES FROM (500, 500) TO (600, 600);
+INSERT INTO prt1_m SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i;
+ANALYZE prt1_m;
+CREATE TABLE prt2_m (a int, b int, c int) PARTITION BY RANGE(((b + a)/2), b);
+CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES FROM (0, 0) TO (250, 250);
+CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES FROM (250, 250) TO (500, 500);
+CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES FROM (500, 500) TO (600, 600);
+INSERT INTO prt2_m SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i;
+ANALYZE prt2_m;
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Sort Key: prt1_m_p1.a, prt2_m_p1.b
+ -> Append
+ -> Hash Full Join
+ Hash Cond: ((prt1_m_p1.a = ((prt2_m_p1.b + prt2_m_p1.a) / 2)) AND (((prt1_m_p1.a + prt1_m_p1.b) / 2) = prt2_m_p1.b))
+ -> Seq Scan on prt1_m_p1
+ Filter: (c = 0)
+ -> Hash
+ -> Seq Scan on prt2_m_p1
+ Filter: (c = 0)
+ -> Hash Full Join
+ Hash Cond: ((prt1_m_p2.a = ((prt2_m_p2.b + prt2_m_p2.a) / 2)) AND (((prt1_m_p2.a + prt1_m_p2.b) / 2) = prt2_m_p2.b))
+ -> Seq Scan on prt1_m_p2
+ Filter: (c = 0)
+ -> Hash
+ -> Seq Scan on prt2_m_p2
+ Filter: (c = 0)
+ -> Hash Full Join
+ Hash Cond: ((prt1_m_p3.a = ((prt2_m_p3.b + prt2_m_p3.a) / 2)) AND (((prt1_m_p3.a + prt1_m_p3.b) / 2) = prt2_m_p3.b))
+ -> Seq Scan on prt1_m_p3
+ Filter: (c = 0)
+ -> Hash
+ -> Seq Scan on prt2_m_p3
+ Filter: (c = 0)
+(24 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+ a | c | b | c
+-----+---+-----+---
+ 0 | 0 | 0 | 0
+ 50 | 0 | |
+ 100 | 0 | |
+ 150 | 0 | 150 | 0
+ 200 | 0 | |
+ 250 | 0 | |
+ 300 | 0 | 300 | 0
+ 350 | 0 | |
+ 400 | 0 | |
+ 450 | 0 | 450 | 0
+ 500 | 0 | |
+ 550 | 0 | |
+ | | 75 | 0
+ | | 225 | 0
+ | | 375 | 0
+ | | 525 | 0
+(16 rows)
+
+--
+-- tests for list partitioned tables.
+--
+\set part_mod 17
+\set cond_mod 47
+\set num_rows 500
+CREATE TABLE plt1 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0001','0002','0003');
+CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0008','0009');
+CREATE TABLE plt1_p4 PARTITION OF plt1 FOR VALUES IN ('0000','0010');
+INSERT INTO plt1 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (7, 11, 12, 13, 14, 15, 16);
+ANALYSE plt1;
+-- plt2 have missing starting 0001, additional 0007, missing ending 0010
+-- and additional 0011 and 0012 bounds
+CREATE TABLE plt2 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002','0003');
+CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0007','0008','0009');
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000','0011','0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 10, 13, 14, 15, 16);
+ANALYSE plt2;
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Join Filter: ((t1_1.b + t2_1.b) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + t2_2.b) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t1_3.b + t2_3.b) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Left Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Right Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Left Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Left Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(7 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+------------------------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Result
+ -> Append
+ -> Hash Right Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Left Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + t2_1.b) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Left Join
+ Hash Cond: ((t2_2.c)::text = (t1_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + t2_2.b) = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash Right Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + t2_3.b) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(28 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 | 0011
+(6 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Append
+ -> Hash Full Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Full Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Full Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Full Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 | 0011
+(8 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt2_p4 t2
+ -> Nested Loop
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> HashAggregate
+ Group Key: (t2_1.c)::text
+ -> Seq Scan on plt2_p1 t2_1
+ -> Materialize
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(29 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt1_p4 t2
+ -> Materialize
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2_1
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_3
+(26 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop Anti Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Anti Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt2_p1 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 188 | 0 | 0001
+ 282 | 0 | 0010
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt1_p4 t2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 470 | 0 | 0011
+(1 row)
+
+--
+-- list partitioned by expression
+--
+CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
+CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0002', '0003');
+CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0004', '0005', '0006');
+CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0008', '0009');
+CREATE TABLE plt1_e_p4 PARTITION OF plt1_e FOR VALUES IN ('0000');
+INSERT INTO plt1_e SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 7, 10, 11, 12, 13, 14, 15, 16);
+ANALYZE plt1_e;
+-- test partition matching with N-way join
+EXPLAIN (COSTS OFF)
+SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
+ QUERY PLAN
+----------------------------------------------------------------------------------------
+ Sort
+ Sort Key: t1.c, t3.c
+ -> HashAggregate
+ Group Key: t1.c, t2.c, t3.c
+ -> Result
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = ltrim(t3.c, 'A'::text))
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt1_e_p4 t3
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ -> Hash Join
+ Hash Cond: ((t2_1.c)::text = ltrim(t3_1.c, 'A'::text))
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_e_p1 t3_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = ltrim(t3_2.c, 'A'::text))
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt1_e_p2 t3_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = ltrim(t3_3.c, 'A'::text))
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt1_e_p3 t3_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(42 rows)
+
+SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
+ avg | avg | avg | c | c | c
+----------------------+---------------------+----------------------+------+------+------
+ 246.5000000000000000 | 22.4666666666666667 | 268.9666666666666667 | 0000 | 0000 | 0000
+ 248.5000000000000000 | 21.3333333333333333 | 269.8333333333333333 | 0002 | 0002 | 0002
+ 249.5000000000000000 | 22.3333333333333333 | 271.8333333333333333 | 0003 | 0003 | 0003
+ 250.5000000000000000 | 23.3333333333333333 | 273.8333333333333333 | 0004 | 0004 | 0004
+ 251.5000000000000000 | 22.7666666666666667 | 274.2666666666666667 | 0005 | 0005 | 0005
+ 252.5000000000000000 | 22.2000000000000000 | 274.7000000000000000 | 0006 | 0006 | 0006
+ 246.0000000000000000 | 23.9655172413793103 | 269.9655172413793103 | 0008 | 0008 | 0008
+ 247.0000000000000000 | 23.3448275862068966 | 270.3448275862068966 | 0009 | 0009 | 0009
+(8 rows)
+
+-- Add an extra partition to plt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (13, 14);
+ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Join Filter: ((t1_1.b + t2_1.b) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + t2_2.b) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t1_3.b + t2_3.b) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Left Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt1_p4 t1
+ -> Hash
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Right Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Left Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Left Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(7 rows)
+
+-- right join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Hash Right Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(17 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 47 | 0013
+ | | 470 | 0011
+ | | 235 | 0014
+(8 rows)
+
+-- full join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Full Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Seq Scan on plt2_p5 t2_4
+(17 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 47 | 0013
+ | | 235 | 0014
+ | | 470 | 0011
+(10 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt2_p4 t2
+ -> Nested Loop
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> HashAggregate
+ Group Key: (t2_1.c)::text
+ -> Seq Scan on plt2_p1 t2_1
+ -> Materialize
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(29 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt1_p4 t2
+ -> Materialize
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2_1
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_3
+(26 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop Anti Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Anti Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt2_p1 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 188 | 0 | 0001
+ 282 | 0 | 0010
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p5 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t2
+ -> Seq Scan on plt1_p1 t2_1
+ -> Seq Scan on plt1_p2 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(21 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 47 | 0 | 0013
+ 235 | 0 | 0014
+ 470 | 0 | 0011
+(3 rows)
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt2_p5;
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0001','0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (1, 13, 14);
+ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Left Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Hash Right Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Full Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(23 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p5 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p1 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt1_p4 t2
+ -> Seq Scan on plt1_p1 t2_1
+ -> Seq Scan on plt1_p2 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(24 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+----------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Nested Loop Anti Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Materialize
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p5 t2_1
+ -> Seq Scan on plt2_p1 t2_2
+ -> Seq Scan on plt2_p2 t2_3
+ -> Seq Scan on plt2_p3 t2_4
+(20 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p5 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p1 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_4
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t2
+ -> Seq Scan on plt1_p1 t2_1
+ -> Seq Scan on plt1_p2 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(21 rows)
+
+-- partition have a NULL on one side, Partition-wise join is possible with
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- in this case NULL will be treated as addition partition bounds.
+DROP TABLE plt2_p5;
+DROP TABLE plt2_p4;
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000',NULL,'0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, case when i % :part_mod = 11 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (0,11,12);
+ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash
+ -> Seq Scan on plt1_p4 t1
+ -> Hash Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Join Filter: ((t1_1.b + t2_1.b) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Join Filter: ((t1_2.b + t2_2.b) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Join Filter: ((t1_3.b + t2_3.b) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+------------------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Append
+ -> Hash Right Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash
+ -> Seq Scan on plt1_p4 t1
+ -> Hash Right Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Left Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Left Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(7 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+------------------------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Result
+ -> Append
+ -> Hash Left Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash
+ -> Seq Scan on plt1_p4 t1
+ -> Hash Left Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + t2_1.b) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Left Join
+ Hash Cond: ((t2_2.c)::text = (t1_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + t2_2.b) = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash Right Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + t2_3.b) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(28 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 |
+(6 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Append
+ -> Hash Full Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash
+ -> Seq Scan on plt1_p4 t1
+ -> Hash Full Join
+ Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+ Filter: ((COALESCE(t1_1.b, 0) + COALESCE(t2_1.b, 0)) = 0)
+ -> Seq Scan on plt2_p1 t2_1
+ -> Hash
+ -> Seq Scan on plt1_p1 t1_1
+ -> Hash Full Join
+ Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+ Filter: ((COALESCE(t1_2.b, 0) + COALESCE(t2_2.b, 0)) = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ -> Hash
+ -> Seq Scan on plt2_p2 t2_2
+ -> Hash Full Join
+ Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+ Filter: ((COALESCE(t1_3.b, 0) + COALESCE(t2_3.b, 0)) = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ -> Hash
+ -> Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ a | c | a | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 94 | 0009 | 94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 | |
+ 282 | 0010 | |
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+ | | 470 |
+(8 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt2_p4 t2
+ -> Materialize
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Nested Loop
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> HashAggregate
+ Group Key: (t2_1.c)::text
+ -> Seq Scan on plt2_p1 t2_1
+ -> Materialize
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(29 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Append
+ -> Nested Loop
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Seq Scan on plt1_p4 t2
+ -> Materialize
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2_1
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_2
+ -> Nested Loop Semi Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_3
+(26 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 0 | 0 | 0000
+ 94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
--- MergeAppend on nullable column
+-- anti join
EXPLAIN (COSTS OFF)
-SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
- QUERY PLAN
------------------------------------------------------------
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
Sort
- Sort Key: prt1_p1.a, b
+ Sort Key: t1.a, t1.c
-> Append
- -> Merge Left Join
- Merge Cond: (prt1_p1.a = b)
- -> Sort
- Sort Key: prt1_p1.a
- -> Seq Scan on prt1_p1
- Filter: ((a < 450) AND (b = 0))
- -> Sort
- Sort Key: b
- -> Result
- One-Time Filter: false
- -> Merge Left Join
- Merge Cond: (prt1_p2.a = prt2_p2.b)
- -> Sort
- Sort Key: prt1_p2.a
- -> Seq Scan on prt1_p2
- Filter: ((a < 450) AND (b = 0))
- -> Sort
- Sort Key: prt2_p2.b
- -> Seq Scan on prt2_p2
- Filter: (b > 250)
-(23 rows)
+ -> Nested Loop Anti Join
+ Join Filter: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p4 t2
+ -> Hash Anti Join
+ Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Hash
+ -> Seq Scan on plt2_p1 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t2_2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t2_3
+(24 rows)
-SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
- a | b
------+-----
- 0 |
- 50 |
- 100 |
- 150 |
- 200 |
- 250 |
- 300 | 300
- 350 |
- 400 |
-(9 rows)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+------
+ 188 | 0 | 0001
+ 282 | 0 | 0010
+(2 rows)
-RESET enable_hashjoin;
-RESET enable_nestloop;
---
--- partitioned by multiple columns
---
-CREATE TABLE prt1_m (a int, b int, c int) PARTITION BY RANGE(a, ((a + b)/2));
-CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES FROM (0, 0) TO (250, 250);
-CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES FROM (250, 250) TO (500, 500);
-CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES FROM (500, 500) TO (600, 600);
-INSERT INTO prt1_m SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i;
-ANALYZE prt1_m;
-CREATE TABLE prt2_m (a int, b int, c int) PARTITION BY RANGE(((b + a)/2), b);
-CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES FROM (0, 0) TO (250, 250);
-CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES FROM (250, 250) TO (500, 500);
-CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES FROM (500, 500) TO (600, 600);
-INSERT INTO prt2_m SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i;
-ANALYZE prt2_m;
EXPLAIN (COSTS OFF)
-SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
- QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
Sort
- Sort Key: prt1_m_p1.a, prt2_m_p1.b
+ Sort Key: t1.a, t1.c
-> Append
- -> Hash Full Join
- Hash Cond: ((prt1_m_p1.a = ((prt2_m_p1.b + prt2_m_p1.a) / 2)) AND (((prt1_m_p1.a + prt1_m_p1.b) / 2) = prt2_m_p1.b))
- -> Seq Scan on prt1_m_p1
- Filter: (c = 0)
- -> Hash
- -> Seq Scan on prt2_m_p1
- Filter: (c = 0)
- -> Hash Full Join
- Hash Cond: ((prt1_m_p2.a = ((prt2_m_p2.b + prt2_m_p2.a) / 2)) AND (((prt1_m_p2.a + prt1_m_p2.b) / 2) = prt2_m_p2.b))
- -> Seq Scan on prt1_m_p2
- Filter: (c = 0)
- -> Hash
- -> Seq Scan on prt2_m_p2
- Filter: (c = 0)
- -> Hash Full Join
- Hash Cond: ((prt1_m_p3.a = ((prt2_m_p3.b + prt2_m_p3.a) / 2)) AND (((prt1_m_p3.a + prt1_m_p3.b) / 2) = prt2_m_p3.b))
- -> Seq Scan on prt1_m_p3
- Filter: (c = 0)
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
-> Hash
- -> Seq Scan on prt2_m_p3
- Filter: (c = 0)
+ -> Seq Scan on plt1_p4 t2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t2_1
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t2_2
+ -> Nested Loop Anti Join
+ Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t2_3
(24 rows)
-SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
- a | c | b | c
------+---+-----+---
- 0 | 0 | 0 | 0
- 50 | 0 | |
- 100 | 0 | |
- 150 | 0 | 150 | 0
- 200 | 0 | |
- 250 | 0 | |
- 300 | 0 | 300 | 0
- 350 | 0 | |
- 400 | 0 | |
- 450 | 0 | 450 | 0
- 500 | 0 | |
- 550 | 0 | |
- | | 75 | 0
- | | 225 | 0
- | | 375 | 0
- | | 525 | 0
-(16 rows)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ a | b | c
+-----+---+---
+ 470 | 0 |
+(1 row)
---
--- tests for list partitioned tables.
---
-CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+-- partition have a NULL on both side with different partition bounds w.r.t other side
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt1_p3;
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN (NULL,'0008','0009');
+INSERT INTO plt1 SELECT i, i % :cond_mod, case when i % :part_mod = 7 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (7,8,9);
ANALYZE plt1;
-CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i;
-ANALYZE plt2;
---
--- list partitioned by expression
---
-CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
-CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
-ANALYZE plt1_e;
--- test partition matching with N-way join
+-- inner join
EXPLAIN (COSTS OFF)
-SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
- QUERY PLAN
---------------------------------------------------------------------------------------
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
Sort
- Sort Key: t1.c, t3.c
- -> HashAggregate
- Group Key: t1.c, t2.c, t3.c
- -> Result
+ Sort Key: t1.a
+ -> Hash Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Join Filter: ((t1.b + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Hash
-> Append
- -> Hash Join
- Hash Cond: (t1.c = t2.c)
- -> Seq Scan on plt1_p1 t1
- -> Hash
- -> Hash Join
- Hash Cond: (t2.c = ltrim(t3.c, 'A'::text))
- -> Seq Scan on plt2_p1 t2
- -> Hash
- -> Seq Scan on plt1_e_p1 t3
- -> Hash Join
- Hash Cond: (t1_1.c = t2_1.c)
- -> Seq Scan on plt1_p2 t1_1
- -> Hash
- -> Hash Join
- Hash Cond: (t2_1.c = ltrim(t3_1.c, 'A'::text))
- -> Seq Scan on plt2_p2 t2_1
- -> Hash
- -> Seq Scan on plt1_e_p2 t3_1
- -> Hash Join
- Hash Cond: (t1_2.c = t2_2.c)
- -> Seq Scan on plt1_p3 t1_2
- -> Hash
- -> Hash Join
- Hash Cond: (t2_2.c = ltrim(t3_2.c, 'A'::text))
- -> Seq Scan on plt2_p3 t2_2
- -> Hash
- -> Seq Scan on plt1_e_p3 t3_2
-(33 rows)
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
-SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
- avg | avg | avg | c | c | c
-----------------------+----------------------+-----------------------+------+------+-------
- 24.0000000000000000 | 24.0000000000000000 | 48.0000000000000000 | 0000 | 0000 | A0000
- 74.0000000000000000 | 75.0000000000000000 | 148.0000000000000000 | 0001 | 0001 | A0001
- 124.0000000000000000 | 124.5000000000000000 | 248.0000000000000000 | 0002 | 0002 | A0002
- 174.0000000000000000 | 174.0000000000000000 | 348.0000000000000000 | 0003 | 0003 | A0003
- 224.0000000000000000 | 225.0000000000000000 | 448.0000000000000000 | 0004 | 0004 | A0004
- 274.0000000000000000 | 274.5000000000000000 | 548.0000000000000000 | 0005 | 0005 | A0005
- 324.0000000000000000 | 324.0000000000000000 | 648.0000000000000000 | 0006 | 0006 | A0006
- 374.0000000000000000 | 375.0000000000000000 | 748.0000000000000000 | 0007 | 0007 | A0007
- 424.0000000000000000 | 424.5000000000000000 | 848.0000000000000000 | 0008 | 0008 | A0008
- 474.0000000000000000 | 474.0000000000000000 | 948.0000000000000000 | 0009 | 0009 | A0009
- 524.0000000000000000 | 525.0000000000000000 | 1048.0000000000000000 | 0010 | 0010 | A0010
- 574.0000000000000000 | 574.5000000000000000 | 1148.0000000000000000 | 0011 | 0011 | A0011
-(12 rows)
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a
+ -> Hash Right Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t2.a
+ -> Hash Left Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t2.a
+ -> Hash Full Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ -> Seq Scan on plt1_p1 t1_1
+ -> Seq Scan on plt1_p2 t1_2
+ -> Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+(22 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> HashAggregate
+ Group Key: (t2.c)::text
+ -> Result
+ -> Append
+ -> Seq Scan on plt1_p4 t2
+ -> Seq Scan on plt1_p1 t2_1
+ -> Seq Scan on plt1_p2 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(22 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt1_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt1_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on plt2_p4 t2
+ -> Seq Scan on plt2_p1 t2_1
+ -> Seq Scan on plt2_p2 t2_2
+ -> Seq Scan on plt2_p3 t2_3
+(19 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+ QUERY PLAN
+--------------------------------------------------
+ Sort
+ Sort Key: t1.a, t1.c
+ -> Hash Anti Join
+ Hash Cond: ((t1.c)::text = (t2.c)::text)
+ -> Append
+ -> Seq Scan on plt2_p4 t1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p1 t1_1
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p2 t1_2
+ Filter: (b = 0)
+ -> Seq Scan on plt2_p3 t1_3
+ Filter: (b = 0)
+ -> Hash
+ -> Append
+ -> Seq Scan on plt1_p4 t2
+ -> Seq Scan on plt1_p1 t2_1
+ -> Seq Scan on plt1_p2 t2_2
+ -> Seq Scan on plt1_p3 t2_3
+(19 rows)
-- joins where one of the relations is proven empty
EXPLAIN (COSTS OFF)
@@ -1225,16 +3871,22 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1
-> Hash Left Join
Hash Cond: (t2.b = a)
-> Append
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t2_1
Filter: (a = 0)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p2 t2_2
Filter: (a = 0)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t2_5
Filter: (a = 0)
-> Hash
-> Result
One-Time Filter: false
-(14 rows)
+(20 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b;
@@ -1245,16 +3897,22 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1
-> Hash Left Join
Hash Cond: (t2.b = a)
-> Append
- -> Seq Scan on prt2_p1 t2
+ -> Seq Scan on prt2_p0 t2
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p1 t2_1
Filter: (a = 0)
- -> Seq Scan on prt2_p2 t2_1
+ -> Seq Scan on prt2_p2 t2_2
Filter: (a = 0)
- -> Seq Scan on prt2_p3 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p4 t2_4
+ Filter: (a = 0)
+ -> Seq Scan on prt2_p5 t2_5
Filter: (a = 0)
-> Hash
-> Result
One-Time Filter: false
-(14 rows)
+(20 rows)
--
-- multiple levels of partitioning
@@ -1615,64 +4273,70 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2 WHERE t1.a = t2.a;
Hash Join
Hash Cond: (t1.a = t2.a)
-> Append
- -> Seq Scan on prt1_p1 t1
- -> Seq Scan on prt1_p2 t1_1
- -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
-> Hash
-> Append
-> Seq Scan on prt4_n_p1 t2
-> Seq Scan on prt4_n_p2 t2_1
-> Seq Scan on prt4_n_p3 t2_2
-(11 rows)
+(13 rows)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2, prt2 t3 WHERE t1.a = t2.a and t1.a = t3.b;
- QUERY PLAN
---------------------------------------------------------
+ QUERY PLAN
+----------------------------------------------------------
Hash Join
- Hash Cond: (t2.a = t1.a)
+ Hash Cond: (t3.b = t1.a)
-> Append
- -> Seq Scan on prt4_n_p1 t2
- -> Seq Scan on prt4_n_p2 t2_1
- -> Seq Scan on prt4_n_p3 t2_2
+ -> Seq Scan on prt2_p0 t3
+ -> Seq Scan on prt2_p1 t3_1
+ -> Seq Scan on prt2_p2 t3_2
+ -> Seq Scan on prt2_p3 t3_3
+ -> Seq Scan on prt2_p4 t3_4
+ -> Seq Scan on prt2_p5 t3_5
-> Hash
- -> Append
- -> Hash Join
- Hash Cond: (t1.a = t3.b)
- -> Seq Scan on prt1_p1 t1
- -> Hash
- -> Seq Scan on prt2_p1 t3
- -> Hash Join
- Hash Cond: (t1_1.a = t3_1.b)
- -> Seq Scan on prt1_p2 t1_1
- -> Hash
- -> Seq Scan on prt2_p2 t3_1
- -> Hash Join
- Hash Cond: (t1_2.a = t3_2.b)
- -> Seq Scan on prt1_p3 t1_2
- -> Hash
- -> Seq Scan on prt2_p3 t3_2
+ -> Hash Join
+ Hash Cond: (t1.a = t2.a)
+ -> Append
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Hash
+ -> Append
+ -> Seq Scan on prt4_n_p1 t2
+ -> Seq Scan on prt4_n_p2 t2_1
+ -> Seq Scan on prt4_n_p3 t2_2
(23 rows)
-- partition-wise join can not be applied if there are no equi-join conditions
-- between partition keys
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON (t1.a < t2.b);
- QUERY PLAN
----------------------------------------------------------
+ QUERY PLAN
+--------------------------------------------
Nested Loop Left Join
+ Join Filter: (t1.a < t2.b)
-> Append
- -> Seq Scan on prt1_p1 t1
- -> Seq Scan on prt1_p2 t1_1
- -> Seq Scan on prt1_p3 t1_2
- -> Append
- -> Index Scan using iprt2_p1_b on prt2_p1 t2
- Index Cond: (t1.a < b)
- -> Index Scan using iprt2_p2_b on prt2_p2 t2_1
- Index Cond: (t1.a < b)
- -> Index Scan using iprt2_p3_b on prt2_p3 t2_2
- Index Cond: (t1.a < b)
-(12 rows)
+ -> Seq Scan on prt1_p0 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p2 t1_2
+ -> Seq Scan on prt1_p3 t1_3
+ -> Seq Scan on prt1_p4 t1_4
+ -> Materialize
+ -> Append
+ -> Seq Scan on prt2_p0 t2
+ -> Seq Scan on prt2_p1 t2_1
+ -> Seq Scan on prt2_p2 t2_2
+ -> Seq Scan on prt2_p3 t2_3
+ -> Seq Scan on prt2_p4 t2_4
+ -> Seq Scan on prt2_p5 t2_5
+(16 rows)
-- equi-join with join condition on partial keys does not qualify for
-- partition-wise join
@@ -1758,16 +4422,17 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 JOIN prt2_n t2 ON (t1.c = t2.c) JOI
-> Seq Scan on prt2_n_p2 t2_1
-> Hash
-> Hash Join
- Hash Cond: (t3.c = (t1.c)::text)
+ Hash Cond: ((t3.c)::text = (t1.c)::text)
-> Append
- -> Seq Scan on plt1_p1 t3
- -> Seq Scan on plt1_p2 t3_1
- -> Seq Scan on plt1_p3 t3_2
+ -> Seq Scan on plt1_p4 t3
+ -> Seq Scan on plt1_p1 t3_1
+ -> Seq Scan on plt1_p2 t3_2
+ -> Seq Scan on plt1_p3 t3_3
-> Hash
-> Append
-> Seq Scan on prt1_n_p1 t1
-> Seq Scan on prt1_n_p2 t1_1
-(16 rows)
+(17 rows)
-- partition-wise join can not be applied for a join between list and range
-- partitioned table
@@ -1778,12 +4443,14 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 FULL JOIN prt1 t2 ON (t1.c = t2.c);
Hash Full Join
Hash Cond: ((t2.c)::text = (t1.c)::text)
-> Append
- -> Seq Scan on prt1_p1 t2
- -> Seq Scan on prt1_p2 t2_1
- -> Seq Scan on prt1_p3 t2_2
+ -> Seq Scan on prt1_p0 t2
+ -> Seq Scan on prt1_p1 t2_1
+ -> Seq Scan on prt1_p2 t2_2
+ -> Seq Scan on prt1_p3 t2_3
+ -> Seq Scan on prt1_p4 t2_4
-> Hash
-> Append
-> Seq Scan on prt1_n_p1 t1
-> Seq Scan on prt1_n_p2 t1_1
-(10 rows)
+(12 rows)
diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql
index ca525d9..5812792 100644
--- a/src/test/regress/sql/partition_join.sql
+++ b/src/test/regress/sql/partition_join.sql
@@ -10,25 +10,39 @@ SET enable_partition_wise_join to true;
-- partitioned by a single column
--
CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_p0 PARTITION OF prt1 FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250);
CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600);
CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500);
-INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0;
+CREATE TABLE prt1_p4 PARTITION OF prt1 FOR VALUES FROM (600) TO (800);
+INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 2 = 0;
+CREATE INDEX iprt1_p0_a on prt1_p0(a);
CREATE INDEX iprt1_p1_a on prt1_p1(a);
CREATE INDEX iprt1_p2_a on prt1_p2(a);
CREATE INDEX iprt1_p3_a on prt1_p3(a);
+CREATE INDEX iprt1_p4_a on prt1_p4(a);
ANALYZE prt1;
+-- prt2 have missing starting MINVALUE to -250 range and
+-- extra bounds from 800 to MAXVALUE
CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_p0 PARTITION OF prt2 FOR VALUES FROM (-250) TO (0);
CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250);
CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500);
CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600);
-INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (MAXVALUE);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 3 = 0;
+CREATE INDEX iprt2_p0_b on prt2_p0(b);
CREATE INDEX iprt2_p1_b on prt2_p1(b);
CREATE INDEX iprt2_p2_b on prt2_p2(b);
CREATE INDEX iprt2_p3_b on prt2_p3(b);
+CREATE INDEX iprt2_p4_b on prt2_p4(b);
ANALYZE prt2;
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
+
-- inner join
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
@@ -67,11 +81,19 @@ EXPLAIN (COSTS OFF)
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
+EXPLAIN (COSTS OFF)
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+
-- Anti-join with aggregates
EXPLAIN (COSTS OFF)
SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b);
SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b);
+EXPLAIN (COSTS OFF)
+SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0;
+SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0;
+
-- lateral reference
EXPLAIN (COSTS OFF)
SELECT * FROM prt1 t1 LEFT JOIN LATERAL
@@ -93,20 +115,30 @@ SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
-- partitioned by expression
--
CREATE TABLE prt1_e (a int, b int, c int) PARTITION BY RANGE(((a + b)/2));
+CREATE TABLE prt1_e_p0 PARTITION OF prt1_e FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES FROM (0) TO (250);
CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES FROM (250) TO (500);
CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt1_e_p4 PARTITION OF prt1_e FOR VALUES FROM (600) TO (MAXVALUE);
INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(600, 799, 2) i;
+CREATE INDEX iprt1_e_p0_ab2 on prt1_e_p1(((a+b)/2));
CREATE INDEX iprt1_e_p1_ab2 on prt1_e_p1(((a+b)/2));
CREATE INDEX iprt1_e_p2_ab2 on prt1_e_p2(((a+b)/2));
CREATE INDEX iprt1_e_p3_ab2 on prt1_e_p3(((a+b)/2));
+CREATE INDEX iprt1_e_p4_ab2 on prt1_e_p1(((a+b)/2));
ANALYZE prt1_e;
CREATE TABLE prt2_e (a int, b int, c int) PARTITION BY RANGE(((b + a)/2));
+CREATE TABLE prt2_e_p0 PARTITION OF prt2_e FOR VALUES FROM (MINVALUE) TO (0);
CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES FROM (0) TO (250);
CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES FROM (250) TO (500);
CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt2_e_p4 PARTITION OF prt2_e FOR VALUES FROM (600) TO (MAXVALUE);
INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(600, 799, 3) i;
ANALYZE prt2_e;
EXPLAIN (COSTS OFF)
@@ -163,6 +195,85 @@ SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT *
RESET enable_hashjoin;
RESET enable_nestloop;
+-- Add an extra partition to prt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+DROP TABLE prt2_p4;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (800);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (800) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999) i WHERE i % 3 = 0;
+ANALYZE prt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- partition-wise join can not handle missing partition on the inner side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.b;
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE coalesce(t1.b, 0) + coalesce(t2.a, 0) = 0 ORDER BY t1.a, t2.a;
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE prt2_p4;
+DROP TABLE prt2_p5;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (700);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (700) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999, 3) i;
+ANALYZE prt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
--
-- partitioned by multiple columns
--
@@ -187,28 +298,79 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1
--
-- tests for list partitioned tables.
--
-CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
-ANALYZE plt1;
+\set part_mod 17
+\set cond_mod 47
+\set num_rows 500
+
+CREATE TABLE plt1 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0001','0002','0003');
+CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0008','0009');
+CREATE TABLE plt1_p4 PARTITION OF plt1 FOR VALUES IN ('0000','0010');
+INSERT INTO plt1 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (7, 11, 12, 13, 14, 15, 16);
+ANALYSE plt1;
+
+-- plt2 have missing starting 0001, additional 0007, missing ending 0010
+-- and additional 0011 and 0012 bounds
+CREATE TABLE plt2 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002','0003');
+CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0007','0008','0009');
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000','0011','0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 10, 13, 14, 15, 16);
+ANALYSE plt2;
+
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
-CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i;
-ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
--
-- list partitioned by expression
--
CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
-CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0002', '0003');
+CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0004', '0005', '0006');
+CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0008', '0009');
+CREATE TABLE plt1_e_p4 PARTITION OF plt1_e FOR VALUES IN ('0000');
+INSERT INTO plt1_e SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 7, 10, 11, 12, 13, 14, 15, 16);
ANALYZE plt1_e;
-- test partition matching with N-way join
@@ -216,6 +378,175 @@ EXPLAIN (COSTS OFF)
SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
+-- Add an extra partition to plt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (13, 14);
+ANALYZE plt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+
+-- full join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt2_p5;
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0001','0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (1, 13, 14);
+ANALYZE plt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- partition have a NULL on one side, Partition-wise join is possible with
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- in this case NULL will be treated as addition partition bounds.
+DROP TABLE plt2_p5;
+DROP TABLE plt2_p4;
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000',NULL,'0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, case when i % :part_mod = 11 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (0,11,12);
+ANALYZE plt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- partition have a NULL on both side with different partition bounds w.r.t other side
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt1_p3;
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN (NULL,'0008','0009');
+INSERT INTO plt1 SELECT i, i % :cond_mod, case when i % :part_mod = 7 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (7,8,9);
+ANALYZE plt1;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
-- joins where one of the relations is proven empty
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a = 1 AND t1.a = 2;
--
1.7.9.5
0002-Modify-bound-comparision-functions-to-accept-members.patchtext/x-patch; charset=US-ASCII; name=0002-Modify-bound-comparision-functions-to-accept-members.patchDownload
From cc0b81ee8891677adb8bff3e98d80993788632dc Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>
Date: Thu, 6 Jul 2017 14:15:22 +0530
Subject: [PATCH 2/4] Modify bound comparision functions to accept members of
PartitionKey
Functions partition_bound_cmp(), partition_rbound_cmp() and
partition_rbound_datum_cmp() are required to merge partition bounds
from joining relations. While doing so, we do not have access to the
PartitionKey of either relations. So, modify these functions to accept
only required members of PartitionKey so that the functions can be
reused for merging bounds.
Ashutosh Bapat.
---
src/backend/catalog/partition.c | 76 ++++++++++++++++++++++-----------------
1 file changed, 44 insertions(+), 32 deletions(-)
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index ebda85e..87b3fbc 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -134,15 +134,17 @@ static List *generate_partition_qual(Relation rel);
static PartitionRangeBound *make_one_range_bound(PartitionKey key, int index,
List *datums, bool lower);
-static int32 partition_rbound_cmp(PartitionKey key,
- Datum *datums1, PartitionRangeDatumKind *kind1,
- bool lower1, PartitionRangeBound *b2);
-static int32 partition_rbound_datum_cmp(PartitionKey key,
- Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
+static int32 partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, Datum *datums1,
+ PartitionRangeDatumKind *kind1, bool lower1,
+ PartitionRangeBound *b2);
+static int32 partition_rbound_datum_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, Datum *rb_datums,
+ PartitionRangeDatumKind *rb_kind,
Datum *tuple_datums);
-static int32 partition_bound_cmp(PartitionKey key,
- PartitionBoundInfo boundinfo,
+static int32 partition_bound_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, PartitionBoundInfo boundinfo,
int offset, void *probe, bool probe_is_bound);
static int partition_bound_bsearch(PartitionKey key,
PartitionBoundInfo boundinfo,
@@ -859,8 +861,9 @@ check_new_partition_bound(char *relname, Relation parent,
* First check if the resulting range would be empty with
* specified lower and upper bounds
*/
- if (partition_rbound_cmp(key, lower->datums, lower->kind, true,
- upper) >= 0)
+ if (partition_rbound_cmp(key->partnatts, key->partsupfunc,
+ key->partcollation, lower->datums,
+ lower->kind, true, upper) >= 0)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -913,9 +916,11 @@ check_new_partition_bound(char *relname, Relation parent,
{
int32 cmpval;
- cmpval = partition_bound_cmp(key, boundinfo,
- offset + 1, upper,
- true);
+ cmpval = partition_bound_cmp(key->partnatts,
+ key->partsupfunc,
+ key->partcollation,
+ boundinfo, offset + 1,
+ upper, true);
if (cmpval < 0)
{
/*
@@ -2595,7 +2600,9 @@ qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
PartitionKey key = (PartitionKey) arg;
- return partition_rbound_cmp(key, b1->datums, b1->kind, b1->lower, b2);
+ return partition_rbound_cmp(key->partnatts, key->partsupfunc,
+ key->partcollation, b1->datums, b1->kind,
+ b1->lower, b2);
}
/*
@@ -2612,7 +2619,7 @@ qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
* two contiguous partitions.
*/
static int32
-partition_rbound_cmp(PartitionKey key,
+partition_rbound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation,
Datum *datums1, PartitionRangeDatumKind *kind1,
bool lower1, PartitionRangeBound *b2)
{
@@ -2622,7 +2629,7 @@ partition_rbound_cmp(PartitionKey key,
PartitionRangeDatumKind *kind2 = b2->kind;
bool lower2 = b2->lower;
- for (i = 0; i < key->partnatts; i++)
+ for (i = 0; i < partnatts; i++)
{
/*
* First, handle cases where the column is unbounded, which should not
@@ -2643,8 +2650,8 @@ partition_rbound_cmp(PartitionKey key,
*/
break;
- cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
- key->partcollation[i],
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
+ partcollation[i],
datums1[i],
datums2[i]));
if (cmpval != 0)
@@ -2670,22 +2677,23 @@ partition_rbound_cmp(PartitionKey key,
* is <, =, or > partition key of tuple (tuple_datums)
*/
static int32
-partition_rbound_datum_cmp(PartitionKey key,
- Datum *rb_datums, PartitionRangeDatumKind *rb_kind,
+partition_rbound_datum_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, Datum *rb_datums,
+ PartitionRangeDatumKind *rb_kind,
Datum *tuple_datums)
{
int i;
int32 cmpval = -1;
- for (i = 0; i < key->partnatts; i++)
+ for (i = 0; i < partnatts; i++)
{
if (rb_kind[i] == PARTITION_RANGE_DATUM_MINVALUE)
return -1;
else if (rb_kind[i] == PARTITION_RANGE_DATUM_MAXVALUE)
return 1;
- cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[i],
- key->partcollation[i],
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[i],
+ partcollation[i],
rb_datums[i],
tuple_datums[i]));
if (cmpval != 0)
@@ -2702,17 +2710,18 @@ partition_rbound_datum_cmp(PartitionKey key,
* specified in *probe.
*/
static int32
-partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo,
- int offset, void *probe, bool probe_is_bound)
+partition_bound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation,
+ PartitionBoundInfo boundinfo, int offset, void *probe,
+ bool probe_is_bound)
{
Datum *bound_datums = boundinfo->datums[offset];
int32 cmpval = -1;
- switch (key->strategy)
+ switch (boundinfo->strategy)
{
case PARTITION_STRATEGY_LIST:
- cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
- key->partcollation[0],
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
+ partcollation[0],
bound_datums[0],
*(Datum *) probe));
break;
@@ -2730,12 +2739,14 @@ partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo,
*/
bool lower = boundinfo->indexes[offset] < 0;
- cmpval = partition_rbound_cmp(key,
- bound_datums, kind, lower,
+ cmpval = partition_rbound_cmp(partnatts, partsupfunc,
+ partcollation, bound_datums,
+ kind, lower,
(PartitionRangeBound *) probe);
}
else
- cmpval = partition_rbound_datum_cmp(key,
+ cmpval = partition_rbound_datum_cmp(partnatts, partsupfunc,
+ partcollation,
bound_datums, kind,
(Datum *) probe);
break;
@@ -2743,7 +2754,7 @@ partition_bound_cmp(PartitionKey key, PartitionBoundInfo boundinfo,
default:
elog(ERROR, "unexpected partition strategy: %d",
- (int) key->strategy);
+ (int) boundinfo->strategy);
}
return cmpval;
@@ -2777,7 +2788,8 @@ partition_bound_bsearch(PartitionKey key, PartitionBoundInfo boundinfo,
int32 cmpval;
mid = (lo + hi + 1) / 2;
- cmpval = partition_bound_cmp(key, boundinfo, mid, probe,
+ cmpval = partition_bound_cmp(key->partnatts, key->partsupfunc,
+ key->partcollation, boundinfo, mid, probe,
probe_is_bound);
if (cmpval <= 0)
{
--
1.7.9.5
0003-WIP-Partition-wise-join-for-1-1-1-0-0-1-partition-ma.patchtext/x-patch; charset=US-ASCII; name=0003-WIP-Partition-wise-join-for-1-1-1-0-0-1-partition-ma.patchDownload
From 00144d214fdfc156c50921f7fe217ad701af6f8a Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <ashutosh.bapat@enterprisedb.com>
Date: Wed, 9 Aug 2017 12:30:34 +0530
Subject: [PATCH 3/4] WIP Partition-wise join for 1:1, 1:0, 0:1 partition
matching.
Earlier version of partition-wise join implementation allowed
partition-wise join between two relations with exactly same partition
bounds. This commit allows partition-wise join to be applied under
following conditions
1. the partition bounds of joining relations are such that rows from
given partition on one side join can join with rows from maximum one
partition on the other side i.e. bounds of a given partition on one
side match/overlap with those of maximum one partition on the other
side. If the mapping happens to be m:n where m > 1 or n > 1, we have
to gang multiple partition relations together into a single relation.
This means that we have to add simple relations during join
processing, something which is not supported right now. ALso, in such
a case, different pairs of joining relations can produce different
partition bounds for the same join relation, which again is not
supported right now.
2. For every partition on outer side that can contribute to the result
of an OUTER side, there exists at least one (taken along with item 1,
it means exactly one) matching partition on the inner side. To
support partition-wise join when the inner matching partition doesn't
exist, we have to add a dummy base relation corresponding to the
non-existent inner partition. We don't have support add base relations
during join processing.
This commit is not complete yet.
known TODO:
1. This patch doesn't support default partitions.
Ashutosh Bapat.
---
src/backend/catalog/partition.c | 1269 +++++++++++++++++++++++++++++++++
src/backend/optimizer/path/joinrels.c | 70 +-
src/backend/optimizer/util/plancat.c | 5 +
src/backend/optimizer/util/relnode.c | 41 +-
src/include/catalog/partition.h | 5 +
src/include/nodes/relation.h | 2 +
6 files changed, 1365 insertions(+), 27 deletions(-)
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 87b3fbc..95a54fd 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -151,6 +151,38 @@ static int partition_bound_bsearch(PartitionKey key,
void *probe, bool probe_is_bound, bool *is_equal);
static void get_partition_dispatch_recurse(Relation rel, Relation parent,
List **pds, List **leaf_part_oids);
+static PartitionBoundInfo partition_range_bounds_merge(int partnatts,
+ FmgrInfo *supfuncs, Oid *collations,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2);
+static PartitionBoundInfo partition_list_bounds_merge(int partnatts,
+ FmgrInfo *partsupfunc, Oid *collations,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2);
+static void generate_matching_part_pairs(int *mergemap1, int npart1,
+ int *mergemap2, int nparts2, JoinType jointype,
+ int nparts, List **parts1, List **parts2);
+static PartitionBoundInfo build_merged_partition_bounds(char strategy,
+ List *merged_datums, List *merged_indexes,
+ List *merged_contents, int null_index);
+static int map_and_merge_partitions(int *partmap1, int *mergemap1, int index1,
+ int *partmap2, int *mergemap2, int index2,
+ int *next_index);
+static int32 partition_range_bound_cmp(int partnatts, FmgrInfo *partsupfunc,
+ Oid *collations, PartitionRangeBound *bound1,
+ PartitionRangeBound *bound2);
+static int partition_range_cmp(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, PartitionRangeBound *lower_bound1,
+ PartitionRangeBound *upper_bound1,
+ PartitionRangeBound *lower_bound2,
+ PartitionRangeBound *upper_bound2, bool *overlap);
+static bool partition_range_merge_next_lb(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, Datum *next_lb_datums,
+ PartitionRangeDatumKind *next_lb_kind,
+ List **merged_datums, List **merged_kinds,
+ List **merged_indexes);
/*
* RelationBuildPartitionDesc
@@ -2906,3 +2938,1240 @@ get_proposed_default_constraint(List *new_part_constraints)
return list_make1(defPartConstraint);
}
+
+/*
+ * Merge the given partition bounds.
+ *
+ * If given partition bounds can not be merged, return NULL.
+ *
+ * The function also returns two lists of partition indexes one for each of the
+ * joining relations. Both the lists contain the same number of elements. The
+ * partition indexes at the same positions in the list indicate partitions from
+ * each side to be joined and their position corresponds to the index of
+ * partition to which the results of the child-join belong in the partitioned
+ * join.
+ */
+extern PartitionBoundInfo
+partition_bounds_merge(int partnatts, FmgrInfo *partsupfunc, Oid *partcollation,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2)
+{
+ PartitionBoundInfo merged_bounds;
+ char strategy;
+
+ /* Bail out if partitioning strategies are different. */
+ if (boundinfo1->strategy != boundinfo2->strategy)
+ return NULL;
+
+ /*
+ * TODO: We should remove this limitation in the final version of the
+ * patch. See TODO in build_merged_partition_bounds().
+ * Bail out if there are default partitions. */
+ if (partition_bound_has_default(boundinfo1) ||
+ partition_bound_has_default(boundinfo2))
+ return NULL;
+
+ *parts1 = NIL;
+ *parts2 = NIL;
+ strategy = boundinfo1->strategy;
+ if (strategy == PARTITION_STRATEGY_LIST)
+ merged_bounds = partition_list_bounds_merge(partnatts, partsupfunc,
+ partcollation, boundinfo1,
+ nparts1, boundinfo2,
+ nparts2, jointype, parts1,
+ parts2);
+ else if (strategy == PARTITION_STRATEGY_RANGE)
+ merged_bounds = partition_range_bounds_merge(partnatts, partsupfunc,
+ partcollation, boundinfo1,
+ nparts1, boundinfo2,
+ nparts2, jointype, parts1,
+ parts2);
+ else
+ elog(ERROR, "unexpected partition strategy: %d", strategy);
+
+ Assert(merged_bounds || (*parts1 == NIL && *parts2 == NIL));
+ return merged_bounds;
+}
+
+/*
+ * partition_get_range_bounds
+ *
+ * Given the index of lower bound in datums array, return lower and upper
+ * bounds and the index of the partition with that lower bound.
+ */
+static int
+partition_get_range_bounds(PartitionBoundInfo bi, int lb_index,
+ PartitionRangeBound *lower,
+ PartitionRangeBound *upper)
+{
+ int part_index;
+
+ /* A lower bound should have at least one more bound after it. */
+ Assert(lb_index < bi->ndatums - 1);
+
+ /* The lower bound should correspond to a valid partition. */
+ part_index = bi->indexes[lb_index + 1];
+ Assert(part_index >= 0);
+
+ lower->kind = bi->kind[lb_index];
+ lower->datums = bi->datums[lb_index];
+ lower->lower = true;
+ upper->kind = bi->kind[lb_index + 1];
+ upper->datums = bi->datums[lb_index + 1];
+ upper->lower = false;
+
+ return part_index;
+}
+
+/*
+ * partition_range_get_next_lb_index
+ *
+ * Given the index of lower bound in datums array return the
+ * index of lower bound of the next partition. When the given index corresponds
+ * to the last partition, return number of datums (ndatums).
+ */
+static int
+partition_range_get_next_lb_index(PartitionBoundInfo bi, int lb_index)
+{
+ /* A lower bound should have at least one more bound after it. */
+ Assert(lb_index < bi->ndatums - 1);
+
+ /* The partition index corresponding to the upper bound should be valid. */
+ Assert(bi->indexes[lb_index + 1] >= 0);
+
+ /*
+ * If there are no bounds left beyond the upper bound, we have reached the
+ * last partition.
+ */
+ if (lb_index + 2 < bi->ndatums)
+ {
+ /*
+ * If the bound next to the upper bound corresponds to no partition,
+ * that's the next lower bound of the next partition. Otherwise, the
+ * current upper bound is the lower bound of the next partition.
+ */
+ if (bi->indexes[lb_index + 2] < 0)
+ return lb_index + 2;
+ else
+ return lb_index + 1;
+ }
+ else
+ return bi->ndatums;
+}
+
+static int32
+partition_range_bound_cmp(int partnatts, FmgrInfo *partsupfunc, Oid *collations,
+ PartitionRangeBound *bound1,
+ PartitionRangeBound *bound2)
+{
+ return partition_rbound_cmp(partnatts, partsupfunc, collations,
+ bound1->datums, bound1->kind, bound1->lower,
+ bound2);
+}
+
+/*
+ * partition_range_cmp
+ *
+ * Compare the bounds of two range partitions and return <, = or > 0, if the
+ * first partition's upper bound is lower than, equal to or higher than the
+ * second partition's upper bound resp.
+ *
+ * Also, set overlaps to true, if the ranges overlap, otherwise set it to
+ * false.
+ */
+static int
+partition_range_cmp(int partnatts, FmgrInfo *supfuncs, Oid *collations,
+ PartitionRangeBound *lower_bound1,
+ PartitionRangeBound *upper_bound1,
+ PartitionRangeBound *lower_bound2,
+ PartitionRangeBound *upper_bound2, bool *overlap)
+{
+ /*
+ * Compare upper bound of the first partition with the lower bound of the
+ * second and vice-versa. If lower bound is higher than the upper bound,
+ * the partitions are not overlapping. All other cases indicate overlapping
+ * partitions.
+ * TODO: Add a testcase which has lower and upper bound matching exactly.
+ * Lower bound is inclusive and upper bound is exclusive, so even if the
+ * datums match, the bounds do not match exactly.
+ */
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ lower_bound1, upper_bound2) > 0)
+ {
+ *overlap = false;
+ return 1;
+ }
+ else if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ lower_bound2, upper_bound1) > 0)
+ {
+ *overlap = false;
+ return -1;
+ }
+ else
+ {
+ *overlap = true;
+ return partition_range_bound_cmp(partnatts, supfuncs, collations,
+ upper_bound1, upper_bound2);
+ }
+}
+
+/*
+ * partition_range_merge
+ *
+ * Merge the partition bounds of given two partitions such that the join
+ * between the given two partitions fits merged bounds.
+ *
+ * "merged_upper" will be set to one of the given upper bounds and
+ * "merged_lower" will be set to one of the given lower bounds.
+ */
+static void
+partition_range_merge(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, JoinType jointype,
+ PartitionRangeBound *left_lb,
+ PartitionRangeBound *left_ub,
+ PartitionRangeBound *right_lb,
+ PartitionRangeBound *right_ub,
+ PartitionRangeBound **merged_lb,
+ PartitionRangeBound **merged_ub)
+{
+ /*
+ * An outer join will have all the rows from the outer side, so merged
+ * bounds will be same as the outer bounds. An inner join will have rows
+ * that fit both the bounds, thus lower merged bound will be higher of two
+ * lower bounds and upper merged bound will be lower of the two upper
+ * bounds.
+ */
+ switch (jointype)
+ {
+ case JOIN_LEFT:
+ case JOIN_ANTI:
+ *merged_ub = left_ub;
+ *merged_lb = left_lb;
+ break;
+
+ case JOIN_RIGHT:
+ *merged_ub = right_ub;
+ *merged_lb = right_lb;
+ break;
+
+ case JOIN_INNER:
+ case JOIN_SEMI:
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_ub, right_ub) < 0)
+ *merged_ub = left_ub;
+ else
+ *merged_ub = right_ub;
+
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_lb, right_lb) > 0)
+ *merged_lb = left_lb;
+ else
+ *merged_lb = right_lb;
+ break;
+
+ case JOIN_FULL:
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_ub, right_ub) > 0)
+ *merged_ub = left_ub;
+ else
+ *merged_ub = right_ub;
+
+ if (partition_range_bound_cmp(partnatts, supfuncs, collations,
+ left_lb, right_lb) < 0)
+ *merged_lb = left_lb;
+ else
+ *merged_lb = right_lb;
+ break;
+
+ default:
+ elog(ERROR, "Unexpected join type %d", jointype);
+ }
+
+ return;
+}
+
+/*
+ * Add the lower bound of the next range to the list of bounds, if the lower
+ * bound is higher or equal to the previous upper bound. If successful return
+ * true, otherwise false.
+ */
+static bool
+partition_range_merge_next_lb(int partnatts, FmgrInfo *supfuncs,
+ Oid *collations, Datum *next_lb_datums,
+ PartitionRangeDatumKind *next_lb_kind,
+ List **merged_datums, List **merged_kinds,
+ List **merged_indexes)
+{
+ int cmpval;
+
+ if (!*merged_datums)
+ {
+ Assert(!*merged_kinds && !*merged_indexes);
+ cmpval = 1;
+ }
+ else
+ {
+ PartitionRangeBound prev_ub;
+
+ prev_ub.datums = llast(*merged_datums);
+ prev_ub.kind = llast(*merged_kinds);
+ prev_ub.lower = false;
+
+ /*
+ * TODO: explain why do we pass lower to be false for the next lower
+ * bound.
+ */
+ cmpval = partition_rbound_cmp(partnatts, supfuncs, collations,
+ next_lb_datums, next_lb_kind, false,
+ &prev_ub);
+ }
+
+ /*
+ * The lower bound is lower than the last upper bound, thus does not fit
+ * the bounds created so far and hence can not be merged with the existing
+ * bounds.
+ */
+ if (cmpval < 0)
+ return false;
+
+ /*
+ * Add bounds of the new merged partition. If the next lower bound is
+ * higher than the last upper bound, add new range with index
+ * corresponding to the lower bound as -1. If the merged lower bound
+ * is same as the last merged upper bound, the last upper bound will be
+ * reused as the lower bound of the next range.
+ */
+ if (cmpval > 0)
+ {
+ *merged_datums = lappend(*merged_datums, next_lb_datums);
+ *merged_kinds = lappend(*merged_kinds, next_lb_kind);
+ *merged_indexes = lappend_int(*merged_indexes, -1);
+ }
+
+ return true;
+}
+
+/*
+ * Merge given two range partition bounds.
+ *
+ * Work horse function for partition_bounds_merge() for range partitioned
+ * tables.
+ *
+ * TODO: for an anti-join, the caller is supposed to send the outer relation as
+ * left relation. May be we should rename left and right as inner and outer. We
+ * don't need to handle RIGHT joins in this function, so renaming them as outer
+ * and inner is fine.
+ */
+static PartitionBoundInfo
+partition_range_bounds_merge(int partnatts, FmgrInfo *supfuncs, Oid *collations,
+ PartitionBoundInfo left_bi, int left_nparts,
+ PartitionBoundInfo right_bi, int right_nparts,
+ JoinType jointype, List **left_parts, List **right_parts)
+{
+ int *left_pmap;
+ int *left_mmap;
+ int *right_pmap;
+ int *right_mmap;
+ int cnt1;
+ int cnt2;
+ int left_part;
+ int right_part;
+ PartitionBoundInfo merged_bounds = NULL;
+ bool merged = true; /* By default we ranges are merge-able. */
+ int left_lb_index;
+ int right_lb_index;
+ int next_index;
+ int cmpval;
+ List *merged_datums = NIL;
+ List *merged_indexes = NIL;
+ List *merged_kinds = NIL;
+
+ Assert(left_bi->strategy == right_bi->strategy &&
+ left_bi->strategy == PARTITION_STRATEGY_RANGE);
+
+ *left_parts = NIL;
+ *right_parts = NIL;
+
+ /* Allocate and initialize partition maps. */
+ left_pmap = (int *) palloc(sizeof(int) * left_nparts);
+ left_mmap = (int *) palloc(sizeof(int) * left_nparts);
+ right_pmap = (int *) palloc(sizeof(int) * right_nparts);
+ right_mmap = (int *) palloc(sizeof(int) * right_nparts);
+
+ for (cnt1 = 0; cnt1 < left_nparts; cnt1++)
+ {
+ left_pmap[cnt1] = -1;
+ left_mmap[cnt1] = -1;
+ }
+ for (cnt2 = 0; cnt2 < right_nparts; cnt2++)
+ {
+ right_pmap[cnt2] = -1;
+ right_mmap[cnt2] = -1;
+ }
+
+ left_lb_index = 0;
+ right_lb_index = 0;
+ next_index = 0;
+ while (left_lb_index < left_bi->ndatums &&
+ right_lb_index < right_bi->ndatums)
+ {
+ PartitionRangeBound left_lb;
+ PartitionRangeBound left_ub;
+ PartitionRangeBound right_lb;
+ PartitionRangeBound right_ub;
+ PartitionRangeBound *merged_lb = NULL;
+ PartitionRangeBound *merged_ub = NULL;
+ int merged_index = -1;
+ bool overlap;
+
+ /* Get the range bounds of the next partition. */
+ left_part = partition_get_range_bounds(left_bi, left_lb_index,
+ &left_lb, &left_ub);
+ right_part = partition_get_range_bounds(right_bi, right_lb_index,
+ &right_lb, &right_ub);
+
+ cmpval = partition_range_cmp(partnatts, supfuncs, collations,
+ &left_lb, &left_ub, &right_lb, &right_ub,
+ &overlap);
+
+ if (overlap)
+ {
+ /* Overlapping ranges, try merging. */
+ partition_range_merge(partnatts, supfuncs, collations, jointype,
+ &left_lb, &left_ub, &right_lb, &right_ub,
+ &merged_lb, &merged_ub);
+ merged_index = map_and_merge_partitions(left_pmap, left_mmap,
+ left_part, right_pmap,
+ right_mmap, right_part,
+ &next_index);
+
+ if (merged_index < 0)
+ {
+ merged = false;
+ break;
+ }
+ }
+
+ if (cmpval == 0)
+ {
+ Assert(overlap);
+
+ /* Move to the next pair of partitions. */
+ left_lb_index = partition_range_get_next_lb_index(left_bi,
+ left_lb_index);
+ right_lb_index = partition_range_get_next_lb_index(right_bi,
+ right_lb_index);
+ }
+ else if (cmpval < 0)
+ {
+ /*
+ * If the partition on the left was not mapped to any partition on
+ * the right. Any row from that partition will not have a matching
+ * row from the other relation. So the partition will be part of
+ * the join if it's an anti-join or the left side is the outer
+ * side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_SEMI ||
+ jointype == JOIN_RIGHT)
+ {
+ /* Nothing to do. */
+ }
+ else if (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI)
+ {
+ if (left_mmap[left_part] < 0)
+ {
+ left_mmap[left_part] = next_index++;
+ merged_index = left_mmap[left_part];
+ merged_lb = &left_lb;
+ merged_ub = &left_ub;
+ }
+ }
+ else
+ {
+ /* Don't know what to do with other join types. Bail out. */
+ merged = false;
+ }
+
+ /* Move to the next partition on the left side. */
+ left_lb_index = partition_range_get_next_lb_index(left_bi,
+ left_lb_index);
+ }
+ else
+ {
+ Assert(cmpval > 0);
+
+ /*
+ * If the partition on the right was not mapped to any partition on
+ * the left. Any row from that partition will not have a matching
+ * row from the other relation. So the partition will be part of
+ * the join if the right side is the outer side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_SEMI ||
+ jointype == JOIN_LEFT || jointype == JOIN_ANTI)
+ {
+ /* Nothing to do. */
+ }
+ else if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ if (right_mmap[right_part] < 0)
+ {
+ right_mmap[right_part] = next_index++;
+ merged_index = right_mmap[right_part];
+ merged_lb = &right_lb;
+ merged_ub = &right_ub;
+ }
+ }
+ else
+ {
+ /* Don't know what to do with other join types. Bail out. */
+ merged = false;
+ }
+
+ /* Move to the next partition on the right side. */
+ right_lb_index = partition_range_get_next_lb_index(right_bi,
+ right_lb_index);
+ }
+
+ if (!merged)
+ break;
+
+ /* A skipped partition is not added to merged bounds. */
+ if (merged_index < 0)
+ continue;
+
+ /*
+ * We have a valid partition index for the next partition of join. The
+ * partition should have valid range.
+ */
+ Assert(merged_lb && merged_ub);
+
+ /* Try merging merged lower bound with the last upper bound. */
+ merged = partition_range_merge_next_lb(partnatts, supfuncs,
+ collations, merged_lb->datums,
+ merged_lb->kind, &merged_datums,
+ &merged_kinds, &merged_indexes);
+ if (merged)
+ {
+ /* Add upper bound with the merged partition index. */
+ merged_datums = lappend(merged_datums, merged_ub->datums);
+ merged_kinds = lappend(merged_kinds, merged_ub->kind);
+ merged_indexes = lappend_int(merged_indexes, merged_index);
+ }
+ else
+ break;
+ }
+
+ /*
+ * We will run the above loop till we exhaust ranges of at least one side
+ * unless we failed to merge the ranges.
+ */
+ Assert (!merged || (left_lb_index >= left_bi->ndatums ||
+ right_lb_index >= right_bi->ndatums));
+
+ /*
+ * Handle any remaining partition bounds. If remaining partitions fall on
+ * the inner side of the join, none of the rows in those partition are
+ * going to be joined with any row on the outer side and hence those
+ * partitions will not be part of the join result. Hence only consider the
+ * remaining partitions on the outer side of the join.
+ */
+ if (merged &&
+ ((left_lb_index < left_bi->ndatums &&
+ (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI)) ||
+ (right_lb_index < right_bi->ndatums &&
+ (jointype == JOIN_RIGHT || jointype == JOIN_FULL))))
+ {
+ int bound_index = -1;
+ PartitionBoundInfo rem_bi = NULL;
+ int *mmap = NULL;
+ int part_index;
+ PartitionRangeBound rem_lb;
+ PartitionRangeBound rem_ub;
+
+ if (left_lb_index < left_bi->ndatums)
+ {
+ Assert(jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI);
+ bound_index = left_lb_index;
+ rem_bi = left_bi;
+ mmap = left_mmap;
+ }
+ else if (right_lb_index < right_bi->ndatums)
+ {
+ Assert(jointype == JOIN_RIGHT || jointype == JOIN_FULL);
+ bound_index = right_lb_index;
+ rem_bi = right_bi;
+ mmap = right_mmap;
+ }
+ Assert((bound_index >= 0 && bound_index < rem_bi->ndatums) &&
+ rem_bi && mmap);
+
+ /*
+ * If the partition corresponding to this lower bound has been already
+ * mapped to a merged partition, don't need to add it again. This may
+ * happen if the range of the last partition on the inner side overlaps
+ * with this partition's range and has upper bound lesser than upper
+ * bound of this partition's range.
+ */
+ part_index = partition_get_range_bounds(rem_bi, bound_index, &rem_lb,
+ &rem_ub);
+ Assert(part_index >= 0);
+ if (mmap[part_index] >= 0)
+ bound_index = partition_range_get_next_lb_index(rem_bi, bound_index);
+
+ /*
+ * Merge lower bound of the next range with the upper bound of last
+ * range.
+ */
+ if (bound_index < rem_bi->ndatums)
+ merged = partition_range_merge_next_lb(partnatts, supfuncs,
+ collations,
+ rem_bi->datums[bound_index],
+ rem_bi->kind[bound_index],
+ &merged_datums,
+ &merged_kinds,
+ &merged_indexes);
+
+ /*
+ * Rest of the bounds correspond to valid ranges so add them after
+ * remapping their partitions as required.
+ */
+ for (bound_index++; merged && bound_index < rem_bi->ndatums;
+ bound_index++)
+ {
+ Datum *datums = rem_bi->datums[bound_index];
+ int index = rem_bi->indexes[bound_index];
+ int part_index;
+
+ /*
+ * Add lower bounds with partition index -1 and assign a new
+ * partition index to the upper bounds.
+ */
+ if (index < 0)
+ part_index = index;
+ else
+ {
+ if (mmap[index] < 0)
+ mmap[index] = next_index++;
+ part_index = mmap[index];
+ }
+
+ merged_indexes = lappend_int(merged_indexes, part_index);
+ merged_datums = lappend(merged_datums, datums);
+ merged_kinds = lappend(merged_kinds,
+ rem_bi->kind[bound_index]);
+ }
+ }
+
+ /* Create PartitionBoundInfo */
+ if (merged)
+ {
+ /* Use maps to match partition from the joining relations. */
+ generate_matching_part_pairs(left_mmap, left_nparts, right_mmap,
+ right_nparts, jointype, next_index,
+ left_parts, right_parts);
+
+ /* Craft a PartitionBoundInfo to return. */
+ if (*left_parts && *right_parts)
+ {
+ Assert(list_length(*left_parts) == list_length(*right_parts));
+ Assert(list_length(*left_parts) == next_index);
+ merged_bounds = build_merged_partition_bounds(left_bi->strategy,
+ merged_datums,
+ merged_indexes,
+ merged_kinds,
+ -1);
+ }
+ }
+
+ list_free(merged_datums);
+ list_free(merged_indexes);
+ pfree(left_pmap);
+ pfree(right_pmap);
+ pfree(left_mmap);
+ pfree(right_mmap);
+
+ /* Free any memory we used in this function. */
+ return merged_bounds;
+}
+
+/*
+ * partition_bounds_merge()'s arm for list partitioned tables.
+ *
+ * The function builds the maps of matching partitions from either relation. It
+ * builds the list of partition key values that may appear in the join result
+ * alongwith the list of indexes of partitions of join to which those values
+ * belong. It then crafts a PartitionBoundInfo structure representing the
+ * partition bounds of the join result.
+ */
+static PartitionBoundInfo
+partition_list_bounds_merge(int partnatts, FmgrInfo *partsupfunc,
+ Oid *partcollation, PartitionBoundInfo left_bi,
+ int left_nparts, PartitionBoundInfo right_bi,
+ int right_nparts, JoinType jointype, List **left_parts,
+ List **right_parts)
+{
+ int *left_pmap; /* left to right partition map */
+ int *left_mmap; /* left to merged partition map */
+ int *right_pmap; /* right to left partition map */
+ int *right_mmap; /* right to merged partition map */
+ int cntl;
+ int cntr;
+ bool merged = true;
+ List *merged_datums = NIL;
+ List *merged_indexes = NIL;
+ int next_index = 0;
+ int null_index;
+ PartitionBoundInfo merged_bounds = NULL;
+ int *left_indexes = left_bi->indexes;
+ int *right_indexes = right_bi->indexes;
+ int left_ni = left_bi->null_index;
+ int right_ni = right_bi->null_index;
+
+ Assert(left_bi->strategy == right_bi->strategy &&
+ left_bi->strategy == PARTITION_STRATEGY_LIST);
+
+ /* List partitions do not require unbounded ranges. */
+ Assert(!left_bi->kind && !right_bi->kind);
+
+ /* Allocate and initialize partition maps. */
+ left_pmap = (int *) palloc(sizeof(int) * left_nparts);
+ left_mmap = (int *) palloc(sizeof(int) * left_nparts);
+ right_pmap = (int *) palloc(sizeof(int) * right_nparts);
+ right_mmap = (int *) palloc(sizeof(int) * right_nparts);
+
+ /* Initialize partition maps. */
+ for (cntl = 0; cntl < left_nparts; cntl++)
+ {
+ left_pmap[cntl] = -1;
+ left_mmap[cntl] = -1;
+ }
+ for (cntr = 0; cntr < right_nparts; cntr++)
+ {
+ right_pmap[cntr] = -1;
+ right_mmap[cntr] = -1;
+ }
+
+ cntl = cntr = 0;
+ while (cntl < left_bi->ndatums && cntr < right_bi->ndatums)
+ {
+ Datum *ldatums = left_bi->datums[cntl];
+ Datum *rdatums = right_bi->datums[cntr];
+ int l_index = left_indexes[cntl];
+ int r_index = right_indexes[cntr];
+ int cmpval;
+ int merged_index;
+ Datum *merged_datum;
+
+ /* Every list datum should map to a valid partition index. */
+ Assert(l_index >= 0 && r_index >= 0);
+
+ cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
+ partcollation[0], ldatums[0],
+ rdatums[0]));
+ if (cmpval == 0)
+ {
+ /*
+ * Try matching partitions containing the matching datums. If
+ * successful, add the datum to the merged bounds with index of
+ * merged partition containing it.
+ */
+ merged_datum = ldatums;
+ merged_index = map_and_merge_partitions(left_pmap, left_mmap, l_index,
+ right_pmap, right_mmap, r_index,
+ &next_index);
+
+ if (merged_index < 0)
+ {
+ merged = false;
+ break;
+ }
+
+ /* Move to the next pair of bounds. */
+ cntl++;
+ cntr++;
+ }
+ else if (cmpval < 0)
+ {
+ /*
+ * This list datum is present in the left side but not the right
+ * side. So it will appear in the join when the left side is outer
+ * side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_RIGHT ||
+ jointype == JOIN_SEMI)
+ merged_index = -1;
+ else if (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI)
+ {
+ if (left_mmap[l_index] < 0)
+ left_mmap[l_index] = next_index++;
+ merged_index = left_mmap[l_index];
+ merged_datum = ldatums;
+ }
+ else
+ {
+ /* Don't know what to do with other join types. */
+ merged = false;
+ break;
+ }
+
+ /* Move to the next datum on the left side. */
+ cntl++;
+ }
+ else
+ {
+ Assert(cmpval > 0);
+
+ /*
+ * This list datum is present in the right side but not the left
+ * side. So it will appear in the join when the right side is outer
+ * side.
+ */
+ if (jointype == JOIN_INNER || jointype == JOIN_LEFT ||
+ jointype == JOIN_SEMI || jointype == JOIN_ANTI)
+ merged_index = -1;
+ else if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ /*
+ * Every list value on the outer side will appear in the
+ * join. Find the merged partition to which this value
+ * belongs.
+ */
+ if (right_mmap[r_index] < 0)
+ right_mmap[r_index] = next_index++;
+ merged_index = right_mmap[r_index];
+ merged_datum = rdatums;
+ }
+ else
+ {
+ /* Don't know what to do with other join types. */
+ merged = false;
+ break;
+ }
+
+ /* Move to the next datum on the right side. */
+ cntr++;
+ }
+
+ /*
+ * Add the datum with appropriate index in the list of datums, if the
+ * rows containing that datum are deemed to be part of the join.
+ */
+ if (merged_index >= 0)
+ {
+ merged_indexes = lappend_int(merged_indexes, merged_index);
+ merged_datums = lappend(merged_datums, merged_datum);
+ }
+ }
+
+ /*
+ * If merge is unsuccessful, bail out without any further processing.
+ * That leaks the memory allocated in this function. So, try not to leak
+ * memory.
+ */
+ if (!merged)
+ goto merge_failed;
+
+ /* We should have exhausted datums on at least one side. */
+ Assert(cntr >= right_bi->ndatums || cntl >= left_bi->ndatums);
+
+ /*
+ * Add any remaining list values on the outer side, assigning partition
+ * indexes if required.
+ */
+ if (jointype == JOIN_LEFT || jointype == JOIN_FULL || jointype == JOIN_ANTI)
+ {
+ for (;cntl < left_bi->ndatums; cntl++)
+ {
+ Datum *ldatums = left_bi->datums[cntl];
+ int l_index = left_indexes[cntl];
+
+ if (left_mmap[l_index] < 0)
+ left_mmap[l_index] = next_index++;
+ merged_indexes = lappend_int(merged_indexes, left_mmap[l_index]);
+ merged_datums = lappend(merged_datums, ldatums);
+ }
+ }
+
+ if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ for (;cntr < right_bi->ndatums; cntr++)
+ {
+ Datum *rdatums = right_bi->datums[cntr];
+ int r_index = right_indexes[cntr];
+
+ if (right_mmap[r_index] < 0)
+ right_mmap[r_index] = next_index++;
+ merged_indexes = lappend_int(merged_indexes, right_mmap[r_index]);
+ merged_datums = lappend(merged_datums, rdatums);
+ }
+ }
+
+ /*
+ * Merge NULL partitions if any. Find the index of merged partition to
+ * which the NULL values belong in the join result. We can eliminate a NULL
+ * partition when it appears only in the inner relation.
+ */
+ if (!partition_bound_accepts_nulls(left_bi) &&
+ !partition_bound_accepts_nulls(right_bi))
+ null_index = -1;
+ else if (partition_bound_accepts_nulls(left_bi) &&
+ !partition_bound_accepts_nulls(right_bi))
+ {
+ if (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+ jointype == JOIN_ANTI)
+ {
+ if (left_mmap[left_ni] < 0)
+ left_mmap[left_ni] = next_index++;
+ null_index = left_mmap[left_ni];
+ }
+ else
+ null_index = -1;
+ }
+ else if (!partition_bound_accepts_nulls(left_bi) &&
+ partition_bound_accepts_nulls(right_bi))
+ {
+ if (jointype == JOIN_RIGHT || jointype == JOIN_FULL)
+ {
+ if (right_mmap[right_ni] < 0)
+ right_mmap[right_ni] = next_index++;
+ null_index = right_mmap[right_ni];
+ }
+ else
+ null_index = -1;
+ }
+ else
+ {
+ /* Both the relations have NULL partitions, try merging them. */
+ null_index = map_and_merge_partitions(left_pmap, left_mmap,
+ left_ni, right_pmap,
+ right_mmap, right_ni,
+ &next_index);
+ if (null_index < 0)
+ merged = false;
+ }
+
+ /* If successful build the output structures. */
+ if (merged)
+ {
+
+ /* Use maps to match partition from the joining relations. */
+ generate_matching_part_pairs(left_mmap, left_nparts, right_mmap,
+ right_nparts, jointype, next_index,
+ left_parts, right_parts);
+ /* Craft a PartitionBoundInfo to return. */
+ if (*left_parts && *right_parts)
+ {
+ Assert(list_length(*left_parts) == list_length(*right_parts));
+ Assert(list_length(*left_parts) == next_index);
+ merged_bounds = build_merged_partition_bounds(left_bi->strategy,
+ merged_datums,
+ merged_indexes, NIL,
+ null_index);
+ }
+ }
+
+merge_failed:
+ list_free(merged_datums);
+ list_free(merged_indexes);
+ pfree(left_pmap);
+ pfree(right_pmap);
+ pfree(left_mmap);
+ pfree(right_mmap);
+
+ return merged_bounds;
+}
+
+/*
+ * map_and_merge_partitions
+ *
+ * If the two given partitions (given by index1 and index2 resp.) are already
+ * mapped to each other return the index of corresponding partition in the
+ * merged set of partitions. If they do not have a merged partition associated
+ * with them, assign a new merged partition index. If the partitions are
+ * already mapped and their mapped partitions are different from each other,
+ * they can not be merged, so return -1.
+ *
+ * partmap1[i] gives the partition of relation 2 which matches ith partition of
+ * relation 1. Similarly for partmap2.
+ *
+ * mergemap1[i] gives the partition in the merged set to which ith partition of
+ * relation 1 maps to. Similarly for mergemap2.
+ *
+ * index1 and index2 are the indexes of matching partition from respective
+ * relations.
+ *
+ * *next_index is used and incremented when the given partitions require a new
+ * merged partition.
+ */
+static int
+map_and_merge_partitions(int *partmap1, int *mergemap1, int index1,
+ int *partmap2, int *mergemap2, int index2,
+ int *next_index)
+{
+ int merged_index;
+
+ /*
+ * If both the partitions are not mapped to each other, update the
+ * maps.
+ */
+ if (partmap1[index1] < 0 && partmap2[index2] < 0)
+ {
+ partmap1[index1] = index2;
+ partmap2[index2] = index1;
+ }
+
+ /*
+ * If the given to partitions map to each other, find the corresponding
+ * merged partition index .
+ */
+ if (partmap1[index1] == index2 && partmap2[index2] == index1)
+ {
+ /*
+ * If both the partitions are mapped to the same merged partition, get
+ * the index of merged partition.
+ */
+ if (mergemap1[index1] == mergemap2[index2])
+ {
+ merged_index = mergemap1[index1];
+
+ /*
+ * If the given two partitions do not have a merged partition
+ * associated with them, allocate a new merged partition.
+ */
+ if (merged_index < 0)
+ {
+ merged_index = *next_index;
+ *next_index = *next_index + 1;
+ mergemap1[index1] = merged_index;
+ mergemap2[index2] = merged_index;
+ }
+ }
+
+ /*
+ * If partition from one relation was mapped to a merged partition but
+ * not the partition from the other relation, map the same merged
+ * partition to the partition from other relation, since matching
+ * partitions map to the same merged partition.
+ */
+ else if (mergemap1[index1] >= 0 && mergemap2[index2] < 0)
+ {
+ mergemap2[index2] = mergemap1[index1];
+ merged_index = mergemap1[index1];
+ }
+ else if (mergemap1[index1] < 0 && mergemap2[index2] >= 0)
+ {
+ mergemap1[index1] = mergemap2[index2];
+ merged_index = mergemap2[index2];
+ }
+ else
+ {
+ Assert(mergemap1[index1] != mergemap2[index2] &&
+ mergemap1[index1] >= 0 && mergemap2[index2] >= 0);
+
+ /*
+ * Both the partitions map to different merged partitions. This
+ * means that multiple partitions from one relation matches to one
+ * partition from the other relation. Partition-wise join does not
+ * handle this case right now, since it requires ganging multiple
+ * partitions together (into one RelOptInfo).
+ */
+ merged_index = -1;
+ }
+ }
+ else
+ {
+ /*
+ * Multiple partitions from one relation map to one partition from the
+ * other relation. Partition-wise join does not handle this case right
+ * now, since it requires ganging multiple partitions together (into
+ * one RelOptInfo).
+ */
+ merged_index = -1;
+ }
+
+ return merged_index;
+}
+
+/*
+ * generate_matching_part_pairs
+ *
+ * Given the merged partition to which partition on either side of join map,
+ * produce the list pairs of partitions which when joined produce the merged
+ * partitions in the order of merged partition indexes.
+ *
+ * If successful, the pairs of partitions are returned as two separate lists
+ * one for each side. Otherwise, those lists will be set to NIL.
+ *
+ * TODO: rename the sides as outer and inner. You may not need to support
+ * JOIN_RIGHT, since we won't see that type here.
+ */
+static void
+generate_matching_part_pairs(int *mergemap1, int nparts1, int *mergemap2,
+ int nparts2, JoinType jointype, int nparts,
+ List **parts1, List **parts2)
+{
+ bool merged = true;
+ int **matching_parts;
+ int cnt1;
+ int cnt2;
+
+ matching_parts = (int **) palloc(sizeof(int *) * 2);
+ matching_parts[0] = (int *) palloc(sizeof(int) * nparts);
+ matching_parts[1] = (int *) palloc(sizeof(int) * nparts);
+
+ *parts1 = NIL;
+ *parts2 = NIL;
+
+ for (cnt1 = 0; cnt1 < nparts; cnt1++)
+ {
+ matching_parts[0][cnt1] = -1;
+ matching_parts[1][cnt1] = -1;
+ }
+
+ /* Set pairs of matching partitions. */
+ for (cnt1 = 0; cnt1 < nparts1; cnt1++)
+ {
+ if (mergemap1[cnt1] >= 0)
+ {
+ Assert(mergemap1[cnt1] < nparts);
+ matching_parts[0][mergemap1[cnt1]] = cnt1;
+ }
+ }
+ for (cnt2 = 0; cnt2 < nparts2; cnt2++)
+ {
+ if (mergemap2[cnt2] >= 0)
+ {
+ Assert(mergemap2[cnt2] < nparts);
+ matching_parts[1][mergemap2[cnt2]] = cnt2;
+ }
+ }
+
+ /*
+ * 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 (cnt1 = 0; cnt1 < nparts; cnt1++)
+ {
+ int part1 = matching_parts[0][cnt1];
+ int part2 = matching_parts[1][cnt1];
+
+ /* 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_RIGHT:
+ Assert(part2 >= 0);
+ if (part1 < 0)
+ merged = false;
+ break;
+
+ case JOIN_FULL:
+ if (part1 < 0 || part2 < 0)
+ merged = false;
+ break;
+
+ default:
+ /* We do not know what to do in this case. Bail out. */
+ merged = false;
+ }
+
+ if (!merged)
+ break;
+
+ *parts1 = lappend_int(*parts1, part1);
+ *parts2 = lappend_int(*parts2, part2);
+ }
+
+ pfree(matching_parts[0]);
+ pfree(matching_parts[1]);
+ pfree(matching_parts);
+
+ if (!merged)
+ {
+ list_free(*parts1);
+ list_free(*parts2);
+ *parts1 = NIL;
+ *parts2 = NIL;
+ }
+}
+
+static PartitionBoundInfo
+build_merged_partition_bounds(char strategy, List *merged_datums,
+ List *merged_indexes, List *merged_kinds,
+ int null_index)
+{
+ int cnt;
+ PartitionBoundInfo merged_bounds;
+ ListCell *lc;
+
+ /* We expect the same number of elements in datums and indexes lists. */
+ Assert(list_length(merged_datums) == list_length(merged_indexes));
+
+ merged_bounds = (PartitionBoundInfo) palloc(sizeof(PartitionBoundInfoData));
+ merged_bounds->strategy = strategy;
+ merged_bounds->ndatums = list_length(merged_datums);
+
+ if (strategy == PARTITION_STRATEGY_RANGE)
+ {
+ Assert(list_length(merged_datums) == list_length(merged_kinds));
+ merged_bounds->kind = (PartitionRangeDatumKind **) palloc(sizeof(PartitionRangeDatumKind *) *
+ list_length(merged_kinds));
+ cnt = 0;
+ foreach(lc, merged_kinds)
+ merged_bounds->kind[cnt++] = lfirst(lc);
+
+ /* There are ndatums+1 indexes in case of range partitions */
+ merged_indexes = lappend_int(merged_indexes, -1);
+ }
+ else
+ merged_bounds->kind = NULL;
+
+ cnt = 0;
+ merged_bounds->datums = (Datum **) palloc(sizeof(Datum *) *
+ list_length(merged_datums));
+ foreach(lc, merged_datums)
+ merged_bounds->datums[cnt++] = lfirst(lc);
+
+ merged_bounds->indexes = (int *) palloc(sizeof(int) *
+ list_length(merged_indexes));
+ cnt = 0;
+ foreach(lc, merged_indexes)
+ merged_bounds->indexes[cnt++] = lfirst_int(lc);
+
+ merged_bounds->null_index = null_index;
+
+ /* TODO: For the time being we do not work with default partitions. */
+ merged_bounds->default_index = -1;
+
+ return merged_bounds;
+}
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 1578dea..91b976b 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1309,8 +1309,13 @@ try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
RelOptInfo *joinrel, SpecialJoinInfo *parent_sjinfo,
List *parent_restrictlist)
{
- int nparts;
int cnt_parts;
+ PartitionScheme part_scheme;
+ PartitionBoundInfo join_boundinfo;
+ List *parts1;
+ List *parts2;
+ ListCell *lc1;
+ ListCell *lc2;
/* Guard against stack overflow due to overly deep partition hierarchy. */
check_stack_depth();
@@ -1349,32 +1354,49 @@ try_partition_wise_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;
/*
- * Since we allow partition-wise join only when the partition bounds of
- * the joining relations exactly match, the partition bounds of the join
- * should match those of the joining relations.
+ * Get the list of matching partitions from both sides of the join. While
+ * doing so, we also build the partition bounds of the join relation,
+ * which should match the bounds calculated for other pairs. TODO: why
+ * should every pair result in the same partition bounds?
*/
- Assert(partition_bounds_equal(joinrel->part_scheme->partnatts,
- joinrel->part_scheme->parttyplen,
- joinrel->part_scheme->parttypbyval,
- joinrel->boundinfo, rel1->boundinfo));
- Assert(partition_bounds_equal(joinrel->part_scheme->partnatts,
- joinrel->part_scheme->parttyplen,
- joinrel->part_scheme->parttypbyval,
- joinrel->boundinfo, rel2->boundinfo));
+ join_boundinfo = partition_bounds_merge(part_scheme->partnatts,
+ part_scheme->partsupfunc,
+ part_scheme->parttypcoll,
+ rel1->boundinfo, rel1->nparts,
+ rel2->boundinfo, rel2->nparts,
+ parent_sjinfo->jointype,
+ &parts1, &parts2);
+
+ Assert(join_boundinfo);
+ Assert(partition_bounds_equal(part_scheme->partnatts,
+ part_scheme->parttyplen,
+ part_scheme->parttypbyval, join_boundinfo,
+ joinrel->boundinfo));
+ elog(DEBUG3, "join between relations %s and %s is considered for partition-wise join.",
+ bmsToString(rel1->relids), bmsToString(rel2->relids));
- nparts = joinrel->nparts;
+ /*
+ * 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));
/*
* Create child-join relations for this partitioned join, if those don't
* exist. Add paths to child-joins for a pair of child relations
* corresponding to the given pair of parent relations.
*/
- for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+ cnt_parts = 0;
+ forboth(lc1, parts1, lc2, parts2)
{
- RelOptInfo *child_rel1 = rel1->part_rels[cnt_parts];
- RelOptInfo *child_rel2 = rel2->part_rels[cnt_parts];
+ int part1 = lfirst_int(lc1);
+ int part2 = lfirst_int(lc2);
+ RelOptInfo *child_rel1;
+ RelOptInfo *child_rel2;
SpecialJoinInfo *child_sjinfo;
List *child_restrictlist;
RelOptInfo *child_joinrel;
@@ -1382,6 +1404,10 @@ try_partition_wise_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];
+
/* We should never try to join two overlapping sets of rels. */
Assert(!bms_overlap(child_rel1->relids, child_rel2->relids));
child_joinrelids = bms_union(child_rel1->relids, child_rel2->relids);
@@ -1415,12 +1441,24 @@ try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
joinrel->part_rels[cnt_parts] = child_joinrel;
}
+ /*
+ * 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);
+ cnt_parts++;
}
+
+ Assert(cnt_parts == joinrel->nparts);
}
/*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 9d35a41..9d3df33 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1918,6 +1918,11 @@ find_partition_scheme(PlannerInfo *root, Relation relation)
memcpy(part_scheme->parttypbyval, partkey->parttypbyval,
sizeof(bool) * partnatts);
+ part_scheme->partsupfunc =
+ (FmgrInfo *) palloc(sizeof(FmgrInfo) * partnatts);
+ memcpy(part_scheme->partsupfunc, partkey->partsupfunc,
+ sizeof(FmgrInfo) * partnatts);
+
/* Add the partitioning scheme to PlannerInfo. */
root->part_schemes = lappend(root->part_schemes, part_scheme);
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 21fd29f..827ef76 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -1601,6 +1601,9 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
int partnatts;
int cnt;
PartitionScheme part_scheme;
+ PartitionBoundInfo join_boundinfo;
+ List *parts1;
+ List *parts2;
/* Nothing to do if partition-wise join technique is disabled. */
if (!enable_partition_wise_join)
@@ -1634,17 +1637,26 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
REL_HAS_ALL_PART_PROPS(inner_rel));
/*
- * For now, our partition matching algorithm can match partitions only
- * when the partition bounds of the joining relations are exactly same.
- * So, bail out otherwise.
+ * Every pair of joining relations would yield the same partition bounds
+ * for a given join (TODO: why?) so we compute the bounds only the first
+ * time. Then for every pair we find the pairs of matching partitions from
+ * the joining relations and join those. TODO: Needs a better explanation
+ * of why is this true. TODO: Also there is no reason to have
+ * part_indexes1 and part_indexes2 pulled here just to be freed up later.
+ * So, we might want to do something better.
*/
- if (outer_rel->nparts != inner_rel->nparts ||
- !partition_bounds_equal(part_scheme->partnatts,
- part_scheme->parttyplen,
- part_scheme->parttypbyval,
- outer_rel->boundinfo, inner_rel->boundinfo))
+ join_boundinfo = partition_bounds_merge(part_scheme->partnatts,
+ part_scheme->partsupfunc,
+ part_scheme->parttypcoll,
+ outer_rel->boundinfo,
+ outer_rel->nparts,
+ inner_rel->boundinfo,
+ inner_rel->nparts,
+ jointype, &parts1, &parts2);
+ if (!join_boundinfo)
{
Assert(!IS_PARTITIONED_REL(joinrel));
+ Assert(!parts1 && !parts2);
return;
}
@@ -1657,13 +1669,16 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
!joinrel->nullable_partexprs && !joinrel->part_rels &&
!joinrel->boundinfo);
+ Assert(list_length(parts1) == list_length(parts2));
+
/*
* Join relation is partitioned using the same partitioning scheme as the
- * joining relations and has same bounds.
+ * joining relations and has same bounds. It will have as many partitions
+ * as the pairs of matching partitions we found.
*/
joinrel->part_scheme = part_scheme;
- joinrel->boundinfo = outer_rel->boundinfo;
- joinrel->nparts = outer_rel->nparts;
+ joinrel->nparts = list_length(parts1);
+ joinrel->boundinfo = join_boundinfo;
partnatts = joinrel->part_scheme->partnatts;
joinrel->partexprs = (List **) palloc0(sizeof(List *) * partnatts);
joinrel->nullable_partexprs =
@@ -1751,4 +1766,8 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
joinrel->part_rels =
(RelOptInfo **) palloc0(sizeof(RelOptInfo *) * joinrel->nparts);
+ /* TODO: OR we could actually create the child-join relations here.*/
+ list_free(parts1);
+ list_free(parts2);
+
}
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index 945ac02..58d62f1 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -107,5 +107,10 @@ extern void update_default_partition_oid(Oid parentId, Oid defaultPartId);
extern void check_default_allows_bound(Relation parent, Relation defaultRel,
PartitionBoundSpec *new_spec);
extern List *get_proposed_default_constraint(List *new_part_constaints);
+extern PartitionBoundInfo partition_bounds_merge(int partnatts,
+ FmgrInfo *supfuncs, Oid *collations,
+ PartitionBoundInfo boundinfo1, int nparts1,
+ PartitionBoundInfo boundinfo2, int nparts2,
+ JoinType jointype, List **parts1, List **parts2);
#endif /* PARTITION_H */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index e085cef..5d6e5f8 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -15,6 +15,7 @@
#define RELATION_H
#include "access/sdir.h"
+#include "fmgr.h"
#include "lib/stringinfo.h"
#include "nodes/params.h"
#include "nodes/parsenodes.h"
@@ -354,6 +355,7 @@ typedef struct PartitionSchemeData
/* Cached information about partition key data types. */
int16 *parttyplen;
bool *parttypbyval;
+ FmgrInfo *partsupfunc;
} PartitionSchemeData;
typedef struct PartitionSchemeData *PartitionScheme;
--
1.7.9.5
On Wed, Oct 11, 2017 at 7:08 AM, Ashutosh Bapat
<ashutosh.bapat@enterprisedb.com> wrote:
Here's updated patch set based on the basic partition-wise join
committed. The patchset applies on top of the patch to optimize the
case of dummy partitioned tables [1].Right now, the advanced partition matching algorithm bails out when
either of the joining relations has a default partition.
So is that something you are going to fix?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Oct 12, 2017 at 9:46 PM, Robert Haas <robertmhaas@gmail.com> wrote:
On Wed, Oct 11, 2017 at 7:08 AM, Ashutosh Bapat
<ashutosh.bapat@enterprisedb.com> wrote:Here's updated patch set based on the basic partition-wise join
committed. The patchset applies on top of the patch to optimize the
case of dummy partitioned tables [1].Right now, the advanced partition matching algorithm bails out when
either of the joining relations has a default partition.So is that something you are going to fix?
Yes, if time permits. I had left the patch unattended while basic
partition-wise join was getting committed. Now that it's committed, I
rebased it. It still has TODOs and some work is required to improve
it. But for the patch to be really complete, we have to deal with the
problem of missing partitions described before. I am fine
collaborating if someone else wants to pick it up.
--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Fri, Oct 13, 2017 at 7:59 AM, Ashutosh Bapat
<ashutosh.bapat@enterprisedb.com> wrote:
On Thu, Oct 12, 2017 at 9:46 PM, Robert Haas <robertmhaas@gmail.com> wrote:
On Wed, Oct 11, 2017 at 7:08 AM, Ashutosh Bapat
<ashutosh.bapat@enterprisedb.com> wrote:Here's updated patch set based on the basic partition-wise join
committed. The patchset applies on top of the patch to optimize the
case of dummy partitioned tables [1].Right now, the advanced partition matching algorithm bails out when
either of the joining relations has a default partition.So is that something you are going to fix?
Yes, if time permits. I had left the patch unattended while basic
partition-wise join was getting committed. Now that it's committed, I
rebased it. It still has TODOs and some work is required to improve
it. But for the patch to be really complete, we have to deal with the
problem of missing partitions described before. I am fine
collaborating if someone else wants to pick it up.
Here's patchset which support advanced partition matching for
partition bounds with default partition. The patchset is rebased on
the latest head.
When a list value is present in one of the joining relations and not
the other, and the other relation has default partition, match (join)
the partition containing that list value with the default partition,
since the default partition may contain rows with that list value. If
the default partition happens to be on the outer side of the join, the
resulting join partition acts as a default partition as it will
contain all the values from the default partition. If the partition
containing the list value happens to be on the outer side of the join,
the resulting join partition is associated with the list value, since
no other partition key value from the default partition makes it to
the join result.
When a range is present (completely or partly) in one of the joining
relations and not the other, and the other relation has default
partition, match (join) the partition corresponding to that range with
the default partition. If the default partition happens to be on the
outer side of the join, the resulting join partition acts as a default
partition as it will contain all the values from the default
partition. If the non-partition corresponding to the range happens to
be on the outer side of the join, the resulting join partition is
associated with that range, since partition key values from the
default partition outside that range won't make it to the join result.
If both the relations have default partition, match (join) the default
partition with each other and deem the resulting join partition as
default partition. If one of the relations has default partition but
not the other, and the default partition happens to be on the outer
side of the join, all its rows will make it to the join. Such a
default partition may get joined to a non-default partition from the
inner side, if inner side has a range missing in the outer side.
If any of the above causes multiple partitions from one side to match
with one or more partitions on the other side, we won't use
partition-wise join as discussed in the first mail of this thread.
I have tested the patches for two-way join, but haven't added any test
involving default partitions to the patch itself. It needs to be
tested for N-way join as well. So, for now I have kept the two patches
supporting the default partition in case of range and list resp.
separate. Also, some of the code duplication in partition matching
functions can be avoided using macros. I will merge those patches into
the main patch and add macros once they are tested appropriately.
For hash partitioned table, we haven't implemented the advanced
partition matching, since it would be rare that somebody has hash
partitioned tables with holes (even if they are allowed).
--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
Attachments:
Here's a new patchset with following changes
1. Rebased on the latest head taking care of partition bound
comparison function changes
2. Refactored the code to avoid duplication.
3. There's an extensive test (provided by Rajkumar) set added, which
is not meant to be committed. That testset has testcases which crash
or reveal a bug. I will fix those crashes and add corresponding
testcases to partition_join.sql.
TODO
1. FIx crashes/bugs in the testcases.
On Sun, Dec 3, 2017 at 4:53 PM, Ashutosh Bapat
<ashutosh.bapat@enterprisedb.com> wrote:
On Fri, Oct 13, 2017 at 7:59 AM, Ashutosh Bapat
<ashutosh.bapat@enterprisedb.com> wrote:On Thu, Oct 12, 2017 at 9:46 PM, Robert Haas <robertmhaas@gmail.com> wrote:
On Wed, Oct 11, 2017 at 7:08 AM, Ashutosh Bapat
<ashutosh.bapat@enterprisedb.com> wrote:Here's updated patch set based on the basic partition-wise join
committed. The patchset applies on top of the patch to optimize the
case of dummy partitioned tables [1].Right now, the advanced partition matching algorithm bails out when
either of the joining relations has a default partition.So is that something you are going to fix?
Yes, if time permits. I had left the patch unattended while basic
partition-wise join was getting committed. Now that it's committed, I
rebased it. It still has TODOs and some work is required to improve
it. But for the patch to be really complete, we have to deal with the
problem of missing partitions described before. I am fine
collaborating if someone else wants to pick it up.Here's patchset which support advanced partition matching for
partition bounds with default partition. The patchset is rebased on
the latest head.When a list value is present in one of the joining relations and not
the other, and the other relation has default partition, match (join)
the partition containing that list value with the default partition,
since the default partition may contain rows with that list value. If
the default partition happens to be on the outer side of the join, the
resulting join partition acts as a default partition as it will
contain all the values from the default partition. If the partition
containing the list value happens to be on the outer side of the join,
the resulting join partition is associated with the list value, since
no other partition key value from the default partition makes it to
the join result.When a range is present (completely or partly) in one of the joining
relations and not the other, and the other relation has default
partition, match (join) the partition corresponding to that range with
the default partition. If the default partition happens to be on the
outer side of the join, the resulting join partition acts as a default
partition as it will contain all the values from the default
partition. If the non-partition corresponding to the range happens to
be on the outer side of the join, the resulting join partition is
associated with that range, since partition key values from the
default partition outside that range won't make it to the join result.If both the relations have default partition, match (join) the default
partition with each other and deem the resulting join partition as
default partition. If one of the relations has default partition but
not the other, and the default partition happens to be on the outer
side of the join, all its rows will make it to the join. Such a
default partition may get joined to a non-default partition from the
inner side, if inner side has a range missing in the outer side.If any of the above causes multiple partitions from one side to match
with one or more partitions on the other side, we won't use
partition-wise join as discussed in the first mail of this thread.I have tested the patches for two-way join, but haven't added any test
involving default partitions to the patch itself. It needs to be
tested for N-way join as well. So, for now I have kept the two patches
supporting the default partition in case of range and list resp.
separate. Also, some of the code duplication in partition matching
functions can be avoided using macros. I will merge those patches into
the main patch and add macros once they are tested appropriately.For hash partitioned table, we haven't implemented the advanced
partition matching, since it would be rare that somebody has hash
partitioned tables with holes (even if they are allowed).--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
Attachments:
Hi Ashutosh.
On 2018/02/07 13:51, Ashutosh Bapat wrote:
Here's a new patchset with following changes
1. Rebased on the latest head taking care of partition bound
comparison function changes
I was about to make these changes myself while revising the fast pruning
patch. Instead, I decided to take a look at your patch and try to use it
in my tree.
I looked at the patch 0001 and noticed that git diff --check says:
src/backend/catalog/partition.c:2900: trailing whitespace.
+partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation,
Also, might be a good idea to write briefly about the new arguments in the
header comment. Something like that they are PartitionKey elements.
Thanks,
Amit
On 2018/02/08 11:55, Amit Langote wrote:
Hi Ashutosh.
On 2018/02/07 13:51, Ashutosh Bapat wrote:
Here's a new patchset with following changes
1. Rebased on the latest head taking care of partition bound
comparison function changesI was about to make these changes myself while revising the fast pruning
patch. Instead, I decided to take a look at your patch and try to use it
in my tree.
I also noticed that a later patch adds partsupfunc to PartitionScheme,
which the pruning patch needs too. So, perhaps would be nice to take out
that portion of the patch. That is, the changes to PartitionScheme struct
definition and those to find_partition_scheme().
Regarding the latter, wouldn't be nice to have a comment before the code
that does the copying about why we don't compare the partsupfunc field to
decide if we have a match or not. I understand it's because the
partsupfunc array contains pointers, not OIDs. But maybe, that's too
obvious to warrant a comment.
Thanks,
Amit
On Thu, Feb 8, 2018 at 8:25 AM, Amit Langote
<Langote_Amit_f8@lab.ntt.co.jp> wrote:
Hi Ashutosh.
On 2018/02/07 13:51, Ashutosh Bapat wrote:
Here's a new patchset with following changes
1. Rebased on the latest head taking care of partition bound
comparison function changesI was about to make these changes myself while revising the fast pruning
patch. Instead, I decided to take a look at your patch and try to use it
in my tree.I looked at the patch 0001 and noticed that git diff --check says:
src/backend/catalog/partition.c:2900: trailing whitespace.
+partition_rbound_datum_cmp(FmgrInfo *partsupfunc, Oid *partcollation,
Thanks. Fixed.
Also, might be a good idea to write briefly about the new arguments in the
header comment. Something like that they are PartitionKey elements.
Here's updated patch set with those comments added.
--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
Attachments:
pg_adv_dp_join_patches_v4.tar.gzapplication/x-gzip; name=pg_adv_dp_join_patches_v4.tar.gzDownload
� .1}Z ��ks�8�.����_�/%U�9������W]����u�u����9q�A���Y�������@� )Q�BT�L7�Z��`���]������������s��N�����Lc�NO#���Fc�7O������a����w2���E��B�^��x����������Y����������S_�s����n��z�%9�$����q�:�E��}�p�9^mf��rq�BJ����6�������p;c�;)�?����?�_h��x���a�|A���C��YM���!�����$������������S��r�>%��w����H�C>��#R��I6F�/1^���������'Yl����l�<L��/_�|�S�I��=y��C�'^���3\�x'�����8
�G�^��'���@��_&�?�o��������������������������9�?<�L�E�/���@�*A/���%��?�{�x��f�^����K���hS4�������3���<A��=����������x:Js�Gp�������lC���
<T��f6��� .�^�q�1�$�����dM^���>|��@����}�������'t{��r�~�������?��7���������������y�����������~�#�<�{6 w�X��N�_�nM����(D�$��W��N���*y�}_�;y��<���1�yx��VM��?zG;��[��\�����OP�v���?��'����Y���]'+Rz=x7<B�������!A�2����i�1���U��f����� /����;���e��W����9�-�����%�Yx~B������/^��#<��Z��t<��;oD���������nq��S-��������S�&��I�|��K��x%�:>]��dAn0������z�zM~ �I�����;�����������y���S����'���3� �?g�
�% ������t;��B?�6��;�p��{sI�ywI��\�.�?���i���Wv���B1Z�q���~9}Y��v��t�SjNfM��g�c���<���
3�_g�Y��{���W\��������c��>/��^!�a�~��,?�\Ln/�YF����M��=��0m[
�/��������_~C7���^���:�>����h��o��&��^`rs�3���*��1+��L��:�HU�oR)���U�&���i=?O�A�P^Y(�����_/oq��W�.nR{M}������Fh����h�)�������� ��nH���,���0�%��1~�������������� �5m]�zy��nh�NT�M�&���WA��#�����lt}�����/[����k���AI-uk���AI=Fk���A_��b
f+b���kPh����k0����'��p�������/���Wh�����Ox`> KO�����#��=��?M���}H���7�/���
�"�]�M�~"�S�t}���
Y�Y�8�� Vq���zq�����=%/`�p;���M�������#�Hz���M>NL�����e/��a��"}��r��h�������BFR�����{,�a��_���+YM)��x�4}�������N���
����,��\����������l�?���| w-������7
�7
��4 o���A��6�4��44���i����i�vo:�Z~]�?�J�cDU��w�\��7'��K\����8��8�e.���K��2�D~I��iI���$�qzd%�K�0�Y��dy�G1*s�y��AX"�����_�����C��1Y%X�c�d��qG�n����%� �8��b>������Om�N��Q�:^���e�Tu�`�7�Kn��U�����nfO���L�1�-+6��|O���#�IM���E
w����G7~������nL����p���D��uc������ t<&zl�Bh<&hA1&� �`��*�M��~������*E�G��TS�
�{������e�<�o��&��
�#+�1��BcR(�G�82��]� O#z������[���(��kIo����@� /C_��i~/���qYOz+���2[&�eB���������(��y���h�����8������9�V��Xb��*t�����h���f�z#����� �r�,�����>=>cSA��V�A
��� ��n<�����h�V�����z�hO�1�"���I��I`;&Z��`L�m+c�c����4-B���Im�7��q�qW|?J{������>�������!�XQo@K��-IK��}4I�/�����|.��>����y9������;"dy�`~�2B�3���3�'�C�<?�zyIWg^J��_��0[�X(e\>����Lf7�j77��������!����f�����cj3�2�����r<>E�"�,��`@����N
������H�MjP���tm�k3Sxl�iL=fL��M1��tL=�1Ua�N���������J��bL}���c��v�������1
�1�bG���1
,�T�
;SC�^;c2c*���1
�cZ��
[v2�����v��q��(V�S����w���t���L��4�`���!:�"3�z]���k�H��GVW�<Noo��&FLwzI������I(�Sk��V.9�Z��|�����9g��/z���0����T��U����x�\~jMWmQ]��K#������*���u<�O��;��G����#� �{I���'Z<���6E�^Lg��#���������LD�K�<b%�[�r��gUC���������%)�������xvC�&����h���1�2'5�2�-s�n��f����;B�f��Yc'<��-*�����%[#6|����6E���O��������������8�?��#A���/�?w���V���5��}�����}���������<�e�j���
�6^��$������e�{�V��f����\A%H*�75��~2`2��0nm�F�Zg,7���������w�����9(z���Q�g��\?(,�V�� e����q���9�Yf�|��A2����r��r3y7*�z��t~&�9�a>�L��r�F.3���p�.��c7�������L+#�#�����X�l�3������c�]v�����JL2g���L�x��c�
y���!�a�-�����=v�����c��8��i��m���c��H9�������}�v���6W�
�-��O�,�C�K=�D74���Xp8^fc��l\�'����,6T��v���i�<aXcy[���2�p��/O����iyw:���I���zR��H��d��|0����mH��]oSzmG.��e�v�H�WhQ t�J���WhQnk$�
|�����J�XD)_�[�)�����\M�4Oq��y�����������m�>=y?�s���S��N�s��J���A<7����j+�����fr�3��'�K4�Av��y�7�����s��2%>�APo���O=��z�(�����
�*_�sk�q����@ak���������O�V��KaGK�@[�w���*�m�L�Y61rF�&����5
S��e�j$��7�P �y��sg�i�&�5���k��Z�����5o@qPP����x�M�����_��=����O���y
��7h<���h�&���K("e�N��W���E�A�4�?���4���7������jY�F���D%��Jjn�9<2�!U����!MG��H�]D�W����'�rJ��
W��;�P_�ZRu/���#fWe�/�M���"��D�����"�����
�y����?�]E����}E������y��[K��E�/,�u��Mm�6���l�iT������"]��e��DW�O]_5> �MBZ��
�|+i�|�� <{kl =uc�^aH��<{m={{I�3o�i�}�e�E^��W��$J�EC���+����v�T��)bQB�G����'����6�Q�M��h����jp~##��c ���x�����,HB+Q:��� �Ei���WV)U`6��c&�J:]�����aYH��������8(��T��Z�P�^�^un���5l�}�4���Z���������k�j���x�P�5�a����7G��&>'��Ai�M�6&�$�n�k�Dnki��c���������������1�&A�k^�&<]KoX�M��ad�7HZO���&��U�=d|�&�Mh>�H}B�>����>������/��b�z,LYNx��� RY���He;���7�`e�^�����7�T
��e{��W���((7Y�������k�d,>X=Y`��
lU��qp����^��D*�U���� S`�2{
����A��;9�<�(�U�0�*��VU���`�*ms�.^��J�Y��� cu-c�E{��h�����������L�����"��4��i�]���
jz�~�
�;����{7Z�
��1�l7�s6�'�9��}�O��v���k�v���9��j%�u6���9e�}�O��v��k�v�}������d��hF�>��zwg� ����XCj���������YvB��{�k��Xc�A�p-����v��5��0l�o��3Z�����~�c��n�����d���N<��V����D+��s��`�`��4����T���aY�����5�vf�qp p�3{�7m��.��O���&�-����m&J���]mw�]�
�O{?\�L>�Xb������"