aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/setrefs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/setrefs.c')
-rw-r--r--src/backend/optimizer/plan/setrefs.c723
1 files changed, 286 insertions, 437 deletions
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 1492df8b030..a983aa32143 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -1,13 +1,14 @@
/*-------------------------------------------------------------------------
*
* setrefs.c
- * Routines to change varno/attno entries to contain references
+ * Post-processing of a completed plan tree: fix references to subplan
+ * vars, and compute regproc values for operators
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.56 1999/08/21 03:49:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.57 1999/08/22 20:14:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,36 +26,24 @@
typedef struct {
List *outer_tlist;
List *inner_tlist;
-} replace_joinvar_refs_context;
+ Index acceptable_rel;
+} join_references_context;
typedef struct {
Index subvarno;
List *subplanTargetList;
} replace_vars_with_subplan_refs_context;
-typedef struct {
- List *groupClause;
- List *targetList;
-} check_having_for_ungrouped_vars_context;
-
-static void set_join_tlist_references(Join *join);
-static void set_nonamescan_tlist_references(SeqScan *nonamescan);
-static void set_noname_tlist_references(Noname *noname);
-static Node *replace_joinvar_refs(Node *clause,
- List *outer_tlist,
- List *inner_tlist);
-static Node *replace_joinvar_refs_mutator(Node *node,
- replace_joinvar_refs_context *context);
-static List *tlist_noname_references(Oid nonameid, List *tlist);
-static void set_result_tlist_references(Result *resultNode);
-static void replace_vars_with_subplan_refs(Node *clause,
- Index subvarno,
- List *subplanTargetList);
-static bool replace_vars_with_subplan_refs_walker(Node *node,
+static void set_join_references(Join *join);
+static void set_uppernode_references(Plan *plan, Index subvarno);
+static Node *join_references_mutator(Node *node,
+ join_references_context *context);
+static Node *replace_vars_with_subplan_refs(Node *node,
+ Index subvarno,
+ List *subplanTargetList);
+static Node *replace_vars_with_subplan_refs_mutator(Node *node,
replace_vars_with_subplan_refs_context *context);
-static bool pull_agg_clause_walker(Node *node, List **listptr);
-static bool check_having_for_ungrouped_vars_walker(Node *node,
- check_having_for_ungrouped_vars_context *context);
+static bool fix_opids_walker(Node *node, void *context);
/*****************************************************************************
*
@@ -63,340 +52,298 @@ static bool check_having_for_ungrouped_vars_walker(Node *node,
*****************************************************************************/
/*
- * set_tlist_references
- * Modifies the target list of nodes in a plan to reference target lists
- * at lower levels.
+ * 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).
*
- * 'plan' is the plan whose target list and children's target lists will
- * be modified
+ * set_plan_references recursively traverses the whole plan tree.
*
* Returns nothing of interest, but modifies internal fields of nodes.
- *
*/
void
-set_tlist_references(Plan *plan)
+set_plan_references(Plan *plan)
{
+ List *pl;
+
if (plan == NULL)
return;
- if (IsA_Join(plan))
- set_join_tlist_references((Join *) plan);
- else if (IsA(plan, SeqScan) && plan->lefttree &&
- IsA_Noname(plan->lefttree))
- set_nonamescan_tlist_references((SeqScan *) plan);
- else if (IsA_Noname(plan))
- set_noname_tlist_references((Noname *) plan);
- else if (IsA(plan, Result))
- set_result_tlist_references((Result *) plan);
- else if (IsA(plan, Hash))
- set_tlist_references(plan->lefttree);
-}
+ /*
+ * Plan-type-specific fixes
+ */
+ switch (nodeTag(plan))
+ {
+ case T_SeqScan:
+ /* nothing special */
+ break;
+ case T_IndexScan:
+ fix_opids((Node *) ((IndexScan *) plan)->indxqual);
+ fix_opids((Node *) ((IndexScan *) plan)->indxqualorig);
+ break;
+ case T_NestLoop:
+ set_join_references((Join *) plan);
+ break;
+ case T_MergeJoin:
+ set_join_references((Join *) plan);
+ fix_opids((Node *) ((MergeJoin *) plan)->mergeclauses);
+ break;
+ case T_HashJoin:
+ set_join_references((Join *) plan);
+ fix_opids((Node *) ((HashJoin *) plan)->hashclauses);
+ break;
+ case T_Material:
+ case T_Sort:
+ case T_Unique:
+ case T_Hash:
+ /* 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.
+ */
+ break;
+ case T_Agg:
+ case T_Group:
+ set_uppernode_references(plan, (Index) 0);
+ break;
+ case T_Result:
+ /* XXX why does Result use a different subvarno? */
+ set_uppernode_references(plan, (Index) OUTER);
+ fix_opids(((Result *) plan)->resconstantqual);
+ break;
+ case T_Append:
+ foreach(pl, ((Append *) plan)->appendplans)
+ {
+ set_plan_references((Plan *) lfirst(pl));
+ }
+ break;
+ default:
+ elog(ERROR, "set_plan_references: unknown plan type %d",
+ nodeTag(plan));
+ break;
+ }
-/*
- * set_join_tlist_references
- * Modifies the target list of a join node by setting the varnos and
- * varattnos to reference the target list of the outer and inner join
- * relations.
- *
- * Creates a target list for a join node to contain references by setting
- * varno values to OUTER or INNER and setting attno values to the
- * result domain number of either the corresponding outer or inner join
- * tuple.
- *
- * 'join' is a join plan node
- *
- * Returns nothing of interest, but modifies internal fields of nodes.
- *
- */
-static void
-set_join_tlist_references(Join *join)
-{
- Plan *outer = join->lefttree;
- Plan *inner = join->righttree;
- List *outer_tlist = ((outer == NULL) ? NIL : outer->targetlist);
- List *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist);
- List *new_join_targetlist = NIL;
- List *qptlist = join->targetlist;
- List *entry;
+ /*
+ * For all plan types, fix operators in targetlist and qual expressions
+ */
+ fix_opids((Node *) plan->targetlist);
+ fix_opids((Node *) plan->qual);
- foreach(entry, qptlist)
+ /*
+ * Now recurse into subplans, if any
+ *
+ * NOTE: it is essential that we recurse into subplans AFTER we set
+ * subplan references in this plan's tlist and quals. If we did the
+ * reference-adjustments bottom-up, then we would fail to match this
+ * plan's var nodes against the already-modified nodes of the subplans.
+ */
+ set_plan_references(plan->lefttree);
+ set_plan_references(plan->righttree);
+ foreach(pl, plan->initPlan)
{
- TargetEntry *xtl = (TargetEntry *) lfirst(entry);
- Node *joinexpr = replace_joinvar_refs(xtl->expr,
- outer_tlist,
- inner_tlist);
+ SubPlan *sp = (SubPlan *) lfirst(pl);
- new_join_targetlist = lappend(new_join_targetlist,
- makeTargetEntry(xtl->resdom, joinexpr));
+ Assert(IsA(sp, SubPlan));
+ set_plan_references(sp->plan);
}
- join->targetlist = new_join_targetlist;
+ foreach(pl, plan->subPlan)
+ {
+ SubPlan *sp = (SubPlan *) lfirst(pl);
- set_tlist_references(outer);
- set_tlist_references(inner);
+ Assert(IsA(sp, SubPlan));
+ set_plan_references(sp->plan);
+ }
}
/*
- * set_nonamescan_tlist_references
- * Modifies the target list of a node that scans a noname relation (i.e., a
- * sort or materialize node) so that the varnos refer to the child noname.
+ * set_join_references
+ * Modifies the target list of a join node to reference its subplans,
+ * by setting the varnos to OUTER or INNER and setting attno values to the
+ * result domain number of either the corresponding outer or inner join
+ * tuple item.
*
- * 'nonamescan' is a seqscan node
+ * Note: this same transformation has already been applied to the quals
+ * of the join by createplan.c. It's a little odd to do it here for the
+ * targetlist and there for the quals, but it's easier that way. (Look
+ * at switch_outer() and the handling of nestloop inner indexscans to
+ * see why.)
*
- * Returns nothing of interest, but modifies internal fields of nodes.
+ * Because the quals are reference-adjusted sooner, we cannot do equal()
+ * comparisons between qual and tlist var nodes during the time between
+ * creation of a plan node by createplan.c and its fixing by this module.
+ * Fortunately, there doesn't seem to be any need to do that.
*
+ * 'join' is a join plan node
*/
static void
-set_nonamescan_tlist_references(SeqScan *nonamescan)
+set_join_references(Join *join)
{
- Noname *noname = (Noname *) nonamescan->plan.lefttree;
+ Plan *outer = join->lefttree;
+ Plan *inner = join->righttree;
+ List *outer_tlist = ((outer == NULL) ? NIL : outer->targetlist);
+ List *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist);
- nonamescan->plan.targetlist = tlist_noname_references(noname->nonameid,
- nonamescan->plan.targetlist);
- /* since we know child is a Noname, skip recursion through
- * set_tlist_references() and just get the job done
- */
- set_noname_tlist_references(noname);
+ join->targetlist = join_references(join->targetlist,
+ outer_tlist,
+ inner_tlist,
+ (Index) 0);
}
/*
- * set_noname_tlist_references
- * The noname's vars are made consistent with (actually, identical to) the
- * modified version of the target list of the node from which noname node
- * receives its tuples.
- *
- * 'noname' is a noname (e.g., sort, materialize) plan node
- *
- * Returns nothing of interest, but modifies internal fields of nodes.
+ * set_uppernode_references
+ * Update the targetlist and quals of an upper-level plan node
+ * to refer to the tuples returned by its lefttree subplan.
*
+ * This is used for single-input plan types like Agg, Group, Result.
*/
static void
-set_noname_tlist_references(Noname *noname)
+set_uppernode_references(Plan *plan, Index subvarno)
{
- Plan *source = noname->plan.lefttree;
+ Plan *subplan = plan->lefttree;
+ List *subplanTargetList;
- if (source != NULL)
- {
- set_tlist_references(source);
- noname->plan.targetlist = copy_vars(noname->plan.targetlist,
- source->targetlist);
- }
+ if (subplan != NULL)
+ subplanTargetList = subplan->targetlist;
else
- elog(ERROR, "calling set_noname_tlist_references with empty lefttree");
+ subplanTargetList = NIL;
+
+ plan->targetlist = (List *)
+ replace_vars_with_subplan_refs((Node *) plan->targetlist,
+ subvarno,
+ subplanTargetList);
+
+ plan->qual = (List *)
+ replace_vars_with_subplan_refs((Node *) plan->qual,
+ subvarno,
+ subplanTargetList);
}
/*
* join_references
- * Creates a new set of join clauses by changing the varno/varattno
- * values of variables in the clauses to reference target list values
- * from the outer and inner join relation target lists.
- * This is just an external interface for replace_joinvar_refs.
- *
- * 'clauses' is the list of join clauses
+ * Creates a new set of targetlist entries or join qual clauses by
+ * changing the varno/varattno values of variables in the clauses
+ * to reference target list values from the outer and inner join
+ * relation target lists.
+ *
+ * This is used in two different scenarios: a normal join clause, where
+ * all the Vars in the clause *must* be replaced by OUTER or INNER references;
+ * and an indexscan being used on the inner side of a nestloop join.
+ * In the latter case we want to replace the outer-relation Vars by OUTER
+ * references, but not touch the Vars of the inner relation.
+ *
+ * For a normal join, acceptable_rel should be zero so that any failure to
+ * match a Var will be reported as an error. For the indexscan case,
+ * pass inner_tlist = NIL and acceptable_rel = the ID of the inner relation.
+ *
+ * 'clauses' is the targetlist or list of join clauses
* 'outer_tlist' is the target list of the outer join relation
- * 'inner_tlist' is the target list of the inner join relation
+ * 'inner_tlist' is the target list of the inner join relation, or NIL
+ * 'acceptable_rel' is either zero or the rangetable index of a relation
+ * whose Vars may appear in the clause without provoking an error.
*
- * Returns the new join clauses. The original clause structure is
+ * Returns the new expression tree. The original clause structure is
* not modified.
- *
*/
List *
join_references(List *clauses,
List *outer_tlist,
- List *inner_tlist)
+ List *inner_tlist,
+ Index acceptable_rel)
{
- return (List *) replace_joinvar_refs((Node *) clauses,
- outer_tlist,
- inner_tlist);
-}
-
-/*
- * replace_joinvar_refs
- *
- * Replaces all variables within a join clause with a new var node
- * whose varno/varattno fields contain a reference to a target list
- * element from either the outer or inner join relation.
- *
- * Returns a suitably modified copy of the join clause;
- * the original is not modified (and must not be!)
- *
- * Side effect: also runs fix_opids on the modified join clause.
- * Really ought to make that happen in a uniform, consistent place...
- *
- * 'clause' is the join clause
- * 'outer_tlist' is the target list of the outer join relation
- * 'inner_tlist' is the target list of the inner join relation
- */
-static Node *
-replace_joinvar_refs(Node *clause,
- List *outer_tlist,
- List *inner_tlist)
-{
- replace_joinvar_refs_context context;
+ join_references_context context;
context.outer_tlist = outer_tlist;
context.inner_tlist = inner_tlist;
- return (Node *) fix_opids((List *)
- replace_joinvar_refs_mutator(clause, &context));
+ context.acceptable_rel = acceptable_rel;
+ return (List *) join_references_mutator((Node *) clauses, &context);
}
static Node *
-replace_joinvar_refs_mutator(Node *node,
- replace_joinvar_refs_context *context)
+join_references_mutator(Node *node,
+ join_references_context *context)
{
if (node == NULL)
return NULL;
if (IsA(node, Var))
{
Var *var = (Var *) node;
- Resdom *resdom = tlist_member(var, context->outer_tlist);
-
- if (resdom != NULL && IsA(resdom, Resdom))
- return (Node *) makeVar(OUTER,
- resdom->resno,
- var->vartype,
- var->vartypmod,
- 0,
- var->varnoold,
- var->varoattno);
- resdom = tlist_member(var, context->inner_tlist);
- if (resdom != NULL && IsA(resdom, Resdom))
- return (Node *) makeVar(INNER,
- resdom->resno,
- var->vartype,
- var->vartypmod,
- 0,
- var->varnoold,
- var->varoattno);
- /* Var not in either tlist, return an unmodified copy. */
- return copyObject(node);
- }
- return expression_tree_mutator(node,
- replace_joinvar_refs_mutator,
- (void *) context);
-}
+ Var *newvar = (Var *) copyObject(var);
+ Resdom *resdom;
-/*
- * tlist_noname_references
- * Creates a new target list for a node that scans a noname relation,
- * setting the varnos to the id of the noname relation and setting varids
- * if necessary (varids are only needed if this is a targetlist internal
- * to the tree, in which case the targetlist entry always contains a var
- * node, so we can just copy it from the noname).
- *
- * 'nonameid' is the id of the noname relation
- * 'tlist' is the target list to be modified
- *
- * Returns new target list
- *
- */
-static List *
-tlist_noname_references(Oid nonameid,
- List *tlist)
-{
- List *t_list = NIL;
- List *entry;
-
- foreach(entry, tlist)
- {
- TargetEntry *xtl = lfirst(entry);
- AttrNumber oattno;
- TargetEntry *noname;
-
- if (IsA(get_expr(xtl), Var))
- oattno = ((Var *) xtl->expr)->varoattno;
- else
- oattno = 0;
-
- noname = makeTargetEntry(xtl->resdom,
- (Node *) makeVar(nonameid,
- xtl->resdom->resno,
- xtl->resdom->restype,
- xtl->resdom->restypmod,
- 0,
- nonameid,
- oattno));
-
- t_list = lappend(t_list, noname);
+ resdom = tlist_member((Node *) var, context->outer_tlist);
+ if (resdom)
+ {
+ newvar->varno = OUTER;
+ newvar->varattno = resdom->resno;
+ return (Node *) newvar;
+ }
+ resdom = tlist_member((Node *) var, context->inner_tlist);
+ if (resdom)
+ {
+ newvar->varno = INNER;
+ newvar->varattno = resdom->resno;
+ return (Node *) newvar;
+ }
+ /*
+ * Var not in either tlist --- either raise an error,
+ * or return the Var unmodified.
+ */
+ if (var->varno != context->acceptable_rel)
+ elog(ERROR, "join_references: variable not in subplan target lists");
+ return (Node *) newvar; /* copy is probably not necessary here... */
}
- return t_list;
-}
-
-/*---------------------------------------------------------
- *
- * set_result_tlist_references
- *
- * Change the target list of a Result node, so that it correctly
- * addresses the tuples returned by its left tree subplan.
- *
- * NOTE:
- * 1) we ignore the right tree! (in the current implementation
- * it is always nil)
- * 2) this routine will probably *NOT* work with nested dot
- * fields....
- */
-static void
-set_result_tlist_references(Result *resultNode)
-{
- Plan *subplan;
- List *resultTargetList;
- List *subplanTargetList;
-
- resultTargetList = ((Plan *) resultNode)->targetlist;
-
/*
- * NOTE: we only consider the left tree subplan. This is usually a seq
- * scan.
+ * expression_tree_mutator will copy SubPlan nodes if given a chance.
+ * We do not want to do that here, because subselect.c has already
+ * constructed the initPlan and subPlan lists of the current plan node
+ * and we mustn't leave those dangling (ie, pointing to different
+ * copies of the nodes than what's in the targetlist & quals...)
+ * Instead, alter the SubPlan in-place. Grotty --- is there a better way?
*/
- subplan = ((Plan *) resultNode)->lefttree;
- if (subplan != NULL)
- subplanTargetList = subplan->targetlist;
- else
- subplanTargetList = NIL;
-
- replace_tlist_with_subplan_refs(resultTargetList,
- (Index) OUTER,
- subplanTargetList);
-}
-
-/*---------------------------------------------------------
- *
- * replace_tlist_with_subplan_refs
- *
- * Applies replace_vars_with_subplan_refs() to each entry of a targetlist.
- */
-void
-replace_tlist_with_subplan_refs(List *tlist,
- Index subvarno,
- List *subplanTargetList)
-{
- List *t;
-
- foreach(t, tlist)
+ if (is_subplan(node))
{
- TargetEntry *entry = (TargetEntry *) lfirst(t);
-
- replace_vars_with_subplan_refs((Node *) get_expr(entry),
- subvarno, subplanTargetList);
+ Expr *expr = (Expr *) node;
+ SubLink *sublink = ((SubPlan *) expr->oper)->sublink;
+
+ /* transform args list (params to be passed to subplan) */
+ expr->args = (List *)
+ join_references_mutator((Node *) expr->args,
+ context);
+ /* transform sublink's oper list as well */
+ sublink->oper = (List *)
+ join_references_mutator((Node *) sublink->oper,
+ context);
+
+ return (Node *) expr;
}
+ return expression_tree_mutator(node,
+ join_references_mutator,
+ (void *) context);
}
-/*---------------------------------------------------------
- *
+/*
* replace_vars_with_subplan_refs
+ * This routine modifies an expression tree so that all Var nodes
+ * reference target nodes of a subplan. It is used to fix up
+ * target and qual expressions of non-join upper-level plan nodes.
*
- * This routine modifies (destructively!) an expression tree so that all
- * Var nodes reference target nodes of a subplan. It is used to fix up
- * target expressions of upper-level plan nodes.
+ * An error is raised if no matching var can be found in the subplan tlist
+ * --- so this routine should only be applied to nodes whose subplans'
+ * targetlists were generated via flatten_tlist() or some such method.
*
- * 'clause': the tree to be fixed
+ * 'node': the tree to be fixed (a targetlist or qual list)
* 'subvarno': varno to be assigned to all Vars
* 'subplanTargetList': target list for subplan
*
- * Afterwards, all Var nodes have varno = subvarno, varattno = resno
- * of corresponding subplan target.
+ * The resulting tree is a copy of the original in which all Var nodes have
+ * varno = subvarno, varattno = resno of corresponding subplan target.
+ * The original tree is not modified.
*/
-static void
-replace_vars_with_subplan_refs(Node *clause,
+static Node *
+replace_vars_with_subplan_refs(Node *node,
Index subvarno,
List *subplanTargetList)
{
@@ -404,182 +351,84 @@ replace_vars_with_subplan_refs(Node *clause,
context.subvarno = subvarno;
context.subplanTargetList = subplanTargetList;
- replace_vars_with_subplan_refs_walker(clause, &context);
+ return replace_vars_with_subplan_refs_mutator(node, &context);
}
-static bool
-replace_vars_with_subplan_refs_walker(Node *node,
+static Node *
+replace_vars_with_subplan_refs_mutator(Node *node,
replace_vars_with_subplan_refs_context *context)
{
if (node == NULL)
- return false;
+ return NULL;
if (IsA(node, Var))
{
Var *var = (Var *) node;
- TargetEntry *subplanVar;
+ Var *newvar = (Var *) copyObject(var);
+ Resdom *resdom;
- subplanVar = match_varid(var, context->subplanTargetList);
- if (!subplanVar)
- elog(ERROR, "replace_vars_with_subplan_refs: variable not in target list");
+ resdom = tlist_member((Node *) var, context->subplanTargetList);
+ if (!resdom)
+ elog(ERROR, "replace_vars_with_subplan_refs: variable not in subplan target list");
- /*
- * Change the varno & varattno fields of the var node.
- */
- var->varno = context->subvarno;
- var->varattno = subplanVar->resdom->resno;
- return false;
+ newvar->varno = context->subvarno;
+ newvar->varattno = resdom->resno;
+ return (Node *) newvar;
+ }
+ /*
+ * expression_tree_mutator will copy SubPlan nodes if given a chance.
+ * We do not want to do that here, because subselect.c has already
+ * constructed the initPlan and subPlan lists of the current plan node
+ * and we mustn't leave those dangling (ie, pointing to different
+ * copies of the nodes than what's in the targetlist & quals...)
+ * Instead, alter the SubPlan in-place. Grotty --- is there a better way?
+ */
+ if (is_subplan(node))
+ {
+ Expr *expr = (Expr *) node;
+ SubLink *sublink = ((SubPlan *) expr->oper)->sublink;
+
+ /* transform args list (params to be passed to subplan) */
+ expr->args = (List *)
+ replace_vars_with_subplan_refs_mutator((Node *) expr->args,
+ context);
+ /* transform sublink's oper list as well */
+ sublink->oper = (List *)
+ replace_vars_with_subplan_refs_mutator((Node *) sublink->oper,
+ context);
+
+ return (Node *) expr;
}
- return expression_tree_walker(node,
- replace_vars_with_subplan_refs_walker,
- (void *) context);
+ return expression_tree_mutator(node,
+ replace_vars_with_subplan_refs_mutator,
+ (void *) context);
}
/*****************************************************************************
- *
+ * OPERATOR REGPROC LOOKUP
*****************************************************************************/
-/*---------------------------------------------------------
- *
- * set_agg_tlist_references -
- * This routine has several responsibilities:
- * * Update the target list of an Agg node so that it points to
- * the tuples returned by its left tree subplan.
- * * If there is a qual list (from a HAVING clause), similarly update
- * vars in it to point to the subplan target list.
- *
- * The return value is TRUE if all qual clauses include Aggrefs, or FALSE
- * if any do not (caller may choose to raise an error condition).
- */
-bool
-set_agg_tlist_references(Agg *aggNode)
-{
- List *subplanTargetList;
- List *tl;
- List *ql;
- bool all_quals_ok;
-
- subplanTargetList = aggNode->plan.lefttree->targetlist;
-
- foreach(tl, aggNode->plan.targetlist)
- {
- TargetEntry *tle = lfirst(tl);
-
- replace_vars_with_subplan_refs(tle->expr,
- (Index) 0,
- subplanTargetList);
- }
-
- all_quals_ok = true;
- foreach(ql, aggNode->plan.qual)
- {
- Node *qual = lfirst(ql);
-
- replace_vars_with_subplan_refs(qual,
- (Index) 0,
- subplanTargetList);
- if (pull_agg_clause(qual) == NIL)
- all_quals_ok = false; /* this qual clause has no agg
- * functions! */
- }
-
- return all_quals_ok;
-}
-
/*
- * pull_agg_clause
- * Recursively pulls all Aggref nodes from an expression clause.
+ * fix_opids
+ * Calculate opid field from opno for each Oper node in given tree.
+ * The given tree can be anything expression_tree_walker handles.
*
- * Returns list of Aggref nodes found. Note the nodes themselves are not
- * copied, only referenced.
+ * The argument is modified in-place. (This is OK since we'd want the
+ * same change for any node, even if it gets visited more than once due to
+ * shared structure.)
*/
-List *
-pull_agg_clause(Node *clause)
-{
- List *result = NIL;
-
- pull_agg_clause_walker(clause, &result);
- return result;
-}
-
-static bool
-pull_agg_clause_walker(Node *node, List **listptr)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Aggref))
- {
- *listptr = lappend(*listptr, node);
- return false;
- }
- return expression_tree_walker(node, pull_agg_clause_walker,
- (void *) listptr);
-}
-
-/*
- * check_having_for_ungrouped_vars takes the havingQual and the list of
- * GROUP BY clauses and checks for subplans in the havingQual that are being
- * passed ungrouped variables as parameters. In other contexts, ungrouped
- * vars in the havingQual will be detected by the parser (see parse_agg.c,
- * exprIsAggOrGroupCol()). But that routine currently does not check subplans,
- * because the necessary info is not computed until the planner runs.
- * This ought to be cleaned up someday.
- */
-
void
-check_having_for_ungrouped_vars(Node *clause, List *groupClause,
- List *targetList)
+fix_opids(Node *node)
{
- check_having_for_ungrouped_vars_context context;
-
- context.groupClause = groupClause;
- context.targetList = targetList;
- check_having_for_ungrouped_vars_walker(clause, &context);
+ /* This tree walk requires no special setup, so away we go... */
+ fix_opids_walker(node, NULL);
}
static bool
-check_having_for_ungrouped_vars_walker(Node *node,
- check_having_for_ungrouped_vars_context *context)
+fix_opids_walker (Node *node, void *context)
{
if (node == NULL)
return false;
- /*
- * We can ignore Vars other than in subplan args lists,
- * since the parser already checked 'em.
- */
- if (is_subplan(node))
- {
- /*
- * The args list of the subplan node represents attributes from
- * outside passed into the sublink.
- */
- List *t;
-
- foreach(t, ((Expr *) node)->args)
- {
- Node *thisarg = lfirst(t);
- bool contained_in_group_clause = false;
- List *gl;
-
- foreach(gl, context->groupClause)
- {
- GroupClause *gcl = lfirst(gl);
- Node *groupexpr;
-
- groupexpr = get_sortgroupclause_expr(gcl,
- context->targetList);
- /* XXX is var_equal correct, or should we use equal()? */
- if (var_equal((Var *) thisarg, (Var *) groupexpr))
- {
- contained_in_group_clause = true;
- break;
- }
- }
-
- if (!contained_in_group_clause)
- elog(ERROR, "Sub-SELECT in HAVING clause must use only GROUPed attributes from outer SELECT");
- }
- }
- return expression_tree_walker(node,
- check_having_for_ungrouped_vars_walker,
- (void *) context);
+ if (is_opclause(node))
+ replace_opid((Oper *) ((Expr *) node)->oper);
+ return expression_tree_walker(node, fix_opids_walker, context);
}