diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/executor/execMain.c | 16 | ||||
-rw-r--r-- | src/backend/executor/execScan.c | 13 | ||||
-rw-r--r-- | src/backend/executor/nodeAppend.c | 79 | ||||
-rw-r--r-- | src/backend/executor/nodeFunctionscan.c | 7 | ||||
-rw-r--r-- | src/backend/executor/nodeSubqueryscan.c | 25 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 9 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 8 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 444 | ||||
-rw-r--r-- | src/backend/optimizer/prep/prepunion.c | 22 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 52 | ||||
-rw-r--r-- | src/backend/optimizer/util/plancat.c | 88 | ||||
-rw-r--r-- | src/include/optimizer/clauses.h | 11 | ||||
-rw-r--r-- | src/include/optimizer/planmain.h | 4 |
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); |