parse_agg.c 10.3 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * parse_agg.c
4 5 6 7 8 9
 *	  handle aggregates in parser
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
10
 *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.22 1999/06/19 03:48:31 tgl Exp $
11 12 13 14 15 16 17 18 19 20
 *
 *-------------------------------------------------------------------------
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "postgres.h"
#include "access/heapam.h"
#include "catalog/pg_aggregate.h"
21
#include "catalog/pg_type.h"
22 23 24 25 26
#include "nodes/nodeFuncs.h"
#include "nodes/primnodes.h"
#include "nodes/relation.h"
#include "optimizer/clauses.h"
#include "parser/parse_agg.h"
27
#include "parser/parse_expr.h"
28 29
#include "parser/parse_node.h"
#include "parser/parse_target.h"
30
#include "parser/parse_coerce.h"
31
#include "utils/syscache.h"
32
#include "utils/lsyscache.h"
33

34
static bool contain_agg_clause(Node *clause);
35 36 37
static bool contain_agg_clause_walker(Node *node, void *context);
static bool exprIsAggOrGroupCol(Node *expr, List *groupClauses);
static bool exprIsAggOrGroupCol_walker(Node *node, List *groupClauses);
38

39
/*
40
 * contain_agg_clause
41
 *	  Recursively find aggref nodes within a clause.
42 43
 *
 *	  Returns true if any aggregate found.
44 45 46 47 48
 *
 * NOTE: we assume that the given clause has been transformed suitably for
 * parser output.  This means we can use the planner's expression_tree_walker,
 * except that we have to process SubLink nodes specially, since they haven't
 * been turned into SubPlan nodes yet.
49
 */
50
static bool
51 52
contain_agg_clause(Node *clause)
{
53 54
	return contain_agg_clause_walker(clause, NULL);
}
55

56 57 58 59 60 61 62 63
static bool
contain_agg_clause_walker(Node *node, void *context)
{
	if (node == NULL)
		return false;
	if (IsA(node, Aggref))
		return true;			/* abort the tree traversal and return true */
	if (IsA(node, SubLink))
64
	{
65 66 67
		/* Examine the lefthand side, but not the oper list nor the subquery */
		SubLink    *sublink = (SubLink *) node;
		return contain_agg_clause_walker((Node *) sublink->lefthand, context);
68
	}
69
	return expression_tree_walker(node, contain_agg_clause_walker, context);
70 71 72 73
}

/*
 * exprIsAggOrGroupCol -
74 75 76 77 78 79 80 81 82 83 84 85 86 87
 *	  returns true if the expression does not contain non-group columns,
 *	  other than within the arguments of aggregate functions.
 *
 * NOTE: we assume that the given clause has been transformed suitably for
 * parser output.  This means we can use the planner's expression_tree_walker,
 * except that we have to process SubLink nodes specially, since they haven't
 * been turned into SubPlan nodes yet.
 *
 * NOTE: in the case of a SubLink, we do not descend into the subquery.  This
 * means we will fail to detect ungrouped columns that appear as outer-level
 * variables within a subquery.  That seems unreasonably hard to handle here.
 * Instead, we expect the planner to check for ungrouped columns after it's
 * found all the outer-level references inside the subquery and converted
 * them into a list of parameters for the subquery.
88
 */
89
static bool
90
exprIsAggOrGroupCol(Node *expr, List *groupClauses)
91
{
92 93 94 95 96
	/* My walker returns TRUE if it finds a subexpression that is NOT
	 * acceptable (since we can abort the recursion at that point).
	 * So, invert its result.
	 */
	return ! exprIsAggOrGroupCol_walker(expr, groupClauses);
97 98
}

99
static bool
100
exprIsAggOrGroupCol_walker(Node *node, List *groupClauses)
101 102 103
{
	List	   *gl;

104 105 106 107 108 109 110 111 112 113 114
	if (node == NULL)
		return false;
	if (IsA(node, Aggref))
		return false;			/* OK; do not examine argument of aggregate */
	if (IsA(node, Const) || IsA(node, Param))
		return false;			/* constants are always acceptable */
	/* Now check to see if expression as a whole matches any GROUP BY item.
	 * We need to do this at every recursion level so that we recognize
	 * GROUPed-BY expressions.
	 */
	foreach(gl, groupClauses)
115
	{
116 117
		if (equal(node, lfirst(gl)))
			return false;		/* acceptable, do not descend more */
118
	}
119 120 121 122 123 124
	/* If we have an ungrouped Var, we have a failure --- unless it is an
	 * outer-level Var.  In that case it's a constant as far as this query
	 * level is concerned, and we can accept it.  (If it's ungrouped as far
	 * as the upper query is concerned, that's someone else's problem...)
	 */
	if (IsA(node, Var))
125
	{
126 127 128
		if (((Var *) node)->varlevelsup == 0)
			return true;		/* found an ungrouped local variable */
		return false;			/* outer-level Var is acceptable */
129
	}
130 131 132 133 134 135 136 137 138 139
	/* Otherwise, recurse. */
	if (IsA(node, SubLink))
	{
		/* Examine the lefthand side, but not the oper list nor the subquery */
		SubLink    *sublink = (SubLink *) node;
		return exprIsAggOrGroupCol_walker((Node *) sublink->lefthand,
										  groupClauses);
	}
	return expression_tree_walker(node, exprIsAggOrGroupCol_walker,
								  (void *) groupClauses);
140 141 142
}

/*
143 144 145 146 147 148 149
 * parseCheckAggregates
 *	Check for aggregates where they shouldn't be and improper grouping.
 *
 *	Ideally this should be done earlier, but it's difficult to distinguish
 *	aggregates from plain functions at the grammar level.  So instead we
 *	check here.  This function should be called after the target list and
 *	qualifications are finalized.
150 151 152 153
 */
void
parseCheckAggregates(ParseState *pstate, Query *qry)
{
154
	List	   *groupClauses = NIL;
155 156
	List	   *tl;

157 158
	/* This should only be called if we found aggregates or grouping */
	Assert(pstate->p_hasAggs || qry->groupClause);
159 160

	/*
B
Bruce Momjian 已提交
161 162 163 164 165
	 * Aggregates must never appear in WHERE clauses. (Note this check
	 * should appear first to deliver an appropriate error message;
	 * otherwise we are likely to generate the generic "illegal use of
	 * aggregates in target list" message, which is outright misleading if
	 * the problem is in WHERE.)
166 167
	 */
	if (contain_agg_clause(qry->qual))
168
		elog(ERROR, "Aggregates not allowed in WHERE clause");
169

170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
	/*
	 * No aggregates allowed in GROUP BY clauses, either.
	 *
	 * While we are at it, build a list of the acceptable GROUP BY expressions
	 * for use by exprIsAggOrGroupCol() (this avoids repeated scans of the
	 * targetlist within the recursive routines...)
	 */
	foreach(tl, qry->groupClause)
	{
		GroupClause *grpcl = lfirst(tl);
		Node		*expr;

		expr = (Node *) get_groupclause_expr(grpcl, qry->targetList);
		if (contain_agg_clause(expr))
			elog(ERROR, "Aggregates not allowed in GROUP BY clause");
		groupClauses = lcons(expr, groupClauses);
	}

188
	/*
189
	 * The target list can only contain aggregates, group columns and
190 191 192 193 194 195
	 * functions thereof.
	 */
	foreach(tl, qry->targetList)
	{
		TargetEntry *tle = lfirst(tl);

196
		if (!exprIsAggOrGroupCol(tle->expr, groupClauses))
197
			elog(ERROR,
198
				 "Illegal use of aggregates or non-group column in target list");
199 200 201
	}

	/*
202
	 * The expression specified in the HAVING clause has the same
203 204
	 * restriction as those in the target list.
	 */
205
	if (!exprIsAggOrGroupCol(qry->havingQual, groupClauses))
206
		elog(ERROR,
207 208 209 210
			 "Illegal use of aggregates or non-group column in HAVING clause");

	/* Release the list storage (but not the pointed-to expressions!) */
	freeList(groupClauses);
211 212 213
}


B
Bruce Momjian 已提交
214
Aggref *
215
ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
216
		 List *target, int precedence)
217 218 219 220 221
{
	Oid			fintype;
	Oid			vartype;
	Oid			xfn1;
	Form_pg_aggregate aggform;
B
Bruce Momjian 已提交
222
	Aggref	   *aggref;
223
	HeapTuple	theAggTuple;
224
	bool		usenulls = false;
225

226 227
	theAggTuple = SearchSysCacheTuple(AGGNAME,
									  PointerGetDatum(aggname),
228 229 230
									  ObjectIdGetDatum(basetype),
									  0, 0);
	if (!HeapTupleIsValid(theAggTuple))
231
		elog(ERROR, "Aggregate %s does not exist", aggname);
232

233
	/*
234
	 * We do a major hack for count(*) here.
235
	 *
236 237 238 239 240
	 * Count(*) poses several problems.  First, we need a field that is
	 * guaranteed to be in the range table, and unique.  Using a constant
	 * causes the optimizer to properly remove the aggragate from any
	 * elements of the query. Using just 'oid', which can not be null, in
	 * the parser fails on:
241
	 *
242 243
	 * select count(*) from tab1, tab2	   -- oid is not unique select
	 * count(*) from viewtable		-- views don't have real oids
244
	 *
245 246 247
	 * So, for an aggregate with parameter '*', we use the first valid range
	 * table entry, and pick the first column from the table. We set a
	 * flag to count nulls, because we could have nulls in that column.
B
Bruce Momjian 已提交
248
	 *
249
	 * It's an ugly job, but someone has to do it. bjm 1998/1/18
B
Bruce Momjian 已提交
250
	 */
251

252 253
	if (nodeTag(lfirst(target)) == T_Const)
	{
254 255
		Const	   *con = (Const *) lfirst(target);

256 257
		if (con->consttype == UNKNOWNOID && VARSIZE(con->constvalue) == VARHDRSZ)
		{
258 259 260
			Attr	   *attr = makeNode(Attr);
			List	   *rtable,
					   *rlist;
261 262 263 264 265 266 267 268
			RangeTblEntry *first_valid_rte;

			Assert(lnext(target) == NULL);

			if (pstate->p_is_rule)
				rtable = lnext(lnext(pstate->p_rtable));
			else
				rtable = pstate->p_rtable;
269

270 271 272 273
			first_valid_rte = NULL;
			foreach(rlist, rtable)
			{
				RangeTblEntry *rte = lfirst(rlist);
274

275 276 277 278
				/* only entries on outer(non-function?) scope */
				if (!rte->inFromCl && rte != pstate->p_target_rangetblentry)
					continue;

B
Bruce Momjian 已提交
279
				first_valid_rte = rte;
280 281 282
				break;
			}
			if (first_valid_rte == NULL)
283
				elog(ERROR, "Can't find column to do aggregate(*) on.");
284

285 286
			attr->relname = first_valid_rte->refname;
			attr->attrs = lcons(makeString(
287
						   get_attname(first_valid_rte->relid, 1)), NIL);
288 289 290 291 292

			lfirst(target) = transformExpr(pstate, (Node *) attr, precedence);
			usenulls = true;
		}
	}
293

294 295 296 297 298 299 300 301
	aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple);
	fintype = aggform->aggfinaltype;
	xfn1 = aggform->aggtransfn1;

	/* only aggregates with transfn1 need a base type */
	if (OidIsValid(xfn1))
	{
		basetype = aggform->aggbasetype;
302
		vartype = exprType(lfirst(target));
303
		if ((basetype != vartype)
B
Bruce Momjian 已提交
304
			&& (!IS_BINARY_COMPATIBLE(basetype, vartype)))
305 306 307 308 309 310
		{
			Type		tp1,
						tp2;

			tp1 = typeidType(basetype);
			tp2 = typeidType(vartype);
311
			elog(ERROR, "Aggregate type mismatch"
B
Bruce Momjian 已提交
312 313
				 "\n\t%s() works on %s, not on %s",
				 aggname, typeTypeName(tp1), typeTypeName(tp2));
314 315 316
		}
	}

B
Bruce Momjian 已提交
317 318 319 320
	aggref = makeNode(Aggref);
	aggref->aggname = pstrdup(aggname);
	aggref->basetype = aggform->aggbasetype;
	aggref->aggtype = fintype;
321

B
Bruce Momjian 已提交
322
	aggref->target = lfirst(target);
323
	if (usenulls)
B
Bruce Momjian 已提交
324
		aggref->usenulls = true;
325

326 327
	pstate->p_hasAggs = true;

B
Bruce Momjian 已提交
328
	return aggref;
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
}

/*
 * Error message when aggregate lookup fails that gives details of the
 * basetype
 */
void
agg_error(char *caller, char *aggname, Oid basetypeID)
{

	/*
	 * basetypeID that is Invalid (zero) means aggregate over all types.
	 * (count)
	 */

	if (basetypeID == InvalidOid)
345
		elog(ERROR, "%s: aggregate '%s' for all types does not exist", caller, aggname);
346 347
	else
	{
348
		elog(ERROR, "%s: aggregate '%s' for '%s' does not exist", caller, aggname,
349 350 351
			 typeidTypeName(basetypeID));
	}
}