aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_agg.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2008-12-28 18:54:01 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2008-12-28 18:54:01 +0000
commit95b07bc7f5010233f52f9d11da74e2e5b653b0a7 (patch)
tree48f5858bf4eca1bfb316ef02bb959ca85f568e0a /src/backend/parser/parse_agg.c
parent38e9348282e9d078487147ba8a85aebec54e3a08 (diff)
downloadpostgresql-95b07bc7f5010233f52f9d11da74e2e5b653b0a7.tar.gz
postgresql-95b07bc7f5010233f52f9d11da74e2e5b653b0a7.zip
Support window functions a la SQL:2008.
Hitoshi Harada, with some kibitzing from Heikki and Tom.
Diffstat (limited to 'src/backend/parser/parse_agg.c')
-rw-r--r--src/backend/parser/parse_agg.c198
1 files changed, 194 insertions, 4 deletions
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index e2645462d57..6dba470e39f 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -1,14 +1,14 @@
/*-------------------------------------------------------------------------
*
* parse_agg.c
- * handle aggregates in parser
+ * handle aggregates and window functions in parser
*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.84 2008/10/04 21:56:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.85 2008/12/28 18:53:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -67,7 +67,8 @@ transformAggregateCall(ParseState *pstate, Aggref *agg)
*/
if (min_varlevel == 0)
{
- if (checkExprHasAggs((Node *) agg->args))
+ if (pstate->p_hasAggs &&
+ checkExprHasAggs((Node *) agg->args))
ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR),
errmsg("aggregate function calls cannot be nested"),
@@ -75,6 +76,15 @@ transformAggregateCall(ParseState *pstate, Aggref *agg)
locate_agg_of_level((Node *) agg->args, 0))));
}
+ /* It can't contain window functions either */
+ if (pstate->p_hasWindowFuncs &&
+ checkExprHasWindowFuncs((Node *) agg->args))
+ ereport(ERROR,
+ (errcode(ERRCODE_GROUPING_ERROR),
+ errmsg("aggregate function calls cannot contain window function calls"),
+ parser_errposition(pstate,
+ locate_windowfunc((Node *) agg->args))));
+
if (min_varlevel < 0)
min_varlevel = 0;
agg->agglevelsup = min_varlevel;
@@ -85,6 +95,98 @@ transformAggregateCall(ParseState *pstate, Aggref *agg)
pstate->p_hasAggs = true;
}
+/*
+ * transformWindowFuncCall -
+ * Finish initial transformation of a window function call
+ *
+ * parse_func.c has recognized the function as a window function, and has set
+ * up all the fields of the WindowFunc except winref. Here we must (1) add
+ * the WindowDef to the pstate (if not a duplicate of one already present) and
+ * set winref to link to it; and (2) mark p_hasWindowFuncs true in the pstate.
+ * Unlike aggregates, only the most closely nested pstate level need be
+ * considered --- there are no "outer window functions" per SQL spec.
+ */
+void
+transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
+ WindowDef *windef)
+{
+ /*
+ * A window function call can't contain another one (but aggs are OK).
+ * XXX is this required by spec, or just an unimplemented feature?
+ */
+ if (pstate->p_hasWindowFuncs &&
+ checkExprHasWindowFuncs((Node *) wfunc->args))
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("window function calls cannot be nested"),
+ parser_errposition(pstate,
+ locate_windowfunc((Node *) wfunc->args))));
+
+ /*
+ * If the OVER clause just specifies a reference name, find that
+ * WINDOW clause (which had better be present). Otherwise, try to
+ * match all the properties of the OVER clause, and make a new entry
+ * in the p_windowdefs list if no luck.
+ */
+ Assert(!windef->name);
+ if (windef->refname &&
+ windef->partitionClause == NIL &&
+ windef->orderClause == NIL)
+ {
+ Index winref = 0;
+ ListCell *lc;
+
+ foreach(lc, pstate->p_windowdefs)
+ {
+ WindowDef *refwin = (WindowDef *) lfirst(lc);
+
+ winref++;
+ if (refwin->name && strcmp(refwin->name, windef->refname) == 0)
+ {
+ wfunc->winref = winref;
+ break;
+ }
+ }
+ if (lc == NULL) /* didn't find it? */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("window \"%s\" does not exist", windef->refname),
+ parser_errposition(pstate, windef->location)));
+ }
+ else
+ {
+ Index winref = 0;
+ ListCell *lc;
+
+ foreach(lc, pstate->p_windowdefs)
+ {
+ WindowDef *refwin = (WindowDef *) lfirst(lc);
+
+ winref++;
+ if (refwin->refname && windef->refname &&
+ strcmp(refwin->name, windef->refname) == 0)
+ /* matched on refname */ ;
+ else if (!refwin->refname && !windef->refname)
+ /* matched, no refname */ ;
+ else
+ continue;
+ if (equal(refwin->partitionClause, windef->partitionClause) &&
+ equal(refwin->orderClause, windef->orderClause))
+ {
+ /* found a duplicate window specification */
+ wfunc->winref = winref;
+ break;
+ }
+ }
+ if (lc == NULL) /* didn't find it? */
+ {
+ pstate->p_windowdefs = lappend(pstate->p_windowdefs, windef);
+ wfunc->winref = list_length(pstate->p_windowdefs);
+ }
+ }
+
+ pstate->p_hasWindowFuncs = true;
+}
/*
* parseCheckAggregates
@@ -207,6 +309,11 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
/*
* Check the targetlist and HAVING clause for ungrouped variables.
+ *
+ * Note: because we check resjunk tlist elements as well as regular ones,
+ * this will also find ungrouped variables that came from ORDER BY and
+ * WINDOW clauses. For that matter, it's also going to examine the
+ * grouping expressions themselves --- but they'll all pass the test ...
*/
clause = (Node *) qry->targetList;
if (hasJoinRTEs)
@@ -226,11 +333,94 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
if (pstate->p_hasAggs && hasSelfRefRTEs)
ereport(ERROR,
(errcode(ERRCODE_INVALID_RECURSION),
- errmsg("aggregates not allowed in a recursive query's recursive term"),
+ errmsg("aggregate functions not allowed in a recursive query's recursive term"),
parser_errposition(pstate,
locate_agg_of_level((Node *) qry, 0))));
}
+/*
+ * parseCheckWindowFuncs
+ * Check for window functions where they shouldn't be.
+ *
+ * We have to forbid window functions in WHERE, JOIN/ON, HAVING, GROUP BY,
+ * and window specifications. (Other clauses, such as RETURNING and LIMIT,
+ * have already been checked.) Transformation of all these clauses must
+ * be completed already.
+ */
+void
+parseCheckWindowFuncs(ParseState *pstate, Query *qry)
+{
+ ListCell *l;
+
+ /* This should only be called if we found window functions */
+ Assert(pstate->p_hasWindowFuncs);
+
+ if (checkExprHasWindowFuncs(qry->jointree->quals))
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("window functions not allowed in WHERE clause"),
+ parser_errposition(pstate,
+ locate_windowfunc(qry->jointree->quals))));
+ if (checkExprHasWindowFuncs((Node *) qry->jointree->fromlist))
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("window functions not allowed in JOIN conditions"),
+ parser_errposition(pstate,
+ locate_windowfunc((Node *) qry->jointree->fromlist))));
+ if (checkExprHasWindowFuncs(qry->havingQual))
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("window functions not allowed in HAVING clause"),
+ parser_errposition(pstate,
+ locate_windowfunc(qry->havingQual))));
+
+ foreach(l, qry->groupClause)
+ {
+ SortGroupClause *grpcl = (SortGroupClause *) lfirst(l);
+ Node *expr;
+
+ expr = get_sortgroupclause_expr(grpcl, qry->targetList);
+ if (checkExprHasWindowFuncs(expr))
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("window functions not allowed in GROUP BY clause"),
+ parser_errposition(pstate,
+ locate_windowfunc(expr))));
+ }
+
+ foreach(l, qry->windowClause)
+ {
+ WindowClause *wc = (WindowClause *) lfirst(l);
+ ListCell *l2;
+
+ foreach(l2, wc->partitionClause)
+ {
+ SortGroupClause *grpcl = (SortGroupClause *) lfirst(l2);
+ Node *expr;
+
+ expr = get_sortgroupclause_expr(grpcl, qry->targetList);
+ if (checkExprHasWindowFuncs(expr))
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("window functions not allowed in window definition"),
+ parser_errposition(pstate,
+ locate_windowfunc(expr))));
+ }
+ foreach(l2, wc->orderClause)
+ {
+ SortGroupClause *grpcl = (SortGroupClause *) lfirst(l2);
+ Node *expr;
+
+ expr = get_sortgroupclause_expr(grpcl, qry->targetList);
+ if (checkExprHasWindowFuncs(expr))
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("window functions not allowed in window definition"),
+ parser_errposition(pstate,
+ locate_windowfunc(expr))));
+ }
+ }
+}
/*
* check_ungrouped_columns -