aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/plan/createplan.c201
-rw-r--r--src/backend/optimizer/plan/setrefs.c238
-rw-r--r--src/backend/optimizer/plan/subselect.c159
3 files changed, 318 insertions, 280 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 3d6f9f94faa..434babc5d85 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.275 2010/05/25 17:44:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.276 2010/07/12 17:01:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -28,6 +28,7 @@
#include "optimizer/planmain.h"
#include "optimizer/predtest.h"
#include "optimizer/restrictinfo.h"
+#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parse_clause.h"
@@ -35,6 +36,7 @@
#include "utils/lsyscache.h"
+static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
static Plan *create_scan_plan(PlannerInfo *root, Path *best_path);
static List *build_relation_tlist(RelOptInfo *rel);
static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
@@ -72,7 +74,10 @@ static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path,
Plan *outer_plan, Plan *inner_plan);
static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path,
Plan *outer_plan, Plan *inner_plan);
-static List *fix_indexqual_references(List *indexquals, IndexPath *index_path);
+static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
+static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
+static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path,
+ List *indexquals);
static List *get_switched_clauses(List *clauses, Relids outerrelids);
static List *order_qual_clauses(PlannerInfo *root, List *clauses);
static void copy_path_costsize(Plan *dest, Path *src);
@@ -103,7 +108,7 @@ static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
static BitmapAnd *make_bitmap_and(List *bitmapplans);
static BitmapOr *make_bitmap_or(List *bitmapplans);
static NestLoop *make_nestloop(List *tlist,
- List *joinclauses, List *otherclauses,
+ List *joinclauses, List *otherclauses, List *nestParams,
Plan *lefttree, Plan *righttree,
JoinType jointype);
static HashJoin *make_hashjoin(List *tlist,
@@ -133,8 +138,8 @@ static Material *make_material(Plan *lefttree);
/*
* create_plan
- * Creates the access plan for a query by tracing backwards through the
- * desired chain of pathnodes, starting at the node 'best_path'. For
+ * Creates the access plan for a query by recursively processing the
+ * desired tree of pathnodes, starting at the node 'best_path'. For
* every pathnode found, we create a corresponding plan node containing
* appropriate id, target list, and qualification information.
*
@@ -151,6 +156,29 @@ create_plan(PlannerInfo *root, Path *best_path)
{
Plan *plan;
+ /* Initialize this module's private workspace in PlannerInfo */
+ root->curOuterRels = NULL;
+ root->curOuterParams = NIL;
+
+ /* Recursively process the path tree */
+ plan = create_plan_recurse(root, best_path);
+
+ /* Check we successfully assigned all NestLoopParams to plan nodes */
+ if (root->curOuterParams != NIL)
+ elog(ERROR, "failed to assign all NestLoopParams to plan nodes");
+
+ return plan;
+}
+
+/*
+ * create_plan_recurse
+ * Recursive guts of create_plan().
+ */
+static Plan *
+create_plan_recurse(PlannerInfo *root, Path *best_path)
+{
+ Plan *plan;
+
switch (best_path->pathtype)
{
case T_SeqScan:
@@ -477,9 +505,16 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
Plan *outer_plan;
Plan *inner_plan;
Plan *plan;
+ Relids saveOuterRels = root->curOuterRels;
+
+ outer_plan = create_plan_recurse(root, best_path->outerjoinpath);
- outer_plan = create_plan(root, best_path->outerjoinpath);
- inner_plan = create_plan(root, best_path->innerjoinpath);
+ /* For a nestloop, include outer relids in curOuterRels for inner side */
+ if (best_path->path.pathtype == T_NestLoop)
+ root->curOuterRels = bms_union(root->curOuterRels,
+ best_path->outerjoinpath->parent->relids);
+
+ inner_plan = create_plan_recurse(root, best_path->innerjoinpath);
switch (best_path->path.pathtype)
{
@@ -496,6 +531,10 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
inner_plan);
break;
case T_NestLoop:
+ /* Restore curOuterRels */
+ bms_free(root->curOuterRels);
+ root->curOuterRels = saveOuterRels;
+
plan = (Plan *) create_nestloop_plan(root,
(NestPath *) best_path,
outer_plan,
@@ -571,7 +610,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
{
Path *subpath = (Path *) lfirst(subpaths);
- subplans = lappend(subplans, create_plan(root, subpath));
+ subplans = lappend(subplans, create_plan_recurse(root, subpath));
}
plan = make_append(subplans, tlist);
@@ -616,7 +655,7 @@ create_material_plan(PlannerInfo *root, MaterialPath *best_path)
Material *plan;
Plan *subplan;
- subplan = create_plan(root, best_path->subpath);
+ subplan = create_plan_recurse(root, best_path->subpath);
/* We don't want any excess columns in the materialized tuples */
disuse_physical_tlist(subplan, best_path->subpath);
@@ -650,7 +689,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
int groupColPos;
ListCell *l;
- subplan = create_plan(root, best_path->subpath);
+ subplan = create_plan_recurse(root, best_path->subpath);
/* Done if we don't need to do any actual unique-ifying */
if (best_path->umethod == UNIQUE_PATH_NOOP)
@@ -904,7 +943,7 @@ create_indexscan_plan(PlannerInfo *root,
* The executor needs a copy with the indexkey on the left of each clause
* and with index attr numbers substituted for table ones.
*/
- fixed_indexquals = fix_indexqual_references(indexquals, best_path);
+ fixed_indexquals = fix_indexqual_references(root, best_path, indexquals);
/*
* If this is an innerjoin scan, the indexclauses will contain join
@@ -975,6 +1014,22 @@ create_indexscan_plan(PlannerInfo *root,
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
qpqual = extract_actual_clauses(qpqual, false);
+ /*
+ * We have to replace any outer-relation variables with nestloop params
+ * in the indexqualorig and qpqual expressions. A bit annoying to have to
+ * do this separately from the processing in fix_indexqual_references ---
+ * rethink this when generalizing the inner indexscan support. But note
+ * we can't really do this earlier because it'd break the comparisons to
+ * predicates above ... (or would it? Those wouldn't have outer refs)
+ */
+ if (best_path->isjoininner)
+ {
+ stripped_indexquals = (List *)
+ replace_nestloop_params(root, (Node *) stripped_indexquals);
+ qpqual = (List *)
+ replace_nestloop_params(root, (Node *) qpqual);
+ }
+
/* Finally ready to build the plan node */
scan_plan = make_indexscan(tlist,
qpqual,
@@ -1267,6 +1322,17 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
*indexqual = lappend(*indexqual, pred);
}
}
+ /*
+ * Replace outer-relation variables with nestloop params, but only
+ * after doing the above comparisons to index predicates.
+ */
+ if (ipath->isjoininner)
+ {
+ *qual = (List *)
+ replace_nestloop_params(root, (Node *) *qual);
+ *indexqual = (List *)
+ replace_nestloop_params(root, (Node *) *indexqual);
+ }
}
else
{
@@ -1572,11 +1638,16 @@ create_nestloop_plan(PlannerInfo *root,
Plan *outer_plan,
Plan *inner_plan)
{
+ NestLoop *join_plan;
List *tlist = build_relation_tlist(best_path->path.parent);
List *joinrestrictclauses = best_path->joinrestrictinfo;
List *joinclauses;
List *otherclauses;
- NestLoop *join_plan;
+ Relids outerrelids;
+ List *nestParams;
+ ListCell *cell;
+ ListCell *prev;
+ ListCell *next;
/*
* If the inner path is a nestloop inner indexscan, it might be using some
@@ -1605,9 +1676,32 @@ create_nestloop_plan(PlannerInfo *root,
otherclauses = NIL;
}
+ /*
+ * Identify any nestloop parameters that should be supplied by this join
+ * node, and move them from root->curOuterParams to the nestParams list.
+ */
+ outerrelids = best_path->outerjoinpath->parent->relids;
+ nestParams = NIL;
+ prev = NULL;
+ for (cell = list_head(root->curOuterParams); cell; cell = next)
+ {
+ NestLoopParam *nlp = (NestLoopParam *) lfirst(cell);
+
+ next = lnext(cell);
+ if (bms_is_member(nlp->paramval->varno, outerrelids))
+ {
+ root->curOuterParams = list_delete_cell(root->curOuterParams,
+ cell, prev);
+ nestParams = lappend(nestParams, nlp);
+ }
+ else
+ prev = cell;
+ }
+
join_plan = make_nestloop(tlist,
joinclauses,
otherclauses,
+ nestParams,
outer_plan,
inner_plan,
best_path->jointype);
@@ -2016,12 +2110,72 @@ create_hashjoin_plan(PlannerInfo *root,
*****************************************************************************/
/*
+ * replace_nestloop_params
+ * Replace outer-relation Vars in the given expression with nestloop Params
+ *
+ * All Vars belonging to the relation(s) identified by root->curOuterRels
+ * are replaced by Params, and entries are added to root->curOuterParams if
+ * not already present.
+ */
+static Node *
+replace_nestloop_params(PlannerInfo *root, Node *expr)
+{
+ /* No setup needed for tree walk, so away we go */
+ return replace_nestloop_params_mutator(expr, root);
+}
+
+static Node *
+replace_nestloop_params_mutator(Node *node, PlannerInfo *root)
+{
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+ Param *param;
+ NestLoopParam *nlp;
+ ListCell *lc;
+
+ /* Upper-level Vars should be long gone at this point */
+ Assert(var->varlevelsup == 0);
+ /* If not to be replaced, we can just return the Var unmodified */
+ if (!bms_is_member(var->varno, root->curOuterRels))
+ return node;
+ /* Create a Param representing the Var */
+ param = assign_nestloop_param(root, var);
+ /* Is this param already listed in root->curOuterParams? */
+ foreach(lc, root->curOuterParams)
+ {
+ nlp = (NestLoopParam *) lfirst(lc);
+ if (nlp->paramno == param->paramid)
+ {
+ Assert(equal(var, nlp->paramval));
+ /* Present, so we can just return the Param */
+ return (Node *) param;
+ }
+ }
+ /* No, so add it */
+ nlp = makeNode(NestLoopParam);
+ nlp->paramno = param->paramid;
+ nlp->paramval = var;
+ root->curOuterParams = lappend(root->curOuterParams, nlp);
+ /* And return the replacement Param */
+ return (Node *) param;
+ }
+ return expression_tree_mutator(node,
+ replace_nestloop_params_mutator,
+ (void *) root);
+}
+
+/*
* fix_indexqual_references
* Adjust indexqual clauses to the form the executor's indexqual
* machinery needs.
*
- * We have three tasks here:
+ * We have four tasks here:
* * Remove RestrictInfo nodes from the input clauses.
+ * * Replace any outer-relation Var nodes with nestloop Params.
+ * (XXX eventually, that responsibility should go elsewhere?)
* * Index keys must be represented by Var nodes with varattno set to the
* index's attribute number, not the attribute number in the original rel.
* * If the index key is on the right, commute the clause to put it on the
@@ -2033,7 +2187,8 @@ create_hashjoin_plan(PlannerInfo *root,
* two separate copies of the subplan tree, or things will go awry).
*/
static List *
-fix_indexqual_references(List *indexquals, IndexPath *index_path)
+fix_indexqual_references(PlannerInfo *root, IndexPath *index_path,
+ List *indexquals)
{
IndexOptInfo *index = index_path->indexinfo;
List *fixed_indexquals;
@@ -2041,26 +2196,20 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path)
fixed_indexquals = NIL;
- /*
- * For each qual clause, commute if needed to put the indexkey operand on
- * the left, and then fix its varattno. (We do not need to change the
- * other side of the clause.)
- */
foreach(l, indexquals)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
- Expr *clause;
+ Node *clause;
Assert(IsA(rinfo, RestrictInfo));
/*
- * Make a copy that will become the fixed clause.
+ * Replace any outer-relation variables with nestloop params.
*
- * We used to try to do a shallow copy here, but that fails if there
- * is a subplan in the arguments of the opclause. So just do a full
- * copy.
+ * This also makes a copy of the clause, so it's safe to modify it
+ * in-place below.
*/
- clause = (Expr *) copyObject((Node *) rinfo->clause);
+ clause = replace_nestloop_params(root, (Node *) rinfo->clause);
if (IsA(clause, OpExpr))
{
@@ -2765,6 +2914,7 @@ static NestLoop *
make_nestloop(List *tlist,
List *joinclauses,
List *otherclauses,
+ List *nestParams,
Plan *lefttree,
Plan *righttree,
JoinType jointype)
@@ -2779,6 +2929,7 @@ make_nestloop(List *tlist,
plan->righttree = righttree;
node->join.jointype = jointype;
node->join.joinqual = joinclauses;
+ node->nestParams = nestParams;
return node;
}
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 70be2e66f2d..450970e5c37 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.160 2010/02/26 02:00:45 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.161 2010/07/12 17:01:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -89,8 +89,6 @@ static Node *fix_scan_expr(PlannerGlobal *glob, Node *node, int rtoffset);
static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
static void set_join_references(PlannerGlobal *glob, Join *join, int rtoffset);
-static void set_inner_join_references(PlannerGlobal *glob, Plan *inner_plan,
- indexed_tlist *outer_itlist);
static void set_upper_references(PlannerGlobal *glob, Plan *plan, int rtoffset);
static void set_dummy_tlist_references(Plan *plan, int rtoffset);
static indexed_tlist *build_tlist_index(List *tlist);
@@ -878,12 +876,11 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
Assert(var->varlevelsup == 0);
/*
- * We should not see any Vars marked INNER, but in a nestloop inner
- * scan there could be OUTER Vars. Leave them alone.
+ * We should not see any Vars marked INNER or OUTER.
*/
Assert(var->varno != INNER);
- if (var->varno > 0 && var->varno != OUTER)
- var->varno += context->rtoffset;
+ Assert(var->varno != OUTER);
+ var->varno += context->rtoffset;
if (var->varnoold > 0)
var->varnoold += context->rtoffset;
return (Node *) var;
@@ -927,10 +924,6 @@ fix_scan_expr_walker(Node *node, fix_scan_expr_context *context)
* values to the result domain number of either the corresponding outer
* or inner join tuple item. Also perform opcode lookup for these
* expressions. and add regclass OIDs to glob->relationOids.
- *
- * In the case of a nestloop with inner indexscan, we will also need to
- * apply the same transformation to any outer vars appearing in the
- * quals of the child indexscan. set_inner_join_references does that.
*/
static void
set_join_references(PlannerGlobal *glob, Join *join, int rtoffset)
@@ -966,8 +959,18 @@ set_join_references(PlannerGlobal *glob, Join *join, int rtoffset)
/* Now do join-type-specific stuff */
if (IsA(join, NestLoop))
{
- /* This processing is split out to handle possible recursion */
- set_inner_join_references(glob, inner_plan, outer_itlist);
+ NestLoop *nl = (NestLoop *) join;
+ ListCell *lc;
+
+ foreach(lc, nl->nestParams)
+ {
+ NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
+
+ nlp->paramval = (Var *) fix_upper_expr(glob,
+ (Node *) nlp->paramval,
+ outer_itlist,
+ rtoffset);
+ }
}
else if (IsA(join, MergeJoin))
{
@@ -997,193 +1000,6 @@ set_join_references(PlannerGlobal *glob, Join *join, int rtoffset)
}
/*
- * set_inner_join_references
- * Handle join references appearing in an inner indexscan's quals
- *
- * To handle bitmap-scan plan trees, we have to be able to recurse down
- * to the bottom BitmapIndexScan nodes; likewise, appendrel indexscans
- * require recursing through Append nodes. This is split out as a separate
- * function so that it can recurse.
- *
- * Note we do *not* apply any rtoffset for non-join Vars; this is because
- * the quals will be processed again by fix_scan_expr when the set_plan_refs
- * recursion reaches the inner indexscan, and so we'd have done it twice.
- */
-static void
-set_inner_join_references(PlannerGlobal *glob, Plan *inner_plan,
- indexed_tlist *outer_itlist)
-{
- if (IsA(inner_plan, IndexScan))
- {
- /*
- * An index is being used to reduce the number of tuples scanned in
- * the inner relation. If there are join clauses being used with the
- * index, we must update their outer-rel var nodes to refer to the
- * outer side of the join.
- */
- IndexScan *innerscan = (IndexScan *) inner_plan;
- List *indexqualorig = innerscan->indexqualorig;
-
- /* No work needed if indexqual refers only to its own rel... */
- if (NumRelids((Node *) indexqualorig) > 1)
- {
- Index innerrel = innerscan->scan.scanrelid;
-
- /* only refs to outer vars get changed in the inner qual */
- innerscan->indexqualorig = fix_join_expr(glob,
- indexqualorig,
- outer_itlist,
- NULL,
- innerrel,
- 0);
- innerscan->indexqual = fix_join_expr(glob,
- innerscan->indexqual,
- outer_itlist,
- NULL,
- innerrel,
- 0);
-
- /*
- * We must fix the inner qpqual too, if it has join clauses (this
- * could happen if special operators are involved: some indexquals
- * may get rechecked as qpquals).
- */
- if (NumRelids((Node *) inner_plan->qual) > 1)
- inner_plan->qual = fix_join_expr(glob,
- inner_plan->qual,
- outer_itlist,
- NULL,
- innerrel,
- 0);
- }
- }
- else if (IsA(inner_plan, BitmapIndexScan))
- {
- /*
- * Same, but index is being used within a bitmap plan.
- */
- BitmapIndexScan *innerscan = (BitmapIndexScan *) inner_plan;
- List *indexqualorig = innerscan->indexqualorig;
-
- /* No work needed if indexqual refers only to its own rel... */
- if (NumRelids((Node *) indexqualorig) > 1)
- {
- Index innerrel = innerscan->scan.scanrelid;
-
- /* only refs to outer vars get changed in the inner qual */
- innerscan->indexqualorig = fix_join_expr(glob,
- indexqualorig,
- outer_itlist,
- NULL,
- innerrel,
- 0);
- innerscan->indexqual = fix_join_expr(glob,
- innerscan->indexqual,
- outer_itlist,
- NULL,
- innerrel,
- 0);
- /* no need to fix inner qpqual */
- Assert(inner_plan->qual == NIL);
- }
- }
- else if (IsA(inner_plan, BitmapHeapScan))
- {
- /*
- * The inner side is a bitmap scan plan. Fix the top node, and
- * recurse to get the lower nodes.
- *
- * Note: create_bitmap_scan_plan removes clauses from bitmapqualorig
- * if they are duplicated in qpqual, so must test these independently.
- */
- BitmapHeapScan *innerscan = (BitmapHeapScan *) inner_plan;
- Index innerrel = innerscan->scan.scanrelid;
- List *bitmapqualorig = innerscan->bitmapqualorig;
-
- /* only refs to outer vars get changed in the inner qual */
- if (NumRelids((Node *) bitmapqualorig) > 1)
- innerscan->bitmapqualorig = fix_join_expr(glob,
- bitmapqualorig,
- outer_itlist,
- NULL,
- innerrel,
- 0);
-
- /*
- * We must fix the inner qpqual too, if it has join clauses (this
- * could happen if special operators are involved: some indexquals may
- * get rechecked as qpquals).
- */
- if (NumRelids((Node *) inner_plan->qual) > 1)
- inner_plan->qual = fix_join_expr(glob,
- inner_plan->qual,
- outer_itlist,
- NULL,
- innerrel,
- 0);
-
- /* Now recurse */
- set_inner_join_references(glob, inner_plan->lefttree, outer_itlist);
- }
- else if (IsA(inner_plan, BitmapAnd))
- {
- /* All we need do here is recurse */
- BitmapAnd *innerscan = (BitmapAnd *) inner_plan;
- ListCell *l;
-
- foreach(l, innerscan->bitmapplans)
- {
- set_inner_join_references(glob, (Plan *) lfirst(l), outer_itlist);
- }
- }
- else if (IsA(inner_plan, BitmapOr))
- {
- /* All we need do here is recurse */
- BitmapOr *innerscan = (BitmapOr *) inner_plan;
- ListCell *l;
-
- foreach(l, innerscan->bitmapplans)
- {
- set_inner_join_references(glob, (Plan *) lfirst(l), outer_itlist);
- }
- }
- else if (IsA(inner_plan, TidScan))
- {
- TidScan *innerscan = (TidScan *) inner_plan;
- Index innerrel = innerscan->scan.scanrelid;
-
- innerscan->tidquals = fix_join_expr(glob,
- innerscan->tidquals,
- outer_itlist,
- NULL,
- innerrel,
- 0);
- }
- else if (IsA(inner_plan, Append))
- {
- /*
- * The inner side is an append plan. Recurse to see if it contains
- * indexscans that need to be fixed.
- */
- Append *appendplan = (Append *) inner_plan;
- ListCell *l;
-
- foreach(l, appendplan->appendplans)
- {
- set_inner_join_references(glob, (Plan *) lfirst(l), outer_itlist);
- }
- }
- else if (IsA(inner_plan, Result))
- {
- /* Recurse through a gating Result node (similar to Append case) */
- Result *result = (Result *) inner_plan;
-
- if (result->plan.lefttree)
- set_inner_join_references(glob, result->plan.lefttree, outer_itlist);
- }
-}
-
-/*
* set_upper_references
* Update the targetlist and quals of an upper-level plan node
* to refer to the tuples returned by its lefttree subplan.
@@ -1532,23 +1348,21 @@ search_indexed_tlist_for_sortgroupref(Node *node,
*
* This is used in two different scenarios: a normal join clause, where
* all the Vars in the clause *must* be replaced by OUTER or INNER references;
- * and an indexscan being used on the inner side of a nestloop join.
- * In the latter case we want to replace the outer-relation Vars by OUTER
- * references, while Vars of the inner relation should be adjusted by rtoffset.
- * (We also implement RETURNING clause fixup using this second scenario.)
+ * and a RETURNING clause, which may contain both Vars of the target relation
+ * and Vars of other relations. In the latter case we want to replace the
+ * other-relation Vars by OUTER references, while leaving target Vars alone.
*
* For a normal join, acceptable_rel should be zero so that any failure to
- * match a Var will be reported as an error. For the indexscan case,
- * pass inner_itlist = NULL and acceptable_rel = the (not-offseted-yet) ID
- * of the inner relation.
+ * match a Var will be reported as an error. For the RETURNING case, pass
+ * inner_itlist = NULL and acceptable_rel = the ID of the target relation.
*
* 'clauses' is the targetlist or list of join clauses
* 'outer_itlist' is the indexed target list of the outer join relation
* 'inner_itlist' is the indexed target list of the inner join relation,
* or NULL
* 'acceptable_rel' is either zero or the rangetable index of a relation
- * whose Vars may appear in the clause without provoking an error.
- * 'rtoffset' is what to add to varno for Vars of acceptable_rel.
+ * whose Vars may appear in the clause without provoking an error
+ * 'rtoffset': how much to increment varnoold by
*
* Returns the new expression tree. The original clause structure is
* not modified.
@@ -1603,8 +1417,8 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
if (var->varno == context->acceptable_rel)
{
var = copyVar(var);
- var->varno += context->rtoffset;
- var->varnoold += context->rtoffset;
+ if (var->varnoold > 0)
+ var->varnoold += context->rtoffset;
return (Node *) var;
}
@@ -1783,7 +1597,7 @@ set_returning_clause_references(PlannerGlobal *glob,
/*
* We can perform the desired Var fixup by abusing the fix_join_expr
- * machinery that normally handles inner indexscan fixup. We search the
+ * machinery that formerly handled inner indexscan fixup. We search the
* top plan's targetlist for Vars of non-result relations, and use
* fix_join_expr to convert RETURNING Vars into references to those tlist
* entries, while leaving result-rel Vars as-is.
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index cf503d51135..b94419a09dc 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.162 2010/04/19 00:55:25 rhaas Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.163 2010/07/12 17:01:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -83,30 +83,20 @@ static bool finalize_primnode(Node *node, finalize_primnode_context *context);
/*
- * Generate a Param node to replace the given Var,
- * which is expected to have varlevelsup > 0 (ie, it is not local).
+ * Select a PARAM_EXEC number to identify the given Var.
+ * If the Var already has a param slot, return that one.
*/
-static Param *
-replace_outer_var(PlannerInfo *root, Var *var)
+static int
+assign_param_for_var(PlannerInfo *root, Var *var)
{
- Param *retval;
ListCell *ppl;
PlannerParamItem *pitem;
Index abslevel;
int i;
- Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level);
abslevel = root->query_level - var->varlevelsup;
- /*
- * If there's already a paramlist entry for this same Var, just use it.
- * NOTE: in sufficiently complex querytrees, it is possible for the same
- * varno/abslevel to refer to different RTEs in different parts of the
- * parsetree, so that different fields might end up sharing the same Param
- * number. As long as we check the vartype/typmod as well, I believe that
- * this sort of aliasing will cause no trouble. The correct field should
- * get stored into the Param slot at execution in each part of the tree.
- */
+ /* If there's already a paramlist entry for this same Var, just use it */
i = 0;
foreach(ppl, root->glob->paramlist)
{
@@ -119,24 +109,76 @@ replace_outer_var(PlannerInfo *root, Var *var)
pvar->varattno == var->varattno &&
pvar->vartype == var->vartype &&
pvar->vartypmod == var->vartypmod)
- break;
+ return i;
}
i++;
}
- if (!ppl)
- {
- /* Nope, so make a new one */
- var = (Var *) copyObject(var);
- var->varlevelsup = 0;
+ /* Nope, so make a new one */
+ var = (Var *) copyObject(var);
+ var->varlevelsup = 0;
- pitem = makeNode(PlannerParamItem);
- pitem->item = (Node *) var;
- pitem->abslevel = abslevel;
+ pitem = makeNode(PlannerParamItem);
+ pitem->item = (Node *) var;
+ pitem->abslevel = abslevel;
- root->glob->paramlist = lappend(root->glob->paramlist, pitem);
- /* i is already the correct index for the new item */
- }
+ root->glob->paramlist = lappend(root->glob->paramlist, pitem);
+
+ /* i is already the correct list index for the new item */
+ return i;
+}
+
+/*
+ * Generate a Param node to replace the given Var,
+ * which is expected to have varlevelsup > 0 (ie, it is not local).
+ */
+static Param *
+replace_outer_var(PlannerInfo *root, Var *var)
+{
+ Param *retval;
+ int i;
+
+ Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level);
+
+ /*
+ * Find the Var in root->glob->paramlist, or add it if not present.
+ *
+ * NOTE: in sufficiently complex querytrees, it is possible for the same
+ * varno/abslevel to refer to different RTEs in different parts of the
+ * parsetree, so that different fields might end up sharing the same Param
+ * number. As long as we check the vartype/typmod as well, I believe that
+ * this sort of aliasing will cause no trouble. The correct field should
+ * get stored into the Param slot at execution in each part of the tree.
+ */
+ i = assign_param_for_var(root, var);
+
+ retval = makeNode(Param);
+ retval->paramkind = PARAM_EXEC;
+ retval->paramid = i;
+ retval->paramtype = var->vartype;
+ retval->paramtypmod = var->vartypmod;
+ retval->location = -1;
+
+ return retval;
+}
+
+/*
+ * Generate a Param node to replace the given Var, which will be supplied
+ * from an upper NestLoop join node.
+ *
+ * Because we allow nestloop and subquery Params to alias each other,
+ * this is effectively the same as replace_outer_var, except that we expect
+ * the Var to be local to the current query level.
+ */
+Param *
+assign_nestloop_param(PlannerInfo *root, Var *var)
+{
+ Param *retval;
+ int i;
+
+ Assert(var->varlevelsup == 0);
+
+ i = assign_param_for_var(root, var);
retval = makeNode(Param);
retval->paramkind = PARAM_EXEC;
@@ -1773,8 +1815,9 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans)
*
* Note: this is a bit overly generous since some parameters of upper
* query levels might belong to query subtrees that don't include this
- * query. However, valid_params is only a debugging crosscheck, so it
- * doesn't seem worth expending lots of cycles to try to be exact.
+ * query, or might be nestloop params that won't be passed down at all.
+ * However, valid_params is only a debugging crosscheck, so it doesn't
+ * seem worth expending lots of cycles to try to be exact.
*/
valid_params = bms_copy(initSetParam);
paramid = 0;
@@ -1849,6 +1892,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
{
finalize_primnode_context context;
int locally_added_param;
+ Bitmapset *nestloop_params;
+ Bitmapset *child_params;
if (plan == NULL)
return NULL;
@@ -1856,6 +1901,7 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
context.root = root;
context.paramids = NULL; /* initialize set to empty */
locally_added_param = -1; /* there isn't one */
+ nestloop_params = NULL; /* there aren't any */
/*
* When we call finalize_primnode, context.paramids sets are automatically
@@ -2056,8 +2102,20 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
break;
case T_NestLoop:
- finalize_primnode((Node *) ((Join *) plan)->joinqual,
- &context);
+ {
+ ListCell *l;
+
+ finalize_primnode((Node *) ((Join *) plan)->joinqual,
+ &context);
+ /* collect set of params that will be passed to right child */
+ foreach(l, ((NestLoop *) plan)->nestParams)
+ {
+ NestLoopParam *nlp = (NestLoopParam *) lfirst(l);
+
+ nestloop_params = bms_add_member(nestloop_params,
+ nlp->paramno);
+ }
+ }
break;
case T_MergeJoin:
@@ -2120,17 +2178,32 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
}
/* Process left and right child plans, if any */
- context.paramids = bms_add_members(context.paramids,
- finalize_plan(root,
- plan->lefttree,
- valid_params,
- scan_params));
-
- context.paramids = bms_add_members(context.paramids,
- finalize_plan(root,
- plan->righttree,
- valid_params,
- scan_params));
+ child_params = finalize_plan(root,
+ plan->lefttree,
+ valid_params,
+ scan_params);
+ context.paramids = bms_add_members(context.paramids, child_params);
+
+ if (nestloop_params)
+ {
+ /* right child can reference nestloop_params as well as valid_params */
+ child_params = finalize_plan(root,
+ plan->righttree,
+ bms_union(nestloop_params, valid_params),
+ scan_params);
+ /* ... and they don't count as parameters used at my level */
+ child_params = bms_difference(child_params, nestloop_params);
+ bms_free(nestloop_params);
+ }
+ else
+ {
+ /* easy case */
+ child_params = finalize_plan(root,
+ plan->righttree,
+ valid_params,
+ scan_params);
+ }
+ context.paramids = bms_add_members(context.paramids, child_params);
/*
* Any locally generated parameter doesn't count towards its generating