WIP patch: pass outer-relation Vars as parameters to indexscans - Mailing list pgsql-hackers

From Tom Lane
Subject WIP patch: pass outer-relation Vars as parameters to indexscans
Date
Msg-id 16864.1278910450@sss.pgh.pa.us
Whole thread Raw
List pgsql-hackers
As per previous discussions, something I want to do for 9.1 is generalize
the notion of "inner indexscan" so that nestloop joins can pass parameters
down more than one level before they reach the scan where they're needed.
To do that, we've got to get rid of the current ad-hoc mechanism that
passes the nestloop's outer tuple to the ReScan function of the inner
indexscan.  Instead, we can use the executor's general-purpose PARAM_EXEC
mechanism.

Attached is a WIP patch that implements this first step.  It compiles and
passes regression tests, but there are a couple of loose ends yet:

* I haven't actually removed the second parameter of ExecReScan and
subsidiary routines.  That will enlarge the patch quite a bit without
making much difference for reviewing or performance testing, so I left
it out of this version.

* EXPLAIN now prints the passed-in values as $0, $1, etc, which is a
considerable step backwards in plan readability.  I have some ideas on
fixing that, but haven't tried to do anything about it yet.

I believe that the patch is approximately a wash for performance in
its current form: there's a bit of extra work in nodeNestloop.c to
extract values from the current outer tuple, but that should be largely
bought back during the uses of the values in the inner scan, because
ExecEvalParam is cheaper than ExecEvalVar.  I haven't tried to demonstrate
that by performance testing, though.

Any comments?  Anyone want to test it before it gets committed?

            regards, tom lane

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 */

pgsql-hackers by date:

Previous
From: Itagaki Takahiro
Date:
Subject: Re: multibyte charater set in levenshtein function
Next
From: KaiGai Kohei
Date:
Subject: Re: [v9.1] Add security hook on initialization of instance