pg_dp_join_v3.patch
invalid/octet-stream
Filename: pg_dp_join_v3.patch
Type: invalid/octet-stream
Part: 0
Patch
Format: unified
Series: patch v3
| File | + | − |
|---|---|---|
| src/backend/catalog/partition.c | 286 | 0 |
| src/backend/optimizer/path/allpaths.c | 181 | 25 |
| src/backend/optimizer/path/costsize.c | 1 | 0 |
| src/backend/optimizer/path/equivclass.c | 2 | 0 |
| src/backend/optimizer/path/joinpath.c | 39 | 5 |
| src/backend/optimizer/path/joinrels.c | 346 | 5 |
| src/backend/optimizer/path/pathkeys.c | 13 | 1 |
| src/backend/optimizer/plan/createplan.c | 23 | 15 |
| src/backend/optimizer/prep/prepunion.c | 51 | 0 |
| src/backend/optimizer/util/pathnode.c | 152 | 0 |
| src/backend/optimizer/util/plancat.c | 14 | 0 |
| src/backend/optimizer/util/relnode.c | 292 | 59 |
| src/backend/utils/adt/selfuncs.c | 3 | 1 |
| src/backend/utils/misc/guc.c | 9 | 0 |
| src/include/catalog/partition.h | 11 | 0 |
| src/include/nodes/relation.h | 32 | 0 |
| src/include/optimizer/cost.h | 1 | 0 |
| src/include/optimizer/pathnode.h | 7 | 0 |
| src/include/optimizer/paths.h | 2 | 0 |
| src/include/optimizer/prep.h | 3 | 0 |
| src/test/regress/expected/partition_join.out | 7031 | 0 |
| src/test/regress/expected/rangefuncs.out | 15 | 14 |
| src/test/regress/parallel_schedule | 1 | 1 |
| src/test/regress/serial_schedule | 1 | 0 |
| src/test/regress/sql/partition_join.sql | 788 | 0 |
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 1a60563..7b8406b 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -13,38 +13,41 @@
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
+#include "catalog/heap.h"
#include "catalog/indexing.h"
#include "catalog/objectaddress.h"
#include "catalog/partition.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_partitioned_table.h"
#include "catalog/pg_partitioned_table_fn.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/parsenodes.h"
#include "optimizer/clauses.h"
+#include "optimizer/cost.h"
#include "optimizer/planmain.h"
#include "optimizer/var.h"
+#include "rewrite/rewriteManip.h"
#include "storage/lmgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/memutils.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/ruleutils.h"
#include "utils/rel.h"
@@ -208,20 +211,49 @@ typedef struct PartitionTreeNodeData
PartitionDesc pdesc;
Oid relid;
int index;
int offset;
int num_leaf_parts;
struct PartitionTreeNodeData *downlink;
struct PartitionTreeNodeData *next;
} PartitionTreeNodeData;
+/*
+ * Partitioning scheme
+ * Structure to hold partitioning scheme for a given relation.
+ *
+ * Multiple relations may be partitioned in the same way. The relations
+ * resulting from joining such relations may be partitioned in the same way as
+ * the joining relations. Similarly, relations derived from such relations by
+ * grouping, sorting be partitioned in the same as the underlying relations.
+ *
+ * PlannerInfo stores a list of distinct "canonical" partitioning schemes.
+ * RelOptInfo of a partitioned relation holds the pointer to "canonical"
+ * partitioning scheme.
+ */
+typedef struct PartitionSchemeData
+{
+ /* Information about partitions */
+ int nparts; /* number of partitions */
+ BoundCollection bounds; /* Partition bounds/lists */
+
+ /* Information about partition keys */
+ char strategy; /* partition strategy */
+ int16 partnatts; /* number of partition attributes */
+ Oid *partopfamily; /* OIDs of operator families */
+ Oid *partopcintype; /* OIDs of opclass declared input data types */
+ Oid *key_types; /* OIDs of partition key data types. */
+ int32 *key_typmods; /* typmods of partition keys. */
+ Oid *key_collations; /* OIDs of collations of partition keys. */
+} PartitionSchemeData;
+
/* Support RelationBuildPartitionKey() */
static PartitionKey copy_partition_key(PartitionKey fromkey);
static KeyTypeCollInfo *copy_key_type_coll_info(int nkeycols,
KeyTypeCollInfo *tcinfo);
/* Support RelationBuildPartitionDesc() */
static int32 list_value_cmp(const void *a, const void *b, void *arg);
static int32 range_partition_cmp(const void *a, const void *b, void *arg);
/* Support check_new_partition_bound() */
@@ -870,20 +902,21 @@ RelationBuildPartitionDesc(Relation rel)
result->bounds->rangeinfo = rangeinfo;
break;
}
}
}
MemoryContextSwitchTo(oldcxt);
rel->rd_partdesc = result;
}
+
/*
* Are two partition bound collections logically equal?
*
* Used in the keep logic of relcache.c (ie, in RelationClearRelation()).
* This is also useful when b1 and b2 are bound collections of two separate
* relations, respectively, because BoundCollection is a canonical
* representation of a set partition bounds (for given partitioning strategy).
*/
bool
partition_bounds_equal(PartitionKey key,
@@ -2411,20 +2444,73 @@ make_range_bound(PartitionKey key, List *val, bool inclusive, bool lower)
bound->val[i] = datumCopy(val->constvalue,
key->tcinfo->typbyval[i],
key->tcinfo->typlen[i]);
i++;
}
}
return bound;
}
+static BoundCollection
+copy_bounds(BoundCollection src_bounds, PartitionKey key, int nparts)
+{
+ BoundCollection dst_bounds;
+ int i;
+
+ dst_bounds = (BoundCollection) palloc(sizeof(BoundCollectionData));
+
+ if (src_bounds->listinfo)
+ {
+ ListInfo *dst_li = (ListInfo *) palloc(sizeof(ListInfo));
+ ListInfo *src_li = src_bounds->listinfo;
+
+ Assert(!src_bounds->rangeinfo);
+ dst_bounds->rangeinfo = NULL;
+
+ /* Copy the ListInfo structure. */
+ dst_li->nvalues = src_li->nvalues;
+ dst_li->has_null = src_li->has_null;
+ dst_li->null_index = src_li->null_index;
+
+ dst_li->values = (Datum *) palloc(sizeof(Datum) * dst_li->nvalues);
+ dst_li->indexes = (int *) palloc(sizeof(int) * dst_li->nvalues);
+ for (i = 0; i < dst_li->nvalues; i++)
+ {
+ dst_li->values[i] = datumCopy(src_li->values[i],
+ key->tcinfo->typbyval[0],
+ key->tcinfo->typlen[0]);
+ dst_li->indexes[i] = src_li->indexes[i];
+ }
+
+ dst_bounds->listinfo = dst_li;
+ }
+ else
+ {
+ RangeInfo *dst_ri = (RangeInfo *) palloc(sizeof(RangeInfo));
+ RangeInfo *src_ri = src_bounds->rangeinfo;
+
+ Assert(!src_bounds->listinfo && src_bounds->rangeinfo);
+ dst_bounds->listinfo = NULL;
+
+ /* Copy RangeInfo structure. */
+ dst_ri = (RangeInfo *) palloc(sizeof(RangeInfo));
+ dst_ri->ranges = (PartitionRange **) palloc(sizeof(PartitionRange *) * nparts);
+ for (i = 0; i < nparts; i++)
+ dst_ri->ranges[i] = copy_range(src_ri->ranges[i], key);
+
+ dst_bounds->rangeinfo = dst_ri;
+ }
+
+ return dst_bounds;
+}
+
/*
* Make and return a copy of input PartitionRange.
*/
static PartitionRange *
copy_range(PartitionRange *src, PartitionKey key)
{
PartitionRange *result;
result = (PartitionRange *) palloc0(sizeof(PartitionRange));
result->lower = copy_range_bound(src->lower, key);
@@ -2647,10 +2733,210 @@ tuple_rightof_bound(PartitionKey key, Datum *tuple, PartitionRangeBound *bound)
static bool
tuple_leftof_bound(PartitionKey key, Datum *tuple, PartitionRangeBound *bound)
{
int32 cmpval = partition_range_tuple_cmp(key, tuple, bound->val);
if (!cmpval)
return !bound->lower ? bound->inclusive : !bound->inclusive;
return cmpval < 0;
}
+
+/*
+ * find_partition_scheme
+ * Find the "canonical" partition scheme for the given base table.
+ *
+ * The function searches the list of canonical partition schemes for one that
+ * exactly matches the partitioning properties of the given relation. If it
+ * does not find one, the function creates a canonical partition scheme
+ * structure and adds it to the list.
+ *
+ * For an umpartitioned table, it returns NULL.
+ */
+extern PartitionScheme
+find_partition_scheme(PlannerInfo *root, Relation relation)
+{
+ PartitionKey part_key = RelationGetPartitionKey(relation);
+ PartitionDesc part_desc = RelationGetPartitionDesc(relation);
+ ListCell *lc;
+ int nparts;
+ int partnatts;
+ int cnt_pks;
+ PartitionScheme part_scheme = NULL;
+
+ /* No partition scheme for an unpartitioned relation. */
+ if (!part_desc || !part_key)
+ return NULL;
+
+ nparts = part_desc->nparts;
+ partnatts = part_key->partnatts;
+
+ /* Search for a matching partition scheme and return if found one. */
+ foreach (lc, root->part_schemes)
+ {
+ part_scheme = lfirst(lc);
+
+ /* Match number of partitions and partitioning strategy. */
+ if (nparts != part_scheme->nparts ||
+ part_key->strategy != part_scheme->strategy ||
+ partnatts != part_scheme->partnatts)
+ continue;
+
+ /* Match the partition key types. */
+ for (cnt_pks = 0; cnt_pks < partnatts; cnt_pks++)
+ {
+ /*
+ * It suffices to check the OID of support function as it always has
+ * two arguemnts and returns boolean. For types, it suffices to match
+ * the type id, mod and collation; len, byval and align are depedent on
+ * the first two.
+ */
+ if (part_key->partopfamily[cnt_pks] != part_scheme->partopfamily[cnt_pks] ||
+ part_key->partopcintype[cnt_pks] != part_scheme->partopcintype[cnt_pks] ||
+ part_key->tcinfo->typid[cnt_pks] != part_scheme->key_types[cnt_pks] ||
+ part_key->tcinfo->typmod[cnt_pks] != part_scheme->key_typmods[cnt_pks] ||
+ part_key->tcinfo->typcoll[cnt_pks] != part_scheme->key_collations[cnt_pks])
+ break;
+ }
+
+ /* Some partition key didn't match. Check next partitioning scheme. */
+ if (cnt_pks < partnatts)
+ continue;
+
+ if (!partition_bounds_equal(part_key, part_desc->bounds,
+ part_scheme->bounds, nparts))
+ continue;
+
+ /* Found matching partition scheme. */
+ return part_scheme;
+ }
+
+ /* Did not find matching partition scheme. Create one. */
+ part_scheme = (PartitionScheme) palloc0(sizeof(PartitionSchemeData));
+
+ /* Copy partition bounds/lists. */
+ part_scheme->nparts = part_desc->nparts;
+ part_scheme->strategy = part_key->strategy;
+ part_scheme->bounds = copy_bounds(part_desc->bounds, part_key,
+ part_scheme->nparts);
+
+ /* Store partition key information. */
+ part_scheme->partnatts = part_key->partnatts;
+
+ part_scheme->partopfamily = (Oid *) palloc(sizeof(Oid) * partnatts);
+ part_scheme->partopcintype = (Oid *) palloc(sizeof(Oid) * partnatts);
+ part_scheme->key_types = (Oid *) palloc(sizeof(Oid) * partnatts);
+ part_scheme->key_typmods = (int32 *) palloc(sizeof(int32) * partnatts);
+ part_scheme->key_collations = (Oid *) palloc(sizeof(Oid) * partnatts);
+
+ for (cnt_pks = 0; cnt_pks < partnatts; cnt_pks++)
+ {
+ part_scheme->partopfamily[cnt_pks] = part_key->partopfamily[cnt_pks];
+ part_scheme->partopcintype[cnt_pks] = part_key->partopcintype[cnt_pks];
+ part_scheme->key_types[cnt_pks] = part_key->tcinfo->typid[cnt_pks];
+ part_scheme->key_typmods[cnt_pks] = part_key->tcinfo->typmod[cnt_pks];
+ part_scheme->key_collations[cnt_pks] = part_key->tcinfo->typcoll[cnt_pks];
+ }
+
+ /* Add the partitioning scheme to PlannerInfo. */
+ root->part_schemes = lappend(root->part_schemes, part_scheme);
+
+ return part_scheme;
+}
+
+/*
+ * build_baserel_partition_keys
+ *
+ * For a base relation, construct an array of partition key expressions. Each
+ * partition key expression is stored as a single member list to accomodate
+ * more partition keys when relations are joined.
+ */
+extern List **
+build_baserel_partition_keys(Relation relation, Index varno)
+{
+ PartitionKey part_key = RelationGetPartitionKey(relation);
+ int num_pkexprs;
+ int cnt_pke;
+ List **partexprs;
+ ListCell *lc;
+
+ if (!part_key || part_key->partnatts <= 0)
+ return NULL;
+
+ num_pkexprs = part_key->partnatts;
+
+ /* Store partition keys as single elements lists. */
+ partexprs = (List **) palloc(sizeof(List *) * num_pkexprs);
+
+ lc = list_head(part_key->partexprs);
+ for (cnt_pke = 0; cnt_pke < num_pkexprs; cnt_pke++)
+ {
+ AttrNumber attno = part_key->partattrs[cnt_pke];
+ Expr *pkexpr;
+
+ if (attno != InvalidAttrNumber)
+ {
+ /* Single column partition key is stored as a Var node. */
+ Form_pg_attribute att_tup;
+
+ if (attno < 0)
+ att_tup = SystemAttributeDefinition(attno,
+ relation->rd_rel->relhasoids);
+ else
+ att_tup = relation->rd_att->attrs[attno - 1];
+
+ pkexpr = (Expr *) makeVar(varno, attno, att_tup->atttypid,
+ att_tup->atttypmod,
+ att_tup->attcollation, 0);
+ }
+ else
+ {
+ if (!lc)
+ elog(ERROR, "wrong number of partition key expressions");
+ pkexpr = (Expr *) copyObject(lfirst(lc));
+ ChangeVarNodes((Node *) pkexpr, 1, varno, 0);
+ lc = lnext(lc);
+ }
+
+ partexprs[cnt_pke] = list_make1(pkexpr);
+ }
+
+ return partexprs;
+}
+
+/*
+ * Returns the number of partitions supported by the given partition scheme.
+ */
+extern int
+PartitionSchemeGetNumParts(PartitionScheme part_scheme)
+{
+ return part_scheme ? part_scheme->nparts : 0;
+}
+
+/*
+ * Returns the number of partition keys supported by the given partition
+ * scheme.
+ */
+extern int
+PartitionSchemeGetNumKeys(PartitionScheme part_scheme)
+{
+ return part_scheme ? part_scheme->partnatts : 0;
+}
+
+/*
+ * Return palloc'ed array of OIDs of partitions.
+ */
+extern Oid *
+PartitionDescGetPartOids(PartitionDesc part_desc)
+{
+ Oid *part_oids;
+ int cnt_parts;
+
+ if (!part_desc || part_desc->nparts <= 0)
+ return NULL;
+
+ part_oids = (Oid *) palloc(sizeof(Oid) * part_desc->nparts);
+ for (cnt_parts = 0; cnt_parts < part_desc->nparts; cnt_parts++)
+ part_oids[cnt_parts] = part_desc->oids[cnt_parts];
+
+ return part_oids;
+}
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 99b6bc8..1e43bab 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -13,20 +13,21 @@
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <limits.h>
#include <math.h>
#include "access/sysattr.h"
#include "access/tsmapi.h"
+#include "catalog/partition.h"
#include "catalog/pg_class.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "foreign/fdwapi.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#ifdef OPTIMIZER_DEBUG
#include "nodes/print.h"
#endif
#include "optimizer/clauses.h"
@@ -37,20 +38,21 @@
#include "optimizer/plancat.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parse_clause.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
#include "utils/lsyscache.h"
+#include "utils/rel.h"
/* results of subquery_is_pushdown_safe */
typedef struct pushdown_safety_info
{
bool *unsafeColumns; /* which output columns are unsafe to use */
bool unsafeVolatile; /* don't push down volatile quals */
bool unsafeLeaky; /* don't push down leaky quals */
} pushdown_safety_info;
@@ -119,20 +121,24 @@ static void check_output_expressions(Query *subquery,
static void compare_tlist_datatypes(List *tlist, List *colTypes,
pushdown_safety_info *safetyInfo);
static bool targetIsInAllPartitionLists(TargetEntry *tle, Query *query);
static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
pushdown_safety_info *safetyInfo);
static void subquery_push_qual(Query *subquery,
RangeTblEntry *rte, Index rti, Node *qual);
static void recurse_push_qual(Node *setOp, Query *topquery,
RangeTblEntry *rte, Index rti, Node *qual);
static void remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel);
+static void add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
+ List *live_childrels);
+static void generate_partition_wise_join_paths(PlannerInfo *root,
+ RelOptInfo *rel);
/*
* make_one_rel
* Finds all possible access paths for executing a query, returning a
* single rel that represents the join of all base rels in the query.
*/
RelOptInfo *
make_one_rel(PlannerInfo *root, List *joinlist)
{
@@ -861,20 +867,48 @@ static void
set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
{
int parentRTindex = rti;
bool has_live_children;
double parent_rows;
double parent_size;
double *parent_attrsizes;
int nattrs;
ListCell *l;
+ Oid *part_oids = NULL;
+ int nparts = 0;
+
+ /*
+ * For a partitioned relation, we will save the child RelOptInfos in parent
+ * RelOptInfo in the same the order as corresponding bounds/lists are
+ * stored in the partition scheme.
+ */
+ if (rel->part_scheme)
+ {
+ RangeTblEntry *rte = root->simple_rte_array[rel->relid];
+
+ /*
+ * We need not lock the relation since it was already locked, either by
+ * the rewriter or when expand_inherited_rtentry() added it to the
+ * query's rangetable.
+ */
+ Relation relation = heap_open(rte->relid, NoLock);
+ PartitionDesc part_desc = RelationGetPartitionDesc(relation);
+
+ part_oids = PartitionDescGetPartOids(part_desc);
+ nparts = part_desc->nparts;
+
+ Assert(part_oids && nparts > 0);
+
+ rel->part_rels = (RelOptInfo **)palloc0(sizeof(RelOptInfo *) * nparts);
+ heap_close(relation, NoLock);
+ }
/*
* Initialize to compute size estimates for whole append relation.
*
* We handle width estimates by weighting the widths of different child
* rels proportionally to their number of rows. This is sensible because
* the use of width estimates is mainly to compute the total relation
* "footprint" if we have to sort or hash it. To do this, we sum the
* total equivalent size (in "double" arithmetic) and then divide by the
* total rowcount estimate. This is done separately for the total rel
@@ -892,36 +926,80 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
foreach(l, root->append_rel_list)
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
int childRTindex;
RangeTblEntry *childRTE;
RelOptInfo *childrel;
List *childquals;
Node *childqual;
ListCell *parentvars;
ListCell *childvars;
+ int cnt_parts;
/* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != parentRTindex)
continue;
childRTindex = appinfo->child_relid;
childRTE = root->simple_rte_array[childRTindex];
/*
* The child rel's RelOptInfo was already created during
* add_base_rels_to_query.
*/
childrel = find_base_rel(root, childRTindex);
+
+ /*
+ * Save topmost parent's relid. If the parent itself is a child of some
+ * other relation, use parent's topmost parent relids.
+ */
+ if (rel->top_parent_relids)
+ childrel->top_parent_relids = rel->top_parent_relids;
+ else
+ childrel->top_parent_relids = bms_copy(rel->relids);
+
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
/*
+ * For a partitioned table, save the child RelOptInfo at its
+ * appropriate place in the parent RelOptInfo.
+ */
+ for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+ {
+ if (part_oids[cnt_parts] == childRTE->relid)
+ {
+ Assert(!rel->part_rels[cnt_parts]);
+ rel->part_rels[cnt_parts] = childrel;
+ }
+ }
+
+ /*
+ * Copy/Modify targetlist. Partition-wise join technique may consider
+ * an OUTER join of another child relation with this child relation.
+ * In that case, even if this child is deemed empty, we will require
+ * the targetlist of this child to construct the nullable side. Hence
+ * set the targetlist before we prove that the child is empty and stop
+ * processing further.
+ *
+ * NB: the resulting childrel->reltarget->exprs may contain arbitrary
+ * expressions, which otherwise would not occur in a rel's targetlist.
+ * Code that might be looking at an appendrel child must cope with
+ * such. (Normally, a rel's targetlist would only include Vars and
+ * PlaceHolderVars.) XXX we do not bother to update the cost or width
+ * fields of childrel->reltarget; not clear if that would be useful.
+ */
+ childrel->reltarget->exprs = (List *)
+ adjust_appendrel_attrs(root,
+ (Node *) rel->reltarget->exprs,
+ appinfo);
+
+ /*
* We have to copy the parent's targetlist and quals to the child,
* with appropriate substitution of variables. However, only the
* baserestrictinfo quals are needed before we can check for
* constraint exclusion; so do that first and then check to see if we
* can disregard this child.
*
* As of 8.4, the child rel's targetlist might contain non-Var
* expressions, which means that substitution into the quals could
* produce opportunities for const-simplification, and perhaps even
* pseudoconstant quals. To deal with this, we strip the RestrictInfo
@@ -953,38 +1031,25 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
if (relation_excluded_by_constraints(root, childrel, childRTE))
{
/*
* This child need not be scanned, so we can omit it from the
* appendrel.
*/
set_dummy_rel_pathlist(childrel);
continue;
}
- /*
- * CE failed, so finish copying/modifying targetlist and join quals.
- *
- * NB: the resulting childrel->reltarget->exprs may contain arbitrary
- * expressions, which otherwise would not occur in a rel's targetlist.
- * Code that might be looking at an appendrel child must cope with
- * such. (Normally, a rel's targetlist would only include Vars and
- * PlaceHolderVars.) XXX we do not bother to update the cost or width
- * fields of childrel->reltarget; not clear if that would be useful.
- */
+ /* CE failed, so finish copying/modifying join quals. */
childrel->joininfo = (List *)
adjust_appendrel_attrs(root,
(Node *) rel->joininfo,
appinfo);
- childrel->reltarget->exprs = (List *)
- adjust_appendrel_attrs(root,
- (Node *) rel->reltarget->exprs,
- appinfo);
/*
* We have to make child entries in the EquivalenceClass data
* structures as well. This is needed either if the parent
* participates in some eclass joins (because we will want to consider
* inner-indexscan joins on the individual children) or if the parent
* has useful pathkeys (because we should try to build MergeAppend
* paths that produce those sort orderings).
*/
if (rel->has_eclass_joins || has_useful_pathkeys(root, rel))
@@ -1073,20 +1138,28 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
}
if (child_width <= 0)
child_width = get_typavgwidth(exprType(childvar),
exprTypmod(childvar));
Assert(child_width > 0);
parent_attrsizes[pndx] += child_width * childrel->rows;
}
}
}
+ /* Should have found all the childrels of a partitioned relation. */
+ if (rel->part_scheme)
+ {
+ int cnt_parts;
+ for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+ Assert(rel->part_rels[cnt_parts]);
+ }
+
if (has_live_children)
{
/*
* Save the finished size estimates.
*/
int i;
Assert(parent_rows > 0);
rel->rows = parent_rows;
rel->reltarget->width = rint(parent_size / parent_rows);
@@ -1115,41 +1188,32 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
/*
* set_append_rel_pathlist
* Build access paths for an "append relation"
*/
static void
set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte)
{
int parentRTindex = rti;
List *live_childrels = NIL;
- List *subpaths = NIL;
- bool subpaths_valid = true;
- List *partial_subpaths = NIL;
- bool partial_subpaths_valid = true;
- List *all_child_pathkeys = NIL;
- List *all_child_outers = NIL;
ListCell *l;
/*
- * Generate access paths for each member relation, and remember the
- * cheapest path for each one. Also, identify all pathkeys (orderings)
- * and parameterizations (required_outer sets) available for the member
- * relations.
+ * Generate access paths for each member relation and remember the
+ * non-dummy children.
*/
foreach(l, root->append_rel_list)
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
int childRTindex;
RangeTblEntry *childRTE;
RelOptInfo *childrel;
- ListCell *lcp;
/* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != parentRTindex)
continue;
/* Re-locate the child RTE and RelOptInfo */
childRTindex = appinfo->child_relid;
childRTE = root->simple_rte_array[childRTindex];
childrel = root->simple_rel_array[childRTindex];
@@ -1170,20 +1234,57 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
/*
* If child is dummy, ignore it.
*/
if (IS_DUMMY_REL(childrel))
continue;
/*
* Child is live, so add it to the live_childrels list for use below.
*/
live_childrels = lappend(live_childrels, childrel);
+ }
+
+ add_paths_to_append_rel(root, rel, live_childrels);
+}
+
+static void
+add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
+ List *live_childrels)
+{
+ List *subpaths = NIL;
+ bool subpaths_valid = true;
+ List *partial_subpaths = NIL;
+ bool partial_subpaths_valid = true;
+ List *all_child_pathkeys = NIL;
+ List *all_child_outers = NIL;
+ ListCell *l;
+
+ /* An append relation with all its children dummy is dummy. */
+ if (!live_childrels)
+ {
+ /* Mark the relation as dummy, if not already done so. */
+ if (!IS_DUMMY_REL(rel))
+ set_dummy_rel_pathlist(rel);
+
+ /* No more paths need to be added. */
+ return;
+ }
+
+ /*
+ * For every non-dummy child, remember the cheapest path. Also, identify
+ * all pathkeys (orderings) and parameterizations (required_outer sets)
+ * available for the non-dummy member relations.
+ */
+ foreach (l, live_childrels)
+ {
+ RelOptInfo *childrel = lfirst(l);
+ ListCell *lcp;
/*
* If child has an unparameterized cheapest-total path, add that to
* the unparameterized Append path we are constructing for the parent.
* If not, there's no workable unparameterized path.
*/
if (childrel->cheapest_total_path->param_info == NULL)
subpaths = accumulate_append_subpath(subpaths,
childrel->cheapest_total_path);
else
@@ -2188,20 +2289,22 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
* Run generate_gather_paths() for each just-processed joinrel. We
* could not do this earlier because both regular and partial paths
* can get added to a particular joinrel at multiple times within
* join_search_one_level. After that, we're done creating paths for
* the joinrel, so run set_cheapest().
*/
foreach(lc, root->join_rel_level[lev])
{
rel = (RelOptInfo *) lfirst(lc);
+ generate_partition_wise_join_paths(root, rel);
+
/* Create GatherPaths for any useful partial paths for rel */
generate_gather_paths(root, rel);
/* Find and save the cheapest paths for this rel */
set_cheapest(rel);
#ifdef OPTIMIZER_DEBUG
debug_print_rel(root, rel);
#endif
}
@@ -2851,20 +2954,73 @@ remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel)
* OK, we don't need it. Replace the expression with a NULL constant.
* Preserve the exposed type of the expression, in case something
* looks at the rowtype of the subquery's result.
*/
tle->expr = (Expr *) makeNullConst(exprType(texpr),
exprTypmod(texpr),
exprCollation(texpr));
}
}
+/*
+ * generate_partition_wise_join_paths
+ * Create appends paths containing partition-wise join paths for given
+ * join relation.
+ */
+static void
+generate_partition_wise_join_paths(PlannerInfo *root, RelOptInfo *rel)
+{
+ List *live_children = NIL;
+ int cnt_part;
+ int nparts = PartitionSchemeGetNumParts(rel->part_scheme);
+
+ /* Handle only join relations. */
+ if (rel->reloptkind != RELOPT_JOINREL &&
+ rel->reloptkind != RELOPT_OTHER_JOINREL)
+ return;
+
+ /* If the relation is not partitioned or is proven dummy, nothing to do. */
+ if (!rel->part_scheme || !rel->part_rels || IS_DUMMY_REL(rel))
+ return;
+
+ for (cnt_part = 0; cnt_part < nparts; cnt_part++)
+ {
+ RelOptInfo *child_rel = rel->part_rels[cnt_part];
+
+ /* Ignore dummy child. */
+ if (!IS_DUMMY_REL(child_rel))
+ {
+ /* Recursively collect the paths from child joinrel. */
+ generate_partition_wise_join_paths(root, child_rel);
+
+ /* Find the cheapest of the paths for this rel. */
+ set_cheapest(child_rel);
+
+#ifdef OPTIMIZER_DEBUG
+ debug_print_rel(root, rel);
+#endif
+
+ live_children = lappend(live_children, child_rel);
+ }
+ }
+
+ /*
+ * Create append paths by collecting sub paths from live children. Even if
+ * there are no live children, we should create an append path with no
+ * subpaths i.e. a dummy access path.
+ */
+ add_paths_to_append_rel(root, rel, live_children);
+
+ if (live_children)
+ pfree(live_children);
+}
+
/*****************************************************************************
* DEBUG SUPPORT
*****************************************************************************/
#ifdef OPTIMIZER_DEBUG
static void
print_relids(PlannerInfo *root, Relids relids)
{
int x;
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 2a49639..a23da1c 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -119,20 +119,21 @@ bool enable_seqscan = true;
bool enable_indexscan = true;
bool enable_indexonlyscan = true;
bool enable_bitmapscan = true;
bool enable_tidscan = true;
bool enable_sort = true;
bool enable_hashagg = true;
bool enable_nestloop = true;
bool enable_material = true;
bool enable_mergejoin = true;
bool enable_hashjoin = true;
+bool enable_partition_wise_join = true;
typedef struct
{
PlannerInfo *root;
QualCost total;
} cost_qual_eval_context;
static List *extract_nonindex_conditions(List *qual_clauses, List *indexquals);
static MergeScanSelCache *cached_scansel(PlannerInfo *root,
RestrictInfo *rinfo,
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 0e50ad5..73026a3 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -2359,20 +2359,22 @@ eclass_useful_for_merging(PlannerInfo *root,
/*
* Note we don't test ec_broken; if we did, we'd need a separate code path
* to look through ec_sources. Checking the members anyway is OK as a
* possibly-overoptimistic heuristic.
*/
/* If specified rel is a child, we must consider the topmost parent rel */
if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
relids = find_childrel_top_parent(root, rel)->relids;
+ else if (rel->reloptkind == RELOPT_OTHER_JOINREL)
+ relids = rel->top_parent_relids;
else
relids = rel->relids;
/* If rel already includes all members of eclass, no point in searching */
if (bms_is_subset(eclass->ec_relids, relids))
return false;
/* To join, we need a member not in the given rel */
foreach(lc, eclass->ec_members)
{
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index cc7384f..db0e469 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -18,23 +18,29 @@
#include "executor/executor.h"
#include "foreign/fdwapi.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
/* Hook for plugins to get control in add_paths_to_joinrel() */
set_join_pathlist_hook_type set_join_pathlist_hook = NULL;
-#define PATH_PARAM_BY_REL(path, rel) \
+#define PATH_PARAM_BY_PARENT(path, rel) \
+ ((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), \
+ (rel)->top_parent_relids))
+#define PATH_PARAM_BY_REL_SELF(path, rel) \
((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), (rel)->relids))
+#define PATH_PARAM_BY_REL(path, rel) \
+ (PATH_PARAM_BY_REL_SELF(path, rel) || PATH_PARAM_BY_PARENT(path, rel))
+
static void sort_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
JoinType jointype, JoinPathExtraData *extra);
static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
JoinType jointype, JoinPathExtraData *extra);
static void consider_parallel_nestloop(PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
@@ -125,38 +131,51 @@ add_paths_to_joinrel(PlannerInfo *root,
* directly to the parameter source rel instead of joining to the other
* input rel. (But see allow_star_schema_join().) This restriction
* reduces the number of parameterized paths we have to deal with at
* higher join levels, without compromising the quality of the resulting
* plan. We express the restriction as a Relids set that must overlap the
* parameterization of any proposed join path.
*/
foreach(lc, root->join_info_list)
{
SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc);
+ Relids joinrelids;
+
+ /*
+ * PlannerInfo doesn't contain the SpecialJoinInfos created for joins
+ * between partitions, even if there is a SpecialJoinInfo node for the
+ * join between the topmost parents. Hence while calculating Relids set
+ * representing the restriction, consider relids of topmost parent of
+ * partitions.
+ */
+ if (joinrel->reloptkind == RELOPT_OTHER_JOINREL)
+ joinrelids = joinrel->top_parent_relids;
+ else
+ joinrelids = joinrel->relids;
/*
* SJ is relevant to this join if we have some part of its RHS
* (possibly not all of it), and haven't yet joined to its LHS. (This
* test is pretty simplistic, but should be sufficient considering the
* join has already been proven legal.) If the SJ is relevant, it
* presents constraints for joining to anything not in its RHS.
*/
- if (bms_overlap(joinrel->relids, sjinfo->min_righthand) &&
- !bms_overlap(joinrel->relids, sjinfo->min_lefthand))
+ if (bms_overlap(joinrelids, sjinfo->min_righthand) &&
+ !bms_overlap(joinrelids, sjinfo->min_lefthand))
extra.param_source_rels = bms_join(extra.param_source_rels,
bms_difference(root->all_baserels,
sjinfo->min_righthand));
/* full joins constrain both sides symmetrically */
if (sjinfo->jointype == JOIN_FULL &&
- bms_overlap(joinrel->relids, sjinfo->min_lefthand) &&
- !bms_overlap(joinrel->relids, sjinfo->min_righthand))
+ bms_overlap(joinrelids, sjinfo->min_lefthand) &&
+ !bms_overlap(joinrelids, sjinfo->min_righthand))
extra.param_source_rels = bms_join(extra.param_source_rels,
bms_difference(root->all_baserels,
sjinfo->min_lefthand));
}
/*
* However, when a LATERAL subquery is involved, there will simply not be
* any paths for the joinrel that aren't parameterized by whatever the
* subquery is parameterized by, unless its parameterization is resolved
* within the joinrel. So we might as well allow additional dependencies
@@ -272,20 +291,35 @@ try_nestloop_path(PlannerInfo *root,
Path *outer_path,
Path *inner_path,
List *pathkeys,
JoinType jointype,
JoinPathExtraData *extra)
{
Relids required_outer;
JoinCostWorkspace workspace;
/*
+ * An inner path parameterized by the parent relation of outer
+ * relation needs to be reparameterized by the outer relation to be used
+ * for parameterized nested loop join.
+ */
+ if (PATH_PARAM_BY_PARENT(inner_path, outer_path->parent))
+ {
+ inner_path = reparameterize_path_for_child(root, inner_path,
+ outer_path->parent);
+
+ /* If we could not translate the path, don't produce nest loop path. */
+ if (!inner_path)
+ return;
+ }
+
+ /*
* Check to see if proposed path is still parameterized, and reject if the
* parameterization wouldn't be sensible --- unless allow_star_schema_join
* says to allow it anyway. Also, we must reject if have_dangerous_phv
* doesn't like the look of it, which could only happen if the nestloop is
* still parameterized.
*/
required_outer = calc_nestloop_required_outer(outer_path,
inner_path);
if (required_outer &&
((!bms_overlap(required_outer, extra->param_source_rels) &&
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 01d4fea..4c51dd2 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -7,38 +7,56 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/optimizer/path/joinrels.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "catalog/partition.h"
+#include "optimizer/clauses.h"
#include "optimizer/joininfo.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
+#include "optimizer/prep.h"
+#include "optimizer/cost.h"
#include "utils/memutils.h"
static void make_rels_by_clause_joins(PlannerInfo *root,
RelOptInfo *old_rel,
ListCell *other_rels);
static void make_rels_by_clauseless_joins(PlannerInfo *root,
RelOptInfo *old_rel,
ListCell *other_rels);
static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
static bool has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel);
static bool is_dummy_rel(RelOptInfo *rel);
static void mark_dummy_rel(RelOptInfo *rel);
static bool restriction_is_constant_false(List *restrictlist,
bool only_pushed_down);
+static void populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
+ RelOptInfo *rel2, RelOptInfo *joinrel,
+ SpecialJoinInfo *sjinfo, List *restrictlist);
+static void try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1,
+ RelOptInfo *rel2, RelOptInfo *joinrel,
+ SpecialJoinInfo *parent_sjinfo,
+ List *parent_restrictlist);
+static SpecialJoinInfo * build_partition_join_sjinfo(PlannerInfo *root,
+ SpecialJoinInfo *parent_sjinfo,
+ List *append_rel_infos1,
+ List *append_rel_infos2);
+static bool have_partkey_equi_join(RelOptInfo *rel1, RelOptInfo *rel2,
+ JoinType jointype, List *restrictlist);
+static int match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel);
/*
* join_search_one_level
* Consider ways to produce join relations containing exactly 'level'
* jointree items. (This is one step of the dynamic-programming method
* embodied in standard_join_search.) Join rel nodes for each feasible
* combination of lower-level rels are created and returned in a list.
* Implementation paths are created for each such joinrel, too.
*
@@ -717,20 +735,44 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
/*
* If we've already proven this join is empty, we needn't consider any
* more paths for it.
*/
if (is_dummy_rel(joinrel))
{
bms_free(joinrelids);
return joinrel;
}
+ /* Add paths to the join relation. */
+ populate_joinrel_with_paths(root, rel1, rel2, joinrel, sjinfo,
+ restrictlist);
+
+ /* Apply partition-wise join technique, if possible. */
+ try_partition_wise_join(root, rel1, rel2, joinrel, sjinfo, restrictlist);
+
+ bms_free(joinrelids);
+
+ return joinrel;
+}
+
+/*
+ * populate_joinrel_with_paths
+ * Create paths to join given input relation and add those to the given
+ * joinrel. The SpecialJoinInfo provides details about the join and the
+ * restrictlist contains the join clauses and the other clauses applicable
+ * for given pair of the joining relations.
+ */
+static void
+populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
+ RelOptInfo *rel2, RelOptInfo *joinrel,
+ SpecialJoinInfo *sjinfo, List *restrictlist)
+{
/*
* Consider paths using each rel as both outer and inner. Depending on
* the join type, a provably empty outer or inner rel might mean the join
* is provably empty too; in which case throw away any previously computed
* paths and mark the join as dummy. (We do it this way since it's
* conceivable that dummy-ness of a multi-element join might only be
* noticeable for certain construction paths.)
*
* Also, a provably constant-false join restriction typically means that
* we can skip evaluating one or both sides of the join. We do this by
@@ -861,27 +903,22 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
mark_dummy_rel(rel2);
add_paths_to_joinrel(root, joinrel, rel1, rel2,
JOIN_ANTI, sjinfo,
restrictlist);
break;
default:
/* other values not expected here */
elog(ERROR, "unrecognized join type: %d", (int) sjinfo->jointype);
break;
}
-
- bms_free(joinrelids);
-
- return joinrel;
}
-
/*
* have_join_order_restriction
* Detect whether the two relations should be joined to satisfy
* a join-order restriction arising from special or lateral joins.
*
* In practice this is always used with have_relevant_joinclause(), and so
* could be merged with that function, but it seems clearer to separate the
* two concerns. We need this test because there are degenerate cases where
* a clauseless join must be performed to satisfy join-order restrictions.
* Also, if one rel has a lateral reference to the other, or both are needed
@@ -1242,10 +1279,314 @@ restriction_is_constant_false(List *restrictlist, bool only_pushed_down)
/* constant NULL is as good as constant FALSE for our purposes */
if (con->constisnull)
return true;
if (!DatumGetBool(con->constvalue))
return true;
}
}
return false;
}
+
+/*
+ * If the join between the given two relations can be executed as
+ * partition-wise join create the join relations for partition-wise join,
+ * create paths for those and then create append paths to combine
+ * partition-wise join results.
+ */
+static void
+try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
+ RelOptInfo *joinrel, SpecialJoinInfo *parent_sjinfo,
+ List *parent_restrictlist)
+{
+ int nparts;
+ int cnt_parts;
+ StringInfo rel1_desc;
+ StringInfo rel2_desc;
+ PartitionScheme part_scheme;
+
+
+ /* Nothing to do, if the join relation is not partitioned. */
+ if (!joinrel->part_scheme)
+ return;
+
+ /*
+ * If an equi-join condition between the partition keys of the joining
+ * relations does not exist, the given partition scheme can not be used for
+ * partition-wise join between these two relations.
+ */
+ if (!have_partkey_equi_join(rel1, rel2, parent_sjinfo->jointype,
+ parent_restrictlist))
+ return;
+
+ Assert(joinrel->part_scheme == rel1->part_scheme &&
+ joinrel->part_scheme == rel2->part_scheme);
+
+ part_scheme = joinrel->part_scheme;
+ nparts = PartitionSchemeGetNumParts(part_scheme);
+ rel1_desc = makeStringInfo();
+ rel2_desc = makeStringInfo();
+
+ /* TODO: remove this notice when finalising the patch. */
+ outBitmapset(rel1_desc, rel1->relids);
+ outBitmapset(rel2_desc, rel2->relids);
+ elog(NOTICE, "join between relations %s and %s is considered for partition-wise join.",
+ rel1_desc->data, rel2_desc->data);
+
+ /*
+ * We allocate the array for child RelOptInfos till we find at least one
+ * join order which can use partition-wise join technique. If no join order
+ * can use partition-wise join technique, there are no child relations.
+ */
+
+ if (!joinrel->part_rels)
+ joinrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) * nparts);
+
+ /*
+ * Create join relations for the partition relations, if they do not exist
+ * already. Add paths to those for the given pair of joining relations.
+ */
+ for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+ {
+ RelOptInfo *child_joinrel;
+ RelOptInfo *child_rel1 = rel1->part_rels[cnt_parts];
+ RelOptInfo *child_rel2 = rel2->part_rels[cnt_parts];
+ SpecialJoinInfo *child_sjinfo;
+ List *child_restrictlist;
+ List *join_aris;
+ List *ari1;
+ List *ari2;
+
+ /* We should never try to join two overlapping sets of rels. */
+ Assert(!bms_overlap(child_rel1->relids, child_rel2->relids));
+
+ /*
+ * Gather the AppendRelInfos for base partition relations
+ * partiticipating in the given partition relations. We need them
+ * construct partition-wise join relation, special join info and
+ * restriction list by substituting the Var and relids from parent to
+ * child.
+ */
+ ari1 = find_appendrelinfos_by_relids(root, child_rel1->relids);
+ ari2 = find_appendrelinfos_by_relids(root, child_rel2->relids);
+
+ /*
+ * Construct SpecialJoinInfo from parent join relations's
+ * SpecialJoinInfo.
+ */
+ child_sjinfo = build_partition_join_sjinfo(root, parent_sjinfo, ari1, ari2);
+
+ /* Construct the parent-child relid map for the join relation. */
+ join_aris = list_concat(ari1, ari2);
+
+ /*
+ * Construct restrictions applicable to the partition-wise join from
+ * those applicable to the join between the parents.
+ */
+ child_restrictlist = (List *) adjust_partitionrel_attrs(root,
+ (Node *)parent_restrictlist,
+ join_aris);
+
+ child_joinrel = joinrel->part_rels[cnt_parts];
+
+ /* Construct the join relation for given partition of the join. */
+ if (!child_joinrel)
+ {
+ child_joinrel = build_partition_join_rel(root, child_rel1,
+ child_rel2, joinrel,
+ child_sjinfo,
+ child_restrictlist,
+ join_aris);
+
+ joinrel->part_rels[cnt_parts] = child_joinrel;
+
+ }
+
+ /*
+ * If we've already proven that this join is empty, we needn't consider
+ * any more paths for it.
+ */
+ if (is_dummy_rel(child_joinrel))
+ continue;
+
+ populate_joinrel_with_paths(root, child_rel1, child_rel2, child_joinrel,
+ child_sjinfo, child_restrictlist);
+
+ /*
+ * If the partition themselves are partitioned, try partition-wise join
+ * recursively.
+ */
+ try_partition_wise_join(root, child_rel1, child_rel2, child_joinrel,
+ child_sjinfo, child_restrictlist);
+ }
+}
+
+/*
+ * Construct the SpecialJoinInfo for the partition-wise join using parents'
+ * special join info. Also, instead of
+ * constructing an sjinfo everytime, we should probably save it in
+ * root->join_info_list and search within it like join_is_legal?
+ */
+static SpecialJoinInfo *
+build_partition_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,
+ List *append_rel_infos1, List *append_rel_infos2)
+{
+ SpecialJoinInfo *sjinfo = copyObject(parent_sjinfo);
+ sjinfo->min_lefthand = adjust_partition_relids(sjinfo->min_lefthand,
+ append_rel_infos1);
+ sjinfo->min_righthand = adjust_partition_relids(sjinfo->min_righthand,
+ append_rel_infos2);
+ sjinfo->syn_lefthand = adjust_partition_relids(sjinfo->syn_lefthand,
+ append_rel_infos1);
+ sjinfo->syn_righthand = adjust_partition_relids(sjinfo->syn_righthand,
+ append_rel_infos2);
+
+ /* Replace the Var nodes of parent with those of children in expressions. */
+ sjinfo->semi_rhs_exprs = (List *) adjust_partitionrel_attrs(root,
+ (Node *) sjinfo->semi_rhs_exprs,
+ append_rel_infos2);
+ return sjinfo;
+}
+
+/*
+ * Substitute oldrelids with newrelids in the given Relids set. It recycles the
+ * given relids input.
+ */
+Relids
+adjust_partition_relids(Relids relids, List *append_rel_infos)
+{
+ ListCell *lc;
+ foreach (lc, append_rel_infos)
+ {
+ AppendRelInfo *ari = lfirst(lc);
+
+ /* Remove old, add new */
+ if (bms_is_member(ari->parent_relid, relids))
+ {
+ relids = bms_del_member(relids, ari->parent_relid);
+ relids = bms_add_member(relids, ari->child_relid);
+ }
+ }
+ return relids;
+}
+
+/*
+ * Returns true if the given relations have equi-join clauses on all the
+ * corresponding partition keys.
+ */
+static bool
+have_partkey_equi_join(RelOptInfo *rel1, RelOptInfo *rel2,
+ JoinType jointype, List *restrictlist)
+{
+ PartitionScheme part_scheme = rel1->part_scheme;
+ ListCell *lc;
+ int cnt_pks;
+ int num_pks = PartitionSchemeGetNumKeys(part_scheme);
+ bool *pk_has_clause;
+
+ Assert(rel1->part_scheme == rel2->part_scheme);
+
+ pk_has_clause = (bool *) palloc0(sizeof(bool) * num_pks);
+
+ foreach (lc, restrictlist)
+ {
+ RestrictInfo *rinfo = lfirst(lc);
+ OpExpr *opexpr;
+ Expr *expr1;
+ Expr *expr2;
+ int ipk1;
+ int ipk2;
+
+ /* If processing an outer join, only use its own join clauses. */
+ if (IS_OUTER_JOIN(jointype) && rinfo->is_pushed_down)
+ continue;
+
+ /* Skip non-equi-join clauses. */
+ if (!rinfo->can_join ||
+ rinfo->hashjoinoperator == InvalidOid ||
+ !rinfo->mergeopfamilies)
+ continue;
+
+ opexpr = (OpExpr *) rinfo->clause;
+ Assert(is_opclause(opexpr));
+
+ /*
+ * If clause of form rel1_expr op rel2_expr OR rel2_expr op rel1_expr,
+ * match the operands to the relations. Otherwise, the clause is
+ * not an equi-join between partition keys of joining relations.
+ */
+ if (bms_is_subset(rinfo->left_relids, rel1->relids) &&
+ bms_is_subset(rinfo->right_relids, rel2->relids))
+ {
+ expr1 = linitial(opexpr->args);
+ expr2 = lsecond(opexpr->args);
+ }
+ else if (bms_is_subset(rinfo->left_relids, rel2->relids) &&
+ bms_is_subset(rinfo->right_relids, rel1->relids))
+ {
+ expr1 = lsecond(opexpr->args);
+ expr2 = linitial(opexpr->args);
+ }
+ else
+ continue;
+
+ /* Associate matching clauses with partition keys. */
+ ipk1 = match_expr_to_partition_keys(expr1, rel1);
+ ipk2 = match_expr_to_partition_keys(expr2, rel2);
+
+ if (ipk1 == ipk2)
+ pk_has_clause[ipk1] = true;
+ }
+
+ /*
+ * If every pair of partition key from either of the joining relation has
+ * at least one equi-join clause associated with it, we have an equi-join
+ * between all corresponding partition keys.
+ */
+ for (cnt_pks = 0; cnt_pks < num_pks; cnt_pks++)
+ {
+ if (!pk_has_clause[cnt_pks])
+ {
+ pfree(pk_has_clause);
+ return false;
+ }
+ }
+
+ pfree(pk_has_clause);
+ return true;
+}
+
+/*
+ * match_expr_to_partition_keys
+ *
+ * Find the partition key which is same as the given expression. If found,
+ * return the index of the partition key, else return -1.
+ */
+static int
+match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel)
+{
+ int cnt_pks;
+ int num_pks = PartitionSchemeGetNumKeys(rel->part_scheme);
+
+ /*
+ * Remove the relabel decoration. We can assume that there is at most one
+ * RelabelType node; eval_const_expressions() will have simplied if more
+ * than one.
+ */
+ if (IsA(expr, RelabelType))
+ expr = (Expr *) ((RelabelType *) expr)->arg;
+
+ for (cnt_pks = 0; cnt_pks < num_pks; cnt_pks++)
+ {
+ List *pkexprs = rel->partexprs[cnt_pks];
+ ListCell *lc;
+
+ foreach(lc, pkexprs)
+ {
+ Expr *pkexpr = lfirst(lc);
+ if (equal(pkexpr, expr))
+ return cnt_pks;
+ }
+ }
+
+ return -1;
+}
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 4436ac1..c8870c8 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1081,26 +1081,38 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
List *mergeclauses,
RelOptInfo *joinrel)
{
List *pathkeys = NIL;
int nClauses = list_length(mergeclauses);
EquivalenceClass **ecs;
int *scores;
int necs;
ListCell *lc;
int j;
+ Relids relids;
/* Might have no mergeclauses */
if (nClauses == 0)
return NIL;
/*
+ * Code below scores equivalence classes by how many equivalence members
+ * can produce join clauses for this join relation. Equivalence members
+ * which do not cover the parents of a partition-wise join relation, can
+ * produce join clauses for partition-wise join relation.
+ */
+ if (joinrel->reloptkind == RELOPT_OTHER_JOINREL)
+ relids = joinrel->top_parent_relids;
+ else
+ relids = joinrel->relids;
+
+ /*
* Make arrays of the ECs used by the mergeclauses (dropping any
* duplicates) and their "popularity" scores.
*/
ecs = (EquivalenceClass **) palloc(nClauses * sizeof(EquivalenceClass *));
scores = (int *) palloc(nClauses * sizeof(int));
necs = 0;
foreach(lc, mergeclauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
@@ -1126,21 +1138,21 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
continue;
/* compute score */
score = 0;
foreach(lc2, oeclass->ec_members)
{
EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
/* Potential future join partner? */
if (!em->em_is_const && !em->em_is_child &&
- !bms_overlap(em->em_relids, joinrel->relids))
+ !bms_overlap(em->em_relids, relids))
score++;
}
ecs[necs] = oeclass;
scores[necs] = score;
necs++;
}
/*
* Find out if we have all the ECs mentioned in query_pathkeys; if so we
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 32f4031..b221e2c 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -235,21 +235,22 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
const AttrNumber *reqColIdx,
bool adjust_tlist_in_place,
int *p_numsortkeys,
AttrNumber **p_sortColIdx,
Oid **p_sortOperators,
Oid **p_collations,
bool **p_nullsFirst);
static EquivalenceMember *find_ec_member_for_tle(EquivalenceClass *ec,
TargetEntry *tle,
Relids relids);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys);
+static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+ Relids relids);
static Sort *make_sort_from_groupcols(List *groupcls,
AttrNumber *grpColIdx,
Plan *lefttree);
static Material *make_material(Plan *lefttree);
static WindowAgg *make_windowagg(List *tlist, Index winref,
int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
int frameOptions, Node *startOffset, Node *endOffset,
Plan *lefttree);
static Group *make_group(List *tlist, List *qual, int numGroupCols,
@@ -1507,21 +1508,21 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
Plan *subplan;
/*
* We don't want any excess columns in the sorted tuples, so request a
* smaller tlist. Otherwise, since Sort doesn't project, tlist
* requirements pass through.
*/
subplan = create_plan_recurse(root, best_path->subpath,
flags | CP_SMALL_TLIST);
- plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys);
+ plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys, NULL);
copy_generic_path_info(&plan->plan, (Path *) best_path);
return plan;
}
/*
* create_group_plan
*
* Create a Group plan for 'best_path' and (recursively) plans
@@ -3517,31 +3518,33 @@ create_mergejoin_plan(PlannerInfo *root,
List *innerpathkeys;
int nClauses;
Oid *mergefamilies;
Oid *mergecollations;
int *mergestrategies;
bool *mergenullsfirst;
int i;
ListCell *lc;
ListCell *lop;
ListCell *lip;
+ Path *outer_path = best_path->jpath.outerjoinpath;
+ Path *inner_path = best_path->jpath.innerjoinpath;
/*
* MergeJoin can project, so we don't have to demand exact tlists from the
* inputs. However, if we're intending to sort an input's result, it's
* best to request a small tlist so we aren't sorting more data than
* necessary.
*/
- outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath,
+ outer_plan = create_plan_recurse(root, outer_path,
(best_path->outersortkeys != NIL) ? CP_SMALL_TLIST : 0);
- inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath,
+ inner_plan = create_plan_recurse(root, inner_path,
(best_path->innersortkeys != NIL) ? CP_SMALL_TLIST : 0);
/* Sort join qual clauses into best execution order */
/* NB: do NOT reorder the mergeclauses */
joinclauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo);
/* Get the join qual clauses (in plain expression form) */
/* Any pseudoconstant clauses are ignored here */
if (IS_OUTER_JOIN(best_path->jpath.jointype))
{
@@ -3573,48 +3576,52 @@ create_mergejoin_plan(PlannerInfo *root,
otherclauses = (List *)
replace_nestloop_params(root, (Node *) otherclauses);
}
/*
* Rearrange mergeclauses, if needed, so that the outer variable is always
* on the left; mark the mergeclause restrictinfos with correct
* outer_is_left status.
*/
mergeclauses = get_switched_clauses(best_path->path_mergeclauses,
- best_path->jpath.outerjoinpath->parent->relids);
+ outer_path->parent->relids);
/*
* Create explicit sort nodes for the outer and inner paths if necessary.
*/
if (best_path->outersortkeys)
{
+ Relids outer_relids = outer_path->parent->relids;
Sort *sort = make_sort_from_pathkeys(outer_plan,
- best_path->outersortkeys);
+ best_path->outersortkeys,
+ outer_relids);
label_sort_with_costsize(root, sort, -1.0);
outer_plan = (Plan *) sort;
outerpathkeys = best_path->outersortkeys;
}
else
- outerpathkeys = best_path->jpath.outerjoinpath->pathkeys;
+ outerpathkeys = outer_path->pathkeys;
if (best_path->innersortkeys)
{
+ Relids inner_relids = inner_path->parent->relids;
Sort *sort = make_sort_from_pathkeys(inner_plan,
- best_path->innersortkeys);
+ best_path->innersortkeys,
+ inner_relids);
label_sort_with_costsize(root, sort, -1.0);
inner_plan = (Plan *) sort;
innerpathkeys = best_path->innersortkeys;
}
else
- innerpathkeys = best_path->jpath.innerjoinpath->pathkeys;
+ innerpathkeys = inner_path->pathkeys;
/*
* If specified, add a materialize node to shield the inner plan from the
* need to handle mark/restore.
*/
if (best_path->materialize_inner)
{
Plan *matplan = (Plan *) make_material(inner_plan);
/*
@@ -5330,25 +5337,25 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
/*
* We shouldn't be trying to sort by an equivalence class that
* contains a constant, so no need to consider such cases any
* further.
*/
if (em->em_is_const)
continue;
/*
- * Ignore child members unless they match the rel being
+ * Ignore child members unless they belong to the rel being
* sorted.
*/
if (em->em_is_child &&
- !bms_equal(em->em_relids, relids))
+ !bms_is_subset(em->em_relids, relids))
continue;
sortexpr = em->em_expr;
exprvars = pull_var_clause((Node *) sortexpr,
PVC_INCLUDE_AGGREGATES |
PVC_INCLUDE_WINDOWFUNCS |
PVC_INCLUDE_PLACEHOLDERS);
foreach(k, exprvars)
{
if (!tlist_member_ignore_relabel(lfirst(k), tlist))
@@ -5445,57 +5452,58 @@ find_ec_member_for_tle(EquivalenceClass *ec,
Expr *emexpr;
/*
* We shouldn't be trying to sort by an equivalence class that
* contains a constant, so no need to consider such cases any further.
*/
if (em->em_is_const)
continue;
/*
- * Ignore child members unless they match the rel being sorted.
+ * Ignore child members unless they belong to the rel being sorted.
*/
if (em->em_is_child &&
- !bms_equal(em->em_relids, relids))
+ !bms_is_subset(em->em_relids, relids))
continue;
/* Match if same expression (after stripping relabel) */
emexpr = em->em_expr;
while (emexpr && IsA(emexpr, RelabelType))
emexpr = ((RelabelType *) emexpr)->arg;
if (equal(emexpr, tlexpr))
return em;
}
return NULL;
}
/*
* make_sort_from_pathkeys
* Create sort plan to sort according to given pathkeys
*
* 'lefttree' is the node which yields input tuples
* 'pathkeys' is the list of pathkeys by which the result is to be sorted
+ * 'relids' is the set of relations required by prepare_sort_from_pathkeys()
*/
static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys)
+make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
{
int numsortkeys;
AttrNumber *sortColIdx;
Oid *sortOperators;
Oid *collations;
bool *nullsFirst;
/* Compute sort column info, and adjust lefttree as needed */
lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
- NULL,
+ relids,
NULL,
false,
&numsortkeys,
&sortColIdx,
&sortOperators,
&collations,
&nullsFirst);
/* Now build the Sort node */
return make_sort(lefttree, numsortkeys,
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 193b2c9..10335d2 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1873,20 +1873,71 @@ adjust_appendrel_attrs(PlannerInfo *root, Node *node, AppendRelInfo *appinfo)
appinfo);
}
result = (Node *) newnode;
}
else
result = adjust_appendrel_attrs_mutator(node, &context);
return result;
}
+/*
+ * find_appendrelinfos_by_relids
+ * Find AppendRelInfo structures for all relations specified by relids.
+ */
+List *
+find_appendrelinfos_by_relids(PlannerInfo *root, Relids relids)
+{
+ ListCell *lc;
+ List *ari_list = NIL;
+
+ foreach (lc, root->append_rel_list)
+ {
+ AppendRelInfo *ari = lfirst(lc);
+
+ if (bms_is_member(ari->child_relid, relids))
+ ari_list = lappend(ari_list, ari);
+ }
+
+ Assert(list_length(ari_list) == bms_num_members(relids));
+ return ari_list;
+}
+
+/*
+ * adjust_partitionrel_attrs
+ * Replace the Var nodes in given node with the corresponding Var nodes
+ * of the child. Given list of AppendRelInfo nodes holds the mapping
+ * between parent and child Var nodes.
+ *
+ * While doing so, we need to make sure to translate non-expression information
+ * in nodes like RestrictInfo.
+ */
+Node *
+adjust_partitionrel_attrs(PlannerInfo *root, Node *node,
+ List *append_rel_infos)
+{
+ ListCell *lc;
+
+ /*
+ * TODO: Instead of copying and mutating the trees one child relation at a
+ * time, we should be able to do this en-masse for all the partitions
+ * involved.
+ */
+ foreach (lc, append_rel_infos)
+ {
+ AppendRelInfo *append_rel_info = lfirst(lc);
+ node = adjust_appendrel_attrs(root, node, append_rel_info);
+ }
+
+ return node;
+}
+
static Node *
adjust_appendrel_attrs_mutator(Node *node,
adjust_appendrel_attrs_context *context)
{
AppendRelInfo *appinfo = context->appinfo;
if (node == NULL)
return NULL;
if (IsA(node, Var))
{
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index abb7507..ba64329 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -16,21 +16,23 @@
#include <math.h>
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
+#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
+#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
#include "utils/selfuncs.h"
typedef enum
{
COSTS_EQUAL, /* path costs are fuzzily equal */
COSTS_BETTER1, /* first path is cheaper than second */
@@ -3202,10 +3204,160 @@ reparameterize_path(PlannerInfo *root, Path *path,
rel,
spath->subpath,
spath->path.pathkeys,
required_outer);
}
default:
break;
}
return NULL;
}
+
+/*
+ * reparameterize_path_for_child
+ * Given a path parameterized by the parent of the given relation,
+ * translate the path to be parameterized by the given child relation.
+ *
+ * The function creates a new path of the same type as the given path, but
+ * parameterized by the given child relation. If it can not reparameterize the
+ * path as required, it returns NULL.
+ */
+Path *
+reparameterize_path_for_child(PlannerInfo *root, Path *path,
+ RelOptInfo *child_rel)
+{
+ Path *new_path;
+ ParamPathInfo *new_ppi;
+ ParamPathInfo *old_ppi;
+ List *child_aris;
+
+ /*
+ * If the path is not parameterized by parent of the given relation, it
+ * doesn't need reparameterization.
+ */
+ if (!path->param_info ||
+ !bms_overlap(PATH_REQ_OUTER(path), child_rel->top_parent_relids))
+ return path;
+
+ switch (nodeTag(path))
+ {
+ case T_Path:
+ new_path = makeNode(Path);
+ memcpy(new_path, path, sizeof(Path));
+ break;
+
+ case T_HashPath:
+ new_path = (Path *) makeNode(HashPath);
+ memcpy(new_path, path, sizeof(HashPath));
+ break;
+
+ case T_MergePath:
+ new_path = (Path *) makeNode(MergePath);
+ memcpy(new_path, path, sizeof(MergePath));
+ break;
+
+ case T_NestPath:
+ new_path = (Path *) makeNode(NestPath);
+ memcpy(new_path, path, sizeof(NestPath));
+ break;
+
+ case T_IndexPath:
+ new_path = (Path *) makeNode(IndexPath);
+ memcpy(new_path, path, sizeof(IndexPath));
+ break;
+
+ case T_AppendPath:
+ new_path = (Path *) makeNode(AppendPath);
+ memcpy(new_path, path, sizeof(AppendPath));
+ break;
+
+ /*
+ * TODO:
+ * If this method of translation is fine add more path types here.
+ */
+
+ default:
+ /* Path type unsupported by this function. */
+ return NULL;
+ }
+
+ /*
+ * Gather AppendRelInfos of the base partition relations in the outer child
+ * relation. We need those for translating parent path to that of child by
+ * substituting parent Var nodes and relids with those of children.
+ */
+ child_aris = find_appendrelinfos_by_relids(root, child_rel->relids);
+
+ /* Adjust the parameterization information. */
+ old_ppi = new_path->param_info;
+ new_ppi = makeNode(ParamPathInfo);
+ new_ppi->ppi_req_outer = adjust_partition_relids(bms_copy(old_ppi->ppi_req_outer),
+ child_aris);
+ new_ppi->ppi_rows = old_ppi->ppi_rows;
+ new_ppi->ppi_clauses = (List *) adjust_partitionrel_attrs(root,
+ (Node *) old_ppi->ppi_clauses,
+ child_aris);
+
+ /* Adjust the path target. */
+ new_path->pathtarget = copy_pathtarget(new_path->pathtarget);
+ new_path->pathtarget->exprs = (List *) adjust_partitionrel_attrs(root,
+ (Node *) new_path->pathtarget->exprs,
+ child_aris);
+ new_path->param_info = new_ppi;
+
+ /*
+ * Change parameterization of sub paths recursively. Also carry out any
+ * pathtype specific adjustments.
+ */
+ switch (nodeTag(path))
+ {
+ case T_HashPath:
+ case T_MergePath:
+ case T_NestPath:
+ {
+ JoinPath *jpath = (JoinPath *)new_path;
+
+ jpath->outerjoinpath = reparameterize_path_for_child(root,
+ jpath->outerjoinpath,
+ child_rel);
+ jpath->innerjoinpath = reparameterize_path_for_child(root,
+ jpath->innerjoinpath,
+ child_rel);
+ jpath->joinrestrictinfo = (List *) adjust_partitionrel_attrs(root,
+ (Node *) jpath->joinrestrictinfo,
+ child_aris);
+ }
+ break;
+
+ case T_AppendPath:
+ {
+ AppendPath *apath = (AppendPath *)new_path;
+ List *subpaths = NIL;
+ ListCell *lc;
+
+ foreach (lc, apath->subpaths)
+ subpaths = lappend(subpaths,
+ reparameterize_path_for_child(root,
+ lfirst(lc),
+ child_rel));
+ apath->subpaths = subpaths;
+ }
+
+ case T_IndexPath:
+ {
+ IndexPath *ipath = (IndexPath *)new_path;
+
+ ipath->indexquals = (List *) adjust_partitionrel_attrs(root,
+ (Node *) ipath->indexquals,
+ child_aris);
+ ipath->indexquals = (List *) adjust_partitionrel_attrs(root,
+ (Node *) ipath->indexorderbys,
+ child_aris);
+ }
+
+ default:
+ /* Nothing to do. */
+ break;
+ }
+
+ return new_path;
+}
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index f8bfa4b..a3f27d1 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -404,20 +404,34 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
}
else
{
rel->serverid = InvalidOid;
rel->fdwroutine = NULL;
}
/* Collect info about relation's foreign keys, if relevant */
get_relation_foreign_keys(root, rel, relation);
+ /*
+ * Lookup partition scheme for the given relation. Only parent relations
+ * can be partitioned.
+ */
+ if (inhparent)
+ rel->part_scheme = find_partition_scheme(root, relation);
+ else
+ rel->part_scheme = NULL;
+
+ if (rel->part_scheme)
+ rel->partexprs = build_baserel_partition_keys(relation, rel->relid);
+ else
+ rel->partexprs = NULL;
+
heap_close(relation, NoLock);
/*
* Allow a plugin to editorialize on the info we obtained from the
* catalogs. Actions might include altering the assumed relation size,
* removing an index, or adding a hypothetical index to the indexlist.
*/
if (get_relation_info_hook)
(*get_relation_info_hook) (root, relationObjectId, inhparent, rel);
}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index deef560..bd0b956 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,29 +8,35 @@
*
*
* IDENTIFICATION
* src/backend/optimizer/util/relnode.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "miscadmin.h"
+#include "catalog/heap.h"
+#include "catalog/partition.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
+#include "nodes/makefuncs.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/placeholder.h"
#include "optimizer/plancat.h"
+#include "optimizer/prep.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
+#include "rewrite/rewriteManip.h"
#include "utils/hsearch.h"
+#include "utils/rel.h"
typedef struct JoinHashEntry
{
Relids join_relids; /* hash key --- MUST BE FIRST */
RelOptInfo *join_rel;
} JoinHashEntry;
static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *input_rel);
@@ -40,20 +46,25 @@ static List *build_joinrel_restrictlist(PlannerInfo *root,
RelOptInfo *inner_rel);
static void build_joinrel_joinlist(RelOptInfo *joinrel,
RelOptInfo *outer_rel,
RelOptInfo *inner_rel);
static List *subbuild_joinrel_restrictlist(RelOptInfo *joinrel,
List *joininfo_list,
List *new_restrictlist);
static List *subbuild_joinrel_joinlist(RelOptInfo *joinrel,
List *joininfo_list,
List *new_joininfo);
+static void set_foreign_rel_properties(RelOptInfo *joinrel,
+ RelOptInfo *outer_rel, RelOptInfo *inner_rel);
+static void build_joinrel_partition_info(RelOptInfo *joinrel,
+ RelOptInfo *outer_rel, RelOptInfo *inner_rel,
+ JoinType jointype);
/*
* setup_simple_rel_arrays
* Prepare the arrays we use for quickly accessing base relations.
*/
void
setup_simple_rel_arrays(PlannerInfo *root)
{
Index rti;
@@ -130,20 +141,24 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
rel->serverid = InvalidOid;
rel->userid = rte->checkAsUser;
rel->useridiscurrent = false;
rel->fdwroutine = NULL;
rel->fdw_private = NULL;
rel->baserestrictinfo = NIL;
rel->baserestrictcost.startup = 0;
rel->baserestrictcost.per_tuple = 0;
rel->joininfo = NIL;
rel->has_eclass_joins = false;
+ rel->part_scheme = NULL;
+ rel->partexprs = NULL;
+ rel->top_parent_relids = NULL;
+ rel->part_rels = NULL;
/* Check type of rtable entry */
switch (rte->rtekind)
{
case RTE_RELATION:
/* Table --- retrieve statistics from the system catalogs */
get_relation_info(root, rte->relid, rte->inh, rel);
break;
case RTE_SUBQUERY:
case RTE_FUNCTION:
@@ -307,20 +322,70 @@ find_join_rel(PlannerInfo *root, Relids relids)
if (bms_equal(rel->relids, relids))
return rel;
}
}
return NULL;
}
/*
+ * set_foreign_rel_properties
+ * Set up foreign-join fields if outer and inner relation are foreign
+ * tables (or joins) belonging to the same server and assigned to the same
+ * user to check access permissions as.
+ *
+ * In addition to an exact match of userid, we allow the case where one side
+ * has zero userid (implying current user) and the other side has explicit
+ * userid that happens to equal the current user; but in that case, pushdown of
+ * the join is only valid for the current user. The useridiscurrent field
+ * records whether we had to make such an assumption for this join or any
+ * sub-join.
+ *
+ * Otherwise these fields are left invalid, so GetForeignJoinPaths will not be
+ * called for the join relation.
+ *
+ */
+static void
+set_foreign_rel_properties(RelOptInfo *joinrel, RelOptInfo *outer_rel,
+ RelOptInfo *inner_rel)
+{
+ if (OidIsValid(outer_rel->serverid) &&
+ inner_rel->serverid == outer_rel->serverid)
+ {
+ if (inner_rel->userid == outer_rel->userid)
+ {
+ joinrel->serverid = outer_rel->serverid;
+ joinrel->userid = outer_rel->userid;
+ joinrel->useridiscurrent = outer_rel->useridiscurrent || inner_rel->useridiscurrent;
+ joinrel->fdwroutine = outer_rel->fdwroutine;
+ }
+ else if (!OidIsValid(inner_rel->userid) &&
+ outer_rel->userid == GetUserId())
+ {
+ joinrel->serverid = outer_rel->serverid;
+ joinrel->userid = outer_rel->userid;
+ joinrel->useridiscurrent = true;
+ joinrel->fdwroutine = outer_rel->fdwroutine;
+ }
+ else if (!OidIsValid(outer_rel->userid) &&
+ inner_rel->userid == GetUserId())
+ {
+ joinrel->serverid = outer_rel->serverid;
+ joinrel->userid = inner_rel->userid;
+ joinrel->useridiscurrent = true;
+ joinrel->fdwroutine = outer_rel->fdwroutine;
+ }
+ }
+}
+
+/*
* build_join_rel
* Returns relation entry corresponding to the union of two given rels,
* creating a new relation entry if none already exists.
*
* 'joinrelids' is the Relids set that uniquely identifies the join
* 'outer_rel' and 'inner_rel' are relation nodes for the relations to be
* joined
* 'sjinfo': join context info
* 'restrictlist_ptr': result variable. If not NULL, *restrictlist_ptr
* receives the list of RestrictInfo nodes that apply to this
@@ -356,21 +421,25 @@ build_join_rel(PlannerInfo *root,
joinrel,
outer_rel,
inner_rel);
return joinrel;
}
/*
* Nope, so make one.
*/
joinrel = makeNode(RelOptInfo);
+
+ Assert(!IS_OTHER_REL(outer_rel->reloptkind) &&
+ !IS_OTHER_REL(inner_rel->reloptkind));
joinrel->reloptkind = RELOPT_JOINREL;
+
joinrel->relids = bms_copy(joinrelids);
joinrel->rows = 0;
/* cheap startup cost is interesting iff not all tuples to be retrieved */
joinrel->consider_startup = (root->tuple_fraction > 0);
joinrel->consider_param_startup = false;
joinrel->consider_parallel = false;
joinrel->reltarget = create_empty_pathtarget();
joinrel->pathlist = NIL;
joinrel->ppilist = NIL;
joinrel->partial_pathlist = NIL;
@@ -402,61 +471,27 @@ build_join_rel(PlannerInfo *root,
joinrel->serverid = InvalidOid;
joinrel->userid = InvalidOid;
joinrel->useridiscurrent = false;
joinrel->fdwroutine = NULL;
joinrel->fdw_private = NULL;
joinrel->baserestrictinfo = NIL;
joinrel->baserestrictcost.startup = 0;
joinrel->baserestrictcost.per_tuple = 0;
joinrel->joininfo = NIL;
joinrel->has_eclass_joins = false;
+ joinrel->part_scheme = NULL;
+ joinrel->partexprs = NULL;
+ joinrel->top_parent_relids = NULL;
+ joinrel->part_rels = NULL;
- /*
- * Set up foreign-join fields if outer and inner relation are foreign
- * tables (or joins) belonging to the same server and assigned to the same
- * user to check access permissions as. In addition to an exact match of
- * userid, we allow the case where one side has zero userid (implying
- * current user) and the other side has explicit userid that happens to
- * equal the current user; but in that case, pushdown of the join is only
- * valid for the current user. The useridiscurrent field records whether
- * we had to make such an assumption for this join or any sub-join.
- *
- * Otherwise these fields are left invalid, so GetForeignJoinPaths will
- * not be called for the join relation.
- */
- if (OidIsValid(outer_rel->serverid) &&
- inner_rel->serverid == outer_rel->serverid)
- {
- if (inner_rel->userid == outer_rel->userid)
- {
- joinrel->serverid = outer_rel->serverid;
- joinrel->userid = outer_rel->userid;
- joinrel->useridiscurrent = outer_rel->useridiscurrent || inner_rel->useridiscurrent;
- joinrel->fdwroutine = outer_rel->fdwroutine;
- }
- else if (!OidIsValid(inner_rel->userid) &&
- outer_rel->userid == GetUserId())
- {
- joinrel->serverid = outer_rel->serverid;
- joinrel->userid = outer_rel->userid;
- joinrel->useridiscurrent = true;
- joinrel->fdwroutine = outer_rel->fdwroutine;
- }
- else if (!OidIsValid(outer_rel->userid) &&
- inner_rel->userid == GetUserId())
- {
- joinrel->serverid = outer_rel->serverid;
- joinrel->userid = inner_rel->userid;
- joinrel->useridiscurrent = true;
- joinrel->fdwroutine = outer_rel->fdwroutine;
- }
- }
+ /* Computer information relevant to the foreign relations. */
+ set_foreign_rel_properties(joinrel, outer_rel, inner_rel);
/*
* Create a new tlist containing just the vars that need to be output from
* this join (ie, are needed for higher joinclauses or final output).
*
* NOTE: the tlist order for a join rel will depend on which pair of outer
* and inner rels we first try to build it from. But the contents should
* be the same regardless.
*/
build_joinrel_tlist(root, joinrel, outer_rel);
@@ -468,20 +503,24 @@ build_join_rel(PlannerInfo *root,
* sets of any PlaceHolderVars computed here to direct_lateral_relids, so
* now we can finish computing that. This is much like the computation of
* the transitively-closed lateral_relids in min_join_parameterization,
* except that here we *do* have to consider the added PHVs.
*/
joinrel->direct_lateral_relids =
bms_del_members(joinrel->direct_lateral_relids, joinrel->relids);
if (bms_is_empty(joinrel->direct_lateral_relids))
joinrel->direct_lateral_relids = NULL;
+ /* Store the partition information. */
+ build_joinrel_partition_info(joinrel, outer_rel, inner_rel,
+ sjinfo->jointype);
+
/*
* Construct restrict and join clause lists for the new joinrel. (The
* caller might or might not need the restrictlist, but I need it anyway
* for set_joinrel_size_estimates().)
*/
restrictlist = build_joinrel_restrictlist(root, joinrel,
outer_rel, inner_rel);
if (restrictlist_ptr)
*restrictlist_ptr = restrictlist;
build_joinrel_joinlist(joinrel, outer_rel, inner_rel);
@@ -510,58 +549,170 @@ build_join_rel(PlannerInfo *root,
* assume this doesn't matter, because we should hit all the same baserels
* and joinclauses while building up to this joinrel no matter which we
* take; therefore, we should make the same decision here however we get
* here.
*/
if (inner_rel->consider_parallel && outer_rel->consider_parallel &&
is_parallel_safe(root, (Node *) restrictlist) &&
is_parallel_safe(root, (Node *) joinrel->reltarget->exprs))
joinrel->consider_parallel = true;
- /*
- * Add the joinrel to the query's joinrel list, and store it into the
- * auxiliary hashtable if there is one. NB: GEQO requires us to append
- * the new joinrel to the end of the list!
- */
- root->join_rel_list = lappend(root->join_rel_list, joinrel);
-
- if (root->join_rel_hash)
- {
- JoinHashEntry *hentry;
- bool found;
-
- hentry = (JoinHashEntry *) hash_search(root->join_rel_hash,
- &(joinrel->relids),
- HASH_ENTER,
- &found);
- Assert(!found);
- hentry->join_rel = joinrel;
- }
+ /* Add the joinrel to the query's PlannerInfo. */
+ add_join_rel(root, joinrel);
/*
* Also, if dynamic-programming join search is active, add the new joinrel
* to the appropriate sublist. Note: you might think the Assert on number
* of members should be for equality, but some of the level 1 rels might
* have been joinrels already, so we can only assert <=.
*/
if (root->join_rel_level)
{
Assert(root->join_cur_level > 0);
Assert(root->join_cur_level <= bms_num_members(joinrel->relids));
root->join_rel_level[root->join_cur_level] =
lappend(root->join_rel_level[root->join_cur_level], joinrel);
}
return joinrel;
}
/*
+ * build_partition_join_rel
+ * Returns relation entry corresponding to the of join between two given
+ * partition relations.
+ *
+ * 'outer_rel' and 'inner_rel' are the RelOptInfos of partition relations being
+ * joined.
+ * 'parent_joinrel' is the RelOptInfo representing the join between parent
+ * relations. Most of the members of new RelOptInfo are produced by
+ * translating corresponding members of this RelOptInfo.
+ * 'sjinfo': context info for join between the partitions
+ * 'restrictlist': list of RestrictInfo nodes that apply to this particular
+ * pair of joinable relations.
+ * 'join_aris': list of AppendRelInfo nodes for base child relations involved
+ * in this join.
+ */
+RelOptInfo *
+build_partition_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
+ RelOptInfo *inner_rel, RelOptInfo *parent_joinrel,
+ SpecialJoinInfo *sjinfo, List *restrictlist,
+ List *join_aris)
+{
+ List *tmp_exprs;
+ RelOptInfo *joinrel = makeNode(RelOptInfo);
+
+ joinrel->reloptkind = RELOPT_OTHER_JOINREL;
+ joinrel->relids = bms_union(outer_rel->relids, inner_rel->relids);
+ joinrel->rows = 0;
+ /* cheap startup cost is interesting iff not all tuples to be retrieved */
+ joinrel->consider_startup = (root->tuple_fraction > 0);
+ joinrel->consider_param_startup = false;
+ joinrel->consider_parallel = false;
+ joinrel->reltarget = create_empty_pathtarget();
+ joinrel->pathlist = NIL;
+ joinrel->ppilist = NIL;
+ joinrel->partial_pathlist = NIL;
+ joinrel->cheapest_startup_path = NULL;
+ joinrel->cheapest_total_path = NULL;
+ joinrel->cheapest_unique_path = NULL;
+ joinrel->cheapest_parameterized_paths = NIL;
+ joinrel->direct_lateral_relids = NULL;
+ joinrel->lateral_relids = NULL;
+ joinrel->relid = 0; /* indicates not a baserel */
+ joinrel->rtekind = RTE_JOIN;
+ joinrel->min_attr = 0;
+ joinrel->max_attr = 0;
+ joinrel->attr_needed = NULL;
+ joinrel->attr_widths = NULL;
+ joinrel->lateral_vars = NIL;
+ joinrel->lateral_referencers = NULL;
+ joinrel->indexlist = NIL;
+ joinrel->pages = 0;
+ joinrel->tuples = 0;
+ joinrel->allvisfrac = 0;
+ joinrel->subroot = NULL;
+ joinrel->subplan_params = NIL;
+ joinrel->serverid = InvalidOid;
+ joinrel->userid = InvalidOid;
+ joinrel->useridiscurrent = false;
+ joinrel->fdwroutine = NULL;
+ joinrel->fdw_private = NULL;
+ joinrel->baserestrictinfo = NIL;
+ joinrel->baserestrictcost.startup = 0;
+ joinrel->baserestrictcost.per_tuple = 0;
+ joinrel->joininfo = NIL;
+ joinrel->has_eclass_joins = false;
+ joinrel->part_scheme = NULL;
+ joinrel->partexprs = NULL;
+ joinrel->top_parent_relids = NULL;
+ joinrel->part_rels = NULL;
+
+
+ /* Only joins between other relations land here. */
+ Assert(IS_OTHER_REL(outer_rel->reloptkind) &&
+ IS_OTHER_REL(inner_rel->reloptkind));
+
+ joinrel->top_parent_relids = bms_union(outer_rel->top_parent_relids,
+ inner_rel->top_parent_relids);
+
+ /* Computer information relevant to foreign relations. */
+ set_foreign_rel_properties(joinrel, outer_rel, inner_rel);
+
+ /*
+ * Produce partition-wise joinrel's targetlist by translating the parent
+ * joinrel's targetlist. This will also include the required placeholder
+ * Vars.
+ */
+ joinrel->reltarget = copy_pathtarget(parent_joinrel->reltarget);
+ tmp_exprs = joinrel->reltarget->exprs;
+ joinrel->reltarget->exprs = (List *) adjust_partitionrel_attrs(root, (Node *) tmp_exprs,
+ join_aris);
+
+ /*
+ * Lateral relids directly referred in this relation will be same as that
+ * of the parent relation.
+ */
+ joinrel->direct_lateral_relids = (Relids) bms_copy(parent_joinrel->direct_lateral_relids);
+ joinrel->lateral_relids = (Relids) bms_copy(parent_joinrel->lateral_relids);
+
+ joinrel->joininfo = (List *) adjust_partitionrel_attrs(root,
+ (Node *) parent_joinrel->joininfo,
+ join_aris);
+
+ /*
+ * If the parent joinrel has pending equivalence classes, so does the
+ * child.
+ */
+ joinrel->has_eclass_joins = parent_joinrel->has_eclass_joins;
+
+ /* Is the join between partitions itself partitioned? */
+ build_joinrel_partition_info(joinrel, outer_rel, inner_rel, sjinfo->jointype);
+
+ /*
+ * Set estimates of the joinrel's size.
+ */
+ set_joinrel_size_estimates(root, joinrel, outer_rel, inner_rel, sjinfo,
+ restrictlist);
+
+ /* Child joinrel is parallel safe if parent is parallel safe. */
+ joinrel->consider_parallel = parent_joinrel->consider_parallel;
+
+ Assert(!find_join_rel(root, joinrel->relids));
+
+ /* Add the relation to the PlannerInfo. */
+ add_join_rel(root, joinrel);
+
+ return joinrel;
+}
+
+/*
* min_join_parameterization
*
* Determine the minimum possible parameterization of a joinrel, that is, the
* set of other rels it contains LATERAL references to. We save this value in
* the join's RelOptInfo. This function is split out of build_join_rel()
* because join_is_legal() needs the value to check a prospective join.
*/
Relids
min_join_parameterization(PlannerInfo *root,
Relids joinrelids,
@@ -1313,10 +1464,92 @@ get_appendrel_parampathinfo(RelOptInfo *appendrel, Relids required_outer)
/* Else build the ParamPathInfo */
ppi = makeNode(ParamPathInfo);
ppi->ppi_req_outer = required_outer;
ppi->ppi_rows = 0;
ppi->ppi_clauses = NIL;
appendrel->ppilist = lappend(appendrel->ppilist, ppi);
return ppi;
}
+
+/*
+ * Adds given join relation to the joinrel list and also to the hashtable if
+ * there is one.
+ */
+void
+add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
+{
+ /* GEQO requires us to append the new joinrel to the end of the list! */
+ root->join_rel_list = lappend(root->join_rel_list, joinrel);
+
+ /* store it into the auxiliary hashtable if there is one. */
+ if (root->join_rel_hash)
+ {
+ JoinHashEntry *hentry;
+ bool found;
+
+ hentry = (JoinHashEntry *) hash_search(root->join_rel_hash,
+ &(joinrel->relids),
+ HASH_ENTER,
+ &found);
+ Assert(!found);
+ hentry->join_rel = joinrel;
+ }
+}
+
+/*
+ * Set up partitioning scheme and partition keys for a join between given two
+ * relations.
+ */
+static void
+build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
+ RelOptInfo *inner_rel, JoinType jointype)
+{
+ int num_pks;
+ int cnt;
+
+ /*
+ * Nothing to do if
+ * a. partition-wise join is disabled.
+ * b. joining relations are not partitioned.
+ * c. partitioning schemes do not match.
+ */
+ if (!enable_partition_wise_join ||
+ !outer_rel->part_scheme || !inner_rel->part_scheme ||
+ outer_rel->part_scheme != inner_rel->part_scheme)
+ {
+ joinrel->part_scheme = NULL;
+ return;
+ }
+
+ /*
+ * This function will be called only once for each joinrel and thus we
+ * should not see partition scheme, partition keys and array for storing
+ * child relations set.
+ */
+ Assert(!joinrel->part_scheme && !joinrel->partexprs &&
+ !joinrel->part_rels);
+
+ num_pks = PartitionSchemeGetNumKeys(outer_rel->part_scheme);
+
+ /* Join relation is partitioned in the same way as the joining relation. */
+ joinrel->part_scheme = outer_rel->part_scheme;
+
+ /*
+ * Collect the partition key expressions. An OUTER join will produce rows
+ * where the partition key columns of inner side are NULL and may not fit
+ * the partitioning scheme with inner partition keys. Since two NULL values
+ * are not considered equal, an equi-join involing inner partition keys
+ * still prohibits cross-partition joins while joining with another
+ * similarly partitioned relation.
+ */
+ joinrel->partexprs = (List **) palloc0(sizeof(List *) * num_pks);
+ for (cnt = 0; cnt < num_pks; cnt++)
+ {
+ List *pkexpr = list_copy(outer_rel->partexprs[cnt]);
+
+ pkexpr = list_concat(pkexpr,
+ list_copy(inner_rel->partexprs[cnt]));
+ joinrel->partexprs[cnt] = pkexpr;
+ }
+}
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 56943f2..16b2eac 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -3405,21 +3405,23 @@ estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows,
else
{
/* not time to process varinfo2 yet */
newvarinfos = lcons(varinfo2, newvarinfos);
}
}
/*
* Sanity check --- don't divide by zero if empty relation.
*/
- Assert(rel->reloptkind == RELOPT_BASEREL);
+ Assert(rel->reloptkind == RELOPT_BASEREL ||
+ rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
+
if (rel->tuples > 0)
{
/*
* Clamp to size of rel, or size of rel / 10 if multiple Vars. The
* fudge factor is because the Vars are probably correlated but we
* don't know by how much. We should never clamp to less than the
* largest ndistinct value for any of the Vars, though, since
* there will surely be at least that many groups.
*/
double clamp = rel->tuples;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index ce4eef9..edc7e58 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -870,20 +870,29 @@ static struct config_bool ConfigureNamesBool[] =
},
{
{"enable_hashjoin", PGC_USERSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of hash join plans."),
NULL
},
&enable_hashjoin,
true,
NULL, NULL, NULL
},
+ {
+ {"enable_partition_wise_join", PGC_USERSET, QUERY_TUNING_METHOD,
+ gettext_noop("Enables partition-wise join."),
+ NULL
+ },
+ &enable_partition_wise_join,
+ true,
+ NULL, NULL, NULL
+ },
{
{"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
gettext_noop("Enables genetic query optimization."),
gettext_noop("This algorithm attempts to do planning without "
"exhaustive searching.")
},
&enable_geqo,
true,
NULL, NULL, NULL
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index 81a4b91..3c59619 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -41,20 +41,24 @@ typedef struct BoundCollectionData *BoundCollection;
*/
typedef struct PartitionDescData
{
int nparts; /* Number of partitions */
Oid *oids; /* OIDs of partitions */
BoundCollection bounds; /* collection of list or range bounds */
} PartitionDescData;
typedef struct PartitionDescData *PartitionDesc;
typedef struct PartitionTreeNodeData *PartitionTreeNode;
+typedef struct PartitionSchemeData *PartitionScheme;
+
+/* Include here to avoid circular dependency with relation.h. */
+struct PlannerInfo;
/* relcache support for partition key information */
extern void RelationBuildPartitionKey(Relation relation);
/* Partition key inquiry functions */
extern int get_partition_key_strategy(PartitionKey key);
extern int get_partition_key_natts(PartitionKey key);
extern List *get_partition_key_exprs(PartitionKey key);
/* Partition key inquiry functions - for a given column */
@@ -77,11 +81,18 @@ extern List *get_qual_from_partbound(Relation rel, Relation parent, Node *bound)
extern List *RelationGetPartitionQual(Relation rel, bool recurse);
/* For tuple routing */
extern PartitionTreeNode RelationGetPartitionTreeNode(Relation rel);
extern List *get_leaf_partition_oids_v2(PartitionTreeNode ptnode);
extern int get_partition_for_tuple(PartitionTreeNode ptnode,
TupleTableSlot *slot,
EState *estate,
Oid *failed_at);
+extern List **build_baserel_partition_keys(Relation relation, Index varno);
+extern PartitionScheme find_partition_scheme(struct PlannerInfo *root,
+ Relation rel);
+extern int PartitionSchemeGetNumParts(PartitionScheme part_scheme);
+extern int PartitionSchemeGetNumKeys(PartitionScheme part_scheme);
+extern Oid *PartitionDescGetPartOids(PartitionDesc part_desc);
+
#endif /* PARTITION_H */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 3a1255a..b00eedc 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -8,20 +8,21 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/nodes/relation.h
*
*-------------------------------------------------------------------------
*/
#ifndef RELATION_H
#define RELATION_H
#include "access/sdir.h"
+#include "catalog/partition.h"
#include "lib/stringinfo.h"
#include "nodes/params.h"
#include "nodes/parsenodes.h"
#include "storage/block.h"
/*
* Relids
* Set of relation identifiers (indexes into the rangetable).
*/
@@ -256,20 +257,23 @@ typedef struct PlannerInfo
List *query_pathkeys; /* desired pathkeys for query_planner() */
List *group_pathkeys; /* groupClause pathkeys, if any */
List *window_pathkeys; /* pathkeys of bottom window, if any */
List *distinct_pathkeys; /* distinctClause pathkeys, if any */
List *sort_pathkeys; /* sortClause pathkeys, if any */
List *initial_rels; /* RelOptInfos we are now trying to join */
+ List *part_schemes; /* Canonicalised partition schemes
+ * used in the query. */
+
/* Use fetch_upper_rel() to get any particular upper rel */
List *upper_rels[UPPERREL_FINAL + 1]; /* upper-rel RelOptInfos */
/* Result tlists chosen by grouping_planner for upper-stage processing */
struct PathTarget *upper_targets[UPPERREL_FINAL + 1];
/*
* grouping_planner passes back its final processed targetlist here, for
* use in relabeling the topmost tlist of the finished Plan.
*/
@@ -345,20 +349,26 @@ typedef struct PlannerInfo
* is present in the query join tree but the members are not. The member
* RTEs and otherrels are used to plan the scans of the individual tables or
* subqueries of the append set; then the parent baserel is given Append
* and/or MergeAppend paths comprising the best paths for the individual
* member rels. (See comments for AppendRelInfo for more information.)
*
* At one time we also made otherrels to represent join RTEs, for use in
* handling join alias Vars. Currently this is not needed because all join
* alias Vars are expanded to non-aliased form during preprocess_expression.
*
+ * We also have relations representing pair-wise joins between partitions of
+ * partitioned tables. These relations are not added to join_rel_level lists
+ * as they are not joined directly by the dynamic programming algorithm.
+ * Adding these two join_rel_level list also means that top level list has more
+ * than one join relation, which is symantically incorrect.
+ *
* There is also a RelOptKind for "upper" relations, which are RelOptInfos
* that describe post-scan/join processing steps, such as aggregation.
* Many of the fields in these RelOptInfos are meaningless, but their Path
* fields always hold Paths showing ways to do that processing step.
*
* Lastly, there is a RelOptKind for "dead" relations, which are base rels
* that we have proven we don't need to join after all.
*
* Parts of this data structure are specific to various scan and join
* mechanisms. It didn't seem worth creating new node types for them.
@@ -464,24 +474,29 @@ typedef struct PlannerInfo
* We store baserestrictcost in the RelOptInfo (for base relations) because
* we know we will need it at least once (to price the sequential scan)
* and may need it multiple times to price index scans.
*----------
*/
typedef enum RelOptKind
{
RELOPT_BASEREL,
RELOPT_JOINREL,
RELOPT_OTHER_MEMBER_REL,
+ RELOPT_OTHER_JOINREL,
RELOPT_UPPER_REL,
RELOPT_DEADREL
} RelOptKind;
+#define IS_OTHER_REL(reloptkind) \
+ ((reloptkind) == RELOPT_OTHER_MEMBER_REL || \
+ (reloptkind) == RELOPT_OTHER_JOINREL)
+
typedef struct RelOptInfo
{
NodeTag type;
RelOptKind reloptkind;
/* all relations included in this RelOptInfo */
Relids relids; /* set of base relids (rangetable indexes) */
/* size estimates generated by planner */
@@ -535,20 +550,37 @@ typedef struct RelOptInfo
struct FdwRoutine *fdwroutine;
void *fdw_private;
/* used by various scans and joins: */
List *baserestrictinfo; /* RestrictInfo structures (if base
* rel) */
QualCost baserestrictcost; /* cost of evaluating the above */
List *joininfo; /* RestrictInfo structures for join clauses
* involving this rel */
bool has_eclass_joins; /* T means joininfo is incomplete */
+
+ /* For partitioned relations, joins or base relations. NULL otherwise. */
+ PartitionScheme part_scheme; /* Partitioning scheme. */
+ struct RelOptInfo **part_rels; /* Array of RelOptInfos of partitions,
+ * stored in the same order as bounds
+ * or lists in PartitionScheme.
+ */
+ List **partexprs; /* Array of list of partition key
+ * expressions. For base relations
+ * these are one element lists. For
+ * join there can be as many elements
+ * as there are number of joining
+ * relations.
+ */
+
+ /* Set only for "other" base or join relations. */
+ Relids top_parent_relids;
} RelOptInfo;
/*
* IndexOptInfo
* Per-index information for planning/optimization
*
* indexkeys[], indexcollations[], opfamily[], and opcintype[]
* each have ncolumns entries.
*
* sortopfamily[], reverse_sort[], and nulls_first[] likewise have
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 2a4df2f..1069726 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -59,20 +59,21 @@ extern bool enable_seqscan;
extern bool enable_indexscan;
extern bool enable_indexonlyscan;
extern bool enable_bitmapscan;
extern bool enable_tidscan;
extern bool enable_sort;
extern bool enable_hashagg;
extern bool enable_nestloop;
extern bool enable_material;
extern bool enable_mergejoin;
extern bool enable_hashjoin;
+extern bool enable_partition_wise_join;
extern int constraint_exclusion;
extern double clamp_row_est(double nrows);
extern double index_pages_fetched(double tuples_fetched, BlockNumber pages,
double index_pages, PlannerInfo *root);
extern void cost_seqscan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
ParamPathInfo *param_info);
extern void cost_samplescan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
ParamPathInfo *param_info);
extern void cost_index(IndexPath *path, PlannerInfo *root,
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 71d9154..2ab31d3 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -222,20 +222,22 @@ extern ModifyTablePath *create_modifytable_path(PlannerInfo *root,
List *rowMarks, OnConflictExpr *onconflict,
int epqParam);
extern LimitPath *create_limit_path(PlannerInfo *root, RelOptInfo *rel,
Path *subpath,
Node *limitOffset, Node *limitCount,
int64 offset_est, int64 count_est);
extern Path *reparameterize_path(PlannerInfo *root, Path *path,
Relids required_outer,
double loop_count);
+extern Path *reparameterize_path_for_child(PlannerInfo *root, Path *path,
+ RelOptInfo *child_rel);
/*
* prototypes for relnode.c
*/
extern void setup_simple_rel_arrays(PlannerInfo *root);
extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
RelOptKind reloptkind);
extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids);
extern RelOptInfo *build_join_rel(PlannerInfo *root,
@@ -260,12 +262,17 @@ extern ParamPathInfo *get_baserel_parampathinfo(PlannerInfo *root,
Relids required_outer);
extern ParamPathInfo *get_joinrel_parampathinfo(PlannerInfo *root,
RelOptInfo *joinrel,
Path *outer_path,
Path *inner_path,
SpecialJoinInfo *sjinfo,
Relids required_outer,
List **restrict_clauses);
extern ParamPathInfo *get_appendrel_parampathinfo(RelOptInfo *appendrel,
Relids required_outer);
+extern RelOptInfo *build_partition_join_rel(PlannerInfo *root,
+ RelOptInfo *outer_rel, RelOptInfo *inner_rel,
+ RelOptInfo *parent_joinrel, SpecialJoinInfo *sjinfo,
+ List *restrictlist, List *join_aris);
+extern void add_join_rel(PlannerInfo *root, RelOptInfo *joinrel);
#endif /* PATHNODE_H */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 44abe83..a799428 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -212,11 +212,13 @@ extern List *make_inner_pathkeys_for_merge(PlannerInfo *root,
List *mergeclauses,
List *outer_pathkeys);
extern List *truncate_useless_pathkeys(PlannerInfo *root,
RelOptInfo *rel,
List *pathkeys);
extern bool has_useful_pathkeys(PlannerInfo *root, RelOptInfo *rel);
extern PathKey *make_canonical_pathkey(PlannerInfo *root,
EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first);
+extern Relids adjust_partition_relids(Relids relids, List *append_rel_infos);
+
#endif /* PATHS_H */
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
index fb35b68..7b149c3 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -21,20 +21,23 @@
/*
* prototypes for prepjointree.c
*/
extern void pull_up_sublinks(PlannerInfo *root);
extern void inline_set_returning_functions(PlannerInfo *root);
extern void pull_up_subqueries(PlannerInfo *root);
extern void flatten_simple_union_all(PlannerInfo *root);
extern void reduce_outer_joins(PlannerInfo *root);
extern Relids get_relids_in_jointree(Node *jtnode, bool include_joins);
extern Relids get_relids_for_join(PlannerInfo *root, int joinrelid);
+extern Node *adjust_partitionrel_attrs(PlannerInfo *root, Node *node,
+ List *append_rel_infos);
+extern List *find_appendrelinfos_by_relids(PlannerInfo *root, Relids relids);
/*
* prototypes for prepqual.c
*/
extern Node *negate_clause(Node *node);
extern Expr *canonicalize_qual(Expr *qual);
/*
* prototypes for prepsecurity.c
*/
diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out
new file mode 100644
index 0000000..0eecade
--- /dev/null
+++ b/src/test/regress/expected/partition_join.out
@@ -0,0 +1,7031 @@
+--
+-- PARTITION_JOIN
+-- Test partition-wise join between partitioned tables
+--
+--
+-- partitioned by a single column
+--
+CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES START (0) END (250);
+CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES START (500) END (600);
+CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES START (250) END (500);
+INSERT INTO prt1 SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1;
+ANALYZE prt1_p1;
+ANALYZE prt1_p2;
+ANALYZE prt1_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1 AS SELECT * FROM prt1;
+CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES START (0) END (250);
+CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES START (250) END (500);
+CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES START (500) END (600);
+INSERT INTO prt2 SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+ANALYZE prt2;
+ANALYZE prt2_p1;
+ANALYZE prt2_p2;
+ANALYZE prt2_p3;
+CREATE TABLE uprt2 AS SELECT * FROM prt2;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ QUERY PLAN
+---------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on public.prt2_p1 t2
+ Output: t2.b, t2.c
+ -> Hash
+ Output: t1.a, t1.c
+ -> Seq Scan on public.prt1_p1 t1
+ Output: t1.a, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+ Hash Cond: (t2_1.b = t1_2.a)
+ -> Seq Scan on public.prt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Hash
+ Output: t1_2.a, t1_2.c
+ -> Seq Scan on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Hash Join
+ Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+ Hash Cond: (t2_2.b = t1_1.a)
+ -> Seq Scan on public.prt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Hash
+ Output: t1_1.a, t1_1.c
+ -> Seq Scan on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ a | c | b | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150
+ 300 | 0300 | 300 | 0300
+ 450 | 0450 | 450 | 0450
+(4 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1, uprt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ QUERY PLAN
+---------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Append
+ -> Hash Right Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on public.prt2_p1 t2
+ Output: t2.b, t2.c
+ -> Hash
+ Output: t1.a, t1.c
+ -> Seq Scan on public.prt1_p1 t1
+ Output: t1.a, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+ Hash Cond: (t2_1.b = t1_2.a)
+ -> Seq Scan on public.prt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Hash
+ Output: t1_2.a, t1_2.c
+ -> Seq Scan on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+ Hash Cond: (t2_2.b = t1_1.a)
+ -> Seq Scan on public.prt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Hash
+ Output: t1_1.a, t1_1.c
+ -> Seq Scan on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ 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 | |
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 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 | |
+(12 rows)
+
+EXPLAIN (VERBOSE, 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.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+ QUERY PLAN
+---------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c
+ -> Append
+ -> Hash Right Join
+ Output: t2.b, t2.c, t1.a, t1.c
+ Hash Cond: (t1.a = t2.b)
+ -> Seq Scan on public.prt1_p1 t1
+ Output: t1.a, t1.c
+ -> Hash
+ Output: t2.b, t2.c
+ -> Seq Scan on public.prt2_p1 t2
+ Output: t2.b, t2.c
+ Filter: ((t2.b % 25) = 0)
+ -> Hash Right Join
+ Output: t2_1.b, t2_1.c, t1_2.a, t1_2.c
+ Hash Cond: (t1_2.a = t2_1.b)
+ -> Seq Scan on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.c
+ -> Hash
+ Output: t2_1.b, t2_1.c
+ -> Seq Scan on public.prt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ Filter: ((t2_1.b % 25) = 0)
+ -> Hash Right Join
+ Output: t2_2.b, t2_2.c, t1_1.a, t1_1.c
+ Hash Cond: (t1_1.a = t2_2.b)
+ -> Seq Scan on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.c
+ -> Hash
+ Output: t2_2.b, t2_2.c
+ -> Seq Scan on public.prt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ Filter: ((t2_2.b % 25) = 0)
+(36 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+ 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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------
+ Sort
+ Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c
+ Sort Key: prt1_p1.a, prt2_p1.b
+ -> Append
+ -> Hash Full Join
+ Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c
+ Hash Cond: (prt1_p1.a = prt2_p1.b)
+ -> Seq Scan on public.prt1_p1
+ Output: prt1_p1.a, prt1_p1.c
+ Filter: ((prt1_p1.a % 25) = 0)
+ -> Hash
+ Output: prt2_p1.b, prt2_p1.c
+ -> Seq Scan on public.prt2_p1
+ Output: prt2_p1.b, prt2_p1.c
+ Filter: ((prt2_p1.b % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c
+ Hash Cond: (prt1_p2.a = prt2_p2.b)
+ -> Seq Scan on public.prt1_p2
+ Output: prt1_p2.a, prt1_p2.c
+ Filter: ((prt1_p2.a % 25) = 0)
+ -> Hash
+ Output: prt2_p2.b, prt2_p2.c
+ -> Seq Scan on public.prt2_p2
+ Output: prt2_p2.b, prt2_p2.c
+ Filter: ((prt2_p2.b % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_p3.a, prt1_p3.c, prt2_p3.b, prt2_p3.c
+ Hash Cond: (prt1_p3.a = prt2_p3.b)
+ -> Seq Scan on public.prt1_p3
+ Output: prt1_p3.a, prt1_p3.c
+ Filter: ((prt1_p3.a % 25) = 0)
+ -> Hash
+ Output: prt2_p3.b, prt2_p3.c
+ -> Seq Scan on public.prt2_p3
+ Output: prt2_p3.b, prt2_p3.c
+ Filter: ((prt2_p3.b % 25) = 0)
+(37 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ 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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 t2 WHERE t2.b % 25 = 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)
+
+-- Cases with non-nullable expressions in subquery results;
+-- make sure these go to null as expected
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------
+ Sort
+ Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c
+ Sort Key: prt1_p1.a, prt2_p1.b
+ -> Append
+ -> Hash Full Join
+ Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c
+ Hash Cond: (prt1_p1.a = prt2_p1.b)
+ Filter: (((50) = prt1_p1.b) OR ((75) = prt2_p1.b))
+ -> Seq Scan on public.prt1_p1
+ Output: prt1_p1.a, prt1_p1.c, prt1_p1.b, 50
+ Filter: ((prt1_p1.a % 25) = 0)
+ -> Hash
+ Output: prt2_p1.b, prt2_p1.c, (75)
+ -> Seq Scan on public.prt2_p1
+ Output: prt2_p1.b, prt2_p1.c, 75
+ Filter: ((prt2_p1.b % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c
+ Hash Cond: (prt1_p2.a = prt2_p2.b)
+ Filter: (((50) = prt1_p2.b) OR ((75) = prt2_p2.b))
+ -> Seq Scan on public.prt1_p2
+ Output: prt1_p2.a, prt1_p2.c, prt1_p2.b, 50
+ Filter: ((prt1_p2.a % 25) = 0)
+ -> Hash
+ Output: prt2_p2.b, prt2_p2.c, (75)
+ -> Seq Scan on public.prt2_p2
+ Output: prt2_p2.b, prt2_p2.c, 75
+ Filter: ((prt2_p2.b % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_p3.a, prt1_p3.c, prt2_p3.b, prt2_p3.c
+ Hash Cond: (prt1_p3.a = prt2_p3.b)
+ Filter: (((50) = prt1_p3.b) OR ((75) = prt2_p3.b))
+ -> Seq Scan on public.prt1_p3
+ Output: prt1_p3.a, prt1_p3.c, prt1_p3.b, 50
+ Filter: ((prt1_p3.a % 25) = 0)
+ -> Hash
+ Output: prt2_p3.b, prt2_p3.c, (75)
+ -> Seq Scan on public.prt2_p3
+ Output: prt2_p3.b, prt2_p3.c, 75
+ Filter: ((prt2_p3.b % 25) = 0)
+(40 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ a | c | b | c
+----+------+----+------
+ 50 | 0050 | |
+ | | 75 | 0075
+(2 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b;
+ a | c | b | c
+----+------+----+------
+ 50 | 0050 | |
+ | | 75 | 0075
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------------
+ Sort
+ Output: prt1_p1.a, prt1_p1.c, (25), prt2_p1.b, prt2_p1.c, (50)
+ Sort Key: prt1_p1.a, prt2_p1.b
+ -> Result
+ Output: prt1_p1.a, prt1_p1.c, (25), prt2_p1.b, prt2_p1.c, (50)
+ -> Append
+ -> Hash Full Join
+ Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c, (25), (50)
+ Hash Cond: (prt1_p1.a = prt2_p1.b)
+ -> Seq Scan on public.prt1_p1
+ Output: prt1_p1.a, prt1_p1.c, 25
+ Filter: ((prt1_p1.a % 25) = 0)
+ -> Hash
+ Output: prt2_p1.b, prt2_p1.c, (50)
+ -> Seq Scan on public.prt2_p1
+ Output: prt2_p1.b, prt2_p1.c, 50
+ Filter: ((prt2_p1.b % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c, (25), (50)
+ Hash Cond: (prt1_p2.a = prt2_p2.b)
+ -> Seq Scan on public.prt1_p2
+ Output: prt1_p2.a, prt1_p2.c, 25
+ Filter: ((prt1_p2.a % 25) = 0)
+ -> Hash
+ Output: prt2_p2.b, prt2_p2.c, (50)
+ -> Seq Scan on public.prt2_p2
+ Output: prt2_p2.b, prt2_p2.c, 50
+ Filter: ((prt2_p2.b % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_p3.a, prt1_p3.c, prt2_p3.b, prt2_p3.c, (25), (50)
+ Hash Cond: (prt1_p3.a = prt2_p3.b)
+ -> Seq Scan on public.prt1_p3
+ Output: prt1_p3.a, prt1_p3.c, 25
+ Filter: ((prt1_p3.a % 25) = 0)
+ -> Hash
+ Output: prt2_p3.b, prt2_p3.c, (50)
+ -> Seq Scan on public.prt2_p3
+ Output: prt2_p3.b, prt2_p3.c, 50
+ Filter: ((prt2_p3.b % 25) = 0)
+(39 rows)
+
+SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ 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)
+
+SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM uprt2 WHERE uprt2.b % 25 = 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)
+
+-- Join with pruned partitions from joining relations
+EXPLAIN (VERBOSE, COSTS OFF)
+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.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on public.prt2_p2 t2
+ Output: t2.b, t2.c
+ Filter: (t2.b > 250)
+ -> Hash
+ Output: t1.a, t1.c
+ -> Seq Scan on public.prt1_p2 t1
+ Output: t1.a, t1.c
+ Filter: ((t1.a < 450) AND ((t1.a % 25) = 0))
+(15 rows)
+
+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.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ a | c | b | c
+-----+------+-----+------
+ 300 | 0300 | 300 | 0300
+(1 row)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1, uprt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+ a | c | b | c
+-----+------+-----+------
+ 300 | 0300 | 300 | 0300
+(1 row)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+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.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ QUERY PLAN
+----------------------------------------------------------------------------------
+ Sort
+ Output: prt1_p1.a, prt1_p1.c, b, c
+ Sort Key: prt1_p1.a, b
+ -> Append
+ -> Nested Loop Left Join
+ Output: prt1_p1.a, prt1_p1.c, b, c
+ Join Filter: (prt1_p1.a = b)
+ -> Seq Scan on public.prt1_p1
+ Output: prt1_p1.a, prt1_p1.c
+ Filter: ((prt1_p1.a < 450) AND ((prt1_p1.a % 25) = 0))
+ -> Result
+ Output: b, c
+ One-Time Filter: false
+ -> Hash Right Join
+ Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c
+ Hash Cond: (prt2_p2.b = prt1_p2.a)
+ -> Seq Scan on public.prt2_p2
+ Output: prt2_p2.b, prt2_p2.c
+ Filter: (prt2_p2.b > 250)
+ -> Hash
+ Output: prt1_p2.a, prt1_p2.c
+ -> Seq Scan on public.prt1_p2
+ Output: prt1_p2.a, prt1_p2.c
+ Filter: ((prt1_p2.a < 450) AND ((prt1_p2.a % 25) = 0))
+(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.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ 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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM uprt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.a % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 5) and (b 4) is considered for partition-wise join.
+ QUERY PLAN
+----------------------------------------------------------------------------------------
+ Sort
+ Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c
+ Sort Key: prt1_p2.a, prt2_p2.b
+ -> Result
+ Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c
+ -> Append
+ -> Hash Right Join
+ Output: prt2_p2.b, prt2_p2.c, prt1_p2.a, prt1_p2.c
+ Hash Cond: (prt1_p2.a = prt2_p2.b)
+ -> Seq Scan on public.prt1_p2
+ Output: prt1_p2.a, prt1_p2.c
+ Filter: (prt1_p2.a < 450)
+ -> Hash
+ Output: prt2_p2.b, prt2_p2.c
+ -> Seq Scan on public.prt2_p2
+ Output: prt2_p2.b, prt2_p2.c
+ Filter: ((prt2_p2.b > 250) AND ((prt2_p2.a % 25) = 0))
+ -> Nested Loop Left Join
+ Output: prt2_p3.b, prt2_p3.c, a, c
+ Join Filter: (a = prt2_p3.b)
+ -> Seq Scan on public.prt2_p3
+ Output: prt2_p3.b, prt2_p3.c
+ Filter: ((prt2_p3.b > 250) AND ((prt2_p3.a % 25) = 0))
+ -> Result
+ Output: a, c
+ One-Time Filter: false
+(26 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 5) and (b 4) is considered for partition-wise join.
+ a | c | b | c
+-----+------+-----+------
+ 300 | 0300 | 300 | 0300
+ | | 375 | 0375
+ | | 450 | 0450
+ | | 525 | 0525
+(4 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM uprt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+ a | c | b | c
+-----+------+-----+------
+ 300 | 0300 | 300 | 0300
+ | | 375 | 0375
+ | | 450 | 0450
+ | | 525 | 0525
+(4 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ QUERY PLAN
+----------------------------------------------------------------------------------
+ Sort
+ Output: prt1_p1.a, prt1_p1.c, b, c
+ Sort Key: prt1_p1.a, b
+ -> Append
+ -> Hash Full Join
+ Output: prt1_p1.a, prt1_p1.c, b, c
+ Hash Cond: (prt1_p1.a = b)
+ -> Seq Scan on public.prt1_p1
+ Output: prt1_p1.a, prt1_p1.c
+ Filter: ((prt1_p1.a < 450) AND ((prt1_p1.a % 25) = 0))
+ -> Hash
+ Output: b, c
+ -> Result
+ Output: b, c
+ One-Time Filter: false
+ -> Hash Full Join
+ Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c
+ Hash Cond: (prt1_p2.a = prt2_p2.b)
+ -> Seq Scan on public.prt1_p2
+ Output: prt1_p2.a, prt1_p2.c
+ Filter: ((prt1_p2.a < 450) AND ((prt1_p2.a % 25) = 0))
+ -> Hash
+ Output: prt2_p2.b, prt2_p2.c
+ -> Seq Scan on public.prt2_p2
+ Output: prt2_p2.b, prt2_p2.c
+ Filter: ((prt2_p2.b > 250) AND ((prt2_p2.b % 25) = 0))
+ -> Hash Full Join
+ Output: a, c, prt2_p3.b, prt2_p3.c
+ Hash Cond: (prt2_p3.b = a)
+ -> Seq Scan on public.prt2_p3
+ Output: prt2_p3.b, prt2_p3.c
+ Filter: ((prt2_p3.b > 250) AND ((prt2_p3.b % 25) = 0))
+ -> Hash
+ Output: a, c
+ -> Result
+ Output: a, c
+ One-Time Filter: false
+(37 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ 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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b 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)
+
+-- joins where one of the relations is proven empty
+EXPLAIN (VERBOSE, 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;
+ QUERY PLAN
+----------------------------------
+ Result
+ Output: t1.a, t1.c, t2.b, t2.c
+ One-Time Filter: false
+(3 rows)
+
+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;
+ a | c | b | c
+---+---+---+---
+(0 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 LEFT JOIN prt2 t2 ON t1.a = t2.b;
+NOTICE: join between relations (b 4) and (b 2) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------
+ Result
+ Output: prt1.a, prt1.c, t2.b, t2.c
+ One-Time Filter: false
+(3 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 LEFT JOIN prt2 t2 ON t1.a = t2.b;
+NOTICE: join between relations (b 4) and (b 2) is considered for partition-wise join.
+ a | c | b | c
+---+---+---+---
+(0 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 4) is considered for partition-wise join.
+ QUERY PLAN
+---------------------------------------------------
+ Sort
+ Output: a, c, t2.b, t2.c
+ Sort Key: a, t2.b
+ -> Hash Left Join
+ Output: a, c, t2.b, t2.c
+ Hash Cond: (t2.b = a)
+ -> Append
+ -> Seq Scan on public.prt2 t2
+ Output: t2.b, t2.c
+ Filter: ((t2.a % 25) = 0)
+ -> Seq Scan on public.prt2_p1 t2_1
+ Output: t2_1.b, t2_1.c
+ Filter: ((t2_1.a % 25) = 0)
+ -> Seq Scan on public.prt2_p2 t2_2
+ Output: t2_2.b, t2_2.c
+ Filter: ((t2_2.a % 25) = 0)
+ -> Seq Scan on public.prt2_p3 t2_3
+ Output: t2_3.b, t2_3.c
+ Filter: ((t2_3.a % 25) = 0)
+ -> Hash
+ Output: a, c
+ -> Result
+ Output: a, c
+ One-Time Filter: false
+(24 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 4) is considered for partition-wise join.
+ a | c | b | c
+---+---+-----+------
+ | | 0 | 0000
+ | | 75 | 0075
+ | | 150 | 0150
+ | | 225 | 0225
+ | | 300 | 0300
+ | | 375 | 0375
+ | | 450 | 0450
+ | | 525 | 0525
+(8 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a = 1 AND a = 2) t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+ a | c | b | c
+---+---+-----+------
+ | | 0 | 0000
+ | | 75 | 0075
+ | | 150 | 0150
+ | | 225 | 0225
+ | | 300 | 0300
+ | | 375 | 0375
+ | | 450 | 0450
+ | | 525 | 0525
+(8 rows)
+
+EXPLAIN (VERBOSE, 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 % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 4) is considered for partition-wise join.
+ QUERY PLAN
+---------------------------------------------------
+ Sort
+ Output: a, c, t2.b, t2.c
+ Sort Key: a, t2.b
+ -> Hash Left Join
+ Output: a, c, t2.b, t2.c
+ Hash Cond: (t2.b = a)
+ -> Append
+ -> Seq Scan on public.prt2 t2
+ Output: t2.b, t2.c
+ Filter: ((t2.a % 25) = 0)
+ -> Seq Scan on public.prt2_p1 t2_1
+ Output: t2_1.b, t2_1.c
+ Filter: ((t2_1.a % 25) = 0)
+ -> Seq Scan on public.prt2_p2 t2_2
+ Output: t2_2.b, t2_2.c
+ Filter: ((t2_2.a % 25) = 0)
+ -> Seq Scan on public.prt2_p3 t2_3
+ Output: t2_3.b, t2_3.c
+ Filter: ((t2_3.a % 25) = 0)
+ -> Hash
+ Output: a, c
+ -> Result
+ Output: a, c
+ One-Time Filter: false
+(24 rows)
+
+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 % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 4) is considered for partition-wise join.
+ a | c | b | c
+---+---+-----+------
+ | | 0 | 0000
+ | | 75 | 0075
+ | | 150 | 0150
+ | | 225 | 0225
+ | | 300 | 0300
+ | | 375 | 0375
+ | | 450 | 0450
+ | | 525 | 0525
+(8 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a = 1 AND a = 2) t1 FULL JOIN uprt2 t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+ a | c | b | c
+---+---+-----+------
+ | | 0 | 0000
+ | | 75 | 0075
+ | | 150 | 0150
+ | | 225 | 0225
+ | | 300 | 0300
+ | | 375 | 0375
+ | | 450 | 0450
+ | | 525 | 0525
+(8 rows)
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 1) and (b 3) is considered for partition-wise join.
+ QUERY PLAN
+---------------------------------------------------------
+ Sort
+ Output: t1.a, t1.b, t1.c
+ Sort Key: t1.a
+ -> Append
+ -> Nested Loop Semi Join
+ Output: t1.a, t1.b, t1.c
+ Join Filter: (t1.a = t1_3.b)
+ -> Seq Scan on public.prt1_p1 t1
+ Output: t1.a, t1.b, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Materialize
+ Output: t1_3.b
+ -> Seq Scan on public.prt2_p1 t1_3
+ Output: t1_3.b
+ Filter: ((t1_3.b % 25) = 0)
+ -> Nested Loop Semi Join
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Join Filter: (t1_2.a = t1_4.b)
+ -> Seq Scan on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Materialize
+ Output: t1_4.b
+ -> Seq Scan on public.prt2_p2 t1_4
+ Output: t1_4.b
+ Filter: ((t1_4.b % 25) = 0)
+ -> Nested Loop Semi Join
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Join Filter: (t1_1.a = t1_5.b)
+ -> Seq Scan on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Materialize
+ Output: t1_5.b
+ -> Seq Scan on public.prt2_p3 t1_5
+ Output: t1_5.b
+ Filter: ((t1_5.b % 25) = 0)
+(37 rows)
+
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 1) and (b 3) is considered for partition-wise join.
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+-- lateral reference
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 4 5) is considered for partition-wise join.
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a
+ Sort Key: t1.a
+ -> Result
+ Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a
+ -> Append
+ -> Nested Loop Left Join
+ Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a))
+ -> Seq Scan on public.prt1_p1 t1
+ Output: t1.a, t1.b, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Join
+ Output: t2.a, t3.a, LEAST(t1.a, t2.a, t3.a)
+ Hash Cond: (t3.b = t2.a)
+ -> Seq Scan on public.prt2_p1 t3
+ Output: t3.a, t3.b
+ -> Hash
+ Output: t2.a
+ -> Seq Scan on public.prt1_p1 t2
+ Output: t2.a
+ Filter: (t1.a = t2.a)
+ -> Nested Loop Left Join
+ Output: t1_2.a, t1_2.b, t1_2.c, t2_2.a, t3_1.a, (LEAST(t1_2.a, t2_2.a, t3_1.a))
+ -> Seq Scan on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Hash Join
+ Output: t2_2.a, t3_1.a, LEAST(t1_2.a, t2_2.a, t3_1.a)
+ Hash Cond: (t3_1.b = t2_2.a)
+ -> Seq Scan on public.prt2_p2 t3_1
+ Output: t3_1.a, t3_1.b
+ -> Hash
+ Output: t2_2.a
+ -> Seq Scan on public.prt1_p2 t2_2
+ Output: t2_2.a
+ Filter: (t1_2.a = t2_2.a)
+ -> Nested Loop Left Join
+ Output: t1_1.a, t1_1.b, t1_1.c, t2_1.a, t3_2.a, (LEAST(t1_1.a, t2_1.a, t3_2.a))
+ -> Seq Scan on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Hash Join
+ Output: t2_1.a, t3_2.a, LEAST(t1_1.a, t2_1.a, t3_2.a)
+ Hash Cond: (t3_2.b = t2_1.a)
+ -> Seq Scan on public.prt2_p3 t3_2
+ Output: t3_2.a, t3_2.b
+ -> Hash
+ Output: t2_1.a
+ -> Seq Scan on public.prt1_p3 t2_1
+ Output: t2_1.a
+ Filter: (t1_1.a = t2_1.a)
+(51 rows)
+
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 4 5) is considered for partition-wise join.
+ a | b | c | t2a | t3a | least
+-----+-----+------+-----+-----+-------
+ 0 | 0 | 0000 | 0 | 0 | 0
+ 50 | 50 | 0050 | | |
+ 100 | 100 | 0100 | | |
+ 150 | 150 | 0150 | 150 | 150 | 150
+ 200 | 200 | 0200 | | |
+ 250 | 250 | 0250 | | |
+ 300 | 300 | 0300 | 300 | 300 | 300
+ 350 | 350 | 0350 | | |
+ 400 | 400 | 0400 | | |
+ 450 | 450 | 0450 | 450 | 450 | 450
+ 500 | 500 | 0500 | | |
+ 550 | 550 | 0550 | | |
+(12 rows)
+
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+ a | b | c | t2a | t3a | least
+-----+-----+------+-----+-----+-------
+ 0 | 0 | 0000 | 0 | 0 | 0
+ 50 | 50 | 0050 | | |
+ 100 | 100 | 0100 | | |
+ 150 | 150 | 0150 | 150 | 150 | 150
+ 200 | 200 | 0200 | | |
+ 250 | 250 | 0250 | | |
+ 300 | 300 | 0300 | 300 | 300 | 300
+ 350 | 350 | 0350 | | |
+ 400 | 400 | 0400 | | |
+ 450 | 450 | 0450 | 450 | 450 | 450
+ 500 | 500 | 0500 | | |
+ 550 | 550 | 0550 | | |
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ QUERY PLAN
+-------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a
+ Sort Key: t1.a
+ -> Nested Loop Left Join
+ Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a
+ -> Append
+ -> Seq Scan on public.prt1 t1
+ Output: t1.a, t1.b, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Seq Scan on public.prt1_p1 t1_1
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Seq Scan on public.prt1_p3 t1_2
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Seq Scan on public.prt1_p2 t1_3
+ Output: t1_3.a, t1_3.b, t1_3.c
+ Filter: ((t1_3.a % 25) = 0)
+ -> Append
+ -> Hash Join
+ Output: t2.a, t3.a, LEAST(t1.a, t2.a, t3.a)
+ Hash Cond: (t3.b = t2.a)
+ -> Seq Scan on public.prt2_p1 t3
+ Output: t3.a, t3.b
+ -> Hash
+ Output: t2.a
+ -> Seq Scan on public.prt1_p1 t2
+ Output: t2.a
+ Filter: (t1.b = t2.a)
+ -> Hash Join
+ Output: t2_2.a, t3_1.a, LEAST(t1.a, t2_2.a, t3_1.a)
+ Hash Cond: (t3_1.b = t2_2.a)
+ -> Seq Scan on public.prt2_p2 t3_1
+ Output: t3_1.a, t3_1.b
+ -> Hash
+ Output: t2_2.a
+ -> Seq Scan on public.prt1_p2 t2_2
+ Output: t2_2.a
+ Filter: (t1.b = t2_2.a)
+ -> Hash Join
+ Output: t2_1.a, t3_2.a, LEAST(t1.a, t2_1.a, t3_2.a)
+ Hash Cond: (t3_2.b = t2_1.a)
+ -> Seq Scan on public.prt2_p3 t3_2
+ Output: t3_2.a, t3_2.b
+ -> Hash
+ Output: t2_1.a
+ -> Seq Scan on public.prt1_p3 t2_1
+ Output: t2_1.a
+ Filter: (t1.b = t2_1.a)
+(49 rows)
+
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ a | b | c | t2a | t3a | least
+-----+-----+------+-----+-----+-------
+ 0 | 0 | 0000 | 0 | 0 | 0
+ 50 | 50 | 0050 | | |
+ 100 | 100 | 0100 | | |
+ 150 | 150 | 0150 | 150 | 150 | 150
+ 200 | 200 | 0200 | | |
+ 250 | 250 | 0250 | | |
+ 300 | 300 | 0300 | 300 | 300 | 300
+ 350 | 350 | 0350 | | |
+ 400 | 400 | 0400 | | |
+ 450 | 450 | 0450 | 450 | 450 | 450
+ 500 | 500 | 0500 | | |
+ 550 | 550 | 0550 | | |
+(12 rows)
+
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+ a | b | c | t2a | t3a | least
+-----+-----+------+-----+-----+-------
+ 0 | 0 | 0000 | 0 | 0 | 0
+ 50 | 50 | 0050 | | |
+ 100 | 100 | 0100 | | |
+ 150 | 150 | 0150 | 150 | 150 | 150
+ 200 | 200 | 0200 | | |
+ 250 | 250 | 0250 | | |
+ 300 | 300 | 0300 | 300 | 300 | 300
+ 350 | 350 | 0350 | | |
+ 400 | 400 | 0400 | | |
+ 450 | 450 | 0450 | 450 | 450 | 450
+ 500 | 500 | 0500 | | |
+ 550 | 550 | 0550 | | |
+(12 rows)
+
+--
+-- partitioned by expression
+--
+CREATE TABLE prt1_e (a int, b int, c varchar) PARTITION BY RANGE(((a + b)/2));
+CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES START (0) END (250);
+CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES START (250) END (500);
+CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES START (500) END (600);
+INSERT INTO prt1_e SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1_e;
+ANALYZE prt1_e_p1;
+ANALYZE prt1_e_p2;
+ANALYZE prt1_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1_e AS SELECT * FROM prt1_e;
+CREATE TABLE prt2_e (a int, b int, c varchar) PARTITION BY RANGE(((b + a)/2));
+CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES START (0) END (250);
+CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES START (250) END (500);
+CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES START (500) END (600);
+INSERT INTO prt2_e SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE prt2_e;
+ANALYZE prt2_e_p1;
+ANALYZE prt2_e_p2;
+ANALYZE prt2_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt2_e AS SELECT * FROM prt2_e;
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Append
+ -> Hash Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: (((t2.b + t2.a) / 2) = ((t1.a + t1.b) / 2))
+ -> Seq Scan on public.prt2_e_p1 t2
+ Output: t2.b, t2.c, t2.a
+ -> Hash
+ Output: t1.a, t1.c, t1.b
+ -> Seq Scan on public.prt1_e_p1 t1
+ Output: t1.a, t1.c, t1.b
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ Hash Cond: (((t2_1.b + t2_1.a) / 2) = ((t1_1.a + t1_1.b) / 2))
+ -> Seq Scan on public.prt2_e_p2 t2_1
+ Output: t2_1.b, t2_1.c, t2_1.a
+ -> Hash
+ Output: t1_1.a, t1_1.c, t1_1.b
+ -> Seq Scan on public.prt1_e_p2 t1_1
+ Output: t1_1.a, t1_1.c, t1_1.b
+ Filter: ((t1_1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+ Hash Cond: (((t2_2.b + t2_2.a) / 2) = ((t1_2.a + t1_2.b) / 2))
+ -> Seq Scan on public.prt2_e_p3 t2_2
+ Output: t2_2.b, t2_2.c, t2_2.a
+ -> Hash
+ Output: t1_2.a, t1_2.c, t1_2.b
+ -> Seq Scan on public.prt1_e_p3 t1_2
+ Output: t1_2.a, t1_2.c, t1_2.b
+ Filter: ((t1_2.a % 25) = 0)
+(34 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.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ a | c | b | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150
+ 300 | 0300 | 300 | 0300
+ 450 | 0450 | 450 | 0450
+(4 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1, uprt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.a % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 LEFT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Append
+ -> Hash Right Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: (((t2.b + t2.a) / 2) = ((t1.a + t1.b) / 2))
+ -> Seq Scan on public.prt2_e_p1 t2
+ Output: t2.b, t2.c, t2.a
+ -> Hash
+ Output: t1.a, t1.c, t1.b
+ -> Seq Scan on public.prt1_e_p1 t1
+ Output: t1.a, t1.c, t1.b
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ Hash Cond: (((t2_1.b + t2_1.a) / 2) = ((t1_1.a + t1_1.b) / 2))
+ -> Seq Scan on public.prt2_e_p2 t2_1
+ Output: t2_1.b, t2_1.c, t2_1.a
+ -> Hash
+ Output: t1_1.a, t1_1.c, t1_1.b
+ -> Seq Scan on public.prt1_e_p2 t1_1
+ Output: t1_1.a, t1_1.c, t1_1.b
+ Filter: ((t1_1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+ Hash Cond: (((t2_2.b + t2_2.a) / 2) = ((t1_2.a + t1_2.b) / 2))
+ -> Seq Scan on public.prt2_e_p3 t2_2
+ Output: t2_2.b, t2_2.c, t2_2.a
+ -> Hash
+ Output: t1_2.a, t1_2.c, t1_2.b
+ -> Seq Scan on public.prt1_e_p3 t1_2
+ Output: t1_2.a, t1_2.c, t1_2.b
+ Filter: ((t1_2.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 LEFT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ 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 | |
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1 LEFT JOIN uprt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 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 | |
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 RIGHT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c
+ -> Append
+ -> Hash Right Join
+ Output: t2.b, t2.c, t1.a, t1.c
+ Hash Cond: (((t1.a + t1.b) / 2) = ((t2.b + t2.a) / 2))
+ -> Seq Scan on public.prt1_e_p1 t1
+ Output: t1.a, t1.c, t1.b
+ -> Hash
+ Output: t2.b, t2.c, t2.a
+ -> Seq Scan on public.prt2_e_p1 t2
+ Output: t2.b, t2.c, t2.a
+ Filter: ((t2.b % 25) = 0)
+ -> Hash Right Join
+ Output: t2_1.b, t2_1.c, t1_1.a, t1_1.c
+ Hash Cond: (((t1_1.a + t1_1.b) / 2) = ((t2_1.b + t2_1.a) / 2))
+ -> Seq Scan on public.prt1_e_p2 t1_1
+ Output: t1_1.a, t1_1.c, t1_1.b
+ -> Hash
+ Output: t2_1.b, t2_1.c, t2_1.a
+ -> Seq Scan on public.prt2_e_p2 t2_1
+ Output: t2_1.b, t2_1.c, t2_1.a
+ Filter: ((t2_1.b % 25) = 0)
+ -> Hash Right Join
+ Output: t2_2.b, t2_2.c, t1_2.a, t1_2.c
+ Hash Cond: (((t1_2.a + t1_2.b) / 2) = ((t2_2.b + t2_2.a) / 2))
+ -> Seq Scan on public.prt1_e_p3 t1_2
+ Output: t1_2.a, t1_2.c, t1_2.b
+ -> Hash
+ Output: t2_2.b, t2_2.c, t2_2.a
+ -> Seq Scan on public.prt2_e_p3 t2_2
+ Output: t2_2.b, t2_2.c, t2_2.a
+ Filter: ((t2_2.b % 25) = 0)
+(36 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 RIGHT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+ 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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1 RIGHT JOIN uprt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_e WHERE prt2_e.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------
+ Sort
+ Output: prt1_e_p1.a, prt1_e_p1.c, prt2_e_p1.b, prt2_e_p1.c
+ Sort Key: prt1_e_p1.a, prt2_e_p1.b
+ -> Append
+ -> Hash Full Join
+ Output: prt1_e_p1.a, prt1_e_p1.c, prt2_e_p1.b, prt2_e_p1.c
+ Hash Cond: (((prt1_e_p1.a + prt1_e_p1.b) / 2) = ((prt2_e_p1.b + prt2_e_p1.a) / 2))
+ -> Seq Scan on public.prt1_e_p1
+ Output: prt1_e_p1.a, prt1_e_p1.c, prt1_e_p1.b
+ Filter: ((prt1_e_p1.a % 25) = 0)
+ -> Hash
+ Output: prt2_e_p1.b, prt2_e_p1.c, prt2_e_p1.a
+ -> Seq Scan on public.prt2_e_p1
+ Output: prt2_e_p1.b, prt2_e_p1.c, prt2_e_p1.a
+ Filter: ((prt2_e_p1.b % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_e_p2.a, prt1_e_p2.c, prt2_e_p2.b, prt2_e_p2.c
+ Hash Cond: (((prt1_e_p2.a + prt1_e_p2.b) / 2) = ((prt2_e_p2.b + prt2_e_p2.a) / 2))
+ -> Seq Scan on public.prt1_e_p2
+ Output: prt1_e_p2.a, prt1_e_p2.c, prt1_e_p2.b
+ Filter: ((prt1_e_p2.a % 25) = 0)
+ -> Hash
+ Output: prt2_e_p2.b, prt2_e_p2.c, prt2_e_p2.a
+ -> Seq Scan on public.prt2_e_p2
+ Output: prt2_e_p2.b, prt2_e_p2.c, prt2_e_p2.a
+ Filter: ((prt2_e_p2.b % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_e_p3.a, prt1_e_p3.c, prt2_e_p3.b, prt2_e_p3.c
+ Hash Cond: (((prt1_e_p3.a + prt1_e_p3.b) / 2) = ((prt2_e_p3.b + prt2_e_p3.a) / 2))
+ -> Seq Scan on public.prt1_e_p3
+ Output: prt1_e_p3.a, prt1_e_p3.c, prt1_e_p3.b
+ Filter: ((prt1_e_p3.a % 25) = 0)
+ -> Hash
+ Output: prt2_e_p3.b, prt2_e_p3.c, prt2_e_p3.a
+ -> Seq Scan on public.prt2_e_p3
+ Output: prt2_e_p3.b, prt2_e_p3.c, prt2_e_p3.a
+ Filter: ((prt2_e_p3.b % 25) = 0)
+(37 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_e WHERE prt2_e.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ 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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_e t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_e t2 WHERE t2.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) 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)
+
+--
+-- N-way join
+--
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 3) is considered for partition-wise join.
+NOTICE: join between relations (b 1 2) and (b 3) is considered for partition-wise join.
+NOTICE: join between relations (b 1 3) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2 3) and (b 1) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+ Sort Key: t1.a
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+ -> Append
+ -> Hash Join
+ Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c
+ Hash Cond: (((t3.a + t3.b) / 2) = t1.a)
+ -> Seq Scan on public.prt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c
+ -> Hash
+ Output: t1.a, t1.c, t2.b, t2.c
+ -> Hash Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on public.prt2_p1 t2
+ Output: t2.b, t2.c
+ -> Hash
+ Output: t1.a, t1.c
+ -> Seq Scan on public.prt1_p1 t1
+ Output: t1.a, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+ Hash Cond: (((t3_1.a + t3_1.b) / 2) = t1_2.a)
+ -> Seq Scan on public.prt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c
+ -> Hash
+ Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+ -> Hash Join
+ Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+ Hash Cond: (t2_1.b = t1_2.a)
+ -> Seq Scan on public.prt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Hash
+ Output: t1_2.a, t1_2.c
+ -> Seq Scan on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Hash Join
+ Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+ Hash Cond: (((t3_2.a + t3_2.b) / 2) = t1_1.a)
+ -> Seq Scan on public.prt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c
+ -> Hash
+ Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+ -> Hash Join
+ Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+ Hash Cond: (t2_2.b = t1_1.a)
+ -> Seq Scan on public.prt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Hash
+ Output: t1_1.a, t1_1.c
+ -> Seq Scan on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+(57 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.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 3) is considered for partition-wise join.
+NOTICE: join between relations (b 1 2) and (b 3) is considered for partition-wise join.
+NOTICE: join between relations (b 1 3) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2 3) and (b 1) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+(4 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM uprt1 t1, uprt2 t2, uprt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+(4 rows)
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+ Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+ -> Append
+ -> Hash Right Join
+ Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c
+ Hash Cond: (((t3.a + t3.b) / 2) = t1.a)
+ -> Seq Scan on public.prt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c
+ -> Hash
+ Output: t1.a, t1.c, t2.b, t2.c
+ -> Hash Right Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on public.prt2_p1 t2
+ Output: t2.b, t2.c
+ -> Hash
+ Output: t1.a, t1.c
+ -> Seq Scan on public.prt1_p1 t1
+ Output: t1.a, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+ Hash Cond: (((t3_1.a + t3_1.b) / 2) = t1_2.a)
+ -> Seq Scan on public.prt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c
+ -> Hash
+ Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+ -> Hash Right Join
+ Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+ Hash Cond: (t2_1.b = t1_2.a)
+ -> Seq Scan on public.prt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Hash
+ Output: t1_2.a, t1_2.c
+ -> Seq Scan on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+ Hash Cond: (((t3_2.a + t3_2.b) / 2) = t1_1.a)
+ -> Seq Scan on public.prt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c
+ -> Hash
+ Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+ -> Hash Right Join
+ Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+ Hash Cond: (t2_2.b = t1_1.a)
+ -> Seq Scan on public.prt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Hash
+ Output: t1_1.a, t1_1.c
+ -> Seq Scan on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+(57 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 50 | 0050 | | | 100 | 0050
+ 100 | 0100 | | | 200 | 0100
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 200 | 0200 | | | 400 | 0200
+ 250 | 0250 | | | 500 | 0250
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 350 | 0350 | | | 700 | 0350
+ 400 | 0400 | | | 800 | 0400
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ 500 | 0500 | | | 1000 | 0500
+ 550 | 0550 | | | 1100 | 0550
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) LEFT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 50 | 0050 | | | 100 | 0050
+ 100 | 0100 | | | 200 | 0100
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 200 | 0200 | | | 400 | 0200
+ 250 | 0250 | | | 500 | 0250
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 350 | 0350 | | | 700 | 0350
+ 400 | 0400 | | | 800 | 0400
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ 500 | 0500 | | | 1000 | 0500
+ 550 | 0550 | | | 1100 | 0550
+(12 rows)
+
+EXPLAIN (VERBOSE, 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 (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 2 4) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+ Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+ -> Append
+ -> Hash Right Join
+ Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c
+ Hash Cond: (((t3.a + t3.b) / 2) = t2.b)
+ -> Seq Scan on public.prt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c
+ -> Hash
+ Output: t1.a, t1.c, t2.b, t2.c
+ -> Hash Right Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on public.prt2_p1 t2
+ Output: t2.b, t2.c
+ -> Hash
+ Output: t1.a, t1.c
+ -> Seq Scan on public.prt1_p1 t1
+ Output: t1.a, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+ Hash Cond: (((t3_1.a + t3_1.b) / 2) = t2_1.b)
+ -> Seq Scan on public.prt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c
+ -> Hash
+ Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+ -> Hash Right Join
+ Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+ Hash Cond: (t2_1.b = t1_2.a)
+ -> Seq Scan on public.prt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Hash
+ Output: t1_2.a, t1_2.c
+ -> Seq Scan on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+ Hash Cond: (((t3_2.a + t3_2.b) / 2) = t2_2.b)
+ -> Seq Scan on public.prt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c
+ -> Hash
+ Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+ -> Hash Right Join
+ Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+ Hash Cond: (t2_2.b = t1_1.a)
+ -> Seq Scan on public.prt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Hash
+ Output: t1_1.a, t1_1.c
+ -> Seq Scan on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+(57 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 (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 2 4) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 50 | 0050 | | | |
+ 100 | 0100 | | | |
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 200 | 0200 | | | |
+ 250 | 0250 | | | |
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 350 | 0350 | | | |
+ 400 | 0400 | | | |
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ 500 | 0500 | | | |
+ 550 | 0550 | | | |
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) LEFT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 50 | 0050 | | | |
+ 100 | 0100 | | | |
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 200 | 0200 | | | |
+ 250 | 0250 | | | |
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 350 | 0350 | | | |
+ 400 | 0400 | | | |
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ 500 | 0500 | | | |
+ 550 | 0550 | | | |
+(12 rows)
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+ Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+ -> Append
+ -> Hash Right Join
+ Output: t3.a, t3.b, t3.c, t1.a, t1.c, t2.b, t2.c
+ Hash Cond: (t2.b = t1.a)
+ -> Seq Scan on public.prt2_p1 t2
+ Output: t2.b, t2.c
+ -> Hash
+ Output: t3.a, t3.b, t3.c, t1.a, t1.c
+ -> Hash Right Join
+ Output: t3.a, t3.b, t3.c, t1.a, t1.c
+ Hash Cond: (t1.a = ((t3.a + t3.b) / 2))
+ -> Seq Scan on public.prt1_p1 t1
+ Output: t1.a, t1.c
+ -> Hash
+ Output: t3.a, t3.b, t3.c
+ -> Seq Scan on public.prt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c
+ Filter: ((t3.a % 25) = 0)
+ -> Hash Right Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c, t2_1.b, t2_1.c
+ Hash Cond: (t2_1.b = t1_2.a)
+ -> Seq Scan on public.prt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Hash
+ Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c
+ -> Hash Right Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c
+ Hash Cond: (t1_2.a = ((t3_1.a + t3_1.b) / 2))
+ -> Seq Scan on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.c
+ -> Hash
+ Output: t3_1.a, t3_1.b, t3_1.c
+ -> Seq Scan on public.prt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c
+ Filter: ((t3_1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c, t2_2.b, t2_2.c
+ Hash Cond: (t2_2.b = t1_1.a)
+ -> Seq Scan on public.prt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Hash
+ Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c
+ -> Hash Right Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c
+ Hash Cond: (t1_1.a = ((t3_2.a + t3_2.b) / 2))
+ -> Seq Scan on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.c
+ -> Hash
+ Output: t3_2.a, t3_2.b, t3_2.c
+ -> Seq Scan on public.prt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c
+ Filter: ((t3_2.a % 25) = 0)
+(57 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 50 | 0050 | | | 100 | 0050
+ 100 | 0100 | | | 200 | 0100
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 200 | 0200 | | | 400 | 0200
+ 250 | 0250 | | | 500 | 0250
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 350 | 0350 | | | 700 | 0350
+ 400 | 0400 | | | 800 | 0400
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ 500 | 0500 | | | 1000 | 0500
+ 550 | 0550 | | | 1100 | 0550
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 50 | 0050 | | | 100 | 0050
+ 100 | 0100 | | | 200 | 0100
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 200 | 0200 | | | 400 | 0200
+ 250 | 0250 | | | 500 | 0250
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 350 | 0350 | | | 700 | 0350
+ 400 | 0400 | | | 800 | 0400
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ 500 | 0500 | | | 1000 | 0500
+ 550 | 0550 | | | 1100 | 0550
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+ Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+ -> Append
+ -> Hash Right Join
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c
+ Hash Cond: (t1.a = t2.b)
+ -> Seq Scan on public.prt1_p1 t1
+ Output: t1.a, t1.c
+ -> Hash
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c
+ -> Hash Right Join
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c
+ Hash Cond: (t2.b = ((t3.a + t3.b) / 2))
+ -> Seq Scan on public.prt2_p1 t2
+ Output: t2.b, t2.c
+ -> Hash
+ Output: t3.a, t3.b, t3.c
+ -> Seq Scan on public.prt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c
+ Filter: ((t3.a % 25) = 0)
+ -> Hash Right Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_2.a, t1_2.c
+ Hash Cond: (t1_2.a = t2_1.b)
+ -> Seq Scan on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.c
+ -> Hash
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+ -> Hash Right Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+ Hash Cond: (t2_1.b = ((t3_1.a + t3_1.b) / 2))
+ -> Seq Scan on public.prt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Hash
+ Output: t3_1.a, t3_1.b, t3_1.c
+ -> Seq Scan on public.prt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c
+ Filter: ((t3_1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_1.a, t1_1.c
+ Hash Cond: (t1_1.a = t2_2.b)
+ -> Seq Scan on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.c
+ -> Hash
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+ -> Hash Right Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+ Hash Cond: (t2_2.b = ((t3_2.a + t3_2.b) / 2))
+ -> Seq Scan on public.prt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Hash
+ Output: t3_2.a, t3_2.b, t3_2.c
+ -> Seq Scan on public.prt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c
+ Filter: ((t3_2.a % 25) = 0)
+(57 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ | | | | 100 | 0050
+ | | | | 200 | 0100
+ | | | | 400 | 0200
+ | | | | 500 | 0250
+ | | | | 700 | 0350
+ | | | | 800 | 0400
+ | | | | 1000 | 0500
+ | | | | 1100 | 0550
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ | | | | 100 | 0050
+ | | | | 200 | 0100
+ | | | | 400 | 0200
+ | | | | 500 | 0250
+ | | | | 700 | 0350
+ | | | | 800 | 0400
+ | | | | 1000 | 0500
+ | | | | 1100 | 0550
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE: join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+ QUERY PLAN
+---------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c, ((prt1_e_p1.a + prt1_e_p1.b)), prt1_e_p1.c
+ Sort Key: prt1_p1.a, prt2_p1.b, ((prt1_e_p1.a + prt1_e_p1.b))
+ -> Result
+ Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c, (prt1_e_p1.a + prt1_e_p1.b), prt1_e_p1.c
+ -> Append
+ -> Hash Full Join
+ Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c, prt1_e_p1.a, prt1_e_p1.b, prt1_e_p1.c
+ Hash Cond: (prt1_p1.a = ((prt1_e_p1.a + prt1_e_p1.b) / 2))
+ -> Hash Full Join
+ Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c
+ Hash Cond: (prt1_p1.a = prt2_p1.b)
+ -> Seq Scan on public.prt1_p1
+ Output: prt1_p1.a, prt1_p1.c
+ Filter: ((prt1_p1.a % 25) = 0)
+ -> Hash
+ Output: prt2_p1.b, prt2_p1.c
+ -> Seq Scan on public.prt2_p1
+ Output: prt2_p1.b, prt2_p1.c
+ Filter: ((prt2_p1.b % 25) = 0)
+ -> Hash
+ Output: prt1_e_p1.a, prt1_e_p1.b, prt1_e_p1.c
+ -> Seq Scan on public.prt1_e_p1
+ Output: prt1_e_p1.a, prt1_e_p1.b, prt1_e_p1.c
+ Filter: ((prt1_e_p1.a % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c, prt1_e_p2.a, prt1_e_p2.b, prt1_e_p2.c
+ Hash Cond: (prt1_p2.a = ((prt1_e_p2.a + prt1_e_p2.b) / 2))
+ -> Hash Full Join
+ Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c
+ Hash Cond: (prt1_p2.a = prt2_p2.b)
+ -> Seq Scan on public.prt1_p2
+ Output: prt1_p2.a, prt1_p2.c
+ Filter: ((prt1_p2.a % 25) = 0)
+ -> Hash
+ Output: prt2_p2.b, prt2_p2.c
+ -> Seq Scan on public.prt2_p2
+ Output: prt2_p2.b, prt2_p2.c
+ Filter: ((prt2_p2.b % 25) = 0)
+ -> Hash
+ Output: prt1_e_p2.a, prt1_e_p2.b, prt1_e_p2.c
+ -> Seq Scan on public.prt1_e_p2
+ Output: prt1_e_p2.a, prt1_e_p2.b, prt1_e_p2.c
+ Filter: ((prt1_e_p2.a % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_p3.a, prt1_p3.c, prt2_p3.b, prt2_p3.c, prt1_e_p3.a, prt1_e_p3.b, prt1_e_p3.c
+ Hash Cond: (prt1_p3.a = ((prt1_e_p3.a + prt1_e_p3.b) / 2))
+ -> Hash Full Join
+ Output: prt1_p3.a, prt1_p3.c, prt2_p3.b, prt2_p3.c
+ Hash Cond: (prt1_p3.a = prt2_p3.b)
+ -> Seq Scan on public.prt1_p3
+ Output: prt1_p3.a, prt1_p3.c
+ Filter: ((prt1_p3.a % 25) = 0)
+ -> Hash
+ Output: prt2_p3.b, prt2_p3.c
+ -> Seq Scan on public.prt2_p3
+ Output: prt2_p3.b, prt2_p3.c
+ Filter: ((prt2_p3.b % 25) = 0)
+ -> Hash
+ Output: prt1_e_p3.a, prt1_e_p3.b, prt1_e_p3.c
+ -> Seq Scan on public.prt1_e_p3
+ Output: prt1_e_p3.a, prt1_e_p3.b, prt1_e_p3.c
+ Filter: ((prt1_e_p3.a % 25) = 0)
+(63 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE: join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 50 | 0050 | | | 100 | 0050
+ 100 | 0100 | | | 200 | 0100
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 200 | 0200 | | | 400 | 0200
+ 250 | 0250 | | | 500 | 0250
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 350 | 0350 | | | 700 | 0350
+ 400 | 0400 | | | 800 | 0400
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ 500 | 0500 | | | 1000 | 0500
+ 550 | 0550 | | | 1100 | 0550
+ | | 75 | 0075 | |
+ | | 225 | 0225 | |
+ | | 375 | 0375 | |
+ | | 525 | 0525 | |
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM uprt1_e WHERE uprt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 50 | 0050 | | | 100 | 0050
+ 100 | 0100 | | | 200 | 0100
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 200 | 0200 | | | 400 | 0200
+ 250 | 0250 | | | 500 | 0250
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 350 | 0350 | | | 700 | 0350
+ 400 | 0400 | | | 800 | 0400
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ 500 | 0500 | | | 1000 | 0500
+ 550 | 0550 | | | 1100 | 0550
+ | | 75 | 0075 | |
+ | | 225 | 0225 | |
+ | | 375 | 0375 | |
+ | | 525 | 0525 | |
+(16 rows)
+
+-- Cases with non-nullable expressions in subquery results;
+-- make sure these go to null as expected
+EXPLAIN (VERBOSE, 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.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.a % 25 = 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;
+NOTICE: join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE: join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+ QUERY PLAN
+----------------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: prt1_p1.a, (50), prt2_p1.b, (75), ((prt1_e_p1.a + prt1_e_p1.b)), (50)
+ Sort Key: prt1_p1.a, prt2_p1.b, ((prt1_e_p1.a + prt1_e_p1.b))
+ -> Result
+ Output: prt1_p1.a, (50), prt2_p1.b, (75), (prt1_e_p1.a + prt1_e_p1.b), (50)
+ -> Append
+ -> Hash Full Join
+ Output: prt1_p1.a, prt2_p1.b, prt1_e_p1.a, prt1_e_p1.b, (50), (75), (50)
+ 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
+ Output: prt1_p1.a, prt2_p1.b, (50), (75)
+ Hash Cond: (prt1_p1.a = prt2_p1.b)
+ -> Seq Scan on public.prt1_p1
+ Output: prt1_p1.a, 50
+ Filter: ((prt1_p1.a % 25) = 0)
+ -> Hash
+ Output: prt2_p1.b, (75)
+ -> Seq Scan on public.prt2_p1
+ Output: prt2_p1.b, 75
+ Filter: ((prt2_p1.b % 25) = 0)
+ -> Hash
+ Output: prt1_e_p1.a, prt1_e_p1.b, (50)
+ -> Seq Scan on public.prt1_e_p1
+ Output: prt1_e_p1.a, prt1_e_p1.b, 50
+ Filter: ((prt1_e_p1.a % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_p2.a, prt2_p2.b, prt1_e_p2.a, prt1_e_p2.b, (50), (75), (50)
+ 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
+ Output: prt1_p2.a, prt2_p2.b, (50), (75)
+ Hash Cond: (prt1_p2.a = prt2_p2.b)
+ -> Seq Scan on public.prt1_p2
+ Output: prt1_p2.a, 50
+ Filter: ((prt1_p2.a % 25) = 0)
+ -> Hash
+ Output: prt2_p2.b, (75)
+ -> Seq Scan on public.prt2_p2
+ Output: prt2_p2.b, 75
+ Filter: ((prt2_p2.b % 25) = 0)
+ -> Hash
+ Output: prt1_e_p2.a, prt1_e_p2.b, (50)
+ -> Seq Scan on public.prt1_e_p2
+ Output: prt1_e_p2.a, prt1_e_p2.b, 50
+ Filter: ((prt1_e_p2.a % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_p3.a, prt2_p3.b, prt1_e_p3.a, prt1_e_p3.b, (50), (75), (50)
+ 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
+ Output: prt1_p3.a, prt2_p3.b, (50), (75)
+ Hash Cond: (prt1_p3.a = prt2_p3.b)
+ -> Seq Scan on public.prt1_p3
+ Output: prt1_p3.a, 50
+ Filter: ((prt1_p3.a % 25) = 0)
+ -> Hash
+ Output: prt2_p3.b, (75)
+ -> Seq Scan on public.prt2_p3
+ Output: prt2_p3.b, 75
+ Filter: ((prt2_p3.b % 25) = 0)
+ -> Hash
+ Output: prt1_e_p3.a, prt1_e_p3.b, (50)
+ -> Seq Scan on public.prt1_e_p3
+ Output: prt1_e_p3.a, prt1_e_p3.b, 50
+ Filter: ((prt1_e_p3.a % 25) = 0)
+(66 rows)
+
+SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.a % 25 = 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;
+NOTICE: join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE: join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+ a | phv | b | phv | ?column? | phv
+----+-----+----+-----+----------+-----
+ 50 | 50 | | | 100 | 50
+ | | 75 | 75 | |
+(2 rows)
+
+SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM uprt1_e WHERE uprt1_e.a % 25 = 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)
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 4) is considered for partition-wise join.
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.b, t1.c
+ Sort Key: t1.a
+ -> Append
+ -> Nested Loop Semi Join
+ Output: t1.a, t1.b, t1.c
+ Join Filter: (t1.a = t1_3.b)
+ -> Seq Scan on public.prt1_p1 t1
+ Output: t1.a, t1.b, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Materialize
+ Output: t1_3.b, t2.a, t2.b
+ -> Hash Join
+ Output: t1_3.b, t2.a, t2.b
+ Hash Cond: (((t2.a + t2.b) / 2) = t1_3.b)
+ -> Seq Scan on public.prt1_e_p1 t2
+ Output: t2.a, t2.b
+ -> Hash
+ Output: t1_3.b
+ -> Seq Scan on public.prt2_p1 t1_3
+ Output: t1_3.b
+ Filter: ((t1_3.b % 25) = 0)
+ -> Nested Loop Semi Join
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Join Filter: (t1_2.a = t1_4.b)
+ -> Seq Scan on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Materialize
+ Output: t1_4.b, t2_1.a, t2_1.b
+ -> Hash Join
+ Output: t1_4.b, t2_1.a, t2_1.b
+ Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_4.b)
+ -> Seq Scan on public.prt1_e_p2 t2_1
+ Output: t2_1.a, t2_1.b
+ -> Hash
+ Output: t1_4.b
+ -> Seq Scan on public.prt2_p2 t1_4
+ Output: t1_4.b
+ Filter: ((t1_4.b % 25) = 0)
+ -> Nested Loop Semi Join
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Join Filter: (t1_1.a = t1_5.b)
+ -> Seq Scan on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Materialize
+ Output: t1_5.b, t2_2.a, t2_2.b
+ -> Hash Join
+ Output: t1_5.b, t2_2.a, t2_2.b
+ Hash Cond: (((t2_2.a + t2_2.b) / 2) = t1_5.b)
+ -> Seq Scan on public.prt1_e_p3 t2_2
+ Output: t2_2.a, t2_2.b
+ -> Hash
+ Output: t1_5.b
+ -> Seq Scan on public.prt2_p3 t1_5
+ Output: t1_5.b
+ Filter: ((t1_5.b % 25) = 0)
+(58 rows)
+
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 4) is considered for partition-wise join.
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1, uprt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a;
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.b, t1.c
+ Sort Key: t1.a
+ -> Append
+ -> Nested Loop Semi Join
+ Output: t1.a, t1.b, t1.c
+ Join Filter: (t1.a = t1_3.b)
+ -> Seq Scan on public.prt1_p1 t1
+ Output: t1.a, t1.b, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Materialize
+ Output: t1_3.b, t1_6.a, t1_6.b
+ -> Hash Semi Join
+ Output: t1_3.b, t1_6.a, t1_6.b
+ Hash Cond: (t1_3.b = ((t1_6.a + t1_6.b) / 2))
+ -> Seq Scan on public.prt2_p1 t1_3
+ Output: t1_3.b
+ -> Hash
+ Output: t1_6.a, t1_6.b
+ -> Seq Scan on public.prt1_e_p1 t1_6
+ Output: t1_6.a, t1_6.b
+ Filter: ((t1_6.a % 25) = 0)
+ -> Nested Loop Semi Join
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Join Filter: (t1_2.a = t1_4.b)
+ -> Seq Scan on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Materialize
+ Output: t1_4.b, t1_7.a, t1_7.b
+ -> Hash Semi Join
+ Output: t1_4.b, t1_7.a, t1_7.b
+ Hash Cond: (t1_4.b = ((t1_7.a + t1_7.b) / 2))
+ -> Seq Scan on public.prt2_p2 t1_4
+ Output: t1_4.b
+ -> Hash
+ Output: t1_7.a, t1_7.b
+ -> Seq Scan on public.prt1_e_p2 t1_7
+ Output: t1_7.a, t1_7.b
+ Filter: ((t1_7.a % 25) = 0)
+ -> Nested Loop Semi Join
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Join Filter: (t1_1.a = t1_5.b)
+ -> Seq Scan on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Materialize
+ Output: t1_5.b, t1_8.a, t1_8.b
+ -> Hash Semi Join
+ Output: t1_5.b, t1_8.a, t1_8.b
+ Hash Cond: (t1_5.b = ((t1_8.a + t1_8.b) / 2))
+ -> Seq Scan on public.prt2_p3 t1_5
+ Output: t1_5.b
+ -> Hash
+ Output: t1_8.a, t1_8.b
+ -> Seq Scan on public.prt1_e_p3 t1_8
+ Output: t1_8.a, t1_8.b
+ Filter: ((t1_8.a % 25) = 0)
+(58 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+-- test merge joins with and without using indexes
+SET enable_hashjoin TO off;
+SET enable_nestloop TO off;
+CREATE INDEX iprt1_a on prt1(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 iprt2_b on prt2(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 iprt1_e_ab2 on prt1_e(((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));
+ANALYZE prt1;
+ANALYZE prt1_p1;
+ANALYZE prt1_p2;
+ANALYZE prt1_p3;
+ANALYZE prt2;
+ANALYZE prt2_p1;
+ANALYZE prt2_p2;
+ANALYZE prt2_p3;
+ANALYZE prt1_e;
+ANALYZE prt1_e_p1;
+ANALYZE prt1_e_p2;
+ANALYZE prt1_e_p3;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+ Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+ -> Append
+ -> Merge Left Join
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c
+ Merge Cond: (t2.b = t1.a)
+ -> Sort
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c
+ Sort Key: t2.b
+ -> Merge Left Join
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c
+ Merge Cond: ((((t3.a + t3.b) / 2)) = t2.b)
+ -> Sort
+ Output: t3.a, t3.b, t3.c, (((t3.a + t3.b) / 2))
+ Sort Key: (((t3.a + t3.b) / 2))
+ -> Seq Scan on public.prt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c, ((t3.a + t3.b) / 2)
+ Filter: ((t3.a % 25) = 0)
+ -> Sort
+ Output: t2.b, t2.c
+ Sort Key: t2.b
+ -> Seq Scan on public.prt2_p1 t2
+ Output: t2.b, t2.c
+ -> Sort
+ Output: t1.a, t1.c
+ Sort Key: t1.a
+ -> Seq Scan on public.prt1_p1 t1
+ Output: t1.a, t1.c
+ -> Merge Left Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_2.a, t1_2.c
+ Merge Cond: (t2_1.b = t1_2.a)
+ -> Sort
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+ Sort Key: t2_1.b
+ -> Merge Left Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+ Merge Cond: ((((t3_1.a + t3_1.b) / 2)) = t2_1.b)
+ -> Sort
+ Output: t3_1.a, t3_1.b, t3_1.c, (((t3_1.a + t3_1.b) / 2))
+ Sort Key: (((t3_1.a + t3_1.b) / 2))
+ -> Seq Scan on public.prt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c, ((t3_1.a + t3_1.b) / 2)
+ Filter: ((t3_1.a % 25) = 0)
+ -> Sort
+ Output: t2_1.b, t2_1.c
+ Sort Key: t2_1.b
+ -> Seq Scan on public.prt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Sort
+ Output: t1_2.a, t1_2.c
+ Sort Key: t1_2.a
+ -> Seq Scan on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.c
+ -> Merge Left Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_1.a, t1_1.c
+ Merge Cond: (t2_2.b = t1_1.a)
+ -> Sort
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+ Sort Key: t2_2.b
+ -> Merge Left Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+ Merge Cond: ((((t3_2.a + t3_2.b) / 2)) = t2_2.b)
+ -> Sort
+ Output: t3_2.a, t3_2.b, t3_2.c, (((t3_2.a + t3_2.b) / 2))
+ Sort Key: (((t3_2.a + t3_2.b) / 2))
+ -> Seq Scan on public.prt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c, ((t3_2.a + t3_2.b) / 2)
+ Filter: ((t3_2.a % 25) = 0)
+ -> Sort
+ Output: t2_2.b, t2_2.c
+ Sort Key: t2_2.b
+ -> Seq Scan on public.prt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Sort
+ Output: t1_1.a, t1_1.c
+ Sort Key: t1_1.a
+ -> Seq Scan on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.c
+(81 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ | | | | 100 | 0050
+ | | | | 200 | 0100
+ | | | | 400 | 0200
+ | | | | 500 | 0250
+ | | | | 700 | 0350
+ | | | | 800 | 0400
+ | | | | 1000 | 0500
+ | | | | 1100 | 0550
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ | | | | 100 | 0050
+ | | | | 200 | 0100
+ | | | | 400 | 0200
+ | | | | 500 | 0250
+ | | | | 700 | 0350
+ | | | | 800 | 0400
+ | | | | 1000 | 0500
+ | | | | 1100 | 0550
+(12 rows)
+
+EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+ QUERY PLAN
+---------------------------------------------------------------------------------
+ Merge Append
+ Sort Key: t1.a
+ -> Merge Semi Join
+ Output: t1.a, t1.b, t1.c
+ Merge Cond: (t1.a = t1_3.b)
+ -> Sort
+ Output: t1.a, t1.b, t1.c
+ Sort Key: t1.a
+ -> Seq Scan on public.prt1_p1 t1
+ Output: t1.a, t1.b, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Materialize
+ Output: t1_3.b, t1_6.a, t1_6.b
+ -> Merge Semi Join
+ Output: t1_3.b, t1_6.a, t1_6.b
+ Merge Cond: (t1_3.b = (((t1_6.a + t1_6.b) / 2)))
+ -> Sort
+ Output: t1_3.b
+ Sort Key: t1_3.b
+ -> Seq Scan on public.prt2_p1 t1_3
+ Output: t1_3.b
+ -> Sort
+ Output: t1_6.a, t1_6.b, (((t1_6.a + t1_6.b) / 2))
+ Sort Key: (((t1_6.a + t1_6.b) / 2))
+ -> Seq Scan on public.prt1_e_p1 t1_6
+ Output: t1_6.a, t1_6.b, ((t1_6.a + t1_6.b) / 2)
+ Filter: ((t1_6.a % 25) = 0)
+ -> Merge Semi Join
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Merge Cond: (t1_2.a = t1_4.b)
+ -> Sort
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Sort Key: t1_2.a
+ -> Seq Scan on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Materialize
+ Output: t1_4.b, t1_7.a, t1_7.b
+ -> Merge Semi Join
+ Output: t1_4.b, t1_7.a, t1_7.b
+ Merge Cond: (t1_4.b = (((t1_7.a + t1_7.b) / 2)))
+ -> Sort
+ Output: t1_4.b
+ Sort Key: t1_4.b
+ -> Seq Scan on public.prt2_p2 t1_4
+ Output: t1_4.b
+ -> Sort
+ Output: t1_7.a, t1_7.b, (((t1_7.a + t1_7.b) / 2))
+ Sort Key: (((t1_7.a + t1_7.b) / 2))
+ -> Seq Scan on public.prt1_e_p2 t1_7
+ Output: t1_7.a, t1_7.b, ((t1_7.a + t1_7.b) / 2)
+ Filter: ((t1_7.a % 25) = 0)
+ -> Merge Semi Join
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Merge Cond: (t1_1.a = t1_5.b)
+ -> Sort
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Sort Key: t1_1.a
+ -> Seq Scan on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Materialize
+ Output: t1_5.b, t1_8.a, t1_8.b
+ -> Merge Semi Join
+ Output: t1_5.b, t1_8.a, t1_8.b
+ Merge Cond: (t1_5.b = (((t1_8.a + t1_8.b) / 2)))
+ -> Sort
+ Output: t1_5.b
+ Sort Key: t1_5.b
+ -> Seq Scan on public.prt2_p3 t1_5
+ Output: t1_5.b
+ -> Sort
+ Output: t1_8.a, t1_8.b, (((t1_8.a + t1_8.b) / 2))
+ Sort Key: (((t1_8.a + t1_8.b) / 2))
+ -> Seq Scan on public.prt1_e_p3 t1_8
+ Output: t1_8.a, t1_8.b, ((t1_8.a + t1_8.b) / 2)
+ Filter: ((t1_8.a % 25) = 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+ Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+ -> Append
+ -> Merge Left Join
+ Output: t3.a, t3.b, t3.c, t1.a, t1.c, t2.b, t2.c
+ Merge Cond: (t1.a = t2.b)
+ -> Sort
+ Output: t3.a, t3.b, t3.c, t1.a, t1.c
+ Sort Key: t1.a
+ -> Merge Left Join
+ Output: t3.a, t3.b, t3.c, t1.a, t1.c
+ Merge Cond: ((((t3.a + t3.b) / 2)) = t1.a)
+ -> Sort
+ Output: t3.a, t3.b, t3.c, (((t3.a + t3.b) / 2))
+ Sort Key: (((t3.a + t3.b) / 2))
+ -> Seq Scan on public.prt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c, ((t3.a + t3.b) / 2)
+ Filter: ((t3.a % 25) = 0)
+ -> Sort
+ Output: t1.a, t1.c
+ Sort Key: t1.a
+ -> Seq Scan on public.prt1_p1 t1
+ Output: t1.a, t1.c
+ -> Sort
+ Output: t2.b, t2.c
+ Sort Key: t2.b
+ -> Seq Scan on public.prt2_p1 t2
+ Output: t2.b, t2.c
+ -> Merge Left Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c, t2_1.b, t2_1.c
+ Merge Cond: (t1_2.a = t2_1.b)
+ -> Sort
+ Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c
+ Sort Key: t1_2.a
+ -> Merge Left Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c
+ Merge Cond: ((((t3_1.a + t3_1.b) / 2)) = t1_2.a)
+ -> Sort
+ Output: t3_1.a, t3_1.b, t3_1.c, (((t3_1.a + t3_1.b) / 2))
+ Sort Key: (((t3_1.a + t3_1.b) / 2))
+ -> Seq Scan on public.prt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c, ((t3_1.a + t3_1.b) / 2)
+ Filter: ((t3_1.a % 25) = 0)
+ -> Sort
+ Output: t1_2.a, t1_2.c
+ Sort Key: t1_2.a
+ -> Seq Scan on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.c
+ -> Sort
+ Output: t2_1.b, t2_1.c
+ Sort Key: t2_1.b
+ -> Seq Scan on public.prt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Merge Left Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c, t2_2.b, t2_2.c
+ Merge Cond: (t1_1.a = t2_2.b)
+ -> Sort
+ Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c
+ Sort Key: t1_1.a
+ -> Merge Left Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c
+ Merge Cond: ((((t3_2.a + t3_2.b) / 2)) = t1_1.a)
+ -> Sort
+ Output: t3_2.a, t3_2.b, t3_2.c, (((t3_2.a + t3_2.b) / 2))
+ Sort Key: (((t3_2.a + t3_2.b) / 2))
+ -> Seq Scan on public.prt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c, ((t3_2.a + t3_2.b) / 2)
+ Filter: ((t3_2.a % 25) = 0)
+ -> Sort
+ Output: t1_1.a, t1_1.c
+ Sort Key: t1_1.a
+ -> Seq Scan on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.c
+ -> Sort
+ Output: t2_2.b, t2_2.c
+ Sort Key: t2_2.b
+ -> Seq Scan on public.prt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+(81 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 50 | 0050 | | | 100 | 0050
+ 100 | 0100 | | | 200 | 0100
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 200 | 0200 | | | 400 | 0200
+ 250 | 0250 | | | 500 | 0250
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 350 | 0350 | | | 700 | 0350
+ 400 | 0400 | | | 800 | 0400
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ 500 | 0500 | | | 1000 | 0500
+ 550 | 0550 | | | 1100 | 0550
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 50 | 0050 | | | 100 | 0050
+ 100 | 0100 | | | 200 | 0100
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 200 | 0200 | | | 400 | 0200
+ 250 | 0250 | | | 500 | 0250
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 350 | 0350 | | | 700 | 0350
+ 400 | 0400 | | | 800 | 0400
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ 500 | 0500 | | | 1000 | 0500
+ 550 | 0550 | | | 1100 | 0550
+(12 rows)
+
+SET enable_seqscan TO off;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ QUERY PLAN
+----------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+ Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+ -> Append
+ -> Merge Left Join
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c
+ Merge Cond: (t2.b = t1.a)
+ -> Sort
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c
+ Sort Key: t2.b
+ -> Merge Left Join
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c
+ Merge Cond: (((t3.a + t3.b) / 2) = t2.b)
+ -> Index Scan using iprt1_e_p1_ab2 on public.prt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c
+ Filter: ((t3.a % 25) = 0)
+ -> Index Scan using iprt2_p1_b on public.prt2_p1 t2
+ Output: t2.b, t2.c
+ -> Index Scan using iprt1_p1_a on public.prt1_p1 t1
+ Output: t1.a, t1.c
+ -> Merge Left Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_2.a, t1_2.c
+ Merge Cond: (t2_1.b = t1_2.a)
+ -> Sort
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+ Sort Key: t2_1.b
+ -> Merge Left Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+ Merge Cond: (((t3_1.a + t3_1.b) / 2) = t2_1.b)
+ -> Index Scan using iprt1_e_p2_ab2 on public.prt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c
+ Filter: ((t3_1.a % 25) = 0)
+ -> Index Scan using iprt2_p2_b on public.prt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Index Scan using iprt1_p2_a on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.c
+ -> Merge Right Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_1.a, t1_1.c
+ Merge Cond: (t2_2.b = ((t3_2.a + t3_2.b) / 2))
+ -> Merge Left Join
+ Output: t2_2.b, t2_2.c, t1_1.a, t1_1.c
+ Merge Cond: (t2_2.b = t1_1.a)
+ -> Index Scan using iprt2_p3_b on public.prt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Index Scan using iprt1_p3_a on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.c
+ -> Index Scan using iprt1_e_p3_ab2 on public.prt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c
+ Filter: ((t3_2.a % 25) = 0)
+(51 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ | | | | 100 | 0050
+ | | | | 200 | 0100
+ | | | | 400 | 0200
+ | | | | 500 | 0250
+ | | | | 700 | 0350
+ | | | | 800 | 0400
+ | | | | 1000 | 0500
+ | | | | 1100 | 0550
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ | | | | 100 | 0050
+ | | | | 200 | 0100
+ | | | | 400 | 0200
+ | | | | 500 | 0250
+ | | | | 700 | 0350
+ | | | | 800 | 0400
+ | | | | 1000 | 0500
+ | | | | 1100 | 0550
+(12 rows)
+
+EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+ QUERY PLAN
+----------------------------------------------------------------------------------
+ Merge Append
+ Sort Key: t1.a
+ -> Merge Semi Join
+ Output: t1.a, t1.b, t1.c
+ Merge Cond: (t1.a = t1_3.b)
+ -> Index Scan using iprt1_p1_a on public.prt1_p1 t1
+ Output: t1.a, t1.b, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Materialize
+ Output: t1_3.b, t1_6.a, t1_6.b
+ -> Merge Semi Join
+ Output: t1_3.b, t1_6.a, t1_6.b
+ Merge Cond: (t1_3.b = ((t1_6.a + t1_6.b) / 2))
+ -> Index Only Scan using iprt2_p1_b on public.prt2_p1 t1_3
+ Output: t1_3.b
+ -> Index Scan using iprt1_e_p1_ab2 on public.prt1_e_p1 t1_6
+ Output: t1_6.a, t1_6.b
+ Filter: ((t1_6.a % 25) = 0)
+ -> Merge Semi Join
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Merge Cond: (t1_2.a = t1_4.b)
+ -> Index Scan using iprt1_p2_a on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Materialize
+ Output: t1_4.b, t1_7.a, t1_7.b
+ -> Merge Semi Join
+ Output: t1_4.b, t1_7.a, t1_7.b
+ Merge Cond: (t1_4.b = ((t1_7.a + t1_7.b) / 2))
+ -> Index Only Scan using iprt2_p2_b on public.prt2_p2 t1_4
+ Output: t1_4.b
+ -> Index Scan using iprt1_e_p2_ab2 on public.prt1_e_p2 t1_7
+ Output: t1_7.a, t1_7.b
+ Filter: ((t1_7.a % 25) = 0)
+ -> Merge Semi Join
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Merge Cond: (t1_1.a = t1_5.b)
+ -> Index Scan using iprt1_p3_a on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Materialize
+ Output: t1_5.b, t1_8.a, t1_8.b
+ -> Merge Semi Join
+ Output: t1_5.b, t1_8.a, t1_8.b
+ Merge Cond: (t1_5.b = ((t1_8.a + t1_8.b) / 2))
+ -> Index Only Scan using iprt2_p3_b on public.prt2_p3 t1_5
+ Output: t1_5.b
+ -> Index Scan using iprt1_e_p3_ab2 on public.prt1_e_p3 t1_8
+ Output: t1_8.a, t1_8.b
+ Filter: ((t1_8.a % 25) = 0)
+(50 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ QUERY PLAN
+----------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+ Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+ -> Append
+ -> Merge Left Join
+ Output: t3.a, t3.b, t3.c, t1.a, t1.c, t2.b, t2.c
+ Merge Cond: (t1.a = t2.b)
+ -> Sort
+ Output: t3.a, t3.b, t3.c, t1.a, t1.c
+ Sort Key: t1.a
+ -> Merge Left Join
+ Output: t3.a, t3.b, t3.c, t1.a, t1.c
+ Merge Cond: (((t3.a + t3.b) / 2) = t1.a)
+ -> Index Scan using iprt1_e_p1_ab2 on public.prt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c
+ Filter: ((t3.a % 25) = 0)
+ -> Index Scan using iprt1_p1_a on public.prt1_p1 t1
+ Output: t1.a, t1.c
+ -> Index Scan using iprt2_p1_b on public.prt2_p1 t2
+ Output: t2.b, t2.c
+ -> Merge Left Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c, t2_1.b, t2_1.c
+ Merge Cond: (t1_2.a = t2_1.b)
+ -> Sort
+ Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c
+ Sort Key: t1_2.a
+ -> Merge Left Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c
+ Merge Cond: (((t3_1.a + t3_1.b) / 2) = t1_2.a)
+ -> Index Scan using iprt1_e_p2_ab2 on public.prt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c
+ Filter: ((t3_1.a % 25) = 0)
+ -> Index Scan using iprt1_p2_a on public.prt1_p2 t1_2
+ Output: t1_2.a, t1_2.c
+ -> Index Scan using iprt2_p2_b on public.prt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Merge Left Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c, t2_2.b, t2_2.c
+ Merge Cond: (t1_1.a = t2_2.b)
+ -> Sort
+ Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c
+ Sort Key: t1_1.a
+ -> Merge Left Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c
+ Merge Cond: (((t3_2.a + t3_2.b) / 2) = t1_1.a)
+ -> Index Scan using iprt1_e_p3_ab2 on public.prt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c
+ Filter: ((t3_2.a % 25) = 0)
+ -> Index Scan using iprt1_p3_a on public.prt1_p3 t1_1
+ Output: t1_1.a, t1_1.c
+ -> Index Scan using iprt2_p3_b on public.prt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+(54 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 50 | 0050 | | | 100 | 0050
+ 100 | 0100 | | | 200 | 0100
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 200 | 0200 | | | 400 | 0200
+ 250 | 0250 | | | 500 | 0250
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 350 | 0350 | | | 700 | 0350
+ 400 | 0400 | | | 800 | 0400
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ 500 | 0500 | | | 1000 | 0500
+ 550 | 0550 | | | 1100 | 0550
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+------
+ 0 | 0000 | 0 | 0000 | 0 | 0000
+ 50 | 0050 | | | 100 | 0050
+ 100 | 0100 | | | 200 | 0100
+ 150 | 0150 | 150 | 0150 | 300 | 0150
+ 200 | 0200 | | | 400 | 0200
+ 250 | 0250 | | | 500 | 0250
+ 300 | 0300 | 300 | 0300 | 600 | 0300
+ 350 | 0350 | | | 700 | 0350
+ 400 | 0400 | | | 800 | 0400
+ 450 | 0450 | 450 | 0450 | 900 | 0450
+ 500 | 0500 | | | 1000 | 0500
+ 550 | 0550 | | | 1100 | 0550
+(12 rows)
+
+-- lateral references and parameterized paths
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 4 5) is considered for partition-wise join.
+ QUERY PLAN
+---------------------------------------------------------------------------
+ Nested Loop Left Join
+ Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a
+ -> Merge Append
+ Sort Key: t1.a
+ -> Index Scan using iprt1_a on public.prt1 t1
+ Output: t1.a, t1.b, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Index Scan using iprt1_p1_a on public.prt1_p1 t1_1
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Index Scan using iprt1_p3_a on public.prt1_p3 t1_2
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Index Scan using iprt1_p2_a on public.prt1_p2 t1_3
+ Output: t1_3.a, t1_3.b, t1_3.c
+ Filter: ((t1_3.a % 25) = 0)
+ -> Append
+ -> Merge Join
+ Output: t2.a, t3.a, LEAST(t1.a, t2.a, t3.a)
+ Merge Cond: (t2.a = t3.b)
+ -> Index Only Scan using iprt1_p1_a on public.prt1_p1 t2
+ Output: t2.a
+ Index Cond: (t2.a = t1.a)
+ -> Index Scan using iprt2_p1_b on public.prt2_p1 t3
+ Output: t3.a, t3.b
+ -> Merge Join
+ Output: t2_2.a, t3_1.a, LEAST(t1.a, t2_2.a, t3_1.a)
+ Merge Cond: (t2_2.a = t3_1.b)
+ -> Index Only Scan using iprt1_p2_a on public.prt1_p2 t2_2
+ Output: t2_2.a
+ Index Cond: (t2_2.a = t1.a)
+ -> Index Scan using iprt2_p2_b on public.prt2_p2 t3_1
+ Output: t3_1.a, t3_1.b
+ -> Merge Join
+ Output: t2_1.a, t3_2.a, LEAST(t1.a, t2_1.a, t3_2.a)
+ Merge Cond: (t2_1.a = t3_2.b)
+ -> Index Only Scan using iprt1_p3_a on public.prt1_p3 t2_1
+ Output: t2_1.a
+ Index Cond: (t2_1.a = t1.a)
+ -> Index Scan using iprt2_p3_b on public.prt2_p3 t3_2
+ Output: t3_2.a, t3_2.b
+(41 rows)
+
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 4 5) is considered for partition-wise join.
+ a | b | c | t2a | t3a | least
+-----+-----+------+-----+-----+-------
+ 0 | 0 | 0000 | 0 | 0 | 0
+ 50 | 50 | 0050 | | |
+ 100 | 100 | 0100 | | |
+ 150 | 150 | 0150 | 150 | 150 | 150
+ 200 | 200 | 0200 | | |
+ 250 | 250 | 0250 | | |
+ 300 | 300 | 0300 | 300 | 300 | 300
+ 350 | 350 | 0350 | | |
+ 400 | 400 | 0400 | | |
+ 450 | 450 | 0450 | 450 | 450 | 450
+ 500 | 500 | 0500 | | |
+ 550 | 550 | 0550 | | |
+(12 rows)
+
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+ a | b | c | t2a | t3a | least
+-----+-----+------+-----+-----+-------
+ 0 | 0 | 0000 | 0 | 0 | 0
+ 50 | 50 | 0050 | | |
+ 100 | 100 | 0100 | | |
+ 150 | 150 | 0150 | 150 | 150 | 150
+ 200 | 200 | 0200 | | |
+ 250 | 250 | 0250 | | |
+ 300 | 300 | 0300 | 300 | 300 | 300
+ 350 | 350 | 0350 | | |
+ 400 | 400 | 0400 | | |
+ 450 | 450 | 0450 | 450 | 450 | 450
+ 500 | 500 | 0500 | | |
+ 550 | 550 | 0550 | | |
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ QUERY PLAN
+---------------------------------------------------------------------------
+ Nested Loop Left Join
+ Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a
+ -> Merge Append
+ Sort Key: t1.a
+ -> Index Scan using iprt1_a on public.prt1 t1
+ Output: t1.a, t1.b, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Index Scan using iprt1_p1_a on public.prt1_p1 t1_1
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Index Scan using iprt1_p3_a on public.prt1_p3 t1_2
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Index Scan using iprt1_p2_a on public.prt1_p2 t1_3
+ Output: t1_3.a, t1_3.b, t1_3.c
+ Filter: ((t1_3.a % 25) = 0)
+ -> Append
+ -> Merge Join
+ Output: t2.a, t3.a, LEAST(t1.a, t2.a, t3.a)
+ Merge Cond: (t2.a = t3.b)
+ -> Index Only Scan using iprt1_p1_a on public.prt1_p1 t2
+ Output: t2.a
+ Index Cond: (t2.a = t1.b)
+ -> Index Scan using iprt2_p1_b on public.prt2_p1 t3
+ Output: t3.a, t3.b
+ -> Merge Join
+ Output: t2_2.a, t3_1.a, LEAST(t1.a, t2_2.a, t3_1.a)
+ Merge Cond: (t2_2.a = t3_1.b)
+ -> Index Only Scan using iprt1_p2_a on public.prt1_p2 t2_2
+ Output: t2_2.a
+ Index Cond: (t2_2.a = t1.b)
+ -> Index Scan using iprt2_p2_b on public.prt2_p2 t3_1
+ Output: t3_1.a, t3_1.b
+ -> Merge Join
+ Output: t2_1.a, t3_2.a, LEAST(t1.a, t2_1.a, t3_2.a)
+ Merge Cond: (t2_1.a = t3_2.b)
+ -> Index Only Scan using iprt1_p3_a on public.prt1_p3 t2_1
+ Output: t2_1.a
+ Index Cond: (t2_1.a = t1.b)
+ -> Index Scan using iprt2_p3_b on public.prt2_p3 t3_2
+ Output: t3_2.a, t3_2.b
+(41 rows)
+
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ a | b | c | t2a | t3a | least
+-----+-----+------+-----+-----+-------
+ 0 | 0 | 0000 | 0 | 0 | 0
+ 50 | 50 | 0050 | | |
+ 100 | 100 | 0100 | | |
+ 150 | 150 | 0150 | 150 | 150 | 150
+ 200 | 200 | 0200 | | |
+ 250 | 250 | 0250 | | |
+ 300 | 300 | 0300 | 300 | 300 | 300
+ 350 | 350 | 0350 | | |
+ 400 | 400 | 0400 | | |
+ 450 | 450 | 0450 | 450 | 450 | 450
+ 500 | 500 | 0500 | | |
+ 550 | 550 | 0550 | | |
+(12 rows)
+
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+ a | b | c | t2a | t3a | least
+-----+-----+------+-----+-----+-------
+ 0 | 0 | 0000 | 0 | 0 | 0
+ 50 | 50 | 0050 | | |
+ 100 | 100 | 0100 | | |
+ 150 | 150 | 0150 | 150 | 150 | 150
+ 200 | 200 | 0200 | | |
+ 250 | 250 | 0250 | | |
+ 300 | 300 | 0300 | 300 | 300 | 300
+ 350 | 350 | 0350 | | |
+ 400 | 400 | 0400 | | |
+ 450 | 450 | 0450 | 450 | 450 | 450
+ 500 | 500 | 0500 | | |
+ 550 | 550 | 0550 | | |
+(12 rows)
+
+RESET enable_hashjoin;
+RESET enable_nestloop;
+RESET enable_seqscan;
+--
+-- partitioned by multiple columns
+--
+CREATE TABLE prt1_m (a int, b int, c varchar) PARTITION BY RANGE(a, ((a + b)/2));
+CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES START (0, 0) END (250, 250);
+CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES START (250, 250) END (500, 500);
+CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES START (500, 500) END (600, 600);
+INSERT INTO prt1_m SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1_m;
+ANALYZE prt1_m_p1;
+ANALYZE prt1_m_p2;
+ANALYZE prt1_m_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1_m AS SELECT * FROM prt1_m;
+CREATE TABLE prt2_m (a int, b int, c varchar) PARTITION BY RANGE(((b + a)/2), b);
+CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES START (0, 0) END (250, 250);
+CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES START (250, 250) END (500, 500);
+CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES START (500, 500) END (600, 600);
+INSERT INTO prt2_m SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE prt2_m;
+ANALYZE prt2_m_p1;
+ANALYZE prt2_m_p2;
+ANALYZE prt2_m_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt2_m AS SELECT * FROM prt2_m;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Append
+ -> Hash Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: ((((t2.b + t2.a) / 2) = t1.a) AND (t2.b = ((t1.a + t1.b) / 2)))
+ -> Seq Scan on public.prt2_m_p1 t2
+ Output: t2.b, t2.c, t2.a
+ -> Hash
+ Output: t1.a, t1.c, t1.b
+ -> Seq Scan on public.prt1_m_p1 t1
+ Output: t1.a, t1.c, t1.b
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ Hash Cond: ((((t2_1.b + t2_1.a) / 2) = t1_1.a) AND (t2_1.b = ((t1_1.a + t1_1.b) / 2)))
+ -> Seq Scan on public.prt2_m_p2 t2_1
+ Output: t2_1.b, t2_1.c, t2_1.a
+ -> Hash
+ Output: t1_1.a, t1_1.c, t1_1.b
+ -> Seq Scan on public.prt1_m_p2 t1_1
+ Output: t1_1.a, t1_1.c, t1_1.b
+ Filter: ((t1_1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+ Hash Cond: ((((t2_2.b + t2_2.a) / 2) = t1_2.a) AND (t2_2.b = ((t1_2.a + t1_2.b) / 2)))
+ -> Seq Scan on public.prt2_m_p3 t2_2
+ Output: t2_2.b, t2_2.c, t2_2.a
+ -> Hash
+ Output: t1_2.a, t1_2.c, t1_2.b
+ -> Seq Scan on public.prt1_m_p3 t1_2
+ Output: t1_2.a, t1_2.c, t1_2.b
+ Filter: ((t1_2.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ a | c | b | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150
+ 300 | 0300 | 300 | 0300
+ 450 | 0450 | 450 | 0450
+(4 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1, uprt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Append
+ -> Hash Right Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: ((((t2.b + t2.a) / 2) = t1.a) AND (t2.b = ((t1.a + t1.b) / 2)))
+ -> Seq Scan on public.prt2_m_p1 t2
+ Output: t2.b, t2.c, t2.a
+ -> Hash
+ Output: t1.a, t1.c, t1.b
+ -> Seq Scan on public.prt1_m_p1 t1
+ Output: t1.a, t1.c, t1.b
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ Hash Cond: ((((t2_1.b + t2_1.a) / 2) = t1_1.a) AND (t2_1.b = ((t1_1.a + t1_1.b) / 2)))
+ -> Seq Scan on public.prt2_m_p2 t2_1
+ Output: t2_1.b, t2_1.c, t2_1.a
+ -> Hash
+ Output: t1_1.a, t1_1.c, t1_1.b
+ -> Seq Scan on public.prt1_m_p2 t1_1
+ Output: t1_1.a, t1_1.c, t1_1.b
+ Filter: ((t1_1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+ Hash Cond: ((((t2_2.b + t2_2.a) / 2) = t1_2.a) AND (t2_2.b = ((t1_2.a + t1_2.b) / 2)))
+ -> Seq Scan on public.prt2_m_p3 t2_2
+ Output: t2_2.b, t2_2.c, t2_2.a
+ -> Hash
+ Output: t1_2.a, t1_2.c, t1_2.b
+ -> Seq Scan on public.prt1_m_p3 t1_2
+ Output: t1_2.a, t1_2.c, t1_2.b
+ Filter: ((t1_2.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ 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 | |
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1 LEFT JOIN uprt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 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 | |
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 RIGHT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c
+ -> Append
+ -> Hash Right Join
+ Output: t2.b, t2.c, t1.a, t1.c
+ Hash Cond: ((((t1.a + t1.b) / 2) = t2.b) AND (t1.a = ((t2.b + t2.a) / 2)))
+ -> Seq Scan on public.prt1_m_p1 t1
+ Output: t1.a, t1.c, t1.b
+ -> Hash
+ Output: t2.b, t2.c, t2.a
+ -> Seq Scan on public.prt2_m_p1 t2
+ Output: t2.b, t2.c, t2.a
+ Filter: ((t2.b % 25) = 0)
+ -> Hash Right Join
+ Output: t2_1.b, t2_1.c, t1_1.a, t1_1.c
+ Hash Cond: ((((t1_1.a + t1_1.b) / 2) = t2_1.b) AND (t1_1.a = ((t2_1.b + t2_1.a) / 2)))
+ -> Seq Scan on public.prt1_m_p2 t1_1
+ Output: t1_1.a, t1_1.c, t1_1.b
+ -> Hash
+ Output: t2_1.b, t2_1.c, t2_1.a
+ -> Seq Scan on public.prt2_m_p2 t2_1
+ Output: t2_1.b, t2_1.c, t2_1.a
+ Filter: ((t2_1.b % 25) = 0)
+ -> Hash Right Join
+ Output: t2_2.b, t2_2.c, t1_2.a, t1_2.c
+ Hash Cond: ((((t1_2.a + t1_2.b) / 2) = t2_2.b) AND (t1_2.a = ((t2_2.b + t2_2.a) / 2)))
+ -> Seq Scan on public.prt1_m_p3 t1_2
+ Output: t1_2.a, t1_2.c, t1_2.b
+ -> Hash
+ Output: t2_2.b, t2_2.c, t2_2.a
+ -> Seq Scan on public.prt2_m_p3 t2_2
+ Output: t2_2.b, t2_2.c, t2_2.a
+ Filter: ((t2_2.b % 25) = 0)
+(36 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 RIGHT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+ 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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1 RIGHT JOIN uprt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.b % 25 = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: prt1_m_p1.a, prt1_m_p1.c, prt2_m_p1.b, prt2_m_p1.c
+ Sort Key: prt1_m_p1.a, prt2_m_p1.b
+ -> Append
+ -> Hash Full Join
+ Output: prt1_m_p1.a, prt1_m_p1.c, prt2_m_p1.b, prt2_m_p1.c
+ 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 public.prt1_m_p1
+ Output: prt1_m_p1.a, prt1_m_p1.c, prt1_m_p1.b
+ Filter: ((prt1_m_p1.a % 25) = 0)
+ -> Hash
+ Output: prt2_m_p1.b, prt2_m_p1.c, prt2_m_p1.a
+ -> Seq Scan on public.prt2_m_p1
+ Output: prt2_m_p1.b, prt2_m_p1.c, prt2_m_p1.a
+ Filter: ((prt2_m_p1.b % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_m_p2.a, prt1_m_p2.c, prt2_m_p2.b, prt2_m_p2.c
+ 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 public.prt1_m_p2
+ Output: prt1_m_p2.a, prt1_m_p2.c, prt1_m_p2.b
+ Filter: ((prt1_m_p2.a % 25) = 0)
+ -> Hash
+ Output: prt2_m_p2.b, prt2_m_p2.c, prt2_m_p2.a
+ -> Seq Scan on public.prt2_m_p2
+ Output: prt2_m_p2.b, prt2_m_p2.c, prt2_m_p2.a
+ Filter: ((prt2_m_p2.b % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_m_p3.a, prt1_m_p3.c, prt2_m_p3.b, prt2_m_p3.c
+ 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 public.prt1_m_p3
+ Output: prt1_m_p3.a, prt1_m_p3.c, prt1_m_p3.b
+ Filter: ((prt1_m_p3.a % 25) = 0)
+ -> Hash
+ Output: prt2_m_p3.b, prt2_m_p3.c, prt2_m_p3.a
+ -> Seq Scan on public.prt2_m_p3
+ Output: prt2_m_p3.b, prt2_m_p3.c, prt2_m_p3.a
+ Filter: ((prt2_m_p3.b % 25) = 0)
+(37 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.b % 25 = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ 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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_m t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_m t2 WHERE t2.b % 25 = 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 | 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)
+
+--
+-- multi-leveled partitions
+--
+CREATE TABLE prt1_l (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_l_p1 PARTITION OF prt1_l FOR VALUES START (0) END (250) PARTITION BY RANGE (b);
+CREATE TABLE prt1_l_p1_p1 PARTITION OF prt1_l_p1 FOR VALUES START (0) END (100);
+CREATE TABLE prt1_l_p1_p2 PARTITION OF prt1_l_p1 FOR VALUES START (100) END (250);
+CREATE TABLE prt1_l_p2 PARTITION OF prt1_l FOR VALUES START (250) END (500) PARTITION BY RANGE (c);
+CREATE TABLE prt1_l_p2_p1 PARTITION OF prt1_l_p2 FOR VALUES START ('0250') END ('0400');
+CREATE TABLE prt1_l_p2_p2 PARTITION OF prt1_l_p2 FOR VALUES START ('0400') END ('0500');
+CREATE TABLE prt1_l_p3 PARTITION OF prt1_l FOR VALUES START (500) END (600) PARTITION BY RANGE ((b + a));
+CREATE TABLE prt1_l_p3_p1 PARTITION OF prt1_l_p3 FOR VALUES START (1000) END (1100);
+CREATE TABLE prt1_l_p3_p2 PARTITION OF prt1_l_p3 FOR VALUES START (1100) END (1200);
+INSERT INTO prt1_l SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1_l;
+ANALYZE prt1_l_p1;
+ANALYZE prt1_l_p1_p1;
+ANALYZE prt1_l_p1_p2;
+ANALYZE prt1_l_p2;
+ANALYZE prt1_l_p2_p1;
+ANALYZE prt1_l_p2_p2;
+ANALYZE prt1_l_p3;
+ANALYZE prt1_l_p3_p1;
+ANALYZE prt1_l_p3_p2;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1_l AS SELECT * FROM prt1_l;
+CREATE TABLE prt2_l (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_l_p1 PARTITION OF prt2_l FOR VALUES START (0) END (250) PARTITION BY RANGE (a);
+CREATE TABLE prt2_l_p1_p1 PARTITION OF prt2_l_p1 FOR VALUES START (0) END (100);
+CREATE TABLE prt2_l_p1_p2 PARTITION OF prt2_l_p1 FOR VALUES START (100) END (250);
+CREATE TABLE prt2_l_p2 PARTITION OF prt2_l FOR VALUES START (250) END (500) PARTITION BY RANGE (c);
+CREATE TABLE prt2_l_p2_p1 PARTITION OF prt2_l_p2 FOR VALUES START ('0250') END ('0400');
+CREATE TABLE prt2_l_p2_p2 PARTITION OF prt2_l_p2 FOR VALUES START ('0400') END ('0500');
+CREATE TABLE prt2_l_p3 PARTITION OF prt2_l FOR VALUES START (500) END (600) PARTITION BY RANGE ((a + b));
+CREATE TABLE prt2_l_p3_p1 PARTITION OF prt2_l_p3 FOR VALUES START (1000) END (1100);
+CREATE TABLE prt2_l_p3_p2 PARTITION OF prt2_l_p3 FOR VALUES START (1100) END (1200);
+INSERT INTO prt2_l SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE prt2_l;
+ANALYZE prt2_l_p1;
+ANALYZE prt2_l_p1_p1;
+ANALYZE prt2_l_p1_p2;
+ANALYZE prt2_l_p2;
+ANALYZE prt2_l_p2_p1;
+ANALYZE prt2_l_p2_p2;
+ANALYZE prt2_l_p3;
+ANALYZE prt2_l_p3_p1;
+ANALYZE prt2_l_p3_p2;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt2_l AS SELECT * FROM prt2_l;
+EXPLAIN (VERBOSE, 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 = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 17) is considered for partition-wise join.
+NOTICE: join between relations (b 8) and (b 21) is considered for partition-wise join.
+NOTICE: join between relations (b 12) and (b 25) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: ((t2.b = t1.a) AND (t2.a = t1.b) AND ((t2.c)::text = (t1.c)::text) AND ((t2.a + t2.b) = (t1.b + t1.a)))
+ -> Seq Scan on public.prt2_l_p1_p1 t2
+ Output: t2.b, t2.c, t2.a
+ -> Hash
+ Output: t1.a, t1.c, t1.b
+ -> Seq Scan on public.prt1_l_p1_p1 t1
+ Output: t1.a, t1.c, t1.b
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ Hash Cond: ((t2_1.b = t1_1.a) AND (t2_1.a = t1_1.b) AND ((t2_1.c)::text = (t1_1.c)::text) AND ((t2_1.a + t2_1.b) = (t1_1.b + t1_1.a)))
+ -> Seq Scan on public.prt2_l_p1_p2 t2_1
+ Output: t2_1.b, t2_1.c, t2_1.a
+ -> Hash
+ Output: t1_1.a, t1_1.c, t1_1.b
+ -> Seq Scan on public.prt1_l_p1_p2 t1_1
+ Output: t1_1.a, t1_1.c, t1_1.b
+ Filter: ((t1_1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+ Hash Cond: ((t2_2.b = t1_2.a) AND (t2_2.a = t1_2.b) AND ((t2_2.c)::text = (t1_2.c)::text) AND ((t2_2.a + t2_2.b) = (t1_2.b + t1_2.a)))
+ -> Seq Scan on public.prt2_l_p2_p1 t2_2
+ Output: t2_2.b, t2_2.c, t2_2.a
+ -> Hash
+ Output: t1_2.a, t1_2.c, t1_2.b
+ -> Seq Scan on public.prt1_l_p2_p1 t1_2
+ Output: t1_2.a, t1_2.c, t1_2.b
+ Filter: ((t1_2.a % 25) = 0)
+ -> Hash Join
+ Output: t1_3.a, t1_3.c, t2_3.b, t2_3.c
+ Hash Cond: ((t2_3.b = t1_3.a) AND (t2_3.a = t1_3.b) AND ((t2_3.c)::text = (t1_3.c)::text) AND ((t2_3.a + t2_3.b) = (t1_3.b + t1_3.a)))
+ -> Seq Scan on public.prt2_l_p2_p2 t2_3
+ Output: t2_3.b, t2_3.c, t2_3.a
+ -> Hash
+ Output: t1_3.a, t1_3.c, t1_3.b
+ -> Seq Scan on public.prt1_l_p2_p2 t1_3
+ Output: t1_3.a, t1_3.c, t1_3.b
+ Filter: ((t1_3.a % 25) = 0)
+ -> Hash Join
+ Output: t1_4.a, t1_4.c, t2_4.b, t2_4.c
+ Hash Cond: ((t2_4.b = t1_4.a) AND (t2_4.a = t1_4.b) AND ((t2_4.c)::text = (t1_4.c)::text) AND ((t2_4.a + t2_4.b) = (t1_4.b + t1_4.a)))
+ -> Seq Scan on public.prt2_l_p3_p1 t2_4
+ Output: t2_4.b, t2_4.c, t2_4.a
+ -> Hash
+ Output: t1_4.a, t1_4.c, t1_4.b
+ -> Seq Scan on public.prt1_l_p3_p1 t1_4
+ Output: t1_4.a, t1_4.c, t1_4.b
+ Filter: ((t1_4.a % 25) = 0)
+ -> Hash Join
+ Output: t1_5.a, t1_5.c, t2_5.b, t2_5.c
+ Hash Cond: ((t2_5.b = t1_5.a) AND (t2_5.a = t1_5.b) AND ((t2_5.c)::text = (t1_5.c)::text) AND ((t2_5.a + t2_5.b) = (t1_5.b + t1_5.a)))
+ -> Seq Scan on public.prt2_l_p3_p2 t2_5
+ Output: t2_5.b, t2_5.c, t2_5.a
+ -> Hash
+ Output: t1_5.a, t1_5.c, t1_5.b
+ -> Seq Scan on public.prt1_l_p3_p2 t1_5
+ Output: t1_5.a, t1_5.c, t1_5.b
+ Filter: ((t1_5.a % 25) = 0)
+(64 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 17) is considered for partition-wise join.
+NOTICE: join between relations (b 8) and (b 21) is considered for partition-wise join.
+NOTICE: join between relations (b 12) and (b 25) is considered for partition-wise join.
+ a | c | b | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 150 | 0150 | 150 | 0150
+ 300 | 0300 | 300 | 0300
+ 450 | 0450 | 450 | 0450
+(4 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1, uprt2_l t2 WHERE t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 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)
+
+EXPLAIN (VERBOSE, 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.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 5) and (b 18) is considered for partition-wise join.
+NOTICE: join between relations (b 9) and (b 22) is considered for partition-wise join.
+NOTICE: join between relations (b 13) and (b 26) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Append
+ -> Hash Right Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: ((t2.b = t1.a) AND (t2.a = t1.b) AND ((t2.c)::text = (t1.c)::text) AND ((t2.a + t2.b) = (t1.b + t1.a)))
+ -> Seq Scan on public.prt2_l_p1_p1 t2
+ Output: t2.b, t2.c, t2.a
+ -> Hash
+ Output: t1.a, t1.c, t1.b
+ -> Seq Scan on public.prt1_l_p1_p1 t1
+ Output: t1.a, t1.c, t1.b
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ Hash Cond: ((t2_1.b = t1_1.a) AND (t2_1.a = t1_1.b) AND ((t2_1.c)::text = (t1_1.c)::text) AND ((t2_1.a + t2_1.b) = (t1_1.b + t1_1.a)))
+ -> Seq Scan on public.prt2_l_p1_p2 t2_1
+ Output: t2_1.b, t2_1.c, t2_1.a
+ -> Hash
+ Output: t1_1.a, t1_1.c, t1_1.b
+ -> Seq Scan on public.prt1_l_p1_p2 t1_1
+ Output: t1_1.a, t1_1.c, t1_1.b
+ Filter: ((t1_1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+ Hash Cond: ((t2_2.b = t1_2.a) AND (t2_2.a = t1_2.b) AND ((t2_2.c)::text = (t1_2.c)::text) AND ((t2_2.a + t2_2.b) = (t1_2.b + t1_2.a)))
+ -> Seq Scan on public.prt2_l_p2_p1 t2_2
+ Output: t2_2.b, t2_2.c, t2_2.a
+ -> Hash
+ Output: t1_2.a, t1_2.c, t1_2.b
+ -> Seq Scan on public.prt1_l_p2_p1 t1_2
+ Output: t1_2.a, t1_2.c, t1_2.b
+ Filter: ((t1_2.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_3.a, t1_3.c, t2_3.b, t2_3.c
+ Hash Cond: ((t2_3.b = t1_3.a) AND (t2_3.a = t1_3.b) AND ((t2_3.c)::text = (t1_3.c)::text) AND ((t2_3.a + t2_3.b) = (t1_3.b + t1_3.a)))
+ -> Seq Scan on public.prt2_l_p2_p2 t2_3
+ Output: t2_3.b, t2_3.c, t2_3.a
+ -> Hash
+ Output: t1_3.a, t1_3.c, t1_3.b
+ -> Seq Scan on public.prt1_l_p2_p2 t1_3
+ Output: t1_3.a, t1_3.c, t1_3.b
+ Filter: ((t1_3.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_4.a, t1_4.c, t2_4.b, t2_4.c
+ Hash Cond: ((t2_4.b = t1_4.a) AND (t2_4.a = t1_4.b) AND ((t2_4.c)::text = (t1_4.c)::text) AND ((t2_4.a + t2_4.b) = (t1_4.b + t1_4.a)))
+ -> Seq Scan on public.prt2_l_p3_p1 t2_4
+ Output: t2_4.b, t2_4.c, t2_4.a
+ -> Hash
+ Output: t1_4.a, t1_4.c, t1_4.b
+ -> Seq Scan on public.prt1_l_p3_p1 t1_4
+ Output: t1_4.a, t1_4.c, t1_4.b
+ Filter: ((t1_4.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_5.a, t1_5.c, t2_5.b, t2_5.c
+ Hash Cond: ((t2_5.b = t1_5.a) AND (t2_5.a = t1_5.b) AND ((t2_5.c)::text = (t1_5.c)::text) AND ((t2_5.a + t2_5.b) = (t1_5.b + t1_5.a)))
+ -> Seq Scan on public.prt2_l_p3_p2 t2_5
+ Output: t2_5.b, t2_5.c, t2_5.a
+ -> Hash
+ Output: t1_5.a, t1_5.c, t1_5.b
+ -> Seq Scan on public.prt1_l_p3_p2 t1_5
+ Output: t1_5.a, t1_5.c, t1_5.b
+ Filter: ((t1_5.a % 25) = 0)
+(64 rows)
+
+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.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 5) and (b 18) is considered for partition-wise join.
+NOTICE: join between relations (b 9) and (b 22) is considered for partition-wise join.
+NOTICE: join between relations (b 13) and (b 26) is considered for partition-wise join.
+ 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 | |
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1 LEFT JOIN uprt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 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 | |
+(12 rows)
+
+EXPLAIN (VERBOSE, 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.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 18) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 22) and (b 9) is considered for partition-wise join.
+NOTICE: join between relations (b 26) and (b 13) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c
+ -> Append
+ -> Hash Right Join
+ Output: t2.b, t2.c, t1.a, t1.c
+ Hash Cond: ((t1.a = t2.b) AND (t1.b = t2.a) AND ((t1.c)::text = (t2.c)::text) AND ((t1.b + t1.a) = (t2.a + t2.b)))
+ -> Seq Scan on public.prt1_l_p1_p1 t1
+ Output: t1.a, t1.c, t1.b
+ -> Hash
+ Output: t2.b, t2.c, t2.a
+ -> Seq Scan on public.prt2_l_p1_p1 t2
+ Output: t2.b, t2.c, t2.a
+ Filter: ((t2.b % 25) = 0)
+ -> Hash Right Join
+ Output: t2_1.b, t2_1.c, t1_1.a, t1_1.c
+ Hash Cond: ((t1_1.a = t2_1.b) AND (t1_1.b = t2_1.a) AND ((t1_1.c)::text = (t2_1.c)::text) AND ((t1_1.b + t1_1.a) = (t2_1.a + t2_1.b)))
+ -> Seq Scan on public.prt1_l_p1_p2 t1_1
+ Output: t1_1.a, t1_1.c, t1_1.b
+ -> Hash
+ Output: t2_1.b, t2_1.c, t2_1.a
+ -> Seq Scan on public.prt2_l_p1_p2 t2_1
+ Output: t2_1.b, t2_1.c, t2_1.a
+ Filter: ((t2_1.b % 25) = 0)
+ -> Hash Right Join
+ Output: t2_2.b, t2_2.c, t1_2.a, t1_2.c
+ Hash Cond: ((t1_2.a = t2_2.b) AND (t1_2.b = t2_2.a) AND ((t1_2.c)::text = (t2_2.c)::text) AND ((t1_2.b + t1_2.a) = (t2_2.a + t2_2.b)))
+ -> Seq Scan on public.prt1_l_p2_p1 t1_2
+ Output: t1_2.a, t1_2.c, t1_2.b
+ -> Hash
+ Output: t2_2.b, t2_2.c, t2_2.a
+ -> Seq Scan on public.prt2_l_p2_p1 t2_2
+ Output: t2_2.b, t2_2.c, t2_2.a
+ Filter: ((t2_2.b % 25) = 0)
+ -> Hash Right Join
+ Output: t2_3.b, t2_3.c, t1_3.a, t1_3.c
+ Hash Cond: ((t1_3.a = t2_3.b) AND (t1_3.b = t2_3.a) AND ((t1_3.c)::text = (t2_3.c)::text) AND ((t1_3.b + t1_3.a) = (t2_3.a + t2_3.b)))
+ -> Seq Scan on public.prt1_l_p2_p2 t1_3
+ Output: t1_3.a, t1_3.c, t1_3.b
+ -> Hash
+ Output: t2_3.b, t2_3.c, t2_3.a
+ -> Seq Scan on public.prt2_l_p2_p2 t2_3
+ Output: t2_3.b, t2_3.c, t2_3.a
+ Filter: ((t2_3.b % 25) = 0)
+ -> Hash Right Join
+ Output: t2_4.b, t2_4.c, t1_4.a, t1_4.c
+ Hash Cond: ((t1_4.a = t2_4.b) AND (t1_4.b = t2_4.a) AND ((t1_4.c)::text = (t2_4.c)::text) AND ((t1_4.b + t1_4.a) = (t2_4.a + t2_4.b)))
+ -> Seq Scan on public.prt1_l_p3_p1 t1_4
+ Output: t1_4.a, t1_4.c, t1_4.b
+ -> Hash
+ Output: t2_4.b, t2_4.c, t2_4.a
+ -> Seq Scan on public.prt2_l_p3_p1 t2_4
+ Output: t2_4.b, t2_4.c, t2_4.a
+ Filter: ((t2_4.b % 25) = 0)
+ -> Hash Right Join
+ Output: t2_5.b, t2_5.c, t1_5.a, t1_5.c
+ Hash Cond: ((t1_5.a = t2_5.b) AND (t1_5.b = t2_5.a) AND ((t1_5.c)::text = (t2_5.c)::text) AND ((t1_5.b + t1_5.a) = (t2_5.a + t2_5.b)))
+ -> Seq Scan on public.prt1_l_p3_p2 t1_5
+ Output: t1_5.a, t1_5.c, t1_5.b
+ -> Hash
+ Output: t2_5.b, t2_5.c, t2_5.a
+ -> Seq Scan on public.prt2_l_p3_p2 t2_5
+ Output: t2_5.b, t2_5.c, t2_5.a
+ Filter: ((t2_5.b % 25) = 0)
+(66 rows)
+
+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.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 18) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 22) and (b 9) is considered for partition-wise join.
+NOTICE: join between relations (b 26) and (b 13) is considered for partition-wise join.
+ 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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1 RIGHT JOIN uprt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 7) and (b 20) is considered for partition-wise join.
+NOTICE: join between relations (b 11) and (b 24) is considered for partition-wise join.
+NOTICE: join between relations (b 15) and (b 28) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: prt1_l_p1_p1.a, prt1_l_p1_p1.c, prt2_l_p1_p1.b, prt2_l_p1_p1.c
+ Sort Key: prt1_l_p1_p1.a, prt2_l_p1_p1.b
+ -> Append
+ -> Hash Full Join
+ Output: prt1_l_p1_p1.a, prt1_l_p1_p1.c, prt2_l_p1_p1.b, prt2_l_p1_p1.c
+ Hash Cond: ((prt1_l_p1_p1.a = prt2_l_p1_p1.b) AND (prt1_l_p1_p1.b = prt2_l_p1_p1.a) AND ((prt1_l_p1_p1.c)::text = (prt2_l_p1_p1.c)::text) AND ((prt1_l_p1_p1.b + prt1_l_p1_p1.a) = (prt2_l_p1_p1.a + prt2_l_p1_p1.b)))
+ -> Seq Scan on public.prt1_l_p1_p1
+ Output: prt1_l_p1_p1.a, prt1_l_p1_p1.c, prt1_l_p1_p1.b
+ Filter: ((prt1_l_p1_p1.a % 25) = 0)
+ -> Hash
+ Output: prt2_l_p1_p1.b, prt2_l_p1_p1.c, prt2_l_p1_p1.a
+ -> Seq Scan on public.prt2_l_p1_p1
+ Output: prt2_l_p1_p1.b, prt2_l_p1_p1.c, prt2_l_p1_p1.a
+ Filter: ((prt2_l_p1_p1.b % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_l_p1_p2.a, prt1_l_p1_p2.c, prt2_l_p1_p2.b, prt2_l_p1_p2.c
+ Hash Cond: ((prt1_l_p1_p2.a = prt2_l_p1_p2.b) AND (prt1_l_p1_p2.b = prt2_l_p1_p2.a) AND ((prt1_l_p1_p2.c)::text = (prt2_l_p1_p2.c)::text) AND ((prt1_l_p1_p2.b + prt1_l_p1_p2.a) = (prt2_l_p1_p2.a + prt2_l_p1_p2.b)))
+ -> Seq Scan on public.prt1_l_p1_p2
+ Output: prt1_l_p1_p2.a, prt1_l_p1_p2.c, prt1_l_p1_p2.b
+ Filter: ((prt1_l_p1_p2.a % 25) = 0)
+ -> Hash
+ Output: prt2_l_p1_p2.b, prt2_l_p1_p2.c, prt2_l_p1_p2.a
+ -> Seq Scan on public.prt2_l_p1_p2
+ Output: prt2_l_p1_p2.b, prt2_l_p1_p2.c, prt2_l_p1_p2.a
+ Filter: ((prt2_l_p1_p2.b % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_l_p2_p1.a, prt1_l_p2_p1.c, prt2_l_p2_p1.b, prt2_l_p2_p1.c
+ Hash Cond: ((prt1_l_p2_p1.a = prt2_l_p2_p1.b) AND (prt1_l_p2_p1.b = prt2_l_p2_p1.a) AND ((prt1_l_p2_p1.c)::text = (prt2_l_p2_p1.c)::text) AND ((prt1_l_p2_p1.b + prt1_l_p2_p1.a) = (prt2_l_p2_p1.a + prt2_l_p2_p1.b)))
+ -> Seq Scan on public.prt1_l_p2_p1
+ Output: prt1_l_p2_p1.a, prt1_l_p2_p1.c, prt1_l_p2_p1.b
+ Filter: ((prt1_l_p2_p1.a % 25) = 0)
+ -> Hash
+ Output: prt2_l_p2_p1.b, prt2_l_p2_p1.c, prt2_l_p2_p1.a
+ -> Seq Scan on public.prt2_l_p2_p1
+ Output: prt2_l_p2_p1.b, prt2_l_p2_p1.c, prt2_l_p2_p1.a
+ Filter: ((prt2_l_p2_p1.b % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_l_p2_p2.a, prt1_l_p2_p2.c, prt2_l_p2_p2.b, prt2_l_p2_p2.c
+ Hash Cond: ((prt1_l_p2_p2.a = prt2_l_p2_p2.b) AND (prt1_l_p2_p2.b = prt2_l_p2_p2.a) AND ((prt1_l_p2_p2.c)::text = (prt2_l_p2_p2.c)::text) AND ((prt1_l_p2_p2.b + prt1_l_p2_p2.a) = (prt2_l_p2_p2.a + prt2_l_p2_p2.b)))
+ -> Seq Scan on public.prt1_l_p2_p2
+ Output: prt1_l_p2_p2.a, prt1_l_p2_p2.c, prt1_l_p2_p2.b
+ Filter: ((prt1_l_p2_p2.a % 25) = 0)
+ -> Hash
+ Output: prt2_l_p2_p2.b, prt2_l_p2_p2.c, prt2_l_p2_p2.a
+ -> Seq Scan on public.prt2_l_p2_p2
+ Output: prt2_l_p2_p2.b, prt2_l_p2_p2.c, prt2_l_p2_p2.a
+ Filter: ((prt2_l_p2_p2.b % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_l_p3_p1.a, prt1_l_p3_p1.c, prt2_l_p3_p1.b, prt2_l_p3_p1.c
+ Hash Cond: ((prt1_l_p3_p1.a = prt2_l_p3_p1.b) AND (prt1_l_p3_p1.b = prt2_l_p3_p1.a) AND ((prt1_l_p3_p1.c)::text = (prt2_l_p3_p1.c)::text) AND ((prt1_l_p3_p1.b + prt1_l_p3_p1.a) = (prt2_l_p3_p1.a + prt2_l_p3_p1.b)))
+ -> Seq Scan on public.prt1_l_p3_p1
+ Output: prt1_l_p3_p1.a, prt1_l_p3_p1.c, prt1_l_p3_p1.b
+ Filter: ((prt1_l_p3_p1.a % 25) = 0)
+ -> Hash
+ Output: prt2_l_p3_p1.b, prt2_l_p3_p1.c, prt2_l_p3_p1.a
+ -> Seq Scan on public.prt2_l_p3_p1
+ Output: prt2_l_p3_p1.b, prt2_l_p3_p1.c, prt2_l_p3_p1.a
+ Filter: ((prt2_l_p3_p1.b % 25) = 0)
+ -> Hash Full Join
+ Output: prt1_l_p3_p2.a, prt1_l_p3_p2.c, prt2_l_p3_p2.b, prt2_l_p3_p2.c
+ Hash Cond: ((prt1_l_p3_p2.a = prt2_l_p3_p2.b) AND (prt1_l_p3_p2.b = prt2_l_p3_p2.a) AND ((prt1_l_p3_p2.c)::text = (prt2_l_p3_p2.c)::text) AND ((prt1_l_p3_p2.b + prt1_l_p3_p2.a) = (prt2_l_p3_p2.a + prt2_l_p3_p2.b)))
+ -> Seq Scan on public.prt1_l_p3_p2
+ Output: prt1_l_p3_p2.a, prt1_l_p3_p2.c, prt1_l_p3_p2.b
+ Filter: ((prt1_l_p3_p2.a % 25) = 0)
+ -> Hash
+ Output: prt2_l_p3_p2.b, prt2_l_p3_p2.c, prt2_l_p3_p2.a
+ -> Seq Scan on public.prt2_l_p3_p2
+ Output: prt2_l_p3_p2.b, prt2_l_p3_p2.c, prt2_l_p3_p2.a
+ Filter: ((prt2_l_p3_p2.b % 25) = 0)
+(70 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 7) and (b 20) is considered for partition-wise join.
+NOTICE: join between relations (b 11) and (b 24) is considered for partition-wise join.
+NOTICE: join between relations (b 15) and (b 28) is considered for partition-wise join.
+ 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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_l t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_l t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.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)
+
+--
+-- 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;
+ANALYZE plt1_p1;
+ANALYZE plt1_p2;
+ANALYZE plt1_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt1 AS SELECT * FROM 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;
+ANALYZE plt2_p1;
+ANALYZE plt2_p2;
+ANALYZE plt2_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt2 AS SELECT * FROM plt2;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Append
+ -> Hash Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: ((t2.c = t1.c) AND (t2.a = t1.a))
+ -> Seq Scan on public.plt2_p1 t2
+ Output: t2.b, t2.c, t2.a
+ -> Hash
+ Output: t1.a, t1.c
+ -> Seq Scan on public.plt1_p1 t1
+ Output: t1.a, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ Hash Cond: ((t2_1.c = t1_1.c) AND (t2_1.a = t1_1.a))
+ -> Seq Scan on public.plt2_p2 t2_1
+ Output: t2_1.b, t2_1.c, t2_1.a
+ -> Hash
+ Output: t1_1.a, t1_1.c
+ -> Seq Scan on public.plt1_p2 t1_1
+ Output: t1_1.a, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+ Hash Cond: ((t2_2.c = t1_2.c) AND (t2_2.a = t1_2.a))
+ -> Seq Scan on public.plt2_p3 t2_2
+ Output: t2_2.b, t2_2.c, t2_2.a
+ -> Hash
+ Output: t1_2.a, t1_2.c
+ -> Seq Scan on public.plt1_p3 t1_2
+ Output: t1_2.a, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ a | c | b | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 150 | 0003 | 150 | 0003
+ 300 | 0006 | 300 | 0006
+ 450 | 0009 | 450 | 0009
+(4 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1, uplt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+ a | c | b | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 150 | 0003 | 150 | 0003
+ 300 | 0006 | 300 | 0006
+ 450 | 0009 | 450 | 0009
+(4 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Append
+ -> Hash Right Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c))
+ -> Seq Scan on public.plt2_p1 t2
+ Output: t2.b, t2.c, t2.a
+ -> Hash
+ Output: t1.a, t1.c
+ -> Seq Scan on public.plt1_p1 t1
+ Output: t1.a, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c))
+ -> Seq Scan on public.plt2_p2 t2_1
+ Output: t2_1.b, t2_1.c, t2_1.a
+ -> Hash
+ Output: t1_1.a, t1_1.c
+ -> Seq Scan on public.plt1_p2 t1_1
+ Output: t1_1.a, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+ Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c))
+ -> Seq Scan on public.plt2_p3 t2_2
+ Output: t2_2.b, t2_2.c, t2_2.a
+ -> Hash
+ Output: t1_2.a, t1_2.c
+ -> Seq Scan on public.plt1_p3 t1_2
+ Output: t1_2.a, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ a | c | b | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 50 | 0001 | |
+ 100 | 0002 | |
+ 150 | 0003 | 150 | 0003
+ 200 | 0004 | |
+ 250 | 0005 | |
+ 300 | 0006 | 300 | 0006
+ 350 | 0007 | |
+ 400 | 0008 | |
+ 450 | 0009 | 450 | 0009
+ 500 | 0010 | |
+ 550 | 0011 | |
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+ a | c | b | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 50 | 0001 | |
+ 100 | 0002 | |
+ 150 | 0003 | 150 | 0003
+ 200 | 0004 | |
+ 250 | 0005 | |
+ 300 | 0006 | 300 | 0006
+ 350 | 0007 | |
+ 400 | 0008 | |
+ 450 | 0009 | 450 | 0009
+ 500 | 0010 | |
+ 550 | 0011 | |
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c
+ -> Append
+ -> Hash Right Join
+ Output: t2.b, t2.c, t1.a, t1.c
+ Hash Cond: ((t1.a = t2.b) AND (t1.c = t2.c))
+ -> Seq Scan on public.plt1_p1 t1
+ Output: t1.a, t1.c
+ -> Hash
+ Output: t2.b, t2.c
+ -> Seq Scan on public.plt2_p1 t2
+ Output: t2.b, t2.c
+ Filter: ((t2.b % 25) = 0)
+ -> Hash Right Join
+ Output: t2_1.b, t2_1.c, t1_1.a, t1_1.c
+ Hash Cond: ((t1_1.a = t2_1.b) AND (t1_1.c = t2_1.c))
+ -> Seq Scan on public.plt1_p2 t1_1
+ Output: t1_1.a, t1_1.c
+ -> Hash
+ Output: t2_1.b, t2_1.c
+ -> Seq Scan on public.plt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ Filter: ((t2_1.b % 25) = 0)
+ -> Hash Right Join
+ Output: t2_2.b, t2_2.c, t1_2.a, t1_2.c
+ Hash Cond: ((t1_2.a = t2_2.b) AND (t1_2.c = t2_2.c))
+ -> Seq Scan on public.plt1_p3 t1_2
+ Output: t1_2.a, t1_2.c
+ -> Hash
+ Output: t2_2.b, t2_2.c
+ -> Seq Scan on public.plt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ Filter: ((t2_2.b % 25) = 0)
+(36 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+ a | c | b | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 150 | 0003 | 150 | 0003
+ 300 | 0006 | 300 | 0006
+ 450 | 0009 | 450 | 0009
+ | | 75 | 0001
+ | | 225 | 0004
+ | | 375 | 0007
+ | | 525 | 0010
+(8 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+ a | c | b | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 150 | 0003 | 150 | 0003
+ 300 | 0006 | 300 | 0006
+ 450 | 0009 | 450 | 0009
+ | | 75 | 0001
+ | | 225 | 0004
+ | | 375 | 0007
+ | | 525 | 0010
+(8 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------
+ Sort
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+ Sort Key: plt1_p1.a, plt2_p1.b
+ -> Append
+ -> Hash Full Join
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+ Hash Cond: ((plt1_p1.a = plt2_p1.b) AND (plt1_p1.c = plt2_p1.c))
+ -> Seq Scan on public.plt1_p1
+ Output: plt1_p1.a, plt1_p1.c
+ Filter: ((plt1_p1.a % 25) = 0)
+ -> Hash
+ Output: plt2_p1.b, plt2_p1.c
+ -> Seq Scan on public.plt2_p1
+ Output: plt2_p1.b, plt2_p1.c
+ Filter: ((plt2_p1.b % 25) = 0)
+ -> Hash Full Join
+ Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c
+ Hash Cond: ((plt1_p2.a = plt2_p2.b) AND (plt1_p2.c = plt2_p2.c))
+ -> Seq Scan on public.plt1_p2
+ Output: plt1_p2.a, plt1_p2.c
+ Filter: ((plt1_p2.a % 25) = 0)
+ -> Hash
+ Output: plt2_p2.b, plt2_p2.c
+ -> Seq Scan on public.plt2_p2
+ Output: plt2_p2.b, plt2_p2.c
+ Filter: ((plt2_p2.b % 25) = 0)
+ -> Hash Full Join
+ Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c
+ Hash Cond: ((plt1_p3.a = plt2_p3.b) AND (plt1_p3.c = plt2_p3.c))
+ -> Seq Scan on public.plt1_p3
+ Output: plt1_p3.a, plt1_p3.c
+ Filter: ((plt1_p3.a % 25) = 0)
+ -> Hash
+ Output: plt2_p3.b, plt2_p3.c
+ -> Seq Scan on public.plt2_p3
+ Output: plt2_p3.b, plt2_p3.c
+ Filter: ((plt2_p3.b % 25) = 0)
+(37 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ a | c | b | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 50 | 0001 | |
+ 100 | 0002 | |
+ 150 | 0003 | 150 | 0003
+ 200 | 0004 | |
+ 250 | 0005 | |
+ 300 | 0006 | 300 | 0006
+ 350 | 0007 | |
+ 400 | 0008 | |
+ 450 | 0009 | 450 | 0009
+ 500 | 0010 | |
+ 550 | 0011 | |
+ | | 75 | 0001
+ | | 225 | 0004
+ | | 375 | 0007
+ | | 525 | 0010
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
+ a | c | b | c
+-----+------+-----+------
+ 0 | 0000 | 0 | 0000
+ 50 | 0001 | |
+ 100 | 0002 | |
+ 150 | 0003 | 150 | 0003
+ 200 | 0004 | |
+ 250 | 0005 | |
+ 300 | 0006 | 300 | 0006
+ 350 | 0007 | |
+ 400 | 0008 | |
+ 450 | 0009 | 450 | 0009
+ 500 | 0010 | |
+ 550 | 0011 | |
+ | | 75 | 0001
+ | | 225 | 0004
+ | | 375 | 0007
+ | | 525 | 0010
+(16 rows)
+
+-- Cases with non-nullable expressions in subquery results;
+-- make sure these go to null as expected
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------------------
+ GroupAggregate
+ Output: sum(plt1_p1.a), plt1_p1.c, avg(plt2_p1.b), plt2_p1.c
+ Group Key: plt1_p1.c, plt2_p1.c
+ -> Sort
+ Output: plt1_p1.c, plt2_p1.c, plt1_p1.a, plt2_p1.b
+ Sort Key: plt1_p1.c, plt2_p1.c
+ -> Result
+ Output: plt1_p1.c, plt2_p1.c, plt1_p1.a, plt2_p1.b
+ -> Append
+ -> Hash Full Join
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+ Hash Cond: ((plt1_p1.c = plt2_p1.c) AND (plt1_p1.a = plt2_p1.b))
+ -> Seq Scan on public.plt1_p1
+ Output: plt1_p1.a, plt1_p1.c
+ Filter: ((plt1_p1.a % 25) = 0)
+ -> Hash
+ Output: plt2_p1.b, plt2_p1.c
+ -> Seq Scan on public.plt2_p1
+ Output: plt2_p1.b, plt2_p1.c
+ Filter: ((plt2_p1.b % 25) = 0)
+ -> Hash Full Join
+ Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c
+ Hash Cond: ((plt1_p2.c = plt2_p2.c) AND (plt1_p2.a = plt2_p2.b))
+ -> Seq Scan on public.plt1_p2
+ Output: plt1_p2.a, plt1_p2.c
+ Filter: ((plt1_p2.a % 25) = 0)
+ -> Hash
+ Output: plt2_p2.b, plt2_p2.c
+ -> Seq Scan on public.plt2_p2
+ Output: plt2_p2.b, plt2_p2.c
+ Filter: ((plt2_p2.b % 25) = 0)
+ -> Hash Full Join
+ Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c
+ Hash Cond: ((plt1_p3.c = plt2_p3.c) AND (plt1_p3.a = plt2_p3.b))
+ -> Seq Scan on public.plt1_p3
+ Output: plt1_p3.a, plt1_p3.c
+ Filter: ((plt1_p3.a % 25) = 0)
+ -> Hash
+ Output: plt2_p3.b, plt2_p3.c
+ -> Seq Scan on public.plt2_p3
+ Output: plt2_p3.b, plt2_p3.c
+ Filter: ((plt2_p3.b % 25) = 0)
+(42 rows)
+
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ sum | c | avg | c
+-----+------+------------------------+------
+ 0 | 0000 | 0.00000000000000000000 | 0000
+ 50 | 0001 | |
+ 100 | 0002 | |
+ 150 | 0003 | 150.0000000000000000 | 0003
+ 200 | 0004 | |
+ 250 | 0005 | |
+ 300 | 0006 | 300.0000000000000000 | 0006
+ 350 | 0007 | |
+ 400 | 0008 | |
+ 450 | 0009 | 450.0000000000000000 | 0009
+ 500 | 0010 | |
+ 550 | 0011 | |
+ | | 75.0000000000000000 | 0001
+ | | 225.0000000000000000 | 0004
+ | | 375.0000000000000000 | 0007
+ | | 525.0000000000000000 | 0010
+(16 rows)
+
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+ sum | c | avg | c
+-----+------+------------------------+------
+ 0 | 0000 | 0.00000000000000000000 | 0000
+ 50 | 0001 | |
+ 100 | 0002 | |
+ 150 | 0003 | 150.0000000000000000 | 0003
+ 200 | 0004 | |
+ 250 | 0005 | |
+ 300 | 0006 | 300.0000000000000000 | 0006
+ 350 | 0007 | |
+ 400 | 0008 | |
+ 450 | 0009 | 450.0000000000000000 | 0009
+ 500 | 0010 | |
+ 550 | 0011 | |
+ | | 75.0000000000000000 | 0001
+ | | 225.0000000000000000 | 0004
+ | | 375.0000000000000000 | 0007
+ | | 525.0000000000000000 | 0010
+(16 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------------------
+ GroupAggregate
+ Output: sum(plt1_p1.a), plt1_p1.c, sum((25)), avg(plt2_p1.b), plt2_p1.c, avg((50))
+ Group Key: plt1_p1.c, plt2_p1.c
+ -> Sort
+ Output: plt1_p1.c, plt2_p1.c, plt1_p1.a, (25), plt2_p1.b, (50)
+ Sort Key: plt1_p1.c, plt2_p1.c
+ -> Result
+ Output: plt1_p1.c, plt2_p1.c, plt1_p1.a, (25), plt2_p1.b, (50)
+ -> Append
+ -> Hash Full Join
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, (25), (50)
+ Hash Cond: ((plt1_p1.c = plt2_p1.c) AND (plt1_p1.a = plt2_p1.b))
+ -> Seq Scan on public.plt1_p1
+ Output: plt1_p1.a, plt1_p1.c, 25
+ Filter: ((plt1_p1.a % 25) = 0)
+ -> Hash
+ Output: plt2_p1.b, plt2_p1.c, (50)
+ -> Seq Scan on public.plt2_p1
+ Output: plt2_p1.b, plt2_p1.c, 50
+ Filter: ((plt2_p1.b % 25) = 0)
+ -> Hash Full Join
+ Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c, (25), (50)
+ Hash Cond: ((plt1_p2.c = plt2_p2.c) AND (plt1_p2.a = plt2_p2.b))
+ -> Seq Scan on public.plt1_p2
+ Output: plt1_p2.a, plt1_p2.c, 25
+ Filter: ((plt1_p2.a % 25) = 0)
+ -> Hash
+ Output: plt2_p2.b, plt2_p2.c, (50)
+ -> Seq Scan on public.plt2_p2
+ Output: plt2_p2.b, plt2_p2.c, 50
+ Filter: ((plt2_p2.b % 25) = 0)
+ -> Hash Full Join
+ Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c, (25), (50)
+ Hash Cond: ((plt1_p3.c = plt2_p3.c) AND (plt1_p3.a = plt2_p3.b))
+ -> Seq Scan on public.plt1_p3
+ Output: plt1_p3.a, plt1_p3.c, 25
+ Filter: ((plt1_p3.a % 25) = 0)
+ -> Hash
+ Output: plt2_p3.b, plt2_p3.c, (50)
+ -> Seq Scan on public.plt2_p3
+ Output: plt2_p3.b, plt2_p3.c, 50
+ Filter: ((plt2_p3.b % 25) = 0)
+(42 rows)
+
+SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ sum | c | sum | avg | c | avg
+-----+------+-----+------------------------+------+---------------------
+ 0 | 0000 | 25 | 0.00000000000000000000 | 0000 | 50.0000000000000000
+ 50 | 0001 | 25 | | |
+ 100 | 0002 | 25 | | |
+ 150 | 0003 | 25 | 150.0000000000000000 | 0003 | 50.0000000000000000
+ 200 | 0004 | 25 | | |
+ 250 | 0005 | 25 | | |
+ 300 | 0006 | 25 | 300.0000000000000000 | 0006 | 50.0000000000000000
+ 350 | 0007 | 25 | | |
+ 400 | 0008 | 25 | | |
+ 450 | 0009 | 25 | 450.0000000000000000 | 0009 | 50.0000000000000000
+ 500 | 0010 | 25 | | |
+ 550 | 0011 | 25 | | |
+ | | | 75.0000000000000000 | 0001 | 50.0000000000000000
+ | | | 225.0000000000000000 | 0004 | 50.0000000000000000
+ | | | 375.0000000000000000 | 0007 | 50.0000000000000000
+ | | | 525.0000000000000000 | 0010 | 50.0000000000000000
+(16 rows)
+
+SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+ sum | c | sum | avg | c | avg
+-----+------+-----+------------------------+------+---------------------
+ 0 | 0000 | 25 | 0.00000000000000000000 | 0000 | 50.0000000000000000
+ 50 | 0001 | 25 | | |
+ 100 | 0002 | 25 | | |
+ 150 | 0003 | 25 | 150.0000000000000000 | 0003 | 50.0000000000000000
+ 200 | 0004 | 25 | | |
+ 250 | 0005 | 25 | | |
+ 300 | 0006 | 25 | 300.0000000000000000 | 0006 | 50.0000000000000000
+ 350 | 0007 | 25 | | |
+ 400 | 0008 | 25 | | |
+ 450 | 0009 | 25 | 450.0000000000000000 | 0009 | 50.0000000000000000
+ 500 | 0010 | 25 | | |
+ 550 | 0011 | 25 | | |
+ | | | 75.0000000000000000 | 0001 | 50.0000000000000000
+ | | | 225.0000000000000000 | 0004 | 50.0000000000000000
+ | | | 375.0000000000000000 | 0007 | 50.0000000000000000
+ | | | 525.0000000000000000 | 0010 | 50.0000000000000000
+(16 rows)
+
+-- Join with pruned partitions from joining relations
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------
+ Sort
+ Output: (sum(t1.a)), t1.c, (avg(t2.b)), t2.c
+ Sort Key: t1.c
+ -> HashAggregate
+ Output: sum(t1.a), t1.c, avg(t2.b), t2.c
+ Group Key: t1.c, t2.c
+ -> Result
+ Output: t1.c, t2.c, t1.a, t2.b
+ -> Append
+ -> Hash Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: (t1.c = t2.c)
+ -> Seq Scan on public.plt1_p3 t1
+ Output: t1.a, t1.c
+ Filter: (t1.c <> ALL ('{0001,0005,0002,0009}'::text[]))
+ -> Hash
+ Output: t2.b, t2.c
+ -> Seq Scan on public.plt2_p3 t2
+ Output: t2.b, t2.c
+ Filter: (t2.c <> ALL ('{0000,0003,0004,0010}'::text[]))
+(20 rows)
+
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ sum | c | avg | c
+--------+------+----------------------+------
+ 137700 | 0006 | 324.0000000000000000 | 0006
+ 158950 | 0007 | 375.0000000000000000 | 0007
+ 169600 | 0008 | 424.5000000000000000 | 0008
+ 229600 | 0011 | 574.5000000000000000 | 0011
+(4 rows)
+
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM uplt1 t1, uplt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+ sum | c | avg | c
+--------+------+----------------------+------
+ 137700 | 0006 | 324.0000000000000000 | 0006
+ 158950 | 0007 | 375.0000000000000000 | 0007
+ 169600 | 0008 | 424.5000000000000000 | 0008
+ 229600 | 0011 | 574.5000000000000000 | 0011
+(4 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------
+ Sort
+ Output: (sum(t1.a)), t1.c, (sum(b)), c
+ Sort Key: t1.c, c
+ -> HashAggregate
+ Output: sum(t1.a), t1.c, sum(b), c
+ Group Key: t1.c, c
+ -> Result
+ Output: t1.c, c, t1.a, b
+ -> Append
+ -> Hash Left Join
+ Output: t1.a, t1.c, b, c
+ Hash Cond: (t1.c = c)
+ -> Seq Scan on public.plt1_p1 t1
+ Output: t1.a, t1.c
+ Filter: (t1.c <> ALL ('{0001,0005,0002,0009}'::text[]))
+ -> Hash
+ Output: b, c
+ -> Result
+ Output: b, c
+ One-Time Filter: false
+ -> Hash Left Join
+ Output: t1_1.a, t1_1.c, t2.b, t2.c
+ Hash Cond: (t1_1.c = t2.c)
+ -> Seq Scan on public.plt1_p3 t1_1
+ Output: t1_1.a, t1_1.c
+ Filter: (t1_1.c <> ALL ('{0001,0005,0002,0009}'::text[]))
+ -> Hash
+ Output: t2.b, t2.c
+ -> Seq Scan on public.plt2_p3 t2
+ Output: t2.b, t2.c
+ Filter: (t2.c <> ALL ('{0000,0003,0004,0010}'::text[]))
+(31 rows)
+
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ sum | c | sum | c
+--------+------+--------+------
+ 600 | 0000 | |
+ 4350 | 0003 | |
+ 5600 | 0004 | |
+ 137700 | 0006 | 137700 | 0006
+ 158950 | 0007 | 159375 | 0007
+ 169600 | 0008 | 169800 | 0008
+ 13100 | 0010 | |
+ 229600 | 0011 | 229800 | 0011
+(8 rows)
+
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+ sum | c | sum | c
+--------+------+--------+------
+ 600 | 0000 | |
+ 4350 | 0003 | |
+ 5600 | 0004 | |
+ 137700 | 0006 | 137700 | 0006
+ 158950 | 0007 | 159375 | 0007
+ 169600 | 0008 | 169800 | 0008
+ 13100 | 0010 | |
+ 229600 | 0011 | 229800 | 0011
+(8 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE: join between relations (b 5) and (b 4) is considered for partition-wise join.
+ QUERY PLAN
+-------------------------------------------------------------------------------------------------
+ Sort
+ Output: (sum(a)), c, (sum(t2.b)), t2.c
+ Sort Key: c, t2.c
+ -> HashAggregate
+ Output: sum(a), c, sum(t2.b), t2.c
+ Group Key: c, t2.c
+ -> Result
+ Output: c, t2.c, a, t2.b
+ -> Append
+ -> Hash Left Join
+ Output: t2.b, t2.c, a, c
+ Hash Cond: (t2.c = c)
+ -> Seq Scan on public.plt2_p2 t2
+ Output: t2.b, t2.c
+ Filter: (t2.c <> ALL ('{0000,0003,0004,0010}'::text[]))
+ -> Hash
+ Output: a, c
+ -> Result
+ Output: a, c
+ One-Time Filter: false
+ -> Hash Right Join
+ Output: t2_1.b, t2_1.c, t1.a, t1.c
+ Hash Cond: (t1.c = t2_1.c)
+ -> Seq Scan on public.plt1_p3 t1
+ Output: t1.a, t1.c
+ Filter: (t1.c <> ALL ('{0001,0005,0002,0009}'::text[]))
+ -> Hash
+ Output: t2_1.b, t2_1.c
+ -> Seq Scan on public.plt2_p3 t2_1
+ Output: t2_1.b, t2_1.c
+ Filter: (t2_1.c <> ALL ('{0000,0003,0004,0010}'::text[]))
+(31 rows)
+
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE: join between relations (b 5) and (b 4) is considered for partition-wise join.
+ sum | c | sum | c
+--------+------+--------+------
+ 137700 | 0006 | 137700 | 0006
+ 158950 | 0007 | 159375 | 0007
+ 169600 | 0008 | 169800 | 0008
+ 229600 | 0011 | 229800 | 0011
+ | | 1275 | 0001
+ | | 1992 | 0002
+ | | 4392 | 0005
+ | | 8058 | 0009
+(8 rows)
+
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+ sum | c | sum | c
+--------+------+--------+------
+ 137700 | 0006 | 137700 | 0006
+ 158950 | 0007 | 159375 | 0007
+ 169600 | 0008 | 169800 | 0008
+ 229600 | 0011 | 229800 | 0011
+ | | 1275 | 0001
+ | | 1992 | 0002
+ | | 4392 | 0005
+ | | 8058 | 0009
+(8 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ QUERY PLAN
+-------------------------------------------------------------------------------------------------
+ Sort
+ Output: (sum(t1.a)), t1.c, (sum(b)), c
+ Sort Key: t1.c, c
+ -> HashAggregate
+ Output: sum(t1.a), t1.c, sum(b), c
+ Group Key: t1.c, c
+ -> Result
+ Output: t1.c, c, t1.a, b
+ -> Append
+ -> Hash Full Join
+ Output: t1.a, t1.c, b, c
+ Hash Cond: (t1.c = c)
+ -> Seq Scan on public.plt1_p1 t1
+ Output: t1.a, t1.c
+ Filter: (t1.c <> ALL ('{0001,0005,0002,0009}'::text[]))
+ -> Hash
+ Output: b, c
+ -> Result
+ Output: b, c
+ One-Time Filter: false
+ -> Hash Full Join
+ Output: a, c, t2.b, t2.c
+ Hash Cond: (t2.c = c)
+ -> Seq Scan on public.plt2_p2 t2
+ Output: t2.b, t2.c
+ Filter: (t2.c <> ALL ('{0000,0003,0004,0010}'::text[]))
+ -> Hash
+ Output: a, c
+ -> Result
+ Output: a, c
+ One-Time Filter: false
+ -> Hash Full Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ Hash Cond: (t1_1.c = t2_1.c)
+ -> Seq Scan on public.plt1_p3 t1_1
+ Output: t1_1.a, t1_1.c
+ Filter: (t1_1.c <> ALL ('{0001,0005,0002,0009}'::text[]))
+ -> Hash
+ Output: t2_1.b, t2_1.c
+ -> Seq Scan on public.plt2_p3 t2_1
+ Output: t2_1.b, t2_1.c
+ Filter: (t2_1.c <> ALL ('{0000,0003,0004,0010}'::text[]))
+(42 rows)
+
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ sum | c | sum | c
+--------+------+--------+------
+ 600 | 0000 | |
+ 4350 | 0003 | |
+ 5600 | 0004 | |
+ 137700 | 0006 | 137700 | 0006
+ 158950 | 0007 | 159375 | 0007
+ 169600 | 0008 | 169800 | 0008
+ 13100 | 0010 | |
+ 229600 | 0011 | 229800 | 0011
+ | | 1275 | 0001
+ | | 1992 | 0002
+ | | 4392 | 0005
+ | | 8058 | 0009
+(12 rows)
+
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+ sum | c | sum | c
+--------+------+--------+------
+ 600 | 0000 | |
+ 4350 | 0003 | |
+ 5600 | 0004 | |
+ 137700 | 0006 | 137700 | 0006
+ 158950 | 0007 | 159375 | 0007
+ 169600 | 0008 | 169800 | 0008
+ 13100 | 0010 | |
+ 229600 | 0011 | 229800 | 0011
+ | | 1275 | 0001
+ | | 1992 | 0002
+ | | 4392 | 0005
+ | | 8058 | 0009
+(12 rows)
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 1) and (b 3) is considered for partition-wise join.
+ QUERY PLAN
+---------------------------------------------------------
+ Sort
+ Output: t1.a, t1.b, t1.c
+ Sort Key: t1.a
+ -> Append
+ -> Nested Loop Semi Join
+ Output: t1.a, t1.b, t1.c
+ Join Filter: (t1.c = t1_3.c)
+ -> Seq Scan on public.plt1_p1 t1
+ Output: t1.a, t1.b, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Materialize
+ Output: t1_3.c
+ -> Seq Scan on public.plt2_p1 t1_3
+ Output: t1_3.c
+ Filter: ((t1_3.b % 25) = 0)
+ -> Nested Loop Semi Join
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Join Filter: (t1_1.c = t1_4.c)
+ -> Seq Scan on public.plt1_p2 t1_1
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Materialize
+ Output: t1_4.c
+ -> Seq Scan on public.plt2_p2 t1_4
+ Output: t1_4.c
+ Filter: ((t1_4.b % 25) = 0)
+ -> Nested Loop Semi Join
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Join Filter: (t1_2.c = t1_5.c)
+ -> Seq Scan on public.plt1_p3 t1_2
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Materialize
+ Output: t1_5.c
+ -> Seq Scan on public.plt2_p3 t1_5
+ Output: t1_5.c
+ Filter: ((t1_5.b % 25) = 0)
+(37 rows)
+
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 1) and (b 3) is considered for partition-wise join.
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 50 | 50 | 0001
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+(8 rows)
+
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 50 | 50 | 0001
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+(8 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;
+ANALYZE plt1_e;
+ANALYZE plt1_e_p1;
+ANALYZE plt1_e_p2;
+ANALYZE plt1_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt1_e AS SELECT * FROM plt1_e;
+CREATE TABLE plt2_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
+CREATE TABLE plt2_e_p1 PARTITION OF plt2_e FOR VALUES IN ('0000', '0003', '0004', '0010');
+CREATE TABLE plt2_e_p2 PARTITION OF plt2_e FOR VALUES IN ('0001', '0005', '0002', '0009');
+CREATE TABLE plt2_e_p3 PARTITION OF plt2_e FOR VALUES IN ('0006', '0007', '0008', '0011');
+INSERT INTO plt2_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE plt2_e;
+ANALYZE plt2_e_p1;
+ANALYZE plt2_e_p2;
+ANALYZE plt2_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt2_e AS SELECT * FROM plt2_e;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1, plt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a
+ -> Append
+ -> Hash Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: ((t2.b = t1.a) AND (ltrim(t2.c, 'A'::text) = ltrim(t1.c, 'A'::text)))
+ -> Seq Scan on public.plt2_e_p1 t2
+ Output: t2.b, t2.c
+ -> Hash
+ Output: t1.a, t1.c
+ -> Seq Scan on public.plt1_e_p1 t1
+ Output: t1.a, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ Hash Cond: ((t2_1.b = t1_1.a) AND (ltrim(t2_1.c, 'A'::text) = ltrim(t1_1.c, 'A'::text)))
+ -> Seq Scan on public.plt2_e_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Hash
+ Output: t1_1.a, t1_1.c
+ -> Seq Scan on public.plt1_e_p2 t1_1
+ Output: t1_1.a, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+ Hash Cond: ((t2_2.b = t1_2.a) AND (ltrim(t2_2.c, 'A'::text) = ltrim(t1_2.c, 'A'::text)))
+ -> Seq Scan on public.plt2_e_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Hash
+ Output: t1_2.a, t1_2.c
+ -> Seq Scan on public.plt1_e_p3 t1_2
+ Output: t1_2.a, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1, plt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ a | c | b | c
+-----+-------+-----+-------
+ 0 | A0000 | 0 | A0000
+ 150 | A0003 | 150 | A0003
+ 300 | A0006 | 300 | A0006
+ 450 | A0009 | 450 | A0009
+(4 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1, uplt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+ a | c | b | c
+-----+-------+-----+-------
+ 0 | A0000 | 0 | A0000
+ 150 | A0003 | 150 | A0003
+ 300 | A0006 | 300 | A0006
+ 450 | A0009 | 450 | A0009
+(4 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 LEFT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Append
+ -> Hash Right Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: ((t2.b = t1.a) AND (ltrim(t2.c, 'A'::text) = ltrim(t1.c, 'A'::text)))
+ -> Seq Scan on public.plt2_e_p1 t2
+ Output: t2.b, t2.c
+ -> Hash
+ Output: t1.a, t1.c
+ -> Seq Scan on public.plt1_e_p1 t1
+ Output: t1.a, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ Hash Cond: ((t2_1.b = t1_1.a) AND (ltrim(t2_1.c, 'A'::text) = ltrim(t1_1.c, 'A'::text)))
+ -> Seq Scan on public.plt2_e_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Hash
+ Output: t1_1.a, t1_1.c
+ -> Seq Scan on public.plt1_e_p2 t1_1
+ Output: t1_1.a, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+ Hash Cond: ((t2_2.b = t1_2.a) AND (ltrim(t2_2.c, 'A'::text) = ltrim(t1_2.c, 'A'::text)))
+ -> Seq Scan on public.plt2_e_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Hash
+ Output: t1_2.a, t1_2.c
+ -> Seq Scan on public.plt1_e_p3 t1_2
+ Output: t1_2.a, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 LEFT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+ a | c | b | c
+-----+-------+-----+-------
+ 0 | A0000 | 0 | A0000
+ 50 | A0001 | |
+ 100 | A0002 | |
+ 150 | A0003 | 150 | A0003
+ 200 | A0004 | |
+ 250 | A0005 | |
+ 300 | A0006 | 300 | A0006
+ 350 | A0007 | |
+ 400 | A0008 | |
+ 450 | A0009 | 450 | A0009
+ 500 | A0010 | |
+ 550 | A0011 | |
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1 LEFT JOIN uplt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+ a | c | b | c
+-----+-------+-----+-------
+ 0 | A0000 | 0 | A0000
+ 50 | A0001 | |
+ 100 | A0002 | |
+ 150 | A0003 | 150 | A0003
+ 200 | A0004 | |
+ 250 | A0005 | |
+ 300 | A0006 | 300 | A0006
+ 350 | A0007 | |
+ 400 | A0008 | |
+ 450 | A0009 | 450 | A0009
+ 500 | A0010 | |
+ 550 | A0011 | |
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 RIGHT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c
+ -> Append
+ -> Hash Right Join
+ Output: t2.b, t2.c, t1.a, t1.c
+ Hash Cond: ((t1.a = t2.b) AND (ltrim(t1.c, 'A'::text) = ltrim(t2.c, 'A'::text)))
+ -> Seq Scan on public.plt1_e_p1 t1
+ Output: t1.a, t1.c
+ -> Hash
+ Output: t2.b, t2.c
+ -> Seq Scan on public.plt2_e_p1 t2
+ Output: t2.b, t2.c
+ Filter: ((t2.b % 25) = 0)
+ -> Hash Right Join
+ Output: t2_1.b, t2_1.c, t1_1.a, t1_1.c
+ Hash Cond: ((t1_1.a = t2_1.b) AND (ltrim(t1_1.c, 'A'::text) = ltrim(t2_1.c, 'A'::text)))
+ -> Seq Scan on public.plt1_e_p2 t1_1
+ Output: t1_1.a, t1_1.c
+ -> Hash
+ Output: t2_1.b, t2_1.c
+ -> Seq Scan on public.plt2_e_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ Filter: ((t2_1.b % 25) = 0)
+ -> Hash Right Join
+ Output: t2_2.b, t2_2.c, t1_2.a, t1_2.c
+ Hash Cond: ((t1_2.a = t2_2.b) AND (ltrim(t1_2.c, 'A'::text) = ltrim(t2_2.c, 'A'::text)))
+ -> Seq Scan on public.plt1_e_p3 t1_2
+ Output: t1_2.a, t1_2.c
+ -> Hash
+ Output: t2_2.b, t2_2.c
+ -> Seq Scan on public.plt2_e_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ Filter: ((t2_2.b % 25) = 0)
+(36 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 RIGHT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+ a | c | b | c
+-----+-------+-----+-------
+ 0 | A0000 | 0 | A0000
+ 150 | A0003 | 150 | A0003
+ 300 | A0006 | 300 | A0006
+ 450 | A0009 | 450 | A0009
+ | | 75 | A0001
+ | | 225 | A0004
+ | | 375 | A0007
+ | | 525 | A0010
+(8 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1 RIGHT JOIN uplt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+ a | c | b | c
+-----+-------+-----+-------
+ 0 | A0000 | 0 | A0000
+ 150 | A0003 | 150 | A0003
+ 300 | A0006 | 300 | A0006
+ 450 | A0009 | 450 | A0009
+ | | 75 | A0001
+ | | 225 | A0004
+ | | 375 | A0007
+ | | 525 | A0010
+(8 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2_e WHERE plt2_e.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ QUERY PLAN
+----------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: plt1_e_p1.a, plt1_e_p1.c, plt2_e_p1.b, plt2_e_p1.c
+ Sort Key: plt1_e_p1.a, plt2_e_p1.b
+ -> Append
+ -> Hash Full Join
+ Output: plt1_e_p1.a, plt1_e_p1.c, plt2_e_p1.b, plt2_e_p1.c
+ Hash Cond: ((plt1_e_p1.a = plt2_e_p1.b) AND (ltrim(plt1_e_p1.c, 'A'::text) = ltrim(plt2_e_p1.c, 'A'::text)))
+ -> Seq Scan on public.plt1_e_p1
+ Output: plt1_e_p1.a, plt1_e_p1.c
+ Filter: ((plt1_e_p1.a % 25) = 0)
+ -> Hash
+ Output: plt2_e_p1.b, plt2_e_p1.c
+ -> Seq Scan on public.plt2_e_p1
+ Output: plt2_e_p1.b, plt2_e_p1.c
+ Filter: ((plt2_e_p1.b % 25) = 0)
+ -> Hash Full Join
+ Output: plt1_e_p2.a, plt1_e_p2.c, plt2_e_p2.b, plt2_e_p2.c
+ Hash Cond: ((plt1_e_p2.a = plt2_e_p2.b) AND (ltrim(plt1_e_p2.c, 'A'::text) = ltrim(plt2_e_p2.c, 'A'::text)))
+ -> Seq Scan on public.plt1_e_p2
+ Output: plt1_e_p2.a, plt1_e_p2.c
+ Filter: ((plt1_e_p2.a % 25) = 0)
+ -> Hash
+ Output: plt2_e_p2.b, plt2_e_p2.c
+ -> Seq Scan on public.plt2_e_p2
+ Output: plt2_e_p2.b, plt2_e_p2.c
+ Filter: ((plt2_e_p2.b % 25) = 0)
+ -> Hash Full Join
+ Output: plt1_e_p3.a, plt1_e_p3.c, plt2_e_p3.b, plt2_e_p3.c
+ Hash Cond: ((plt1_e_p3.a = plt2_e_p3.b) AND (ltrim(plt1_e_p3.c, 'A'::text) = ltrim(plt2_e_p3.c, 'A'::text)))
+ -> Seq Scan on public.plt1_e_p3
+ Output: plt1_e_p3.a, plt1_e_p3.c
+ Filter: ((plt1_e_p3.a % 25) = 0)
+ -> Hash
+ Output: plt2_e_p3.b, plt2_e_p3.c
+ -> Seq Scan on public.plt2_e_p3
+ Output: plt2_e_p3.b, plt2_e_p3.c
+ Filter: ((plt2_e_p3.b % 25) = 0)
+(37 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2_e WHERE plt2_e.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b;
+NOTICE: join between relations (b 4) and (b 5) is considered for partition-wise join.
+ a | c | b | c
+-----+-------+-----+-------
+ 0 | A0000 | 0 | A0000
+ 50 | A0001 | |
+ 100 | A0002 | |
+ 150 | A0003 | 150 | A0003
+ 200 | A0004 | |
+ 250 | A0005 | |
+ 300 | A0006 | 300 | A0006
+ 350 | A0007 | |
+ 400 | A0008 | |
+ 450 | A0009 | 450 | A0009
+ 500 | A0010 | |
+ 550 | A0011 | |
+ | | 75 | A0001
+ | | 225 | A0004
+ | | 375 | A0007
+ | | 525 | A0010
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uplt1_e t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2_e t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b;
+ a | c | b | c
+-----+-------+-----+-------
+ 0 | A0000 | 0 | A0000
+ 50 | A0001 | |
+ 100 | A0002 | |
+ 150 | A0003 | 150 | A0003
+ 200 | A0004 | |
+ 250 | A0005 | |
+ 300 | A0006 | 300 | A0006
+ 350 | A0007 | |
+ 400 | A0008 | |
+ 450 | A0009 | 450 | A0009
+ 500 | A0010 | |
+ 550 | A0011 | |
+ | | 75 | A0001
+ | | 225 | A0004
+ | | 375 | A0007
+ | | 525 | A0010
+(16 rows)
+
+--
+-- N-way join
+--
+EXPLAIN (VERBOSE, 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;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 3) is considered for partition-wise join.
+NOTICE: join between relations (b 1 2) and (b 3) is considered for partition-wise join.
+NOTICE: join between relations (b 1 3) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2 3) and (b 1) is considered for partition-wise join.
+ QUERY PLAN
+------------------------------------------------------------------------------------------
+ Sort
+ Output: (avg(t1.a)), (avg(t2.b)), (avg((t3.a + t3.b))), t1.c, t2.c, t3.c
+ Sort Key: t1.c, t3.c
+ -> HashAggregate
+ Output: avg(t1.a), avg(t2.b), avg((t3.a + t3.b)), t1.c, t2.c, t3.c
+ Group Key: t1.c, t2.c, t3.c
+ -> Result
+ Output: t1.c, t2.c, t3.c, t1.a, t2.b, t3.a, t3.b
+ -> Append
+ -> Hash Join
+ Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c
+ Hash Cond: (t1.c = t2.c)
+ -> Seq Scan on public.plt1_p1 t1
+ Output: t1.a, t1.c
+ -> Hash
+ Output: t2.b, t2.c, t3.a, t3.b, t3.c
+ -> Hash Join
+ Output: t2.b, t2.c, t3.a, t3.b, t3.c
+ Hash Cond: (t2.c = ltrim(t3.c, 'A'::text))
+ -> Seq Scan on public.plt2_p1 t2
+ Output: t2.b, t2.c
+ -> Hash
+ Output: t3.a, t3.b, t3.c
+ -> Seq Scan on public.plt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c
+ -> Hash Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+ Hash Cond: (t1_1.c = t2_1.c)
+ -> Seq Scan on public.plt1_p2 t1_1
+ Output: t1_1.a, t1_1.c
+ -> Hash
+ Output: t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+ -> Hash Join
+ Output: t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+ Hash Cond: (t2_1.c = ltrim(t3_1.c, 'A'::text))
+ -> Seq Scan on public.plt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Hash
+ Output: t3_1.a, t3_1.b, t3_1.c
+ -> Seq Scan on public.plt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c
+ -> Hash Join
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+ Hash Cond: (t1_2.c = t2_2.c)
+ -> Seq Scan on public.plt1_p3 t1_2
+ Output: t1_2.a, t1_2.c
+ -> Hash
+ Output: t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+ -> Hash Join
+ Output: t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+ Hash Cond: (t2_2.c = ltrim(t3_2.c, 'A'::text))
+ -> Seq Scan on public.plt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Hash
+ Output: t3_2.a, t3_2.b, t3_2.c
+ -> Seq Scan on public.plt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c
+(57 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;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 3) is considered for partition-wise join.
+NOTICE: join between relations (b 1 2) and (b 3) is considered for partition-wise join.
+NOTICE: join between relations (b 1 3) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2 3) and (b 1) is considered for partition-wise join.
+ 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)
+
+SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM uplt1 t1, uplt2 t2, uplt1_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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+ Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+ -> Append
+ -> Hash Right Join
+ Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c
+ Hash Cond: ((t3.a = t1.a) AND (ltrim(t3.c, 'A'::text) = t1.c))
+ -> Seq Scan on public.plt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c
+ -> Hash
+ Output: t1.a, t1.c, t2.b, t2.c
+ -> Hash Right Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: ((t2.b = t1.a) AND (t2.c = t1.c))
+ -> Seq Scan on public.plt2_p1 t2
+ Output: t2.b, t2.c
+ -> Hash
+ Output: t1.a, t1.c
+ -> Seq Scan on public.plt1_p1 t1
+ Output: t1.a, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+ Hash Cond: ((t3_1.a = t1_1.a) AND (ltrim(t3_1.c, 'A'::text) = t1_1.c))
+ -> Seq Scan on public.plt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c
+ -> Hash
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ -> Hash Right Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ Hash Cond: ((t2_1.b = t1_1.a) AND (t2_1.c = t1_1.c))
+ -> Seq Scan on public.plt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Hash
+ Output: t1_1.a, t1_1.c
+ -> Seq Scan on public.plt1_p2 t1_1
+ Output: t1_1.a, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+ Hash Cond: ((t3_2.a = t1_2.a) AND (ltrim(t3_2.c, 'A'::text) = t1_2.c))
+ -> Seq Scan on public.plt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c
+ -> Hash
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+ -> Hash Right Join
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+ Hash Cond: ((t2_2.b = t1_2.a) AND (t2_2.c = t1_2.c))
+ -> Seq Scan on public.plt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Hash
+ Output: t1_2.a, t1_2.c
+ -> Seq Scan on public.plt1_p3 t1_2
+ Output: t1_2.a, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+(57 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 50 | 0001 | | | 100 | A0001
+ 100 | 0002 | | | 200 | A0002
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 200 | 0004 | | | 400 | A0004
+ 250 | 0005 | | | 500 | A0005
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 350 | 0007 | | | 700 | A0007
+ 400 | 0008 | | | 800 | A0008
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ 500 | 0010 | | | 1000 | A0010
+ 550 | 0011 | | | 1100 | A0011
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN uplt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 50 | 0001 | | | 100 | A0001
+ 100 | 0002 | | | 200 | A0002
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 200 | 0004 | | | 400 | A0004
+ 250 | 0005 | | | 500 | A0005
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 350 | 0007 | | | 700 | A0007
+ 400 | 0008 | | | 800 | A0008
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ 500 | 0010 | | | 1000 | A0010
+ 550 | 0011 | | | 1100 | A0011
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 2 4) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+ Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+ -> Append
+ -> Hash Right Join
+ Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c
+ Hash Cond: ((t3.a = t2.b) AND (ltrim(t3.c, 'A'::text) = t2.c))
+ -> Seq Scan on public.plt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c
+ -> Hash
+ Output: t1.a, t1.c, t2.b, t2.c
+ -> Hash Right Join
+ Output: t1.a, t1.c, t2.b, t2.c
+ Hash Cond: ((t2.b = t1.a) AND (t2.c = t1.c))
+ -> Seq Scan on public.plt2_p1 t2
+ Output: t2.b, t2.c
+ -> Hash
+ Output: t1.a, t1.c
+ -> Seq Scan on public.plt1_p1 t1
+ Output: t1.a, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+ Hash Cond: ((t3_1.a = t2_1.b) AND (ltrim(t3_1.c, 'A'::text) = t2_1.c))
+ -> Seq Scan on public.plt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c
+ -> Hash
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ -> Hash Right Join
+ Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ Hash Cond: ((t2_1.b = t1_1.a) AND (t2_1.c = t1_1.c))
+ -> Seq Scan on public.plt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Hash
+ Output: t1_1.a, t1_1.c
+ -> Seq Scan on public.plt1_p2 t1_1
+ Output: t1_1.a, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+ Hash Cond: ((t3_2.a = t2_2.b) AND (ltrim(t3_2.c, 'A'::text) = t2_2.c))
+ -> Seq Scan on public.plt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c
+ -> Hash
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+ -> Hash Right Join
+ Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+ Hash Cond: ((t2_2.b = t1_2.a) AND (t2_2.c = t1_2.c))
+ -> Seq Scan on public.plt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Hash
+ Output: t1_2.a, t1_2.c
+ -> Seq Scan on public.plt1_p3 t1_2
+ Output: t1_2.a, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+(57 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 2 4) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 50 | 0001 | | | |
+ 100 | 0002 | | | |
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 200 | 0004 | | | |
+ 250 | 0005 | | | |
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 350 | 0007 | | | |
+ 400 | 0008 | | | |
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ 500 | 0010 | | | |
+ 550 | 0011 | | | |
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 50 | 0001 | | | |
+ 100 | 0002 | | | |
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 200 | 0004 | | | |
+ 250 | 0005 | | | |
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 350 | 0007 | | | |
+ 400 | 0008 | | | |
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ 500 | 0010 | | | |
+ 550 | 0011 | | | |
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+ Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+ -> Append
+ -> Hash Right Join
+ Output: t3.a, t3.b, t3.c, t1.a, t1.c, t2.b, t2.c
+ Hash Cond: ((t2.b = t1.a) AND (t2.c = t1.c))
+ -> Seq Scan on public.plt2_p1 t2
+ Output: t2.b, t2.c
+ -> Hash
+ Output: t3.a, t3.b, t3.c, t1.a, t1.c
+ -> Hash Right Join
+ Output: t3.a, t3.b, t3.c, t1.a, t1.c
+ Hash Cond: ((t1.c = ltrim(t3.c, 'A'::text)) AND (t1.a = t3.a))
+ -> Seq Scan on public.plt1_p1 t1
+ Output: t1.a, t1.c
+ -> Hash
+ Output: t3.a, t3.b, t3.c
+ -> Seq Scan on public.plt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c
+ Filter: ((t3.a % 25) = 0)
+ -> Hash Right Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t1_1.a, t1_1.c, t2_1.b, t2_1.c
+ Hash Cond: ((t2_1.b = t1_1.a) AND (t2_1.c = t1_1.c))
+ -> Seq Scan on public.plt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Hash
+ Output: t3_1.a, t3_1.b, t3_1.c, t1_1.a, t1_1.c
+ -> Hash Right Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t1_1.a, t1_1.c
+ Hash Cond: ((t1_1.c = ltrim(t3_1.c, 'A'::text)) AND (t1_1.a = t3_1.a))
+ -> Seq Scan on public.plt1_p2 t1_1
+ Output: t1_1.a, t1_1.c
+ -> Hash
+ Output: t3_1.a, t3_1.b, t3_1.c
+ -> Seq Scan on public.plt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c
+ Filter: ((t3_1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t1_2.a, t1_2.c, t2_2.b, t2_2.c
+ Hash Cond: ((t2_2.b = t1_2.a) AND (t2_2.c = t1_2.c))
+ -> Seq Scan on public.plt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Hash
+ Output: t3_2.a, t3_2.b, t3_2.c, t1_2.a, t1_2.c
+ -> Hash Right Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t1_2.a, t1_2.c
+ Hash Cond: ((t1_2.c = ltrim(t3_2.c, 'A'::text)) AND (t1_2.a = t3_2.a))
+ -> Seq Scan on public.plt1_p3 t1_2
+ Output: t1_2.a, t1_2.c
+ -> Hash
+ Output: t3_2.a, t3_2.b, t3_2.c
+ -> Seq Scan on public.plt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c
+ Filter: ((t3_2.a % 25) = 0)
+(57 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 50 | 0001 | | | 100 | A0001
+ 100 | 0002 | | | 200 | A0002
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 200 | 0004 | | | 400 | A0004
+ 250 | 0005 | | | 500 | A0005
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 350 | 0007 | | | 700 | A0007
+ 400 | 0008 | | | 800 | A0008
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ 500 | 0010 | | | 1000 | A0010
+ 550 | 0011 | | | 1100 | A0011
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 50 | 0001 | | | 100 | A0001
+ 100 | 0002 | | | 200 | A0002
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 200 | 0004 | | | 400 | A0004
+ 250 | 0005 | | | 500 | A0005
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 350 | 0007 | | | 700 | A0007
+ 400 | 0008 | | | 800 | A0008
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ 500 | 0010 | | | 1000 | A0010
+ 550 | 0011 | | | 1100 | A0011
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+ Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+ -> Append
+ -> Hash Right Join
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c
+ Hash Cond: ((t1.a = t2.b) AND (t1.c = t2.c))
+ -> Seq Scan on public.plt1_p1 t1
+ Output: t1.a, t1.c
+ -> Hash
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c
+ -> Hash Right Join
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c
+ Hash Cond: ((t2.b = t3.a) AND (t2.c = ltrim(t3.c, 'A'::text)))
+ -> Seq Scan on public.plt2_p1 t2
+ Output: t2.b, t2.c
+ -> Hash
+ Output: t3.a, t3.b, t3.c
+ -> Seq Scan on public.plt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c
+ Filter: ((t3.a % 25) = 0)
+ -> Hash Right Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_1.a, t1_1.c
+ Hash Cond: ((t1_1.a = t2_1.b) AND (t1_1.c = t2_1.c))
+ -> Seq Scan on public.plt1_p2 t1_1
+ Output: t1_1.a, t1_1.c
+ -> Hash
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+ -> Hash Right Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+ Hash Cond: ((t2_1.b = t3_1.a) AND (t2_1.c = ltrim(t3_1.c, 'A'::text)))
+ -> Seq Scan on public.plt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Hash
+ Output: t3_1.a, t3_1.b, t3_1.c
+ -> Seq Scan on public.plt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c
+ Filter: ((t3_1.a % 25) = 0)
+ -> Hash Right Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_2.a, t1_2.c
+ Hash Cond: ((t1_2.a = t2_2.b) AND (t1_2.c = t2_2.c))
+ -> Seq Scan on public.plt1_p3 t1_2
+ Output: t1_2.a, t1_2.c
+ -> Hash
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+ -> Hash Right Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+ Hash Cond: ((t2_2.b = t3_2.a) AND (t2_2.c = ltrim(t3_2.c, 'A'::text)))
+ -> Seq Scan on public.plt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Hash
+ Output: t3_2.a, t3_2.b, t3_2.c
+ -> Seq Scan on public.plt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c
+ Filter: ((t3_2.a % 25) = 0)
+(57 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ | | | | 100 | A0001
+ | | | | 200 | A0002
+ | | | | 400 | A0004
+ | | | | 500 | A0005
+ | | | | 700 | A0007
+ | | | | 800 | A0008
+ | | | | 1000 | A0010
+ | | | | 1100 | A0011
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ | | | | 100 | A0001
+ | | | | 200 | A0002
+ | | | | 400 | A0004
+ | | | | 500 | A0005
+ | | | | 700 | A0007
+ | | | | 800 | A0008
+ | | | | 1000 | A0010
+ | | | | 1100 | A0011
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE: join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+ QUERY PLAN
+---------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, ((plt1_e_p1.a + plt1_e_p1.b)), plt1_e_p1.c
+ Sort Key: plt1_p1.a, plt2_p1.b, ((plt1_e_p1.a + plt1_e_p1.b))
+ -> Result
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, (plt1_e_p1.a + plt1_e_p1.b), plt1_e_p1.c
+ -> Append
+ -> Hash Full Join
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c
+ Hash Cond: ((plt1_p1.a = plt1_e_p1.a) AND (plt1_p1.c = ltrim(plt1_e_p1.c, 'A'::text)))
+ -> Hash Full Join
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+ Hash Cond: ((plt1_p1.a = plt2_p1.b) AND (plt1_p1.c = plt2_p1.c))
+ -> Seq Scan on public.plt1_p1
+ Output: plt1_p1.a, plt1_p1.c
+ Filter: ((plt1_p1.a % 25) = 0)
+ -> Hash
+ Output: plt2_p1.b, plt2_p1.c
+ -> Seq Scan on public.plt2_p1
+ Output: plt2_p1.b, plt2_p1.c
+ Filter: ((plt2_p1.b % 25) = 0)
+ -> Hash
+ Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c
+ -> Seq Scan on public.plt1_e_p1
+ Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c
+ Filter: ((plt1_e_p1.a % 25) = 0)
+ -> Hash Full Join
+ Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c, plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c
+ Hash Cond: ((plt1_p2.a = plt1_e_p2.a) AND (plt1_p2.c = ltrim(plt1_e_p2.c, 'A'::text)))
+ -> Hash Full Join
+ Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c
+ Hash Cond: ((plt1_p2.a = plt2_p2.b) AND (plt1_p2.c = plt2_p2.c))
+ -> Seq Scan on public.plt1_p2
+ Output: plt1_p2.a, plt1_p2.c
+ Filter: ((plt1_p2.a % 25) = 0)
+ -> Hash
+ Output: plt2_p2.b, plt2_p2.c
+ -> Seq Scan on public.plt2_p2
+ Output: plt2_p2.b, plt2_p2.c
+ Filter: ((plt2_p2.b % 25) = 0)
+ -> Hash
+ Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c
+ -> Seq Scan on public.plt1_e_p2
+ Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c
+ Filter: ((plt1_e_p2.a % 25) = 0)
+ -> Hash Full Join
+ Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c, plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c
+ Hash Cond: ((plt1_p3.a = plt1_e_p3.a) AND (plt1_p3.c = ltrim(plt1_e_p3.c, 'A'::text)))
+ -> Hash Full Join
+ Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c
+ Hash Cond: ((plt1_p3.a = plt2_p3.b) AND (plt1_p3.c = plt2_p3.c))
+ -> Seq Scan on public.plt1_p3
+ Output: plt1_p3.a, plt1_p3.c
+ Filter: ((plt1_p3.a % 25) = 0)
+ -> Hash
+ Output: plt2_p3.b, plt2_p3.c
+ -> Seq Scan on public.plt2_p3
+ Output: plt2_p3.b, plt2_p3.c
+ Filter: ((plt2_p3.b % 25) = 0)
+ -> Hash
+ Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c
+ -> Seq Scan on public.plt1_e_p3
+ Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c
+ Filter: ((plt1_e_p3.a % 25) = 0)
+(63 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE: join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 50 | 0001 | | | 100 | A0001
+ 100 | 0002 | | | 200 | A0002
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 200 | 0004 | | | 400 | A0004
+ 250 | 0005 | | | 500 | A0005
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 350 | 0007 | | | 700 | A0007
+ 400 | 0008 | | | 800 | A0008
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ 500 | 0010 | | | 1000 | A0010
+ 550 | 0011 | | | 1100 | A0011
+ | | 75 | 0001 | |
+ | | 225 | 0004 | |
+ | | 375 | 0007 | |
+ | | 525 | 0010 | |
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 50 | 0001 | | | 100 | A0001
+ 100 | 0002 | | | 200 | A0002
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 200 | 0004 | | | 400 | A0004
+ 250 | 0005 | | | 500 | A0005
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 350 | 0007 | | | 700 | A0007
+ 400 | 0008 | | | 800 | A0008
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ 500 | 0010 | | | 1000 | A0010
+ 550 | 0011 | | | 1100 | A0011
+ | | 75 | 0001 | |
+ | | 225 | 0004 | |
+ | | 375 | 0007 | |
+ | | 525 | 0010 | |
+(16 rows)
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1, plt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 4) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.b, t1.c
+ Sort Key: t1.a
+ -> Append
+ -> Nested Loop Semi Join
+ Output: t1.a, t1.b, t1.c
+ Join Filter: (t1.c = t1_3.c)
+ -> Seq Scan on public.plt1_p1 t1
+ Output: t1.a, t1.b, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_3.c, t2.c
+ Hash Cond: (t1_3.c = ltrim(t2.c, 'A'::text))
+ -> Seq Scan on public.plt2_p1 t1_3
+ Output: t1_3.c
+ -> Hash
+ Output: t2.c
+ -> Seq Scan on public.plt1_e_p1 t2
+ Output: t2.c
+ -> Nested Loop Semi Join
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Join Filter: (t1_1.c = t1_4.c)
+ -> Seq Scan on public.plt1_p2 t1_1
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_4.c, t2_1.c
+ Hash Cond: (t1_4.c = ltrim(t2_1.c, 'A'::text))
+ -> Seq Scan on public.plt2_p2 t1_4
+ Output: t1_4.c
+ -> Hash
+ Output: t2_1.c
+ -> Seq Scan on public.plt1_e_p2 t2_1
+ Output: t2_1.c
+ -> Nested Loop Semi Join
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Join Filter: (t1_2.c = t1_5.c)
+ -> Seq Scan on public.plt1_p3 t1_2
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Hash Join
+ Output: t1_5.c, t2_2.c
+ Hash Cond: (t1_5.c = ltrim(t2_2.c, 'A'::text))
+ -> Seq Scan on public.plt2_p3 t1_5
+ Output: t1_5.c
+ -> Hash
+ Output: t2_2.c
+ -> Seq Scan on public.plt1_e_p3 t2_2
+ Output: t2_2.c
+(49 rows)
+
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1, plt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 4) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 4) is considered for partition-wise join.
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 50 | 50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1, uplt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a;
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 50 | 50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.b, t1.c
+ Sort Key: t1.a
+ -> Append
+ -> Nested Loop Semi Join
+ Output: t1.a, t1.b, t1.c
+ Join Filter: (t1.c = t1_3.c)
+ -> Seq Scan on public.plt1_p1 t1
+ Output: t1.a, t1.b, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_3.c, t1_6.c
+ Hash Cond: (t1_3.c = ltrim(t1_6.c, 'A'::text))
+ -> Seq Scan on public.plt2_p1 t1_3
+ Output: t1_3.c
+ -> Hash
+ Output: t1_6.c
+ -> HashAggregate
+ Output: t1_6.c
+ Group Key: ltrim(t1_6.c, 'A'::text)
+ -> Seq Scan on public.plt1_e_p1 t1_6
+ Output: t1_6.c, ltrim(t1_6.c, 'A'::text)
+ Filter: ((t1_6.a % 25) = 0)
+ -> Nested Loop Semi Join
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Join Filter: (t1_1.c = t1_4.c)
+ -> Seq Scan on public.plt1_p2 t1_1
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Hash Join
+ Output: t1_4.c, t1_7.c
+ Hash Cond: (t1_4.c = ltrim(t1_7.c, 'A'::text))
+ -> Seq Scan on public.plt2_p2 t1_4
+ Output: t1_4.c
+ -> Hash
+ Output: t1_7.c
+ -> HashAggregate
+ Output: t1_7.c
+ Group Key: ltrim(t1_7.c, 'A'::text)
+ -> Seq Scan on public.plt1_e_p2 t1_7
+ Output: t1_7.c, ltrim(t1_7.c, 'A'::text)
+ Filter: ((t1_7.a % 25) = 0)
+ -> Nested Loop Semi Join
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Join Filter: (t1_2.c = t1_5.c)
+ -> Seq Scan on public.plt1_p3 t1_2
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Hash Join
+ Output: t1_5.c, t1_8.c
+ Hash Cond: (t1_5.c = ltrim(t1_8.c, 'A'::text))
+ -> Seq Scan on public.plt2_p3 t1_5
+ Output: t1_5.c
+ -> Hash
+ Output: t1_8.c
+ -> HashAggregate
+ Output: t1_8.c
+ Group Key: ltrim(t1_8.c, 'A'::text)
+ -> Seq Scan on public.plt1_e_p3 t1_8
+ Output: t1_8.c, ltrim(t1_8.c, 'A'::text)
+ Filter: ((t1_8.a % 25) = 0)
+(61 rows)
+
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 50 | 50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 50 | 50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+-- test merge join with and without index scan
+CREATE INDEX iplt1_c on plt1(c);
+CREATE INDEX iplt1_p1_c on plt1_p1(c);
+CREATE INDEX iplt1_p2_c on plt1_p2(c);
+CREATE INDEX iplt1_p3_c on plt1_p3(c);
+CREATE INDEX iplt2_c on plt2(c);
+CREATE INDEX iplt2_p1_c on plt2_p1(c);
+CREATE INDEX iplt2_p2_c on plt2_p2(c);
+CREATE INDEX iplt2_p3_c on plt2_p3(c);
+CREATE INDEX iplt1_e_c on plt1_e(ltrim(c, 'A'));
+CREATE INDEX iplt1_e_p1_c on plt1_e_p1(ltrim(c, 'A'));
+CREATE INDEX iplt1_e_p2_c on plt1_e_p2(ltrim(c, 'A'));
+CREATE INDEX iplt1_e_p3_c on plt1_e_p3(ltrim(c, 'A'));
+ANALYZE plt1;
+ANALYZE plt1_p1;
+ANALYZE plt1_p2;
+ANALYZE plt1_p3;
+ANALYZE plt2;
+ANALYZE plt2_p1;
+ANALYZE plt2_p2;
+ANALYZE plt2_p3;
+ANALYZE plt1_e;
+ANALYZE plt1_e_p1;
+ANALYZE plt1_e_p2;
+ANALYZE plt1_e_p3;
+SET enable_hashjoin TO off;
+SET enable_nestloop TO off;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+ Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+ -> Append
+ -> Merge Left Join
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c
+ Merge Cond: ((t2.b = t1.a) AND (t2.c = t1.c))
+ -> Sort
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c
+ Sort Key: t2.b, t2.c
+ -> Merge Left Join
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c
+ Merge Cond: ((t3.a = t2.b) AND ((ltrim(t3.c, 'A'::text)) = t2.c))
+ -> Sort
+ Output: t3.a, t3.b, t3.c, (ltrim(t3.c, 'A'::text))
+ Sort Key: t3.a, (ltrim(t3.c, 'A'::text))
+ -> Seq Scan on public.plt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c, ltrim(t3.c, 'A'::text)
+ Filter: ((t3.a % 25) = 0)
+ -> Sort
+ Output: t2.b, t2.c
+ Sort Key: t2.b, t2.c
+ -> Seq Scan on public.plt2_p1 t2
+ Output: t2.b, t2.c
+ -> Sort
+ Output: t1.a, t1.c
+ Sort Key: t1.a, t1.c
+ -> Seq Scan on public.plt1_p1 t1
+ Output: t1.a, t1.c
+ -> Merge Left Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_1.a, t1_1.c
+ Merge Cond: ((t2_1.c = t1_1.c) AND (t2_1.b = t1_1.a))
+ -> Sort
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+ Sort Key: t2_1.c, t2_1.b
+ -> Merge Left Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+ Merge Cond: ((t3_1.a = t2_1.b) AND ((ltrim(t3_1.c, 'A'::text)) = t2_1.c))
+ -> Sort
+ Output: t3_1.a, t3_1.b, t3_1.c, (ltrim(t3_1.c, 'A'::text))
+ Sort Key: t3_1.a, (ltrim(t3_1.c, 'A'::text))
+ -> Seq Scan on public.plt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c, ltrim(t3_1.c, 'A'::text)
+ Filter: ((t3_1.a % 25) = 0)
+ -> Sort
+ Output: t2_1.b, t2_1.c
+ Sort Key: t2_1.b, t2_1.c
+ -> Seq Scan on public.plt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Sort
+ Output: t1_1.a, t1_1.c
+ Sort Key: t1_1.c, t1_1.a
+ -> Seq Scan on public.plt1_p2 t1_1
+ Output: t1_1.a, t1_1.c
+ -> Merge Left Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_2.a, t1_2.c
+ Merge Cond: ((t2_2.b = t1_2.a) AND (t2_2.c = t1_2.c))
+ -> Sort
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+ Sort Key: t2_2.b, t2_2.c
+ -> Merge Left Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+ Merge Cond: ((t3_2.a = t2_2.b) AND ((ltrim(t3_2.c, 'A'::text)) = t2_2.c))
+ -> Sort
+ Output: t3_2.a, t3_2.b, t3_2.c, (ltrim(t3_2.c, 'A'::text))
+ Sort Key: t3_2.a, (ltrim(t3_2.c, 'A'::text))
+ -> Seq Scan on public.plt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c, ltrim(t3_2.c, 'A'::text)
+ Filter: ((t3_2.a % 25) = 0)
+ -> Sort
+ Output: t2_2.b, t2_2.c
+ Sort Key: t2_2.b, t2_2.c
+ -> Seq Scan on public.plt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Sort
+ Output: t1_2.a, t1_2.c
+ Sort Key: t1_2.a, t1_2.c
+ -> Seq Scan on public.plt1_p3 t1_2
+ Output: t1_2.a, t1_2.c
+(81 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ | | | | 100 | A0001
+ | | | | 200 | A0002
+ | | | | 400 | A0004
+ | | | | 500 | A0005
+ | | | | 700 | A0007
+ | | | | 800 | A0008
+ | | | | 1000 | A0010
+ | | | | 1100 | A0011
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ | | | | 100 | A0001
+ | | | | 200 | A0002
+ | | | | 400 | A0004
+ | | | | 500 | A0005
+ | | | | 700 | A0007
+ | | | | 800 | A0008
+ | | | | 1000 | A0010
+ | | | | 1100 | A0011
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE: join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+ QUERY PLAN
+---------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, ((plt1_e_p1.a + plt1_e_p1.b)), plt1_e_p1.c
+ Sort Key: plt1_p1.a, plt2_p1.b, ((plt1_e_p1.a + plt1_e_p1.b))
+ -> Result
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, (plt1_e_p1.a + plt1_e_p1.b), plt1_e_p1.c
+ -> Append
+ -> Merge Full Join
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c
+ Merge Cond: ((plt1_p1.a = plt1_e_p1.a) AND (plt1_p1.c = (ltrim(plt1_e_p1.c, 'A'::text))))
+ -> Sort
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+ Sort Key: plt1_p1.a, plt1_p1.c
+ -> Merge Full Join
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+ Merge Cond: ((plt1_p1.a = plt2_p1.b) AND (plt1_p1.c = plt2_p1.c))
+ -> Sort
+ Output: plt1_p1.a, plt1_p1.c
+ Sort Key: plt1_p1.a, plt1_p1.c
+ -> Seq Scan on public.plt1_p1
+ Output: plt1_p1.a, plt1_p1.c
+ Filter: ((plt1_p1.a % 25) = 0)
+ -> Sort
+ Output: plt2_p1.b, plt2_p1.c
+ Sort Key: plt2_p1.b, plt2_p1.c
+ -> Seq Scan on public.plt2_p1
+ Output: plt2_p1.b, plt2_p1.c
+ Filter: ((plt2_p1.b % 25) = 0)
+ -> Sort
+ Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c, (ltrim(plt1_e_p1.c, 'A'::text))
+ Sort Key: plt1_e_p1.a, (ltrim(plt1_e_p1.c, 'A'::text))
+ -> Seq Scan on public.plt1_e_p1
+ Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c, ltrim(plt1_e_p1.c, 'A'::text)
+ Filter: ((plt1_e_p1.a % 25) = 0)
+ -> Merge Full Join
+ Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c, plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c
+ Merge Cond: ((plt1_p2.a = plt1_e_p2.a) AND (plt1_p2.c = (ltrim(plt1_e_p2.c, 'A'::text))))
+ -> Sort
+ Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c
+ Sort Key: plt1_p2.a, plt1_p2.c
+ -> Merge Full Join
+ Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c
+ Merge Cond: ((plt1_p2.a = plt2_p2.b) AND (plt1_p2.c = plt2_p2.c))
+ -> Sort
+ Output: plt1_p2.a, plt1_p2.c
+ Sort Key: plt1_p2.a, plt1_p2.c
+ -> Seq Scan on public.plt1_p2
+ Output: plt1_p2.a, plt1_p2.c
+ Filter: ((plt1_p2.a % 25) = 0)
+ -> Sort
+ Output: plt2_p2.b, plt2_p2.c
+ Sort Key: plt2_p2.b, plt2_p2.c
+ -> Seq Scan on public.plt2_p2
+ Output: plt2_p2.b, plt2_p2.c
+ Filter: ((plt2_p2.b % 25) = 0)
+ -> Sort
+ Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c, (ltrim(plt1_e_p2.c, 'A'::text))
+ Sort Key: plt1_e_p2.a, (ltrim(plt1_e_p2.c, 'A'::text))
+ -> Seq Scan on public.plt1_e_p2
+ Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c, ltrim(plt1_e_p2.c, 'A'::text)
+ Filter: ((plt1_e_p2.a % 25) = 0)
+ -> Merge Full Join
+ Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c, plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c
+ Merge Cond: ((plt1_p3.a = plt1_e_p3.a) AND (plt1_p3.c = (ltrim(plt1_e_p3.c, 'A'::text))))
+ -> Sort
+ Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c
+ Sort Key: plt1_p3.a, plt1_p3.c
+ -> Merge Full Join
+ Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c
+ Merge Cond: ((plt1_p3.a = plt2_p3.b) AND (plt1_p3.c = plt2_p3.c))
+ -> Sort
+ Output: plt1_p3.a, plt1_p3.c
+ Sort Key: plt1_p3.a, plt1_p3.c
+ -> Seq Scan on public.plt1_p3
+ Output: plt1_p3.a, plt1_p3.c
+ Filter: ((plt1_p3.a % 25) = 0)
+ -> Sort
+ Output: plt2_p3.b, plt2_p3.c
+ Sort Key: plt2_p3.b, plt2_p3.c
+ -> Seq Scan on public.plt2_p3
+ Output: plt2_p3.b, plt2_p3.c
+ Filter: ((plt2_p3.b % 25) = 0)
+ -> Sort
+ Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c, (ltrim(plt1_e_p3.c, 'A'::text))
+ Sort Key: plt1_e_p3.a, (ltrim(plt1_e_p3.c, 'A'::text))
+ -> Seq Scan on public.plt1_e_p3
+ Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c, ltrim(plt1_e_p3.c, 'A'::text)
+ Filter: ((plt1_e_p3.a % 25) = 0)
+(87 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE: join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 50 | 0001 | | | 100 | A0001
+ 100 | 0002 | | | 200 | A0002
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 200 | 0004 | | | 400 | A0004
+ 250 | 0005 | | | 500 | A0005
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 350 | 0007 | | | 700 | A0007
+ 400 | 0008 | | | 800 | A0008
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ 500 | 0010 | | | 1000 | A0010
+ 550 | 0011 | | | 1100 | A0011
+ | | 75 | 0001 | |
+ | | 225 | 0004 | |
+ | | 375 | 0007 | |
+ | | 525 | 0010 | |
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 50 | 0001 | | | 100 | A0001
+ 100 | 0002 | | | 200 | A0002
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 200 | 0004 | | | 400 | A0004
+ 250 | 0005 | | | 500 | A0005
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 350 | 0007 | | | 700 | A0007
+ 400 | 0008 | | | 800 | A0008
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ 500 | 0010 | | | 1000 | A0010
+ 550 | 0011 | | | 1100 | A0011
+ | | 75 | 0001 | |
+ | | 225 | 0004 | |
+ | | 375 | 0007 | |
+ | | 525 | 0010 | |
+(16 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.b, t1.c
+ Sort Key: t1.a
+ -> Append
+ -> Merge Semi Join
+ Output: t1.a, t1.b, t1.c
+ Merge Cond: (t1.c = t1_3.c)
+ -> Sort
+ Output: t1.a, t1.b, t1.c
+ Sort Key: t1.c
+ -> Seq Scan on public.plt1_p1 t1
+ Output: t1.a, t1.b, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Materialize
+ Output: t1_3.c, t1_6.c
+ -> Merge Join
+ Output: t1_3.c, t1_6.c
+ Merge Cond: ((ltrim(t1_6.c, 'A'::text)) = t1_3.c)
+ -> Sort
+ Output: t1_6.c, (ltrim(t1_6.c, 'A'::text))
+ Sort Key: (ltrim(t1_6.c, 'A'::text))
+ -> HashAggregate
+ Output: t1_6.c, (ltrim(t1_6.c, 'A'::text))
+ Group Key: ltrim(t1_6.c, 'A'::text)
+ -> Seq Scan on public.plt1_e_p1 t1_6
+ Output: t1_6.c, ltrim(t1_6.c, 'A'::text)
+ Filter: ((t1_6.a % 25) = 0)
+ -> Sort
+ Output: t1_3.c
+ Sort Key: t1_3.c
+ -> Seq Scan on public.plt2_p1 t1_3
+ Output: t1_3.c
+ -> Merge Semi Join
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Merge Cond: (t1_1.c = t1_4.c)
+ -> Sort
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Sort Key: t1_1.c
+ -> Seq Scan on public.plt1_p2 t1_1
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Materialize
+ Output: t1_4.c, t1_7.c
+ -> Merge Join
+ Output: t1_4.c, t1_7.c
+ Merge Cond: ((ltrim(t1_7.c, 'A'::text)) = t1_4.c)
+ -> Sort
+ Output: t1_7.c, (ltrim(t1_7.c, 'A'::text))
+ Sort Key: (ltrim(t1_7.c, 'A'::text))
+ -> HashAggregate
+ Output: t1_7.c, (ltrim(t1_7.c, 'A'::text))
+ Group Key: ltrim(t1_7.c, 'A'::text)
+ -> Seq Scan on public.plt1_e_p2 t1_7
+ Output: t1_7.c, ltrim(t1_7.c, 'A'::text)
+ Filter: ((t1_7.a % 25) = 0)
+ -> Sort
+ Output: t1_4.c
+ Sort Key: t1_4.c
+ -> Seq Scan on public.plt2_p2 t1_4
+ Output: t1_4.c
+ -> Merge Semi Join
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Merge Cond: (t1_2.c = t1_5.c)
+ -> Sort
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Sort Key: t1_2.c
+ -> Seq Scan on public.plt1_p3 t1_2
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Materialize
+ Output: t1_5.c, t1_8.c
+ -> Merge Join
+ Output: t1_5.c, t1_8.c
+ Merge Cond: ((ltrim(t1_8.c, 'A'::text)) = t1_5.c)
+ -> Sort
+ Output: t1_8.c, (ltrim(t1_8.c, 'A'::text))
+ Sort Key: (ltrim(t1_8.c, 'A'::text))
+ -> HashAggregate
+ Output: t1_8.c, (ltrim(t1_8.c, 'A'::text))
+ Group Key: ltrim(t1_8.c, 'A'::text)
+ -> Seq Scan on public.plt1_e_p3 t1_8
+ Output: t1_8.c, ltrim(t1_8.c, 'A'::text)
+ Filter: ((t1_8.a % 25) = 0)
+ -> Sort
+ Output: t1_5.c
+ Sort Key: t1_5.c
+ -> Seq Scan on public.plt2_p3 t1_5
+ Output: t1_5.c
+(88 rows)
+
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 50 | 50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 50 | 50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+SET enable_seqscan TO off;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+ Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+ -> Result
+ Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+ -> Append
+ -> Merge Left Join
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c
+ Merge Cond: ((t2.b = t1.a) AND (t2.c = t1.c))
+ -> Sort
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c
+ Sort Key: t2.b, t2.c
+ -> Merge Left Join
+ Output: t3.a, t3.b, t3.c, t2.b, t2.c
+ Merge Cond: (ltrim(t3.c, 'A'::text) = t2.c)
+ Join Filter: (t2.b = t3.a)
+ -> Index Scan using iplt1_e_p1_c on public.plt1_e_p1 t3
+ Output: t3.a, t3.b, t3.c
+ Filter: ((t3.a % 25) = 0)
+ -> Index Scan using iplt2_p1_c on public.plt2_p1 t2
+ Output: t2.b, t2.c
+ -> Sort
+ Output: t1.a, t1.c
+ Sort Key: t1.a, t1.c
+ -> Index Scan using iplt1_p1_c on public.plt1_p1 t1
+ Output: t1.a, t1.c
+ -> Merge Left Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_1.a, t1_1.c
+ Merge Cond: ((t2_1.c = t1_1.c) AND (t2_1.b = t1_1.a))
+ -> Sort
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+ Sort Key: t2_1.c, t2_1.b
+ -> Merge Left Join
+ Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+ Merge Cond: (ltrim(t3_1.c, 'A'::text) = t2_1.c)
+ Join Filter: (t2_1.b = t3_1.a)
+ -> Index Scan using iplt1_e_p2_c on public.plt1_e_p2 t3_1
+ Output: t3_1.a, t3_1.b, t3_1.c
+ Filter: ((t3_1.a % 25) = 0)
+ -> Index Scan using iplt2_p2_c on public.plt2_p2 t2_1
+ Output: t2_1.b, t2_1.c
+ -> Sort
+ Output: t1_1.a, t1_1.c
+ Sort Key: t1_1.c, t1_1.a
+ -> Index Scan using iplt1_p2_c on public.plt1_p2 t1_1
+ Output: t1_1.a, t1_1.c
+ -> Merge Left Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_2.a, t1_2.c
+ Merge Cond: ((t2_2.b = t1_2.a) AND (t2_2.c = t1_2.c))
+ -> Sort
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+ Sort Key: t2_2.b, t2_2.c
+ -> Merge Left Join
+ Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+ Merge Cond: (ltrim(t3_2.c, 'A'::text) = t2_2.c)
+ Join Filter: (t2_2.b = t3_2.a)
+ -> Index Scan using iplt1_e_p3_c on public.plt1_e_p3 t3_2
+ Output: t3_2.a, t3_2.b, t3_2.c
+ Filter: ((t3_2.a % 25) = 0)
+ -> Index Scan using iplt2_p3_c on public.plt2_p3 t2_2
+ Output: t2_2.b, t2_2.c
+ -> Sort
+ Output: t1_2.a, t1_2.c
+ Sort Key: t1_2.a, t1_2.c
+ -> Index Scan using iplt1_p3_c on public.plt1_p3 t1_2
+ Output: t1_2.a, t1_2.c
+(66 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE: join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE: join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ | | | | 100 | A0001
+ | | | | 200 | A0002
+ | | | | 400 | A0004
+ | | | | 500 | A0005
+ | | | | 700 | A0007
+ | | | | 800 | A0008
+ | | | | 1000 | A0010
+ | | | | 1100 | A0011
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ | | | | 100 | A0001
+ | | | | 200 | A0002
+ | | | | 400 | A0004
+ | | | | 500 | A0005
+ | | | | 700 | A0007
+ | | | | 800 | A0008
+ | | | | 1000 | A0010
+ | | | | 1100 | A0011
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE: join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+ QUERY PLAN
+---------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, ((plt1_e_p1.a + plt1_e_p1.b)), plt1_e_p1.c
+ Sort Key: plt1_p1.a, plt2_p1.b, ((plt1_e_p1.a + plt1_e_p1.b))
+ -> Result
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, (plt1_e_p1.a + plt1_e_p1.b), plt1_e_p1.c
+ -> Append
+ -> Merge Full Join
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c
+ Merge Cond: ((plt1_p1.a = plt1_e_p1.a) AND (plt1_p1.c = (ltrim(plt1_e_p1.c, 'A'::text))))
+ -> Sort
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+ Sort Key: plt1_p1.a, plt1_p1.c
+ -> Merge Full Join
+ Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+ Merge Cond: ((plt1_p1.a = plt2_p1.b) AND (plt1_p1.c = plt2_p1.c))
+ -> Sort
+ Output: plt1_p1.a, plt1_p1.c
+ Sort Key: plt1_p1.a, plt1_p1.c
+ -> Index Scan using iplt1_p1_c on public.plt1_p1
+ Output: plt1_p1.a, plt1_p1.c
+ Filter: ((plt1_p1.a % 25) = 0)
+ -> Sort
+ Output: plt2_p1.b, plt2_p1.c
+ Sort Key: plt2_p1.b, plt2_p1.c
+ -> Index Scan using iplt2_p1_c on public.plt2_p1
+ Output: plt2_p1.b, plt2_p1.c
+ Filter: ((plt2_p1.b % 25) = 0)
+ -> Sort
+ Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c, (ltrim(plt1_e_p1.c, 'A'::text))
+ Sort Key: plt1_e_p1.a, (ltrim(plt1_e_p1.c, 'A'::text))
+ -> Index Scan using iplt1_e_p1_c on public.plt1_e_p1
+ Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c, ltrim(plt1_e_p1.c, 'A'::text)
+ Filter: ((plt1_e_p1.a % 25) = 0)
+ -> Merge Full Join
+ Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c, plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c
+ Merge Cond: ((plt1_p2.a = plt1_e_p2.a) AND (plt1_p2.c = (ltrim(plt1_e_p2.c, 'A'::text))))
+ -> Sort
+ Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c
+ Sort Key: plt1_p2.a, plt1_p2.c
+ -> Merge Full Join
+ Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c
+ Merge Cond: ((plt1_p2.a = plt2_p2.b) AND (plt1_p2.c = plt2_p2.c))
+ -> Sort
+ Output: plt1_p2.a, plt1_p2.c
+ Sort Key: plt1_p2.a, plt1_p2.c
+ -> Index Scan using iplt1_p2_c on public.plt1_p2
+ Output: plt1_p2.a, plt1_p2.c
+ Filter: ((plt1_p2.a % 25) = 0)
+ -> Sort
+ Output: plt2_p2.b, plt2_p2.c
+ Sort Key: plt2_p2.b, plt2_p2.c
+ -> Index Scan using iplt2_p2_c on public.plt2_p2
+ Output: plt2_p2.b, plt2_p2.c
+ Filter: ((plt2_p2.b % 25) = 0)
+ -> Sort
+ Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c, (ltrim(plt1_e_p2.c, 'A'::text))
+ Sort Key: plt1_e_p2.a, (ltrim(plt1_e_p2.c, 'A'::text))
+ -> Index Scan using iplt1_e_p2_c on public.plt1_e_p2
+ Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c, ltrim(plt1_e_p2.c, 'A'::text)
+ Filter: ((plt1_e_p2.a % 25) = 0)
+ -> Merge Full Join
+ Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c, plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c
+ Merge Cond: ((plt1_p3.a = plt1_e_p3.a) AND (plt1_p3.c = (ltrim(plt1_e_p3.c, 'A'::text))))
+ -> Sort
+ Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c
+ Sort Key: plt1_p3.a, plt1_p3.c
+ -> Merge Full Join
+ Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c
+ Merge Cond: ((plt1_p3.a = plt2_p3.b) AND (plt1_p3.c = plt2_p3.c))
+ -> Sort
+ Output: plt1_p3.a, plt1_p3.c
+ Sort Key: plt1_p3.a, plt1_p3.c
+ -> Index Scan using iplt1_p3_c on public.plt1_p3
+ Output: plt1_p3.a, plt1_p3.c
+ Filter: ((plt1_p3.a % 25) = 0)
+ -> Sort
+ Output: plt2_p3.b, plt2_p3.c
+ Sort Key: plt2_p3.b, plt2_p3.c
+ -> Index Scan using iplt2_p3_c on public.plt2_p3
+ Output: plt2_p3.b, plt2_p3.c
+ Filter: ((plt2_p3.b % 25) = 0)
+ -> Sort
+ Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c, (ltrim(plt1_e_p3.c, 'A'::text))
+ Sort Key: plt1_e_p3.a, (ltrim(plt1_e_p3.c, 'A'::text))
+ -> Index Scan using iplt1_e_p3_c on public.plt1_e_p3
+ Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c, ltrim(plt1_e_p3.c, 'A'::text)
+ Filter: ((plt1_e_p3.a % 25) = 0)
+(87 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE: join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE: join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 50 | 0001 | | | 100 | A0001
+ 100 | 0002 | | | 200 | A0002
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 200 | 0004 | | | 400 | A0004
+ 250 | 0005 | | | 500 | A0005
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 350 | 0007 | | | 700 | A0007
+ 400 | 0008 | | | 800 | A0008
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ 500 | 0010 | | | 1000 | A0010
+ 550 | 0011 | | | 1100 | A0011
+ | | 75 | 0001 | |
+ | | 225 | 0004 | |
+ | | 375 | 0007 | |
+ | | 525 | 0010 | |
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+ a | c | b | c | ?column? | c
+-----+------+-----+------+----------+-------
+ 0 | 0000 | 0 | 0000 | 0 | A0000
+ 50 | 0001 | | | 100 | A0001
+ 100 | 0002 | | | 200 | A0002
+ 150 | 0003 | 150 | 0003 | 300 | A0003
+ 200 | 0004 | | | 400 | A0004
+ 250 | 0005 | | | 500 | A0005
+ 300 | 0006 | 300 | 0006 | 600 | A0006
+ 350 | 0007 | | | 700 | A0007
+ 400 | 0008 | | | 800 | A0008
+ 450 | 0009 | 450 | 0009 | 900 | A0009
+ 500 | 0010 | | | 1000 | A0010
+ 550 | 0011 | | | 1100 | A0011
+ | | 75 | 0001 | |
+ | | 225 | 0004 | |
+ | | 375 | 0007 | |
+ | | 525 | 0010 | |
+(16 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+ QUERY PLAN
+--------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.b, t1.c
+ Sort Key: t1.a
+ -> Append
+ -> Merge Semi Join
+ Output: t1.a, t1.b, t1.c
+ Merge Cond: (t1.c = t1_3.c)
+ -> Index Scan using iplt1_p1_c on public.plt1_p1 t1
+ Output: t1.a, t1.b, t1.c
+ Filter: ((t1.a % 25) = 0)
+ -> Materialize
+ Output: t1_3.c, t1_6.c
+ -> Merge Semi Join
+ Output: t1_3.c, t1_6.c
+ Merge Cond: (t1_3.c = ltrim(t1_6.c, 'A'::text))
+ -> Index Only Scan using iplt2_p1_c on public.plt2_p1 t1_3
+ Output: t1_3.c
+ -> Materialize
+ Output: t1_6.c
+ -> Index Scan using iplt1_e_p1_c on public.plt1_e_p1 t1_6
+ Output: t1_6.c
+ Filter: ((t1_6.a % 25) = 0)
+ -> Merge Semi Join
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Merge Cond: (t1_1.c = t1_4.c)
+ -> Index Scan using iplt1_p2_c on public.plt1_p2 t1_1
+ Output: t1_1.a, t1_1.b, t1_1.c
+ Filter: ((t1_1.a % 25) = 0)
+ -> Materialize
+ Output: t1_4.c, t1_7.c
+ -> Merge Semi Join
+ Output: t1_4.c, t1_7.c
+ Merge Cond: (t1_4.c = ltrim(t1_7.c, 'A'::text))
+ -> Index Only Scan using iplt2_p2_c on public.plt2_p2 t1_4
+ Output: t1_4.c
+ -> Materialize
+ Output: t1_7.c
+ -> Index Scan using iplt1_e_p2_c on public.plt1_e_p2 t1_7
+ Output: t1_7.c
+ Filter: ((t1_7.a % 25) = 0)
+ -> Merge Semi Join
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Merge Cond: (t1_2.c = t1_5.c)
+ -> Index Scan using iplt1_p3_c on public.plt1_p3 t1_2
+ Output: t1_2.a, t1_2.b, t1_2.c
+ Filter: ((t1_2.a % 25) = 0)
+ -> Materialize
+ Output: t1_5.c, t1_8.c
+ -> Merge Semi Join
+ Output: t1_5.c, t1_8.c
+ Merge Cond: (t1_5.c = ltrim(t1_8.c, 'A'::text))
+ -> Index Only Scan using iplt2_p3_c on public.plt2_p3 t1_5
+ Output: t1_5.c
+ -> Materialize
+ Output: t1_8.c
+ -> Index Scan using iplt1_e_p3_c on public.plt1_e_p3 t1_8
+ Output: t1_8.c
+ Filter: ((t1_8.a % 25) = 0)
+(58 rows)
+
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE: join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE: join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 50 | 50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+ a | b | c
+-----+-----+------
+ 0 | 0 | 0000
+ 50 | 50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+RESET enable_hashjoin;
+RESET enable_nestloop;
+RESET enable_seqscan;
+--
+-- negative testcases
+--
+CREATE TABLE prt1_n (a int, b int, c varchar) PARTITION BY RANGE(c);
+CREATE TABLE prt1_n_p1 PARTITION OF prt1_n FOR VALUES START ('0000') END ('0250');
+CREATE TABLE prt1_n_p2 PARTITION OF prt1_n FOR VALUES START ('0250') END ('0500');
+INSERT INTO prt1_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 499, 2) i;
+ANALYZE prt1_n;
+ANALYZE prt1_n_p1;
+ANALYZE prt1_n_p2;
+CREATE TABLE prt2_n (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE prt2_n_p1 PARTITION OF prt2_n FOR VALUES IN ('0000', '0003', '0004', '0010', '0006', '0007');
+CREATE TABLE prt2_n_p2 PARTITION OF prt2_n FOR VALUES IN ('0001', '0005', '0002', '0009', '0008', '0011');
+INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt2_n;
+ANALYZE prt2_n_p1;
+ANALYZE prt2_n_p2;
+CREATE TABLE prt3_n (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE prt3_n_p1 PARTITION OF prt3_n FOR VALUES IN ('0000', '0004', '0006', '0007');
+CREATE TABLE prt3_n_p2 PARTITION OF prt3_n FOR VALUES IN ('0001', '0002', '0008', '0010');
+CREATE TABLE prt3_n_p3 PARTITION OF prt3_n FOR VALUES IN ('0003', '0005', '0009', '0011');
+INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt3_n;
+ANALYZE prt3_n_p1;
+ANALYZE prt3_n_p2;
+ANALYZE prt3_n_p3;
+CREATE TABLE prt4_n (a int, b int, c text) PARTITION BY RANGE(a);
+CREATE TABLE prt4_n_p1 PARTITION OF prt4_n FOR VALUES START (0) END (300);
+CREATE TABLE prt4_n_p2 PARTITION OF prt4_n FOR VALUES START (300) END (500);
+CREATE TABLE prt4_n_p3 PARTITION OF prt4_n FOR VALUES START (500) END (600);
+INSERT INTO prt4_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt4_n;
+ANALYZE prt4_n_p1;
+ANALYZE prt4_n_p2;
+ANALYZE prt4_n_p3;
+-- partition-wise join can not be applied if the partition ranges differ
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2 WHERE t1.a = t2.a;
+ QUERY PLAN
+----------------------------------------------
+ Hash Join
+ Hash Cond: (t1.a = t2.a)
+ -> Append
+ -> Seq Scan on prt1 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on prt4_n t2
+ -> Seq Scan on prt4_n_p1 t2_1
+ -> Seq Scan on prt4_n_p2 t2_2
+ -> Seq Scan on prt4_n_p3 t2_3
+(13 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 FULL JOIN prt4_n t2 ON t1.a = t2.a;
+ QUERY PLAN
+----------------------------------------------
+ Hash Full Join
+ Hash Cond: (t1.a = t2.a)
+ -> Append
+ -> Seq Scan on prt1 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_3
+ -> Hash
+ -> Append
+ -> Seq Scan on prt4_n t2
+ -> Seq Scan on prt4_n_p1 t2_1
+ -> Seq Scan on prt4_n_p2 t2_2
+ -> Seq Scan on prt4_n_p3 t2_3
+(13 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
+---------------------------------------------------------
+ Nested Loop Left Join
+ -> Append
+ -> Seq Scan on prt1 t1
+ -> Seq Scan on prt1_p1 t1_1
+ -> Seq Scan on prt1_p3 t1_2
+ -> Seq Scan on prt1_p2 t1_3
+ -> Append
+ -> Seq Scan on prt2 t2
+ Filter: (t1.a < b)
+ -> Index Scan using iprt2_p1_b on prt2_p1 t2_1
+ Index Cond: (t1.a < b)
+ -> Index Scan using iprt2_p2_b on prt2_p2 t2_2
+ Index Cond: (t1.a < b)
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (t1.a < b)
+(15 rows)
+
+-- equi-join with join condition on partial keys does not qualify for
+-- partition-wise join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t1.a % 25 = 0;
+ QUERY PLAN
+----------------------------------------------
+ Hash Join
+ Hash Cond: (((t2.b + t2.a) / 2) = t1.a)
+ -> Append
+ -> Seq Scan on prt2_m t2
+ -> Seq Scan on prt2_m_p1 t2_1
+ -> Seq Scan on prt2_m_p2 t2_2
+ -> Seq Scan on prt2_m_p3 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_m t1
+ Filter: ((a % 25) = 0)
+ -> Seq Scan on prt1_m_p1 t1_1
+ Filter: ((a % 25) = 0)
+ -> Seq Scan on prt1_m_p2 t1_2
+ Filter: ((a % 25) = 0)
+ -> Seq Scan on prt1_m_p3 t1_3
+ Filter: ((a % 25) = 0)
+(17 rows)
+
+-- equi-join between out-of-order partition key columns does not qualify for
+-- partition-wise join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = t2.b WHERE t1.a % 25 = 0;
+ QUERY PLAN
+----------------------------------------------
+ Hash Right Join
+ Hash Cond: (t2.b = t1.a)
+ -> Append
+ -> Seq Scan on prt2_m t2
+ -> Seq Scan on prt2_m_p1 t2_1
+ -> Seq Scan on prt2_m_p2 t2_2
+ -> Seq Scan on prt2_m_p3 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_m t1
+ Filter: ((a % 25) = 0)
+ -> Seq Scan on prt1_m_p1 t1_1
+ Filter: ((a % 25) = 0)
+ -> Seq Scan on prt1_m_p2 t1_2
+ Filter: ((a % 25) = 0)
+ -> Seq Scan on prt1_m_p3 t1_3
+ Filter: ((a % 25) = 0)
+(17 rows)
+
+-- equi-join between non-key columns does not qualify for partition-wise join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.c = t2.c WHERE t1.a % 25 = 0;
+ QUERY PLAN
+----------------------------------------------
+ Hash Right Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ -> Append
+ -> Seq Scan on prt2_m t2
+ -> Seq Scan on prt2_m_p1 t2_1
+ -> Seq Scan on prt2_m_p2 t2_2
+ -> Seq Scan on prt2_m_p3 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_m t1
+ Filter: ((a % 25) = 0)
+ -> Seq Scan on prt1_m_p1 t1_1
+ Filter: ((a % 25) = 0)
+ -> Seq Scan on prt1_m_p2 t1_2
+ Filter: ((a % 25) = 0)
+ -> Seq Scan on prt1_m_p3 t1_3
+ Filter: ((a % 25) = 0)
+(17 rows)
+
+-- partition-wise join can not be applied for a join between list and range
+-- partitioned table
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1, prt2_n t2 WHERE t1.c = t2.c;
+ QUERY PLAN
+----------------------------------------------
+ Hash Join
+ Hash Cond: (t2.c = (t1.c)::text)
+ -> Append
+ -> Seq Scan on prt2_n t2
+ -> Seq Scan on prt2_n_p1 t2_1
+ -> Seq Scan on prt2_n_p2 t2_2
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_n t1
+ -> Seq Scan on prt1_n_p1 t1_1
+ -> Seq Scan on prt1_n_p2 t1_2
+(11 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 LEFT JOIN prt2_n t2 ON (t1.c = t2.c);
+ QUERY PLAN
+----------------------------------------------
+ Hash Right Join
+ Hash Cond: (t2.c = (t1.c)::text)
+ -> Append
+ -> Seq Scan on prt2_n t2
+ -> Seq Scan on prt2_n_p1 t2_1
+ -> Seq Scan on prt2_n_p2 t2_2
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_n t1
+ -> Seq Scan on prt1_n_p1 t1_1
+ -> Seq Scan on prt1_n_p2 t1_2
+(11 rows)
+
+-- partition-wise join can not be applied between tables with different
+-- partition lists
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 RIGHT JOIN prt1 t2 ON (t1.c = t2.c);
+ QUERY PLAN
+----------------------------------------------
+ Hash Left Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ -> Append
+ -> Seq Scan on prt1 t2
+ -> Seq Scan on prt1_p1 t2_1
+ -> Seq Scan on prt1_p3 t2_2
+ -> Seq Scan on prt1_p2 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_n t1
+ -> Seq Scan on prt1_n_p1 t1_1
+ -> Seq Scan on prt1_n_p2 t1_2
+(12 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 FULL JOIN prt1 t2 ON (t1.c = t2.c);
+ QUERY PLAN
+----------------------------------------------
+ Hash Full Join
+ Hash Cond: ((t2.c)::text = (t1.c)::text)
+ -> Append
+ -> Seq Scan on prt1 t2
+ -> Seq Scan on prt1_p1 t2_1
+ -> Seq Scan on prt1_p3 t2_2
+ -> Seq Scan on prt1_p2 t2_3
+ -> Hash
+ -> Append
+ -> Seq Scan on prt1_n t1
+ -> Seq Scan on prt1_n_p1 t1_1
+ -> Seq Scan on prt1_n_p2 t1_2
+(12 rows)
+
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index f06cfa4..16e7f56 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -1,25 +1,26 @@
SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%';
- name | setting
-----------------------+---------
- enable_bitmapscan | on
- enable_hashagg | on
- enable_hashjoin | on
- enable_indexonlyscan | on
- enable_indexscan | on
- enable_material | on
- enable_mergejoin | on
- enable_nestloop | on
- enable_seqscan | on
- enable_sort | on
- enable_tidscan | on
-(11 rows)
+ name | setting
+----------------------------+---------
+ enable_bitmapscan | on
+ enable_hashagg | on
+ enable_hashjoin | on
+ enable_indexonlyscan | on
+ enable_indexscan | on
+ enable_material | on
+ enable_mergejoin | on
+ enable_nestloop | on
+ enable_partition_wise_join | on
+ enable_seqscan | on
+ enable_sort | on
+ enable_tidscan | on
+(12 rows)
CREATE TABLE foo2(fooid int, f2 int);
INSERT INTO foo2 VALUES(1, 11);
INSERT INTO foo2 VALUES(2, 22);
INSERT INTO foo2 VALUES(1, 111);
CREATE FUNCTION foot(int) returns setof foo2 as 'SELECT * FROM foo2 WHERE fooid = $1 ORDER BY f2;' LANGUAGE SQL;
-- function with ORDINALITY
select * from foot(1) with ordinality as z(a,b,ord);
a | b | ord
---+-----+-----
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 8641769..5ad149d 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -93,21 +93,21 @@ test: alter_generic alter_operator misc psql async dbsize misc_functions tsrf
# rules cannot run concurrently with any test that creates a view
test: rules psql_crosstab amutils
# run by itself so it can run parallel workers
test: select_parallel
# ----------
# Another group of parallel tests
# ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass partition_join
# ----------
# Another group of parallel tests
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
# event triggers cannot run concurrently with any test that runs DDL
test: event_trigger
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 835cf35..b4773b8 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -162,10 +162,11 @@ test: truncate
test: alter_table
test: sequence
test: polymorphism
test: rowtypes
test: returning
test: largeobject
test: with
test: xml
test: event_trigger
test: stats
+test: partition_join
diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql
new file mode 100644
index 0000000..45e8a64
--- /dev/null
+++ b/src/test/regress/sql/partition_join.sql
@@ -0,0 +1,788 @@
+--
+-- PARTITION_JOIN
+-- Test partition-wise join between partitioned tables
+--
+
+--
+-- partitioned by a single column
+--
+CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES START (0) END (250);
+CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES START (500) END (600);
+CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES START (250) END (500);
+INSERT INTO prt1 SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1;
+ANALYZE prt1_p1;
+ANALYZE prt1_p2;
+ANALYZE prt1_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1 AS SELECT * FROM prt1;
+
+CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES START (0) END (250);
+CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES START (250) END (500);
+CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES START (500) END (600);
+INSERT INTO prt2 SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+ANALYZE prt2;
+ANALYZE prt2_p1;
+ANALYZE prt2_p2;
+ANALYZE prt2_p3;
+CREATE TABLE uprt2 AS SELECT * FROM prt2;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1, uprt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, 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.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+
+-- Cases with non-nullable expressions in subquery results;
+-- make sure these go to null as expected
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+
+-- Join with pruned partitions from joining relations
+EXPLAIN (VERBOSE, COSTS OFF)
+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.a % 25 = 0 ORDER BY t1.a, t2.b;
+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.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1, uprt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+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.a % 25 = 0 ORDER BY t1.a, t2.b;
+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.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM uprt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM uprt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b ORDER BY t1.a, t2.b;
+
+-- joins where one of the relations is proven empty
+EXPLAIN (VERBOSE, 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;
+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;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 LEFT JOIN prt2 t2 ON t1.a = t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 LEFT JOIN prt2 t2 ON t1.a = t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a = 1 AND a = 2) t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, 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 % 25 = 0 ORDER BY t1.a, t2.b;
+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 % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a = 1 AND a = 2) t1 FULL JOIN uprt2 t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+-- lateral reference
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+
+--
+-- partitioned by expression
+--
+CREATE TABLE prt1_e (a int, b int, c varchar) PARTITION BY RANGE(((a + b)/2));
+CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES START (0) END (250);
+CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES START (250) END (500);
+CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES START (500) END (600);
+INSERT INTO prt1_e SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1_e;
+ANALYZE prt1_e_p1;
+ANALYZE prt1_e_p2;
+ANALYZE prt1_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1_e AS SELECT * FROM prt1_e;
+
+CREATE TABLE prt2_e (a int, b int, c varchar) PARTITION BY RANGE(((b + a)/2));
+CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES START (0) END (250);
+CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES START (250) END (500);
+CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES START (500) END (600);
+INSERT INTO prt2_e SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE prt2_e;
+ANALYZE prt2_e_p1;
+ANALYZE prt2_e_p2;
+ANALYZE prt2_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt2_e AS SELECT * FROM prt2_e;
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b;
+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.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1, uprt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 LEFT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 LEFT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1 LEFT JOIN uprt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 RIGHT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 RIGHT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1 RIGHT JOIN uprt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_e WHERE prt2_e.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_e WHERE prt2_e.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_e t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_e t2 WHERE t2.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b;
+
+--
+-- N-way join
+--
+EXPLAIN (VERBOSE, 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.a % 25 = 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.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM uprt1 t1, uprt2 t2, uprt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) LEFT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, 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 (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+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 (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) LEFT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM uprt1_e WHERE uprt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+-- Cases with non-nullable expressions in subquery results;
+-- make sure these go to null as expected
+EXPLAIN (VERBOSE, 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.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.a % 25 = 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;
+SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.a % 25 = 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;
+SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM uprt1_e WHERE uprt1_e.a % 25 = 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;
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1, uprt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+-- test merge joins with and without using indexes
+SET enable_hashjoin TO off;
+SET enable_nestloop TO off;
+
+CREATE INDEX iprt1_a on prt1(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 iprt2_b on prt2(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 iprt1_e_ab2 on prt1_e(((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));
+
+ANALYZE prt1;
+ANALYZE prt1_p1;
+ANALYZE prt1_p2;
+ANALYZE prt1_p3;
+ANALYZE prt2;
+ANALYZE prt2_p1;
+ANALYZE prt2_p2;
+ANALYZE prt2_p3;
+ANALYZE prt1_e;
+ANALYZE prt1_e_p1;
+ANALYZE prt1_e_p2;
+ANALYZE prt1_e_p3;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+SET enable_seqscan TO off;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+-- lateral references and parameterized paths
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+ ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+
+RESET enable_hashjoin;
+RESET enable_nestloop;
+RESET enable_seqscan;
+
+--
+-- partitioned by multiple columns
+--
+CREATE TABLE prt1_m (a int, b int, c varchar) PARTITION BY RANGE(a, ((a + b)/2));
+CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES START (0, 0) END (250, 250);
+CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES START (250, 250) END (500, 500);
+CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES START (500, 500) END (600, 600);
+INSERT INTO prt1_m SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1_m;
+ANALYZE prt1_m_p1;
+ANALYZE prt1_m_p2;
+ANALYZE prt1_m_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1_m AS SELECT * FROM prt1_m;
+
+CREATE TABLE prt2_m (a int, b int, c varchar) PARTITION BY RANGE(((b + a)/2), b);
+CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES START (0, 0) END (250, 250);
+CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES START (250, 250) END (500, 500);
+CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES START (500, 500) END (600, 600);
+INSERT INTO prt2_m SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE prt2_m;
+ANALYZE prt2_m_p1;
+ANALYZE prt2_m_p2;
+ANALYZE prt2_m_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt2_m AS SELECT * FROM prt2_m;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1, uprt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1 LEFT JOIN uprt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 RIGHT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 RIGHT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1 RIGHT JOIN uprt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.b % 25 = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.b % 25 = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_m t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_m t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+
+--
+-- multi-leveled partitions
+--
+CREATE TABLE prt1_l (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_l_p1 PARTITION OF prt1_l FOR VALUES START (0) END (250) PARTITION BY RANGE (b);
+CREATE TABLE prt1_l_p1_p1 PARTITION OF prt1_l_p1 FOR VALUES START (0) END (100);
+CREATE TABLE prt1_l_p1_p2 PARTITION OF prt1_l_p1 FOR VALUES START (100) END (250);
+CREATE TABLE prt1_l_p2 PARTITION OF prt1_l FOR VALUES START (250) END (500) PARTITION BY RANGE (c);
+CREATE TABLE prt1_l_p2_p1 PARTITION OF prt1_l_p2 FOR VALUES START ('0250') END ('0400');
+CREATE TABLE prt1_l_p2_p2 PARTITION OF prt1_l_p2 FOR VALUES START ('0400') END ('0500');
+CREATE TABLE prt1_l_p3 PARTITION OF prt1_l FOR VALUES START (500) END (600) PARTITION BY RANGE ((b + a));
+CREATE TABLE prt1_l_p3_p1 PARTITION OF prt1_l_p3 FOR VALUES START (1000) END (1100);
+CREATE TABLE prt1_l_p3_p2 PARTITION OF prt1_l_p3 FOR VALUES START (1100) END (1200);
+INSERT INTO prt1_l SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1_l;
+ANALYZE prt1_l_p1;
+ANALYZE prt1_l_p1_p1;
+ANALYZE prt1_l_p1_p2;
+ANALYZE prt1_l_p2;
+ANALYZE prt1_l_p2_p1;
+ANALYZE prt1_l_p2_p2;
+ANALYZE prt1_l_p3;
+ANALYZE prt1_l_p3_p1;
+ANALYZE prt1_l_p3_p2;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1_l AS SELECT * FROM prt1_l;
+
+CREATE TABLE prt2_l (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_l_p1 PARTITION OF prt2_l FOR VALUES START (0) END (250) PARTITION BY RANGE (a);
+CREATE TABLE prt2_l_p1_p1 PARTITION OF prt2_l_p1 FOR VALUES START (0) END (100);
+CREATE TABLE prt2_l_p1_p2 PARTITION OF prt2_l_p1 FOR VALUES START (100) END (250);
+CREATE TABLE prt2_l_p2 PARTITION OF prt2_l FOR VALUES START (250) END (500) PARTITION BY RANGE (c);
+CREATE TABLE prt2_l_p2_p1 PARTITION OF prt2_l_p2 FOR VALUES START ('0250') END ('0400');
+CREATE TABLE prt2_l_p2_p2 PARTITION OF prt2_l_p2 FOR VALUES START ('0400') END ('0500');
+CREATE TABLE prt2_l_p3 PARTITION OF prt2_l FOR VALUES START (500) END (600) PARTITION BY RANGE ((a + b));
+CREATE TABLE prt2_l_p3_p1 PARTITION OF prt2_l_p3 FOR VALUES START (1000) END (1100);
+CREATE TABLE prt2_l_p3_p2 PARTITION OF prt2_l_p3 FOR VALUES START (1100) END (1200);
+INSERT INTO prt2_l SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE prt2_l;
+ANALYZE prt2_l_p1;
+ANALYZE prt2_l_p1_p1;
+ANALYZE prt2_l_p1_p2;
+ANALYZE prt2_l_p2;
+ANALYZE prt2_l_p2_p1;
+ANALYZE prt2_l_p2_p2;
+ANALYZE prt2_l_p3;
+ANALYZE prt2_l_p3_p1;
+ANALYZE prt2_l_p3_p2;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt2_l AS SELECT * FROM prt2_l;
+
+EXPLAIN (VERBOSE, 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 = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 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 = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1, uprt2_l t2 WHERE t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, 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.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 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.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1 LEFT JOIN uprt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, 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.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 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.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1 RIGHT JOIN uprt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_l t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_l t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b;
+
+-- lateral reference
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1_l t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.b = t3.a AND t2.c = t3.c AND t2.b + t2.a = t3.a + t3.b)) ss
+ ON t1.a = ss.t2a AND t1.b = ss.t2a AND t1.c = ss.t2c AND t1.b + t1.a = ss.t2a + ss.t2b WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM prt1_l t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.b = t3.a AND t2.c = t3.c AND t2.b + t2.a = t3.a + t3.b)) ss
+ ON t1.a = ss.t2a AND t1.b = ss.t2a AND t1.c = ss.t2c AND t1.b + t1.a = ss.t2a + ss.t2b WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM uprt1_l t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1_l t2 JOIN uprt2_l t3 ON (t2.a = t3.b AND t2.b = t3.a AND t2.c = t3.c AND t2.b + t2.a = t3.a + t3.b)) ss
+ ON t1.a = ss.t2a AND t1.b = ss.t2a AND t1.c = ss.t2c AND t1.b + t1.a = ss.t2a + ss.t2b WHERE t1.a % 25 = 0 ORDER BY t1.a;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1_l t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.b = t3.a AND t2.c = t3.c AND t2.b + t2.a = t3.a + t3.b)) ss
+ ON t1.b = ss.t2a AND t1.b = ss.t2a AND t1.c = ss.t2c AND t1.b + t1.a = ss.t2a + ss.t2b WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM prt1_l t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.b = t3.a AND t2.c = t3.c AND t2.b + t2.a = t3.a + t3.b)) ss
+ ON t1.b = ss.t2a AND t1.b = ss.t2a AND t1.c = ss.t2c AND t1.b + t1.a = ss.t2a + ss.t2b WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM uprt1_l t1 LEFT JOIN LATERAL
+ (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1_l t2 JOIN uprt2_l t3 ON (t2.a = t3.b AND t2.b = t3.a AND t2.c = t3.c AND t2.b + t2.a = t3.a + t3.b)) ss
+ ON t1.b = ss.t2a AND t1.b = ss.t2a AND t1.c = ss.t2c AND t1.b + t1.a = ss.t2a + ss.t2b WHERE t1.a % 25 = 0 ORDER BY t1.a;
+
+--
+-- 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;
+ANALYZE plt1_p1;
+ANALYZE plt1_p2;
+ANALYZE plt1_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt1 AS SELECT * FROM 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;
+ANALYZE plt2_p1;
+ANALYZE plt2_p2;
+ANALYZE plt2_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt2 AS SELECT * FROM plt2;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1, uplt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 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 plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 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 uplt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
+
+-- Cases with non-nullable expressions in subquery results;
+-- make sure these go to null as expected
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+
+-- Join with pruned partitions from joining relations
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM uplt1 t1, uplt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 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;
+ANALYZE plt1_e;
+ANALYZE plt1_e_p1;
+ANALYZE plt1_e_p2;
+ANALYZE plt1_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt1_e AS SELECT * FROM plt1_e;
+
+CREATE TABLE plt2_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
+CREATE TABLE plt2_e_p1 PARTITION OF plt2_e FOR VALUES IN ('0000', '0003', '0004', '0010');
+CREATE TABLE plt2_e_p2 PARTITION OF plt2_e FOR VALUES IN ('0001', '0005', '0002', '0009');
+CREATE TABLE plt2_e_p3 PARTITION OF plt2_e FOR VALUES IN ('0006', '0007', '0008', '0011');
+INSERT INTO plt2_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE plt2_e;
+ANALYZE plt2_e_p1;
+ANALYZE plt2_e_p2;
+ANALYZE plt2_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt2_e AS SELECT * FROM plt2_e;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1, plt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1, plt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1, uplt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 LEFT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 LEFT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1 LEFT JOIN uplt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 RIGHT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 RIGHT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1 RIGHT JOIN uplt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2_e WHERE plt2_e.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2_e WHERE plt2_e.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uplt1_e t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2_e t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b;
+
+--
+-- N-way join
+--
+EXPLAIN (VERBOSE, 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;
+SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM uplt1 t1, uplt2 t2, uplt1_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;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN uplt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1, plt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1, plt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1, uplt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 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 ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+-- test merge join with and without index scan
+CREATE INDEX iplt1_c on plt1(c);
+CREATE INDEX iplt1_p1_c on plt1_p1(c);
+CREATE INDEX iplt1_p2_c on plt1_p2(c);
+CREATE INDEX iplt1_p3_c on plt1_p3(c);
+CREATE INDEX iplt2_c on plt2(c);
+CREATE INDEX iplt2_p1_c on plt2_p1(c);
+CREATE INDEX iplt2_p2_c on plt2_p2(c);
+CREATE INDEX iplt2_p3_c on plt2_p3(c);
+CREATE INDEX iplt1_e_c on plt1_e(ltrim(c, 'A'));
+CREATE INDEX iplt1_e_p1_c on plt1_e_p1(ltrim(c, 'A'));
+CREATE INDEX iplt1_e_p2_c on plt1_e_p2(ltrim(c, 'A'));
+CREATE INDEX iplt1_e_p3_c on plt1_e_p3(ltrim(c, 'A'));
+
+ANALYZE plt1;
+ANALYZE plt1_p1;
+ANALYZE plt1_p2;
+ANALYZE plt1_p3;
+ANALYZE plt2;
+ANALYZE plt2_p1;
+ANALYZE plt2_p2;
+ANALYZE plt2_p3;
+ANALYZE plt1_e;
+ANALYZE plt1_e_p1;
+ANALYZE plt1_e_p2;
+ANALYZE plt1_e_p3;
+
+SET enable_hashjoin TO off;
+SET enable_nestloop TO off;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 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 ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+SET enable_seqscan TO off;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 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 ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+RESET enable_hashjoin;
+RESET enable_nestloop;
+RESET enable_seqscan;
+
+--
+-- negative testcases
+--
+
+CREATE TABLE prt1_n (a int, b int, c varchar) PARTITION BY RANGE(c);
+CREATE TABLE prt1_n_p1 PARTITION OF prt1_n FOR VALUES START ('0000') END ('0250');
+CREATE TABLE prt1_n_p2 PARTITION OF prt1_n FOR VALUES START ('0250') END ('0500');
+INSERT INTO prt1_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 499, 2) i;
+ANALYZE prt1_n;
+ANALYZE prt1_n_p1;
+ANALYZE prt1_n_p2;
+
+CREATE TABLE prt2_n (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE prt2_n_p1 PARTITION OF prt2_n FOR VALUES IN ('0000', '0003', '0004', '0010', '0006', '0007');
+CREATE TABLE prt2_n_p2 PARTITION OF prt2_n FOR VALUES IN ('0001', '0005', '0002', '0009', '0008', '0011');
+INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt2_n;
+ANALYZE prt2_n_p1;
+ANALYZE prt2_n_p2;
+
+CREATE TABLE prt3_n (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE prt3_n_p1 PARTITION OF prt3_n FOR VALUES IN ('0000', '0004', '0006', '0007');
+CREATE TABLE prt3_n_p2 PARTITION OF prt3_n FOR VALUES IN ('0001', '0002', '0008', '0010');
+CREATE TABLE prt3_n_p3 PARTITION OF prt3_n FOR VALUES IN ('0003', '0005', '0009', '0011');
+INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt3_n;
+ANALYZE prt3_n_p1;
+ANALYZE prt3_n_p2;
+ANALYZE prt3_n_p3;
+
+CREATE TABLE prt4_n (a int, b int, c text) PARTITION BY RANGE(a);
+CREATE TABLE prt4_n_p1 PARTITION OF prt4_n FOR VALUES START (0) END (300);
+CREATE TABLE prt4_n_p2 PARTITION OF prt4_n FOR VALUES START (300) END (500);
+CREATE TABLE prt4_n_p3 PARTITION OF prt4_n FOR VALUES START (500) END (600);
+INSERT INTO prt4_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt4_n;
+ANALYZE prt4_n_p1;
+ANALYZE prt4_n_p2;
+ANALYZE prt4_n_p3;
+
+-- partition-wise join can not be applied if the partition ranges differ
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2 WHERE t1.a = t2.a;
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 FULL JOIN prt4_n t2 ON t1.a = t2.a;
+-- 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);
+-- equi-join with join condition on partial keys does not qualify for
+-- partition-wise join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t1.a % 25 = 0;
+-- equi-join between out-of-order partition key columns does not qualify for
+-- partition-wise join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = t2.b WHERE t1.a % 25 = 0;
+-- equi-join between non-key columns does not qualify for partition-wise join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.c = t2.c WHERE t1.a % 25 = 0;
+-- partition-wise join can not be applied for a join between list and range
+-- partitioned table
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1, prt2_n t2 WHERE t1.c = t2.c;
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 LEFT JOIN prt2_n t2 ON (t1.c = t2.c);
+-- partition-wise join can not be applied between tables with different
+-- partition lists
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 RIGHT JOIN prt1 t2 ON (t1.c = t2.c);
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 FULL JOIN prt1 t2 ON (t1.c = t2.c);