/*-------------------------------------------------------------------------
*
- * subselect.c--
+ * subselect.c
+ * Planning routines for subselects and parameters.
+ *
+ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.44 2000/10/26 21:36:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
-
-#include "nodes/pg_list.h"
-#include "nodes/plannodes.h"
-#include "nodes/parsenodes.h"
-#include "nodes/relation.h"
#include "nodes/makefuncs.h"
-#include "nodes/nodeFuncs.h"
-
-#include "optimizer/subselect.h"
-#include "optimizer/planner.h"
-#include "optimizer/planmain.h"
-#include "optimizer/internal.h"
-#include "optimizer/paths.h"
#include "optimizer/clauses.h"
-#include "optimizer/keys.h"
-#include "optimizer/tlist.h"
-#include "optimizer/var.h"
#include "optimizer/cost.h"
+#include "optimizer/planmain.h"
+#include "optimizer/planner.h"
+#include "optimizer/subselect.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_oper.h"
+#include "utils/lsyscache.h"
-int PlannerQueryLevel; /* level of current query */
-List *PlannerVarParam; /* correlation Vars to Param mapper */
-List *PlannerParamVar; /* to get Var from Param->paramid */
+
+Index PlannerQueryLevel; /* level of current query */
List *PlannerInitPlan; /* init subplans for current query */
-int PlannerPlanId;
+List *PlannerParamVar; /* to get Var from Param->paramid */
+
+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.
+ *--------------------
+ */
+/*
+ * Create a new entry in the PlannerParamVar list, and return its index.
+ *
+ * var contains the data to be copied, except for varlevelsup which
+ * is set from the absolute level value given by varlevel.
+ */
static int
-_new_param(Var *var, int varlevel)
+new_param(Var *var, Index varlevel)
{
- List *last;
- int i = 0;
+ Var *paramVar = (Var *) copyObject(var);
- if (PlannerParamVar == NULL)
- last = PlannerParamVar = makeNode(List);
- else
- {
- for (last = PlannerParamVar;;)
- {
- i++;
- if (lnext(last) == NULL)
- break;
- last = lnext(last);
- }
- lnext(last) = makeNode(List);
- last = lnext(last);
- }
+ paramVar->varlevelsup = varlevel;
- lnext(last) = NULL;
- lfirst(last) = makeVar(var->varno, var->varattno, var->vartype,
- var->vartypmod, varlevel, var->varnoold, var->varoattno);
+ PlannerParamVar = lappend(PlannerParamVar, paramVar);
- return i;
+ 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_var(Var *var)
{
- List **rt = (List **) nth(var->varlevelsup, PlannerVarParam);
- List *vpe = rt[var->varno - 1];
+ List *ppv;
Param *retval;
+ Index varlevel;
int i;
- if (vpe == NULL)
- {
- vpe = rt[var->varno - 1] = makeNode(List);
- lfirsti(vpe) = -1;
- lnext(vpe) = NULL;
- }
+ Assert(var->varlevelsup > 0 && var->varlevelsup < PlannerQueryLevel);
+ varlevel = PlannerQueryLevel - var->varlevelsup;
- for (i = ObjectIdAttributeNumber;; i++)
+ /*
+ * If there's already a PlannerParamVar 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 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. The correct field should get stored into the Param slot at
+ * execution in each part of the tree.
+ */
+ i = 0;
+ foreach(ppv, PlannerParamVar)
{
- if (i == var->varattno)
+ Var *pvar = lfirst(ppv);
+
+ if (pvar->varno == var->varno &&
+ pvar->varattno == var->varattno &&
+ pvar->varlevelsup == varlevel &&
+ pvar->vartype == var->vartype)
break;
- if (lnext(vpe) == NULL)
- {
- lnext(vpe) = makeNode(List);
- vpe = lnext(vpe);
- lfirsti(vpe) = -1;
- lnext(vpe) = NULL;
- }
- else
- vpe = lnext(vpe);
+ i++;
}
- if ((i = lfirsti(vpe)) < 0) /* parameter is not assigned */
- i = _new_param(var, PlannerQueryLevel - var->varlevelsup);
+ if (!ppv)
+ {
+ /* Nope, so make a new one */
+ i = new_param(var, varlevel);
+ }
retval = makeNode(Param);
retval->paramkind = PARAM_EXEC;
return retval;
}
+/*
+ * Convert a bare SubLink (as created by the parser) into a SubPlan.
+ */
static Node *
-_make_subplan(SubLink *slink)
+make_subplan(SubLink *slink)
{
SubPlan *node = makeNode(SubPlan);
+ Query *subquery = (Query *) (slink->subselect);
+ double tuple_fraction;
Plan *plan;
List *lst;
Node *result;
- List *saved_ip = PlannerInitPlan;
- PlannerInitPlan = NULL;
-
- PlannerQueryLevel++; /* we becomes child */
-
- node->plan = plan = union_planner((Query *) slink->subselect);
+ /*
+ * Check to see if this node was already processed; if so we have
+ * trouble. We check to see if the linked-to Query appears to have
+ * been planned already, too.
+ */
+ if (subquery == NULL)
+ elog(ERROR, "make_subplan: invalid expression structure (SubLink already processed?)");
+ if (subquery->base_rel_list != NIL)
+ elog(ERROR, "make_subplan: invalid expression structure (subquery already processed?)");
/*
- * Assign subPlan, extParam and locParam to plan nodes. At the moment,
- * SS_finalize_plan doesn't handle initPlan-s and so we assigne them
- * to the topmost plan node and take care about its extParam too.
+ * Copy the source Query node. This is a quick and dirty kluge to resolve
+ * the fact that the parser can generate trees with multiple links to the
+ * same sub-Query node, but the planner wants to scribble on the Query.
+ * Try to clean this up when we do querytree redesign...
*/
- (void) SS_finalize_plan(plan);
- plan->initPlan = PlannerInitPlan;
+ subquery = (Query *) copyObject(subquery);
- /* Get extParam from InitPlan-s */
- foreach(lst, PlannerInitPlan)
- {
- List *lp;
+ /*
+ * For an EXISTS subplan, tell lower-level planner to expect that only
+ * the first tuple will be retrieved. For ALL and ANY subplans, we
+ * will be able to stop evaluating if the test condition fails, so
+ * very often not all the tuples will be retrieved; for lack of a
+ * better idea, specify 50% retrieval. For EXPR and MULTIEXPR
+ * subplans, use default behavior (we're only expecting one row out,
+ * anyway).
+ *
+ * NOTE: if you change these numbers, also change cost_qual_eval_walker()
+ * in path/costsize.c.
+ *
+ * XXX If an ALL/ANY subplan is uncorrelated, we may decide to
+ * materialize its result below. In that case it would've been better
+ * to specify full retrieval. At present, however, we can only detect
+ * correlation or lack of it after we've made the subplan :-(. Perhaps
+ * detection of correlation should be done as a separate step.
+ * Meanwhile, we don't want to be too optimistic about the percentage
+ * of tuples retrieved, for fear of selecting a plan that's bad for
+ * the materialization case.
+ */
+ if (slink->subLinkType == EXISTS_SUBLINK)
+ tuple_fraction = 1.0; /* just like a LIMIT 1 */
+ else if (slink->subLinkType == ALL_SUBLINK ||
+ slink->subLinkType == ANY_SUBLINK)
+ tuple_fraction = 0.5; /* 50% */
+ else
+ tuple_fraction = -1.0; /* default behavior */
- foreach(lp, ((SubPlan *) lfirst(lst))->plan->extParam)
- {
- if (!intMember(lfirsti(lp), plan->extParam))
- plan->extParam = lappendi(plan->extParam, lfirsti(lp));
- }
- }
+ /*
+ * Generate the plan for the subquery.
+ */
+ node->plan = plan = subquery_planner(subquery, tuple_fraction);
- /* and now we are parent again */
- PlannerInitPlan = saved_ip;
- PlannerQueryLevel--;
+ node->plan_id = PlannerPlanId++; /* Assign unique ID to this SubPlan */
- node->plan_id = PlannerPlanId++;
- node->rtable = ((Query *) slink->subselect)->rtable;
+ node->rtable = subquery->rtable;
node->sublink = slink;
- slink->subselect = NULL; /* cool ?! */
- /* make parParam list */
+ slink->subselect = NULL; /* cool ?! see error check above! */
+
+ /*
+ * Make parParam list of params that current query level will pass
+ * to this child plan.
+ */
foreach(lst, plan->extParam)
{
- Var *var = nth(lfirsti(lst), PlannerParamVar);
+ int paramid = lfirsti(lst);
+ Var *var = nth(paramid, PlannerParamVar);
+ /* note varlevelsup is absolute level number */
if (var->varlevelsup == PlannerQueryLevel)
- node->parParam = lappendi(node->parParam, lfirsti(lst));
+ node->parParam = lappendi(node->parParam, paramid);
}
/*
- * Un-correlated or undirect correlated plans of EXISTS or EXPR types
- * can be used as initPlans...
+ * Un-correlated or undirect correlated plans of EXISTS, EXPR, or
+ * MULTIEXPR types can be used as initPlans. For EXISTS or EXPR, we
+ * just produce a Param referring to the result of evaluating the
+ * initPlan. For MULTIEXPR, we must build an AND or OR-clause of the
+ * individual comparison operators, using the appropriate lefthand
+ * side expressions and Params for the initPlan's target items.
*/
- if (node->parParam == NULL && slink->subLinkType == EXPR_SUBLINK)
+ if (node->parParam == NIL && slink->subLinkType == EXISTS_SUBLINK)
+ {
+ Var *var = makeVar(0, 0, BOOLOID, -1, 0);
+ Param *prm = makeNode(Param);
+
+ prm->paramkind = PARAM_EXEC;
+ prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
+ prm->paramtype = var->vartype;
+ pfree(var); /* var is only needed for new_param */
+ node->setParam = lappendi(node->setParam, prm->paramid);
+ PlannerInitPlan = lappend(PlannerInitPlan, node);
+ result = (Node *) prm;
+ }
+ else if (node->parParam == NIL && slink->subLinkType == EXPR_SUBLINK)
+ {
+ TargetEntry *te = lfirst(plan->targetlist);
+
+ /* need a var node just to pass to new_param()... */
+ Var *var = makeVar(0, 0, te->resdom->restype,
+ te->resdom->restypmod, 0);
+ Param *prm = makeNode(Param);
+
+ prm->paramkind = PARAM_EXEC;
+ prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
+ prm->paramtype = var->vartype;
+ pfree(var); /* var is only needed for new_param */
+ node->setParam = lappendi(node->setParam, prm->paramid);
+ PlannerInitPlan = lappend(PlannerInitPlan, node);
+ result = (Node *) prm;
+ }
+ else if (node->parParam == NIL && slink->subLinkType == MULTIEXPR_SUBLINK)
{
+ List *newoper = NIL;
int i = 0;
- /* transform right side of all sublink Oper-s into Param */
+ /*
+ * Convert oper list of Opers into a list of Exprs, using lefthand
+ * arguments and Params representing inside results.
+ */
foreach(lst, slink->oper)
{
- List *rside = lnext(((Expr *) lfirst(lst))->args);
+ Oper *oper = (Oper *) lfirst(lst);
+ Node *lefthand = nth(i, slink->lefthand);
TargetEntry *te = nth(i, plan->targetlist);
+
+ /* need a var node just to pass to new_param()... */
Var *var = makeVar(0, 0, te->resdom->restype,
- te->resdom->restypmod,
- PlannerQueryLevel, 0, 0);
+ te->resdom->restypmod, 0);
Param *prm = makeNode(Param);
+ Operator tup;
+ Form_pg_operator opform;
+ Node *left,
+ *right;
prm->paramkind = PARAM_EXEC;
- prm->paramid = (AttrNumber) _new_param(var, PlannerQueryLevel);
+ prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
prm->paramtype = var->vartype;
- lfirst(rside) = prm;
+ pfree(var); /* var is only needed for new_param */
+
+ Assert(IsA(oper, Oper));
+ tup = get_operator_tuple(oper->opno);
+ Assert(HeapTupleIsValid(tup));
+ opform = (Form_pg_operator) GETSTRUCT(tup);
+
+ /*
+ * Note: we use make_operand in case runtime type conversion
+ * function calls must be inserted for this operator!
+ */
+ left = make_operand("", lefthand,
+ exprType(lefthand), opform->oprleft);
+ right = make_operand("", (Node *) prm,
+ prm->paramtype, opform->oprright);
+ newoper = lappend(newoper,
+ make_opclause(oper,
+ (Var *) left,
+ (Var *) right));
node->setParam = lappendi(node->setParam, prm->paramid);
- pfree(var);
i++;
}
+ slink->oper = newoper;
+ slink->lefthand = NIL;
PlannerInitPlan = lappend(PlannerInitPlan, node);
if (i > 1)
- result = (Node *) ((slink->useor) ? make_orclause(slink->oper) :
- make_andclause(slink->oper));
+ result = (Node *) ((slink->useor) ? make_orclause(newoper) :
+ make_andclause(newoper));
else
- result = (Node *) lfirst(slink->oper);
- }
- else if (node->parParam == NULL && slink->subLinkType == EXISTS_SUBLINK)
- {
- Var *var = makeVar(0, 0, BOOLOID, -1, PlannerQueryLevel, 0, 0);
- Param *prm = makeNode(Param);
-
- prm->paramkind = PARAM_EXEC;
- prm->paramid = (AttrNumber) _new_param(var, PlannerQueryLevel);
- prm->paramtype = var->vartype;
- node->setParam = lappendi(node->setParam, prm->paramid);
- pfree(var);
- PlannerInitPlan = lappend(PlannerInitPlan, node);
- result = (Node *) prm;
+ result = (Node *) lfirst(newoper);
}
else
-/* make expression of SUBPLAN type */
{
Expr *expr = makeNode(Expr);
- List *args = NULL;
+ List *args = NIL;
+ List *newoper = NIL;
int i = 0;
- expr->typeOid = BOOLOID;
+ /*
+ * We can't convert subplans of ALL_SUBLINK or ANY_SUBLINK types
+ * to initPlans, even when they are uncorrelated or undirect
+ * correlated, because we need to scan the output of the subplan
+ * for each outer tuple. However, we have the option to tack a
+ * MATERIAL node onto the top of an uncorrelated/undirect
+ * correlated subplan, which lets us do the work of evaluating the
+ * subplan only once. We do this if the subplan's top plan node
+ * is anything more complicated than a plain sequential scan, and
+ * we do it even for seqscan if the qual appears selective enough
+ * to eliminate many tuples.
+ */
+ if (node->parParam == NIL)
+ {
+ bool use_material;
+
+ switch (nodeTag(plan))
+ {
+ case T_SeqScan:
+ if (plan->initPlan || plan->subPlan)
+ use_material = true;
+ else
+ {
+ Selectivity qualsel;
+
+ qualsel = clauselist_selectivity(subquery,
+ plan->qual,
+ 0);
+ /* Is 10% selectivity a good threshold?? */
+ use_material = qualsel < 0.10;
+ }
+ break;
+ case T_Material:
+ case T_Sort:
+
+ /*
+ * Don't add another Material node if there's one
+ * already, nor if the top node is a Sort, since Sort
+ * materializes its output anyway. (I doubt either
+ * case can happen in practice for a subplan, but...)
+ */
+ use_material = false;
+ break;
+ default:
+ use_material = true;
+ break;
+ }
+ if (use_material)
+ {
+ plan = (Plan *) make_material(plan->targetlist, plan);
+ node->plan = plan;
+ }
+ }
+
+ /*
+ * Make expression of SUBPLAN type
+ */
+ expr->typeOid = BOOLOID;/* bogus, but we don't really care */
expr->opType = SUBPLAN_EXPR;
expr->oper = (Node *) node;
/*
- * Make expr->args from parParam. Left sides of sublink Oper-s are
- * handled by optimizer directly... Also, transform right side of
- * sublink Oper-s into Const.
+ * Make expr->args from parParam.
*/
foreach(lst, node->parParam)
{
Var *var = nth(lfirsti(lst), PlannerParamVar);
var = (Var *) copyObject(var);
+
+ /*
+ * Must fix absolute-level varlevelsup from the
+ * PlannerParamVar entry. But since var is at current subplan
+ * level, this is easy:
+ */
var->varlevelsup = 0;
args = lappend(args, var);
}
+ expr->args = args;
+
+ /*
+ * Convert oper list of Opers into a list of Exprs, using lefthand
+ * arguments and Consts representing inside results.
+ */
foreach(lst, slink->oper)
{
- List *rside = lnext(((Expr *) lfirst(lst))->args);
+ Oper *oper = (Oper *) lfirst(lst);
+ Node *lefthand = nth(i, slink->lefthand);
TargetEntry *te = nth(i, plan->targetlist);
- Const *con = makeConst(te->resdom->restype,
- 0, 0, true, 0, 0, 0);
-
- lfirst(rside) = con;
+ Const *con;
+ Operator tup;
+ Form_pg_operator opform;
+ Node *left,
+ *right;
+
+ /*
+ * XXX really ought to fill in constlen and constbyval
+ * correctly, but right now ExecEvalExpr won't look at them...
+ */
+ con = makeConst(te->resdom->restype, 0, 0, true, 0, 0, 0);
+
+ Assert(IsA(oper, Oper));
+ tup = get_operator_tuple(oper->opno);
+ Assert(HeapTupleIsValid(tup));
+ opform = (Form_pg_operator) GETSTRUCT(tup);
+
+ /*
+ * Note: we use make_operand in case runtime type conversion
+ * function calls must be inserted for this operator!
+ */
+ left = make_operand("", lefthand,
+ exprType(lefthand), opform->oprleft);
+ right = make_operand("", (Node *) con,
+ con->consttype, opform->oprright);
+ newoper = lappend(newoper,
+ make_opclause(oper,
+ (Var *) left,
+ (Var *) right));
i++;
}
- expr->args = args;
+ slink->oper = newoper;
+ slink->lefthand = NIL;
result = (Node *) expr;
}
return result;
-
}
-static List *
-set_unioni(List *l1, List *l2)
-{
- if (l1 == NULL)
- return l2;
- if (l2 == NULL)
- return l1;
-
- return nconc(l1, set_differencei(l2, l1));
-}
+/*
+ * finalize_primnode: build lists of subplans and params appearing
+ * in the given expression tree. NOTE: items are added to lists passed in,
+ * so caller must initialize lists to NIL before first call!
+ *
+ * Note: the subplan list that is constructed here and assigned to the
+ * plan's subPlan field will be replaced with an up-to-date list in
+ * set_plan_references(). We could almost dispense with building this
+ * subplan list at all; I believe the only place that uses it is the
+ * check in make_subplan to see whether a subselect has any subselects.
+ */
-static List *
-_finalize_primnode(void *expr, List **subplan)
+typedef struct finalize_primnode_results
{
- List *result = NULL;
-
- if (expr == NULL)
- return NULL;
+ List *subplans; /* List of subplans found in expr */
+ List *paramids; /* List of PARAM_EXEC paramids found */
+} finalize_primnode_results;
- if (IsA(expr, Param))
- {
- if (((Param *) expr)->paramkind == PARAM_EXEC)
- return lconsi(((Param *) expr)->paramid, (List *) NULL);
- }
- else if (single_node(expr))
- return NULL;
- else if (IsA(expr, List))
+static bool
+finalize_primnode(Node *node, finalize_primnode_results *results)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Param))
{
- List *le;
+ if (((Param *) node)->paramkind == PARAM_EXEC)
+ {
+ int paramid = (int) ((Param *) node)->paramid;
- foreach(le, (List *) expr)
- result = set_unioni(result,
- _finalize_primnode(lfirst(le), subplan));
- }
- else if (IsA(expr, Iter))
- return _finalize_primnode(((Iter *) expr)->iterexpr, subplan);
- else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
- not_clause(expr) || is_funcclause(expr))
- return _finalize_primnode(((Expr *) expr)->args, subplan);
- else if (IsA(expr, Aggreg))
- return _finalize_primnode(((Aggreg *) expr)->target, subplan);
- else if (IsA(expr, ArrayRef))
- {
- result = _finalize_primnode(((ArrayRef *) expr)->refupperindexpr, subplan);
- result = set_unioni(result,
- _finalize_primnode(((ArrayRef *) expr)->reflowerindexpr, subplan));
- result = set_unioni(result,
- _finalize_primnode(((ArrayRef *) expr)->refexpr, subplan));
- result = set_unioni(result,
- _finalize_primnode(((ArrayRef *) expr)->refassgnexpr, subplan));
+ if (!intMember(paramid, results->paramids))
+ results->paramids = lconsi(paramid, results->paramids);
+ }
+ return false; /* no more to do here */
}
- else if (IsA(expr, TargetEntry))
- return _finalize_primnode(((TargetEntry *) expr)->expr, subplan);
- else if (is_subplan(expr))
+ if (is_subplan(node))
{
+ SubPlan *subplan = (SubPlan *) ((Expr *) node)->oper;
List *lst;
- *subplan = lappend(*subplan, ((Expr *) expr)->oper);
- foreach(lst, ((SubPlan *) ((Expr *) expr)->oper)->plan->extParam)
+ /* Add subplan to subplans list */
+ results->subplans = lappend(results->subplans, subplan);
+ /* Check extParam list for params to add to paramids */
+ foreach(lst, subplan->plan->extParam)
{
- Var *var = nth(lfirsti(lst), PlannerParamVar);
+ int paramid = lfirsti(lst);
+ Var *var = nth(paramid, PlannerParamVar);
+ /* note varlevelsup is absolute level number */
if (var->varlevelsup < PlannerQueryLevel &&
- !intMember(lfirsti(lst), result))
- result = lappendi(result, lfirsti(lst));
+ !intMember(paramid, results->paramids))
+ results->paramids = lconsi(paramid, results->paramids);
}
+ /* fall through to recurse into subplan args */
}
- else
- elog(ERROR, "_finalize_primnode: can't handle node %d",
- nodeTag(expr));
-
- return result;
+ return expression_tree_walker(node, finalize_primnode,
+ (void *) results);
}
+/*
+ * Replace correlation vars (uplevel vars) with Params.
+ */
+
+static Node *replace_correlation_vars_mutator(Node *node, void *context);
+
Node *
SS_replace_correlation_vars(Node *expr)
{
+ /* No setup needed for tree walk, so away we go */
+ return replace_correlation_vars_mutator(expr, NULL);
+}
- if (expr == NULL)
+static Node *
+replace_correlation_vars_mutator(Node *node, void *context)
+{
+ if (node == NULL)
return NULL;
- if (IsA(expr, List))
- {
- List *le;
-
- foreach(le, (List *) expr)
- lfirst(le) = SS_replace_correlation_vars((Node *) lfirst(le));
- }
- else if (IsA(expr, Var))
+ if (IsA(node, Var))
{
- if (((Var *) expr)->varlevelsup > 0)
- {
- Assert(((Var *) expr)->varlevelsup < PlannerQueryLevel);
- expr = (Node *) _replace_var((Var *) expr);
- }
+ if (((Var *) node)->varlevelsup > 0)
+ return (Node *) replace_var((Var *) node);
}
- else if (IsA(expr, Iter))
- {
- ((Iter *) expr)->iterexpr =
- SS_replace_correlation_vars(((Iter *) expr)->iterexpr);
- }
- else if (single_node(expr))
- return expr;
- else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
- not_clause(expr) || is_funcclause(expr))
- ((Expr *) expr)->args = (List *)
- SS_replace_correlation_vars((Node *) ((Expr *) expr)->args);
- else if (IsA(expr, Aggreg))
- ((Aggreg *) expr)->target =
- SS_replace_correlation_vars((Node *) ((Aggreg *) expr)->target);
- else if (IsA(expr, ArrayRef))
- {
- ((ArrayRef *) expr)->refupperindexpr = (List *)
- SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->refupperindexpr);
- ((ArrayRef *) expr)->reflowerindexpr = (List *)
- SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->reflowerindexpr);
- ((ArrayRef *) expr)->refexpr =
- SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->refexpr);
- ((ArrayRef *) expr)->refassgnexpr =
- SS_replace_correlation_vars(((ArrayRef *) expr)->refassgnexpr);
- }
- else if (IsA(expr, TargetEntry))
- ((TargetEntry *) expr)->expr =
- SS_replace_correlation_vars((Node *) ((TargetEntry *) expr)->expr);
- else if (IsA(expr, SubLink))
- {
- List *le;
-
- foreach(le, ((SubLink *) expr)->oper) /* left sides only */
- {
- List *oparg = ((Expr *) lfirst(le))->args;
+ return expression_tree_mutator(node,
+ replace_correlation_vars_mutator,
+ context);
+}
- lfirst(oparg) = (List *)
- SS_replace_correlation_vars((Node *) lfirst(oparg));
- }
- ((SubLink *) expr)->lefthand = (List *)
- SS_replace_correlation_vars((Node *) ((SubLink *) expr)->lefthand);
- }
- else
- elog(NOTICE, "SS_replace_correlation_vars: can't handle node %d",
- nodeTag(expr));
+/*
+ * Expand SubLinks to SubPlans in the given expression.
+ */
- return expr;
-}
+static Node *process_sublinks_mutator(Node *node, void *context);
Node *
SS_process_sublinks(Node *expr)
{
- if (expr == NULL)
+ /* No setup needed for tree walk, so away we go */
+ return process_sublinks_mutator(expr, NULL);
+}
+
+static Node *
+process_sublinks_mutator(Node *node, void *context)
+{
+ if (node == NULL)
return NULL;
- if (IsA(expr, List))
+ if (IsA(node, SubLink))
{
- List *le;
+ SubLink *sublink = (SubLink *) node;
- foreach(le, (List *) expr)
- lfirst(le) = SS_process_sublinks((Node *) lfirst(le));
- }
- else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
- not_clause(expr) || is_funcclause(expr))
- ((Expr *) expr)->args = (List *)
- SS_process_sublinks((Node *) ((Expr *) expr)->args);
- else if (IsA(expr, SubLink))/* got it! */
- {
- expr = _make_subplan((SubLink *) expr);
+ /*
+ * First, scan the lefthand-side expressions, if any. This is a
+ * tad klugy since we modify the input SubLink node, but that
+ * should be OK (make_subplan does it too!)
+ */
+ sublink->lefthand = (List *)
+ process_sublinks_mutator((Node *) sublink->lefthand, context);
+ /* Now build the SubPlan node and make the expr to return */
+ return make_subplan(sublink);
}
- return expr;
+ /*
+ * Note that we will never see a SubPlan expression in the input
+ * (since this is the very routine that creates 'em to begin with). So
+ * the code in expression_tree_mutator() that might do inappropriate
+ * things with SubPlans or SubLinks will not be exercised.
+ */
+ Assert(!is_subplan(node));
+
+ return expression_tree_mutator(node,
+ process_sublinks_mutator,
+ context);
}
List *
SS_finalize_plan(Plan *plan)
{
- List *extParam = NULL;
- List *locParam = NULL;
- List *subPlan = NULL;
- List *param_list;
+ List *extParam = NIL;
+ List *locParam = NIL;
+ finalize_primnode_results results;
List *lst;
if (plan == NULL)
- return NULL;
+ return NIL;
+
+ results.subplans = NIL; /* initialize lists to NIL */
+ results.paramids = NIL;
+
+ /*
+ * When we call finalize_primnode, results.paramids lists are
+ * automatically merged together. But when recursing to self, we have
+ * to do it the hard way. We want the paramids list to include params
+ * in subplans as well as at this level. (We don't care about finding
+ * subplans of subplans, though.)
+ */
- param_list = _finalize_primnode(plan->targetlist, &subPlan);
- Assert(subPlan == NULL);
+ /* Find params and subplans in targetlist and qual */
+ finalize_primnode((Node *) plan->targetlist, &results);
+ finalize_primnode((Node *) plan->qual, &results);
+ /* Check additional node-type-specific fields */
switch (nodeTag(plan))
{
case T_Result:
- param_list = set_unioni(param_list,
- _finalize_primnode(((Result *) plan)->resconstantqual, &subPlan));
+ finalize_primnode(((Result *) plan)->resconstantqual,
+ &results);
break;
case T_Append:
foreach(lst, ((Append *) plan)->appendplans)
- param_list = set_unioni(param_list,
+ results.paramids = set_unioni(results.paramids,
SS_finalize_plan((Plan *) lfirst(lst)));
break;
+ case T_SubqueryScan:
+ results.paramids = set_unioni(results.paramids,
+ SS_finalize_plan(((SubqueryScan *) plan)->subplan));
+ break;
+
case T_IndexScan:
- param_list = set_unioni(param_list,
- _finalize_primnode(((IndexScan *) plan)->indxqual, &subPlan));
- Assert(subPlan == NULL);
+ finalize_primnode((Node *) ((IndexScan *) plan)->indxqual,
+ &results);
+
+ /*
+ * we need not look at indxqualorig, since it will have the
+ * same param references as indxqual, and we aren't really
+ * concerned yet about having a complete subplan list.
+ */
+ break;
+
+ case T_NestLoop:
+ finalize_primnode((Node *) ((Join *) plan)->joinqual,
+ &results);
break;
case T_MergeJoin:
- param_list = set_unioni(param_list,
- _finalize_primnode(((MergeJoin *) plan)->mergeclauses, &subPlan));
- Assert(subPlan == NULL);
+ finalize_primnode((Node *) ((Join *) plan)->joinqual,
+ &results);
+ finalize_primnode((Node *) ((MergeJoin *) plan)->mergeclauses,
+ &results);
break;
case T_HashJoin:
- param_list = set_unioni(param_list,
- _finalize_primnode(((HashJoin *) plan)->hashclauses, &subPlan));
- Assert(subPlan == NULL);
+ finalize_primnode((Node *) ((Join *) plan)->joinqual,
+ &results);
+ finalize_primnode((Node *) ((HashJoin *) plan)->hashclauses,
+ &results);
break;
case T_Hash:
- param_list = set_unioni(param_list,
- _finalize_primnode(((Hash *) plan)->hashkey, &subPlan));
- Assert(subPlan == NULL);
+ finalize_primnode(((Hash *) plan)->hashkey,
+ &results);
break;
- case T_Agg:
- param_list = set_unioni(param_list,
- _finalize_primnode(((Agg *) plan)->aggs, &subPlan));
- Assert(subPlan == NULL);
+ case T_TidScan:
+ finalize_primnode((Node *) ((TidScan *) plan)->tideval,
+ &results);
break;
+ case T_Agg:
case T_SeqScan:
- case T_NestLoop:
case T_Material:
case T_Sort:
case T_Unique:
+ case T_SetOp:
+ case T_Limit:
case T_Group:
break;
+
default:
- elog(ERROR, "SS_finalize_plan: node %d unsupported", nodeTag(plan));
- return NULL;
+ elog(ERROR, "SS_finalize_plan: node %d unsupported",
+ nodeTag(plan));
}
- param_list = set_unioni(param_list, _finalize_primnode(plan->qual, &subPlan));
- param_list = set_unioni(param_list, SS_finalize_plan(plan->lefttree));
- param_list = set_unioni(param_list, SS_finalize_plan(plan->righttree));
+ /* Process left and right subplans, if any */
+ results.paramids = set_unioni(results.paramids,
+ SS_finalize_plan(plan->lefttree));
+ results.paramids = set_unioni(results.paramids,
+ SS_finalize_plan(plan->righttree));
+
+ /* Now we have all the paramids and subplans */
- foreach(lst, param_list)
+ foreach(lst, results.paramids)
{
- Var *var = nth(lfirsti(lst), PlannerParamVar);
+ int paramid = lfirsti(lst);
+ Var *var = nth(paramid, PlannerParamVar);
+ /* note varlevelsup is absolute level number */
if (var->varlevelsup < PlannerQueryLevel)
- extParam = lappendi(extParam, lfirsti(lst));
+ extParam = lappendi(extParam, paramid);
else if (var->varlevelsup > PlannerQueryLevel)
- elog(ERROR, "SS_finalize_plan: plan shouldn't reference subplan' variable");
+ elog(ERROR, "SS_finalize_plan: plan shouldn't reference subplan's variable");
else
{
Assert(var->varno == 0 && var->varattno == 0);
- locParam = lappendi(locParam, lfirsti(lst));
+ locParam = lappendi(locParam, paramid);
}
}
plan->extParam = extParam;
plan->locParam = locParam;
- plan->subPlan = subPlan;
-
- return param_list;
-
-}
-
-List *SS_pull_subplan(void *expr);
-
-List *
-SS_pull_subplan(void *expr)
-{
- List *result = NULL;
-
- if (expr == NULL || single_node(expr))
- return NULL;
+ plan->subPlan = results.subplans;
- if (IsA(expr, List))
- {
- List *le;
-
- foreach(le, (List *) expr)
- result = nconc(result, SS_pull_subplan(lfirst(le)));
- }
- else if (IsA(expr, Iter))
- return SS_pull_subplan(((Iter *) expr)->iterexpr);
- else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
- not_clause(expr) || is_funcclause(expr))
- return SS_pull_subplan(((Expr *) expr)->args);
- else if (IsA(expr, Aggreg))
- return SS_pull_subplan(((Aggreg *) expr)->target);
- else if (IsA(expr, ArrayRef))
- {
- result = SS_pull_subplan(((ArrayRef *) expr)->refupperindexpr);
- result = nconc(result,
- SS_pull_subplan(((ArrayRef *) expr)->reflowerindexpr));
- result = nconc(result,
- SS_pull_subplan(((ArrayRef *) expr)->refexpr));
- result = nconc(result,
- SS_pull_subplan(((ArrayRef *) expr)->refassgnexpr));
- }
- else if (IsA(expr, TargetEntry))
- return SS_pull_subplan(((TargetEntry *) expr)->expr);
- else if (is_subplan(expr))
- return lcons(((Expr *) expr)->oper, NULL);
- else
- elog(ERROR, "SS_pull_subplan: can't handle node %d",
- nodeTag(expr));
-
- return result;
+ return results.paramids;
}