Turn the rangetable used by the executor into a flat list, and avoid storing
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 22 Feb 2007 22:00:26 +0000 (22:00 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 22 Feb 2007 22:00:26 +0000 (22:00 +0000)
useless substructure for its RangeTblEntry nodes.  (I chose to keep using the
same struct node type and just zero out the link fields for unneeded info,
rather than making a separate ExecRangeTblEntry type --- it seemed too
fragile to have two different rangetable representations.)

Along the way, put subplans into a list in the toplevel PlannedStmt node,
and have SubPlan nodes refer to them by list index instead of direct pointers.
Vadim wanted to do that years ago, but I never understood what he was on about
until now.  It makes things a *whole* lot more robust, because we can stop
worrying about duplicate processing of subplans during expression tree
traversals.  That's been a constant source of bugs, and it's finally gone.

There are some consequent simplifications yet to be made, like not using
a separate EState for subplans in the executor, but I'll tackle that later.

32 files changed:
doc/src/sgml/indexam.sgml
src/backend/commands/explain.c
src/backend/executor/execMain.c
src/backend/executor/execUtils.c
src/backend/executor/nodeFunctionscan.c
src/backend/executor/nodeSubplan.c
src/backend/executor/nodeSubqueryscan.c
src/backend/executor/nodeValuesscan.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/print.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/planagg.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/relnode.c
src/backend/parser/parse_expr.c
src/backend/utils/adt/selfuncs.c
src/include/executor/executor.h
src/include/nodes/execnodes.h
src/include/nodes/plannodes.h
src/include/nodes/primnodes.h
src/include/nodes/print.h
src/include/nodes/relation.h
src/include/optimizer/cost.h
src/include/optimizer/planmain.h

index d559be0b6eb5bada5f380a22228ff960edbfcbe3..247f7f48cbef4568fbcff47c6b4a0953d03ed527 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.21 2007/01/31 20:56:17 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.22 2007/02/22 22:00:22 tgl Exp $ -->
 
 <chapter id="indexam">
  <title>Index Access Method Interface Definition</title>
@@ -903,7 +903,7 @@ amcostestimate (PlannerInfo *root,
      * Also, we charge for evaluation of the indexquals at each index row.
      * All the costs are assumed to be paid incrementally during the scan.
      */
-    cost_qual_eval(&amp;index_qual_cost, indexQuals);
+    cost_qual_eval(&amp;index_qual_cost, indexQuals, root);
     *indexStartupCost = index_qual_cost.startup;
     *indexTotalCost = seq_page_cost * numIndexPages +
         (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples;
index 58b7e6ded9a48b1a74b344a45022fba02159e298..34784eb078e8d47306d0ea21247b729df52ca7bb 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.156 2007/02/20 17:32:14 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.157 2007/02/22 22:00:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,6 +37,7 @@ typedef struct ExplainState
    bool        printNodes;     /* do nodeToString() too */
    bool        printAnalyze;   /* print actual times */
    /* other states */
+   PlannedStmt *pstmt;         /* top of plan */
    List       *rtable;         /* range table */
 } ExplainState;
 
@@ -260,6 +261,7 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
 
    es->printNodes = stmt->verbose;
    es->printAnalyze = stmt->analyze;
+   es->pstmt = queryDesc->plannedstmt;
    es->rtable = queryDesc->plannedstmt->rtable;
 
    if (es->printNodes)
@@ -858,7 +860,6 @@ explain_outNode(StringInfo str,
    /* initPlan-s */
    if (plan->initPlan)
    {
-       List       *saved_rtable = es->rtable;
        ListCell   *lst;
 
        for (i = 0; i < indent; i++)
@@ -869,16 +870,15 @@ explain_outNode(StringInfo str,
            SubPlanState *sps = (SubPlanState *) lfirst(lst);
            SubPlan    *sp = (SubPlan *) sps->xprstate.expr;
 
-           es->rtable = sp->rtable;
            for (i = 0; i < indent; i++)
                appendStringInfo(str, "  ");
            appendStringInfo(str, "    ->  ");
-           explain_outNode(str, sp->plan,
+           explain_outNode(str,
+                           exec_subplan_get_plan(es->pstmt, sp),
                            sps->planstate,
                            NULL,
                            indent + 4, es);
        }
-       es->rtable = saved_rtable;
    }
 
    /* lefttree */
@@ -994,12 +994,6 @@ explain_outNode(StringInfo str,
        SubqueryScan *subqueryscan = (SubqueryScan *) plan;
        SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
        Plan       *subnode = subqueryscan->subplan;
-       RangeTblEntry *rte = rt_fetch(subqueryscan->scan.scanrelid,
-                                     es->rtable);
-       List       *saved_rtable = es->rtable;
-
-       Assert(rte->rtekind == RTE_SUBQUERY);
-       es->rtable = rte->subquery->rtable;
 
        for (i = 0; i < indent; i++)
            appendStringInfo(str, "  ");
@@ -1009,14 +1003,11 @@ explain_outNode(StringInfo str,
                        subquerystate->subplan,
                        NULL,
                        indent + 3, es);
-
-       es->rtable = saved_rtable;
    }
 
    /* subPlan-s */
    if (planstate->subPlan)
    {
-       List       *saved_rtable = es->rtable;
        ListCell   *lst;
 
        for (i = 0; i < indent; i++)
@@ -1027,16 +1018,15 @@ explain_outNode(StringInfo str,
            SubPlanState *sps = (SubPlanState *) lfirst(lst);
            SubPlan    *sp = (SubPlan *) sps->xprstate.expr;
 
-           es->rtable = sp->rtable;
            for (i = 0; i < indent; i++)
                appendStringInfo(str, "  ");
            appendStringInfo(str, "    ->  ");
-           explain_outNode(str, sp->plan,
+           explain_outNode(str,
+                           exec_subplan_get_plan(es->pstmt, sp),
                            sps->planstate,
                            NULL,
                            indent + 4, es);
        }
-       es->rtable = saved_rtable;
    }
 }
 
index 405b58f9fd6b087ec18ecda7541f7b1a98e63c9c..d0df0ea6f472f28e48f822234b6cab7bfa85ba8c 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.287 2007/02/20 17:32:14 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.288 2007/02/22 22:00:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -92,9 +92,9 @@ static void ExecProcessReturning(ProjectionInfo *projectReturning,
                     DestReceiver *dest);
 static TupleTableSlot *EvalPlanQualNext(EState *estate);
 static void EndEvalPlanQual(EState *estate);
+static void ExecCheckRTPerms(List *rangeTable);
 static void ExecCheckRTEPerms(RangeTblEntry *rte);
 static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
-static void ExecCheckRangeTblReadOnly(List *rtable);
 static void EvalPlanQualStart(evalPlanQual *epq, EState *estate,
                  evalPlanQual *priorepq);
 static void EvalPlanQualStop(evalPlanQual *epq);
@@ -348,16 +348,14 @@ ExecutorRewind(QueryDesc *queryDesc)
  * ExecCheckRTPerms
  *     Check access permissions for all relations listed in a range table.
  */
-void
+static void
 ExecCheckRTPerms(List *rangeTable)
 {
    ListCell   *l;
 
    foreach(l, rangeTable)
    {
-       RangeTblEntry *rte = lfirst(l);
-
-       ExecCheckRTEPerms(rte);
+       ExecCheckRTEPerms((RangeTblEntry *) lfirst(l));
    }
 }
 
@@ -373,12 +371,9 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
    Oid         userid;
 
    /*
-    * Only plain-relation RTEs need to be checked here.  Subquery RTEs are
-    * checked by ExecInitSubqueryScan if the subquery is still a separate
-    * subquery --- if it's been pulled up into our query level then the RTEs
-    * are in our rangetable and will be checked here. Function RTEs are
+    * Only plain-relation RTEs need to be checked here.  Function RTEs are
     * checked by init_fcache when the function is prepared for execution.
-    * Join and special RTEs need no checks.
+    * Join, subquery, and special RTEs need no checks.
     */
    if (rte->rtekind != RTE_RELATION)
        return;
@@ -417,6 +412,8 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
 static void
 ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
 {
+   ListCell   *l;
+
    /*
     * CREATE TABLE AS or SELECT INTO?
     *
@@ -426,32 +423,9 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
        goto fail;
 
    /* Fail if write permissions are requested on any non-temp table */
-   ExecCheckRangeTblReadOnly(plannedstmt->rtable);
-
-   return;
-
-fail:
-   ereport(ERROR,
-           (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
-            errmsg("transaction is read-only")));
-}
-
-static void
-ExecCheckRangeTblReadOnly(List *rtable)
-{
-   ListCell   *l;
-
-   /* Fail if write permissions are requested on any non-temp table */
-   foreach(l, rtable)
+   foreach(l, plannedstmt->rtable)
    {
-       RangeTblEntry *rte = lfirst(l);
-
-       if (rte->rtekind == RTE_SUBQUERY)
-       {
-           Assert(!rte->subquery->into);
-           ExecCheckRangeTblReadOnly(rte->subquery->rtable);
-           continue;
-       }
+       RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
 
        if (rte->rtekind != RTE_RELATION)
            continue;
@@ -494,9 +468,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
    ListCell   *l;
 
    /*
-    * Do permissions checks.  It's sufficient to examine the query's top
-    * rangetable here --- subplan RTEs will be checked during
-    * ExecInitSubPlan().
+    * Do permissions checks
     */
    ExecCheckRTPerms(rangeTable);
 
index d9957573883b3bf909c44f2a5ef775feec3f5b77..d188a38489d1e470202e306d2a9fc1dc82f7e841 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.145 2007/02/20 17:32:14 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.146 2007/02/22 22:00:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -847,7 +847,6 @@ ExecRelationIsTargetRelation(EState *estate, Index scanrelid)
 Relation
 ExecOpenScanRelation(EState *estate, Index scanrelid)
 {
-   RangeTblEntry *rtentry;
    Oid         reloid;
    LOCKMODE    lockmode;
    ResultRelInfo *resultRelInfos;
@@ -885,8 +884,7 @@ ExecOpenScanRelation(EState *estate, Index scanrelid)
    }
 
    /* OK, open the relation and acquire lock as needed */
-   rtentry = rt_fetch(scanrelid, estate->es_range_table);
-   reloid = rtentry->relid;
+   reloid = getrelid(scanrelid, estate->es_range_table);
 
    return heap_open(reloid, lockmode);
 }
index d3d9886e3c3aee128500537974eec19cf7893b27..87ac754a1e37e556282a78ed1edcf469bf05f52b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.43 2007/02/19 02:23:11 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.44 2007/02/22 22:00:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,7 +24,6 @@
 
 #include "executor/nodeFunctionscan.h"
 #include "funcapi.h"
-#include "parser/parsetree.h"
 #include "utils/builtins.h"
 
 
index 32167a94efbff491b18797034acac2422c90ad5d..9bc96921f416fcd77736707e71f2142cbb88f779 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.85 2007/02/06 02:59:11 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.86 2007/02/22 22:00:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -637,13 +637,9 @@ void
 ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags)
 {
    SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+   Plan       *plan = exec_subplan_get_plan(estate->es_plannedstmt, subplan);
    EState     *sp_estate;
 
-   /*
-    * Do access checking on the rangetable entries in the subquery.
-    */
-   ExecCheckRTPerms(subplan->rtable);
-
    /*
     * initialize my state
     */
@@ -668,18 +664,21 @@ ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags)
     * shares our Param ID space and es_query_cxt, however.  XXX if rangetable
     * access were done differently, the subquery could share our EState,
     * which would eliminate some thrashing about in this module...
+    *
+    * XXX make that happen!
     */
    sp_estate = CreateSubExecutorState(estate);
    node->sub_estate = sp_estate;
 
-   sp_estate->es_range_table = subplan->rtable;
+   sp_estate->es_range_table = estate->es_range_table;
    sp_estate->es_param_list_info = estate->es_param_list_info;
    sp_estate->es_param_exec_vals = estate->es_param_exec_vals;
    sp_estate->es_tupleTable =
-       ExecCreateTupleTable(ExecCountSlotsNode(subplan->plan) + 10);
+       ExecCreateTupleTable(ExecCountSlotsNode(plan) + 10);
    sp_estate->es_snapshot = estate->es_snapshot;
    sp_estate->es_crosscheck_snapshot = estate->es_crosscheck_snapshot;
    sp_estate->es_instrument = estate->es_instrument;
+   sp_estate->es_plannedstmt = estate->es_plannedstmt;
 
    /*
     * Start up the subplan (this is a very cut-down form of InitPlan())
@@ -692,7 +691,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate, int eflags)
    if (subplan->parParam == NIL && subplan->setParam == NIL)
        eflags |= EXEC_FLAG_REWIND;
 
-   node->planstate = ExecInitNode(subplan->plan, sp_estate, eflags);
+   node->planstate = ExecInitNode(plan, sp_estate, eflags);
 
    node->needShutdown = true;  /* now we need to shutdown the subplan */
 
index 24d470d8edd1f914698ecc312eb3705d6c7ae86c..6d58a8cad4e63b92b273dde8f6fabb934a473099 100644 (file)
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.35 2007/01/05 22:19:28 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.36 2007/02/22 22:00:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,7 +29,6 @@
 
 #include "executor/execdebug.h"
 #include "executor/nodeSubqueryscan.h"
-#include "parser/parsetree.h"
 
 static TupleTableSlot *SubqueryNext(SubqueryScanState *node);
 
@@ -104,17 +103,18 @@ SubqueryScanState *
 ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 {
    SubqueryScanState *subquerystate;
-   RangeTblEntry *rte;
    EState     *sp_estate;
 
    /* check for unsupported flags */
    Assert(!(eflags & EXEC_FLAG_MARK));
 
    /*
-    * SubqueryScan should not have any "normal" children.
+    * SubqueryScan should not have any "normal" children.  Also, if planner
+    * left anything in subrtable, it's fishy.
     */
    Assert(outerPlan(node) == NULL);
    Assert(innerPlan(node) == NULL);
+   Assert(node->subrtable == NIL);
 
    /*
     * create state structure
@@ -152,25 +152,18 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
     * initialize subquery
     *
     * This should agree with ExecInitSubPlan
-    */
-   rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
-   Assert(rte->rtekind == RTE_SUBQUERY);
-
-   /*
-    * Do access checking on the rangetable entries in the subquery.
-    */
-   ExecCheckRTPerms(rte->subquery->rtable);
-
-   /*
+    *
     * The subquery needs its own EState because it has its own rangetable. It
     * shares our Param ID space and es_query_cxt, however.  XXX if rangetable
     * access were done differently, the subquery could share our EState,
     * which would eliminate some thrashing about in this module...
+    *
+    * XXX make that happen!
     */
    sp_estate = CreateSubExecutorState(estate);
    subquerystate->sss_SubEState = sp_estate;
 
-   sp_estate->es_range_table = rte->subquery->rtable;
+   sp_estate->es_range_table = estate->es_range_table;
    sp_estate->es_param_list_info = estate->es_param_list_info;
    sp_estate->es_param_exec_vals = estate->es_param_exec_vals;
    sp_estate->es_tupleTable =
@@ -178,6 +171,7 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
    sp_estate->es_snapshot = estate->es_snapshot;
    sp_estate->es_crosscheck_snapshot = estate->es_crosscheck_snapshot;
    sp_estate->es_instrument = estate->es_instrument;
+   sp_estate->es_plannedstmt = estate->es_plannedstmt;
 
    /*
     * Start up the subplan (this is a very cut-down form of InitPlan())
index 96e4b98a4e25cd947c30d2b5f16ca85796264810..bfd837e0985900937723c7b6abbbeaa707cfb3c2 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/nodeValuesscan.c,v 1.6 2007/02/19 02:23:11 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/nodeValuesscan.c,v 1.7 2007/02/22 22:00:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,7 +25,6 @@
 
 #include "executor/executor.h"
 #include "executor/nodeValuesscan.h"
-#include "parser/parsetree.h"
 #include "utils/memutils.h"
 
 
index 4d10afc022db95447f238c1fc51894d048fb9bf0..efa1f86945337a9857df0cb0b6fc8f5b6d4d88ba 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.367 2007/02/20 17:32:15 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.368 2007/02/22 22:00:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -78,6 +78,7 @@ _copyPlannedStmt(PlannedStmt *from)
    COPY_NODE_FIELD(rtable);
    COPY_NODE_FIELD(resultRelations);
    COPY_NODE_FIELD(into);
+   COPY_NODE_FIELD(subplans);
    COPY_NODE_FIELD(returningLists);
    COPY_NODE_FIELD(rowMarks);
    COPY_SCALAR_FIELD(nParamExec);
@@ -366,6 +367,7 @@ _copySubqueryScan(SubqueryScan *from)
     * copy remainder of node
     */
    COPY_NODE_FIELD(subplan);
+   COPY_NODE_FIELD(subrtable);
 
    return newnode;
 }
@@ -957,9 +959,8 @@ _copySubPlan(SubPlan *from)
    COPY_SCALAR_FIELD(subLinkType);
    COPY_NODE_FIELD(testexpr);
    COPY_NODE_FIELD(paramIds);
-   COPY_NODE_FIELD(plan);
    COPY_SCALAR_FIELD(plan_id);
-   COPY_NODE_FIELD(rtable);
+   COPY_SCALAR_FIELD(firstColType);
    COPY_SCALAR_FIELD(useHashTable);
    COPY_SCALAR_FIELD(unknownEqFalse);
    COPY_NODE_FIELD(setParam);
index dafa15f0287dcd849e36d1a8e3222a1de9acd5b6..1007930814ac36694122856b47582bb99621fd0e 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.299 2007/02/20 17:32:15 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.300 2007/02/22 22:00:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -306,9 +306,8 @@ _equalSubPlan(SubPlan *a, SubPlan *b)
    COMPARE_SCALAR_FIELD(subLinkType);
    COMPARE_NODE_FIELD(testexpr);
    COMPARE_NODE_FIELD(paramIds);
-   /* should compare plans, but have to settle for comparing plan IDs */
    COMPARE_SCALAR_FIELD(plan_id);
-   COMPARE_NODE_FIELD(rtable);
+   COMPARE_SCALAR_FIELD(firstColType);
    COMPARE_SCALAR_FIELD(useHashTable);
    COMPARE_SCALAR_FIELD(unknownEqFalse);
    COMPARE_NODE_FIELD(setParam);
index c8cc25abb1330ce9c8cc25d8dd90375bafbf0f6c..64f235201d0def2ba1d63eeae6d7b5b029d729b8 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.300 2007/02/20 17:32:15 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.301 2007/02/22 22:00:23 tgl Exp $
  *
  * NOTES
  *   Every node type that can appear in stored rules' parsetrees *must*
@@ -245,6 +245,7 @@ _outPlannedStmt(StringInfo str, PlannedStmt *node)
    WRITE_NODE_FIELD(rtable);
    WRITE_NODE_FIELD(resultRelations);
    WRITE_NODE_FIELD(into);
+   WRITE_NODE_FIELD(subplans);
    WRITE_NODE_FIELD(returningLists);
    WRITE_NODE_FIELD(rowMarks);
    WRITE_INT_FIELD(nParamExec);
@@ -415,6 +416,7 @@ _outSubqueryScan(StringInfo str, SubqueryScan *node)
    _outScanInfo(str, (Scan *) node);
 
    WRITE_NODE_FIELD(subplan);
+   WRITE_NODE_FIELD(subrtable);
 }
 
 static void
@@ -823,9 +825,8 @@ _outSubPlan(StringInfo str, SubPlan *node)
    WRITE_ENUM_FIELD(subLinkType, SubLinkType);
    WRITE_NODE_FIELD(testexpr);
    WRITE_NODE_FIELD(paramIds);
-   WRITE_NODE_FIELD(plan);
    WRITE_INT_FIELD(plan_id);
-   WRITE_NODE_FIELD(rtable);
+   WRITE_OID_FIELD(firstColType);
    WRITE_BOOL_FIELD(useHashTable);
    WRITE_BOOL_FIELD(unknownEqFalse);
    WRITE_NODE_FIELD(setParam);
@@ -1259,7 +1260,9 @@ _outPlannerGlobal(StringInfo str, PlannerGlobal *node)
 
    /* NB: this isn't a complete set of fields */
    WRITE_NODE_FIELD(paramlist);
-   WRITE_INT_FIELD(next_plan_id);
+   WRITE_NODE_FIELD(subplans);
+   WRITE_NODE_FIELD(subrtables);
+   WRITE_NODE_FIELD(finalrtable);
 }
 
 static void
@@ -1317,6 +1320,7 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node)
    WRITE_UINT_FIELD(pages);
    WRITE_FLOAT_FIELD(tuples, "%.0f");
    WRITE_NODE_FIELD(subplan);
+   WRITE_NODE_FIELD(subrtable);
    WRITE_NODE_FIELD(baserestrictinfo);
    WRITE_NODE_FIELD(joininfo);
    WRITE_BOOL_FIELD(has_eclass_joins);
index 06d28b56b21641c959e113c50ede6915dba6594d..c6edfbed8a054d465d55b01c8d35eb4e54a72127 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.84 2007/02/10 14:58:54 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.85 2007/02/22 22:00:23 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -25,7 +25,6 @@
 #include "parser/parsetree.h"
 #include "utils/lsyscache.h"
 
-static char *plannode_type(Plan *p);
 
 /*
  * print
@@ -488,171 +487,3 @@ print_slot(TupleTableSlot *slot)
 
    debugtup(slot, NULL);
 }
-
-static char *
-plannode_type(Plan *p)
-{
-   switch (nodeTag(p))
-   {
-       case T_Plan:
-           return "PLAN";
-       case T_Result:
-           return "RESULT";
-       case T_Append:
-           return "APPEND";
-       case T_BitmapAnd:
-           return "BITMAPAND";
-       case T_BitmapOr:
-           return "BITMAPOR";
-       case T_Scan:
-           return "SCAN";
-       case T_SeqScan:
-           return "SEQSCAN";
-       case T_IndexScan:
-           return "INDEXSCAN";
-       case T_BitmapIndexScan:
-           return "BITMAPINDEXSCAN";
-       case T_BitmapHeapScan:
-           return "BITMAPHEAPSCAN";
-       case T_TidScan:
-           return "TIDSCAN";
-       case T_SubqueryScan:
-           return "SUBQUERYSCAN";
-       case T_FunctionScan:
-           return "FUNCTIONSCAN";
-       case T_ValuesScan:
-           return "VALUESSCAN";
-       case T_Join:
-           return "JOIN";
-       case T_NestLoop:
-           return "NESTLOOP";
-       case T_MergeJoin:
-           return "MERGEJOIN";
-       case T_HashJoin:
-           return "HASHJOIN";
-       case T_Material:
-           return "MATERIAL";
-       case T_Sort:
-           return "SORT";
-       case T_Agg:
-           return "AGG";
-       case T_Unique:
-           return "UNIQUE";
-       case T_SetOp:
-           return "SETOP";
-       case T_Limit:
-           return "LIMIT";
-       case T_Hash:
-           return "HASH";
-       case T_Group:
-           return "GROUP";
-       default:
-           return "UNKNOWN";
-   }
-}
-
-/*
- * Recursively prints a simple text description of the plan tree
- */
-void
-print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
-{
-   int         i;
-   char        extraInfo[NAMEDATALEN + 100];
-
-   if (!p)
-       return;
-   for (i = 0; i < indentLevel; i++)
-       printf(" ");
-   printf("%s%s :c=%.2f..%.2f :r=%.0f :w=%d ", label, plannode_type(p),
-          p->startup_cost, p->total_cost,
-          p->plan_rows, p->plan_width);
-   if (IsA(p, Scan) ||
-       IsA(p, SeqScan) ||
-       IsA(p, BitmapHeapScan))
-   {
-       RangeTblEntry *rte;
-
-       rte = rt_fetch(((Scan *) p)->scanrelid, parsetree->rtable);
-       strlcpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
-   }
-   else if (IsA(p, IndexScan))
-   {
-       RangeTblEntry *rte;
-
-       rte = rt_fetch(((IndexScan *) p)->scan.scanrelid, parsetree->rtable);
-       strlcpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
-   }
-   else if (IsA(p, FunctionScan))
-   {
-       RangeTblEntry *rte;
-
-       rte = rt_fetch(((FunctionScan *) p)->scan.scanrelid, parsetree->rtable);
-       strlcpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
-   }
-   else if (IsA(p, ValuesScan))
-   {
-       RangeTblEntry *rte;
-
-       rte = rt_fetch(((ValuesScan *) p)->scan.scanrelid, parsetree->rtable);
-       strlcpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
-   }
-   else
-       extraInfo[0] = '\0';
-   if (extraInfo[0] != '\0')
-       printf(" ( %s )\n", extraInfo);
-   else
-       printf("\n");
-   print_plan_recursive(p->lefttree, parsetree, indentLevel + 3, "l: ");
-   print_plan_recursive(p->righttree, parsetree, indentLevel + 3, "r: ");
-
-   if (IsA(p, Append))
-   {
-       ListCell   *l;
-       Append     *appendplan = (Append *) p;
-
-       foreach(l, appendplan->appendplans)
-       {
-           Plan       *subnode = (Plan *) lfirst(l);
-
-           print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: ");
-       }
-   }
-
-   if (IsA(p, BitmapAnd))
-   {
-       ListCell   *l;
-       BitmapAnd  *bitmapandplan = (BitmapAnd *) p;
-
-       foreach(l, bitmapandplan->bitmapplans)
-       {
-           Plan       *subnode = (Plan *) lfirst(l);
-
-           print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: ");
-       }
-   }
-
-   if (IsA(p, BitmapOr))
-   {
-       ListCell   *l;
-       BitmapOr   *bitmaporplan = (BitmapOr *) p;
-
-       foreach(l, bitmaporplan->bitmapplans)
-       {
-           Plan       *subnode = (Plan *) lfirst(l);
-
-           print_plan_recursive(subnode, parsetree, indentLevel + 3, "a: ");
-       }
-   }
-}
-
-/*
- * print_plan
- *
- * prints just the plan node types
- */
-void
-print_plan(Plan *p, Query *parsetree)
-{
-   print_plan_recursive(p, parsetree, 0, "");
-}
index 6c6e80071c1ccfc12bbeed1a9d0c92373a750bb4..7aa2bd7e6fb2c050aefce6853fe93da4ea5ddee9 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.160 2007/02/20 17:32:15 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.161 2007/02/22 22:00:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -521,6 +521,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
                                    root->query_level + 1,
                                    tuple_fraction,
                                    &subroot);
+   rel->subrtable = subroot->parse->rtable;
 
    /* Copy number of output rows from subplan */
    rel->tuples = rel->subplan->plan_rows;
index 422ef9232226ad82dd22d73f2d712b5eb80a0c90..3dbb3bd802dbd8cd7cef7b14d27c8eb62f97a604 100644 (file)
@@ -54,7 +54,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.177 2007/01/22 20:00:39 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.178 2007/02/22 22:00:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -107,11 +107,16 @@ bool      enable_nestloop = true;
 bool       enable_mergejoin = true;
 bool       enable_hashjoin = true;
 
+typedef struct
+{
+   PlannerInfo *root;
+   QualCost    total;
+} cost_qual_eval_context;
 
 static MergeScanSelCache *cached_scansel(PlannerInfo *root,
                                         RestrictInfo *rinfo,
                                         PathKey *pathkey);
-static bool cost_qual_eval_walker(Node *node, QualCost *total);
+static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context);
 static Selectivity approx_selectivity(PlannerInfo *root, List *quals,
                   JoinType jointype);
 static Selectivity join_in_selectivity(JoinPath *path, PlannerInfo *root);
@@ -362,7 +367,7 @@ cost_index(IndexPath *path, PlannerInfo *root,
    {
        QualCost    index_qual_cost;
 
-       cost_qual_eval(&index_qual_cost, indexQuals);
+       cost_qual_eval(&index_qual_cost, indexQuals, root);
        /* any startup cost still has to be paid ... */
        cpu_per_tuple -= index_qual_cost.per_tuple;
    }
@@ -855,7 +860,7 @@ cost_functionscan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
    Assert(rte->rtekind == RTE_FUNCTION);
 
    /* Estimate costs of executing the function expression */
-   cost_qual_eval_node(&exprcost, rte->funcexpr);
+   cost_qual_eval_node(&exprcost, rte->funcexpr, root);
 
    startup_cost += exprcost.startup;
    cpu_per_tuple = exprcost.per_tuple;
@@ -1241,7 +1246,7 @@ cost_nestloop(NestPath *path, PlannerInfo *root)
    ntuples = outer_path_rows * inner_path_rows * joininfactor;
 
    /* CPU costs */
-   cost_qual_eval(&restrict_qual_cost, path->joinrestrictinfo);
+   cost_qual_eval(&restrict_qual_cost, path->joinrestrictinfo, root);
    startup_cost += restrict_qual_cost.startup;
    cpu_per_tuple = cpu_tuple_cost + restrict_qual_cost.per_tuple;
    run_cost += cpu_per_tuple * ntuples;
@@ -1301,8 +1306,8 @@ cost_mergejoin(MergePath *path, PlannerInfo *root)
     */
    merge_selec = approx_selectivity(root, mergeclauses,
                                     path->jpath.jointype);
-   cost_qual_eval(&merge_qual_cost, mergeclauses);
-   cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo);
+   cost_qual_eval(&merge_qual_cost, mergeclauses, root);
+   cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo, root);
    qp_qual_cost.startup -= merge_qual_cost.startup;
    qp_qual_cost.per_tuple -= merge_qual_cost.per_tuple;
 
@@ -1587,8 +1592,8 @@ cost_hashjoin(HashPath *path, PlannerInfo *root)
     */
    hash_selec = approx_selectivity(root, hashclauses,
                                    path->jpath.jointype);
-   cost_qual_eval(&hash_qual_cost, hashclauses);
-   cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo);
+   cost_qual_eval(&hash_qual_cost, hashclauses, root);
+   cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo, root);
    qp_qual_cost.startup -= hash_qual_cost.startup;
    qp_qual_cost.per_tuple -= hash_qual_cost.per_tuple;
 
@@ -1756,12 +1761,14 @@ cost_hashjoin(HashPath *path, PlannerInfo *root)
  *     and a per-evaluation component.
  */
 void
-cost_qual_eval(QualCost *cost, List *quals)
+cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
 {
+   cost_qual_eval_context context;
    ListCell   *l;
 
-   cost->startup = 0;
-   cost->per_tuple = 0;
+   context.root = root;
+   context.total.startup = 0;
+   context.total.per_tuple = 0;
 
    /* We don't charge any cost for the implicit ANDing at top level ... */
 
@@ -1769,8 +1776,10 @@ cost_qual_eval(QualCost *cost, List *quals)
    {
        Node       *qual = (Node *) lfirst(l);
 
-       cost_qual_eval_walker(qual, cost);
+       cost_qual_eval_walker(qual, &context);
    }
+
+   *cost = context.total;
 }
 
 /*
@@ -1778,15 +1787,21 @@ cost_qual_eval(QualCost *cost, List *quals)
  *     As above, for a single RestrictInfo or expression.
  */
 void
-cost_qual_eval_node(QualCost *cost, Node *qual)
+cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root)
 {
-   cost->startup = 0;
-   cost->per_tuple = 0;
-   cost_qual_eval_walker(qual, cost);
+   cost_qual_eval_context context;
+
+   context.root = root;
+   context.total.startup = 0;
+   context.total.per_tuple = 0;
+
+   cost_qual_eval_walker(qual, &context);
+
+   *cost = context.total;
 }
 
 static bool
-cost_qual_eval_walker(Node *node, QualCost *total)
+cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 {
    if (node == NULL)
        return false;
@@ -1803,18 +1818,19 @@ cost_qual_eval_walker(Node *node, QualCost *total)
 
        if (rinfo->eval_cost.startup < 0)
        {
-           rinfo->eval_cost.startup = 0;
-           rinfo->eval_cost.per_tuple = 0;
+           cost_qual_eval_context locContext;
+
+           locContext.root = context->root;
+           locContext.total.startup = 0;
+           locContext.total.per_tuple = 0;
            /*
             * For an OR clause, recurse into the marked-up tree so that
             * we set the eval_cost for contained RestrictInfos too.
             */
            if (rinfo->orclause)
-               cost_qual_eval_walker((Node *) rinfo->orclause,
-                                     &rinfo->eval_cost);
+               cost_qual_eval_walker((Node *) rinfo->orclause, &locContext);
            else
-               cost_qual_eval_walker((Node *) rinfo->clause,
-                                     &rinfo->eval_cost);
+               cost_qual_eval_walker((Node *) rinfo->clause, &locContext);
            /*
             * If the RestrictInfo is marked pseudoconstant, it will be tested
             * only once, so treat its cost as all startup cost.
@@ -1822,12 +1838,13 @@ cost_qual_eval_walker(Node *node, QualCost *total)
            if (rinfo->pseudoconstant)
            {
                /* count one execution during startup */
-               rinfo->eval_cost.startup += rinfo->eval_cost.per_tuple;
-               rinfo->eval_cost.per_tuple = 0;
+               locContext.total.startup += locContext.total.per_tuple;
+               locContext.total.per_tuple = 0;
            }
+           rinfo->eval_cost = locContext.total;
        }
-       total->startup += rinfo->eval_cost.startup;
-       total->per_tuple += rinfo->eval_cost.per_tuple;
+       context->total.startup += rinfo->eval_cost.startup;
+       context->total.per_tuple += rinfo->eval_cost.per_tuple;
        /* do NOT recurse into children */
        return false;
    }
@@ -1849,8 +1866,8 @@ cost_qual_eval_walker(Node *node, QualCost *total)
     */
    if (IsA(node, FuncExpr))
    {
-       total->per_tuple += get_func_cost(((FuncExpr *) node)->funcid) *
-           cpu_operator_cost;
+       context->total.per_tuple +=
+           get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost;
    }
    else if (IsA(node, OpExpr) ||
             IsA(node, DistinctExpr) ||
@@ -1858,8 +1875,8 @@ cost_qual_eval_walker(Node *node, QualCost *total)
    {
        /* rely on struct equivalence to treat these all alike */
        set_opfuncid((OpExpr *) node);
-       total->per_tuple += get_func_cost(((OpExpr *) node)->opfuncid) *
-           cpu_operator_cost;
+       context->total.per_tuple +=
+           get_func_cost(((OpExpr *) node)->opfuncid) * cpu_operator_cost;
    }
    else if (IsA(node, ScalarArrayOpExpr))
    {
@@ -1871,7 +1888,7 @@ cost_qual_eval_walker(Node *node, QualCost *total)
        Node       *arraynode = (Node *) lsecond(saop->args);
 
        set_sa_opfuncid(saop);
-       total->per_tuple += get_func_cost(saop->opfuncid) *
+       context->total.per_tuple += get_func_cost(saop->opfuncid) *
            cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
    }
    else if (IsA(node, RowCompareExpr))
@@ -1884,7 +1901,7 @@ cost_qual_eval_walker(Node *node, QualCost *total)
        {
            Oid     opid = lfirst_oid(lc);
 
-           total->per_tuple += get_func_cost(get_opcode(opid)) *
+           context->total.per_tuple += get_func_cost(get_opcode(opid)) *
                cpu_operator_cost;
        }
    }
@@ -1905,7 +1922,7 @@ cost_qual_eval_walker(Node *node, QualCost *total)
         * subplan by hashing.
         */
        SubPlan    *subplan = (SubPlan *) node;
-       Plan       *plan = subplan->plan;
+       Plan       *plan = planner_subplan_get_plan(context->root, subplan);
 
        if (subplan->useHashTable)
        {
@@ -1915,7 +1932,7 @@ cost_qual_eval_walker(Node *node, QualCost *total)
             * cpu_operator_cost per tuple for the work of loading the
             * hashtable, too.
             */
-           total->startup += plan->total_cost +
+           context->total.startup += plan->total_cost +
                cpu_operator_cost * plan->plan_rows;
 
            /*
@@ -1941,20 +1958,21 @@ cost_qual_eval_walker(Node *node, QualCost *total)
            if (subplan->subLinkType == EXISTS_SUBLINK)
            {
                /* we only need to fetch 1 tuple */
-               total->per_tuple += plan_run_cost / plan->plan_rows;
+               context->total.per_tuple += plan_run_cost / plan->plan_rows;
            }
            else if (subplan->subLinkType == ALL_SUBLINK ||
                     subplan->subLinkType == ANY_SUBLINK)
            {
                /* assume we need 50% of the tuples */
-               total->per_tuple += 0.50 * plan_run_cost;
+               context->total.per_tuple += 0.50 * plan_run_cost;
                /* also charge a cpu_operator_cost per row examined */
-               total->per_tuple += 0.50 * plan->plan_rows * cpu_operator_cost;
+               context->total.per_tuple +=
+                   0.50 * plan->plan_rows * cpu_operator_cost;
            }
            else
            {
                /* assume we need all tuples */
-               total->per_tuple += plan_run_cost;
+               context->total.per_tuple += plan_run_cost;
            }
 
            /*
@@ -1967,15 +1985,15 @@ cost_qual_eval_walker(Node *node, QualCost *total)
            if (subplan->parParam == NIL &&
                (IsA(plan, Sort) ||
                 IsA(plan, Material)))
-               total->startup += plan->startup_cost;
+               context->total.startup += plan->startup_cost;
            else
-               total->per_tuple += plan->startup_cost;
+               context->total.per_tuple += plan->startup_cost;
        }
    }
 
    /* recurse into children */
    return expression_tree_walker(node, cost_qual_eval_walker,
-                                 (void *) total);
+                                 (void *) context);
 }
 
 
@@ -2042,7 +2060,7 @@ set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel)
 
    rel->rows = clamp_row_est(nrows);
 
-   cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo);
+   cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo, root);
 
    set_rel_width(root, rel);
 }
index 4d3d926a167fa58b1d2f06af0ae60013cbba26a3..dbfa2c4e58e415981f8b538a7ef535580dea9697 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.225 2007/02/19 02:23:12 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.226 2007/02/22 22:00:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -128,12 +128,12 @@ static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
  * 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:
- *   (1) Create a corresponding plan node containing appropriate id,
- *       target list, and qualification information.
- *   (2) Modify qual clauses of join nodes so that subplan attributes are
- *       referenced using relative values.
- *   (3) Target lists are not modified, but will be in setrefs.c.
+ *   every pathnode found, we create a corresponding plan node containing
+ *   appropriate id, target list, and qualification information.
+ *
+ *   The tlists and quals in the plan tree are still in planner format,
+ *   ie, Vars still correspond to the parser's numbering.  This will be
+ *   fixed later by setrefs.c.
  *
  *   best_path is the best access path
  *
@@ -421,7 +421,8 @@ create_gating_plan(PlannerInfo *root, Plan *plan, List *quals)
    if (!pseudoconstants)
        return plan;
 
-   return (Plan *) make_result((List *) copyObject(plan->targetlist),
+   return (Plan *) make_result(root,
+                               plan->targetlist,
                                (Node *) pseudoconstants,
                                plan);
 }
@@ -519,7 +520,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
    if (best_path->subpaths == NIL)
    {
        /* Generate a Result plan with constant-FALSE gating qual */
-       return (Plan *) make_result(tlist,
+       return (Plan *) make_result(root,
+                                   tlist,
                                    (Node *) list_make1(makeBoolConst(false,
                                                                      false)),
                                    NULL);
@@ -559,7 +561,7 @@ create_result_plan(PlannerInfo *root, ResultPath *best_path)
 
    quals = order_qual_clauses(root, best_path->quals);
 
-   return make_result(tlist, (Node *) quals, NULL);
+   return make_result(root, tlist, (Node *) quals, NULL);
 }
 
 /*
@@ -682,7 +684,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
         * node to help it along.
         */
        if (!is_projection_capable_plan(subplan))
-           subplan = (Plan *) make_result(newtlist, NULL, subplan);
+           subplan = (Plan *) make_result(root, newtlist, NULL, subplan);
        else
            subplan->targetlist = newtlist;
    }
@@ -1065,12 +1067,6 @@ create_bitmap_scan_plan(PlannerInfo *root,
     */
    bitmapqualorig = list_difference_ptr(bitmapqualorig, qpqual);
 
-   /*
-    * Copy the finished bitmapqualorig to make sure we have an independent
-    * copy --- needed in case there are subplans in the index quals
-    */
-   bitmapqualorig = copyObject(bitmapqualorig);
-
    /* Finally ready to build the plan node */
    scan_plan = make_bitmap_heapscan(tlist,
                                     qpqual,
@@ -1333,7 +1329,8 @@ create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
    scan_plan = make_subqueryscan(tlist,
                                  scan_clauses,
                                  scan_relid,
-                                 best_path->parent->subplan);
+                                 best_path->parent->subplan,
+                                 best_path->parent->subrtable);
 
    copy_path_costsize(&scan_plan->scan.plan, best_path);
 
@@ -2115,7 +2112,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
        Node       *clause = (Node *) lfirst(lc);
        QualCost    qcost;
 
-       cost_qual_eval_node(&qcost, clause);
+       cost_qual_eval_node(&qcost, clause, root);
        items[i].clause = clause;
        items[i].cost = qcost.per_tuple;
        i++;
@@ -2326,7 +2323,8 @@ SubqueryScan *
 make_subqueryscan(List *qptlist,
                  List *qpqual,
                  Index scanrelid,
-                 Plan *subplan)
+                 Plan *subplan,
+                 List *subrtable)
 {
    SubqueryScan *node = makeNode(SubqueryScan);
    Plan       *plan = &node->scan.plan;
@@ -2345,6 +2343,7 @@ make_subqueryscan(List *qptlist,
    plan->righttree = NULL;
    node->scan.scanrelid = scanrelid;
    node->subplan = subplan;
+   node->subrtable = subrtable;
 
    return node;
 }
@@ -2524,7 +2523,7 @@ make_hash(Plan *lefttree)
     * plan; this only affects EXPLAIN display not decisions.
     */
    plan->startup_cost = plan->total_cost;
-   plan->targetlist = copyObject(lefttree->targetlist);
+   plan->targetlist = lefttree->targetlist;
    plan->qual = NIL;
    plan->lefttree = lefttree;
    plan->righttree = NULL;
@@ -2583,7 +2582,7 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
              lefttree->plan_width);
    plan->startup_cost = sort_path.startup_cost;
    plan->total_cost = sort_path.total_cost;
-   plan->targetlist = copyObject(lefttree->targetlist);
+   plan->targetlist = lefttree->targetlist;
    plan->qual = NIL;
    plan->lefttree = lefttree;
    plan->righttree = NULL;
@@ -2741,10 +2740,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys)
             * Do we need to insert a Result node?
             */
            if (!is_projection_capable_plan(lefttree))
-           {
-               tlist = copyObject(tlist);
-               lefttree = (Plan *) make_result(tlist, NULL, lefttree);
-           }
+               lefttree = (Plan *) make_result(root, tlist, NULL, lefttree);
 
            /*
             * Add resjunk entry to input's tlist
@@ -2905,7 +2901,7 @@ make_material(Plan *lefttree)
    Plan       *plan = &node->plan;
 
    /* cost should be inserted by caller */
-   plan->targetlist = copyObject(lefttree->targetlist);
+   plan->targetlist = lefttree->targetlist;
    plan->qual = NIL;
    plan->lefttree = lefttree;
    plan->righttree = NULL;
@@ -2996,12 +2992,12 @@ make_agg(PlannerInfo *root, List *tlist, List *qual,
     */
    if (qual)
    {
-       cost_qual_eval(&qual_cost, qual);
+       cost_qual_eval(&qual_cost, qual, root);
        plan->startup_cost += qual_cost.startup;
        plan->total_cost += qual_cost.startup;
        plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
    }
-   cost_qual_eval(&qual_cost, tlist);
+   cost_qual_eval(&qual_cost, tlist, root);
    plan->startup_cost += qual_cost.startup;
    plan->total_cost += qual_cost.startup;
    plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
@@ -3059,12 +3055,12 @@ make_group(PlannerInfo *root,
     */
    if (qual)
    {
-       cost_qual_eval(&qual_cost, qual);
+       cost_qual_eval(&qual_cost, qual, root);
        plan->startup_cost += qual_cost.startup;
        plan->total_cost += qual_cost.startup;
        plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
    }
-   cost_qual_eval(&qual_cost, tlist);
+   cost_qual_eval(&qual_cost, tlist, root);
    plan->startup_cost += qual_cost.startup;
    plan->total_cost += qual_cost.startup;
    plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
@@ -3108,7 +3104,7 @@ make_unique(Plan *lefttree, List *distinctList)
     * has a better idea.
     */
 
-   plan->targetlist = copyObject(lefttree->targetlist);
+   plan->targetlist = lefttree->targetlist;
    plan->qual = NIL;
    plan->lefttree = lefttree;
    plan->righttree = NULL;
@@ -3174,7 +3170,7 @@ make_setop(SetOpCmd cmd, Plan *lefttree,
    if (plan->plan_rows < 1)
        plan->plan_rows = 1;
 
-   plan->targetlist = copyObject(lefttree->targetlist);
+   plan->targetlist = lefttree->targetlist;
    plan->qual = NIL;
    plan->lefttree = lefttree;
    plan->righttree = NULL;
@@ -3272,7 +3268,7 @@ make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount,
            plan->plan_rows = 1;
    }
 
-   plan->targetlist = copyObject(lefttree->targetlist);
+   plan->targetlist = lefttree->targetlist;
    plan->qual = NIL;
    plan->lefttree = lefttree;
    plan->righttree = NULL;
@@ -3293,7 +3289,8 @@ make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount,
  * cost.  In either case, tlist eval cost is not to be included here.
  */
 Result *
-make_result(List *tlist,
+make_result(PlannerInfo *root,
+           List *tlist,
            Node *resconstantqual,
            Plan *subplan)
 {
@@ -3312,7 +3309,7 @@ make_result(List *tlist,
        {
            QualCost    qual_cost;
 
-           cost_qual_eval(&qual_cost, (List *) resconstantqual);
+           cost_qual_eval(&qual_cost, (List *) resconstantqual, root);
            /* resconstantqual is evaluated once at startup */
            plan->startup_cost += qual_cost.startup + qual_cost.per_tuple;
            plan->total_cost += qual_cost.startup + qual_cost.per_tuple;
index 406cc9dd49665845ef9d889565dbd1226c72b236..5411072b8dbb5f15e2c365f874b817ebc21bd197 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.28 2007/02/20 17:32:15 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.29 2007/02/22 22:00:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -188,10 +188,10 @@ optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path)
    /*
     * Generate the output plan --- basically just a Result
     */
-   plan = (Plan *) make_result(tlist, hqual, NULL);
+   plan = (Plan *) make_result(root, tlist, hqual, NULL);
 
    /* Account for evaluation cost of the tlist (make_result did the rest) */
-   cost_qual_eval(&tlist_cost, tlist);
+   cost_qual_eval(&tlist_cost, tlist, root);
    plan->startup_cost += tlist_cost.startup;
    plan->total_cost += tlist_cost.startup + tlist_cost.per_tuple;
 
index 3bb603a0f614bb53ff4cbb0eebefee770afbd819..b45288dc5b8faafe9a827aacb56fbcccd69c6a0c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.214 2007/02/20 17:32:15 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.215 2007/02/22 22:00:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -88,6 +88,8 @@ planner(Query *parse, bool isCursor, int cursorOptions,
    double      tuple_fraction;
    PlannerInfo *root;
    Plan       *top_plan;
+   ListCell   *lp,
+              *lr;
 
    /*
     * Set up global state for this planner invocation.  This data is needed
@@ -99,7 +101,9 @@ planner(Query *parse, bool isCursor, int cursorOptions,
 
    glob->boundParams = boundParams;
    glob->paramlist = NIL;
-   glob->next_plan_id = 0;
+   glob->subplans = NIL;
+   glob->subrtables = NIL;
+   glob->finalrtable = NIL;
 
    /* Determine what fraction of the plan is likely to be scanned */
    if (isCursor)
@@ -132,7 +136,17 @@ planner(Query *parse, bool isCursor, int cursorOptions,
    }
 
    /* final cleanup of the plan */
-   top_plan = set_plan_references(top_plan, parse->rtable);
+   Assert(glob->finalrtable == NIL);
+   top_plan = set_plan_references(glob, top_plan, root->parse->rtable);
+   /* ... and the subplans (both regular subplans and initplans) */
+   Assert(list_length(glob->subplans) == list_length(glob->subrtables));
+   forboth(lp, glob->subplans, lr, glob->subrtables)
+   {
+       Plan   *subplan = (Plan *) lfirst(lp);
+       List   *subrtable = (List *) lfirst(lr);
+
+       lfirst(lp) = set_plan_references(glob, subplan, subrtable);
+   }
 
    /* build the PlannedStmt result */
    result = makeNode(PlannedStmt);
@@ -140,9 +154,10 @@ planner(Query *parse, bool isCursor, int cursorOptions,
    result->commandType = parse->commandType;
    result->canSetTag = parse->canSetTag;
    result->planTree = top_plan;
-   result->rtable = parse->rtable;
+   result->rtable = glob->finalrtable;
    result->resultRelations = root->resultRelations;
    result->into = parse->into;
+   result->subplans = glob->subplans;
    result->returningLists = root->returningLists;
    result->rowMarks = parse->rowMarks;
    result->nParamExec = list_length(glob->paramlist);
@@ -182,7 +197,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
                 Index level, double tuple_fraction,
                 PlannerInfo **subroot)
 {
-   int         saved_plan_id = glob->next_plan_id;
+   int         num_old_subplans = list_length(glob->subplans);
    PlannerInfo *root;
    Plan       *plan;
    List       *newHaving;
@@ -384,7 +399,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
     * initPlan list and extParam/allParam sets for plan nodes, and attach the
     * initPlans to the top plan node.
     */
-   if (root->glob->next_plan_id != saved_plan_id || root->query_level > 1)
+   if (list_length(glob->subplans) != num_old_subplans ||
+       root->query_level > 1)
        SS_finalize_plan(root, plan);
 
    /* Return internal info if caller wants it */
@@ -621,7 +637,8 @@ inheritance_planner(PlannerInfo *root)
     * If we managed to exclude every child rel, return a dummy plan
     */
    if (subplans == NIL)
-       return (Plan *) make_result(tlist,
+       return (Plan *) make_result(root,
+                                   tlist,
                                    (Node *) list_make1(makeBoolConst(false,
                                                                      false)),
                                    NULL);
@@ -639,6 +656,10 @@ inheritance_planner(PlannerInfo *root)
     */
    parse->rtable = rtable;
 
+   /* Suppress Append if there's only one surviving child rel */
+   if (list_length(subplans) == 1)
+       return (Plan *) linitial(subplans);
+
    return (Plan *) make_append(subplans, true, tlist);
 }
 
@@ -897,7 +918,9 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
                 */
                if (!is_projection_capable_plan(result_plan))
                {
-                   result_plan = (Plan *) make_result(sub_tlist, NULL,
+                   result_plan = (Plan *) make_result(root,
+                                                      sub_tlist,
+                                                      NULL,
                                                       result_plan);
                }
                else
@@ -928,7 +951,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
                 * tuples) --- so make_agg() and make_group() are responsible
                 * for computing the added cost.
                 */
-               cost_qual_eval(&tlist_cost, sub_tlist);
+               cost_qual_eval(&tlist_cost, sub_tlist, root);
                result_plan->startup_cost += tlist_cost.startup;
                result_plan->total_cost += tlist_cost.startup +
                    tlist_cost.per_tuple * result_plan->plan_rows;
@@ -1051,7 +1074,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
                 * this routine to avoid having to generate the plan in the
                 * first place.
                 */
-               result_plan = (Plan *) make_result(tlist,
+               result_plan = (Plan *) make_result(root,
+                                                  tlist,
                                                   parse->havingQual,
                                                   NULL);
            }
@@ -1110,6 +1134,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
    {
        List       *rlist;
 
+       Assert(parse->resultRelation);
        rlist = set_returning_clause_references(parse->returningList,
                                                result_plan,
                                                parse->resultRelation);
@@ -1544,7 +1569,7 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
  * pass down only c,d,a+b, but it's not really worth the trouble to
  * eliminate simple var references from the subplan.  We will avoid doing
  * the extra computation to recompute a+b at the outer level; see
- * replace_vars_with_subplan_refs() in setrefs.c.)
+ * fix_upper_expr() in setrefs.c.)
  *
  * If we are grouping or aggregating, *and* there are no non-Var grouping
  * expressions, then the returned tlist is effectively dummy; we do not
index 3d9f5486bccfce75f312eee1be38fb6b7ec90a75..aaa383742e7bdb2b798f72920a934aaec2176dff 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.130 2007/02/19 02:23:12 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.131 2007/02/22 22:00:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,48 +40,60 @@ typedef struct
    tlist_vinfo vars[1];        /* VARIABLE LENGTH ARRAY */
 } indexed_tlist;               /* VARIABLE LENGTH STRUCT */
 
+typedef struct
+{
+   int         rtoffset;
+} fix_scan_expr_context;
+
 typedef struct
 {
    indexed_tlist *outer_itlist;
    indexed_tlist *inner_itlist;
    Index       acceptable_rel;
-} join_references_context;
+   int         rtoffset;
+} fix_join_expr_context;
 
 typedef struct
 {
    indexed_tlist *subplan_itlist;
    Index       subvarno;
-} replace_vars_with_subplan_refs_context;
+   int         rtoffset;
+} fix_upper_expr_context;
+
+#define fix_scan_list(lst, rtoffset) \
+   ((List *) fix_scan_expr((Node *) (lst), rtoffset))
 
-static Plan *set_subqueryscan_references(SubqueryScan *plan, List *rtable);
+static Plan *set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset);
+static Plan *set_subqueryscan_references(PlannerGlobal *glob,
+                                        SubqueryScan *plan,
+                                        int rtoffset);
 static bool trivial_subqueryscan(SubqueryScan *plan);
-static void adjust_plan_varnos(Plan *plan, int rtoffset);
-static void adjust_expr_varnos(Node *node, int rtoffset);
-static bool adjust_expr_varnos_walker(Node *node, int *context);
-static void fix_expr_references(Plan *plan, Node *node);
-static bool fix_expr_references_walker(Node *node, void *context);
-static void set_join_references(Join *join);
+static Node *fix_scan_expr(Node *node, int rtoffset);
+static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
+static void set_join_references(Join *join, int rtoffset);
 static void set_inner_join_references(Plan *inner_plan,
                          indexed_tlist *outer_itlist);
-static void set_uppernode_references(Plan *plan, Index subvarno);
+static void set_upper_references(Plan *plan, Index subvarno, int rtoffset);
 static indexed_tlist *build_tlist_index(List *tlist);
 static Var *search_indexed_tlist_for_var(Var *var,
                             indexed_tlist *itlist,
-                            Index newvarno);
+                            Index newvarno,
+                            int rtoffset);
 static Var *search_indexed_tlist_for_non_var(Node *node,
                                 indexed_tlist *itlist,
                                 Index newvarno);
-static List *join_references(List *clauses,
-               indexed_tlist *outer_itlist,
-               indexed_tlist *inner_itlist,
-               Index acceptable_rel);
-static Node *join_references_mutator(Node *node,
-                       join_references_context *context);
-static Node *replace_vars_with_subplan_refs(Node *node,
-                              indexed_tlist *subplan_itlist,
-                              Index subvarno);
-static Node *replace_vars_with_subplan_refs_mutator(Node *node,
-                           replace_vars_with_subplan_refs_context *context);
+static List *fix_join_expr(List *clauses,
+                          indexed_tlist *outer_itlist,
+                          indexed_tlist *inner_itlist,
+                          Index acceptable_rel, int rtoffset);
+static Node *fix_join_expr_mutator(Node *node,
+                                  fix_join_expr_context *context);
+static Node *fix_upper_expr(Node *node,
+                           indexed_tlist *subplan_itlist,
+                           Index subvarno,
+                           int rtoffset);
+static Node *fix_upper_expr_mutator(Node *node,
+                                   fix_upper_expr_context *context);
 static bool fix_opfuncids_walker(Node *node, void *context);
 
 
@@ -96,34 +108,87 @@ static bool fix_opfuncids_walker(Node *node, void *context);
  *
  * This is the final processing pass of the planner/optimizer. The plan
  * tree is complete; we just have to adjust some representational details
- * for the convenience of the executor.  We update Vars in upper plan nodes
- * to refer to the outputs of their subplans, and we compute regproc OIDs
- * for operators (ie, we look up the function that implements each op).
+ * for the convenience of the executor:
+ *
+ * 1. We flatten the various subquery rangetables into a single list, and
+ * zero out RangeTblEntry fields that are not useful to the executor.
+ *
+ * 2. We adjust Vars in scan nodes to be consistent with the flat rangetable.
+ *
+ * 3. We adjust Vars in upper plan nodes to refer to the outputs of their
+ * subplans.
+ *
+ * 4. We compute regproc OIDs for operators (ie, we look up the function
+ * that implements each op).
  *
  * We also perform one final optimization step, which is to delete
  * SubqueryScan plan nodes that aren't doing anything useful (ie, have
  * no qual and a no-op targetlist).  The reason for doing this last is that
  * it can't readily be done before set_plan_references, because it would
- * break set_uppernode_references: the Vars in the subquery's top tlist
- * won't match up with the Vars in the outer plan tree.  The SubqueryScan
+ * break set_upper_references: the Vars in the subquery's top tlist
+ * wouldn't match up with the Vars in the outer plan tree.  The SubqueryScan
  * serves a necessary function as a buffer between outer query and subquery
- * variable numbering ... but the executor doesn't care about that, only the
- * planner.
+ * variable numbering ... but after we've flattened the rangetable this is
+ * no longer a problem, since there's only one rtindex namespace.
  *
  * set_plan_references recursively traverses the whole plan tree.
  *
+ * Inputs:
+ * glob: global data for planner run
+ * plan: the topmost node of the plan
+ * rtable: the rangetable for the current subquery
+ *
  * The return value is normally the same Plan node passed in, but can be
  * different when the passed-in Plan is a SubqueryScan we decide isn't needed.
  *
- * Note: to delete a SubqueryScan, we have to renumber Vars in its child nodes
- * and append the modified subquery rangetable to the outer rangetable.
- * Therefore "rtable" is an in/out argument and really should be declared
- * "List **".  But in the interest of notational simplicity we don't do that.
- * (Since rtable can't be NIL if there's a SubqueryScan, the list header
- * address won't change when we append a subquery rangetable.)
+ * The flattened rangetable entries are appended to glob->finalrtable.
+ *
+ * Notice that we modify Plan nodes in-place, but use expression_tree_mutator
+ * to process targetlist and qual expressions.  We can assume that the Plan
+ * nodes were just built by the planner and are not multiply referenced, but
+ * it's not so safe to assume that for expression tree nodes.
  */
 Plan *
-set_plan_references(Plan *plan, List *rtable)
+set_plan_references(PlannerGlobal *glob, Plan *plan, List *rtable)
+{
+   int         rtoffset = list_length(glob->finalrtable);
+   ListCell   *lc;
+
+   /*
+    * In the flat rangetable, we zero out substructure pointers that are
+    * not needed by the executor; this reduces the storage space and
+    * copying cost for cached plans.
+    */
+   foreach(lc, rtable)
+   {
+       RangeTblEntry  *rte = (RangeTblEntry *) lfirst(lc);
+       RangeTblEntry  *newrte;
+
+       /* flat copy to duplicate all the scalar fields */
+       newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry));
+       memcpy(newrte, rte, sizeof(RangeTblEntry));
+
+       /* zap unneeded sub-structure (we keep only the eref Alias) */
+       newrte->subquery = NULL;
+       newrte->funcexpr = NULL;
+       newrte->funccoltypes = NIL;
+       newrte->funccoltypmods = NIL;
+       newrte->values_lists = NIL;
+       newrte->joinaliasvars = NIL;
+       newrte->alias = NULL;
+
+       glob->finalrtable = lappend(glob->finalrtable, newrte);
+   }
+
+   /* Now fix the Plan tree */
+   return set_plan_refs(glob, plan, rtoffset);
+}
+
+/*
+ * set_plan_refs: recurse through the Plan nodes of a single subquery level
+ */
+static Plan *
+set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
 {
    ListCell   *l;
 
@@ -136,73 +201,109 @@ set_plan_references(Plan *plan, List *rtable)
    switch (nodeTag(plan))
    {
        case T_SeqScan:
-           fix_expr_references(plan, (Node *) plan->targetlist);
-           fix_expr_references(plan, (Node *) plan->qual);
+           {
+               SeqScan *splan = (SeqScan *) plan;
+
+               splan->scanrelid += rtoffset;
+               splan->plan.targetlist =
+                   fix_scan_list(splan->plan.targetlist, rtoffset);
+               splan->plan.qual =
+                   fix_scan_list(splan->plan.qual, rtoffset);
+           }
            break;
        case T_IndexScan:
-           fix_expr_references(plan, (Node *) plan->targetlist);
-           fix_expr_references(plan, (Node *) plan->qual);
-           fix_expr_references(plan,
-                               (Node *) ((IndexScan *) plan)->indexqual);
-           fix_expr_references(plan,
-                               (Node *) ((IndexScan *) plan)->indexqualorig);
+           {
+               IndexScan *splan = (IndexScan *) plan;
+
+               splan->scan.scanrelid += rtoffset;
+               splan->scan.plan.targetlist =
+                   fix_scan_list(splan->scan.plan.targetlist, rtoffset);
+               splan->scan.plan.qual =
+                   fix_scan_list(splan->scan.plan.qual, rtoffset);
+               splan->indexqual =
+                   fix_scan_list(splan->indexqual, rtoffset);
+               splan->indexqualorig =
+                   fix_scan_list(splan->indexqualorig, rtoffset);
+           }
            break;
        case T_BitmapIndexScan:
-           /* no need to fix targetlist and qual */
-           Assert(plan->targetlist == NIL);
-           Assert(plan->qual == NIL);
-           fix_expr_references(plan,
-                            (Node *) ((BitmapIndexScan *) plan)->indexqual);
-           fix_expr_references(plan,
-                        (Node *) ((BitmapIndexScan *) plan)->indexqualorig);
+           {
+               BitmapIndexScan *splan = (BitmapIndexScan *) plan;
+
+               splan->scan.scanrelid += rtoffset;
+               /* no need to fix targetlist and qual */
+               Assert(splan->scan.plan.targetlist == NIL);
+               Assert(splan->scan.plan.qual == NIL);
+               splan->indexqual =
+                   fix_scan_list(splan->indexqual, rtoffset);
+               splan->indexqualorig =
+                   fix_scan_list(splan->indexqualorig, rtoffset);
+           }
            break;
        case T_BitmapHeapScan:
-           fix_expr_references(plan, (Node *) plan->targetlist);
-           fix_expr_references(plan, (Node *) plan->qual);
-           fix_expr_references(plan,
-                        (Node *) ((BitmapHeapScan *) plan)->bitmapqualorig);
+           {
+               BitmapHeapScan *splan = (BitmapHeapScan *) plan;
+
+               splan->scan.scanrelid += rtoffset;
+               splan->scan.plan.targetlist =
+                   fix_scan_list(splan->scan.plan.targetlist, rtoffset);
+               splan->scan.plan.qual =
+                   fix_scan_list(splan->scan.plan.qual, rtoffset);
+               splan->bitmapqualorig =
+                   fix_scan_list(splan->bitmapqualorig, rtoffset);
+           }
            break;
        case T_TidScan:
-           fix_expr_references(plan, (Node *) plan->targetlist);
-           fix_expr_references(plan, (Node *) plan->qual);
-           fix_expr_references(plan, (Node *) ((TidScan *) plan)->tidquals);
+           {
+               TidScan *splan = (TidScan *) plan;
+
+               splan->scan.scanrelid += rtoffset;
+               splan->scan.plan.targetlist =
+                   fix_scan_list(splan->scan.plan.targetlist, rtoffset);
+               splan->scan.plan.qual =
+                   fix_scan_list(splan->scan.plan.qual, rtoffset);
+               splan->tidquals =
+                   fix_scan_list(splan->tidquals, rtoffset);
+           }
            break;
        case T_SubqueryScan:
            /* Needs special treatment, see comments below */
-           return set_subqueryscan_references((SubqueryScan *) plan, rtable);
+           return set_subqueryscan_references(glob,
+                                              (SubqueryScan *) plan,
+                                              rtoffset);
        case T_FunctionScan:
-           fix_expr_references(plan, (Node *) plan->targetlist);
-           fix_expr_references(plan, (Node *) plan->qual);
-           fix_expr_references(plan, ((FunctionScan *) plan)->funcexpr);
+           {
+               FunctionScan *splan = (FunctionScan *) plan;
+
+               splan->scan.scanrelid += rtoffset;
+               splan->scan.plan.targetlist =
+                   fix_scan_list(splan->scan.plan.targetlist, rtoffset);
+               splan->scan.plan.qual =
+                   fix_scan_list(splan->scan.plan.qual, rtoffset);
+               splan->funcexpr =
+                   fix_scan_expr(splan->funcexpr, rtoffset);
+           }
            break;
        case T_ValuesScan:
-           fix_expr_references(plan, (Node *) plan->targetlist);
-           fix_expr_references(plan, (Node *) plan->qual);
-           fix_expr_references(plan,
-                               (Node *) ((ValuesScan *) plan)->values_lists);
+           {
+               ValuesScan *splan = (ValuesScan *) plan;
+
+               splan->scan.scanrelid += rtoffset;
+               splan->scan.plan.targetlist =
+                   fix_scan_list(splan->scan.plan.targetlist, rtoffset);
+               splan->scan.plan.qual =
+                   fix_scan_list(splan->scan.plan.qual, rtoffset);
+               splan->values_lists =
+                   fix_scan_list(splan->values_lists, rtoffset);
+           }
            break;
+
        case T_NestLoop:
-           set_join_references((Join *) plan);
-           fix_expr_references(plan, (Node *) plan->targetlist);
-           fix_expr_references(plan, (Node *) plan->qual);
-           fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
-           break;
        case T_MergeJoin:
-           set_join_references((Join *) plan);
-           fix_expr_references(plan, (Node *) plan->targetlist);
-           fix_expr_references(plan, (Node *) plan->qual);
-           fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
-           fix_expr_references(plan,
-                               (Node *) ((MergeJoin *) plan)->mergeclauses);
-           break;
        case T_HashJoin:
-           set_join_references((Join *) plan);
-           fix_expr_references(plan, (Node *) plan->targetlist);
-           fix_expr_references(plan, (Node *) plan->qual);
-           fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
-           fix_expr_references(plan,
-                               (Node *) ((HashJoin *) plan)->hashclauses);
+           set_join_references((Join *) plan, rtoffset);
            break;
+
        case T_Hash:
        case T_Material:
        case T_Sort:
@@ -211,75 +312,113 @@ set_plan_references(Plan *plan, List *rtable)
 
            /*
             * These plan types don't actually bother to evaluate their
-            * targetlists (because they just return their unmodified input
-            * tuples).  The optimizer is lazy about creating really valid
-            * targetlists for them --- it tends to just put in a pointer to
-            * the child plan node's tlist.  Hence, we leave the tlist alone.
-            * In particular, we do not want to process subplans in the tlist,
-            * since we will likely end up reprocessing subplans that also
-            * appear in lower levels of the plan tree!
-            *
+            * targetlistsbecause they just return their unmodified input
+            * tuples.  Even though the targetlist won't be used by the
+            * executor, we fix it up for possible use by EXPLAIN (not to
+            * mention ease of debugging --- wrong varnos are very confusing).
+            */
+           plan->targetlist =
+               fix_scan_list(plan->targetlist, rtoffset);
+           /*
             * Since these plan types don't check quals either, we should not
             * find any qual expression attached to them.
             */
            Assert(plan->qual == NIL);
            break;
        case T_Limit:
-
-           /*
-            * Like the plan types above, Limit doesn't evaluate its tlist or
-            * quals.  It does have live expressions for limit/offset,
-            * however.
-            */
-           Assert(plan->qual == NIL);
-           fix_expr_references(plan, ((Limit *) plan)->limitOffset);
-           fix_expr_references(plan, ((Limit *) plan)->limitCount);
+           {
+               Limit *splan = (Limit *) plan;
+
+               /*
+                * Like the plan types above, Limit doesn't evaluate its tlist
+                * or quals.  It does have live expressions for limit/offset,
+                * however.
+                */
+               splan->plan.targetlist =
+                   fix_scan_list(splan->plan.targetlist, rtoffset);
+               Assert(splan->plan.qual == NIL);
+               splan->limitOffset =
+                   fix_scan_expr(splan->limitOffset, rtoffset);
+               splan->limitCount =
+                   fix_scan_expr(splan->limitCount, rtoffset);
+           }
            break;
        case T_Agg:
        case T_Group:
-           set_uppernode_references(plan, (Index) 0);
-           fix_expr_references(plan, (Node *) plan->targetlist);
-           fix_expr_references(plan, (Node *) plan->qual);
+           set_upper_references(plan, (Index) 0, rtoffset);
            break;
        case T_Result:
-
-           /*
-            * Result may or may not have a subplan; no need to fix up subplan
-            * references if it hasn't got one...
-            *
-            * XXX why does Result use a different subvarno from Agg/Group?
-            */
-           if (plan->lefttree != NULL)
-               set_uppernode_references(plan, (Index) OUTER);
-           fix_expr_references(plan, (Node *) plan->targetlist);
-           fix_expr_references(plan, (Node *) plan->qual);
-           fix_expr_references(plan, ((Result *) plan)->resconstantqual);
+           {
+               Result *splan = (Result *) plan;
+
+               /*
+                * Result may or may not have a subplan; if not, it's more
+                * like a scan node than an upper node.
+                *
+                * XXX why does Result use a different subvarno from Agg/Group?
+                */
+               if (splan->plan.lefttree != NULL)
+                   set_upper_references(plan, (Index) OUTER, rtoffset);
+               else
+               {
+                   splan->plan.targetlist =
+                       fix_scan_list(splan->plan.targetlist, rtoffset);
+                   splan->plan.qual =
+                       fix_scan_list(splan->plan.qual, rtoffset);
+               }
+               /* resconstantqual can't contain any subplan variable refs */
+               splan->resconstantqual =
+                   fix_scan_expr(splan->resconstantqual, rtoffset);
+           }
            break;
        case T_Append:
-
-           /*
-            * Append, like Sort et al, doesn't actually evaluate its
-            * targetlist or check quals, and we haven't bothered to give it
-            * its own tlist copy. So, don't fix targetlist/qual. But do
-            * recurse into child plans.
-            */
-           Assert(plan->qual == NIL);
-           foreach(l, ((Append *) plan)->appendplans)
-               lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable);
+           {
+               Append *splan = (Append *) plan;
+
+               /*
+                * Append, like Sort et al, doesn't actually evaluate its
+                * targetlist or check quals.
+                */
+               splan->plan.targetlist =
+                   fix_scan_list(splan->plan.targetlist, rtoffset);
+               Assert(splan->plan.qual == NIL);
+               foreach(l, splan->appendplans)
+               {
+                   lfirst(l) = set_plan_refs(glob,
+                                             (Plan *) lfirst(l),
+                                             rtoffset);
+               }
+           }
            break;
        case T_BitmapAnd:
-           /* BitmapAnd works like Append, but has no tlist */
-           Assert(plan->targetlist == NIL);
-           Assert(plan->qual == NIL);
-           foreach(l, ((BitmapAnd *) plan)->bitmapplans)
-               lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable);
+           {
+               BitmapAnd *splan = (BitmapAnd *) plan;
+
+               /* BitmapAnd works like Append, but has no tlist */
+               Assert(splan->plan.targetlist == NIL);
+               Assert(splan->plan.qual == NIL);
+               foreach(l, splan->bitmapplans)
+               {
+                   lfirst(l) = set_plan_refs(glob,
+                                             (Plan *) lfirst(l),
+                                             rtoffset);
+               }
+           }
            break;
        case T_BitmapOr:
-           /* BitmapOr works like Append, but has no tlist */
-           Assert(plan->targetlist == NIL);
-           Assert(plan->qual == NIL);
-           foreach(l, ((BitmapOr *) plan)->bitmapplans)
-               lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable);
+           {
+               BitmapOr *splan = (BitmapOr *) plan;
+
+               /* BitmapOr works like Append, but has no tlist */
+               Assert(splan->plan.targetlist == NIL);
+               Assert(splan->plan.qual == NIL);
+               foreach(l, splan->bitmapplans)
+               {
+                   lfirst(l) = set_plan_refs(glob,
+                                             (Plan *) lfirst(l),
+                                             rtoffset);
+               }
+           }
            break;
        default:
            elog(ERROR, "unrecognized node type: %d",
@@ -288,25 +427,15 @@ set_plan_references(Plan *plan, List *rtable)
    }
 
    /*
-    * Now recurse into child plans and initplans, if any
+    * Now recurse into child plans, if any
     *
     * NOTE: it is essential that we recurse into child plans AFTER we set
     * subplan references in this plan's tlist and quals.  If we did the
     * reference-adjustments bottom-up, then we would fail to match this
     * plan's var nodes against the already-modified nodes of the children.
-    * Fortunately, that consideration doesn't apply to SubPlan nodes; else
-    * we'd need two passes over the expression trees.
     */
-   plan->lefttree = set_plan_references(plan->lefttree, rtable);
-   plan->righttree = set_plan_references(plan->righttree, rtable);
-
-   foreach(l, plan->initPlan)
-   {
-       SubPlan    *sp = (SubPlan *) lfirst(l);
-
-       Assert(IsA(sp, SubPlan));
-       sp->plan = set_plan_references(sp->plan, sp->rtable);
-   }
+   plan->lefttree = set_plan_refs(glob, plan->lefttree, rtoffset);
+   plan->righttree = set_plan_refs(glob, plan->righttree, rtoffset);
 
    return plan;
 }
@@ -319,54 +448,29 @@ set_plan_references(Plan *plan, List *rtable)
  * to do the normal processing on it.
  */
 static Plan *
-set_subqueryscan_references(SubqueryScan *plan, List *rtable)
+set_subqueryscan_references(PlannerGlobal *glob,
+                           SubqueryScan *plan,
+                           int rtoffset)
 {
    Plan       *result;
-   RangeTblEntry *rte;
-   ListCell   *l;
 
    /* First, recursively process the subplan */
-   rte = rt_fetch(plan->scan.scanrelid, rtable);
-   Assert(rte->rtekind == RTE_SUBQUERY);
-   plan->subplan = set_plan_references(plan->subplan,
-                                       rte->subquery->rtable);
-
-   /*
-    * We have to process any initplans too; set_plan_references can't do it
-    * for us because of the possibility of double-processing.
-    */
-   foreach(l, plan->scan.plan.initPlan)
-   {
-       SubPlan    *sp = (SubPlan *) lfirst(l);
+   plan->subplan = set_plan_references(glob, plan->subplan, plan->subrtable);
 
-       Assert(IsA(sp, SubPlan));
-       sp->plan = set_plan_references(sp->plan, sp->rtable);
-   }
+   /* subrtable is no longer needed in the plan tree */
+   plan->subrtable = NIL;
 
    if (trivial_subqueryscan(plan))
    {
        /*
-        * We can omit the SubqueryScan node and just pull up the subplan. We
-        * have to merge its rtable into the outer rtable, which means
-        * adjusting varnos throughout the subtree.
+        * We can omit the SubqueryScan node and just pull up the subplan.
         */
-       int         rtoffset = list_length(rtable);
-       List       *sub_rtable;
        ListCell   *lp,
                   *lc;
 
-       sub_rtable = copyObject(rte->subquery->rtable);
-       rtable = list_concat(rtable, sub_rtable);
-
-       /*
-        * we have to copy the subplan to make sure there are no duplicately
-        * linked nodes in it, else adjust_plan_varnos might increment some
-        * varnos twice
-        */
-       result = copyObject(plan->subplan);
-
-       adjust_plan_varnos(result, rtoffset);
+       result = plan->subplan;
 
+       /* We have to be sure we don't lose any initplans */
        result->initPlan = list_concat(plan->scan.plan.initPlan,
                                       result->initPlan);
 
@@ -391,14 +495,17 @@ set_subqueryscan_references(SubqueryScan *plan, List *rtable)
        /*
         * Keep the SubqueryScan node.  We have to do the processing that
         * set_plan_references would otherwise have done on it.  Notice we do
-        * not do set_uppernode_references() here, because a SubqueryScan will
+        * not do set_upper_references() here, because a SubqueryScan will
         * always have been created with correct references to its subplan's
         * outputs to begin with.
         */
-       result = (Plan *) plan;
+       plan->scan.scanrelid += rtoffset;
+       plan->scan.plan.targetlist =
+           fix_scan_list(plan->scan.plan.targetlist, rtoffset);
+       plan->scan.plan.qual =
+           fix_scan_list(plan->scan.plan.qual, rtoffset);
 
-       fix_expr_references(result, (Node *) result->targetlist);
-       fix_expr_references(result, (Node *) result->qual);
+       result = (Plan *) plan;
    }
 
    return result;
@@ -463,261 +570,72 @@ trivial_subqueryscan(SubqueryScan *plan)
 }
 
 /*
- * adjust_plan_varnos
- *     Offset varnos and other rangetable indexes in a plan tree by rtoffset.
- */
-static void
-adjust_plan_varnos(Plan *plan, int rtoffset)
-{
-   ListCell   *l;
-
-   if (plan == NULL)
-       return;
-
-   /*
-    * Plan-type-specific fixes
-    */
-   switch (nodeTag(plan))
-   {
-       case T_SeqScan:
-           ((SeqScan *) plan)->scanrelid += rtoffset;
-           adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-           adjust_expr_varnos((Node *) plan->qual, rtoffset);
-           break;
-       case T_IndexScan:
-           ((IndexScan *) plan)->scan.scanrelid += rtoffset;
-           adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-           adjust_expr_varnos((Node *) plan->qual, rtoffset);
-           adjust_expr_varnos((Node *) ((IndexScan *) plan)->indexqual,
-                              rtoffset);
-           adjust_expr_varnos((Node *) ((IndexScan *) plan)->indexqualorig,
-                              rtoffset);
-           break;
-       case T_BitmapIndexScan:
-           ((BitmapIndexScan *) plan)->scan.scanrelid += rtoffset;
-           /* no need to fix targetlist and qual */
-           Assert(plan->targetlist == NIL);
-           Assert(plan->qual == NIL);
-           adjust_expr_varnos((Node *) ((BitmapIndexScan *) plan)->indexqual,
-                              rtoffset);
-           adjust_expr_varnos((Node *) ((BitmapIndexScan *) plan)->indexqualorig,
-                              rtoffset);
-           break;
-       case T_BitmapHeapScan:
-           ((BitmapHeapScan *) plan)->scan.scanrelid += rtoffset;
-           adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-           adjust_expr_varnos((Node *) plan->qual, rtoffset);
-           adjust_expr_varnos((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig,
-                              rtoffset);
-           break;
-       case T_TidScan:
-           ((TidScan *) plan)->scan.scanrelid += rtoffset;
-           adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-           adjust_expr_varnos((Node *) plan->qual, rtoffset);
-           adjust_expr_varnos((Node *) ((TidScan *) plan)->tidquals,
-                              rtoffset);
-           break;
-       case T_SubqueryScan:
-           ((SubqueryScan *) plan)->scan.scanrelid += rtoffset;
-           adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-           adjust_expr_varnos((Node *) plan->qual, rtoffset);
-           /* we should not recurse into the subquery! */
-           break;
-       case T_FunctionScan:
-           ((FunctionScan *) plan)->scan.scanrelid += rtoffset;
-           adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-           adjust_expr_varnos((Node *) plan->qual, rtoffset);
-           adjust_expr_varnos(((FunctionScan *) plan)->funcexpr,
-                              rtoffset);
-           break;
-       case T_ValuesScan:
-           ((ValuesScan *) plan)->scan.scanrelid += rtoffset;
-           adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-           adjust_expr_varnos((Node *) plan->qual, rtoffset);
-           adjust_expr_varnos((Node *) ((ValuesScan *) plan)->values_lists,
-                              rtoffset);
-           break;
-       case T_NestLoop:
-           adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-           adjust_expr_varnos((Node *) plan->qual, rtoffset);
-           adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset);
-           break;
-       case T_MergeJoin:
-           adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-           adjust_expr_varnos((Node *) plan->qual, rtoffset);
-           adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset);
-           adjust_expr_varnos((Node *) ((MergeJoin *) plan)->mergeclauses,
-                              rtoffset);
-           break;
-       case T_HashJoin:
-           adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-           adjust_expr_varnos((Node *) plan->qual, rtoffset);
-           adjust_expr_varnos((Node *) ((Join *) plan)->joinqual, rtoffset);
-           adjust_expr_varnos((Node *) ((HashJoin *) plan)->hashclauses,
-                              rtoffset);
-           break;
-       case T_Hash:
-       case T_Material:
-       case T_Sort:
-       case T_Unique:
-       case T_SetOp:
-
-           /*
-            * Even though the targetlist won't be used by the executor, we
-            * fix it up for possible use by EXPLAIN (not to mention ease of
-            * debugging --- wrong varnos are very confusing).
-            */
-           adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-           Assert(plan->qual == NIL);
-           break;
-       case T_Limit:
-
-           /*
-            * Like the plan types above, Limit doesn't evaluate its tlist or
-            * quals.  It does have live expressions for limit/offset,
-            * however.
-            */
-           adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-           Assert(plan->qual == NIL);
-           adjust_expr_varnos(((Limit *) plan)->limitOffset, rtoffset);
-           adjust_expr_varnos(((Limit *) plan)->limitCount, rtoffset);
-           break;
-       case T_Agg:
-       case T_Group:
-           adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-           adjust_expr_varnos((Node *) plan->qual, rtoffset);
-           break;
-       case T_Result:
-           adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-           adjust_expr_varnos((Node *) plan->qual, rtoffset);
-           adjust_expr_varnos(((Result *) plan)->resconstantqual, rtoffset);
-           break;
-       case T_Append:
-           adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
-           Assert(plan->qual == NIL);
-           foreach(l, ((Append *) plan)->appendplans)
-               adjust_plan_varnos((Plan *) lfirst(l), rtoffset);
-           break;
-       case T_BitmapAnd:
-           /* BitmapAnd works like Append, but has no tlist */
-           Assert(plan->targetlist == NIL);
-           Assert(plan->qual == NIL);
-           foreach(l, ((BitmapAnd *) plan)->bitmapplans)
-               adjust_plan_varnos((Plan *) lfirst(l), rtoffset);
-           break;
-       case T_BitmapOr:
-           /* BitmapOr works like Append, but has no tlist */
-           Assert(plan->targetlist == NIL);
-           Assert(plan->qual == NIL);
-           foreach(l, ((BitmapOr *) plan)->bitmapplans)
-               adjust_plan_varnos((Plan *) lfirst(l), rtoffset);
-           break;
-       default:
-           elog(ERROR, "unrecognized node type: %d",
-                (int) nodeTag(plan));
-           break;
-   }
-
-   /*
-    * Now recurse into child plans.
-    *
-    * We don't need to (and in fact mustn't) recurse into subqueries, so no
-    * need to examine initPlan list.
-    */
-   adjust_plan_varnos(plan->lefttree, rtoffset);
-   adjust_plan_varnos(plan->righttree, rtoffset);
-}
-
-/*
- * adjust_expr_varnos
- *     Offset varnos of Vars in an expression by rtoffset.
+ * fix_scan_expr
+ *     Do set_plan_references processing on a scan-level expression
  *
- * This is different from the rewriter's OffsetVarNodes in that it has to
- * work on an already-planned expression tree; in particular, we should not
- * disturb INNER and OUTER references. On the other hand, we don't have to
- * recurse into subqueries nor deal with outer-level Vars, so it's pretty
- * simple.
+ * This consists of incrementing all Vars' varnos by rtoffset and
+ * looking up operator opcode info for OpExpr and related nodes.
  */
-static void
-adjust_expr_varnos(Node *node, int rtoffset)
+static Node *
+fix_scan_expr(Node *node, int rtoffset)
 {
-   /* This tree walk requires no special setup, so away we go... */
-   adjust_expr_varnos_walker(node, &rtoffset);
+   fix_scan_expr_context context;
+
+   context.rtoffset = rtoffset;
+   return fix_scan_expr_mutator(node, &context);
 }
 
-static bool
-adjust_expr_varnos_walker(Node *node, int *context)
+static Node *
+fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
 {
    if (node == NULL)
-       return false;
+       return NULL;
    if (IsA(node, Var))
    {
-       Var        *var = (Var *) node;
+       Var        *var = (Var *) copyObject(node);
 
        Assert(var->varlevelsup == 0);
-       if (var->varno > 0 && var->varno != INNER && var->varno != OUTER)
-           var->varno += *context;
+       /*
+        * 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;
-       return false;
+           var->varnoold += context->rtoffset;
+       return (Node *) var;
    }
-   return expression_tree_walker(node, adjust_expr_varnos_walker,
-                                 (void *) context);
-}
-
-/*
- * fix_expr_references
- *   Do final cleanup on expressions (targetlists or quals).
- *
- * This consists of looking up operator opcode info for OpExpr nodes
- * and recursively performing set_plan_references on subplans.
- *
- * The Plan argument is currently unused, but might be needed again someday.
- */
-static void
-fix_expr_references(Plan *plan, Node *node)
-{
-   /* This tree walk requires no special setup, so away we go... */
-   fix_expr_references_walker(node, NULL);
-}
-
-static bool
-fix_expr_references_walker(Node *node, void *context)
-{
-   if (node == NULL)
-       return false;
+   /*
+    * Since we update opcode info in-place, this part could possibly
+    * scribble on the planner's input data structures, but it's OK.
+    */
    if (IsA(node, OpExpr))
        set_opfuncid((OpExpr *) node);
    else if (IsA(node, DistinctExpr))
        set_opfuncid((OpExpr *) node);  /* rely on struct equivalence */
-   else if (IsA(node, ScalarArrayOpExpr))
-       set_sa_opfuncid((ScalarArrayOpExpr *) node);
    else if (IsA(node, NullIfExpr))
        set_opfuncid((OpExpr *) node);  /* rely on struct equivalence */
-   else if (IsA(node, SubPlan))
-   {
-       SubPlan    *sp = (SubPlan *) node;
-
-       sp->plan = set_plan_references(sp->plan, sp->rtable);
-   }
-   return expression_tree_walker(node, fix_expr_references_walker, context);
+   else if (IsA(node, ScalarArrayOpExpr))
+       set_sa_opfuncid((ScalarArrayOpExpr *) node);
+   return expression_tree_mutator(node, fix_scan_expr_mutator,
+                                  (void *) context);
 }
 
 /*
  * set_join_references
- *   Modifies the target list and quals of a join node to reference its
+ *   Modify the target list and quals of a join node to reference its
  *   subplans, by setting the varnos to OUTER or INNER and setting attno
  *   values to the result domain number of either the corresponding outer
- *   or inner join tuple item.
+ *   or inner join tuple item.  Also perform opcode lookup for these
+ *   expressions.
  *
  * 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.
- *
- * 'join' is a join plan node
  */
 static void
-set_join_references(Join *join)
+set_join_references(Join *join, int rtoffset)
 {
    Plan       *outer_plan = join->plan.lefttree;
    Plan       *inner_plan = join->plan.righttree;
@@ -728,43 +646,47 @@ set_join_references(Join *join)
    inner_itlist = build_tlist_index(inner_plan->targetlist);
 
    /* All join plans have tlist, qual, and joinqual */
-   join->plan.targetlist = join_references(join->plan.targetlist,
-                                           outer_itlist,
-                                           inner_itlist,
-                                           (Index) 0);
-   join->plan.qual = join_references(join->plan.qual,
-                                     outer_itlist,
-                                     inner_itlist,
-                                     (Index) 0);
-   join->joinqual = join_references(join->joinqual,
-                                    outer_itlist,
-                                    inner_itlist,
-                                    (Index) 0);
+   join->plan.targetlist = fix_join_expr(join->plan.targetlist,
+                                         outer_itlist,
+                                         inner_itlist,
+                                         (Index) 0,
+                                         rtoffset);
+   join->plan.qual = fix_join_expr(join->plan.qual,
+                                   outer_itlist,
+                                   inner_itlist,
+                                   (Index) 0,
+                                   rtoffset);
+   join->joinqual = fix_join_expr(join->joinqual,
+                                  outer_itlist,
+                                  inner_itlist,
+                                  (Index) 0,
+                                  rtoffset);
 
    /* Now do join-type-specific stuff */
    if (IsA(join, NestLoop))
    {
        /* This processing is split out to handle possible recursion */
-       set_inner_join_references(inner_plan,
-                                 outer_itlist);
+       set_inner_join_references(inner_plan, outer_itlist);
    }
    else if (IsA(join, MergeJoin))
    {
        MergeJoin  *mj = (MergeJoin *) join;
 
-       mj->mergeclauses = join_references(mj->mergeclauses,
-                                          outer_itlist,
-                                          inner_itlist,
-                                          (Index) 0);
+       mj->mergeclauses = fix_join_expr(mj->mergeclauses,
+                                        outer_itlist,
+                                        inner_itlist,
+                                        (Index) 0,
+                                        rtoffset);
    }
    else if (IsA(join, HashJoin))
    {
        HashJoin   *hj = (HashJoin *) join;
 
-       hj->hashclauses = join_references(hj->hashclauses,
-                                         outer_itlist,
-                                         inner_itlist,
-                                         (Index) 0);
+       hj->hashclauses = fix_join_expr(hj->hashclauses,
+                                       outer_itlist,
+                                       inner_itlist,
+                                       (Index) 0,
+                                       rtoffset);
    }
 
    pfree(outer_itlist);
@@ -779,6 +701,10 @@ set_join_references(Join *join)
  * 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(Plan *inner_plan, indexed_tlist *outer_itlist)
@@ -800,14 +726,16 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
            Index       innerrel = innerscan->scan.scanrelid;
 
            /* only refs to outer vars get changed in the inner qual */
-           innerscan->indexqualorig = join_references(indexqualorig,
-                                                      outer_itlist,
-                                                      NULL,
-                                                      innerrel);
-           innerscan->indexqual = join_references(innerscan->indexqual,
-                                                  outer_itlist,
-                                                  NULL,
-                                                  innerrel);
+           innerscan->indexqualorig = fix_join_expr(indexqualorig,
+                                                    outer_itlist,
+                                                    NULL,
+                                                    innerrel,
+                                                    0);
+           innerscan->indexqual = fix_join_expr(innerscan->indexqual,
+                                                outer_itlist,
+                                                NULL,
+                                                innerrel,
+                                                0);
 
            /*
             * We must fix the inner qpqual too, if it has join clauses (this
@@ -815,10 +743,11 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
             * may get rechecked as qpquals).
             */
            if (NumRelids((Node *) inner_plan->qual) > 1)
-               inner_plan->qual = join_references(inner_plan->qual,
-                                                  outer_itlist,
-                                                  NULL,
-                                                  innerrel);
+               inner_plan->qual = fix_join_expr(inner_plan->qual,
+                                                outer_itlist,
+                                                NULL,
+                                                innerrel,
+                                                0);
        }
    }
    else if (IsA(inner_plan, BitmapIndexScan))
@@ -835,14 +764,16 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
            Index       innerrel = innerscan->scan.scanrelid;
 
            /* only refs to outer vars get changed in the inner qual */
-           innerscan->indexqualorig = join_references(indexqualorig,
-                                                      outer_itlist,
-                                                      NULL,
-                                                      innerrel);
-           innerscan->indexqual = join_references(innerscan->indexqual,
-                                                  outer_itlist,
-                                                  NULL,
-                                                  innerrel);
+           innerscan->indexqualorig = fix_join_expr(indexqualorig,
+                                                    outer_itlist,
+                                                    NULL,
+                                                    innerrel,
+                                                    0);
+           innerscan->indexqual = fix_join_expr(innerscan->indexqual,
+                                                outer_itlist,
+                                                NULL,
+                                                innerrel,
+                                                0);
            /* no need to fix inner qpqual */
            Assert(inner_plan->qual == NIL);
        }
@@ -862,10 +793,11 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
 
        /* only refs to outer vars get changed in the inner qual */
        if (NumRelids((Node *) bitmapqualorig) > 1)
-           innerscan->bitmapqualorig = join_references(bitmapqualorig,
-                                                       outer_itlist,
-                                                       NULL,
-                                                       innerrel);
+           innerscan->bitmapqualorig = fix_join_expr(bitmapqualorig,
+                                                     outer_itlist,
+                                                     NULL,
+                                                     innerrel,
+                                                     0);
 
        /*
         * We must fix the inner qpqual too, if it has join clauses (this
@@ -873,14 +805,14 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
         * get rechecked as qpquals).
         */
        if (NumRelids((Node *) inner_plan->qual) > 1)
-           inner_plan->qual = join_references(inner_plan->qual,
-                                              outer_itlist,
-                                              NULL,
-                                              innerrel);
+           inner_plan->qual = fix_join_expr(inner_plan->qual,
+                                            outer_itlist,
+                                            NULL,
+                                            innerrel,
+                                            0);
 
        /* Now recurse */
-       set_inner_join_references(inner_plan->lefttree,
-                                 outer_itlist);
+       set_inner_join_references(inner_plan->lefttree, outer_itlist);
    }
    else if (IsA(inner_plan, BitmapAnd))
    {
@@ -890,8 +822,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
 
        foreach(l, innerscan->bitmapplans)
        {
-           set_inner_join_references((Plan *) lfirst(l),
-                                     outer_itlist);
+           set_inner_join_references((Plan *) lfirst(l), outer_itlist);
        }
    }
    else if (IsA(inner_plan, BitmapOr))
@@ -902,10 +833,20 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
 
        foreach(l, innerscan->bitmapplans)
        {
-           set_inner_join_references((Plan *) lfirst(l),
-                                     outer_itlist);
+           set_inner_join_references((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(innerscan->tidquals,
+                                           outer_itlist,
+                                           NULL,
+                                           innerrel,
+                                           0);
+   }
    else if (IsA(inner_plan, Append))
    {
        /*
@@ -917,8 +858,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
 
        foreach(l, appendplan->appendplans)
        {
-           set_inner_join_references((Plan *) lfirst(l),
-                                     outer_itlist);
+           set_inner_join_references((Plan *) lfirst(l), outer_itlist);
        }
    }
    else if (IsA(inner_plan, Result))
@@ -929,22 +869,13 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
        if (result->plan.lefttree)
            set_inner_join_references(result->plan.lefttree, outer_itlist);
    }
-   else if (IsA(inner_plan, TidScan))
-   {
-       TidScan    *innerscan = (TidScan *) inner_plan;
-       Index       innerrel = innerscan->scan.scanrelid;
-
-       innerscan->tidquals = join_references(innerscan->tidquals,
-                                             outer_itlist,
-                                             NULL,
-                                             innerrel);
-   }
 }
 
 /*
- * set_uppernode_references
+ * set_upper_references
  *   Update the targetlist and quals of an upper-level plan node
  *   to refer to the tuples returned by its lefttree subplan.
+ *   Also perform opcode lookup for these expressions.
  *
  * This is used for single-input plan types like Agg, Group, Result.
  *
@@ -958,7 +889,7 @@ set_inner_join_references(Plan *inner_plan, indexed_tlist *outer_itlist)
  * the expression.
  */
 static void
-set_uppernode_references(Plan *plan, Index subvarno)
+set_upper_references(Plan *plan, Index subvarno, int rtoffset)
 {
    Plan       *subplan = plan->lefttree;
    indexed_tlist *subplan_itlist;
@@ -976,9 +907,10 @@ set_uppernode_references(Plan *plan, Index subvarno)
        TargetEntry *tle = (TargetEntry *) lfirst(l);
        Node       *newexpr;
 
-       newexpr = replace_vars_with_subplan_refs((Node *) tle->expr,
-                                                subplan_itlist,
-                                                subvarno);
+       newexpr = fix_upper_expr((Node *) tle->expr,
+                                subplan_itlist,
+                                subvarno,
+                                rtoffset);
        tle = flatCopyTargetEntry(tle);
        tle->expr = (Expr *) newexpr;
        output_targetlist = lappend(output_targetlist, tle);
@@ -986,9 +918,10 @@ set_uppernode_references(Plan *plan, Index subvarno)
    plan->targetlist = output_targetlist;
 
    plan->qual = (List *)
-       replace_vars_with_subplan_refs((Node *) plan->qual,
-                                      subplan_itlist,
-                                      subvarno);
+       fix_upper_expr((Node *) plan->qual,
+                      subplan_itlist,
+                      subvarno,
+                      rtoffset);
 
    pfree(subplan_itlist);
 }
@@ -1096,10 +1029,12 @@ build_tlist_index_other_vars(List *tlist, Index ignore_rel)
  *
  * If a match is found, return a copy of the given Var with suitably
  * modified varno/varattno (to wit, newvarno and the resno of the TLE entry).
+ * Also ensure that varnoold is incremented by rtoffset.
  * If no match, return NULL.
  */
 static Var *
-search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, Index newvarno)
+search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist,
+                            Index newvarno, int rtoffset)
 {
    Index       varno = var->varno;
    AttrNumber  varattno = var->varattno;
@@ -1117,6 +1052,8 @@ search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, Index newvarno)
 
            newvar->varno = newvarno;
            newvar->varattno = vinfo->resno;
+           if (newvar->varnoold > 0)
+               newvar->varnoold += rtoffset;
            return newvar;
        }
        vinfo++;
@@ -1157,22 +1094,23 @@ search_indexed_tlist_for_non_var(Node *node,
 }
 
 /*
- * join_references
- *    Creates a new set of targetlist entries or join qual clauses by
+ * fix_join_expr
+ *    Create a new set of targetlist entries or join qual clauses by
  *    changing the varno/varattno values of variables in the clauses
  *    to reference target list values from the outer and inner join
- *    relation target lists.
+ *    relation target lists.  Also perform opcode lookup.
  *
  * 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, but not touch the Vars of the inner relation.  (We also
- * implement RETURNING clause fixup using this second scenario.)
+ * 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 ID of the inner relation.
+ * 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
@@ -1180,27 +1118,29 @@ search_indexed_tlist_for_non_var(Node *node,
  *     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.
  */
 static List *
-join_references(List *clauses,
-               indexed_tlist *outer_itlist,
-               indexed_tlist *inner_itlist,
-               Index acceptable_rel)
+fix_join_expr(List *clauses,
+             indexed_tlist *outer_itlist,
+             indexed_tlist *inner_itlist,
+             Index acceptable_rel,
+             int rtoffset)
 {
-   join_references_context context;
+   fix_join_expr_context context;
 
    context.outer_itlist = outer_itlist;
    context.inner_itlist = inner_itlist;
    context.acceptable_rel = acceptable_rel;
-   return (List *) join_references_mutator((Node *) clauses, &context);
+   context.rtoffset = rtoffset;
+   return (List *) fix_join_expr_mutator((Node *) clauses, &context);
 }
 
 static Node *
-join_references_mutator(Node *node,
-                       join_references_context *context)
+fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
 {
    Var        *newvar;
 
@@ -1213,21 +1153,28 @@ join_references_mutator(Node *node,
        /* First look for the var in the input tlists */
        newvar = search_indexed_tlist_for_var(var,
                                              context->outer_itlist,
-                                             OUTER);
+                                             OUTER,
+                                             context->rtoffset);
        if (newvar)
            return (Node *) newvar;
        if (context->inner_itlist)
        {
            newvar = search_indexed_tlist_for_var(var,
                                                  context->inner_itlist,
-                                                 INNER);
+                                                 INNER,
+                                                 context->rtoffset);
            if (newvar)
                return (Node *) newvar;
        }
 
-       /* Return the Var unmodified, if it's for acceptable_rel */
+       /* If it's for acceptable_rel, adjust and return it */
        if (var->varno == context->acceptable_rel)
-           return (Node *) copyObject(var);
+       {
+           var = (Var *) copyObject(var);
+           var->varno += context->rtoffset;
+           var->varnoold += context->rtoffset;
+           return (Node *) var;
+       }
 
        /* No referent found for Var */
        elog(ERROR, "variable not found in subplan target lists");
@@ -1249,16 +1196,30 @@ join_references_mutator(Node *node,
        if (newvar)
            return (Node *) newvar;
    }
+   /*
+    * Since we update opcode info in-place, this part could possibly
+    * scribble on the planner's input data structures, but it's OK.
+    */
+   if (IsA(node, OpExpr))
+       set_opfuncid((OpExpr *) node);
+   else if (IsA(node, DistinctExpr))
+       set_opfuncid((OpExpr *) node);  /* rely on struct equivalence */
+   else if (IsA(node, NullIfExpr))
+       set_opfuncid((OpExpr *) node);  /* rely on struct equivalence */
+   else if (IsA(node, ScalarArrayOpExpr))
+       set_sa_opfuncid((ScalarArrayOpExpr *) node);
    return expression_tree_mutator(node,
-                                  join_references_mutator,
+                                  fix_join_expr_mutator,
                                   (void *) context);
 }
 
 /*
- * replace_vars_with_subplan_refs
- *     This routine modifies an expression tree so that all Var nodes
- *     reference target nodes of a subplan.  It is used to fix up
- *     target and qual expressions of non-join upper-level plan nodes.
+ * fix_upper_expr
+ *     Modifies an expression tree so that all Var nodes reference outputs
+ *     of a subplan.  Also performs opcode lookup.
+ *
+ * This is used to fix up target and qual expressions of non-join upper-level
+ * plan nodes.
  *
  * An error is raised if no matching var can be found in the subplan tlist
  * --- so this routine should only be applied to nodes whose subplans'
@@ -1273,26 +1234,28 @@ join_references_mutator(Node *node,
  * 'node': the tree to be fixed (a target item or qual)
  * 'subplan_itlist': indexed target list for subplan
  * 'subvarno': varno to be assigned to all Vars
+ * 'rtoffset': how much to increment varnoold by
  *
  * The resulting tree is a copy of the original in which all Var nodes have
  * varno = subvarno, varattno = resno of corresponding subplan target.
  * The original tree is not modified.
  */
 static Node *
-replace_vars_with_subplan_refs(Node *node,
-                              indexed_tlist *subplan_itlist,
-                              Index subvarno)
+fix_upper_expr(Node *node,
+              indexed_tlist *subplan_itlist,
+              Index subvarno,
+              int rtoffset)
 {
-   replace_vars_with_subplan_refs_context context;
+   fix_upper_expr_context context;
 
    context.subplan_itlist = subplan_itlist;
    context.subvarno = subvarno;
-   return replace_vars_with_subplan_refs_mutator(node, &context);
+   context.rtoffset = rtoffset;
+   return fix_upper_expr_mutator(node, &context);
 }
 
 static Node *
-replace_vars_with_subplan_refs_mutator(Node *node,
-                            replace_vars_with_subplan_refs_context *context)
+fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
 {
    Var        *newvar;
 
@@ -1304,7 +1267,8 @@ replace_vars_with_subplan_refs_mutator(Node *node,
 
        newvar = search_indexed_tlist_for_var(var,
                                              context->subplan_itlist,
-                                             context->subvarno);
+                                             context->subvarno,
+                                             context->rtoffset);
        if (!newvar)
            elog(ERROR, "variable not found in subplan target list");
        return (Node *) newvar;
@@ -1318,8 +1282,20 @@ replace_vars_with_subplan_refs_mutator(Node *node,
        if (newvar)
            return (Node *) newvar;
    }
+   /*
+    * Since we update opcode info in-place, this part could possibly
+    * scribble on the planner's input data structures, but it's OK.
+    */
+   if (IsA(node, OpExpr))
+       set_opfuncid((OpExpr *) node);
+   else if (IsA(node, DistinctExpr))
+       set_opfuncid((OpExpr *) node);  /* rely on struct equivalence */
+   else if (IsA(node, NullIfExpr))
+       set_opfuncid((OpExpr *) node);  /* rely on struct equivalence */
+   else if (IsA(node, ScalarArrayOpExpr))
+       set_sa_opfuncid((ScalarArrayOpExpr *) node);
    return expression_tree_mutator(node,
-                                  replace_vars_with_subplan_refs_mutator,
+                                  fix_upper_expr_mutator,
                                   (void *) context);
 }
 
@@ -1335,12 +1311,15 @@ replace_vars_with_subplan_refs_mutator(Node *node,
  * adjusted RETURNING list, result-table Vars will still have their
  * original varno, but Vars for other rels will have varno OUTER.
  *
- * We also must apply fix_expr_references to the list.
+ * We also must perform opcode lookup.
  *
  * 'rlist': the RETURNING targetlist to be fixed
  * 'topplan': the top Plan node for the query (not yet passed through
  *     set_plan_references)
- * 'resultRelation': RT index of the query's result relation
+ * 'resultRelation': RT index of the associated result relation
+ *
+ * Note: we assume that result relations will have rtoffset zero, that is,
+ * they are not coming from a subplan.
  */
 List *
 set_returning_clause_references(List *rlist,
@@ -1350,20 +1329,19 @@ set_returning_clause_references(List *rlist,
    indexed_tlist *itlist;
 
    /*
-    * We can perform the desired Var fixup by abusing the join_references
+    * 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
-    * join_references to convert RETURNING Vars into references to those
+    * fix_join_expr to convert RETURNING Vars into references to those
     * tlist entries, while leaving result-rel Vars as-is.
     */
    itlist = build_tlist_index_other_vars(topplan->targetlist, resultRelation);
 
-   rlist = join_references(rlist,
-                           itlist,
-                           NULL,
-                           resultRelation);
-
-   fix_expr_references(topplan, (Node *) rlist);
+   rlist = fix_join_expr(rlist,
+                         itlist,
+                         NULL,
+                         resultRelation,
+                         0);
 
    pfree(itlist);
 
@@ -1399,10 +1377,10 @@ fix_opfuncids_walker(Node *node, void *context)
        set_opfuncid((OpExpr *) node);
    else if (IsA(node, DistinctExpr))
        set_opfuncid((OpExpr *) node);  /* rely on struct equivalence */
-   else if (IsA(node, ScalarArrayOpExpr))
-       set_sa_opfuncid((ScalarArrayOpExpr *) node);
    else if (IsA(node, NullIfExpr))
        set_opfuncid((OpExpr *) node);  /* rely on struct equivalence */
+   else if (IsA(node, ScalarArrayOpExpr))
+       set_sa_opfuncid((ScalarArrayOpExpr *) node);
    return expression_tree_walker(node, fix_opfuncids_walker, context);
 }
 
index b62aeb853d85e0e4e460c1e8cb696ff3afe6a1cc..d19e9d298c01cbd632811cdb6d6f08e4a7ada497 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.120 2007/02/19 07:03:30 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.121 2007/02/22 22:00:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -46,6 +46,7 @@ typedef struct process_sublinks_context
 
 typedef struct finalize_primnode_context
 {
+   PlannerInfo *root;
    Bitmapset  *paramids;       /* Set of PARAM_EXEC paramids found */
    Bitmapset  *outer_params;   /* Set of accessible outer paramids */
 } finalize_primnode_context;
@@ -57,12 +58,13 @@ static Node *convert_testexpr(PlannerInfo *root,
                              List **righthandIds);
 static Node *convert_testexpr_mutator(Node *node,
                         convert_testexpr_context *context);
-static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
+static bool subplan_is_hashable(SubLink *slink, SubPlan *node, Plan *plan);
 static bool hash_ok_operator(OpExpr *expr);
 static Node *replace_correlation_vars_mutator(Node *node, PlannerInfo *root);
 static Node *process_sublinks_mutator(Node *node,
                                      process_sublinks_context *context);
-static Bitmapset *finalize_plan(Plan *plan, List *rtable,
+static Bitmapset *finalize_plan(PlannerInfo *root,
+             Plan *plan,
              Bitmapset *outer_params,
              Bitmapset *valid_params);
 static bool finalize_primnode(Node *node, finalize_primnode_context *context);
@@ -203,6 +205,24 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod)
    return retval;
 }
 
+/*
+ * Get the datatype of the first column of the plan's output.
+ *
+ * This is a hack to support exprType(), which doesn't have any way to get
+ * at the plan associated with a SubPlan node.  We really only need the value
+ * for EXPR_SUBLINK and ARRAY_SUBLINK subplans, but for consistency we set
+ * it always.
+ */
+static Oid
+get_first_col_type(Plan *plan)
+{
+   TargetEntry *tent = (TargetEntry *) linitial(plan->targetlist);
+
+   Assert(IsA(tent, TargetEntry));
+   Assert(!tent->resjunk);
+   return exprType((Node *) tent->expr);
+}
+
 /*
  * Convert a SubLink (as created by the parser) into a SubPlan.
  *
@@ -219,10 +239,11 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod)
 static Node *
 make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual)
 {
-   SubPlan    *node = makeNode(SubPlan);
    Query      *subquery = (Query *) (slink->subselect);
    double      tuple_fraction;
+   SubPlan    *node;
    Plan       *plan;
+   PlannerInfo *subroot;
    Bitmapset  *tmpset;
    int         paramid;
    Node       *result;
@@ -266,22 +287,19 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual)
    /*
     * Generate the plan for the subquery.
     */
-   node->plan = plan = subquery_planner(root->glob, subquery,
-                                        root->query_level + 1,
-                                        tuple_fraction,
-                                        NULL);
-
-   /* Assign quasi-unique ID to this SubPlan */
-   node->plan_id = root->glob->next_plan_id++;
-
-   node->rtable = subquery->rtable;
+   plan = subquery_planner(root->glob, subquery,
+                           root->query_level + 1,
+                           tuple_fraction,
+                           &subroot);
 
    /*
-    * Initialize other fields of the SubPlan node.
+    * Initialize the SubPlan node.  Note plan_id isn't set yet.
     */
+   node = makeNode(SubPlan);
    node->subLinkType = slink->subLinkType;
    node->testexpr = NULL;
    node->paramIds = NIL;
+   node->firstColType = get_first_col_type(plan);
    node->useHashTable = false;
    /* At top level of a qual, can treat UNKNOWN the same as FALSE */
    node->unknownEqFalse = isTopQual;
@@ -384,7 +402,7 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual)
         * tuple.  But if it's an IN (= ANY) test, we might be able to use a
         * hashtable to avoid comparing all the tuples.
         */
-       if (subplan_is_hashable(slink, node))
+       if (subplan_is_hashable(slink, node, plan))
            node->useHashTable = true;
 
        /*
@@ -411,7 +429,7 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual)
                    break;
            }
            if (use_material)
-               node->plan = plan = materialize_finished_plan(plan);
+               plan = materialize_finished_plan(plan);
        }
 
        /*
@@ -435,6 +453,15 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual)
        result = (Node *) node;
    }
 
+   /*
+    * Add the subplan and its rtable to the global lists.
+    */
+   root->glob->subplans = lappend(root->glob->subplans,
+                                  plan);
+   root->glob->subrtables = lappend(root->glob->subrtables,
+                                    subroot->parse->rtable);
+   node->plan_id = list_length(root->glob->subplans);
+
    return result;
 }
 
@@ -539,7 +566,7 @@ convert_testexpr_mutator(Node *node,
  * on its plan and parParam fields, however.
  */
 static bool
-subplan_is_hashable(SubLink *slink, SubPlan *node)
+subplan_is_hashable(SubLink *slink, SubPlan *node, Plan *plan)
 {
    double      subquery_size;
    ListCell   *l;
@@ -574,8 +601,8 @@ subplan_is_hashable(SubLink *slink, SubPlan *node)
     * actually be stored as MinimalTuples; this provides some fudge factor
     * for hashtable overhead.)
     */
-   subquery_size = node->plan->plan_rows *
-       (MAXALIGN(node->plan->plan_width) + MAXALIGN(sizeof(HeapTupleHeaderData)));
+   subquery_size = plan->plan_rows *
+       (MAXALIGN(plan->plan_width) + MAXALIGN(sizeof(HeapTupleHeaderData)));
    if (subquery_size > work_mem * 1024L)
        return false;
 
@@ -964,7 +991,7 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan)
    /*
     * Now recurse through plan tree.
     */
-   (void) finalize_plan(plan, root->parse->rtable, outer_params, valid_params);
+   (void) finalize_plan(root, plan, outer_params, valid_params);
 
    bms_free(outer_params);
    bms_free(valid_params);
@@ -988,16 +1015,16 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan)
    initplan_cost = 0;
    foreach(l, plan->initPlan)
    {
-       SubPlan    *initplan = (SubPlan *) lfirst(l);
+       SubPlan    *initsubplan = (SubPlan *) lfirst(l);
+       Plan       *initplan = planner_subplan_get_plan(root, initsubplan);
        ListCell   *l2;
 
-       initExtParam = bms_add_members(initExtParam,
-                                      initplan->plan->extParam);
-       foreach(l2, initplan->setParam)
+       initExtParam = bms_add_members(initExtParam, initplan->extParam);
+       foreach(l2, initsubplan->setParam)
        {
            initSetParam = bms_add_member(initSetParam, lfirst_int(l2));
        }
-       initplan_cost += initplan->plan->total_cost;
+       initplan_cost += initplan->total_cost;
    }
    /* allParam must include all these params */
    plan->allParam = bms_add_members(plan->allParam, initExtParam);
@@ -1019,7 +1046,7 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan)
  * This is just an internal notational convenience.
  */
 static Bitmapset *
-finalize_plan(Plan *plan, List *rtable,
+finalize_plan(PlannerInfo *root, Plan *plan,
              Bitmapset *outer_params, Bitmapset *valid_params)
 {
    finalize_primnode_context context;
@@ -1027,6 +1054,7 @@ finalize_plan(Plan *plan, List *rtable,
    if (plan == NULL)
        return NULL;
 
+   context.root = root;
    context.paramids = NULL;    /* initialize set to empty */
    context.outer_params = outer_params;
 
@@ -1110,8 +1138,8 @@ finalize_plan(Plan *plan, List *rtable,
                {
                    context.paramids =
                        bms_add_members(context.paramids,
-                                       finalize_plan((Plan *) lfirst(l),
-                                                     rtable,
+                                       finalize_plan(root,
+                                                     (Plan *) lfirst(l),
                                                      outer_params,
                                                      valid_params));
                }
@@ -1126,8 +1154,8 @@ finalize_plan(Plan *plan, List *rtable,
                {
                    context.paramids =
                        bms_add_members(context.paramids,
-                                       finalize_plan((Plan *) lfirst(l),
-                                                     rtable,
+                                       finalize_plan(root,
+                                                     (Plan *) lfirst(l),
                                                      outer_params,
                                                      valid_params));
                }
@@ -1142,8 +1170,8 @@ finalize_plan(Plan *plan, List *rtable,
                {
                    context.paramids =
                        bms_add_members(context.paramids,
-                                       finalize_plan((Plan *) lfirst(l),
-                                                     rtable,
+                                       finalize_plan(root,
+                                                     (Plan *) lfirst(l),
                                                      outer_params,
                                                      valid_params));
                }
@@ -1193,14 +1221,14 @@ finalize_plan(Plan *plan, List *rtable,
 
    /* Process left and right child plans, if any */
    context.paramids = bms_add_members(context.paramids,
-                                      finalize_plan(plan->lefttree,
-                                                    rtable,
+                                      finalize_plan(root,
+                                                    plan->lefttree,
                                                     outer_params,
                                                     valid_params));
 
    context.paramids = bms_add_members(context.paramids,
-                                      finalize_plan(plan->righttree,
-                                                    rtable,
+                                      finalize_plan(root,
+                                                    plan->righttree,
                                                     outer_params,
                                                     valid_params));
 
@@ -1252,10 +1280,11 @@ finalize_primnode(Node *node, finalize_primnode_context *context)
    if (is_subplan(node))
    {
        SubPlan    *subplan = (SubPlan *) node;
+       Plan       *plan = planner_subplan_get_plan(context->root, subplan);
 
        /* Add outer-level params needed by the subplan to paramids */
        context->paramids = bms_join(context->paramids,
-                                    bms_intersect(subplan->plan->extParam,
+                                    bms_intersect(plan->extParam,
                                                   context->outer_params));
        /* fall through to recurse into subplan args */
    }
@@ -1299,16 +1328,21 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
    root->query_level--;
    root->init_plans = saved_init_plans;
 
+   /*
+    * Add the subplan and its rtable to the global lists.
+    */
+   root->glob->subplans = lappend(root->glob->subplans,
+                                  plan);
+   root->glob->subrtables = lappend(root->glob->subrtables,
+                                    root->parse->rtable);
+
    /*
     * Create a SubPlan node and add it to the outer list of InitPlans.
     */
    node = makeNode(SubPlan);
    node->subLinkType = EXPR_SUBLINK;
-   node->plan = plan;
-   /* Assign quasi-unique ID to this SubPlan */
-   node->plan_id = root->glob->next_plan_id++;
-
-   node->rtable = root->parse->rtable;
+   node->firstColType = get_first_col_type(plan);
+   node->plan_id = list_length(root->glob->subplans);
 
    root->init_plans = lappend(root->init_plans, node);
 
index 2f06e1bf5322dfe3a03c14d310c9dbc03d964590..f09ddb1d23d1111205017deea44617f66fef7be0 100644 (file)
@@ -22,7 +22,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.138 2007/02/19 07:03:30 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.139 2007/02/22 22:00:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -169,6 +169,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
        RangeTblRef *rtr = (RangeTblRef *) setOp;
        RangeTblEntry *rte = rt_fetch(rtr->rtindex, root->parse->rtable);
        Query      *subquery = rte->subquery;
+       PlannerInfo *subroot;
        Plan       *subplan,
                   *plan;
 
@@ -180,7 +181,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
        subplan = subquery_planner(root->glob, subquery,
                                   root->query_level + 1,
                                   tuple_fraction,
-                                  NULL);
+                                  &subroot);
 
        /*
         * Add a SubqueryScan with the caller-requested targetlist
@@ -193,7 +194,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
                                                   refnames_tlist),
                              NIL,
                              rtr->rtindex,
-                             subplan);
+                             subplan,
+                             subroot->parse->rtable);
 
        /*
         * We don't bother to determine the subquery's output ordering since
@@ -223,7 +225,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
         * output columns.
         *
         * XXX you don't really want to know about this: setrefs.c will apply
-        * replace_vars_with_subplan_refs() to the Result node's tlist. This
+        * fix_upper_expr() to the Result node's tlist. This
         * would fail if the Vars generated by generate_setop_tlist() were not
         * exactly equal() to the corresponding tlist entries of the subplan.
         * However, since the subplan was generated by generate_union_plan()
@@ -235,7 +237,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
            !tlist_same_datatypes(plan->targetlist, colTypes, junkOK))
        {
            plan = (Plan *)
-               make_result(generate_setop_tlist(colTypes, flag,
+               make_result(root,
+                           generate_setop_tlist(colTypes, flag,
                                                 0,
                                                 false,
                                                 plan->targetlist,
@@ -1216,28 +1219,6 @@ adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context)
    Assert(!IsA(node, SubLink));
    Assert(!IsA(node, Query));
 
-   /*
-    * BUT: although we don't need to recurse into subplans, we do need to
-    * make sure that they are copied, not just referenced as
-    * expression_tree_mutator will do by default.  Otherwise we'll have the
-    * same subplan node referenced from each arm of the finished APPEND plan,
-    * which will cause trouble in the executor.  This is a kluge that should
-    * go away when we redesign querytrees.
-    */
-   if (is_subplan(node))
-   {
-       SubPlan    *subplan;
-
-       /* Copy the node and process subplan args */
-       node = expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
-                                      (void *) context);
-       /* Make sure we have separate copies of subplan and its rtable */
-       subplan = (SubPlan *) node;
-       subplan->plan = copyObject(subplan->plan);
-       subplan->rtable = copyObject(subplan->rtable);
-       return node;
-   }
-
    return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
                                   (void *) context);
 }
index f90d55001979b65c15f8fdb222473cc349e83a1e..852b0f533e75d4251e128bb5aae0a3eb3e05b631 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.235 2007/02/19 07:03:30 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.236 2007/02/22 22:00:24 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -2989,7 +2989,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
             */
            if (contain_subplans(param))
                goto fail;
-           cost_qual_eval(&eval_cost, list_make1(param));
+           cost_qual_eval(&eval_cost, list_make1(param), NULL);
            if (eval_cost.startup + eval_cost.per_tuple >
                10 * cpu_operator_cost)
                goto fail;
index 8cc6c81746f3db605c323198a344483d1c2c2e69..6dfd2f61149e71c2ef87ec965b64a1b850e04aea 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.85 2007/01/20 20:45:40 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.86 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -82,6 +82,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
    rel->pages = 0;
    rel->tuples = 0;
    rel->subplan = NULL;
+   rel->subrtable = NIL;
    rel->baserestrictinfo = NIL;
    rel->baserestrictcost.startup = 0;
    rel->baserestrictcost.per_tuple = 0;
@@ -333,6 +334,7 @@ build_join_rel(PlannerInfo *root,
    joinrel->pages = 0;
    joinrel->tuples = 0;
    joinrel->subplan = NULL;
+   joinrel->subrtable = NIL;
    joinrel->baserestrictinfo = NIL;
    joinrel->baserestrictcost.startup = 0;
    joinrel->baserestrictcost.per_tuple = 0;
index 8f34748f9987372ab03f8e52116d3bf428fc0592..5791215a95f29589e5ab9375498d0a6e3f182c61 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.211 2007/02/11 22:18:15 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.212 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1769,12 +1769,7 @@ exprType(Node *expr)
                    subplan->subLinkType == ARRAY_SUBLINK)
                {
                    /* get the type of the subselect's first target column */
-                   TargetEntry *tent;
-
-                   tent = (TargetEntry *) linitial(subplan->plan->targetlist);
-                   Assert(IsA(tent, TargetEntry));
-                   Assert(!tent->resjunk);
-                   type = exprType((Node *) tent->expr);
+                   type = subplan->firstColType;
                    if (subplan->subLinkType == ARRAY_SUBLINK)
                    {
                        type = get_array_type(type);
@@ -1782,7 +1777,7 @@ exprType(Node *expr)
                            ereport(ERROR,
                                    (errcode(ERRCODE_UNDEFINED_OBJECT),
                                     errmsg("could not find array type for data type %s",
-                           format_type_be(exprType((Node *) tent->expr)))));
+                                           format_type_be(subplan->firstColType))));
                    }
                }
                else
index c6b8e8481c498d822de7700ca293713910021716..f25a8d8f70de62575cec804b5bedd90b5efdbb6b 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.226 2007/02/19 07:03:31 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.227 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -4964,7 +4964,7 @@ genericcostestimate(PlannerInfo *root,
     * more expensive than it's worth, though, considering all the other
     * inaccuracies here ...
     */
-   cost_qual_eval(&index_qual_cost, indexQuals);
+   cost_qual_eval(&index_qual_cost, indexQuals, root);
    qual_op_cost = cpu_operator_cost * list_length(indexQuals);
    qual_arg_cost = index_qual_cost.startup +
        index_qual_cost.per_tuple - qual_op_cost;
index 38260b5ecd6c211dcd9b18ed590f688b0d04e1ee..7dfcabf5ac28227979700e7794a3baa71587a6bb 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.137 2007/02/20 17:32:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.138 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -130,7 +130,6 @@ extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc,
            ScanDirection direction, long count);
 extern void ExecutorEnd(QueryDesc *queryDesc);
 extern void ExecutorRewind(QueryDesc *queryDesc);
-extern void ExecCheckRTPerms(List *rangeTable);
 extern void ExecEndPlan(PlanState *planstate, EState *estate);
 extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
 extern void ExecConstraints(ResultRelInfo *resultRelInfo,
index b576f4610e3e75e7b7e84de5bb6b52b676db18ee..d0d47a8fd2b1ffce3a43753d14165fcff5d0002f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.168 2007/02/20 17:32:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.169 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -302,7 +302,7 @@ typedef struct EState
    ScanDirection es_direction; /* current scan direction */
    Snapshot    es_snapshot;    /* time qual to use */
    Snapshot    es_crosscheck_snapshot; /* crosscheck time qual for RI */
-   List       *es_range_table; /* List of RangeTableEntrys */
+   List       *es_range_table; /* List of RangeTblEntry */
 
    /* Info about target table for insert/update/delete queries: */
    ResultRelInfo *es_result_relations; /* array of ResultRelInfos */
index 537981462b23e7b51585f63ab97e9116e8c37106..5a7445f69c0522db705059fdca13beade304492a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.91 2007/02/20 17:32:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.92 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,6 +48,8 @@ typedef struct PlannedStmt
 
    IntoClause *into;           /* target for SELECT INTO / CREATE TABLE AS */
 
+   List       *subplans;       /* Plan trees for SubPlan expressions */
+
    /*
     * If the query has a returningList then the planner will store a list of
     * processed targetlists (one per result relation) here.  We must have a
@@ -65,6 +67,10 @@ typedef struct PlannedStmt
    int         nParamExec;     /* number of PARAM_EXEC Params used */
 } PlannedStmt;
 
+/* macro for fetching the Plan associated with a SubPlan node */
+#define exec_subplan_get_plan(plannedstmt, subplan) \
+   ((Plan *) list_nth((plannedstmt)->subplans, (subplan)->plan_id - 1))
+
 
 /* ----------------
  *     Plan node
@@ -313,12 +319,17 @@ typedef struct TidScan
  * the generic lefttree field as you might expect. This is because we do
  * not want plan-tree-traversal routines to recurse into the subplan without
  * knowing that they are changing Query contexts.
+ *
+ * Note: subrtable is used just to carry the subquery rangetable from
+ * createplan.c to setrefs.c; it should always be NIL by the time the
+ * executor sees the plan.
  * ----------------
  */
 typedef struct SubqueryScan
 {
    Scan        scan;
    Plan       *subplan;
+   List       *subrtable;      /* temporary workspace for planner */
 } SubqueryScan;
 
 /* ----------------
index 185673f729a85f90750eff29f7e30ef2c720181b..7efb6ec77cfaa970fa1062b1d976d6ed97e85bac 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.126 2007/02/20 17:32:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.127 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -428,8 +428,10 @@ typedef struct SubLink
  * SubPlan - executable expression node for a subplan (sub-SELECT)
  *
  * The planner replaces SubLink nodes in expression trees with SubPlan
- * nodes after it has finished planning the subquery.  SubPlan contains
- * a sub-plantree and rtable instead of a sub-Query.
+ * nodes after it has finished planning the subquery.  SubPlan references
+ * a sub-plantree stored in the subplans list of the toplevel PlannedStmt.
+ * (We avoid a direct link to make it easier to copy expression trees
+ * without causing multiple processing of the subplan.)
  *
  * In an ordinary subplan, testexpr points to an executable expression
  * (OpExpr, an AND/OR tree of OpExprs, or RowCompareExpr) for the combining
@@ -463,12 +465,10 @@ typedef struct SubPlan
    /* The combining operators, transformed to an executable expression: */
    Node       *testexpr;       /* OpExpr or RowCompareExpr expression tree */
    List       *paramIds;       /* IDs of Params embedded in the above */
-   /* The subselect, transformed to a Plan: */
-   struct Plan *plan;          /* subselect plan itself */
-   int         plan_id;        /* kluge because we haven't equal-funcs for
-                                * plan nodes... we compare this instead of
-                                * subselect plan */
-   List       *rtable;         /* range table for subselect */
+   /* Identification of the Plan tree to use: */
+   int         plan_id;        /* Index (from 1) in PlannedStmt.subplans */
+   /* Extra data saved for the convenience of exprType(): */
+   Oid         firstColType;   /* Type of first column of subplan result */
    /* Information about execution strategy: */
    bool        useHashTable;   /* TRUE to store subselect output in a hash
                                 * table (implies we are doing "IN") */
index fdb0cb0a5aeacfe10492011af4f97375c02b8eaf..76ad5abe1f5df70cf2c5f0e5e37fd7e6d4649b06 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/print.h,v 1.26 2007/01/05 22:19:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/print.h,v 1.27 2007/02/22 22:00:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,8 +31,5 @@ extern void print_expr(Node *expr, List *rtable);
 extern void print_pathkeys(List *pathkeys, List *rtable);
 extern void print_tl(List *tlist, List *rtable);
 extern void print_slot(TupleTableSlot *slot);
-extern void print_plan_recursive(Plan *p, Query *parsetree,
-                    int indentLevel, char *label);
-extern void print_plan(Plan *p, Query *parsetree);
 
 #endif   /* PRINT_H */
index 59ec830f3ff6a23d1c065d7715a773d38bdb4037..19bcb51ea08c2bf8566f4033ca5a0a447ab52ff8 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.137 2007/02/20 17:32:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.138 2007/02/22 22:00:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,9 +64,17 @@ typedef struct PlannerGlobal
 
    List       *paramlist;      /* to keep track of cross-level Params */
 
-   int         next_plan_id;   /* hack for distinguishing SubPlans */
+   List       *subplans;       /* Plans for SubPlan nodes */
+
+   List       *subrtables;     /* Rangetables for SubPlan nodes */
+
+   List       *finalrtable;    /* "flat" rangetable for executor */
 } PlannerGlobal;
 
+/* macro for fetching the Plan associated with a SubPlan node */
+#define planner_subplan_get_plan(root, subplan) \
+   ((Plan *) list_nth((root)->glob->subplans, (subplan)->plan_id - 1))
+
 
 /*----------
  * PlannerInfo
@@ -228,6 +236,7 @@ typedef struct PlannerInfo
  *     pages - number of disk pages in relation (zero if not a table)
  *     tuples - number of tuples in relation (not considering restrictions)
  *     subplan - plan for subquery (NULL if it's not a subquery)
+ *     subrtable - rangetable for subquery (NIL if it's not a subquery)
  *
  *     Note: for a subquery, tuples and subplan are not set immediately
  *     upon creation of the RelOptInfo object; they are filled in when
@@ -310,6 +319,7 @@ typedef struct RelOptInfo
    BlockNumber pages;
    double      tuples;
    struct Plan *subplan;       /* if subquery */
+   List       *subrtable;      /* if subquery */
 
    /* used by various scans and joins: */
    List       *baserestrictinfo;       /* RestrictInfo structures (if base
index fde65ed874a0d1cb6e4244d7478daac101d3e003..ef92d685eb4a433ef75bd77e5a7fbd9448bacef3 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.84 2007/01/22 01:35:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.85 2007/02/22 22:00:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -88,8 +88,8 @@ extern void cost_group(Path *path, PlannerInfo *root,
 extern void cost_nestloop(NestPath *path, PlannerInfo *root);
 extern void cost_mergejoin(MergePath *path, PlannerInfo *root);
 extern void cost_hashjoin(HashPath *path, PlannerInfo *root);
-extern void cost_qual_eval(QualCost *cost, List *quals);
-extern void cost_qual_eval_node(QualCost *cost, Node *qual);
+extern void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root);
+extern void cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root);
 extern void set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 extern void set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel,
                           RelOptInfo *outer_rel,
index 6bf34e162a7c479f45cf951ee15d27a77b92425d..b5ad4b3c3b503a5ae93db14f8a613477963e1505 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.99 2007/01/22 01:35:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.100 2007/02/22 22:00:26 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,7 +36,7 @@ extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist,
  */
 extern Plan *create_plan(PlannerInfo *root, Path *best_path);
 extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
-                 Index scanrelid, Plan *subplan);
+                 Index scanrelid, Plan *subplan, List *subrtable);
 extern Append *make_append(List *appendplans, bool isTarget, List *tlist);
 extern Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
                        List *pathkeys);
@@ -60,7 +60,8 @@ extern Limit *make_limit(Plan *lefttree, Node *limitOffset, Node *limitCount,
           int64 offset_est, int64 count_est);
 extern SetOp *make_setop(SetOpCmd cmd, Plan *lefttree,
           List *distinctList, AttrNumber flagColIdx);
-extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
+extern Result *make_result(PlannerInfo *root, List *tlist,
+                          Node *resconstantqual, Plan *subplan);
 extern bool is_projection_capable_plan(Plan *plan);
 
 /*
@@ -91,7 +92,9 @@ extern RestrictInfo *build_implied_join_equality(Oid opno,
 /*
  * prototypes for plan/setrefs.c
  */
-extern Plan *set_plan_references(Plan *plan, List *rtable);
+extern Plan *set_plan_references(PlannerGlobal *glob,
+                                Plan *plan,
+                                List *rtable);
 extern List *set_returning_clause_references(List *rlist,
                                Plan *topplan,
                                Index resultRelation);