/*-------------------------------------------------------------------------
 *
 * setrefs.c
 *	  Routines to change varno/attno entries to contain references
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.50 1999/06/06 17:38:10 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <sys/types.h>

#include "postgres.h"

#include "nodes/pg_list.h"
#include "nodes/plannodes.h"
#include "nodes/primnodes.h"
#include "nodes/relation.h"

#include "utils/elog.h"
#include "nodes/nodeFuncs.h"
#include "nodes/makefuncs.h"

#include "optimizer/internal.h"
#include "optimizer/clauses.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/keys.h"
#include "optimizer/planmain.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "optimizer/tlist.h"

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_clause_joinvar_refs(Node *clause,
							List *outer_tlist,
							List *inner_tlist);
static Var *replace_joinvar_refs(Var *var,
					 List *outer_tlist,
					 List *inner_tlist);
static List *tlist_noname_references(Oid nonameid, List *tlist);
static bool OperandIsInner(Node *opnd, int inner_relid);
static List *pull_agg_clause(Node *clause);
static void set_result_tlist_references(Result *resultNode);
static void replace_vars_with_subplan_refs(Node *clause,
							   Index subvarno,
							   List *subplanTargetList);

/*****************************************************************************
 *
 *		SUBPLAN REFERENCES
 *
 *****************************************************************************/

/*
 * set_tlist_references
 *	  Modifies the target list of nodes in a plan to reference target lists
 *	  at lower levels.
 *
 * 'plan' is the plan whose target list and children's target lists will
 *		be modified
 *
 * Returns nothing of interest, but modifies internal fields of nodes.
 *
 */
void
set_tlist_references(Plan *plan)
{
	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(plan, Sort))
		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);
}

/*
 * 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 = ((Plan *) join)->lefttree;
	Plan	   *inner = ((Plan *) 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 = ((Plan *) join)->targetlist;
	List	   *entry;

	foreach(entry, qptlist)
	{
		TargetEntry *xtl = (TargetEntry *) lfirst(entry);
		Node	   *joinvar = replace_clause_joinvar_refs(xtl->expr,
														  outer_tlist,
														  inner_tlist);

		new_join_targetlist = lappend(new_join_targetlist,
								  makeTargetEntry(xtl->resdom, joinvar));
	}

	((Plan *) join)->targetlist = new_join_targetlist;
	if (outer != NULL)
		set_tlist_references(outer);
	if (inner != NULL)
		set_tlist_references(inner);
}

/*
 * set_nonamescan_tlist_references
 *	  Modifies the target list of a node that scans a noname relation (i.e., a
 *	  sort or hash node) so that the varnos refer to the child noname.
 *
 * 'nonamescan' is a seqscan node
 *
 * Returns nothing of interest, but modifies internal fields of nodes.
 *
 */
static void
set_nonamescan_tlist_references(SeqScan *nonamescan)
{
	Noname	   *noname = (Noname *) ((Plan *) nonamescan)->lefttree;

	((Plan *) nonamescan)->targetlist = tlist_noname_references(noname->nonameid,
									  ((Plan *) nonamescan)->targetlist);
	set_noname_tlist_references(noname);
}

/*
 * 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, hash) plan node
 *
 * Returns nothing of interest, but modifies internal fields of nodes.
 *
 */
static void
set_noname_tlist_references(Noname *noname)
{
	Plan	   *source = ((Plan *) noname)->lefttree;

	if (source != NULL)
	{
		set_tlist_references(source);
		((Plan *) noname)->targetlist = copy_vars(((Plan *) noname)->targetlist,
												  (source)->targetlist);
	}
	else
		elog(ERROR, "calling set_noname_tlist_references with empty lefttree");
}

/*
 * 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_clause_joinvar_refs.
 *
 * 'clauses' is the 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
 *
 * Returns the new join clauses.  The original clause structure is
 * not modified.
 *
 */
List *
join_references(List *clauses,
				List *outer_tlist,
				List *inner_tlist)
{
	return (List *) replace_clause_joinvar_refs((Node *) clauses,
												outer_tlist,
												inner_tlist);
}

/*
 * index_outerjoin_references
 *	  Given a list of join clauses, replace the operand corresponding to the
 *	  outer relation in the join with references to the corresponding target
 *	  list element in 'outer_tlist' (the outer is rather obscurely
 *	  identified as the side that doesn't contain a var whose varno equals
 *	  'inner_relid').
 *
 *	  As a side effect, the operator is replaced by the regproc id.
 *
 * 'inner_indxqual' is the list of join clauses (so-called because they
 * are used as qualifications for the inner (inbex) scan of a nestloop)
 *
 * Returns the new list of clauses.
 *
 */
List *
index_outerjoin_references(List *inner_indxqual,
						   List *outer_tlist,
						   Index inner_relid)
{
	List	   *t_list = NIL;
	Expr	   *temp = NULL;
	List	   *t_clause = NIL;
	Expr	   *clause = NULL;

	foreach(t_clause, inner_indxqual)
	{
		clause = lfirst(t_clause);

		/*
		 * if inner scan on the right.
		 */
		if (OperandIsInner((Node *) get_rightop(clause), inner_relid))
		{
			Var		   *joinvar = (Var *)
			replace_clause_joinvar_refs((Node *) get_leftop(clause),
										outer_tlist,
										NIL);

			temp = make_opclause(replace_opid((Oper *) ((Expr *) clause)->oper),
								 joinvar,
								 get_rightop(clause));
			t_list = lappend(t_list, temp);
		}
		else
		{
			/* inner scan on left */
			Var		   *joinvar = (Var *)
			replace_clause_joinvar_refs((Node *) get_rightop(clause),
										outer_tlist,
										NIL);

			temp = make_opclause(replace_opid((Oper *) ((Expr *) clause)->oper),
								 get_leftop(clause),
								 joinvar);
			t_list = lappend(t_list, temp);
		}

	}
	return t_list;
}

/*
 * replace_clause_joinvar_refs
 * 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.
 *
 * '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
 *
 * Returns the new join clause.
 * NB: it is critical that the original clause structure not be modified!
 * The changes must be applied to a copy.
 *
 * XXX the current implementation does not copy unchanged primitive
 * nodes; they remain shared with the original.  Is this safe?
 */
static Node *
replace_clause_joinvar_refs(Node *clause,
							List *outer_tlist,
							List *inner_tlist)
{
	if (clause == NULL)
		return NULL;
	if (IsA(clause, Var))
	{
		Var		   *temp = replace_joinvar_refs((Var *) clause,
												outer_tlist, inner_tlist);

		if (temp != NULL)
			return (Node *) temp;
		else
			return clause;
	}
	else if (single_node(clause))
		return clause;
	else if (and_clause(clause))
	{
		return (Node *) make_andclause((List *)
			replace_clause_joinvar_refs((Node *) ((Expr *) clause)->args,
										outer_tlist,
										inner_tlist));
	}
	else if (or_clause(clause))
	{
		return (Node *) make_orclause((List *)
			replace_clause_joinvar_refs((Node *) ((Expr *) clause)->args,
										outer_tlist,
										inner_tlist));
	}
	else if (IsA(clause, ArrayRef))
	{
		ArrayRef   *oldnode = (ArrayRef *) clause;
		ArrayRef   *newnode = makeNode(ArrayRef);

		newnode->refattrlength = oldnode->refattrlength;
		newnode->refelemlength = oldnode->refelemlength;
		newnode->refelemtype = oldnode->refelemtype;
		newnode->refelembyval = oldnode->refelembyval;
		newnode->refupperindexpr = (List *)
			replace_clause_joinvar_refs((Node *) oldnode->refupperindexpr,
										outer_tlist,
										inner_tlist);
		newnode->reflowerindexpr = (List *)
			replace_clause_joinvar_refs((Node *) oldnode->reflowerindexpr,
										outer_tlist,
										inner_tlist);
		newnode->refexpr =
			replace_clause_joinvar_refs(oldnode->refexpr,
										outer_tlist,
										inner_tlist);
		newnode->refassgnexpr =
			replace_clause_joinvar_refs(oldnode->refassgnexpr,
										outer_tlist,
										inner_tlist);

		return (Node *) newnode;
	}
	else if (is_funcclause(clause))
	{
		return (Node *) make_funcclause(
										(Func *) ((Expr *) clause)->oper,
									(List *) replace_clause_joinvar_refs(
										(Node *) ((Expr *) clause)->args,
															 outer_tlist,
														   inner_tlist));
	}
	else if (not_clause(clause))
	{
		return (Node *) make_notclause((Expr *)
									   replace_clause_joinvar_refs(
							  (Node *) get_notclausearg((Expr *) clause),
															 outer_tlist,
														   inner_tlist));
	}
	else if (is_opclause(clause))
	{
		return (Node *) make_opclause(
						  replace_opid((Oper *) ((Expr *) clause)->oper),
									  (Var *) replace_clause_joinvar_refs(
									(Node *) get_leftop((Expr *) clause),
															 outer_tlist,
															inner_tlist),
									  (Var *) replace_clause_joinvar_refs(
								   (Node *) get_rightop((Expr *) clause),
															 outer_tlist,
														   inner_tlist));
	}
	else if (IsA(clause, List))
	{
		List	   *t_list = NIL;
		List	   *subclause;

		foreach(subclause, (List *) clause)
		{
			t_list = lappend(t_list,
						   replace_clause_joinvar_refs(lfirst(subclause),
													   outer_tlist,
													   inner_tlist));
		}
		return (Node *) t_list;
	}
	else if (is_subplan(clause))
	{
		/* This is a tad wasteful of space, but it works... */
		Expr	   *newclause = (Expr *) copyObject(clause);

		newclause->args = (List *)
			replace_clause_joinvar_refs((Node *) newclause->args,
										outer_tlist,
										inner_tlist);
		((SubPlan *) newclause->oper)->sublink->oper = (List *)
			replace_clause_joinvar_refs(
				   (Node *) ((SubPlan *) newclause->oper)->sublink->oper,
										outer_tlist,
										inner_tlist);
		return (Node *) newclause;
	}
	else if (IsA(clause, CaseExpr))
	{
		CaseExpr   *oldnode = (CaseExpr *) clause;
		CaseExpr   *newnode = makeNode(CaseExpr);

		newnode->casetype = oldnode->casetype;
		newnode->arg = oldnode->arg;	/* XXX should always be null
										 * anyway ... */
		newnode->args = (List *)
			replace_clause_joinvar_refs((Node *) oldnode->args,
										outer_tlist,
										inner_tlist);
		newnode->defresult =
			replace_clause_joinvar_refs(oldnode->defresult,
										outer_tlist,
										inner_tlist);

		return (Node *) newnode;
	}
	else if (IsA(clause, CaseWhen))
	{
		CaseWhen   *oldnode = (CaseWhen *) clause;
		CaseWhen   *newnode = makeNode(CaseWhen);

		newnode->expr =
			replace_clause_joinvar_refs(oldnode->expr,
										outer_tlist,
										inner_tlist);
		newnode->result =
			replace_clause_joinvar_refs(oldnode->result,
										outer_tlist,
										inner_tlist);

		return (Node *) newnode;
	}
	else
	{
		elog(ERROR, "replace_clause_joinvar_refs: unsupported clause %d",
			 nodeTag(clause));
		return NULL;
	}
}

static Var *
replace_joinvar_refs(Var *var, List *outer_tlist, List *inner_tlist)
{
	Resdom	   *outer_resdom;

	outer_resdom = tlist_member(var, outer_tlist);

	if (outer_resdom != NULL && IsA(outer_resdom, Resdom))
	{
		return (makeVar(OUTER,
						outer_resdom->resno,
						var->vartype,
						var->vartypmod,
						0,
						var->varnoold,
						var->varoattno));
	}
	else
	{
		Resdom	   *inner_resdom;

		inner_resdom = tlist_member(var, inner_tlist);
		if (inner_resdom != NULL && IsA(inner_resdom, Resdom))
		{
			return (makeVar(INNER,
							inner_resdom->resno,
							var->vartype,
							var->vartypmod,
							0,
							var->varnoold,
							var->varoattno));
		}
	}
	return (Var *) NULL;
}

/*
 * 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;
	TargetEntry *noname = (TargetEntry *) NULL;
	TargetEntry *xtl = NULL;
	List	   *entry;

	foreach(entry, tlist)
	{
		AttrNumber	oattno;

		xtl = lfirst(entry);
		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);
	}
	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.
	 */
	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)
	{
		TargetEntry *entry = (TargetEntry *) lfirst(t);

		replace_vars_with_subplan_refs((Node *) get_expr(entry),
									   subvarno, subplanTargetList);
	}
}

/*---------------------------------------------------------
 *
 * replace_vars_with_subplan_refs
 *
 * 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.
 *
 * 'clause': the tree to be fixed
 * '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.
 */
static void
replace_vars_with_subplan_refs(Node *clause,
							   Index subvarno,
							   List *subplanTargetList)
{
	List	   *t;

	if (clause == NULL)
		return;
	if (IsA(clause, Var))
	{

		/*
		 * Ha! A Var node!
		 *
		 * It could be that this varnode has been created by make_groupplan
		 * and is already set up to reference the subplan target list. We
		 * recognize that case by varno = 1, varnoold = -1, varattno =
		 * varoattno, and varlevelsup = 0.	(Probably ought to have an
		 * explicit flag, but this should do for now.)
		 */
		Var		   *var = (Var *) clause;
		TargetEntry *subplanVar;

		if (var->varno == (Index) 1 &&
			var->varnoold == ((Index) -1) &&
			var->varattno == var->varoattno &&
			var->varlevelsup == 0)
			return;				/* OK to leave it alone */

		/* Otherwise it had better be in the subplan list. */
		subplanVar = match_varid(var, subplanTargetList);
		if (!subplanVar)
			elog(ERROR, "replace_vars_with_subplan_refs: variable not in target list");

		/*
		 * Change the varno & varattno fields of the var node.
		 */
		var->varno = subvarno;
		var->varattno = subplanVar->resdom->resno;
	}
	else if (single_node(clause))
	{
		/* do nothing! */
	}
	else if (IsA(clause, Iter))
		replace_vars_with_subplan_refs(((Iter *) clause)->iterexpr,
									   subvarno, subplanTargetList);
	else if (is_subplan(clause))
	{
		foreach(t, ((Expr *) clause)->args)
			replace_vars_with_subplan_refs(lfirst(t),
										   subvarno, subplanTargetList);
		foreach(t, ((SubPlan *) ((Expr *) clause)->oper)->sublink->oper)
			replace_vars_with_subplan_refs(lfirst(((Expr *) lfirst(t))->args),
										   subvarno, subplanTargetList);
	}
	else if (IsA(clause, Expr))
	{

		/*
		 * Recursively scan the arguments of an expression. NOTE: this
		 * must come after is_subplan() case since subplan is a kind of
		 * Expr node.
		 */
		foreach(t, ((Expr *) clause)->args)
			replace_vars_with_subplan_refs(lfirst(t),
										   subvarno, subplanTargetList);
	}
	else if (IsA(clause, Aggref))
		replace_vars_with_subplan_refs(((Aggref *) clause)->target,
									   subvarno, subplanTargetList);
	else if (IsA(clause, ArrayRef))
	{
		ArrayRef   *aref = (ArrayRef *) clause;

		foreach(t, aref->refupperindexpr)
			replace_vars_with_subplan_refs(lfirst(t),
										   subvarno, subplanTargetList);
		foreach(t, aref->reflowerindexpr)
			replace_vars_with_subplan_refs(lfirst(t),
										   subvarno, subplanTargetList);
		replace_vars_with_subplan_refs(aref->refexpr,
									   subvarno, subplanTargetList);
		replace_vars_with_subplan_refs(aref->refassgnexpr,
									   subvarno, subplanTargetList);
	}
	else if (case_clause(clause))
	{
		foreach(t, ((CaseExpr *) clause)->args)
		{
			CaseWhen   *when = (CaseWhen *) lfirst(t);

			replace_vars_with_subplan_refs(when->expr,
										   subvarno, subplanTargetList);
			replace_vars_with_subplan_refs(when->result,
										   subvarno, subplanTargetList);
		}
		replace_vars_with_subplan_refs(((CaseExpr *) clause)->defresult,
									   subvarno, subplanTargetList);
	}
	else
	{
		elog(ERROR, "replace_vars_with_subplan_refs: Cannot handle node type %d",
			 nodeTag(clause));
	}
}

static bool
OperandIsInner(Node *opnd, int inner_relid)
{

	/*
	 * Can be the inner scan if its a varnode or a function and the
	 * inner_relid is equal to the varnode's var number or in the case of
	 * a function the first argument's var number (all args in a
	 * functional index are from the same relation).
	 */
	if (IsA(opnd, Var) &&
		(inner_relid == ((Var *) opnd)->varno))
		return true;
	if (is_funcclause(opnd))
	{
		List	   *firstArg = lfirst(((Expr *) opnd)->args);

		if (IsA(firstArg, Var) &&
			(inner_relid == ((Var *) firstArg)->varno))
			return true;
	}
	return false;
}

/*****************************************************************************
 *
 *****************************************************************************/

/*---------------------------------------------------------
 *
 * 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.
 *	* Generate the aggNode->aggs list of Aggref nodes contained in the Agg.
 *
 * 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;
	aggNode->aggs = NIL;

	foreach(tl, aggNode->plan.targetlist)
	{
		TargetEntry *tle = lfirst(tl);

		replace_vars_with_subplan_refs(tle->expr,
									   (Index) 0,
									   subplanTargetList);
		aggNode->aggs = nconc(pull_agg_clause(tle->expr), aggNode->aggs);
	}

	all_quals_ok = true;
	foreach(ql, aggNode->plan.qual)
	{
		Node	   *qual = lfirst(ql);
		List	   *qualaggs;

		replace_vars_with_subplan_refs(qual,
									   (Index) 0,
									   subplanTargetList);
		qualaggs = pull_agg_clause(qual);
		if (qualaggs == NIL)
			all_quals_ok = false;		/* this qual clause has no agg
										 * functions! */
		else
			aggNode->aggs = nconc(qualaggs, aggNode->aggs);
	}

	return all_quals_ok;
}

/*
 * Make a list of all Aggref nodes contained in the given expression.
 */
static List *
pull_agg_clause(Node *clause)
{
	List	   *agg_list = NIL;
	List	   *t;

	if (clause == NULL)
		return NIL;
	else if (single_node(clause))
		return NIL;
	else if (IsA(clause, Iter))
		return pull_agg_clause(((Iter *) clause)->iterexpr);
	else if (is_subplan(clause))
	{
		SubLink    *sublink = ((SubPlan *) ((Expr *) clause)->oper)->sublink;

		/*
		 * Only the lefthand side of the sublink should be checked for
		 * aggregates to be attached to the aggs list
		 */
		foreach(t, sublink->lefthand)
			agg_list = nconc(pull_agg_clause(lfirst(t)), agg_list);
		/* The first argument of ...->oper has also to be checked */
		foreach(t, sublink->oper)
			agg_list = nconc(pull_agg_clause(lfirst(t)), agg_list);
	}
	else if (IsA(clause, Expr))
	{

		/*
		 * Recursively scan the arguments of an expression. NOTE: this
		 * must come after is_subplan() case since subplan is a kind of
		 * Expr node.
		 */
		foreach(t, ((Expr *) clause)->args)
			agg_list = nconc(pull_agg_clause(lfirst(t)), agg_list);
	}
	else if (IsA(clause, Aggref))
	{
		return lcons(clause,
					 pull_agg_clause(((Aggref *) clause)->target));
	}
	else if (IsA(clause, ArrayRef))
	{
		ArrayRef   *aref = (ArrayRef *) clause;

		foreach(t, aref->refupperindexpr)
			agg_list = nconc(pull_agg_clause(lfirst(t)), agg_list);
		foreach(t, aref->reflowerindexpr)
			agg_list = nconc(pull_agg_clause(lfirst(t)), agg_list);
		agg_list = nconc(pull_agg_clause(aref->refexpr), agg_list);
		agg_list = nconc(pull_agg_clause(aref->refassgnexpr), agg_list);
	}
	else if (case_clause(clause))
	{
		foreach(t, ((CaseExpr *) clause)->args)
		{
			CaseWhen   *when = (CaseWhen *) lfirst(t);

			agg_list = nconc(agg_list, pull_agg_clause(when->expr));
			agg_list = nconc(agg_list, pull_agg_clause(when->result));
		}
		agg_list = nconc(pull_agg_clause(((CaseExpr *) clause)->defresult),
						 agg_list);
	}
	else
	{
		elog(ERROR, "pull_agg_clause: Cannot handle node type %d",
			 nodeTag(clause));
	}

	return agg_list;
}


/*
 * 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.
 *
 * NOTE: the havingClause has been cnf-ified, so AND subclauses have been
 * turned into a plain List.  Thus, this routine has to cope with List nodes
 * where the routine above does not...
 */

void
check_having_for_ungrouped_vars(Node *clause, List *groupClause,
								List *targetList)
{
	List	   *t;

	if (clause == NULL)
		return;

	if (IsA(clause, Var))
	{

		/*
		 * Ignore vars elsewhere in the having clause, since the parser
		 * already checked 'em.
		 */
	}
	else if (single_node(clause))
	{
		/* ignore */
	}
	else if (IsA(clause, Iter))
	{
		check_having_for_ungrouped_vars(((Iter *) clause)->iterexpr,
										groupClause, targetList);
	}
	else if (is_subplan(clause))
	{

		/*
		 * The args list of the subplan node represents attributes from
		 * outside passed into the sublink.
		 */
		foreach(t, ((Expr *) clause)->args)
		{
			bool		contained_in_group_clause = false;
			List	   *gl;

			foreach(gl, groupClause)
			{
				if (var_equal(lfirst(t),
							  get_groupclause_expr((GroupClause *)
												lfirst(gl), targetList)))
				{
					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");
		}
	}
	else if (IsA(clause, Expr))
	{

		/*
		 * Recursively scan the arguments of an expression. NOTE: this
		 * must come after is_subplan() case since subplan is a kind of
		 * Expr node.
		 */
		foreach(t, ((Expr *) clause)->args)
			check_having_for_ungrouped_vars(lfirst(t), groupClause,
											targetList);
	}
	else if (IsA(clause, List))
	{

		/*
		 * Recursively scan AND subclauses (see NOTE above).
		 */
		foreach(t, ((List *) clause))
			check_having_for_ungrouped_vars(lfirst(t), groupClause,
											targetList);
	}
	else if (IsA(clause, Aggref))
	{
		check_having_for_ungrouped_vars(((Aggref *) clause)->target,
										groupClause, targetList);
	}
	else if (IsA(clause, ArrayRef))
	{
		ArrayRef   *aref = (ArrayRef *) clause;

		/*
		 * This is an arrayref. Recursively call this routine for its
		 * expression and its index expression...
		 */
		foreach(t, aref->refupperindexpr)
			check_having_for_ungrouped_vars(lfirst(t), groupClause,
											targetList);
		foreach(t, aref->reflowerindexpr)
			check_having_for_ungrouped_vars(lfirst(t), groupClause,
											targetList);
		check_having_for_ungrouped_vars(aref->refexpr, groupClause,
										targetList);
		check_having_for_ungrouped_vars(aref->refassgnexpr, groupClause,
										targetList);
	}
	else if (case_clause(clause))
	{
		foreach(t, ((CaseExpr *) clause)->args)
		{
			CaseWhen   *when = (CaseWhen *) lfirst(t);

			check_having_for_ungrouped_vars(when->expr, groupClause,
											targetList);
			check_having_for_ungrouped_vars(when->result, groupClause,
											targetList);
		}
		check_having_for_ungrouped_vars(((CaseExpr *) clause)->defresult,
										groupClause, targetList);
	}
	else
	{
		elog(ERROR, "check_having_for_ungrouped_vars: Cannot handle node type %d",
			 nodeTag(clause));
	}
}
