]> granicus.if.org Git - postgresql/commitdiff
Teach the planner to remove SubqueryScan nodes from the plan if they
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 22 May 2005 22:30:20 +0000 (22:30 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 22 May 2005 22:30:20 +0000 (22:30 +0000)
aren't doing anything useful (ie, neither selection nor projection).
Also, extend to SubqueryScan the hacks already in place to avoid
unnecessary ExecProject calls when the result would just be the same
tuple the subquery already delivered.  This saves some overhead in
UNION and other set operations, as well as avoiding overhead for
unflatten-able subqueries.  Per example from Sokolov Yura.

13 files changed:
src/backend/executor/execMain.c
src/backend/executor/execScan.c
src/backend/executor/nodeAppend.c
src/backend/executor/nodeFunctionscan.c
src/backend/executor/nodeSubqueryscan.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/plancat.c
src/include/optimizer/clauses.h
src/include/optimizer/planmain.h

index b45850bd2b4e91ccf9c424b34a18f5c98ac19753..a390829bb8ef749dbb5882ce3b88ceb3d0fa202b 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.248 2005/05/06 17:24:53 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.249 2005/05/22 22:30:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -353,16 +353,10 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
        AclId           userid;
 
        /*
-        * If it's a subquery, recursively examine its rangetable.
-        */
-       if (rte->rtekind == RTE_SUBQUERY)
-       {
-               ExecCheckRTPerms(rte->subquery->rtable);
-               return;
-       }
-
-       /*
-        * Otherwise, only plain-relation RTEs need to be checked here.
+        * 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 checked by init_fcache when the function is
         * prepared for execution. Join and special RTEs need no checks.
         */
index 1e80fa7be06a7603475baf76b908d61d3c43559a..843aa15101ccd268af738fa40bb43459d77cf6fd 100644 (file)
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execScan.c,v 1.35 2005/03/16 21:38:06 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execScan.c,v 1.36 2005/05/22 22:30:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,7 +48,6 @@ TupleTableSlot *
 ExecScan(ScanState *node,
                 ExecScanAccessMtd accessMtd)   /* function returning a tuple */
 {
-       EState     *estate;
        ExprContext *econtext;
        List       *qual;
        ProjectionInfo *projInfo;
@@ -58,11 +57,16 @@ ExecScan(ScanState *node,
        /*
         * Fetch data from node
         */
-       estate = node->ps.state;
-       econtext = node->ps.ps_ExprContext;
        qual = node->ps.qual;
        projInfo = node->ps.ps_ProjInfo;
 
+       /*
+        * If we have neither a qual to check nor a projection to do,
+        * just skip all the overhead and return the raw scan tuple.
+        */
+       if (!qual && !projInfo)
+               return (*accessMtd) (node);
+
        /*
         * Check to see if we're still projecting out tuples from a previous
         * scan tuple (because there is a function-returning-set in the
@@ -83,6 +87,7 @@ ExecScan(ScanState *node,
         * storage allocated in the previous tuple cycle.  Note this can't
         * happen until we're done projecting out tuples from a scan tuple.
         */
+       econtext = node->ps.ps_ExprContext;
        ResetExprContext(econtext);
 
        /*
index 0f77741a82501d743ccdce60b2583a5980f6b176..b88eec46a40699b61648b300c7d3a64eab7a7312 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeAppend.c,v 1.63 2005/04/24 11:46:20 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeAppend.c,v 1.64 2005/05/22 22:30:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -251,67 +251,52 @@ ExecCountSlotsAppend(Append *node)
 /* ----------------------------------------------------------------
  *        ExecAppend
  *
- *             Handles the iteration over the multiple scans.
+ *             Handles iteration over multiple subplans.
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
 ExecAppend(AppendState *node)
 {
-       EState     *estate;
-       int                     whichplan;
-       PlanState  *subnode;
-       TupleTableSlot *result;
-       TupleTableSlot *result_slot;
-       ScanDirection direction;
-
-       /*
-        * get information from the node
-        */
-       estate = node->ps.state;
-       direction = estate->es_direction;
-       whichplan = node->as_whichplan;
-       result_slot = node->ps.ps_ResultTupleSlot;
-
-       /*
-        * figure out which subplan we are currently processing
-        */
-       subnode = node->appendplans[whichplan];
-
-       /*
-        * get a tuple from the subplan
-        */
-       result = ExecProcNode(subnode);
-
-       if (!TupIsNull(result))
+       for (;;)
        {
+               PlanState  *subnode;
+               TupleTableSlot *result;
+
                /*
-                * if the subplan gave us something then return it as-is.  We do
-                * NOT make use of the result slot that was set up in ExecInitAppend,
-                * first because there's no reason to and second because it may have
-                * the wrong tuple descriptor in inherited-UPDATE cases.
+                * figure out which subplan we are currently processing
                 */
-               return result;
-       }
-       else
-       {
+               subnode = node->appendplans[node->as_whichplan];
+
                /*
-                * .. go on to the "next" subplan in the appropriate direction and
-                * try processing again (recursively)
+                * get a tuple from the subplan
                 */
-               if (ScanDirectionIsForward(direction))
-                       node->as_whichplan++;
-               else
-                       node->as_whichplan--;
+               result = ExecProcNode(subnode);
+
+               if (!TupIsNull(result))
+               {
+                       /*
+                        * If the subplan gave us something then return it as-is.
+                        * We do NOT make use of the result slot that was set up in
+                        * ExecInitAppend, first because there's no reason to and
+                        * second because it may have the wrong tuple descriptor in
+                        * inherited-UPDATE cases.
+                        */
+                       return result;
+               }
 
                /*
-                * return something from next node or an empty slot if all of our
-                * subplans have been exhausted.  The empty slot is the one set up
+                * Go on to the "next" subplan in the appropriate direction.
+                * If no more subplans, return the empty slot set up for us
                 * by ExecInitAppend.
                 */
-               if (exec_append_initialize_next(node))
-                       return ExecAppend(node);
+               if (ScanDirectionIsForward(node->ps.state->es_direction))
+                       node->as_whichplan++;
                else
-                       return ExecClearTuple(result_slot);
+                       node->as_whichplan--;
+               if (!exec_append_initialize_next(node))
+                       return ExecClearTuple(node->ps.ps_ResultTupleSlot);
+
+               /* Else loop back and try to get a tuple from the new subplan */
        }
 }
 
index f8f13f1c786f63f4cf6e889cf3935343f9c33152..5cd6de45fda7705f3fffeb863ca75f67eb6e4f4c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.33 2005/04/14 22:09:40 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.34 2005/05/22 22:30:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -219,8 +219,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
        BlessTupleDesc(tupdesc);
 
        scanstate->tupdesc = tupdesc;
-       ExecSetSlotDescriptor(scanstate->ss.ss_ScanTupleSlot,
-                                                 tupdesc, false);
+       ExecAssignScanType(&scanstate->ss, tupdesc, false);
 
        /*
         * Other node-specific setup
@@ -235,7 +234,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
         * Initialize result tuple type and projection info.
         */
        ExecAssignResultTypeFromTL(&scanstate->ss.ps);
-       ExecAssignProjectionInfo(&scanstate->ss.ps);
+       ExecAssignScanProjectionInfo(&scanstate->ss);
 
        return scanstate;
 }
index 9bc5a6294d0e6a52b50120bdd09d5435b10e1ef9..90e59f90f4d4f1f3a645c65973c7423edf5d613d 100644 (file)
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.25 2004/12/31 21:59:45 pgsql Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.26 2005/05/22 22:30:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -78,6 +78,10 @@ SubqueryNext(SubqueryScanState *node)
 
        MemoryContextSwitchTo(oldcontext);
 
+       /*
+        * We just overwrite our ScanTupleSlot with the subplan's result slot,
+        * rather than expending the cycles for ExecCopySlot().
+        */
        node->ss.ss_ScanTupleSlot = slot;
 
        return slot;
@@ -144,12 +148,13 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate)
                ExecInitExpr((Expr *) node->scan.plan.qual,
                                         (PlanState *) subquerystate);
 
-#define SUBQUERYSCAN_NSLOTS 1
+#define SUBQUERYSCAN_NSLOTS 2
 
        /*
         * tuple table initialization
         */
        ExecInitResultTupleSlot(estate, &subquerystate->ss.ps);
+       ExecInitScanTupleSlot(estate, &subquerystate->ss);
 
        /*
         * initialize subquery
@@ -159,6 +164,11 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate)
        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, however.  XXX if
@@ -187,14 +197,20 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate)
 
        MemoryContextSwitchTo(oldcontext);
 
-       subquerystate->ss.ss_ScanTupleSlot = NULL;
        subquerystate->ss.ps.ps_TupFromTlist = false;
 
+       /*
+        * Initialize scan tuple type (needed by ExecAssignScanProjectionInfo)
+        */
+       ExecAssignScanType(&subquerystate->ss,
+                                          ExecGetResultType(subquerystate->subplan),
+                                          false);
+
        /*
         * Initialize result tuple type and projection info.
         */
        ExecAssignResultTypeFromTL(&subquerystate->ss.ps);
-       ExecAssignProjectionInfo(&subquerystate->ss.ps);
+       ExecAssignScanProjectionInfo(&subquerystate->ss);
 
        return subquerystate;
 }
@@ -230,6 +246,7 @@ ExecEndSubqueryScan(SubqueryScanState *node)
         * clean out the upper tuple table
         */
        ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+       node->ss.ss_ScanTupleSlot = NULL;               /* not ours to clear */
 
        /*
         * close down subquery
index e042468c6654199d1318ecc1ed828746f9726c9d..930355b5204d5d37a59e766f268a8e57a15c16e3 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.188 2005/04/25 04:27:12 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.189 2005/05/22 22:30:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -308,10 +308,11 @@ use_physical_tlist(RelOptInfo *rel)
        int                     i;
 
        /*
-        * Currently, can't do this for subquery or function scans.  (This is
-        * mainly because we don't have an equivalent of build_physical_tlist
-        * for them; worth adding?)
+        * OK for subquery scans, but not function scans.  (This is mainly
+        * because build_physical_tlist doesn't support them; worth adding?)
         */
+       if (rel->rtekind == RTE_SUBQUERY)
+               return true;
        if (rel->rtekind != RTE_RELATION)
                return false;
 
index 2e83a7417df466ed6da67458475ed4b4791ef835..c14983baa0f052436391f35305763d1d534a4e43 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.185 2005/04/28 21:47:13 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.186 2005/05/22 22:30:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -144,12 +144,12 @@ planner(Query *parse, bool isCursor, int cursorOptions,
                        result_plan = materialize_finished_plan(result_plan);
        }
 
+       /* final cleanup of the plan */
+       result_plan = set_plan_references(result_plan, parse->rtable);
+
        /* executor wants to know total number of Params used overall */
        result_plan->nParamExec = list_length(PlannerParamList);
 
-       /* final cleanup of the plan */
-       set_plan_references(result_plan, parse->rtable);
-
        /* restore state for outer planner, if any */
        PlannerQueryLevel = save_PlannerQueryLevel;
        PlannerParamList = save_PlannerParamList;
index 0dc0f1393c8503a59e34d65455a71dee8e542bfd..c3eb36bcfc4815d0d12b12c9dd5040bc10fd4630 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.109 2005/04/25 01:30:13 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.110 2005/05/22 22:30:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,6 +41,11 @@ typedef struct
        bool            tlist_has_non_vars;
 } replace_vars_with_subplan_refs_context;
 
+static Plan *set_subqueryscan_references(SubqueryScan *plan, List *rtable);
+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, List *rtable);
@@ -76,23 +81,42 @@ static void set_sa_opfuncid(ScalarArrayOpExpr *opexpr);
 
 /*
  * set_plan_references
- *       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).
  *
- *       set_plan_references recursively traverses the whole plan tree.
+ * 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).
  *
- * Returns nothing of interest, but modifies internal fields of nodes.
+ * 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
+ * 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.
+ *
+ * set_plan_references recursively traverses the whole plan tree.
+ *
+ * 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.)
  */
-void
+Plan *
 set_plan_references(Plan *plan, List *rtable)
 {
        ListCell   *l;
 
        if (plan == NULL)
-               return;
+               return NULL;
 
        /*
         * Plan-type-specific fixes
@@ -133,25 +157,8 @@ set_plan_references(Plan *plan, List *rtable)
                                                                (Node *) ((TidScan *) plan)->tideval);
                        break;
                case T_SubqueryScan:
-                       {
-                               RangeTblEntry *rte;
-
-                               /*
-                                * We do not do set_uppernode_references() here, because a
-                                * SubqueryScan will always have been created with correct
-                                * references to its subplan's outputs to begin with.
-                                */
-                               fix_expr_references(plan, (Node *) plan->targetlist);
-                               fix_expr_references(plan, (Node *) plan->qual);
-
-                               /* Recurse into subplan too */
-                               rte = rt_fetch(((SubqueryScan *) plan)->scan.scanrelid,
-                                                          rtable);
-                               Assert(rte->rtekind == RTE_SUBQUERY);
-                               set_plan_references(((SubqueryScan *) plan)->subplan,
-                                                                       rte->subquery->rtable);
-                       }
-                       break;
+                       /* Needs special treatment, see comments below */
+                       return set_subqueryscan_references((SubqueryScan *) plan, rtable);
                case T_FunctionScan:
                        {
                                RangeTblEntry *rte;
@@ -194,14 +201,18 @@ set_plan_references(Plan *plan, List *rtable)
 
                        /*
                         * These plan types don't actually bother to evaluate their
-                        * targetlists or quals (because they just return their
-                        * unmodified input tuples).  The optimizer is lazy about
-                        * creating really valid targetlists for them.  Best to just
-                        * leave the targetlist alone.  In particular, we do not want
-                        * to process subplans for them, since we will likely end up
-                        * reprocessing subplans that also appear in lower levels of
-                        * the plan tree!
+                        * 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!
+                        *
+                        * 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:
 
@@ -210,6 +221,7 @@ set_plan_references(Plan *plan, List *rtable)
                         * 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);
                        break;
@@ -237,22 +249,27 @@ set_plan_references(Plan *plan, List *rtable)
 
                        /*
                         * Append, like Sort et al, doesn't actually evaluate its
-                        * targetlist or quals, and we haven't bothered to give it its
-                        * own tlist copy. So, don't fix targetlist/qual. But do
+                        * 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)
-                               set_plan_references((Plan *) lfirst(l), rtable);
+                               lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable);
                        break;
                case T_BitmapAnd:
-                       /* BitmapAnd works like Append */
+                       /* BitmapAnd works like Append, but has no tlist */
+                       Assert(plan->targetlist == NIL);
+                       Assert(plan->qual == NIL);
                        foreach(l, ((BitmapAnd *) plan)->bitmapplans)
-                               set_plan_references((Plan *) lfirst(l), rtable);
+                               lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable);
                        break;
                case T_BitmapOr:
-                       /* BitmapOr works like Append */
+                       /* BitmapOr works like Append, but has no tlist */
+                       Assert(plan->targetlist == NIL);
+                       Assert(plan->qual == NIL);
                        foreach(l, ((BitmapOr *) plan)->bitmapplans)
-                               set_plan_references((Plan *) lfirst(l), rtable);
+                               lfirst(l) = set_plan_references((Plan *) lfirst(l), rtable);
                        break;
                default:
                        elog(ERROR, "unrecognized node type: %d",
@@ -270,16 +287,349 @@ set_plan_references(Plan *plan, List *rtable)
         * children.  Fortunately, that consideration doesn't apply to SubPlan
         * nodes; else we'd need two passes over the expression trees.
         */
-       set_plan_references(plan->lefttree, rtable);
-       set_plan_references(plan->righttree, rtable);
+       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));
-               set_plan_references(sp->plan, sp->rtable);
+               sp->plan = set_plan_references(sp->plan, sp->rtable);
+       }
+
+       return plan;
+}
+
+/*
+ * set_subqueryscan_references
+ *             Do set_plan_references processing on a SubqueryScan
+ *
+ * We try to strip out the SubqueryScan entirely; if we can't, we have
+ * to do the normal processing on it.
+ */
+static Plan *
+set_subqueryscan_references(SubqueryScan *plan, List *rtable)
+{
+       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);
+
+               Assert(IsA(sp, SubPlan));
+               sp->plan = set_plan_references(sp->plan, sp->rtable);
+       }
+
+       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.
+                */
+               int             rtoffset = list_length(rtable);
+               List   *sub_rtable;
+
+               sub_rtable = copyObject(rte->subquery->rtable);
+               range_table_walker(sub_rtable,
+                                                  adjust_expr_varnos_walker,
+                                                  (void *) &rtoffset,
+                                                  QTW_IGNORE_RT_SUBQUERIES);
+               rtable = list_concat(rtable, sub_rtable);
+
+               result = plan->subplan;
+
+               adjust_plan_varnos(result, rtoffset);
+
+               result->initPlan = list_concat(plan->scan.plan.initPlan,
+                                                                          result->initPlan);
+       }
+       else
+       {
+               /*
+                * 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 always have been created with correct
+                * references to its subplan's outputs to begin with.
+                */
+               result = (Plan *) plan;
+
+               fix_expr_references(result, (Node *) result->targetlist);
+               fix_expr_references(result, (Node *) result->qual);
+       }
+
+       return result;
+}
+
+/*
+ * trivial_subqueryscan
+ *             Detect whether a SubqueryScan can be deleted from the plan tree.
+ *
+ * We can delete it if it has no qual to check and the targetlist just
+ * regurgitates the output of the child plan.
+ */
+static bool
+trivial_subqueryscan(SubqueryScan *plan)
+{
+       int                     attrno;
+       ListCell   *lp,
+                          *lc;
+
+       if (plan->scan.plan.qual != NIL)
+               return false;
+
+       attrno = 1;
+       forboth(lp, plan->scan.plan.targetlist, lc, plan->subplan->targetlist)
+       {
+               TargetEntry *ptle = (TargetEntry *) lfirst(lp);
+               TargetEntry *ctle = (TargetEntry *) lfirst(lc);
+               Var                *var = (Var *) ptle->expr;
+
+               if (ptle->resjunk != ctle->resjunk)
+                       return false;           /* tlist doesn't match junk status */
+               if (!var || !IsA(var, Var))
+                       return false;           /* tlist item not a Var */
+               Assert(var->varno == plan->scan.scanrelid);
+               Assert(var->varlevelsup == 0);
+               if (var->varattno != attrno)
+                       return false;           /* out of order */
+               attrno++;
+       }
+
+       if (lp)
+               return false;                   /* parent tlist longer than child */
+
+       /* extra child items are OK only if all are resjunk */
+       for_each_cell(lc, lc)
+       {
+               TargetEntry *ctle = (TargetEntry *) lfirst(lc);
+
+               if (!ctle->resjunk)
+                       return false;
+       }
+
+       return true;
+}
+
+/*
+ * 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)->tideval,
+                                                          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);
+                       /* rte was already fixed by set_subqueryscan_references */
+                       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).
+                        * We have to make a copy because the tlist is very likely
+                        * shared with lower plan levels.
+                        */
+                       plan->targetlist = copyObject(plan->targetlist);
+                       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.
+                        */
+                       plan->targetlist = copyObject(plan->targetlist);
+                       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:
+
+                       /*
+                        * 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, copy tlist before fixing.  Then
+                        * recurse into child plans.
+                        */
+                       plan->targetlist = copyObject(plan->targetlist);
+                       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.
+ *
+ * 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.
+ */
+static void
+adjust_expr_varnos(Node *node, int rtoffset)
+{
+       /* This tree walk requires no special setup, so away we go... */
+       adjust_expr_varnos_walker(node, &rtoffset);
+}
+
+static bool
+adjust_expr_varnos_walker(Node *node, int *context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Var))
+       {
+               Var                *var = (Var *) node;
+
+               Assert(var->varlevelsup == 0);
+               if (var->varno > 0 && var->varno != INNER && var->varno != OUTER)
+                       var->varno += *context;
+               if (var->varnoold > 0)
+                       var->varnoold += *context;
+               return false;
        }
+       return expression_tree_walker(node, adjust_expr_varnos_walker,
+                                                                 (void *) context);
 }
 
 /*
@@ -315,7 +665,7 @@ fix_expr_references_walker(Node *node, void *context)
        {
                SubPlan    *sp = (SubPlan *) node;
 
-               set_plan_references(sp->plan, sp->rtable);
+               sp->plan = set_plan_references(sp->plan, sp->rtable);
        }
        return expression_tree_walker(node, fix_expr_references_walker, context);
 }
index ae3c3a8c18219d39a0671f6b6409fa3b4fa5e09e..0f532a99453f1047f5fd4d1965d249154a6603df 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.120 2005/04/06 16:34:06 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.121 2005/05/22 22:30:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -61,6 +61,7 @@ static List *recurse_union_children(Node *setOp, Query *parse,
                                           SetOperationStmt *top_union,
                                           List *refnames_tlist);
 static List *generate_setop_tlist(List *colTypes, int flag,
+                                        Index varno,
                                         bool hack_constants,
                                         List *input_tlist,
                                         List *refnames_tlist);
@@ -119,8 +120,8 @@ plan_set_operations(Query *parse, List **sortClauses)
        /*
         * Recurse on setOperations tree to generate plans for set ops. The
         * final output plan should have just the column types shown as the
-        * output from the top-level node, plus possibly resjunk working
-        * column (we can rely on upper-level nodes to deal with that).
+        * output from the top-level node, plus possibly resjunk working
+        * columns (we can rely on upper-level nodes to deal with that).
         */
        return recurse_set_operations((Node *) topop, parse,
                                                                  topop->colTypes, true, -1,
@@ -163,7 +164,9 @@ recurse_set_operations(Node *setOp, Query *parse,
                 * Add a SubqueryScan with the caller-requested targetlist
                 */
                plan = (Plan *)
-                       make_subqueryscan(generate_setop_tlist(colTypes, flag, true,
+                       make_subqueryscan(generate_setop_tlist(colTypes, flag,
+                                                                                                  rtr->rtindex,
+                                                                                                  true,
                                                                                                   subplan->targetlist,
                                                                                                   refnames_tlist),
                                                          NIL,
@@ -202,13 +205,15 @@ recurse_set_operations(Node *setOp, Query *parse,
                 * the subplan.  However, since the subplan was generated by
                 * generate_union_plan() or generate_nonunion_plan(), and hence
                 * its tlist was generated by generate_append_tlist(), this will
-                * work.
+                * work.  We just tell generate_setop_tlist() to use varno 0.
                 */
                if (flag >= 0 ||
                        !tlist_same_datatypes(plan->targetlist, colTypes, junkOK))
                {
                        plan = (Plan *)
-                               make_result(generate_setop_tlist(colTypes, flag, false,
+                               make_result(generate_setop_tlist(colTypes, flag,
+                                                                                                0,
+                                                                                                false,
                                                                                                 plan->targetlist,
                                                                                                 refnames_tlist),
                                                        NULL,
@@ -415,12 +420,14 @@ recurse_union_children(Node *setOp, Query *parse,
  *
  * colTypes: column datatypes for non-junk columns
  * flag: -1 if no flag column needed, 0 or 1 to create a const flag column
+ * varno: varno to use in generated Vars
  * hack_constants: true to copy up constants (see comments in code)
  * input_tlist: targetlist of this node's input node
  * refnames_tlist: targetlist to take column names from
  */
 static List *
 generate_setop_tlist(List *colTypes, int flag,
+                                        Index varno,
                                         bool hack_constants,
                                         List *input_tlist,
                                         List *refnames_tlist)
@@ -462,7 +469,7 @@ generate_setop_tlist(List *colTypes, int flag,
                if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const))
                        expr = (Node *) inputtle->expr;
                else
-                       expr = (Node *) makeVar(0,
+                       expr = (Node *) makeVar(varno,
                                                                        inputtle->resno,
                                                                        exprType((Node *) inputtle->expr),
                                                                        exprTypmod((Node *) inputtle->expr),
@@ -513,6 +520,7 @@ generate_setop_tlist(List *colTypes, int flag,
  *
  * The entries in the Append's targetlist should always be simple Vars;
  * we just have to make sure they have the right datatypes and typmods.
+ * The Vars are always generated with varno 0.
  */
 static List *
 generate_append_tlist(List *colTypes, bool flag,
index 8ddb4df45a31a77fe8d3693d9fed261304832202..429e5d2495cda6c3c06fd75ab6903c677f0b9e08 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.196 2005/04/23 04:42:53 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.197 2005/05/22 22:30:20 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -3026,8 +3026,6 @@ query_tree_walker(Query *query,
                                  void *context,
                                  int flags)
 {
-       ListCell   *rt;
-
        Assert(query != NULL && IsA(query, Query));
 
        if (walker((Node *) query->targetList, context))
@@ -3044,7 +3042,25 @@ query_tree_walker(Query *query,
                return true;
        if (walker(query->in_info_list, context))
                return true;
-       foreach(rt, query->rtable)
+       if (range_table_walker(query->rtable, walker, context, flags))
+               return true;
+       return false;
+}
+
+/*
+ * range_table_walker is just the part of query_tree_walker that scans
+ * a query's rangetable.  This is split out since it can be useful on
+ * its own.
+ */
+bool
+range_table_walker(List *rtable,
+                                  bool (*walker) (),
+                                  void *context,
+                                  int flags)
+{
+       ListCell   *rt;
+
+       foreach(rt, rtable)
        {
                RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
 
@@ -3532,9 +3548,6 @@ query_tree_mutator(Query *query,
                                   void *context,
                                   int flags)
 {
-       List       *newrt;
-       ListCell   *rt;
-
        Assert(query != NULL && IsA(query, Query));
 
        if (!(flags & QTW_DONT_COPY_QUERY))
@@ -3552,8 +3565,26 @@ query_tree_mutator(Query *query,
        MUTATE(query->limitOffset, query->limitOffset, Node *);
        MUTATE(query->limitCount, query->limitCount, Node *);
        MUTATE(query->in_info_list, query->in_info_list, List *);
-       newrt = NIL;
-       foreach(rt, query->rtable)
+       query->rtable = range_table_mutator(query->rtable,
+                                                                               mutator, context, flags);
+       return query;
+}
+
+/*
+ * range_table_mutator is just the part of query_tree_mutator that processes
+ * a query's rangetable.  This is split out since it can be useful on
+ * its own.
+ */
+List *
+range_table_mutator(List *rtable,
+                                       Node *(*mutator) (),
+                                       void *context,
+                                       int flags)
+{
+       List       *newrt = NIL;
+       ListCell   *rt;
+
+       foreach(rt, rtable)
        {
                RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
                RangeTblEntry *newrte;
@@ -3592,8 +3623,7 @@ query_tree_mutator(Query *query,
                }
                newrt = lappend(newrt, newrte);
        }
-       query->rtable = newrt;
-       return query;
+       return newrt;
 }
 
 /*
index 8c9b34f0fe7a70052ac047aa96123facce58d08b..ad4e3cd34724f0eb64542c0e1fc2361e6078ad79 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.106 2005/04/22 21:58:31 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.107 2005/05/22 22:30:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,6 +27,7 @@
 #include "optimizer/plancat.h"
 #include "optimizer/tlist.h"
 #include "parser/parsetree.h"
+#include "parser/parse_expr.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
@@ -353,49 +354,82 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
  * relation, but that data is not readily available to ExecTypeFromTL.
  * For now, we don't apply the physical-tlist optimization when there are
  * dropped cols.
+ *
+ * We also support building a "physical" tlist for subqueries, since the
+ * same optimization can occur in SubqueryScan nodes.
  */
 List *
 build_physical_tlist(Query *root, RelOptInfo *rel)
 {
+       List       *tlist = NIL;
        Index           varno = rel->relid;
        RangeTblEntry *rte = rt_fetch(varno, root->rtable);
        Relation        relation;
-       List       *tlist = NIL;
+       Query      *subquery;
+       Var                *var;
+       ListCell   *l;
        int                     attrno,
                                numattrs;
 
-       Assert(rte->rtekind == RTE_RELATION);
+       switch (rte->rtekind)
+       {
+               case RTE_RELATION:
+                       relation = heap_open(rte->relid, AccessShareLock);
 
-       relation = heap_open(rte->relid, AccessShareLock);
+                       numattrs = RelationGetNumberOfAttributes(relation);
+                       for (attrno = 1; attrno <= numattrs; attrno++)
+                       {
+                               Form_pg_attribute att_tup = relation->rd_att->attrs[attrno - 1];
 
-       numattrs = RelationGetNumberOfAttributes(relation);
+                               if (att_tup->attisdropped)
+                               {
+                                       /* found a dropped col, so punt */
+                                       tlist = NIL;
+                                       break;
+                               }
 
-       for (attrno = 1; attrno <= numattrs; attrno++)
-       {
-               Form_pg_attribute att_tup = relation->rd_att->attrs[attrno - 1];
-               Var        *var;
+                               var = makeVar(varno,
+                                                         attrno,
+                                                         att_tup->atttypid,
+                                                         att_tup->atttypmod,
+                                                         0);
+
+                               tlist = lappend(tlist,
+                                                               makeTargetEntry((Expr *) var,
+                                                                                               attrno,
+                                                                                               NULL,
+                                                                                               false));
+                       }
 
-               if (att_tup->attisdropped)
-               {
-                       /* found a dropped col, so punt */
-                       tlist = NIL;
+                       heap_close(relation, AccessShareLock);
                        break;
-               }
 
-               var = makeVar(varno,
-                                         attrno,
-                                         att_tup->atttypid,
-                                         att_tup->atttypmod,
-                                         0);
-
-               tlist = lappend(tlist,
-                                               makeTargetEntry((Expr *) var,
-                                                                               attrno,
-                                                                               NULL,
-                                                                               false));
-       }
+               case RTE_SUBQUERY:
+                       subquery = rte->subquery;
+                       foreach(l, subquery->targetList)
+                       {
+                               TargetEntry *tle = (TargetEntry *) lfirst(l);
+
+                               var = makeVar(varno,
+                                                         tle->resno,
+                                                         exprType((Node *) tle->expr),
+                                                         exprTypmod((Node *) tle->expr),
+                                                         0);
+
+                               tlist = lappend(tlist,
+                                                               makeTargetEntry((Expr *) var,
+                                                                                               tle->resno,
+                                                                                               NULL,
+                                                                                               tle->resjunk));
+                       }
+                       break;
 
-       heap_close(relation, AccessShareLock);
+               default:
+                       /* caller error */
+                       elog(ERROR, "unsupported RTE kind %d in build_physical_tlist",
+                                (int) rte->rtekind);
+                       break;
+       }
 
        return tlist;
 }
index b2ec17ef40574e5610d4ab6aa20b1fd1d7cb5623..ae5ab79f987dd244567d0c30e843a1f0060aa845 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.78 2005/01/28 19:34:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.79 2005/05/22 22:30:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -87,9 +87,14 @@ extern Node *expression_tree_mutator(Node *node, Node *(*mutator) (),
 #define QTW_DONT_COPY_QUERY                    0x04            /* do not copy top Query */
 
 extern bool query_tree_walker(Query *query, bool (*walker) (),
-                                                                                 void *context, int flags);
+                                                         void *context, int flags);
 extern Query *query_tree_mutator(Query *query, Node *(*mutator) (),
-                                                                                        void *context, int flags);
+                                                                void *context, int flags);
+
+extern bool range_table_walker(List *rtable, bool (*walker) (),
+                                                          void *context, int flags);
+extern List *range_table_mutator(List *rtable, Node *(*mutator) (),
+                                                                void *context, int flags);
 
 extern bool query_or_expression_tree_walker(Node *node, bool (*walker) (),
                                                                                           void *context, int flags);
index 129439ddec895f086d3841cca6590f16e1630e43..bb27bf01d57ca3a0d4f786e424170a6c185c53ad 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.84 2005/04/25 02:14:48 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.85 2005/05/22 22:30:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -74,7 +74,7 @@ extern void process_implied_equality(Query *root,
 /*
  * prototypes for plan/setrefs.c
  */
-extern void set_plan_references(Plan *plan, List *rtable);
+extern Plan *set_plan_references(Plan *plan, List *rtable);
 extern void fix_opfuncids(Node *node);
 extern void set_opfuncid(OpExpr *opexpr);