7
7
*
8
8
*
9
9
* IDENTIFICATION
10
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.29 1999/10/07 04:23:12 tgl Exp $
10
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.30 1999/12/09 05:58:54 tgl Exp $
11
11
*
12
12
*-------------------------------------------------------------------------
13
13
*/
19
19
#include "parser/parse_agg.h"
20
20
#include "parser/parse_coerce.h"
21
21
#include "parser/parse_expr.h"
22
+ #include "parser/parsetree.h"
22
23
#include "utils/lsyscache.h"
23
24
#include "utils/syscache.h"
24
25
26
+ typedef struct {
27
+ ParseState * pstate ;
28
+ List * groupClauses ;
29
+ }check_ungrouped_columns_context ;
30
+
25
31
static bool contain_agg_clause (Node * clause );
26
32
static bool contain_agg_clause_walker (Node * node ,void * context );
27
- static bool exprIsAggOrGroupCol (Node * expr ,List * groupClauses );
28
- static bool exprIsAggOrGroupCol_walker (Node * node ,List * groupClauses );
33
+ static void check_ungrouped_columns (Node * node ,ParseState * pstate ,
34
+ List * groupClauses );
35
+ static bool check_ungrouped_columns_walker (Node * node ,
36
+ check_ungrouped_columns_context * context );
29
37
30
38
/*
31
39
* contain_agg_clause
@@ -53,9 +61,11 @@ contain_agg_clause_walker(Node *node, void *context)
53
61
}
54
62
55
63
/*
56
- * exprIsAggOrGroupCol -
57
- * returns true if the expression does not contain non-group columns,
58
- * other than within the arguments of aggregate functions.
64
+ * check_ungrouped_columns -
65
+ * Scan the given expression tree for ungrouped variables (variables
66
+ * that are not listed in the groupClauses list and are not within
67
+ * the arguments of aggregate functions). Emit a suitable error message
68
+ * if any are found.
59
69
*
60
70
* NOTE: we assume that the given clause has been transformed suitably for
61
71
* parser output. This means we can use the planner's expression_tree_walker.
@@ -68,50 +78,70 @@ contain_agg_clause_walker(Node *node, void *context)
68
78
* inside the subquery and converted them into a list of parameters for the
69
79
* subquery.
70
80
*/
71
- static bool
72
- exprIsAggOrGroupCol (Node * expr ,List * groupClauses )
81
+ static void
82
+ check_ungrouped_columns (Node * node ,ParseState * pstate ,
83
+ List * groupClauses )
73
84
{
74
- /* My walker returns TRUE if it finds a subexpression that is NOT
75
- * acceptable (since we can abort the recursion at that point).
76
- * So, invert its result.
77
- */
78
- return ! exprIsAggOrGroupCol_walker ( expr , groupClauses );
85
+ check_ungrouped_columns_context context ;
86
+
87
+ context . pstate = pstate ;
88
+ context . groupClauses = groupClauses ;
89
+ check_ungrouped_columns_walker ( node , & context );
79
90
}
80
91
81
92
static bool
82
- exprIsAggOrGroupCol_walker (Node * node ,List * groupClauses )
93
+ check_ungrouped_columns_walker (Node * node ,
94
+ check_ungrouped_columns_context * context )
83
95
{
84
96
List * gl ;
85
97
86
98
if (node == NULL )
87
99
return false;
88
- if (IsA (node ,Aggref ))
89
- return false;/* OK; do not examine argument of aggregate */
90
100
if (IsA (node ,Const )|| IsA (node ,Param ))
91
101
return false;/* constants are always acceptable */
92
- /* Now check to see if expression as a whole matches any GROUP BY item.
102
+ /*
103
+ * If we find an aggregate function, do not recurse into its arguments.
104
+ */
105
+ if (IsA (node ,Aggref ))
106
+ return false;
107
+ /*
108
+ * Check to see if subexpression as a whole matches any GROUP BY item.
93
109
* We need to do this at every recursion level so that we recognize
94
- * GROUPed-BY expressions.
110
+ * GROUPed-BY expressions before reaching variables within them .
95
111
*/
96
- foreach (gl ,groupClauses )
112
+ foreach (gl ,context -> groupClauses )
97
113
{
98
114
if (equal (node ,lfirst (gl )))
99
115
return false;/* acceptable, do not descend more */
100
116
}
101
- /* If we have an ungrouped Var, we have a failure --- unless it is an
117
+ /*
118
+ * If we have an ungrouped Var, we have a failure --- unless it is an
102
119
* outer-level Var. In that case it's a constant as far as this query
103
120
* level is concerned, and we can accept it. (If it's ungrouped as far
104
121
* as the upper query is concerned, that's someone else's problem...)
105
122
*/
106
123
if (IsA (node ,Var ))
107
124
{
108
- if (((Var * )node )-> varlevelsup == 0 )
109
- return true;/* found an ungrouped local variable */
110
- return false;/* outer-level Var is acceptable */
125
+ Var * var = (Var * )node ;
126
+ RangeTblEntry * rte ;
127
+ char * attname ;
128
+
129
+ if (var -> varlevelsup > 0 )
130
+ return false;/* outer-level Var is acceptable */
131
+ /* Found an ungrouped local variable; generate error message */
132
+ Assert (var -> varno > 0 &&
133
+ var -> varno <=length (context -> pstate -> p_rtable ));
134
+ rte = rt_fetch (var -> varno ,context -> pstate -> p_rtable );
135
+ attname = get_attname (rte -> relid ,var -> varattno );
136
+ if (!attname )
137
+ elog (ERROR ,"cache lookup of attribute %d in relation %u failed" ,
138
+ var -> varattno ,rte -> relid );
139
+ elog (ERROR ,"Attribute %s.%s must be GROUPed or used in an aggregate function" ,
140
+ rte -> refname ,attname );
111
141
}
112
142
/* Otherwise, recurse. */
113
- return expression_tree_walker (node ,exprIsAggOrGroupCol_walker ,
114
- (void * )groupClauses );
143
+ return expression_tree_walker (node ,check_ungrouped_columns_walker ,
144
+ (void * )context );
115
145
}
116
146
117
147
/*
@@ -135,9 +165,9 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
135
165
/*
136
166
* Aggregates must never appear in WHERE clauses. (Note this check
137
167
* should appear first to deliver an appropriate error message;
138
- * otherwise we are likely togenerate the generic "illegal use of
139
- *aggregates in target list" message , which is outright misleading if
140
- *the problem is in WHERE.)
168
+ * otherwise we are likely tocomplain about some innocent variable
169
+ * inthe target list, which is outright misleading if the problem
170
+ * is in WHERE.)
141
171
*/
142
172
if (contain_agg_clause (qry -> qual ))
143
173
elog (ERROR ,"Aggregates not allowed in WHERE clause" );
@@ -146,8 +176,8 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
146
176
* No aggregates allowed in GROUP BY clauses, either.
147
177
*
148
178
* While we are at it, build a list of the acceptable GROUP BY expressions
149
- * for use byexprIsAggOrGroupCol () (this avoids repeated scans of the
150
- * targetlist within the recursiveroutines ...)
179
+ * for use bycheck_ungrouped_columns () (this avoids repeated scans of the
180
+ * targetlist within the recursiveroutine ...)
151
181
*/
152
182
foreach (tl ,qry -> groupClause )
153
183
{
@@ -161,26 +191,10 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
161
191
}
162
192
163
193
/*
164
- * The expression specified in the HAVING clause can only contain
165
- * aggregates, group columns and functions thereof. As with WHERE,
166
- * we want to point the finger at HAVING before the target list.
194
+ * Check the targetlist and HAVING clause for ungrouped variables.
167
195
*/
168
- if (!exprIsAggOrGroupCol (qry -> havingQual ,groupClauses ))
169
- elog (ERROR ,
170
- "Illegal use of aggregates or non-group column in HAVING clause" );
171
-
172
- /*
173
- * The target list can only contain aggregates, group columns and
174
- * functions thereof.
175
- */
176
- foreach (tl ,qry -> targetList )
177
- {
178
- TargetEntry * tle = lfirst (tl );
179
-
180
- if (!exprIsAggOrGroupCol (tle -> expr ,groupClauses ))
181
- elog (ERROR ,
182
- "Illegal use of aggregates or non-group column in target list" );
183
- }
196
+ check_ungrouped_columns ((Node * )qry -> targetList ,pstate ,groupClauses );
197
+ check_ungrouped_columns ((Node * )qry -> havingQual ,pstate ,groupClauses );
184
198
185
199
/* Release the list storage (but not the pointed-to expressions!) */
186
200
freeList (groupClauses );