set_join_pathlist_hook_type set_join_pathlist_hook = NULL;
/*
- * Paths parameterized by the parent can be considered to be parameterized by
- * any of its child.
+ * Paths parameterized by a parent rel can be considered to be parameterized
+ * by any of its children, when we are performing partitionwise joins. These
+ * macros simplify checking for such cases. Beware multiple eval of args.
*/
#define PATH_PARAM_BY_PARENT(path, rel) \
((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), \
/* If we got past that, we shouldn't have any unsafe outer-join refs */
Assert(!have_unsafe_outer_join_ref(root, outerrelids, inner_paramrels));
+ /*
+ * If the inner path is parameterized, it is parameterized by the topmost
+ * parent of the outer rel, not the outer rel itself. We will need to
+ * translate the parameterization, if this path is chosen, during
+ * create_plan(). Here we just check whether we will be able to perform
+ * the translation, and if not avoid creating a nestloop path.
+ */
+ if (PATH_PARAM_BY_PARENT(inner_path, outer_path->parent) &&
+ !path_is_reparameterizable_by_child(inner_path, outer_path->parent))
+ {
+ bms_free(required_outer);
+ return;
+ }
+
/*
* Do a precheck to quickly eliminate obviously-inferior paths. We
* calculate a cheap lower bound on the path's cost and then use
workspace.startup_cost, workspace.total_cost,
pathkeys, required_outer))
{
- /*
- * If the inner path is parameterized, it is parameterized by the
- * topmost parent of the outer rel, not the outer rel itself. Fix
- * that.
- */
- if (PATH_PARAM_BY_PARENT(inner_path, outer_path->parent))
- {
- inner_path = reparameterize_path_by_child(root, inner_path,
- outer_path->parent);
-
- /*
- * If we could not translate the path, we can't create nest loop
- * path.
- */
- if (!inner_path)
- {
- bms_free(required_outer);
- return;
- }
- }
-
add_path(joinrel, (Path *)
create_nestloop_path(root,
joinrel,
return;
}
+ /*
+ * If the inner path is parameterized, it is parameterized by the topmost
+ * parent of the outer rel, not the outer rel itself. We will need to
+ * translate the parameterization, if this path is chosen, during
+ * create_plan(). Here we just check whether we will be able to perform
+ * the translation, and if not avoid creating a nestloop path.
+ */
+ if (PATH_PARAM_BY_PARENT(inner_path, outer_path->parent) &&
+ !path_is_reparameterizable_by_child(inner_path, outer_path->parent))
+ return;
+
/*
* Before creating a path, get a quick lower bound on what it is likely to
* cost. Bail out right away if it looks terrible.
if (!add_partial_path_precheck(joinrel, workspace.total_cost, pathkeys))
return;
- /*
- * If the inner path is parameterized, it is parameterized by the topmost
- * parent of the outer rel, not the outer rel itself. Fix that.
- */
- if (PATH_PARAM_BY_PARENT(inner_path, outer_path->parent))
- {
- inner_path = reparameterize_path_by_child(root, inner_path,
- outer_path->parent);
-
- /*
- * If we could not translate the path, we can't create nest loop path.
- */
- if (!inner_path)
- return;
- }
-
/* Might be good enough to be worth trying, so let's try it. */
add_partial_path(joinrel, (Path *)
create_nestloop_path(root,
#include "optimizer/cost.h"
#include "optimizer/optimizer.h"
#include "optimizer/paramassign.h"
+#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/placeholder.h"
#include "optimizer/plancat.h"
List *nestParams;
Relids saveOuterRels = root->curOuterRels;
+ /*
+ * If the inner path is parameterized by the topmost parent of the outer
+ * rel rather than the outer rel itself, fix that. (Nothing happens here
+ * if it is not so parameterized.)
+ */
+ best_path->jpath.innerjoinpath =
+ reparameterize_path_by_child(root,
+ best_path->jpath.innerjoinpath,
+ best_path->jpath.outerjoinpath->parent);
+
+ /*
+ * Failure here probably means that reparameterize_path_by_child() is not
+ * in sync with path_is_reparameterizable_by_child().
+ */
+ Assert(best_path->jpath.innerjoinpath != NULL);
+
/* NestLoop can project, so no need to be picky about child tlists */
outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath, 0);
static List *reparameterize_pathlist_by_child(PlannerInfo *root,
List *pathlist,
RelOptInfo *child_rel);
+static bool pathlist_is_reparameterizable_by_child(List *pathlist,
+ RelOptInfo *child_rel);
/*****************************************************************************
{
NestPath *pathnode = makeNode(NestPath);
Relids inner_req_outer = PATH_REQ_OUTER(inner_path);
+ Relids outerrelids;
+
+ /*
+ * Paths are parameterized by top-level parents, so run parameterization
+ * tests on the parent relids.
+ */
+ if (outer_path->parent->top_parent_relids)
+ outerrelids = outer_path->parent->top_parent_relids;
+ else
+ outerrelids = outer_path->parent->relids;
/*
* If the inner path is parameterized by the outer, we must drop any
* estimates for this path. We detect such clauses by checking for serial
* number match to clauses already enforced in the inner path.
*/
- if (bms_overlap(inner_req_outer, outer_path->parent->relids))
+ if (bms_overlap(inner_req_outer, outerrelids))
{
Bitmapset *enforced_serials = get_param_path_clause_serials(inner_path);
List *jclauses = NIL;
* Given a path parameterized by the parent of the given child 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. Most fields from the original
- * path can simply be flat-copied, but any expressions must be adjusted to
- * refer to the correct varnos, and any paths must be recursively
- * reparameterized. Other fields that refer to specific relids also need
- * adjustment.
+ * Most fields in the path are not changed, but any expressions must be
+ * adjusted to refer to the correct varnos, and any subpaths must be
+ * recursively reparameterized. Other fields that refer to specific relids
+ * also need adjustment.
*
* The cost, number of rows, width and parallel path properties depend upon
- * path->parent, which does not change during the translation. Hence those
- * members are copied as they are.
+ * path->parent, which does not change during the translation. So we need
+ * not change those.
*
* Currently, only a few path types are supported here, though more could be
* added at need. We return NULL if we can't reparameterize the given path.
+ *
+ * Note that this function can change referenced RangeTblEntries, RelOptInfos
+ * and IndexOptInfos as well as the Path structures. Therefore, it's only safe
+ * to call during create_plan(), when we have made a final choice of which Path
+ * to use for each RangeTblEntry/RelOptInfo/IndexOptInfo.
+ *
+ * Keep this code in sync with path_is_reparameterizable_by_child()!
*/
Path *
reparameterize_path_by_child(PlannerInfo *root, Path *path,
RelOptInfo *child_rel)
{
-
-#define FLAT_COPY_PATH(newnode, node, nodetype) \
- ( (newnode) = makeNode(nodetype), \
- memcpy((newnode), (node), sizeof(nodetype)) )
+ Path *new_path;
+ ParamPathInfo *new_ppi;
+ ParamPathInfo *old_ppi;
+ Relids required_outer;
#define ADJUST_CHILD_ATTRS(node) \
- ((node) = \
- (List *) adjust_appendrel_attrs_multilevel(root, (Node *) (node), \
- child_rel, \
- child_rel->top_parent))
+ ((node) = (void *) adjust_appendrel_attrs_multilevel(root, \
+ (Node *) (node), \
+ child_rel, \
+ child_rel->top_parent))
#define REPARAMETERIZE_CHILD_PATH(path) \
do { \
} \
} while(0)
- Path *new_path;
- ParamPathInfo *new_ppi;
- ParamPathInfo *old_ppi;
- Relids required_outer;
-
/*
- * If the path is not parameterized by parent of the given relation, it
- * doesn't need reparameterization.
+ * If the path is not parameterized by the parent of the given relation,
+ * it doesn't need reparameterization.
*/
if (!path->param_info ||
!bms_overlap(PATH_REQ_OUTER(path), child_rel->top_parent_relids))
return path;
/*
- * If possible, reparameterize the given path, making a copy.
+ * If possible, reparameterize the given path.
*
* This function is currently only applied to the inner side of a nestloop
* join that is being partitioned by the partitionwise-join code. Hence,
switch (nodeTag(path))
{
case T_Path:
- FLAT_COPY_PATH(new_path, path, Path);
+ new_path = path;
+ ADJUST_CHILD_ATTRS(new_path->parent->baserestrictinfo);
+ if (path->pathtype == T_SampleScan)
+ {
+ Index scan_relid = path->parent->relid;
+ RangeTblEntry *rte;
+
+ /* it should be a base rel with a tablesample clause... */
+ Assert(scan_relid > 0);
+ rte = planner_rt_fetch(scan_relid, root);
+ Assert(rte->rtekind == RTE_RELATION);
+ Assert(rte->tablesample != NULL);
+
+ ADJUST_CHILD_ATTRS(rte->tablesample);
+ }
break;
case T_IndexPath:
{
- IndexPath *ipath;
+ IndexPath *ipath = (IndexPath *) path;
- FLAT_COPY_PATH(ipath, path, IndexPath);
+ ADJUST_CHILD_ATTRS(ipath->indexinfo->indrestrictinfo);
ADJUST_CHILD_ATTRS(ipath->indexclauses);
new_path = (Path *) ipath;
}
case T_BitmapHeapPath:
{
- BitmapHeapPath *bhpath;
+ BitmapHeapPath *bhpath = (BitmapHeapPath *) path;
- FLAT_COPY_PATH(bhpath, path, BitmapHeapPath);
+ ADJUST_CHILD_ATTRS(bhpath->path.parent->baserestrictinfo);
REPARAMETERIZE_CHILD_PATH(bhpath->bitmapqual);
new_path = (Path *) bhpath;
}
case T_BitmapAndPath:
{
- BitmapAndPath *bapath;
+ BitmapAndPath *bapath = (BitmapAndPath *) path;
- FLAT_COPY_PATH(bapath, path, BitmapAndPath);
REPARAMETERIZE_CHILD_PATH_LIST(bapath->bitmapquals);
new_path = (Path *) bapath;
}
case T_BitmapOrPath:
{
- BitmapOrPath *bopath;
+ BitmapOrPath *bopath = (BitmapOrPath *) path;
- FLAT_COPY_PATH(bopath, path, BitmapOrPath);
REPARAMETERIZE_CHILD_PATH_LIST(bopath->bitmapquals);
new_path = (Path *) bopath;
}
case T_ForeignPath:
{
- ForeignPath *fpath;
+ ForeignPath *fpath = (ForeignPath *) path;
ReparameterizeForeignPathByChild_function rfpc_func;
- FLAT_COPY_PATH(fpath, path, ForeignPath);
+ ADJUST_CHILD_ATTRS(fpath->path.parent->baserestrictinfo);
if (fpath->fdw_outerpath)
REPARAMETERIZE_CHILD_PATH(fpath->fdw_outerpath);
if (fpath->fdw_restrictinfo)
case T_CustomPath:
{
- CustomPath *cpath;
+ CustomPath *cpath = (CustomPath *) path;
- FLAT_COPY_PATH(cpath, path, CustomPath);
+ ADJUST_CHILD_ATTRS(cpath->path.parent->baserestrictinfo);
REPARAMETERIZE_CHILD_PATH_LIST(cpath->custom_paths);
if (cpath->custom_restrictinfo)
ADJUST_CHILD_ATTRS(cpath->custom_restrictinfo);
case T_NestPath:
{
- JoinPath *jpath;
- NestPath *npath;
-
- FLAT_COPY_PATH(npath, path, NestPath);
+ NestPath *npath = (NestPath *) path;
+ JoinPath *jpath = (JoinPath *) npath;
- jpath = (JoinPath *) npath;
REPARAMETERIZE_CHILD_PATH(jpath->outerjoinpath);
REPARAMETERIZE_CHILD_PATH(jpath->innerjoinpath);
ADJUST_CHILD_ATTRS(jpath->joinrestrictinfo);
case T_MergePath:
{
- JoinPath *jpath;
- MergePath *mpath;
+ MergePath *mpath = (MergePath *) path;
+ JoinPath *jpath = (JoinPath *) mpath;
- FLAT_COPY_PATH(mpath, path, MergePath);
-
- jpath = (JoinPath *) mpath;
REPARAMETERIZE_CHILD_PATH(jpath->outerjoinpath);
REPARAMETERIZE_CHILD_PATH(jpath->innerjoinpath);
ADJUST_CHILD_ATTRS(jpath->joinrestrictinfo);
case T_HashPath:
{
- JoinPath *jpath;
- HashPath *hpath;
-
- FLAT_COPY_PATH(hpath, path, HashPath);
+ HashPath *hpath = (HashPath *) path;
+ JoinPath *jpath = (JoinPath *) hpath;
- jpath = (JoinPath *) hpath;
REPARAMETERIZE_CHILD_PATH(jpath->outerjoinpath);
REPARAMETERIZE_CHILD_PATH(jpath->innerjoinpath);
ADJUST_CHILD_ATTRS(jpath->joinrestrictinfo);
case T_AppendPath:
{
- AppendPath *apath;
+ AppendPath *apath = (AppendPath *) path;
- FLAT_COPY_PATH(apath, path, AppendPath);
REPARAMETERIZE_CHILD_PATH_LIST(apath->subpaths);
new_path = (Path *) apath;
}
case T_MaterialPath:
{
- MaterialPath *mpath;
+ MaterialPath *mpath = (MaterialPath *) path;
- FLAT_COPY_PATH(mpath, path, MaterialPath);
REPARAMETERIZE_CHILD_PATH(mpath->subpath);
new_path = (Path *) mpath;
}
case T_MemoizePath:
{
- MemoizePath *mpath;
+ MemoizePath *mpath = (MemoizePath *) path;
- FLAT_COPY_PATH(mpath, path, MemoizePath);
REPARAMETERIZE_CHILD_PATH(mpath->subpath);
ADJUST_CHILD_ATTRS(mpath->param_exprs);
new_path = (Path *) mpath;
case T_GatherPath:
{
- GatherPath *gpath;
+ GatherPath *gpath = (GatherPath *) path;
- FLAT_COPY_PATH(gpath, path, GatherPath);
REPARAMETERIZE_CHILD_PATH(gpath->subpath);
new_path = (Path *) gpath;
}
break;
default:
-
/* We don't know how to reparameterize this path. */
return NULL;
}
return new_path;
}
+/*
+ * path_is_reparameterizable_by_child
+ * Given a path parameterized by the parent of the given child relation,
+ * see if it can be translated to be parameterized by the child relation.
+ *
+ * This must return true if and only if reparameterize_path_by_child()
+ * would succeed on this path. Currently it's sufficient to verify that
+ * the path and all of its subpaths (if any) are of the types handled by
+ * that function. However, subpaths that are not parameterized can be
+ * disregarded since they won't require translation.
+ */
+bool
+path_is_reparameterizable_by_child(Path *path, RelOptInfo *child_rel)
+{
+#define REJECT_IF_PATH_NOT_REPARAMETERIZABLE(path) \
+do { \
+ if (!path_is_reparameterizable_by_child(path, child_rel)) \
+ return false; \
+} while(0)
+
+#define REJECT_IF_PATH_LIST_NOT_REPARAMETERIZABLE(pathlist) \
+do { \
+ if (!pathlist_is_reparameterizable_by_child(pathlist, child_rel)) \
+ return false; \
+} while(0)
+
+ /*
+ * If the path is not parameterized by the parent of the given relation,
+ * it doesn't need reparameterization.
+ */
+ if (!path->param_info ||
+ !bms_overlap(PATH_REQ_OUTER(path), child_rel->top_parent_relids))
+ return true;
+
+ /*
+ * Check that the path type is one that reparameterize_path_by_child() can
+ * handle, and recursively check subpaths.
+ */
+ switch (nodeTag(path))
+ {
+ case T_Path:
+ case T_IndexPath:
+ break;
+
+ case T_BitmapHeapPath:
+ {
+ BitmapHeapPath *bhpath = (BitmapHeapPath *) path;
+
+ REJECT_IF_PATH_NOT_REPARAMETERIZABLE(bhpath->bitmapqual);
+ }
+ break;
+
+ case T_BitmapAndPath:
+ {
+ BitmapAndPath *bapath = (BitmapAndPath *) path;
+
+ REJECT_IF_PATH_LIST_NOT_REPARAMETERIZABLE(bapath->bitmapquals);
+ }
+ break;
+
+ case T_BitmapOrPath:
+ {
+ BitmapOrPath *bopath = (BitmapOrPath *) path;
+
+ REJECT_IF_PATH_LIST_NOT_REPARAMETERIZABLE(bopath->bitmapquals);
+ }
+ break;
+
+ case T_ForeignPath:
+ {
+ ForeignPath *fpath = (ForeignPath *) path;
+
+ if (fpath->fdw_outerpath)
+ REJECT_IF_PATH_NOT_REPARAMETERIZABLE(fpath->fdw_outerpath);
+ }
+ break;
+
+ case T_CustomPath:
+ {
+ CustomPath *cpath = (CustomPath *) path;
+
+ REJECT_IF_PATH_LIST_NOT_REPARAMETERIZABLE(cpath->custom_paths);
+ }
+ break;
+
+ case T_NestPath:
+ case T_MergePath:
+ case T_HashPath:
+ {
+ JoinPath *jpath = (JoinPath *) path;
+
+ REJECT_IF_PATH_NOT_REPARAMETERIZABLE(jpath->outerjoinpath);
+ REJECT_IF_PATH_NOT_REPARAMETERIZABLE(jpath->innerjoinpath);
+ }
+ break;
+
+ case T_AppendPath:
+ {
+ AppendPath *apath = (AppendPath *) path;
+
+ REJECT_IF_PATH_LIST_NOT_REPARAMETERIZABLE(apath->subpaths);
+ }
+ break;
+
+ case T_MaterialPath:
+ {
+ MaterialPath *mpath = (MaterialPath *) path;
+
+ REJECT_IF_PATH_NOT_REPARAMETERIZABLE(mpath->subpath);
+ }
+ break;
+
+ case T_MemoizePath:
+ {
+ MemoizePath *mpath = (MemoizePath *) path;
+
+ REJECT_IF_PATH_NOT_REPARAMETERIZABLE(mpath->subpath);
+ }
+ break;
+
+ case T_GatherPath:
+ {
+ GatherPath *gpath = (GatherPath *) path;
+
+ REJECT_IF_PATH_NOT_REPARAMETERIZABLE(gpath->subpath);
+ }
+ break;
+
+ default:
+ /* We don't know how to reparameterize this path. */
+ return false;
+ }
+
+ return true;
+}
+
/*
* reparameterize_pathlist_by_child
* Helper function to reparameterize a list of paths by given child rel.
+ *
+ * Returns NIL to indicate failure, so pathlist had better not be NIL.
*/
static List *
reparameterize_pathlist_by_child(PlannerInfo *root,
return result;
}
+
+/*
+ * pathlist_is_reparameterizable_by_child
+ * Helper function to check if a list of paths can be reparameterized.
+ */
+static bool
+pathlist_is_reparameterizable_by_child(List *pathlist, RelOptInfo *child_rel)
+{
+ ListCell *lc;
+
+ foreach(lc, pathlist)
+ {
+ Path *path = (Path *) lfirst(lc);
+
+ if (!path_is_reparameterizable_by_child(path, child_rel))
+ return false;
+ }
+
+ return true;
+}
double loop_count);
extern Path *reparameterize_path_by_child(PlannerInfo *root, Path *path,
RelOptInfo *child_rel);
+extern bool path_is_reparameterizable_by_child(Path *path,
+ RelOptInfo *child_rel);
/*
* prototypes for relnode.c
550 | |
(12 rows)
+-- lateral reference in sample scan
+EXPLAIN (COSTS OFF)
+SELECT * FROM prt1 t1 JOIN LATERAL
+ (SELECT * FROM prt1 t2 TABLESAMPLE SYSTEM (t1.a) REPEATABLE(t1.b)) s
+ ON t1.a = s.a;
+ QUERY PLAN
+-------------------------------------------------------------
+ Append
+ -> Nested Loop
+ -> Seq Scan on prt1_p1 t1_1
+ -> Sample Scan on prt1_p1 t2_1
+ Sampling: system (t1_1.a) REPEATABLE (t1_1.b)
+ Filter: (t1_1.a = a)
+ -> Nested Loop
+ -> Seq Scan on prt1_p2 t1_2
+ -> Sample Scan on prt1_p2 t2_2
+ Sampling: system (t1_2.a) REPEATABLE (t1_2.b)
+ Filter: (t1_2.a = a)
+ -> Nested Loop
+ -> Seq Scan on prt1_p3 t1_3
+ -> Sample Scan on prt1_p3 t2_3
+ Sampling: system (t1_3.a) REPEATABLE (t1_3.b)
+ Filter: (t1_3.a = a)
+(16 rows)
+
+-- lateral reference in scan's restriction clauses
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t1.b AS t1b, t2.* FROM prt2 t2) s
+ ON t1.a = s.b WHERE s.t1b = s.a;
+ QUERY PLAN
+---------------------------------------------------------------
+ Aggregate
+ -> Append
+ -> Nested Loop
+ -> Seq Scan on prt1_p1 t1_1
+ -> Index Scan using iprt2_p1_b on prt2_p1 t2_1
+ Index Cond: (b = t1_1.a)
+ Filter: (t1_1.b = a)
+ -> Nested Loop
+ -> Seq Scan on prt1_p2 t1_2
+ -> Index Scan using iprt2_p2_b on prt2_p2 t2_2
+ Index Cond: (b = t1_2.a)
+ Filter: (t1_2.b = a)
+ -> Nested Loop
+ -> Seq Scan on prt1_p3 t1_3
+ -> Index Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ Filter: (t1_3.b = a)
+(17 rows)
+
+SELECT count(*) FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t1.b AS t1b, t2.* FROM prt2 t2) s
+ ON t1.a = s.b WHERE s.t1b = s.a;
+ count
+-------
+ 100
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t1.b AS t1b, t2.* FROM prt2 t2) s
+ ON t1.a = s.b WHERE s.t1b = s.b;
+ QUERY PLAN
+--------------------------------------------------------------------
+ Aggregate
+ -> Append
+ -> Nested Loop
+ -> Seq Scan on prt1_p1 t1_1
+ -> Index Only Scan using iprt2_p1_b on prt2_p1 t2_1
+ Index Cond: (b = t1_1.a)
+ Filter: (b = t1_1.b)
+ -> Nested Loop
+ -> Seq Scan on prt1_p2 t1_2
+ -> Index Only Scan using iprt2_p2_b on prt2_p2 t2_2
+ Index Cond: (b = t1_2.a)
+ Filter: (b = t1_2.b)
+ -> Nested Loop
+ -> Seq Scan on prt1_p3 t1_3
+ -> Index Only Scan using iprt2_p3_b on prt2_p3 t2_3
+ Index Cond: (b = t1_3.a)
+ Filter: (b = t1_3.b)
+(17 rows)
+
+SELECT count(*) FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t1.b AS t1b, t2.* FROM prt2 t2) s
+ ON t1.a = s.b WHERE s.t1b = s.b;
+ count
+-------
+ 5
+(1 row)
+
-- bug with inadequate sort key representation
SET enable_partitionwise_aggregate TO true;
SET enable_hashjoin TO false;
550 | 0 | 0002 | | | | |
(12 rows)
+-- partitionwise join with lateral reference in sample scan
+EXPLAIN (COSTS OFF)
+SELECT * FROM prt1_l t1 JOIN LATERAL
+ (SELECT * FROM prt1_l t2 TABLESAMPLE SYSTEM (t1.a) REPEATABLE(t1.b)) s
+ ON t1.a = s.a AND t1.b = s.b AND t1.c = s.c;
+ QUERY PLAN
+----------------------------------------------------------------------------------------
+ Append
+ -> Nested Loop
+ -> Seq Scan on prt1_l_p1 t1_1
+ -> Sample Scan on prt1_l_p1 t2_1
+ Sampling: system (t1_1.a) REPEATABLE (t1_1.b)
+ Filter: ((t1_1.a = a) AND (t1_1.b = b) AND ((t1_1.c)::text = (c)::text))
+ -> Nested Loop
+ -> Seq Scan on prt1_l_p2_p1 t1_2
+ -> Sample Scan on prt1_l_p2_p1 t2_2
+ Sampling: system (t1_2.a) REPEATABLE (t1_2.b)
+ Filter: ((t1_2.a = a) AND (t1_2.b = b) AND ((t1_2.c)::text = (c)::text))
+ -> Nested Loop
+ -> Seq Scan on prt1_l_p2_p2 t1_3
+ -> Sample Scan on prt1_l_p2_p2 t2_3
+ Sampling: system (t1_3.a) REPEATABLE (t1_3.b)
+ Filter: ((t1_3.a = a) AND (t1_3.b = b) AND ((t1_3.c)::text = (c)::text))
+ -> Nested Loop
+ -> Seq Scan on prt1_l_p3_p1 t1_4
+ -> Sample Scan on prt1_l_p3_p1 t2_4
+ Sampling: system (t1_4.a) REPEATABLE (t1_4.b)
+ Filter: ((t1_4.a = a) AND (t1_4.b = b) AND ((t1_4.c)::text = (c)::text))
+ -> Nested Loop
+ -> Seq Scan on prt1_l_p3_p2 t1_5
+ -> Sample Scan on prt1_l_p3_p2 t2_5
+ Sampling: system (t1_5.a) REPEATABLE (t1_5.b)
+ Filter: ((t1_5.a = a) AND (t1_5.b = b) AND ((t1_5.c)::text = (c)::text))
+(26 rows)
+
+-- partitionwise join with lateral reference in scan's restriction clauses
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) FROM prt1_l t1 LEFT JOIN LATERAL
+ (SELECT t1.b AS t1b, t2.* FROM prt2_l t2) s
+ ON t1.a = s.b AND t1.b = s.a AND t1.c = s.c
+ WHERE s.t1b = s.a;
+ QUERY PLAN
+---------------------------------------------------------------------------------------------------------------
+ Aggregate
+ -> Append
+ -> Nested Loop
+ -> Seq Scan on prt1_l_p1 t1_1
+ -> Seq Scan on prt2_l_p1 t2_1
+ Filter: ((a = t1_1.b) AND (t1_1.a = b) AND (t1_1.b = a) AND ((t1_1.c)::text = (c)::text))
+ -> Nested Loop
+ -> Seq Scan on prt1_l_p2_p1 t1_2
+ -> Seq Scan on prt2_l_p2_p1 t2_2
+ Filter: ((a = t1_2.b) AND (t1_2.a = b) AND (t1_2.b = a) AND ((t1_2.c)::text = (c)::text))
+ -> Nested Loop
+ -> Seq Scan on prt1_l_p2_p2 t1_3
+ -> Seq Scan on prt2_l_p2_p2 t2_3
+ Filter: ((a = t1_3.b) AND (t1_3.a = b) AND (t1_3.b = a) AND ((t1_3.c)::text = (c)::text))
+ -> Nested Loop
+ -> Seq Scan on prt1_l_p3_p1 t1_4
+ -> Seq Scan on prt2_l_p3_p1 t2_4
+ Filter: ((a = t1_4.b) AND (t1_4.a = b) AND (t1_4.b = a) AND ((t1_4.c)::text = (c)::text))
+ -> Nested Loop
+ -> Seq Scan on prt1_l_p3_p2 t1_5
+ -> Seq Scan on prt2_l_p3_p2 t2_5
+ Filter: ((a = t1_5.b) AND (t1_5.a = b) AND (t1_5.b = a) AND ((t1_5.c)::text = (c)::text))
+(22 rows)
+
+SELECT COUNT(*) FROM prt1_l t1 LEFT JOIN LATERAL
+ (SELECT t1.b AS t1b, t2.* FROM prt2_l t2) s
+ ON t1.a = s.b AND t1.b = s.a AND t1.c = s.c
+ WHERE s.t1b = s.a;
+ count
+-------
+ 100
+(1 row)
+
-- join with one side empty
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c;
(SELECT t2.a AS t2a, t3.a AS t3a, t2.b t2b, t2.c t2c, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
ON t1.c = ss.t2c WHERE (t1.b + coalesce(ss.t2b, 0)) = 0 ORDER BY t1.a;
+-- lateral reference in sample scan
+EXPLAIN (COSTS OFF)
+SELECT * FROM prt1 t1 JOIN LATERAL
+ (SELECT * FROM prt1 t2 TABLESAMPLE SYSTEM (t1.a) REPEATABLE(t1.b)) s
+ ON t1.a = s.a;
+
+-- lateral reference in scan's restriction clauses
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t1.b AS t1b, t2.* FROM prt2 t2) s
+ ON t1.a = s.b WHERE s.t1b = s.a;
+SELECT count(*) FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t1.b AS t1b, t2.* FROM prt2 t2) s
+ ON t1.a = s.b WHERE s.t1b = s.a;
+
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t1.b AS t1b, t2.* FROM prt2 t2) s
+ ON t1.a = s.b WHERE s.t1b = s.b;
+SELECT count(*) FROM prt1 t1 LEFT JOIN LATERAL
+ (SELECT t1.b AS t1b, t2.* FROM prt2 t2) s
+ ON t1.a = s.b WHERE s.t1b = s.b;
+
-- bug with inadequate sort key representation
SET enable_partitionwise_aggregate TO true;
SET enable_hashjoin TO false;
(SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.b AS t3b, least(t1.a,t2.a,t3.b) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.c = t3.c)) ss
ON t1.a = ss.t2a AND t1.c = ss.t2c WHERE t1.b = 0 ORDER BY t1.a;
+-- partitionwise join with lateral reference in sample scan
+EXPLAIN (COSTS OFF)
+SELECT * FROM prt1_l t1 JOIN LATERAL
+ (SELECT * FROM prt1_l t2 TABLESAMPLE SYSTEM (t1.a) REPEATABLE(t1.b)) s
+ ON t1.a = s.a AND t1.b = s.b AND t1.c = s.c;
+
+-- partitionwise join with lateral reference in scan's restriction clauses
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) FROM prt1_l t1 LEFT JOIN LATERAL
+ (SELECT t1.b AS t1b, t2.* FROM prt2_l t2) s
+ ON t1.a = s.b AND t1.b = s.a AND t1.c = s.c
+ WHERE s.t1b = s.a;
+SELECT COUNT(*) FROM prt1_l t1 LEFT JOIN LATERAL
+ (SELECT t1.b AS t1b, t2.* FROM prt2_l t2) s
+ ON t1.a = s.b AND t1.b = s.a AND t1.c = s.c
+ WHERE s.t1b = s.a;
+
-- join with one side empty
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c;