static Node *build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
+             List *plan_params,
              SubLinkType subLinkType, Node *testexpr,
              bool adjust_testexpr, bool unknownEqFalse);
 static List *generate_subquery_params(PlannerInfo *root, List *tlist,
 
 
 /*
- * Select a PARAM_EXEC number to identify the given Var.
- * If the Var already has a param slot, return that one.
+ * Select a PARAM_EXEC number to identify the given Var as a parameter for
+ * the current subquery, or for a nestloop's inner scan.
+ * If the Var already has a param in the current context, return that one.
  */
 static int
 assign_param_for_var(PlannerInfo *root, Var *var)
 {
    ListCell   *ppl;
    PlannerParamItem *pitem;
-   Index       abslevel;
-   int         i;
+   Index       levelsup;
 
-   abslevel = root->query_level - var->varlevelsup;
+   /* Find the query level the Var belongs to */
+   for (levelsup = var->varlevelsup; levelsup > 0; levelsup--)
+       root = root->parent_root;
 
-   /* If there's already a paramlist entry for this same Var, just use it */
-   i = 0;
-   foreach(ppl, root->glob->paramlist)
+   /* If there's already a matching PlannerParamItem there, just use it */
+   foreach(ppl, root->plan_params)
    {
        pitem = (PlannerParamItem *) lfirst(ppl);
-       if (pitem->abslevel == abslevel && IsA(pitem->item, Var))
+       if (IsA(pitem->item, Var))
        {
            Var        *pvar = (Var *) pitem->item;
 
+           /*
+            * This comparison must match _equalVar(), except for ignoring
+            * varlevelsup.  Note that _equalVar() ignores the location.
+            */
            if (pvar->varno == var->varno &&
                pvar->varattno == var->varattno &&
                pvar->vartype == var->vartype &&
-               pvar->vartypmod == var->vartypmod)
-               return i;
+               pvar->vartypmod == var->vartypmod &&
+               pvar->varcollid == var->varcollid &&
+               pvar->varnoold == var->varnoold &&
+               pvar->varoattno == var->varoattno)
+               return pitem->paramId;
        }
-       i++;
    }
 
    /* Nope, so make a new one */
 
    pitem = makeNode(PlannerParamItem);
    pitem->item = (Node *) var;
-   pitem->abslevel = abslevel;
+   pitem->paramId = root->glob->nParamExec++;
 
-   root->glob->paramlist = lappend(root->glob->paramlist, pitem);
+   root->plan_params = lappend(root->plan_params, pitem);
 
-   /* i is already the correct list index for the new item */
-   return i;
+   return pitem->paramId;
 }
 
 /*
 
    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.
-    */
+   /* Find the Var in the appropriate plan_params, or add it if not present */
    i = assign_param_for_var(root, var);
 
    retval = makeNode(Param);
    retval->paramtype = var->vartype;
    retval->paramtypmod = var->vartypmod;
    retval->paramcollid = var->varcollid;
-   retval->location = -1;
+   retval->location = var->location;
 
    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
+ * This is effectively the same as replace_outer_var, except that we expect
  * the Var to be local to the current query level.
  */
 Param *
    retval->paramtype = var->vartype;
    retval->paramtypmod = var->vartypmod;
    retval->paramcollid = var->varcollid;
-   retval->location = -1;
+   retval->location = var->location;
 
    return retval;
 }
 
 /*
- * Select a PARAM_EXEC number to identify the given PlaceHolderVar.
- * If the PlaceHolderVar already has a param slot, return that one.
+ * Select a PARAM_EXEC number to identify the given PlaceHolderVar as a
+ * parameter for the current subquery, or for a nestloop's inner scan.
+ * If the PHV already has a param in the current context, return that one.
  *
  * This is just like assign_param_for_var, except for PlaceHolderVars.
  */
 {
    ListCell   *ppl;
    PlannerParamItem *pitem;
-   Index       abslevel;
-   int         i;
+   Index       levelsup;
 
-   abslevel = root->query_level - phv->phlevelsup;
+   /* Find the query level the PHV belongs to */
+   for (levelsup = phv->phlevelsup; levelsup > 0; levelsup--)
+       root = root->parent_root;
 
-   /* If there's already a paramlist entry for this same PHV, just use it */
-   i = 0;
-   foreach(ppl, root->glob->paramlist)
+   /* If there's already a matching PlannerParamItem there, just use it */
+   foreach(ppl, root->plan_params)
    {
        pitem = (PlannerParamItem *) lfirst(ppl);
-       if (pitem->abslevel == abslevel && IsA(pitem->item, PlaceHolderVar))
+       if (IsA(pitem->item, PlaceHolderVar))
        {
            PlaceHolderVar *pphv = (PlaceHolderVar *) pitem->item;
 
            /* We assume comparing the PHIDs is sufficient */
            if (pphv->phid == phv->phid)
-               return i;
+               return pitem->paramId;
        }
-       i++;
    }
 
    /* Nope, so make a new one */
 
    pitem = makeNode(PlannerParamItem);
    pitem->item = (Node *) phv;
-   pitem->abslevel = abslevel;
+   pitem->paramId = root->glob->nParamExec++;
 
-   root->glob->paramlist = lappend(root->glob->paramlist, pitem);
+   root->plan_params = lappend(root->plan_params, pitem);
 
-   /* i is already the correct list index for the new item */
-   return i;
+   return pitem->paramId;
 }
 
 /*
 
    Assert(phv->phlevelsup > 0 && phv->phlevelsup < root->query_level);
 
-   /*
-    * Find the PlaceHolderVar in root->glob->paramlist, or add it if not
-    * present.
-    */
+   /* Find the PHV in the appropriate plan_params, or add it if not present */
    i = assign_param_for_placeholdervar(root, phv);
 
    retval = makeNode(Param);
 {
    Param      *retval;
    PlannerParamItem *pitem;
-   Index       abslevel;
-   int         i;
+   Index       levelsup;
 
    Assert(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level);
-   abslevel = root->query_level - agg->agglevelsup;
+
+   /* Find the query level the Aggref belongs to */
+   for (levelsup = agg->agglevelsup; levelsup > 0; levelsup--)
+       root = root->parent_root;
 
    /*
     * It does not seem worthwhile to try to match duplicate outer aggs. Just
 
    pitem = makeNode(PlannerParamItem);
    pitem->item = (Node *) agg;
-   pitem->abslevel = abslevel;
+   pitem->paramId = root->glob->nParamExec++;
 
-   root->glob->paramlist = lappend(root->glob->paramlist, pitem);
-   i = list_length(root->glob->paramlist) - 1;
+   root->plan_params = lappend(root->plan_params, pitem);
 
    retval = makeNode(Param);
    retval->paramkind = PARAM_EXEC;
-   retval->paramid = i;
+   retval->paramid = pitem->paramId;
    retval->paramtype = agg->aggtype;
    retval->paramtypmod = -1;
    retval->paramcollid = agg->aggcollid;
-   retval->location = -1;
+   retval->location = agg->location;
 
    return retval;
 }
 /*
  * Generate a new Param node that will not conflict with any other.
  *
- * This is used to allocate PARAM_EXEC slots for subplan outputs.
+ * This is used to create Params representing subplan outputs.
+ * We don't need to build a PlannerParamItem for such a Param, but we do
+ * need to record the PARAM_EXEC slot number as being allocated.
  */
 static Param *
 generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod,
                   Oid paramcollation)
 {
    Param      *retval;
-   PlannerParamItem *pitem;
 
    retval = makeNode(Param);
    retval->paramkind = PARAM_EXEC;
-   retval->paramid = list_length(root->glob->paramlist);
+   retval->paramid = root->glob->nParamExec++;
    retval->paramtype = paramtype;
    retval->paramtypmod = paramtypmod;
    retval->paramcollid = paramcollation;
    retval->location = -1;
 
-   pitem = makeNode(PlannerParamItem);
-   pitem->item = (Node *) retval;
-   pitem->abslevel = root->query_level;
-
-   root->glob->paramlist = lappend(root->glob->paramlist, pitem);
-
    return retval;
 }
 
  * is not actually used to carry a value at runtime).  Such parameters are
  * used for special runtime signaling purposes, such as connecting a
  * recursive union node to its worktable scan node or forcing plan
- * re-evaluation within the EvalPlanQual mechanism.
+ * re-evaluation within the EvalPlanQual mechanism.  No actual Param node
+ * exists with this ID, however.
  */
 int
 SS_assign_special_param(PlannerInfo *root)
 {
-   Param      *param;
-
-   /* We generate a Param of datatype INTERNAL */
-   param = generate_new_param(root, INTERNALOID, -1, InvalidOid);
-   /* ... but the caller only cares about its ID */
-   return param->paramid;
+   return root->glob->nParamExec++;
 }
 
 /*
    double      tuple_fraction;
    Plan       *plan;
    PlannerInfo *subroot;
+   List       *plan_params;
    Node       *result;
 
    /*
    else
        tuple_fraction = 0.0;   /* default behavior */
 
+   /* plan_params should not be in use in current query level */
+   Assert(root->plan_params == NIL);
+
    /*
     * Generate the plan for the subquery.
     */
                            false, tuple_fraction,
                            &subroot);
 
+   /* Isolate the params needed by this specific subplan */
+   plan_params = root->plan_params;
+   root->plan_params = NIL;
+
    /* And convert to SubPlan or InitPlan format. */
-   result = build_subplan(root, plan, subroot,
+   result = build_subplan(root, plan, subroot, plan_params,
                           subLinkType, testexpr, true, isTopQual);
 
    /*
                                    false, 0.0,
                                    &subroot);
 
+           /* Isolate the params needed by this specific subplan */
+           plan_params = root->plan_params;
+           root->plan_params = NIL;
+
            /* Now we can check if it'll fit in work_mem */
            if (subplan_is_hashable(plan))
            {
 
                /* OK, convert to SubPlan format. */
                hashplan = (SubPlan *) build_subplan(root, plan, subroot,
+                                                    plan_params,
                                                     ANY_SUBLINK, newtestexpr,
                                                     false, true);
                /* Check we got what we expected */
  */
 static Node *
 build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
+             List *plan_params,
              SubLinkType subLinkType, Node *testexpr,
              bool adjust_testexpr, bool unknownEqFalse)
 {
    Node       *result;
    SubPlan    *splan;
    bool        isInitPlan;
-   Bitmapset  *tmpset;
-   int         paramid;
+   ListCell   *lc;
 
    /*
     * Initialize the SubPlan node.  Note plan_id, plan_name, and cost fields
     * Make parParam and args lists of param IDs and expressions that current
     * query level will pass to this child plan.
     */
-   tmpset = bms_copy(plan->extParam);
-   while ((paramid = bms_first_member(tmpset)) >= 0)
+   foreach(lc, plan_params)
    {
-       PlannerParamItem *pitem = list_nth(root->glob->paramlist, paramid);
-
-       if (pitem->abslevel == root->query_level)
-       {
-           Node       *arg;
-
-           /*
-            * The Var, PlaceHolderVar, or Aggref has already been adjusted to
-            * have the correct varlevelsup, phlevelsup, or agglevelsup.  We
-            * probably don't even need to copy it again, but be safe.
-            */
-           arg = copyObject(pitem->item);
+       PlannerParamItem *pitem = (PlannerParamItem *) lfirst(lc);
+       Node       *arg = pitem->item;
 
-           /*
-            * If it's a PlaceHolderVar or Aggref, its arguments might contain
-            * SubLinks, which have not yet been processed (see the comments
-            * for SS_replace_correlation_vars).  Do that now.
-            */
-           if (IsA(arg, PlaceHolderVar) ||
-               IsA(arg, Aggref))
-               arg = SS_process_sublinks(root, arg, false);
+       /*
+        * The Var, PlaceHolderVar, or Aggref has already been adjusted to
+        * have the correct varlevelsup, phlevelsup, or agglevelsup.
+        *
+        * If it's a PlaceHolderVar or Aggref, its arguments might contain
+        * SubLinks, which have not yet been processed (see the comments for
+        * SS_replace_correlation_vars).  Do that now.
+        */
+       if (IsA(arg, PlaceHolderVar) ||
+           IsA(arg, Aggref))
+           arg = SS_process_sublinks(root, arg, false);
 
-           splan->parParam = lappend_int(splan->parParam, paramid);
-           splan->args = lappend(splan->args, arg);
-       }
+       splan->parParam = lappend_int(splan->parParam, pitem->paramId);
+       splan->args = lappend(splan->args, arg);
    }
-   bms_free(tmpset);
 
    /*
     * Un-correlated or undirect correlated plans of EXISTS, EXPR, ARRAY, or
        Plan       *plan;
        PlannerInfo *subroot;
        SubPlan    *splan;
-       Bitmapset  *tmpset;
        int         paramid;
-       Param      *prm;
 
        /*
         * Ignore SELECT CTEs that are not actually referenced anywhere.
         */
        subquery = (Query *) copyObject(cte->ctequery);
 
+       /* plan_params should not be in use in current query level */
+       Assert(root->plan_params == NIL);
+
        /*
         * Generate the plan for the CTE query.  Always plan for full
         * retrieval --- we don't have enough info to predict otherwise.
                                cte->cterecursive, 0.0,
                                &subroot);
 
+       /*
+        * Since the current query level doesn't yet contain any RTEs, it
+        * should not be possible for the CTE to have requested parameters of
+        * this level.
+        */
+       if (root->plan_params)
+           elog(ERROR, "unexpected outer reference in CTE query");
+
        /*
         * Make a SubPlan node for it.  This is just enough unlike
         * build_subplan that we can't share code.
        splan->args = NIL;
 
        /*
-        * Make parParam and args lists of param IDs and expressions that
-        * current query level will pass to this child plan.  Even though this
-        * is an initplan, there could be side-references to earlier
-        * initplan's outputs, specifically their CTE output parameters.
+        * The node can't have any inputs (since it's an initplan), so the
+        * parParam and args lists remain empty.  (It could contain references
+        * to earlier CTEs' output param IDs, but CTE outputs are not
+        * propagated via the args list.)
         */
-       tmpset = bms_copy(plan->extParam);
-       while ((paramid = bms_first_member(tmpset)) >= 0)
-       {
-           PlannerParamItem *pitem = list_nth(root->glob->paramlist, paramid);
-
-           if (pitem->abslevel == root->query_level)
-           {
-               prm = (Param *) pitem->item;
-               if (!IsA(prm, Param) ||
-                   prm->paramtype != INTERNALOID)
-                   elog(ERROR, "bogus local parameter passed to WITH query");
-
-               splan->parParam = lappend_int(splan->parParam, paramid);
-               splan->args = lappend(splan->args, copyObject(prm));
-           }
-       }
-       bms_free(tmpset);
 
        /*
-        * Assign a param to represent the query output.  We only really care
-        * about reserving a parameter ID number.
+        * Assign a param ID to represent the CTE's output.  No ordinary
+        * "evaluation" of this param slot ever happens, but we use the param
+        * ID for setParam/chgParam signaling just as if the CTE plan were
+        * returning a simple scalar output.  (Also, the executor abuses the
+        * ParamExecData slot for this param ID for communication among
+        * multiple CteScan nodes that might be scanning this CTE.)
         */
-       prm = generate_new_param(root, INTERNALOID, -1, InvalidOid);
-       splan->setParam = list_make1_int(prm->paramid);
+       paramid = SS_assign_special_param(root);
+       splan->setParam = list_make1_int(paramid);
 
        /*
         * Add the subplan and its PlannerInfo to the global lists.
               *initExtParam,
               *initSetParam;
    Cost        initplan_cost;
-   int         paramid;
+   PlannerInfo *proot;
    ListCell   *l;
 
    /*
    /*
     * Now determine the set of params that are validly referenceable in this
     * query level; to wit, those available from outer query levels plus the
-    * output parameters of any initPlans.  (We do not include output
+    * output parameters of any local initPlans.  (We do not include output
     * parameters of regular subplans.  Those should only appear within the
     * testexpr of SubPlan nodes, and are taken care of locally within
     * finalize_primnode.  Likewise, special parameters that are generated by
     * nodes such as ModifyTable are handled within finalize_plan.)
-    *
-    * 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;
-   foreach(l, root->glob->paramlist)
+   for (proot = root->parent_root; proot != NULL; proot = proot->parent_root)
    {
-       PlannerParamItem *pitem = (PlannerParamItem *) lfirst(l);
-
-       if (pitem->abslevel < root->query_level)
+       /* Include ordinary Var/PHV/Aggref params */
+       foreach(l, proot->plan_params)
        {
-           /* valid outer-level parameter */
-           valid_params = bms_add_member(valid_params, paramid);
+           PlannerParamItem *pitem = (PlannerParamItem *) lfirst(l);
+
+           valid_params = bms_add_member(valid_params, pitem->paramId);
        }
+       /* Include any outputs of outer-level initPlans */
+       foreach(l, proot->init_plans)
+       {
+           SubPlan    *initsubplan = (SubPlan *) lfirst(l);
+           ListCell   *l2;
 
-       paramid++;
+           foreach(l2, initsubplan->setParam)
+           {
+               valid_params = bms_add_member(valid_params, lfirst_int(l2));
+           }
+       }
+       /* Include worktable ID, if a recursive query is being planned */
+       if (proot->wt_param_id >= 0)
+           valid_params = bms_add_member(valid_params, proot->wt_param_id);
    }
 
    /*
 
 
    ParamListInfo boundParams;  /* Param values provided to planner() */
 
-   List       *paramlist;      /* to keep track of cross-level Params */
+   List       *paramlist;      /* unused, will be removed in 9.3 */
 
    List       *subplans;       /* Plans for SubPlan nodes */
 
    Index       lastRowMarkId;  /* highest PlanRowMark ID assigned */
 
    bool        transientPlan;  /* redo plan when TransactionXmin changes? */
+
+   /* Added post-release, will be in a saner place in 9.3: */
+   int         nParamExec;     /* number of PARAM_EXEC Params used */
 } PlannerGlobal;
 
 /* macro for fetching the Plan associated with a SubPlan node */
 
    /* optional private data for join_search_hook, e.g., GEQO */
    void       *join_search_private;
+
+   /* Added post-release, will be in a saner place in 9.3: */
+   List       *plan_params;    /* list of PlannerParamItems, see below */
 } PlannerInfo;
 
 
 } MinMaxAggInfo;
 
 /*
- * 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 four kinds:
- *
- * A Var: the slot represents a variable of that level that must be passed
+ * At runtime, PARAM_EXEC 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 planner is responsible for assigning nonconflicting PARAM_EXEC IDs to
+ * the PARAM_EXEC Params it generates.
+ *
+ * Outer references are managed via root->plan_params, which is a list of
+ * PlannerParamItems.  While planning a subquery, each parent query level's
+ * plan_params contains the values required from it by the current subquery.
+ * During create_plan(), we use plan_params to track values that must be
+ * passed from outer to inner sides of NestLoop plan nodes.
+ *
+ * The item a PlannerParamItem represents can be one of three kinds:
+ *
+ * A Var: the slot represents a variable of this 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.
+ * from a NestLoop node to its inner scan.  The varlevelsup value in the Var
+ * will always be zero.
  *
  * A PlaceHolderVar: this works much like the Var case, except that the
  * entry is a PlaceHolderVar node with a contained expression. The PHV
  * subquery.  The Aggref itself has agglevelsup = 0, and its argument tree
  * is adjusted to match in level.
  *
- * A Param: the slot holds the result of a subplan (it is a setParam item
- * for that subplan).  The absolute level shown for such items corresponds
- * to the parent query of the subplan.
- *
  * Note: we detect duplicate Var and PlaceHolderVar 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* for 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.
+ * them into one slot, but we do not bother to do that for Aggrefs.
+ * The scope of duplicate-elimination only extends across the set of
+ * parameters passed from one query level into a single subquery, or for
+ * nestloop parameters across the set of nestloop parameters used in a single
+ * query level.  So there is no possibility of a PARAM_EXEC slot being used
+ * for conflicting purposes.
+ *
+ * In addition, PARAM_EXEC slots are assigned for Params representing outputs
+ * from subplans (values that are setParam items for those subplans).  These
+ * IDs need not be tracked via PlannerParamItems, since we do not need any
+ * duplicate-elimination nor later processing of the represented expressions.
+ * Instead, we just record the assignment of the slot number by incrementing
+ * root->glob->nParamExec.
  */
 typedef struct PlannerParamItem
 {
    NodeTag     type;
 
-   Node       *item;           /* the Var, PlaceHolderVar, Aggref, or Param */
-   Index       abslevel;       /* its absolute query level */
+   Node       *item;           /* the Var, PlaceHolderVar, or Aggref */
+   int         paramId;        /* its assigned PARAM_EXEC slot number */
 } PlannerParamItem;
 
 /*