aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/executor/execMain.c16
-rw-r--r--src/backend/executor/execScan.c13
-rw-r--r--src/backend/executor/nodeAppend.c79
-rw-r--r--src/backend/executor/nodeFunctionscan.c7
-rw-r--r--src/backend/executor/nodeSubqueryscan.c25
-rw-r--r--src/backend/optimizer/plan/createplan.c9
-rw-r--r--src/backend/optimizer/plan/planner.c8
-rw-r--r--src/backend/optimizer/plan/setrefs.c444
-rw-r--r--src/backend/optimizer/prep/prepunion.c22
-rw-r--r--src/backend/optimizer/util/clauses.c52
-rw-r--r--src/backend/optimizer/util/plancat.c88
-rw-r--r--src/include/optimizer/clauses.h11
-rw-r--r--src/include/optimizer/planmain.h4
13 files changed, 603 insertions, 175 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index b45850bd2b4..a390829bb8e 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -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.
*/
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 1e80fa7be06..843aa15101c 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -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,12 +57,17 @@ 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
* projection expressions). If so, try to project another one.
@@ -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);
/*
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index 0f77741a825..b88eec46a40 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -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 */
}
}
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index f8f13f1c786..5cd6de45fda 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -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;
}
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bc5a6294d0..90e59f90f4d 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -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
@@ -160,6 +165,11 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate)
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
* rangetable access were done differently, the subquery could share
@@ -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
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index e042468c665..930355b5204 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -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;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 2e83a7417df..c14983baa0f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -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;
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 0dc0f1393c8..c3eb36bcfc4 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -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);
}
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index ae3c3a8c182..0f532a99453 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -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 a 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,
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 8ddb4df45a3..429e5d2495c 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -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;
}
/*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 8c9b34f0fe7..ad4e3cd3472 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -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;
}
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index b2ec17ef405..ae5ab79f987 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -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);
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 129439ddec8..bb27bf01d57 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -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);