diff options
Diffstat (limited to 'src/backend/optimizer/plan/subselect.c')
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 215 |
1 files changed, 144 insertions, 71 deletions
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 4be69d77cfd..930c3133030 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.75 2003/04/29 22:13:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.76 2003/06/06 15:04:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,6 +28,7 @@ #include "parser/parse_expr.h" #include "parser/parse_oper.h" #include "parser/parse_relation.h" +#include "rewrite/rewriteManip.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -35,27 +36,42 @@ Index PlannerQueryLevel; /* level of current query */ List *PlannerInitPlan; /* init subplans for current query */ -List *PlannerParamVar; /* to get Var from Param->paramid */ +List *PlannerParamList; /* to keep track of cross-level Params */ int PlannerPlanId = 0; /* to assign unique ID to subquery plans */ -/*-------------------- - * PlannerParamVar is a list of Var nodes, wherein the n'th entry - * (n counts from 0) corresponds to Param->paramid = n. The Var nodes - * are ordinary except for one thing: their varlevelsup field does NOT - * have the usual interpretation of "subplan levels out from current". - * Instead, it contains the absolute plan level, with the outermost - * plan being level 1 and nested plans having higher level numbers. - * This nonstandardness is useful because we don't have to run around - * and update the list elements when we enter or exit a subplan - * recursion level. But we must pay attention not to confuse this - * meaning with the normal meaning of varlevelsup. +/* + * PlannerParamList keeps track of the PARAM_EXEC slots that we have decided + * we need for the query. At runtime these slots are used to pass values + * either down into subqueries (for outer references in subqueries) or up out + * of subqueries (for the results of a subplan). The n'th entry in the list + * (n counts from 0) corresponds to Param->paramid = n. + * + * Each ParamList item shows the absolute query level it is associated with, + * where the outermost query is level 1 and nested subqueries have higher + * numbers. The item the parameter slot represents can be one of three kinds: + * + * A Var: the slot represents a variable of that level that must be passed + * down because subqueries have outer references to it. The varlevelsup + * value in the Var will always be zero. + * + * An Aggref (with an expression tree representing its argument): the slot + * represents an aggregate expression that is an outer reference for some + * subquery. The Aggref itself has agglevelsup = 0, and its argument tree + * is adjusted to match in level. * - * We also need to create Param slots that don't correspond to any outer Var. - * For these, we set varno = 0 and varlevelsup = 0, so that they can't - * accidentally match an outer Var. - *-------------------- + * A Param: the slot holds the result of a subplan (it is a setParam item + * for that subplan). The absolute level shown for such items corresponds + * to the parent query of the subplan. + * + * Note: we detect duplicate Var parameters and coalesce them into one slot, + * but we do not do this for Aggref or Param slots. */ +typedef struct PlannerParamItem +{ + Node *item; /* the Var, Aggref, or Param */ + Index abslevel; /* its absolute query level */ +} PlannerParamItem; typedef struct finalize_primnode_context @@ -78,42 +94,25 @@ static bool finalize_primnode(Node *node, finalize_primnode_context *context); /* - * Create a new entry in the PlannerParamVar list, and return its index. - * - * var contains the data to use, except for varlevelsup which - * is set from the absolute level value given by varlevel. NOTE that - * the passed var is scribbled on and placed directly into the list! - * Generally, caller should have just created or copied it. - */ -static int -new_param(Var *var, Index varlevel) -{ - var->varlevelsup = varlevel; - - PlannerParamVar = lappend(PlannerParamVar, var); - - return length(PlannerParamVar) - 1; -} - -/* * Generate a Param node to replace the given Var, * which is expected to have varlevelsup > 0 (ie, it is not local). */ static Param * -replace_var(Var *var) +replace_outer_var(Var *var) { - List *ppv; Param *retval; - Index varlevel; + List *ppl; + PlannerParamItem *pitem; + Index abslevel; int i; Assert(var->varlevelsup > 0 && var->varlevelsup < PlannerQueryLevel); - varlevel = PlannerQueryLevel - var->varlevelsup; + abslevel = PlannerQueryLevel - var->varlevelsup; /* - * If there's already a PlannerParamVar entry for this same Var, just + * If there's already a PlannerParamList entry for this same Var, just * use it. NOTE: in sufficiently complex querytrees, it is possible - * for the same varno/varlevel to refer to different RTEs in different + * for the same varno/abslevel to refer to different RTEs in different * parts of the parsetree, so that different fields might end up * sharing the same Param number. As long as we check the vartype as * well, I believe that this sort of aliasing will cause no trouble. @@ -121,22 +120,33 @@ replace_var(Var *var) * execution in each part of the tree. */ i = 0; - foreach(ppv, PlannerParamVar) + foreach(ppl, PlannerParamList) { - Var *pvar = lfirst(ppv); + pitem = (PlannerParamItem *) lfirst(ppl); + if (pitem->abslevel == abslevel && IsA(pitem->item, Var)) + { + Var *pvar = (Var *) pitem->item; - if (pvar->varno == var->varno && - pvar->varattno == var->varattno && - pvar->varlevelsup == varlevel && - pvar->vartype == var->vartype) - break; + if (pvar->varno == var->varno && + pvar->varattno == var->varattno && + pvar->vartype == var->vartype) + break; + } i++; } - if (!ppv) + if (!ppl) { /* Nope, so make a new one */ - i = new_param((Var *) copyObject(var), varlevel); + var = (Var *) copyObject(var); + var->varlevelsup = 0; + + pitem = (PlannerParamItem *) palloc(sizeof(PlannerParamItem)); + pitem->item = (Node *) var; + pitem->abslevel = abslevel; + + PlannerParamList = lappend(PlannerParamList, pitem); + /* i is already the correct index for the new item */ } retval = makeNode(Param); @@ -148,18 +158,67 @@ replace_var(Var *var) } /* + * Generate a Param node to replace the given Aggref + * which is expected to have agglevelsup > 0 (ie, it is not local). + */ +static Param * +replace_outer_agg(Aggref *agg) +{ + Param *retval; + PlannerParamItem *pitem; + Index abslevel; + int i; + + Assert(agg->agglevelsup > 0 && agg->agglevelsup < PlannerQueryLevel); + abslevel = PlannerQueryLevel - agg->agglevelsup; + + /* + * It does not seem worthwhile to try to match duplicate outer aggs. + * Just make a new slot every time. + */ + agg = (Aggref *) copyObject(agg); + IncrementVarSublevelsUp((Node *) agg, - ((int) agg->agglevelsup), 0); + Assert(agg->agglevelsup == 0); + + pitem = (PlannerParamItem *) palloc(sizeof(PlannerParamItem)); + pitem->item = (Node *) agg; + pitem->abslevel = abslevel; + + PlannerParamList = lappend(PlannerParamList, pitem); + i = length(PlannerParamList) - 1; + + retval = makeNode(Param); + retval->paramkind = PARAM_EXEC; + retval->paramid = (AttrNumber) i; + retval->paramtype = agg->aggtype; + + return retval; +} + +/* * Generate a new Param node that will not conflict with any other. + * + * This is used to allocate PARAM_EXEC slots for subplan outputs. + * + * paramtypmod is currently unused but might be wanted someday. */ static Param * generate_new_param(Oid paramtype, int32 paramtypmod) { - Var *var = makeVar(0, 0, paramtype, paramtypmod, 0); - Param *retval = makeNode(Param); + Param *retval; + PlannerParamItem *pitem; + retval = makeNode(Param); retval->paramkind = PARAM_EXEC; - retval->paramid = (AttrNumber) new_param(var, 0); + retval->paramid = (AttrNumber) length(PlannerParamList); retval->paramtype = paramtype; + pitem = (PlannerParamItem *) palloc(sizeof(PlannerParamItem)); + pitem->item = (Node *) retval; + pitem->abslevel = PlannerQueryLevel; + + PlannerParamList = lappend(PlannerParamList, pitem); + return retval; } @@ -256,10 +315,9 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual) tmpset = bms_copy(plan->extParam); while ((paramid = bms_first_member(tmpset)) >= 0) { - Var *var = nth(paramid, PlannerParamVar); + PlannerParamItem *pitem = nth(paramid, PlannerParamList); - /* note varlevelsup is absolute level number */ - if (var->varlevelsup == PlannerQueryLevel) + if (pitem->abslevel == PlannerQueryLevel) node->parParam = lappendi(node->parParam, paramid); } bms_free(tmpset); @@ -408,17 +466,14 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual) args = NIL; foreach(lst, node->parParam) { - Var *var = nth(lfirsti(lst), PlannerParamVar); - - var = (Var *) copyObject(var); + PlannerParamItem *pitem = nth(lfirsti(lst), PlannerParamList); /* - * Must fix absolute-level varlevelsup from the - * PlannerParamVar entry. But since var is at current subplan - * level, this is easy: + * The Var or Aggref has already been adjusted to have the + * correct varlevelsup or agglevelsup. We probably don't even + * need to copy it again, but be safe. */ - var->varlevelsup = 0; - args = lappend(args, var); + args = lappend(args, copyObject(pitem->item)); } node->args = args; @@ -682,6 +737,20 @@ convert_IN_to_join(Query *parse, SubLink *sublink) /* * Replace correlation vars (uplevel vars) with Params. + * + * Uplevel aggregates are replaced, too. + * + * Note: it is critical that this runs immediately after SS_process_sublinks. + * Since we do not recurse into the arguments of uplevel aggregates, they will + * get copied to the appropriate subplan args list in the parent query with + * uplevel vars not replaced by Params, but only adjusted in level (see + * replace_outer_agg). That's exactly what we want for the vars of the parent + * level --- but if an aggregate's argument contains any further-up variables, + * they have to be replaced with Params in their turn. That will happen when + * the parent level runs SS_replace_correlation_vars. Therefore it must do + * so after expanding its sublinks to subplans. And we don't want any steps + * in between, else those steps would never get applied to the aggregate + * argument expressions, either in the parent or the child level. */ Node * SS_replace_correlation_vars(Node *expr) @@ -698,7 +767,12 @@ replace_correlation_vars_mutator(Node *node, void *context) if (IsA(node, Var)) { if (((Var *) node)->varlevelsup > 0) - return (Node *) replace_var((Var *) node); + return (Node *) replace_outer_var((Var *) node); + } + if (IsA(node, Aggref)) + { + if (((Aggref *) node)->agglevelsup > 0) + return (Node *) replace_outer_agg((Aggref *) node); } return expression_tree_mutator(node, replace_correlation_vars_mutator, @@ -785,19 +859,18 @@ SS_finalize_plan(Plan *plan, List *rtable) * We do this once to save time in the per-plan recursion steps. */ paramid = 0; - foreach(lst, PlannerParamVar) + foreach(lst, PlannerParamList) { - Var *var = (Var *) lfirst(lst); + PlannerParamItem *pitem = (PlannerParamItem *) lfirst(lst); - /* note varlevelsup is absolute level number */ - if (var->varlevelsup < PlannerQueryLevel) + if (pitem->abslevel < PlannerQueryLevel) { /* valid outer-level parameter */ outer_params = bms_add_member(outer_params, paramid); valid_params = bms_add_member(valid_params, paramid); } - else if (var->varlevelsup == PlannerQueryLevel && - var->varno == 0 && var->varattno == 0) + else if (pitem->abslevel == PlannerQueryLevel && + IsA(pitem->item, Param)) { /* valid local parameter (i.e., a setParam of my child) */ valid_params = bms_add_member(valid_params, paramid); |