aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/analyze.c12
-rw-r--r--src/backend/parser/parse_agg.c250
-rw-r--r--src/backend/parser/parse_clause.c5
-rw-r--r--src/backend/parser/parse_func.c8
4 files changed, 165 insertions, 110 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 79b36caad75..9ac8132f08a 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.272 2003/05/28 16:03:56 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.273 2003/06/06 15:04:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1593,7 +1593,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
elog(ERROR, "Rule WHERE condition may not contain references to other relations");
/* aggregates not allowed (but subselects are okay) */
- if (contain_agg_clause(stmt->whereClause))
+ if (pstate->p_hasAggs)
elog(ERROR, "Rule WHERE condition may not contain aggregate functions");
/* save info about sublinks in where clause */
@@ -1808,7 +1808,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
- if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
+ if (pstate->p_hasAggs || qry->groupClause)
parseCheckAggregates(pstate, qry);
if (stmt->forUpdate != NIL)
@@ -2013,7 +2013,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
- if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
+ if (pstate->p_hasAggs || qry->groupClause)
parseCheckAggregates(pstate, qry);
if (forUpdate != NIL)
@@ -2536,9 +2536,9 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
expr = transformExpr(pstate, expr);
/* Cannot contain subselects or aggregates */
- if (contain_subplans(expr))
+ if (pstate->p_hasSubLinks)
elog(ERROR, "Cannot use subselects in EXECUTE parameters");
- if (contain_agg_clause(expr))
+ if (pstate->p_hasAggs)
elog(ERROR, "Cannot use aggregates in EXECUTE parameters");
given_type_id = exprType(expr);
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 92ea3b9bd9e..49d952bf8af 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.52 2003/04/03 18:04:09 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.53 2003/06/06 15:04:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,6 +19,7 @@
#include "optimizer/var.h"
#include "parser/parse_agg.h"
#include "parser/parsetree.h"
+#include "rewrite/rewriteManip.h"
typedef struct
@@ -34,6 +35,147 @@ static void check_ungrouped_columns(Node *node, ParseState *pstate,
static bool check_ungrouped_columns_walker(Node *node,
check_ungrouped_columns_context *context);
+
+/*
+ * transformAggregateCall -
+ * Finish initial transformation of an aggregate call
+ *
+ * parse_func.c has recognized the function as an aggregate, and has set
+ * up all the fields of the Aggref except agglevelsup. Here we must
+ * determine which query level the aggregate actually belongs to, set
+ * agglevelsup accordingly, and mark p_hasAggs true in the corresponding
+ * pstate level.
+ */
+void
+transformAggregateCall(ParseState *pstate, Aggref *agg)
+{
+ int min_varlevel;
+
+ /*
+ * The aggregate's level is the same as the level of the lowest-level
+ * variable or aggregate in its argument; or if it contains no variables
+ * at all, we presume it to be local.
+ */
+ min_varlevel = find_minimum_var_level((Node *) agg->target);
+
+ /*
+ * An aggregate can't directly contain another aggregate call of the
+ * same level (though outer aggs are okay). We can skip this check
+ * if we didn't find any local vars or aggs.
+ */
+ if (min_varlevel == 0)
+ {
+ if (checkExprHasAggs((Node *) agg->target))
+ elog(ERROR, "aggregate function calls may not be nested");
+ }
+
+ if (min_varlevel < 0)
+ min_varlevel = 0;
+ agg->agglevelsup = min_varlevel;
+
+ /* Mark the correct pstate as having aggregates */
+ while (min_varlevel-- > 0)
+ pstate = pstate->parentParseState;
+ pstate->p_hasAggs = true;
+}
+
+
+/*
+ * 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.
+ */
+void
+parseCheckAggregates(ParseState *pstate, Query *qry)
+{
+ List *groupClauses = NIL;
+ bool have_non_var_grouping = false;
+ List *lst;
+ bool hasJoinRTEs;
+ Node *clause;
+
+ /* This should only be called if we found aggregates or grouping */
+ Assert(pstate->p_hasAggs || qry->groupClause);
+
+ /*
+ * Aggregates must never appear in WHERE or JOIN/ON clauses.
+ *
+ * (Note this check should appear first to deliver an appropriate error
+ * message; otherwise we are likely to complain about some innocent
+ * variable in the target list, which is outright misleading if the
+ * problem is in WHERE.)
+ */
+ if (checkExprHasAggs(qry->jointree->quals))
+ elog(ERROR, "Aggregates not allowed in WHERE clause");
+ if (checkExprHasAggs((Node *) qry->jointree->fromlist))
+ elog(ERROR, "Aggregates not allowed in JOIN conditions");
+
+ /*
+ * 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 check_ungrouped_columns() (this avoids
+ * repeated scans of the targetlist within the recursive routine...).
+ * And detect whether any of the expressions aren't simple Vars.
+ */
+ foreach(lst, qry->groupClause)
+ {
+ GroupClause *grpcl = (GroupClause *) lfirst(lst);
+ Node *expr;
+
+ expr = get_sortgroupclause_expr(grpcl, qry->targetList);
+ if (expr == NULL)
+ continue; /* probably cannot happen */
+ if (checkExprHasAggs(expr))
+ elog(ERROR, "Aggregates not allowed in GROUP BY clause");
+ groupClauses = lcons(expr, groupClauses);
+ if (!IsA(expr, Var))
+ have_non_var_grouping = true;
+ }
+
+ /*
+ * If there are join alias vars involved, we have to flatten them
+ * to the underlying vars, so that aliased and unaliased vars will be
+ * correctly taken as equal. We can skip the expense of doing this
+ * if no rangetable entries are RTE_JOIN kind.
+ */
+ hasJoinRTEs = false;
+ foreach(lst, pstate->p_rtable)
+ {
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(lst);
+
+ if (rte->rtekind == RTE_JOIN)
+ {
+ hasJoinRTEs = true;
+ break;
+ }
+ }
+
+ if (hasJoinRTEs)
+ groupClauses = (List *) flatten_join_alias_vars(qry,
+ (Node *) groupClauses);
+
+ /*
+ * Check the targetlist and HAVING clause for ungrouped variables.
+ */
+ clause = (Node *) qry->targetList;
+ if (hasJoinRTEs)
+ clause = flatten_join_alias_vars(qry, clause);
+ check_ungrouped_columns(clause, pstate,
+ groupClauses, have_non_var_grouping);
+
+ clause = (Node *) qry->havingQual;
+ if (hasJoinRTEs)
+ clause = flatten_join_alias_vars(qry, clause);
+ check_ungrouped_columns(clause, pstate,
+ groupClauses, have_non_var_grouping);
+}
+
+
/*
* check_ungrouped_columns -
* Scan the given expression tree for ungrouped variables (variables
@@ -81,10 +223,15 @@ check_ungrouped_columns_walker(Node *node,
return false; /* constants are always acceptable */
/*
- * If we find an aggregate function, do not recurse into its
- * arguments; ungrouped vars in the arguments are not an error.
+ * If we find an aggregate call of the original level, do not recurse
+ * into its arguments; ungrouped vars in the arguments are not an error.
+ * We can also skip looking at the arguments of aggregates of higher
+ * levels, since they could not possibly contain Vars that are of concern
+ * to us (see transformAggregateCall). We do need to look into the
+ * arguments of aggregates of lower levels, however.
*/
- if (IsA(node, Aggref))
+ if (IsA(node, Aggref) &&
+ (int) ((Aggref *) node)->agglevelsup >= context->sublevels_up)
return false;
/*
@@ -165,98 +312,3 @@ check_ungrouped_columns_walker(Node *node,
return expression_tree_walker(node, check_ungrouped_columns_walker,
(void *) context);
}
-
-/*
- * 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.
- */
-void
-parseCheckAggregates(ParseState *pstate, Query *qry)
-{
- List *groupClauses = NIL;
- bool have_non_var_grouping = false;
- List *lst;
- bool hasJoinRTEs;
- Node *clause;
-
- /* This should only be called if we found aggregates, GROUP, or HAVING */
- Assert(pstate->p_hasAggs || qry->groupClause || qry->havingQual);
-
- /*
- * Aggregates must never appear in WHERE or JOIN/ON clauses.
- *
- * (Note this check should appear first to deliver an appropriate error
- * message; otherwise we are likely to complain about some innocent
- * variable in the target list, which is outright misleading if the
- * problem is in WHERE.)
- */
- if (contain_agg_clause(qry->jointree->quals))
- elog(ERROR, "Aggregates not allowed in WHERE clause");
- if (contain_agg_clause((Node *) qry->jointree->fromlist))
- elog(ERROR, "Aggregates not allowed in JOIN conditions");
-
- /*
- * 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 check_ungrouped_columns() (this avoids
- * repeated scans of the targetlist within the recursive routine...).
- * And detect whether any of the expressions aren't simple Vars.
- */
- foreach(lst, qry->groupClause)
- {
- GroupClause *grpcl = (GroupClause *) lfirst(lst);
- Node *expr;
-
- expr = get_sortgroupclause_expr(grpcl, qry->targetList);
- if (expr == NULL)
- continue; /* probably cannot happen */
- if (contain_agg_clause(expr))
- elog(ERROR, "Aggregates not allowed in GROUP BY clause");
- groupClauses = lcons(expr, groupClauses);
- if (!IsA(expr, Var))
- have_non_var_grouping = true;
- }
-
- /*
- * If there are join alias vars involved, we have to flatten them
- * to the underlying vars, so that aliased and unaliased vars will be
- * correctly taken as equal. We can skip the expense of doing this
- * if no rangetable entries are RTE_JOIN kind.
- */
- hasJoinRTEs = false;
- foreach(lst, pstate->p_rtable)
- {
- RangeTblEntry *rte = (RangeTblEntry *) lfirst(lst);
-
- if (rte->rtekind == RTE_JOIN)
- {
- hasJoinRTEs = true;
- break;
- }
- }
-
- if (hasJoinRTEs)
- groupClauses = (List *) flatten_join_alias_vars(qry,
- (Node *) groupClauses);
-
- /*
- * Check the targetlist and HAVING clause for ungrouped variables.
- */
- clause = (Node *) qry->targetList;
- if (hasJoinRTEs)
- clause = flatten_join_alias_vars(qry, clause);
- check_ungrouped_columns(clause, pstate,
- groupClauses, have_non_var_grouping);
-
- clause = (Node *) qry->havingQual;
- if (hasJoinRTEs)
- clause = flatten_join_alias_vars(qry, clause);
- check_ungrouped_columns(clause, pstate,
- groupClauses, have_non_var_grouping);
-}
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 1c8cb8bc0e3..a29eb007fb7 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.113 2003/04/29 22:13:10 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.114 2003/06/06 15:04:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -30,6 +30,7 @@
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_type.h"
+#include "rewrite/rewriteManip.h"
#include "utils/builtins.h"
#include "utils/guc.h"
@@ -494,7 +495,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
*/
if (pstate->p_hasAggs)
{
- if (contain_agg_clause(funcexpr))
+ if (checkExprHasAggs(funcexpr))
elog(ERROR, "cannot use aggregate function in FROM function expression");
}
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index e9a40e03179..6f858e17a8b 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.148 2003/05/26 00:11:27 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.149 2003/06/06 15:04:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,6 +20,7 @@
#include "catalog/pg_proc.h"
#include "lib/stringinfo.h"
#include "nodes/makefuncs.h"
+#include "parser/parse_agg.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
@@ -336,12 +337,13 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
aggref->aggstar = agg_star;
aggref->aggdistinct = agg_distinct;
+ /* parse_agg.c does additional aggregate-specific processing */
+ transformAggregateCall(pstate, aggref);
+
retval = (Node *) aggref;
if (retset)
elog(ERROR, "Aggregates may not return sets");
-
- pstate->p_hasAggs = true;
}
return retval;