1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-17 17:02:08 +03:00

Support window functions a la SQL:2008.

Hitoshi Harada, with some kibitzing from Heikki and Tom.
This commit is contained in:
Tom Lane
2008-12-28 18:54:01 +00:00
parent 38e9348282
commit 95b07bc7f5
92 changed files with 6720 additions and 321 deletions

View File

@ -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 -