indexscan-params-1.patch
text/x-patch
Filename: indexscan-params-1.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
| File | + | − |
|---|---|---|
| src/backend/executor/nodeBitmapIndexscan.c | 10 | 21 |
| src/backend/executor/nodeIndexscan.c | 16 | 35 |
| src/backend/executor/nodeNestloop.c | 26 | 6 |
| src/backend/nodes/copyfuncs.c | 22 | 0 |
| src/backend/nodes/outfuncs.c | 16 | 0 |
| src/backend/optimizer/plan/createplan.c | 175 | 24 |
| src/backend/optimizer/plan/setrefs.c | 25 | 211 |
| src/backend/optimizer/plan/subselect.c | 116 | 43 |
| src/include/nodes/nodes.h | 1 | 0 |
| src/include/nodes/plannodes.h | 13 | 0 |
| src/include/nodes/primnodes.h | 2 | 1 |
| src/include/nodes/relation.h | 19 | 5 |
| src/include/optimizer/subselect.h | 1 | 0 |
Index: src/backend/executor/nodeBitmapIndexscan.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/executor/nodeBitmapIndexscan.c,v
retrieving revision 1.33
diff -c -r1.33 nodeBitmapIndexscan.c
*** src/backend/executor/nodeBitmapIndexscan.c 2 Jan 2010 16:57:41 -0000 1.33
--- src/backend/executor/nodeBitmapIndexscan.c 12 Jul 2010 04:26:44 -0000
***************
*** 108,144 ****
/* ----------------------------------------------------------------
* ExecBitmapIndexReScan(node)
*
! * Recalculates the value of the scan keys whose value depends on
! * information known at runtime and rescans the indexed relation.
* ----------------------------------------------------------------
*/
void
ExecBitmapIndexReScan(BitmapIndexScanState *node, ExprContext *exprCtxt)
{
! ExprContext *econtext;
!
! econtext = node->biss_RuntimeContext; /* context for runtime keys */
if (econtext)
- {
- /*
- * If we are being passed an outer tuple, save it for runtime key
- * calc.
- */
- if (exprCtxt != NULL)
- econtext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
-
- /*
- * Reset the runtime-key context so we don't leak memory as each outer
- * tuple is scanned. Note this assumes that we will recalculate *all*
- * runtime keys on each call.
- */
ResetExprContext(econtext);
- }
/*
! * If we are doing runtime key calculations (ie, the index keys depend on
! * data from an outer scan), compute the new key values.
*
* Array keys are also treated as runtime keys; note that if we return
* with biss_RuntimeKeysReady still false, then there is an empty array
--- 108,133 ----
/* ----------------------------------------------------------------
* ExecBitmapIndexReScan(node)
*
! * Recalculates the values of any scan keys whose value depends on
! * information known at runtime, then rescans the indexed relation.
* ----------------------------------------------------------------
*/
void
ExecBitmapIndexReScan(BitmapIndexScanState *node, ExprContext *exprCtxt)
{
! ExprContext *econtext = node->biss_RuntimeContext;
+ /*
+ * Reset the runtime-key context so we don't leak memory as each outer
+ * tuple is scanned. Note this assumes that we will recalculate *all*
+ * runtime keys on each call.
+ */
if (econtext)
ResetExprContext(econtext);
/*
! * If we are doing runtime key calculations (ie, any of the index key
! * values weren't simple Consts), compute the new key values.
*
* Array keys are also treated as runtime keys; note that if we return
* with biss_RuntimeKeysReady still false, then there is an empty array
Index: src/backend/executor/nodeIndexscan.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v
retrieving revision 1.139
diff -c -r1.139 nodeIndexscan.c
*** src/backend/executor/nodeIndexscan.c 26 Feb 2010 02:00:42 -0000 1.139
--- src/backend/executor/nodeIndexscan.c 12 Jul 2010 04:26:44 -0000
***************
*** 151,158 ****
/* ----------------------------------------------------------------
* ExecIndexReScan(node)
*
! * Recalculates the value of the scan keys whose value depends on
! * information known at runtime and rescans the indexed relation.
* Updating the scan key was formerly done separately in
* ExecUpdateIndexScanKeys. Integrating it into ReScan makes
* rescans of indices and relations/general streams more uniform.
--- 151,159 ----
/* ----------------------------------------------------------------
* ExecIndexReScan(node)
*
! * Recalculates the values of any scan keys whose value depends on
! * information known at runtime, then rescans the indexed relation.
! *
* Updating the scan key was formerly done separately in
* ExecUpdateIndexScanKeys. Integrating it into ReScan makes
* rescans of indices and relations/general streams more uniform.
***************
*** 161,202 ****
void
ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
{
- ExprContext *econtext;
-
- econtext = node->iss_RuntimeContext; /* context for runtime keys */
-
- if (econtext)
- {
- /*
- * If we are being passed an outer tuple, save it for runtime key
- * calc. We also need to link it into the "regular" per-tuple
- * econtext, so it can be used during indexqualorig evaluations.
- */
- if (exprCtxt != NULL)
- {
- ExprContext *stdecontext;
-
- econtext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
- stdecontext = node->ss.ps.ps_ExprContext;
- stdecontext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
- }
-
- /*
- * Reset the runtime-key context so we don't leak memory as each outer
- * tuple is scanned. Note this assumes that we will recalculate *all*
- * runtime keys on each call.
- */
- ResetExprContext(econtext);
- }
-
/*
! * If we are doing runtime key calculations (ie, the index keys depend on
! * data from an outer scan), compute the new key values
*/
if (node->iss_NumRuntimeKeys != 0)
ExecIndexEvalRuntimeKeys(econtext,
node->iss_RuntimeKeys,
node->iss_NumRuntimeKeys);
node->iss_RuntimeKeysReady = true;
/* reset index scan */
--- 162,183 ----
void
ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
{
/*
! * If we are doing runtime key calculations (ie, any of the index key
! * values weren't simple Consts), compute the new key values. But first,
! * reset the context so we don't leak memory as each outer tuple is
! * scanned. Note this assumes that we will recalculate *all* runtime keys
! * on each call.
*/
if (node->iss_NumRuntimeKeys != 0)
+ {
+ ExprContext *econtext = node->iss_RuntimeContext;
+
+ ResetExprContext(econtext);
ExecIndexEvalRuntimeKeys(econtext,
node->iss_RuntimeKeys,
node->iss_NumRuntimeKeys);
+ }
node->iss_RuntimeKeysReady = true;
/* reset index scan */
***************
*** 229,239 ****
/*
* For each run-time key, extract the run-time expression and evaluate
! * it with respect to the current outer tuple. We then stick the
! * result into the proper scan key.
*
* Note: the result of the eval could be a pass-by-ref value that's
! * stored in the outer scan's tuple, not in
* econtext->ecxt_per_tuple_memory. We assume that the outer tuple
* will stay put throughout our scan. If this is wrong, we could copy
* the result into our context explicitly, but I think that's not
--- 210,220 ----
/*
* For each run-time key, extract the run-time expression and evaluate
! * it with respect to the current context. We then stick the result
! * into the proper scan key.
*
* Note: the result of the eval could be a pass-by-ref value that's
! * stored in some outer scan's tuple, not in
* econtext->ecxt_per_tuple_memory. We assume that the outer tuple
* will stay put throughout our scan. If this is wrong, we could copy
* the result into our context explicitly, but I think that's not
Index: src/backend/executor/nodeNestloop.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v
retrieving revision 1.55
diff -c -r1.55 nodeNestloop.c
*** src/backend/executor/nodeNestloop.c 2 Jan 2010 16:57:44 -0000 1.55
--- src/backend/executor/nodeNestloop.c 12 Jul 2010 04:26:44 -0000
***************
*** 59,64 ****
--- 59,65 ----
TupleTableSlot *
ExecNestLoop(NestLoopState *node)
{
+ NestLoop *nl;
PlanState *innerPlan;
PlanState *outerPlan;
TupleTableSlot *outerTupleSlot;
***************
*** 66,77 ****
--- 67,80 ----
List *joinqual;
List *otherqual;
ExprContext *econtext;
+ ListCell *lc;
/*
* get information from the node
*/
ENL1_printf("getting info from node");
+ nl = (NestLoop *) node->js.ps.plan;
joinqual = node->js.joinqual;
otherqual = node->js.ps.qual;
outerPlan = outerPlanState(node);
***************
*** 134,149 ****
node->nl_MatchedOuter = false;
/*
! * now rescan the inner plan
*/
! ENL1_printf("rescanning inner plan");
/*
! * The scan key of the inner plan might depend on the current
! * outer tuple (e.g. in index scans), that's why we pass our expr
! * context.
*/
! ExecReScan(innerPlan, econtext);
}
/*
--- 137,169 ----
node->nl_MatchedOuter = false;
/*
! * fetch the values of any outer Vars that must be passed to
! * the inner scan, and store them in the appropriate PARAM_EXEC
! * slots.
*/
! foreach(lc, nl->nestParams)
! {
! NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
! int paramno = nlp->paramno;
! ParamExecData *prm;
!
! prm = &(econtext->ecxt_param_exec_vals[paramno]);
! /* Param value should be an OUTER var */
! Assert(nlp->paramval->varno == OUTER);
! Assert(nlp->paramval->varattno > 0);
! prm->value = slot_getattr(outerTupleSlot,
! nlp->paramval->varattno,
! &(prm->isnull));
! /* Flag parameter value as changed */
! innerPlan->chgParam = bms_add_member(innerPlan->chgParam,
! paramno);
! }
/*
! * now rescan the inner plan
*/
! ENL1_printf("rescanning inner plan");
! ExecReScan(innerPlan, NULL);
}
/*
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.464
diff -c -r1.464 copyfuncs.c
*** src/backend/nodes/copyfuncs.c 26 Feb 2010 02:00:43 -0000 1.464
--- src/backend/nodes/copyfuncs.c 12 Jul 2010 04:26:44 -0000
***************
*** 564,569 ****
--- 564,574 ----
*/
CopyJoinFields((Join *) from, (Join *) newnode);
+ /*
+ * copy remainder of node
+ */
+ COPY_NODE_FIELD(nestParams);
+
return newnode;
}
***************
*** 845,850 ****
--- 850,869 ----
}
/*
+ * _copyNestLoopParam
+ */
+ static NestLoopParam *
+ _copyNestLoopParam(NestLoopParam *from)
+ {
+ NestLoopParam *newnode = makeNode(NestLoopParam);
+
+ COPY_SCALAR_FIELD(paramno);
+ COPY_NODE_FIELD(paramval);
+
+ return newnode;
+ }
+
+ /*
* _copyPlanRowMark
*/
static PlanRowMark *
***************
*** 3671,3676 ****
--- 3690,3698 ----
case T_Limit:
retval = _copyLimit(from);
break;
+ case T_NestLoopParam:
+ retval = _copyNestLoopParam(from);
+ break;
case T_PlanRowMark:
retval = _copyPlanRowMark(from);
break;
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.385
diff -c -r1.385 outfuncs.c
*** src/backend/nodes/outfuncs.c 30 Mar 2010 21:58:10 -0000 1.385
--- src/backend/nodes/outfuncs.c 12 Jul 2010 04:26:44 -0000
***************
*** 517,522 ****
--- 517,524 ----
WRITE_NODE_TYPE("NESTLOOP");
_outJoinPlanInfo(str, (Join *) node);
+
+ WRITE_NODE_FIELD(nestParams);
}
static void
***************
*** 749,754 ****
--- 751,765 ----
}
static void
+ _outNestLoopParam(StringInfo str, NestLoopParam *node)
+ {
+ WRITE_NODE_TYPE("NESTLOOPPARAM");
+
+ WRITE_INT_FIELD(paramno);
+ WRITE_NODE_FIELD(paramval);
+ }
+
+ static void
_outPlanRowMark(StringInfo str, PlanRowMark *node)
{
WRITE_NODE_TYPE("PLANROWMARK");
***************
*** 1565,1570 ****
--- 1576,1583 ----
WRITE_BOOL_FIELD(hasPseudoConstantQuals);
WRITE_BOOL_FIELD(hasRecursion);
WRITE_INT_FIELD(wt_param_id);
+ WRITE_BITMAPSET_FIELD(curOuterRels);
+ WRITE_NODE_FIELD(curOuterParams);
}
static void
***************
*** 2562,2567 ****
--- 2575,2583 ----
case T_Limit:
_outLimit(str, obj);
break;
+ case T_NestLoopParam:
+ _outNestLoopParam(str, obj);
+ break;
case T_PlanRowMark:
_outPlanRowMark(str, obj);
break;
Index: src/backend/optimizer/plan/createplan.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v
retrieving revision 1.275
diff -c -r1.275 createplan.c
*** src/backend/optimizer/plan/createplan.c 25 May 2010 17:44:41 -0000 1.275
--- src/backend/optimizer/plan/createplan.c 12 Jul 2010 04:26:44 -0000
***************
*** 28,33 ****
--- 28,34 ----
#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,40 ****
--- 36,42 ----
#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,78 ****
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 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);
--- 74,83 ----
Plan *outer_plan, Plan *inner_plan);
static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path,
Plan *outer_plan, Plan *inner_plan);
! 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,109 ****
static BitmapAnd *make_bitmap_and(List *bitmapplans);
static BitmapOr *make_bitmap_or(List *bitmapplans);
static NestLoop *make_nestloop(List *tlist,
! List *joinclauses, List *otherclauses,
Plan *lefttree, Plan *righttree,
JoinType jointype);
static HashJoin *make_hashjoin(List *tlist,
--- 108,114 ----
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 *nestParams,
Plan *lefttree, Plan *righttree,
JoinType jointype);
static HashJoin *make_hashjoin(List *tlist,
***************
*** 133,140 ****
/*
* 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
* every pathnode found, we create a corresponding plan node containing
* appropriate id, target list, and qualification information.
*
--- 138,145 ----
/*
* create_plan
! * 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,156 ****
--- 156,184 ----
{
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,485 ****
Plan *outer_plan;
Plan *inner_plan;
Plan *plan;
! outer_plan = create_plan(root, best_path->outerjoinpath);
! inner_plan = create_plan(root, best_path->innerjoinpath);
switch (best_path->path.pathtype)
{
--- 505,520 ----
Plan *outer_plan;
Plan *inner_plan;
Plan *plan;
+ Relids saveOuterRels = root->curOuterRels;
! outer_plan = create_plan_recurse(root, best_path->outerjoinpath);
!
! /* 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,501 ****
--- 531,540 ----
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,577 ****
{
Path *subpath = (Path *) lfirst(subpaths);
! subplans = lappend(subplans, create_plan(root, subpath));
}
plan = make_append(subplans, tlist);
--- 610,616 ----
{
Path *subpath = (Path *) lfirst(subpaths);
! subplans = lappend(subplans, create_plan_recurse(root, subpath));
}
plan = make_append(subplans, tlist);
***************
*** 616,622 ****
Material *plan;
Plan *subplan;
! subplan = create_plan(root, best_path->subpath);
/* We don't want any excess columns in the materialized tuples */
disuse_physical_tlist(subplan, best_path->subpath);
--- 655,661 ----
Material *plan;
Plan *subplan;
! 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,656 ****
int groupColPos;
ListCell *l;
! subplan = create_plan(root, best_path->subpath);
/* Done if we don't need to do any actual unique-ifying */
if (best_path->umethod == UNIQUE_PATH_NOOP)
--- 689,695 ----
int groupColPos;
ListCell *l;
! 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,910 ****
* 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);
/*
* If this is an innerjoin scan, the indexclauses will contain join
--- 943,949 ----
* 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(root, best_path, indexquals);
/*
* If this is an innerjoin scan, the indexclauses will contain join
***************
*** 975,980 ****
--- 1014,1035 ----
/* 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,1272 ****
--- 1322,1338 ----
*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,1582 ****
Plan *outer_plan,
Plan *inner_plan)
{
List *tlist = build_relation_tlist(best_path->path.parent);
List *joinrestrictclauses = best_path->joinrestrictinfo;
List *joinclauses;
List *otherclauses;
! NestLoop *join_plan;
/*
* If the inner path is a nestloop inner indexscan, it might be using some
--- 1638,1653 ----
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;
! 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,1613 ****
--- 1676,1707 ----
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,2027 ****
*****************************************************************************/
/*
* fix_indexqual_references
* Adjust indexqual clauses to the form the executor's indexqual
* machinery needs.
*
! * We have three tasks here:
* * Remove RestrictInfo nodes from the input clauses.
* * 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
--- 2110,2181 ----
*****************************************************************************/
/*
+ * 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 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,2039 ****
* two separate copies of the subplan tree, or things will go awry).
*/
static List *
! fix_indexqual_references(List *indexquals, IndexPath *index_path)
{
IndexOptInfo *index = index_path->indexinfo;
List *fixed_indexquals;
--- 2187,2194 ----
* two separate copies of the subplan tree, or things will go awry).
*/
static List *
! fix_indexqual_references(PlannerInfo *root, IndexPath *index_path,
! List *indexquals)
{
IndexOptInfo *index = index_path->indexinfo;
List *fixed_indexquals;
***************
*** 2041,2066 ****
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;
Assert(IsA(rinfo, RestrictInfo));
/*
! * Make a copy that will become the fixed clause.
*
! * 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.
*/
! clause = (Expr *) copyObject((Node *) rinfo->clause);
if (IsA(clause, OpExpr))
{
--- 2196,2215 ----
fixed_indexquals = NIL;
foreach(l, indexquals)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
! Node *clause;
Assert(IsA(rinfo, RestrictInfo));
/*
! * Replace any outer-relation variables with nestloop params.
*
! * This also makes a copy of the clause, so it's safe to modify it
! * in-place below.
*/
! clause = replace_nestloop_params(root, (Node *) rinfo->clause);
if (IsA(clause, OpExpr))
{
***************
*** 2765,2770 ****
--- 2914,2920 ----
make_nestloop(List *tlist,
List *joinclauses,
List *otherclauses,
+ List *nestParams,
Plan *lefttree,
Plan *righttree,
JoinType jointype)
***************
*** 2779,2784 ****
--- 2929,2935 ----
plan->righttree = righttree;
node->join.jointype = jointype;
node->join.joinqual = joinclauses;
+ node->nestParams = nestParams;
return node;
}
Index: src/backend/optimizer/plan/setrefs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v
retrieving revision 1.160
diff -c -r1.160 setrefs.c
*** src/backend/optimizer/plan/setrefs.c 26 Feb 2010 02:00:45 -0000 1.160
--- src/backend/optimizer/plan/setrefs.c 12 Jul 2010 04:26:44 -0000
***************
*** 89,96 ****
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);
--- 89,94 ----
***************
*** 878,889 ****
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.
*/
Assert(var->varno != INNER);
! if (var->varno > 0 && var->varno != OUTER)
! var->varno += context->rtoffset;
if (var->varnoold > 0)
var->varnoold += context->rtoffset;
return (Node *) var;
--- 876,886 ----
Assert(var->varlevelsup == 0);
/*
! * We should not see any Vars marked INNER or OUTER.
*/
Assert(var->varno != INNER);
! Assert(var->varno != OUTER);
! var->varno += context->rtoffset;
if (var->varnoold > 0)
var->varnoold += context->rtoffset;
return (Node *) var;
***************
*** 927,936 ****
* 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)
--- 924,929 ----
***************
*** 966,973 ****
/* 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);
}
else if (IsA(join, MergeJoin))
{
--- 959,976 ----
/* Now do join-type-specific stuff */
if (IsA(join, NestLoop))
{
! 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,1189 ****
}
/*
- * 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.
--- 1000,1005 ----
***************
*** 1532,1554 ****
*
* 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.)
*
* 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.
*
* '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.
*
* Returns the new expression tree. The original clause structure is
* not modified.
--- 1348,1368 ----
*
* 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 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 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': how much to increment varnoold by
*
* Returns the new expression tree. The original clause structure is
* not modified.
***************
*** 1603,1610 ****
if (var->varno == context->acceptable_rel)
{
var = copyVar(var);
! var->varno += context->rtoffset;
! var->varnoold += context->rtoffset;
return (Node *) var;
}
--- 1417,1424 ----
if (var->varno == context->acceptable_rel)
{
var = copyVar(var);
! if (var->varnoold > 0)
! var->varnoold += context->rtoffset;
return (Node *) var;
}
***************
*** 1783,1789 ****
/*
* We can perform the desired Var fixup by abusing the fix_join_expr
! * machinery that normally handles 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.
--- 1597,1603 ----
/*
* We can perform the desired Var fixup by abusing the fix_join_expr
! * 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.
Index: src/backend/optimizer/plan/subselect.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v
retrieving revision 1.162
diff -c -r1.162 subselect.c
*** src/backend/optimizer/plan/subselect.c 19 Apr 2010 00:55:25 -0000 1.162
--- src/backend/optimizer/plan/subselect.c 12 Jul 2010 04:26:44 -0000
***************
*** 83,112 ****
/*
! * 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;
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.
! */
i = 0;
foreach(ppl, root->glob->paramlist)
{
--- 83,102 ----
/*
! * Select a PARAM_EXEC number to identify the given Var.
! * If the Var already has a param slot, return that one.
*/
! static int
! assign_param_for_var(PlannerInfo *root, Var *var)
{
ListCell *ppl;
PlannerParamItem *pitem;
Index abslevel;
int i;
abslevel = root->query_level - var->varlevelsup;
! /* If there's already a paramlist entry for this same Var, just use it */
i = 0;
foreach(ppl, root->glob->paramlist)
{
***************
*** 119,142 ****
pvar->varattno == var->varattno &&
pvar->vartype == var->vartype &&
pvar->vartypmod == var->vartypmod)
! break;
}
i++;
}
! if (!ppl)
! {
! /* Nope, so make a new one */
! var = (Var *) copyObject(var);
! var->varlevelsup = 0;
!
! 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 */
! }
retval = makeNode(Param);
retval->paramkind = PARAM_EXEC;
--- 109,184 ----
pvar->varattno == var->varattno &&
pvar->vartype == var->vartype &&
pvar->vartypmod == var->vartypmod)
! return i;
}
i++;
}
! /* Nope, so make a new one */
! var = (Var *) copyObject(var);
! var->varlevelsup = 0;
! pitem = makeNode(PlannerParamItem);
! pitem->item = (Node *) var;
! pitem->abslevel = abslevel;
!
! 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,1780 ****
*
* 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.
*/
valid_params = bms_copy(initSetParam);
paramid = 0;
--- 1815,1823 ----
*
* 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, 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,1854 ****
--- 1892,1899 ----
{
finalize_primnode_context context;
int locally_added_param;
+ Bitmapset *nestloop_params;
+ Bitmapset *child_params;
if (plan == NULL)
return NULL;
***************
*** 1856,1861 ****
--- 1901,1907 ----
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,2063 ****
break;
case T_NestLoop:
! finalize_primnode((Node *) ((Join *) plan)->joinqual,
! &context);
break;
case T_MergeJoin:
--- 2102,2121 ----
break;
case T_NestLoop:
! {
! 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,2136 ****
}
/* 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));
/*
* Any locally generated parameter doesn't count towards its generating
--- 2178,2209 ----
}
/* Process left and right child plans, if any */
! 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
Index: src/include/nodes/nodes.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/nodes.h,v
retrieving revision 1.234
diff -c -r1.234 nodes.h
*** src/include/nodes/nodes.h 28 Mar 2010 22:59:33 -0000 1.234
--- src/include/nodes/nodes.h 12 Jul 2010 04:26:44 -0000
***************
*** 74,79 ****
--- 74,80 ----
T_LockRows,
T_Limit,
/* these aren't subclasses of Plan: */
+ T_NestLoopParam,
T_PlanRowMark,
T_PlanInvalItem,
Index: src/include/nodes/plannodes.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/plannodes.h,v
retrieving revision 1.117
diff -c -r1.117 plannodes.h
*** src/include/nodes/plannodes.h 26 Feb 2010 02:01:25 -0000 1.117
--- src/include/nodes/plannodes.h 12 Jul 2010 04:26:44 -0000
***************
*** 432,444 ****
--- 432,457 ----
/* ----------------
* nest loop join node
+ *
+ * The nestParams list identifies any executor Params that must be passed
+ * into execution of the inner subplan carrying values from the current row
+ * of the outer subplan. Currently we restrict these values to be simple
+ * Vars, but perhaps someday that'd be worth relaxing.
* ----------------
*/
typedef struct NestLoop
{
Join join;
+ List *nestParams; /* list of NestLoopParam nodes */
} NestLoop;
+ typedef struct NestLoopParam
+ {
+ NodeTag type;
+ int paramno; /* number of the PARAM_EXEC Param to set */
+ Var *paramval; /* outer-relation Var to assign to Param */
+ } NestLoopParam;
+
/* ----------------
* merge join node
*
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/primnodes.h,v
retrieving revision 1.156
diff -c -r1.156 primnodes.h
*** src/include/nodes/primnodes.h 26 Feb 2010 02:01:25 -0000 1.156
--- src/include/nodes/primnodes.h 12 Jul 2010 04:26:44 -0000
***************
*** 175,181 ****
* Such parameters are numbered from 1 to n.
*
* PARAM_EXEC: The parameter is an internal executor parameter, used
! * for passing values into and out of sub-queries.
* For historical reasons, such parameters are numbered from 0.
* These numbers are independent of PARAM_EXTERN numbers.
*
--- 175,182 ----
* Such parameters are numbered from 1 to n.
*
* PARAM_EXEC: The parameter is an internal executor parameter, used
! * for passing values into and out of sub-queries or from
! * nestloop joins to their inner scans.
* For historical reasons, such parameters are numbered from 0.
* These numbers are independent of PARAM_EXTERN numbers.
*
Index: src/include/nodes/relation.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/relation.h,v
retrieving revision 1.187
diff -c -r1.187 relation.h
*** src/include/nodes/relation.h 6 Jul 2010 19:19:00 -0000 1.187
--- src/include/nodes/relation.h 12 Jul 2010 04:26:45 -0000
***************
*** 209,214 ****
--- 209,218 ----
int wt_param_id; /* PARAM_EXEC ID for the work table */
struct Plan *non_recursive_plan; /* plan for non-recursive term */
+ /* These fields are workspace for createplan.c */
+ Relids curOuterRels; /* outer rels above current node */
+ List *curOuterParams; /* not-yet-assigned NestLoopParams */
+
/* optional private data for join_search_hook, e.g., GEQO */
void *join_search_private;
} PlannerInfo;
***************
*** 1330,1345 ****
/*
* glob->paramlist keeps track of the PARAM_EXEC slots that we have decided
* we need for the query. At runtime these slots are used to pass values
! * either down into subqueries (for outer references in subqueries) or up out
! * of subqueries (for the results of a subplan). The n'th entry in the list
! * (n counts from 0) corresponds to Param->paramid = n.
*
* Each paramlist item shows the absolute query level it is associated with,
* where the outermost query is level 1 and nested subqueries have higher
* numbers. The item the parameter slot represents can be one of three kinds:
*
* A Var: the slot represents a variable of that level that must be passed
! * down because subqueries have outer references to it. The varlevelsup
* value in the Var will always be zero.
*
* An Aggref (with an expression tree representing its argument): the slot
--- 1334,1353 ----
/*
* glob->paramlist keeps track of the PARAM_EXEC slots that we have decided
* we need for the query. At runtime these slots are used to pass values
! * around from one plan node to another. They can be used to pass values
! * down into subqueries (for outer references in subqueries), or up out of
! * subqueries (for the results of a subplan), or from a NestLoop plan node
! * into its inner relation (when the inner scan is parameterized with values
! * from the outer relation). The n'th entry in the list (n counts from 0)
! * corresponds to Param->paramid = n.
*
* Each paramlist item shows the absolute query level it is associated with,
* where the outermost query is level 1 and nested subqueries have higher
* numbers. The item the parameter slot represents can be one of three kinds:
*
* A Var: the slot represents a variable of that level that must be passed
! * down because subqueries have outer references to it, or must be passed
! * from a NestLoop node of that level to its inner scan. The varlevelsup
* value in the Var will always be zero.
*
* An Aggref (with an expression tree representing its argument): the slot
***************
*** 1352,1358 ****
* to the parent query of the subplan.
*
* Note: we detect duplicate Var parameters and coalesce them into one slot,
! * but we do not do this for Aggref or Param slots.
*/
typedef struct PlannerParamItem
{
--- 1360,1372 ----
* to the parent query of the subplan.
*
* Note: we detect duplicate Var parameters and coalesce them into one slot,
! * but we do not bother to do this for Aggrefs, and it would be incorrect
! * to do so for Param slots. Duplicate detection is actually *necessary*
! * in the case of NestLoop parameters since it serves to match up the usage
! * of a Param (in the inner scan) with the assignment of the value (in the
! * NestLoop node). This might result in the same PARAM_EXEC slot being used
! * by multiple NestLoop nodes or SubPlan nodes, but no harm is done since
! * the same value would be assigned anyway.
*/
typedef struct PlannerParamItem
{
Index: src/include/optimizer/subselect.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/optimizer/subselect.h,v
retrieving revision 1.39
diff -c -r1.39 subselect.h
*** src/include/optimizer/subselect.h 2 Jan 2010 16:58:07 -0000 1.39
--- src/include/optimizer/subselect.h 12 Jul 2010 04:26:45 -0000
***************
*** 29,34 ****
--- 29,35 ----
bool attach_initplans);
extern Param *SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
Oid resulttype, int32 resulttypmod);
+ extern Param *assign_nestloop_param(PlannerInfo *root, Var *var);
extern int SS_assign_special_param(PlannerInfo *root);
#endif /* SUBSELECT_H */