diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 12 | ||||
-rw-r--r-- | src/backend/parser/parse_agg.c | 250 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 5 | ||||
-rw-r--r-- | src/backend/parser/parse_func.c | 8 |
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; |