pg_dp_join.patch
text/x-patch
Filename: pg_dp_join.patch
Type: text/x-patch
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/catalog/partition.c | 17 | 77 |
| src/backend/nodes/outfuncs.c | 11 | 0 |
| src/backend/optimizer/path/allpaths.c | 112 | 25 |
| src/backend/optimizer/path/costsize.c | 1 | 0 |
| src/backend/optimizer/path/equivclass.c | 2 | 0 |
| src/backend/optimizer/path/joinpath.c | 24 | 1 |
| src/backend/optimizer/path/joinrels.c | 791 | 5 |
| src/backend/optimizer/path/pathkeys.c | 13 | 1 |
| src/backend/optimizer/plan/createplan.c | 23 | 15 |
| src/backend/optimizer/prep/prepunion.c | 59 | 0 |
| src/backend/optimizer/util/pathnode.c | 153 | 0 |
| src/backend/optimizer/util/relnode.c | 368 | 60 |
| src/backend/utils/adt/selfuncs.c | 3 | 1 |
| src/backend/utils/misc/guc.c | 9 | 0 |
| src/include/catalog/partition.h | 66 | 0 |
| src/include/nodes/nodes.h | 1 | 0 |
| src/include/nodes/relation.h | 48 | 0 |
| src/include/optimizer/cost.h | 1 | 0 |
| src/include/optimizer/pathnode.h | 10 | 0 |
| src/include/optimizer/paths.h | 3 | 0 |
| src/include/optimizer/prep.h | 3 | 0 |
| src/test/regress/expected/partition_join.out | 6838 | 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 | 746 | 0 |
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index f17ac29..8ce704a 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -43,80 +43,20 @@
#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"
#include "utils/syscache.h"
-/* Type and collation information for partition key columns */
-typedef struct KeyTypeCollInfo
-{
- Oid *typid;
- int32 *typmod;
- int16 *typlen;
- bool *typbyval;
- char *typalign;
- Oid *typcoll;
-} KeyTypeCollInfo;
-
-/*
- * Partition key information
- */
-typedef struct PartitionKeyData
-{
- char strategy; /* partition strategy */
- int16 partnatts; /* number of partition attributes */
- AttrNumber *partattrs; /* partition attnums */
- Oid *partopfamily; /* OIDs of operator families */
- Oid *partopcintype; /* OIDs of opclass declared input data types */
- FmgrInfo *partsupfunc; /* lookup info for support funcs */
- List *partexprs; /* partition key expressions, if any */
- char **partcolnames; /* partition key column names */
- KeyTypeCollInfo *tcinfo; /* type and collation info (all columns) */
-} PartitionKeyData;
-
-/* Internal representation of a list partition bound */
-typedef struct PartitionListInfo
-{
- int nvalues; /* number of values in the following array */
- Datum *values; /* values contained in the list */
- bool *nulls;
-} PartitionListInfo;
-
-/* Internal representation of a range partition bound */
-typedef struct RangeBound
-{
- Datum *val; /* composite bound value, if any */
- bool infinite; /* bound is +/- infinity */
- bool inclusive; /* bound is inclusive (vs exclusive) */
- bool lower; /* this is the lower (vs upper) bound */
-} RangeBound;
-
-typedef struct PartitionRangeInfo
-{
- RangeBound *lower;
- RangeBound *upper;
-} PartitionRangeInfo;
-
-/*
- * Information about a single partition
- */
-typedef struct PartitionInfoData
-{
- Oid oid; /* partition OID */
- PartitionListInfo *list; /* list partition info */
- PartitionRangeInfo *range; /* range partition info */
-} PartitionInfoData;
-
/*
* PartitionKeyExecInfo
*
* This struct holds the information needed to extract partition
* column values from a heap tuple.
*
* Key copy of the rd_partkey of rel
* ExpressionState exec state for expressions, or NIL if none
*/
typedef struct PartitionKeyExecInfo
@@ -226,37 +166,37 @@ static PartitionListInfo *make_list_from_spec(PartitionKey key,
static PartitionListInfo *copy_list_info(PartitionListInfo *src,
PartitionKey key);
static bool equal_list_info(PartitionKey key, PartitionListInfo *l1,
PartitionListInfo *l2);
static bool partition_list_values_equal(PartitionKey key,
Datum val1, Datum val2);
/* Range partition related support functions */
static PartitionRangeInfo *make_range_from_spec(PartitionKey key,
PartitionRangeSpec *range_spec);
-static RangeBound *make_range_bound(PartitionKey key, List *val, bool inclusive,
+static PartitionRangeBound *make_range_bound(PartitionKey key, List *val, bool inclusive,
bool lower);
static PartitionRangeInfo *copy_range_info(PartitionRangeInfo *src,
PartitionKey key);
-static RangeBound *copy_range_bound(RangeBound *src, PartitionKey key);
+static PartitionRangeBound *copy_range_bound(PartitionRangeBound *src, PartitionKey key);
static bool equal_range_info(PartitionKey key, PartitionRangeInfo *r1,
PartitionRangeInfo *r2);
static int32 partition_range_cmp(PartitionKey key, PartitionRangeInfo *r1,
PartitionRangeInfo *r2);
-static int32 partition_range_bound_cmp(PartitionKey key, RangeBound *b1,
- RangeBound *b2);
+static int32 partition_range_bound_cmp(PartitionKey key, PartitionRangeBound *b1,
+ PartitionRangeBound *b2);
static int32 partition_range_tuple_cmp(PartitionKey key,
Datum *val1, Datum *val2);
static bool partition_range_overlaps(PartitionKey key,
PartitionRangeInfo *r1, PartitionRangeInfo *r2);
-static bool tuple_rightof_bound(PartitionKey key, Datum *tuple, RangeBound *bound);
-static bool tuple_leftof_bound(PartitionKey key, Datum *tuple, RangeBound *bound);
+static bool tuple_rightof_bound(PartitionKey key, Datum *tuple, PartitionRangeBound *bound);
+static bool tuple_leftof_bound(PartitionKey key, Datum *tuple, PartitionRangeBound *bound);
static int range_partition_bsearch(PartitionKey key, PartitionDesc pdesc,
Datum *tuple);
/*
* Partition key related functions
*/
/*
* RelationBuildPartitionKey
* Build and attach to relcache partition key data of relation
@@ -1097,21 +1037,21 @@ list_overlaps_existing_partition(PartitionKey key,
}
/*
* Is a new partition's range empty?
*/
static bool
partition_range_empty(PartitionKey key, PartitionRangeSpec *range_spec)
{
PartitionRangeInfo *range;
- RangeBound *lower,
+ PartitionRangeBound *lower,
*upper;
range = make_range_from_spec(key, range_spec);
lower = range->lower;
upper = range->upper;
/*
* Range is not empty if one (and only one) of the bounds is infinity.
* Both cannot be infinite because of how the syntax is specified.
*/
@@ -2104,27 +2044,27 @@ make_range_from_spec(PartitionKey key, PartitionRangeSpec *range_spec)
range_spec->lowerinc,
true);
range->upper = make_range_bound(key,
range_spec->upper,
range_spec->upperinc,
false);
return range;
}
-static RangeBound *
+static PartitionRangeBound *
make_range_bound(PartitionKey key, List *val, bool inclusive, bool lower)
{
- RangeBound *bound;
+ PartitionRangeBound *bound;
ListCell *cell;
- bound = (RangeBound *) palloc0(sizeof(RangeBound));
+ bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
bound->infinite = (val == NIL);
bound->inclusive = inclusive;
bound->lower = lower;
if (val)
{
int i;
bound->val = (Datum *) palloc0(key->partnatts * sizeof(Datum));
@@ -2157,28 +2097,28 @@ copy_range_info(PartitionRangeInfo *src, PartitionKey key)
{
PartitionRangeInfo *result;
result = (PartitionRangeInfo *) palloc0(sizeof(PartitionRangeInfo));
result->lower = copy_range_bound(src->lower, key);
result->upper = copy_range_bound(src->upper, key);
return result;
}
-static RangeBound *
-copy_range_bound(RangeBound *src, PartitionKey key)
+static PartitionRangeBound *
+copy_range_bound(PartitionRangeBound *src, PartitionKey key)
{
int i;
int partnatts = get_partition_key_natts(key);
- RangeBound *result;
+ PartitionRangeBound *result;
- result = (RangeBound *) palloc0(sizeof(RangeBound));
+ result = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
result->infinite = src->infinite;
result->inclusive = src->inclusive;
result->lower = src->lower;
if (src->val)
{
result->val = (Datum *) palloc0(partnatts * sizeof(Datum));
for (i = 0; i < partnatts; i++)
result->val[i] = datumCopy(src->val[i],
key->tcinfo->typbyval[i],
@@ -2212,21 +2152,21 @@ partition_range_cmp(PartitionKey key, PartitionRangeInfo *r1,
cmp = partition_range_bound_cmp(key, r1->lower, r2->lower);
if (cmp == 0)
cmp = partition_range_bound_cmp(key, r1->upper, r2->upper);
return cmp;
}
/* Returns for two range partition bounds whether, b1 <=, =, >= b2 */
static int32
-partition_range_bound_cmp(PartitionKey key, RangeBound *b1, RangeBound *b2)
+partition_range_bound_cmp(PartitionKey key, PartitionRangeBound *b1, PartitionRangeBound *b2)
{
int32 result;
/*
* First, handle cases involving infinity, which don't require invoking
* the comparison proc.
*/
if (b1->infinite && b2->infinite)
{
/*
@@ -2337,31 +2277,31 @@ range_partition_bsearch(PartitionKey key, PartitionDesc pdesc,
}
low = idx + 1;
}
return -1;
}
/* Does range key lie to the right of partition bound */
static bool
-tuple_rightof_bound(PartitionKey key, Datum *tuple, RangeBound *bound)
+tuple_rightof_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;
}
/* Does range key lie to the left of partition bound */
static bool
-tuple_leftof_bound(PartitionKey key, Datum *tuple, RangeBound *bound)
+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;
}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 3b8fa6b..cd14840 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -204,20 +204,31 @@ _outBitmapset(StringInfo str, const Bitmapset *bms)
}
/* for use by extensions which define extensible nodes */
void
outBitmapset(StringInfo str, const Bitmapset *bms)
{
_outBitmapset(str, bms);
}
/*
+ * TODO: remove, used for debugging through gdb.
+ */
+char *
+bms_to_char(const Bitmapset *bms)
+{
+ StringInfo str = makeStringInfo();
+ outBitmapset(str, bms);
+ return str->data;
+}
+
+/*
* Print the value of a Datum given its type.
*/
void
outDatum(StringInfo str, Datum value, int typlen, bool typbyval)
{
Size length,
i;
char *s;
length = datumGetSize(value, typbyval, typlen);
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 04264b4..09781be 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -119,20 +119,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)
{
@@ -908,20 +912,40 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
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);
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
/*
+ * 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 +977,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))
@@ -1115,41 +1126,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 +1172,46 @@ 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;
+
+ /*
+ * 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 +2216,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
}
@@ -2849,20 +2879,77 @@ 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;
+ PartitionOptInfo *part_info = rel->part_info;
+
+ /* Handle only join relations. */
+ if (rel->reloptkind != RELOPT_JOINREL &&
+ rel->reloptkind != RELOPT_OTHER_JOINREL)
+ return;
+
+ /* If the relation is not partitioned, nothing to do. */
+ if (!part_info)
+ return;
+
+ for (cnt_part = 0; cnt_part < part_info->nparts; cnt_part++)
+ {
+ RelOptInfo *child_rel = part_info->part_rels[cnt_part];
+
+ /* Ignore dummy child. */
+ if (!IS_DUMMY_REL(child_rel))
+ {
+ /* Collect the paths from child joinrel. */
+ /* TODO: we need to reassess how do we do this, since the join
+ * between parents will require append paths for the child joinrel.
+ */
+ generate_partition_wise_join_paths(root, child_rel);
+
+ /* Find the cheapest of the paths for this rel. */
+ set_cheapest(child_rel);
+
+ /* Should we generate any gather paths? */
+#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..62a3f3a 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->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..8b34ab9 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)->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,
@@ -121,20 +127,22 @@ add_paths_to_joinrel(PlannerInfo *root,
* Decide whether it's sensible to generate parameterized paths for this
* joinrel, and if so, which relations such paths should require. There
* is usually no need to create a parameterized result path unless there
* is a join order restriction that prevents joining one of our input rels
* 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.
+ * TODO: do we need sjinfo created for children to added to the list in the
+ * planner info? OR how do we handle the code below?
*/
foreach(lc, root->join_info_list)
{
SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc);
/*
* 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
@@ -272,20 +280,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..6378d2e 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -7,38 +7,71 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/optimizer/path/joinrels.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.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 PartitionOptInfo *build_joinrel_part_info(RelOptInfo *rel1,
+ RelOptInfo *rel2, JoinType jointype,
+ List *restrictlist);
+static bool have_same_part_info(PartitionOptInfo *part_info1,
+ PartitionOptInfo *part_info2);
+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);
+static bool partkey_types_match(PartitionOptInfo *part_info1,
+ PartitionOptInfo *part_info2);
+static bool have_same_partition_lists(PartitionOptInfo *part_info1,
+ PartitionOptInfo *part_info2);
+static bool have_same_partition_bounds(PartitionOptInfo *part_info1,
+ PartitionOptInfo *part_info2);
+static bool compare_partition_bounds(PartitionOptInfo *part_info,
+ PartitionRangeBound *bound1, PartitionRangeBound *bound2);
+static bool match_joinrel_part_info(RelOptInfo *joinrel, RelOptInfo *rel1,
+ RelOptInfo *rel2, JoinType jointype,
+ List *restrictlist);
/*
* 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 +750,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 +918,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 +1294,744 @@ 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.
+ *
+ * TODO: naming convention in this function is screwd up, sometimes we use
+ * part_ to indicate a partition variable and sometimes we use parent_ to
+ * indicate parent variable. Need to organize it better.
+ */
+static void
+try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
+ RelOptInfo *joinrel, SpecialJoinInfo *parent_sjinfo,
+ List *parent_restrictlist)
+{
+ PartitionOptInfo *part_info1 = rel1->part_info;
+ PartitionOptInfo *part_info2 = rel2->part_info;
+ int nparts;
+ int cnt_parts;
+ StringInfo rel1_desc;
+ StringInfo rel2_desc;
+ PartitionOptInfo *part_info;
+
+ if (!joinrel->part_info)
+ {
+ /* Can this join be executed using partition-wise join? */
+ joinrel->part_info = build_joinrel_part_info(rel1, rel2,
+ parent_sjinfo->jointype,
+ parent_restrictlist);
+
+ /* If partition-wise join is not possible, nothing to do. */
+ if (!joinrel->part_info)
+ return;
+ }
+ else
+ {
+ /*
+ * Generally, we should check whether the partition scheme for the join
+ * is same as the partitioning scheme of both the joining relations. If
+ * not, we should create a new PartitionOptInfo for new partitioning
+ * scheme. But we allow partition-wise join only in those cases, when
+ * the partitioning scheme of resultant relation is same irrespective
+ * of the order in which the relations are joined. Hence make sure that
+ * the already computed partition scheme for join matches that required
+ * by the current pair of joining relations.
+ */
+ Assert(match_joinrel_part_info(joinrel, rel1, rel2,
+ parent_sjinfo->jointype,
+ parent_restrictlist));
+ }
+
+ part_info = joinrel->part_info;
+ nparts = part_info->nparts;
+ 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);
+
+ /*
+ * 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 *part_joinrel;
+ RelOptInfo *part_rel1 = part_info1->part_rels[cnt_parts];
+ RelOptInfo *part_rel2 = part_info2->part_rels[cnt_parts];
+ SpecialJoinInfo *sjinfo;
+ List *restrictlist;
+ List *join_aris;
+ List *ari1;
+ List *ari2;
+ Relids joinrelids;
+
+ /* We should never try to join two overlapping sets of rels. */
+ Assert(!bms_overlap(part_rel1->relids, part_rel2->relids));
+
+ /* Construct Relids set that identifies the joinrel. */
+ joinrelids = bms_union(part_rel1->relids, part_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, part_rel1->relids);
+ ari2 = find_appendrelinfos_by_relids(root, part_rel2->relids);
+
+ /*
+ * Construct SpecialJoinInfo from parent join relations's
+ * SpecialJoinInfo.
+ */
+ 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.
+ */
+ restrictlist = (List *) adjust_partitionrel_attrs(root,
+ (Node *)parent_restrictlist,
+ join_aris);
+
+ part_joinrel = part_info->part_rels[cnt_parts];
+
+ /* Construct the join relation for given partition of the join. */
+ if (!part_joinrel)
+ {
+ part_joinrel = make_joinrel(root, RELOPT_OTHER_JOINREL,
+ joinrelids);
+
+ /*
+ * Fill partition join relation by translating information from the
+ * parent join relation.
+ */
+ fill_partition_join_rel(root, part_rel1, part_rel2, part_joinrel,
+ joinrel, sjinfo, restrictlist, join_aris);
+
+ part_info->part_rels[cnt_parts] = part_joinrel;
+
+ add_joinrel_to_list(root, part_joinrel);
+ }
+
+ /* TODO: set_append_rel_size() calls add_child_rel_equivalences() for
+ * adding child equivalence classes one parent at a time. So, we do not
+ * add equivalence classes with multiple child relations. Do we need to
+ * do that? Possibily because of the parent node translation above we
+ * don't need it. Anyway, is there a way to verify it?
+ */
+ Assert(bms_equal(part_joinrel->relids, joinrelids));
+
+ bms_free(joinrelids);
+
+ /*
+ * If we've already proven that this join is empty, we needn't consider
+ * any more paths for it.
+ */
+ if (is_dummy_rel(part_joinrel))
+ continue;
+
+ populate_joinrel_with_paths(root, part_rel1, part_rel2, part_joinrel,
+ sjinfo, restrictlist);
+
+ /*
+ * If the partition themselves are partitioned, try partition-wise join
+ * recursively.
+ * TODO: we need to decide the correct place for this function to be
+ * called. The recursive nature of this call probably requires
+ * AppendPath to be added before we try to populate the other kinds of
+ * join paths for parent relations. So, we may have to call this
+ * function before populate_joinrel_with_paths().
+ */
+ try_partition_wise_join(root, part_rel1, part_rel2, part_joinrel, sjinfo,
+ 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;
+}
+
+/*
+ * have_same_partition_lists
+ *
+ * For given list partitioned relations, return true if lists for all the
+ * partitions of both the relations match. Return false otherwise.
+ */
+static bool
+have_same_partition_lists(PartitionOptInfo *part_info1,
+ PartitionOptInfo *part_info2)
+{
+ int cnt_parts;
+
+ Assert(part_info1->strategy == PARTITION_STRAT_LIST &&
+ part_info2->strategy == PARTITION_STRAT_LIST);
+
+ Assert(part_info1->nparts == part_info2->nparts);
+
+ /* List partition has only one partition key. */
+ Assert(part_info1->partnatts == 1 && part_info2->partnatts == 1);
+
+ for (cnt_parts = 0; cnt_parts < part_info1->nparts; cnt_parts++)
+ {
+ PartitionListInfo *list1 = part_info1->lists[cnt_parts];
+ PartitionListInfo *list2 = part_info2->lists[cnt_parts];
+ int cnt;
+
+ /*
+ * If number of items in the lists do not match or the one of the lists
+ * contains NULL value and other doesn't, lists do not match.
+ *
+ * TODO: This condition can be relaxed to accomodate subset of items,
+ * but probably not in the first cut.
+ */
+ if (list1->nvalues != list2->nvalues)
+ return false;
+
+ /* Compare actual values in the list. */
+ for (cnt = 0; cnt < list1->nvalues; cnt++)
+ {
+ Datum cmpval;
+
+ if (list1->nulls[cnt] != list2->nulls[cnt])
+ return false;
+
+ /* Skip NULL value comparisons. */
+ if (list1->nulls[cnt])
+ continue;
+
+ cmpval = FunctionCall2Coll(&part_info1->partsupfunc[0],
+ part_info1->tcinfo->typcoll[0],
+ list1->values[cnt],
+ list2->values[cnt]);
+ if (DatumGetInt32(cmpval))
+ return false;
+ }
+ }
+
+ /* Ok, everything matches, return true. */
+ return true;
+}
+
+/*
+ * have_same_partition_bounds
+ *
+ * For given partitioned relations, return true if the bounds of all the
+ * partitions of the both the relations match. Return false otherwise.
+ */
+static bool
+have_same_partition_bounds(PartitionOptInfo *part_info1,
+ PartitionOptInfo *part_info2)
+{
+ int cnt_parts;
+ Assert(part_info1->strategy == PARTITION_STRAT_RANGE &&
+ part_info2->strategy == PARTITION_STRAT_RANGE);
+
+ Assert(part_info1->nparts == part_info2->nparts);
+
+ for (cnt_parts = 0; cnt_parts < part_info1->nparts; cnt_parts++)
+ {
+ PartitionRangeBound *lower1 = part_info1->ranges[cnt_parts]->lower;
+ PartitionRangeBound *lower2 = part_info2->ranges[cnt_parts]->lower;
+ PartitionRangeBound *upper1 = part_info1->ranges[cnt_parts]->upper;
+ PartitionRangeBound *upper2 = part_info2->ranges[cnt_parts]->upper;
+
+ if (!compare_partition_bounds(part_info1, lower1, lower2) ||
+ !compare_partition_bounds(part_info1, upper1, upper2))
+ return false;
+ }
+
+ /* Ok, everything matches. */
+ return true;
+}
+
+/*
+ * compare_partition_bounds
+ *
+ * Return true if given bound specifications match.
+ */
+static bool
+compare_partition_bounds(PartitionOptInfo *part_info,
+ PartitionRangeBound *bound1,
+ PartitionRangeBound *bound2)
+{
+ int cnt_pks;
+ Datum *val1 = bound1->val;
+ Datum *val2 = bound2->val;
+
+ if (bound1->infinite != bound2->infinite ||
+ bound1->inclusive != bound2->inclusive ||
+ bound1->lower != bound2->lower)
+ return false;
+
+ /*
+ * TODO:
+ * Following part is common with compare_range_keys(), but that takes
+ * PartitionKey as argument, which we don't have here.
+ */
+ for (cnt_pks = 0; cnt_pks < part_info->partnatts; cnt_pks++)
+ {
+ Datum cmpval;
+
+ cmpval = FunctionCall2Coll(&part_info->partsupfunc[cnt_pks],
+ part_info->tcinfo->typcoll[cnt_pks],
+ val1[cnt_pks], val2[cnt_pks]);
+
+ if (DatumGetInt32(cmpval))
+ return false;
+ }
+
+ /* Ok, everything matches. */
+ return true;
+}
+
+/*
+ * partkey_types_match
+ *
+ * Returns true if the partition key datatype and collation match for given
+ * partitioned relations. Returns false otherwise.
+ */
+static bool
+partkey_types_match(PartitionOptInfo *part_info1, PartitionOptInfo *part_info2)
+{
+ int cnt_pks;
+ Oid *partopfamily1 = part_info1->partopfamily;
+ Oid *partopfamily2 = part_info2->partopfamily;
+ Oid *partopcintype1 = part_info1->partopcintype;
+ Oid *partopcintype2 = part_info2->partopcintype;
+ FmgrInfo *partsupfunc1 = part_info1->partsupfunc;
+ FmgrInfo *partsupfunc2 = part_info2->partsupfunc;
+ KeyTypeCollInfo *tcinfo1 = part_info1->tcinfo;
+ KeyTypeCollInfo *tcinfo2 = part_info2->tcinfo;
+
+ if (part_info1->partnatts != part_info1->partnatts)
+ return false;
+
+ for (cnt_pks = 0; cnt_pks < part_info1->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 (partopfamily1[cnt_pks] != partopfamily2[cnt_pks] ||
+ partopcintype1[cnt_pks] != partopcintype2[cnt_pks] ||
+ partsupfunc1[cnt_pks].fn_oid != partsupfunc2[cnt_pks].fn_oid ||
+ tcinfo1->typid[cnt_pks] != tcinfo2->typid[cnt_pks] ||
+ tcinfo1->typmod[cnt_pks] != tcinfo2->typmod[cnt_pks] ||
+ tcinfo1->typcoll[cnt_pks] != tcinfo2->typcoll[cnt_pks])
+ return false;
+ }
+
+ /* Everything matches. */
+ return true;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * Check whether the given two partitioning schemes are same.
+ */
+static bool
+have_same_part_info(PartitionOptInfo *part_info1, PartitionOptInfo *part_info2)
+{
+ int cnt;
+
+ /*
+ * If number of partitions, number of partition keys and partitioning
+ * strategy of the joining relations do not match, we can not apply
+ * partition-wise join.
+ *
+ * TODO: it should be possible to push an inner join down even if the number of
+ * partitions differ but the common partitions match. In such a case pushing
+ * down outer joins would be tricky, but still doable using empty relation
+ * for non-existing partition.
+ */
+ if (!part_info1 || !part_info2 ||
+ part_info1->nparts != part_info2->nparts ||
+ part_info1->strategy != part_info2->strategy ||
+ part_info1->partnatts != part_info2->partnatts)
+ return false;
+
+ /*
+ * Partition-wise join between multi-level partitioned tables is not
+ * supported, since the partition hierarchy doesn't get translated into the
+ * corresponding RelOptInfo hierarchy. Since the partition hierarchy is
+ * flattened, if we add append paths to intermediate partition relations,
+ * the corresponding rows will be added multiple times.
+ */
+ for (cnt = 0; cnt < part_info1->nparts; cnt++)
+ {
+ RelOptInfo *part_rel1 = part_info1->part_rels[cnt];
+ RelOptInfo *part_rel2 = part_info2->part_rels[cnt];
+ if (!part_rel1 || !part_rel2 || part_rel1->part_info ||
+ part_rel2->part_info)
+ return false;
+ }
+
+ /*
+ * Partition-wise join can not be applied if datatypes and collations of
+ * the partition keys do not match.
+ */
+ if (!partkey_types_match(part_info1, part_info2))
+ return false;
+
+ /*
+ * Partition-wise join can not be applied if the partition bounds or lists
+ * of joining relations do not match.
+ */
+ switch (part_info1->strategy)
+ {
+ case PARTITION_STRAT_LIST:
+ if (!have_same_partition_lists(part_info1, part_info2))
+ return false;
+ break;
+
+ case PARTITION_STRAT_RANGE:
+ if (!have_same_partition_bounds(part_info1, part_info2))
+ return false;
+ break;
+
+ default:
+ /* Unknown partition strategy. */
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * build_joinrel_part_info
+ *
+ * Given partition description of two joining relations, construct partition
+ * description for join between those relations. If we can execute
+ * partition-wise join the resultant join is partitioned in the same way as the
+ * joining relations. Otherwise, it is not partitioned and in such case the
+ * function returns NULL.
+ *
+ * TODO find the right place for this function.
+ *
+ * TODO:
+ * This function and
+ * build_partition_info() should be named consistently e.g.
+ * build_joinrel_partition_info and build_simplerel_partition_info or
+ * build_baserel_partition_info resp.
+ *
+ * TODO: this code can be simplified a lot if we precompute the partitioning
+ * compatibility between relations. See Robert's idea in 9th August mail on
+ * thread "Is any ordering of N-way join always partition-wise joinable?"
+ */
+static PartitionOptInfo *
+build_joinrel_part_info(RelOptInfo *rel1, RelOptInfo *rel2,
+ JoinType jointype, List *restrictlist)
+{
+ PartitionOptInfo *part_info;
+ PartitionOptInfo *part_info1 = rel1->part_info;
+ PartitionOptInfo *part_info2 = rel2->part_info;
+ int cnt;
+ int num_pks;
+
+ /* Do nothing, if user doesn't want to try partition-wise join. */
+ if (!enable_partition_wise_join)
+ return NULL;
+
+ if (!have_same_part_info(part_info1, part_info2))
+ return NULL;
+
+ /*
+ * Partition-wise join can not be applied if there is no equi-join
+ * condition between partition keys.
+ */
+ if (!have_partkey_equi_join(rel1, rel2, jointype, restrictlist))
+ return NULL;
+
+ /* The join is partitioned the similar to the joining relations. */
+ part_info = makeNode(PartitionOptInfo);
+
+ /* Information related to the partititions. */
+ part_info->nparts = part_info1->nparts;
+ /* Allocate space for partition RelOptInfos, which will be filled later. */
+ part_info->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *
+ part_info->nparts);
+ part_info->lists = part_info1->lists;
+ part_info->ranges = part_info1->ranges;
+
+ /* Information related to partition keys. */
+ part_info->strategy = part_info1->strategy;
+ part_info->partnatts = part_info1->partnatts;
+ part_info->partopfamily = part_info1->partopfamily;
+ part_info->partopcintype = part_info1->partopcintype;
+ part_info->partsupfunc = part_info1->partsupfunc;
+ part_info->tcinfo = part_info1->tcinfo;
+ num_pks = part_info->partnatts;
+ part_info->partexprs = (List **) palloc0(sizeof(List *) * num_pks);
+ for (cnt = 0; cnt < num_pks; cnt++)
+ {
+ List *pkexpr = list_copy(part_info1->partexprs[cnt]);
+
+ pkexpr = list_concat(pkexpr,
+ list_copy(part_info2->partexprs[cnt]));
+ part_info->partexprs[cnt] = pkexpr;
+ }
+
+ return part_info;
+}
+
+/*
+ * 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)
+{
+ PartitionOptInfo *part_info1 = rel1->part_info;
+ PartitionOptInfo *part_info2 = rel2->part_info;
+ ListCell *lc;
+ int cnt_pks;
+ int num_pks;
+ bool *pk_has_clause;
+
+ Assert(part_info1->partnatts == part_info2->partnatts);
+ num_pks = part_info1->partnatts;
+ 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)
+{
+ PartitionOptInfo *part_info = rel->part_info;
+ int cnt_pks;
+
+ /*
+ * 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 < part_info->partnatts; cnt_pks++)
+ {
+ List *pkexprs = part_info->partexprs[cnt_pks];
+ ListCell *lc;
+
+ foreach(lc, pkexprs)
+ {
+ Expr *pkexpr = lfirst(lc);
+ if (equal(pkexpr, expr))
+ return cnt_pks;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Checks
+ * 1. if the partitioning scheme of the join relation match that of the joining
+ * relations.
+ * 2. if there exists equi-join condition between the partition keys of the
+ * joining relations.
+ * 3. if the partition keys of the join contain the partition keys of both the
+ * relations.
+ */
+static bool
+match_joinrel_part_info(RelOptInfo *joinrel, RelOptInfo *rel1,
+ RelOptInfo *rel2, JoinType jointype,
+ List *restrictlist)
+{
+ int cnt;
+ int num_pks;
+ PartitionOptInfo *join_part_info = joinrel->part_info;
+ PartitionOptInfo *part_info1 = rel1->part_info;
+ PartitionOptInfo *part_info2= rel1->part_info;
+
+ Assert(enable_partition_wise_join);
+
+ /*
+ * If partitioning scheme of the join doesn't match that of the joining
+ * relations, join relations is not partitioned in the same way as one or
+ * both of the joining relations.
+ */
+ if (!have_same_part_info(join_part_info, part_info1) ||
+ !have_same_part_info(join_part_info, part_info2))
+ return false;
+
+ /*
+ * 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, jointype, restrictlist))
+ return false;
+
+ /*
+ * If the partition keys from either of the joining relations are not part
+ * of the partition keys of the join relation, again we can not use the
+ * given partition scheme for joining the given relations partition-wise.
+ */
+ Assert(join_part_info->partnatts == part_info1->partnatts);
+ Assert(join_part_info->partnatts == part_info2->partnatts);
+
+ num_pks = join_part_info->partnatts;
+ for (cnt = 0; cnt < num_pks; cnt++)
+ {
+ List *pkexpr = join_part_info->partexprs[cnt];
+ List *pkexpr1 = part_info1->partexprs[cnt];
+ List *pkexpr2 = part_info2->partexprs[cnt];
+ List *diff_list;
+
+ diff_list = list_difference(pkexpr1, pkexpr);
+ if (diff_list)
+ {
+ list_free(diff_list);
+ return false;
+ }
+
+ diff_list = list_difference(pkexpr2, pkexpr);
+ if (diff_list)
+ {
+ list_free(diff_list);
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 4436ac1..dba6772 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->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 592214b..7e3e3b8 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1873,20 +1873,79 @@ 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.
+ *
+ * TODO:
+ * Note: This function is expected to be called only in case of partitioned
+ * tables, where the child table has the same schema as the parent table. This
+ * allows us to just restamp the Var nodes with child's relid.
+ *
+ * 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: for partitioned tables, since the partitions have same structure
+ * as that of their parents, it should suffice to just restamp the Var node
+ * rather than copying, but both require a new tree being allocated.
+ * 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..4e04133 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 */
@@ -1889,20 +1891,21 @@ calc_nestloop_required_outer(Path *outer_path, Path *inner_path)
/* inner_path can require rels from outer path, but not vice versa */
Assert(!bms_overlap(outer_paramrels, inner_path->parent->relids));
/* easy case if inner path is not parameterized */
if (!inner_paramrels)
return bms_copy(outer_paramrels);
/* else, form the union ... */
required_outer = bms_union(outer_paramrels, inner_paramrels);
/* ... and remove any mention of now-satisfied outer rels */
required_outer = bms_del_members(required_outer,
outer_path->parent->relids);
+
/* maintain invariant that required_outer is exactly NULL if empty */
if (bms_is_empty(required_outer))
{
bms_free(required_outer);
required_outer = NULL;
}
return required_outer;
}
/*
@@ -3202,10 +3205,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->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/relnode.c b/src/backend/optimizer/util/relnode.c
index deef560..cea06c7 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,29 +8,34 @@
*
*
* IDENTIFICATION
* src/backend/optimizer/util/relnode.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "miscadmin.h"
+#include "catalog/heap.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 +45,24 @@ 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 PartitionOptInfo *build_partition_info(PlannerInfo *root,
+ RelOptInfo *rel);
+static void set_foreign_rel_properties(RelOptInfo *joinrel,
+ RelOptInfo *outer_rel, RelOptInfo *inner_rel);
/*
* setup_simple_rel_arrays
* Prepare the arrays we use for quickly accessing base relations.
*/
void
setup_simple_rel_arrays(PlannerInfo *root)
{
Index rti;
@@ -165,27 +174,33 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
break;
default:
elog(ERROR, "unrecognized RTE kind: %d",
(int) rte->rtekind);
break;
}
/* Save the finished struct in the query's simple_rel_array */
root->simple_rel_array[relid] = rel;
+ /* Get the partitioning information, if any. */
+ if (rte->rtekind == RTE_RELATION)
+ rel->part_info = build_partition_info(root, rel);
+ else
+ rel->part_info = NULL;
+
/*
* If this rel is an appendrel parent, recurse to build "other rel"
* RelOptInfos for its children. They are "other rels" because they are
* not in the main join tree, but we will need RelOptInfos to plan access
* to them.
*/
- if (rte->inh)
+ if (!rel->part_info && rte->inh)
{
ListCell *l;
foreach(l, root->append_rel_list)
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
/* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != relid)
continue;
@@ -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,25 @@ 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_info = NULL;
+ joinrel->parent_relids = 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);
@@ -510,57 +543,146 @@ 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_joinrel_to_list(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;
}
+RelOptInfo *
+make_joinrel(PlannerInfo *root, RelOptKind reloptkind, Relids joinrelids)
+{
+ RelOptInfo *joinrel = makeNode(RelOptInfo);
+ joinrel->reloptkind = RELOPT_OTHER_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;
+ 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_info = NULL;
+ joinrel->parent_relids = NULL;
+
+ return joinrel;
+}
+
+RelOptInfo *
+fill_partition_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
+ RelOptInfo *joinrel, RelOptInfo *parent_joinrel,
+ SpecialJoinInfo *sjinfo, List *restrictlist,
+ List *join_aris)
+{
+ List *tmp_exprs;
+
+ /* Only joins between other relations land here. */
+ Assert(IS_OTHER_REL(rel1->reloptkind) && IS_OTHER_REL(rel2->reloptkind));
+
+ joinrel->parent_relids = bms_copy(parent_joinrel->relids);
+
+ /* Computer information relevant to foreign relations. */
+ set_foreign_rel_properties(joinrel, rel1, rel2);
+
+ /*
+ * 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;
+
+ /*
+ * Set estimates of the joinrel's size.
+ */
+ set_joinrel_size_estimates(root, joinrel, rel1, rel2, sjinfo,
+ restrictlist);
+
+ /* Child joinrel is parallel safe if parent is parallel safe. */
+ joinrel->consider_parallel = parent_joinrel->consider_parallel;
+
+ 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,
@@ -1313,10 +1435,196 @@ 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;
}
+
+/*
+ * build_partition_info
+ *
+ * Retrieves partitioning information for given relation.
+ *
+ * The function also builds the RelOptInfos of the partitions recursively.
+ * TODO: complete the prologue.
+ */
+static PartitionOptInfo *
+build_partition_info(PlannerInfo *root, RelOptInfo *rel)
+{
+ Relation relation;
+ PartitionKey part_key;
+ PartitionDesc part_desc;
+ PartitionOptInfo *part_info;
+ RangeTblEntry *rte;
+ ListCell *lc;
+ int num_pkexprs;
+ int cnt_pke;
+ int nparts;
+ int cnt_parts;
+ Expr *pkexpr;
+
+ /* The given relation should be simple relation. */
+ Assert(rel->reloptkind == RELOPT_BASEREL ||
+ rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
+ Assert(rel->relid != 0);
+
+ rte = root->simple_rte_array[rel->relid];
+ Assert(rte);
+
+ /*
+ * If it's not parent of the inheritance hierarchy, it can not be
+ * partiioned relation.
+ */
+ if (!rte->inh)
+ return NULL;
+
+ /*
+ * 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 = heap_open(rte->relid, NoLock);
+ part_desc = RelationGetPartitionDesc(relation);
+ part_key = RelationGetPartitionKey(relation);
+
+ /* Nothing to do for an unpartitioned relation. */
+ if (!part_desc || !part_key)
+ {
+ heap_close(relation, NoLock);
+ return NULL;
+ }
+
+ part_info = makeNode(PartitionOptInfo);
+
+ /* Store partition descriptor information. */
+ nparts = part_info->nparts = part_desc->nparts;
+ /* TODO: Should we copy the contents of the these arrays? */
+ part_info->lists = (PartitionListInfo **) palloc(sizeof(PartitionListInfo *) * nparts);
+ part_info->ranges = (PartitionRangeInfo **) palloc(sizeof(PartitionRangeInfo *) * nparts);
+
+ for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+ {
+ part_info->lists[cnt_parts] = part_desc->parts[cnt_parts]->list;
+ part_info->ranges[cnt_parts] = part_desc->parts[cnt_parts]->range;
+ }
+
+ /* Store partition key information. */
+ part_info->strategy = part_key->strategy;
+ part_info->partnatts = part_key->partnatts;
+ num_pkexprs = part_info->partnatts;
+ /* TODO: Should we copy the contents of these arrays? */
+ part_info->partopfamily = part_key->partopfamily;
+ part_info->partopcintype = part_key->partopcintype;
+ part_info->partsupfunc = part_key->partsupfunc;
+ part_info->tcinfo = part_key->tcinfo;
+
+ /* Store partition keys as single elements lists. */
+ part_info->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];
+
+ 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(rel->relid, 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, rel->relid, 0);
+ lc = lnext(lc);
+ }
+
+ part_info->partexprs[cnt_pke] = list_make1(pkexpr);
+ }
+
+ /* Find RelOptInfo of the partitions. */
+ part_info->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *
+ nparts);
+ foreach(lc, root->append_rel_list)
+ {
+ AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
+ int childRTindex = appinfo->child_relid;
+ RangeTblEntry *childRTE = root->simple_rte_array[childRTindex];
+ RelOptInfo *childrel = root->simple_rel_array[childRTindex];
+
+ /* append_rel_list contains all append rels; ignore others */
+ if (appinfo->parent_relid != rel->relid)
+ continue;
+
+ /* If we haven't created a RelOptInfo aleady, create one. */
+ if (!childrel)
+ childrel = build_simple_rel(root, childRTindex,
+ RELOPT_OTHER_MEMBER_REL);
+
+ /* Save the parent relids for parameterized path handling. */
+ childrel->parent_relids = bms_copy(rel->relids);
+
+ /*
+ * OIDs of the partitions are arranged to match the partition bounds or
+ * list in corresponding arrays. Arrange RelOptInfo's of partitions in
+ * the same fashion.
+ */
+ for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+ {
+ if (part_desc->parts[cnt_parts]->oid == childRTE->relid)
+ {
+ /* Every partition can be seen only once. */
+ Assert(!part_info->part_rels[cnt_parts]);
+ part_info->part_rels[cnt_parts] = childrel;
+
+ break;
+ }
+ }
+ }
+
+ heap_close(relation, NoLock);
+
+ /* We must have found RelOptInfos of all the partitions. */
+ for (cnt_parts = 0; cnt_parts < part_info->nparts; cnt_parts++)
+ Assert(part_info->part_rels[cnt_parts]);
+
+ return part_info;
+}
+
+/*
+ * Adds given join relation to the joinrel list and also to the hashtable if
+ * there is one.
+ */
+void
+add_joinrel_to_list(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;
+ }
+}
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 c5178f7..3412eae 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 14fd29e..8b928a3 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -12,22 +12,88 @@
*/
#ifndef PARTITION_H
#define PARTITION_H
#include "fmgr.h"
#include "executor/tuptable.h"
#include "nodes/execnodes.h"
#include "parser/parse_node.h"
#include "utils/relcache.h"
+/* Type and collation information for partition key columns */
+typedef struct KeyTypeCollInfo
+{
+ Oid *typid;
+ int32 *typmod;
+ int16 *typlen;
+ bool *typbyval;
+ char *typalign;
+ Oid *typcoll;
+} KeyTypeCollInfo;
+
+/*
+ * Partition key information
+ */
+typedef struct PartitionKeyData
+{
+ char strategy; /* partition strategy */
+ int16 partnatts; /* number of partition attributes */
+ AttrNumber *partattrs; /* partition attnums */
+ Oid *partopfamily; /* OIDs of operator families */
+ Oid *partopcintype; /* OIDs of opclass declared input data types */
+ FmgrInfo *partsupfunc; /* lookup info for support funcs */
+ List *partexprs; /* partition key expressions, if any */
+ char **partcolnames; /* partition key column names */
+ KeyTypeCollInfo *tcinfo; /* type and collation info (all columns) */
+} PartitionKeyData;
+
typedef struct PartitionKeyData *PartitionKey;
+/* Internal representation of a list partition bound */
+typedef struct PartitionListInfo
+{
+ int nvalues; /* number of values in the following array */
+ Datum *values; /* values contained in the list */
+ bool *nulls;
+} PartitionListInfo;
+
+/*
+ * TODO: the patch by Amit L named this structure as RangeBound, but that
+ * conflicts with a structure with the same name in rangetypes.h. Hence renamed
+ * it here. It should be changed to whatever Amit L uses in the next set of
+ * patches.
+ */
+/* Internal representation of a range partition bound */
+typedef struct PartitionRangeBound
+{
+ Datum *val; /* the bound value, if any */
+ bool infinite; /* bound is +/- infinity */
+ bool inclusive; /* bound is inclusive (vs exclusive) */
+ bool lower; /* this is the lower (vs upper) bound */
+} PartitionRangeBound;
+
+typedef struct PartitionRangeInfo
+{
+ PartitionRangeBound *lower;
+ PartitionRangeBound *upper;
+} PartitionRangeInfo;
+
+/*
+ * Information about a single partition
+ */
+typedef struct PartitionInfoData
+{
+ Oid oid; /* partition OID */
+ PartitionListInfo *list; /* list partition info */
+ PartitionRangeInfo *range; /* range partition info */
+} PartitionInfoData;
+
/*
* Information about partitions of a partitioned table.
*/
typedef struct PartitionInfoData *PartitionInfo;
typedef struct PartitionDescData
{
int nparts; /* Number of partitions */
PartitionInfo *parts; /* Array of PartitionInfoData pointers */
} PartitionDescData;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index bb62112..4bb5966 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -218,20 +218,21 @@ typedef enum NodeTag
T_DomainConstraintState,
/*
* TAGS FOR PLANNER NODES (relation.h)
*/
T_PlannerInfo = 500,
T_PlannerGlobal,
T_RelOptInfo,
T_IndexOptInfo,
T_ForeignKeyOptInfo,
+ T_PartitionOptInfo,
T_ParamPathInfo,
T_Path,
T_IndexPath,
T_BitmapHeapPath,
T_BitmapAndPath,
T_BitmapOrPath,
T_TidPath,
T_SubqueryScanPath,
T_ForeignPath,
T_CustomPath,
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 2709cc7..fae6c1a 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -12,20 +12,21 @@
*-------------------------------------------------------------------------
*/
#ifndef RELATION_H
#define RELATION_H
#include "access/sdir.h"
#include "lib/stringinfo.h"
#include "nodes/params.h"
#include "nodes/parsenodes.h"
#include "storage/block.h"
+#include "catalog/partition.h"
/*
* Relids
* Set of relation identifiers (indexes into the rangetable).
*/
typedef Bitmapset *Relids;
/*
* When looking for a "cheapest path", this enum specifies whether we want
@@ -345,20 +346,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 +471,31 @@ 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 PartitionOptInfo PartitionOptInfo;
+
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 +549,29 @@ 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. */
+ /*
+ * TODO: Notice recursive usage of RelOptInfo.
+ */
+ PartitionOptInfo *part_info;
+
+ /* Set only for "other" base or join relations. */
+ Relids 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
@@ -649,20 +672,45 @@ typedef struct ForeignKeyOptInfo
/* Derived info about whether FK's equality conditions match the query: */
int nmatched_ec; /* # of FK cols matched by ECs */
int nmatched_rcols; /* # of FK cols matched by non-EC rinfos */
int nmatched_ri; /* total # of non-EC rinfos matched to FK */
/* Pointer to eclass matching each column's condition, if there is one */
struct EquivalenceClass *eclass[INDEX_MAX_KEYS];
/* List of non-EC RestrictInfos matching each column's condition */
List *rinfos[INDEX_MAX_KEYS];
} ForeignKeyOptInfo;
+/*
+ * PartitionOptInfo
+ * Partitioning information for planning/optimization
+ *
+ * TODO: complete the comment
+ */
+typedef struct PartitionOptInfo
+{
+ NodeTag type;
+
+ /* Information about partitions */
+ int nparts; /* number of partitions */
+ RelOptInfo **part_rels; /* RelOptInfos of partitions */
+ PartitionListInfo **lists; /* list bounds */
+ PartitionRangeInfo **ranges; /* range lower bounds */
+
+ /* Information about partition keys */
+ char strategy; /* partition strategy */
+ int16 partnatts; /* number of partition attributes */
+ List **partexprs; /* partition key expressions. */
+ Oid *partopfamily; /* OIDs of operator families */
+ Oid *partopcintype; /* OIDs of opclass declared input data types */
+ FmgrInfo *partsupfunc; /* lookup info for support funcs */
+ KeyTypeCollInfo *tcinfo; /* type and collation info (all columns) */
+} PartitionOptInfo;
/*
* EquivalenceClasses
*
* Whenever we can determine that a mergejoinable equality clause A = B is
* not delayed by any outer join, we create an EquivalenceClass containing
* the expressions A and B to record this knowledge. If we later find another
* equivalence B = C, we add C to the existing EquivalenceClass; this may
* require merging two existing EquivalenceClasses. At the end of the qual
* distribution process, we have sets of values that are known all transitively
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..5a4b054 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,20 @@ 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 *make_joinrel(PlannerInfo *root, RelOptKind reloptkind,
+ Relids joinrelids);
+extern RelOptInfo *fill_partition_join_rel(PlannerInfo *root, RelOptInfo *rel1,
+ RelOptInfo *rel2, RelOptInfo *joinrel,
+ RelOptInfo *parent_joinrel,
+ SpecialJoinInfo *sjinfo,
+ List *restrictlist, List *join_aris);
+extern void add_joinrel_to_list(PlannerInfo *root, RelOptInfo *joinrel);
#endif /* PATHNODE_H */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 44abe83..152db2b 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -212,11 +212,14 @@ 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);
+/* TODO: need a better place to save this function signature. */
+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..307076e
--- /dev/null
+++ b/src/test/regress/expected/partition_join.out
@@ -0,0 +1,6838 @@
+--
+-- 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)
+
+-- 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;
+ QUERY PLAN
+----------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a
+ -> 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)))
+ -> Append
+ -> Seq Scan on public.prt2_l t2
+ Output: t2.b, t2.c, t2.a
+ -> Seq Scan on public.prt2_l_p1 t2_1
+ Output: t2_1.b, t2_1.c, t2_1.a
+ -> Seq Scan on public.prt2_l_p1_p1 t2_2
+ Output: t2_2.b, t2_2.c, t2_2.a
+ -> Seq Scan on public.prt2_l_p1_p2 t2_3
+ Output: t2_3.b, t2_3.c, t2_3.a
+ -> Seq Scan on public.prt2_l_p2 t2_4
+ Output: t2_4.b, t2_4.c, t2_4.a
+ -> Seq Scan on public.prt2_l_p2_p1 t2_5
+ Output: t2_5.b, t2_5.c, t2_5.a
+ -> Seq Scan on public.prt2_l_p2_p2 t2_6
+ Output: t2_6.b, t2_6.c, t2_6.a
+ -> Seq Scan on public.prt2_l_p3 t2_7
+ Output: t2_7.b, t2_7.c, t2_7.a
+ -> Seq Scan on public.prt2_l_p3_p1 t2_8
+ Output: t2_8.b, t2_8.c, t2_8.a
+ -> Seq Scan on public.prt2_l_p3_p2 t2_9
+ Output: t2_9.b, t2_9.c, t2_9.a
+ -> Hash
+ Output: t1.a, t1.c, t1.b
+ -> Append
+ -> Seq Scan on public.prt1_l t1
+ Output: t1.a, t1.c, t1.b
+ Filter: ((t1.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p1 t1_1
+ Output: t1_1.a, t1_1.c, t1_1.b
+ Filter: ((t1_1.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p1_p1 t1_2
+ Output: t1_2.a, t1_2.c, t1_2.b
+ Filter: ((t1_2.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p1_p2 t1_3
+ Output: t1_3.a, t1_3.c, t1_3.b
+ Filter: ((t1_3.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p2 t1_4
+ Output: t1_4.a, t1_4.c, t1_4.b
+ Filter: ((t1_4.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p2_p1 t1_5
+ Output: t1_5.a, t1_5.c, t1_5.b
+ Filter: ((t1_5.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p2_p2 t1_6
+ Output: t1_6.a, t1_6.c, t1_6.b
+ Filter: ((t1_6.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p3 t1_7
+ Output: t1_7.a, t1_7.c, t1_7.b
+ Filter: ((t1_7.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p3_p1 t1_8
+ Output: t1_8.a, t1_8.c, t1_8.b
+ Filter: ((t1_8.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p3_p2 t1_9
+ Output: t1_9.a, t1_9.c, t1_9.b
+ Filter: ((t1_9.a % 25) = 0)
+(60 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;
+ 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;
+ QUERY PLAN
+----------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> 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)))
+ -> Append
+ -> Seq Scan on public.prt2_l t2
+ Output: t2.b, t2.c, t2.a
+ -> Seq Scan on public.prt2_l_p1 t2_1
+ Output: t2_1.b, t2_1.c, t2_1.a
+ -> Seq Scan on public.prt2_l_p1_p1 t2_2
+ Output: t2_2.b, t2_2.c, t2_2.a
+ -> Seq Scan on public.prt2_l_p1_p2 t2_3
+ Output: t2_3.b, t2_3.c, t2_3.a
+ -> Seq Scan on public.prt2_l_p2 t2_4
+ Output: t2_4.b, t2_4.c, t2_4.a
+ -> Seq Scan on public.prt2_l_p2_p1 t2_5
+ Output: t2_5.b, t2_5.c, t2_5.a
+ -> Seq Scan on public.prt2_l_p2_p2 t2_6
+ Output: t2_6.b, t2_6.c, t2_6.a
+ -> Seq Scan on public.prt2_l_p3 t2_7
+ Output: t2_7.b, t2_7.c, t2_7.a
+ -> Seq Scan on public.prt2_l_p3_p1 t2_8
+ Output: t2_8.b, t2_8.c, t2_8.a
+ -> Seq Scan on public.prt2_l_p3_p2 t2_9
+ Output: t2_9.b, t2_9.c, t2_9.a
+ -> Hash
+ Output: t1.a, t1.c, t1.b
+ -> Append
+ -> Seq Scan on public.prt1_l t1
+ Output: t1.a, t1.c, t1.b
+ Filter: ((t1.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p1 t1_1
+ Output: t1_1.a, t1_1.c, t1_1.b
+ Filter: ((t1_1.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p1_p1 t1_2
+ Output: t1_2.a, t1_2.c, t1_2.b
+ Filter: ((t1_2.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p1_p2 t1_3
+ Output: t1_3.a, t1_3.c, t1_3.b
+ Filter: ((t1_3.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p2 t1_4
+ Output: t1_4.a, t1_4.c, t1_4.b
+ Filter: ((t1_4.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p2_p1 t1_5
+ Output: t1_5.a, t1_5.c, t1_5.b
+ Filter: ((t1_5.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p2_p2 t1_6
+ Output: t1_6.a, t1_6.c, t1_6.b
+ Filter: ((t1_6.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p3 t1_7
+ Output: t1_7.a, t1_7.c, t1_7.b
+ Filter: ((t1_7.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p3_p1 t1_8
+ Output: t1_8.a, t1_8.c, t1_8.b
+ Filter: ((t1_8.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p3_p2 t1_9
+ Output: t1_9.a, t1_9.c, t1_9.b
+ Filter: ((t1_9.a % 25) = 0)
+(60 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;
+ 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;
+ QUERY PLAN
+----------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t1.a, t1.c, t2.b, t2.c
+ Sort Key: t1.a, t2.b
+ -> Hash Right Join
+ Output: t1.a, t1.c, t2.b, t2.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)))
+ -> Append
+ -> Seq Scan on public.prt1_l t1
+ Output: t1.a, t1.c, t1.b
+ -> Seq Scan on public.prt1_l_p1 t1_1
+ Output: t1_1.a, t1_1.c, t1_1.b
+ -> Seq Scan on public.prt1_l_p1_p1 t1_2
+ Output: t1_2.a, t1_2.c, t1_2.b
+ -> Seq Scan on public.prt1_l_p1_p2 t1_3
+ Output: t1_3.a, t1_3.c, t1_3.b
+ -> Seq Scan on public.prt1_l_p2 t1_4
+ Output: t1_4.a, t1_4.c, t1_4.b
+ -> Seq Scan on public.prt1_l_p2_p1 t1_5
+ Output: t1_5.a, t1_5.c, t1_5.b
+ -> Seq Scan on public.prt1_l_p2_p2 t1_6
+ Output: t1_6.a, t1_6.c, t1_6.b
+ -> Seq Scan on public.prt1_l_p3 t1_7
+ Output: t1_7.a, t1_7.c, t1_7.b
+ -> Seq Scan on public.prt1_l_p3_p1 t1_8
+ Output: t1_8.a, t1_8.c, t1_8.b
+ -> Seq Scan on public.prt1_l_p3_p2 t1_9
+ Output: t1_9.a, t1_9.c, t1_9.b
+ -> Hash
+ Output: t2.b, t2.c, t2.a
+ -> Append
+ -> Seq Scan on public.prt2_l t2
+ Output: t2.b, t2.c, t2.a
+ Filter: ((t2.b % 25) = 0)
+ -> Seq Scan on public.prt2_l_p1 t2_1
+ Output: t2_1.b, t2_1.c, t2_1.a
+ Filter: ((t2_1.b % 25) = 0)
+ -> Seq Scan on public.prt2_l_p1_p1 t2_2
+ Output: t2_2.b, t2_2.c, t2_2.a
+ Filter: ((t2_2.b % 25) = 0)
+ -> Seq Scan on public.prt2_l_p1_p2 t2_3
+ Output: t2_3.b, t2_3.c, t2_3.a
+ Filter: ((t2_3.b % 25) = 0)
+ -> Seq Scan on public.prt2_l_p2 t2_4
+ Output: t2_4.b, t2_4.c, t2_4.a
+ Filter: ((t2_4.b % 25) = 0)
+ -> Seq Scan on public.prt2_l_p2_p1 t2_5
+ Output: t2_5.b, t2_5.c, t2_5.a
+ Filter: ((t2_5.b % 25) = 0)
+ -> Seq Scan on public.prt2_l_p2_p2 t2_6
+ Output: t2_6.b, t2_6.c, t2_6.a
+ Filter: ((t2_6.b % 25) = 0)
+ -> Seq Scan on public.prt2_l_p3 t2_7
+ Output: t2_7.b, t2_7.c, t2_7.a
+ Filter: ((t2_7.b % 25) = 0)
+ -> Seq Scan on public.prt2_l_p3_p1 t2_8
+ Output: t2_8.b, t2_8.c, t2_8.a
+ Filter: ((t2_8.b % 25) = 0)
+ -> Seq Scan on public.prt2_l_p3_p2 t2_9
+ Output: t2_9.b, t2_9.c, t2_9.a
+ Filter: ((t2_9.b % 25) = 0)
+(60 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;
+ 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;
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: prt1_l.a, prt1_l.c, prt2_l.b, prt2_l.c
+ Sort Key: prt1_l.a, prt2_l.b
+ -> Hash Full Join
+ Output: prt1_l.a, prt1_l.c, prt2_l.b, prt2_l.c
+ Hash Cond: ((prt1_l.a = prt2_l.b) AND (prt1_l.b = prt2_l.a) AND ((prt1_l.c)::text = (prt2_l.c)::text) AND ((prt1_l.b + prt1_l.a) = (prt2_l.a + prt2_l.b)))
+ -> Append
+ -> Seq Scan on public.prt1_l
+ Output: prt1_l.a, prt1_l.c, prt1_l.b
+ Filter: ((prt1_l.a % 25) = 0)
+ -> Seq Scan on public.prt1_l_p1
+ Output: prt1_l_p1.a, prt1_l_p1.c, prt1_l_p1.b
+ Filter: ((prt1_l_p1.a % 25) = 0)
+ -> 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)
+ -> 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)
+ -> Seq Scan on public.prt1_l_p2
+ Output: prt1_l_p2.a, prt1_l_p2.c, prt1_l_p2.b
+ Filter: ((prt1_l_p2.a % 25) = 0)
+ -> 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)
+ -> 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)
+ -> Seq Scan on public.prt1_l_p3
+ Output: prt1_l_p3.a, prt1_l_p3.c, prt1_l_p3.b
+ Filter: ((prt1_l_p3.a % 25) = 0)
+ -> 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)
+ -> 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.b, prt2_l.c, prt2_l.a
+ -> Append
+ -> Seq Scan on public.prt2_l
+ Output: prt2_l.b, prt2_l.c, prt2_l.a
+ Filter: ((prt2_l.b % 25) = 0)
+ -> Seq Scan on public.prt2_l_p1
+ Output: prt2_l_p1.b, prt2_l_p1.c, prt2_l_p1.a
+ Filter: ((prt2_l_p1.b % 25) = 0)
+ -> 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)
+ -> 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)
+ -> Seq Scan on public.prt2_l_p2
+ Output: prt2_l_p2.b, prt2_l_p2.c, prt2_l_p2.a
+ Filter: ((prt2_l_p2.b % 25) = 0)
+ -> 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)
+ -> 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)
+ -> Seq Scan on public.prt2_l_p3
+ Output: prt2_l_p3.b, prt2_l_p3.c, prt2_l_p3.a
+ Filter: ((prt2_l_p3.b % 25) = 0)
+ -> 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)
+ -> 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;
+ 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 1cb5dfc..d62841d 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
# 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 8958d8c..49a27b1 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -161,10 +161,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..19c7d29
--- /dev/null
+++ b/src/test/regress/sql/partition_join.sql
@@ -0,0 +1,746 @@
+--
+-- 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;
+
+-- 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;
+
+--
+-- 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);