mirror of
https://github.com/postgres/postgres.git
synced 2025-08-22 21:53:06 +03:00
Add some basic support for window frame clauses to the window-functions
patch. This includes the ability to force the frame to cover the whole partition, and the ability to make the frame end exactly on the current row rather than its last ORDER BY peer. Supporting any more of the full SQL frame-clause syntax will require nontrivial hacking on the window aggregate code, so it'll have to wait for 8.5 or beyond.
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.648 2008/12/28 18:53:58 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.649 2008/12/31 00:08:36 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -406,6 +406,7 @@ static TypeName *TableFuncTypeName(List *columns);
|
||||
%type <list> window_clause window_definition_list opt_partition_clause
|
||||
%type <windef> window_definition over_clause window_specification
|
||||
%type <str> opt_existing_window_name
|
||||
%type <ival> opt_frame_clause frame_extent frame_bound
|
||||
|
||||
|
||||
/*
|
||||
@@ -439,7 +440,7 @@ static TypeName *TableFuncTypeName(List *columns);
|
||||
EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT
|
||||
EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
|
||||
|
||||
FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
|
||||
FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD
|
||||
FREEZE FROM FULL FUNCTION
|
||||
|
||||
GLOBAL GRANT GRANTED GREATEST GROUP_P
|
||||
@@ -469,14 +470,14 @@ static TypeName *TableFuncTypeName(List *columns);
|
||||
ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
|
||||
|
||||
PARSER PARTIAL PARTITION PASSWORD PLACING PLANS POSITION
|
||||
PRECISION PRESERVE PREPARE PREPARED PRIMARY
|
||||
PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
|
||||
PRIOR PRIVILEGES PROCEDURAL PROCEDURE
|
||||
|
||||
QUOTE
|
||||
|
||||
READ REAL REASSIGN RECHECK RECURSIVE REFERENCES REINDEX RELATIVE_P RELEASE
|
||||
RENAME REPEATABLE REPLACE REPLICA RESET RESTART RESTRICT RETURNING RETURNS
|
||||
REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE
|
||||
RANGE READ REAL REASSIGN RECHECK RECURSIVE REFERENCES REINDEX
|
||||
RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART
|
||||
RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE
|
||||
|
||||
SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE
|
||||
SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SHARE
|
||||
@@ -488,7 +489,7 @@ static TypeName *TableFuncTypeName(List *columns);
|
||||
TO TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
|
||||
TRUNCATE TRUSTED TYPE_P
|
||||
|
||||
UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
|
||||
UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
|
||||
UPDATE USER USING
|
||||
|
||||
VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
|
||||
@@ -533,10 +534,12 @@ static TypeName *TableFuncTypeName(List *columns);
|
||||
* between POSTFIXOP and Op. We can safely assign the same priority to
|
||||
* various unreserved keywords as needed to resolve ambiguities (this can't
|
||||
* have any bad effects since obviously the keywords will still behave the
|
||||
* same as if they weren't keywords). We need to do this for PARTITION
|
||||
* to support opt_existing_window_name.
|
||||
* same as if they weren't keywords). We need to do this for PARTITION,
|
||||
* RANGE, ROWS to support opt_existing_window_name; and for RANGE, ROWS
|
||||
* so that they can follow a_expr without creating
|
||||
* postfix-operator problems.
|
||||
*/
|
||||
%nonassoc IDENT PARTITION
|
||||
%nonassoc IDENT PARTITION RANGE ROWS
|
||||
%left Op OPERATOR /* multi-character ops and user-defined operators */
|
||||
%nonassoc NOTNULL
|
||||
%nonassoc ISNULL
|
||||
@@ -9235,10 +9238,11 @@ over_clause: OVER window_specification
|
||||
| OVER ColId
|
||||
{
|
||||
WindowDef *n = makeNode(WindowDef);
|
||||
n->name = NULL;
|
||||
n->refname = $2;
|
||||
n->name = $2;
|
||||
n->refname = NULL;
|
||||
n->partitionClause = NIL;
|
||||
n->orderClause = NIL;
|
||||
n->frameOptions = FRAMEOPTION_DEFAULTS;
|
||||
n->location = @2;
|
||||
$$ = n;
|
||||
}
|
||||
@@ -9247,13 +9251,14 @@ over_clause: OVER window_specification
|
||||
;
|
||||
|
||||
window_specification: '(' opt_existing_window_name opt_partition_clause
|
||||
opt_sort_clause ')'
|
||||
opt_sort_clause opt_frame_clause ')'
|
||||
{
|
||||
WindowDef *n = makeNode(WindowDef);
|
||||
n->name = NULL;
|
||||
n->refname = $2;
|
||||
n->partitionClause = $3;
|
||||
n->orderClause = $4;
|
||||
n->frameOptions = $5;
|
||||
n->location = @1;
|
||||
$$ = n;
|
||||
}
|
||||
@@ -9268,7 +9273,6 @@ window_specification: '(' opt_existing_window_name opt_partition_clause
|
||||
* that the shift/reduce conflict is resolved in favor of reducing the rule.
|
||||
* These keywords are thus precluded from being an existing_window_name but
|
||||
* are not reserved for any other purpose.
|
||||
* (RANGE/ROWS are not an issue as of 8.4 for lack of frame_clause support.)
|
||||
*/
|
||||
opt_existing_window_name: ColId { $$ = $1; }
|
||||
| /*EMPTY*/ %prec Op { $$ = NULL; }
|
||||
@@ -9278,6 +9282,83 @@ opt_partition_clause: PARTITION BY expr_list { $$ = $3; }
|
||||
| /*EMPTY*/ { $$ = NIL; }
|
||||
;
|
||||
|
||||
/*
|
||||
* This is only a subset of the full SQL:2008 frame_clause grammar.
|
||||
* We don't support <expression> PRECEDING, <expression> FOLLOWING,
|
||||
* nor <window frame exclusion> yet.
|
||||
*/
|
||||
opt_frame_clause:
|
||||
RANGE frame_extent
|
||||
{
|
||||
$$ = FRAMEOPTION_NONDEFAULT | FRAMEOPTION_RANGE | $2;
|
||||
}
|
||||
| ROWS frame_extent
|
||||
{
|
||||
$$ = FRAMEOPTION_NONDEFAULT | FRAMEOPTION_ROWS | $2;
|
||||
}
|
||||
| /*EMPTY*/
|
||||
{ $$ = FRAMEOPTION_DEFAULTS; }
|
||||
;
|
||||
|
||||
frame_extent: frame_bound
|
||||
{
|
||||
/* reject invalid cases */
|
||||
if ($1 & FRAMEOPTION_START_UNBOUNDED_FOLLOWING)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WINDOWING_ERROR),
|
||||
errmsg("frame start cannot be UNBOUNDED FOLLOWING"),
|
||||
scanner_errposition(@1)));
|
||||
if ($1 & FRAMEOPTION_START_CURRENT_ROW)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("frame start at CURRENT ROW is not implemented"),
|
||||
scanner_errposition(@1)));
|
||||
$$ = $1 | FRAMEOPTION_END_CURRENT_ROW;
|
||||
}
|
||||
| BETWEEN frame_bound AND frame_bound
|
||||
{
|
||||
/* reject invalid cases */
|
||||
if ($2 & FRAMEOPTION_START_UNBOUNDED_FOLLOWING)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WINDOWING_ERROR),
|
||||
errmsg("frame start cannot be UNBOUNDED FOLLOWING"),
|
||||
scanner_errposition(@2)));
|
||||
if ($2 & FRAMEOPTION_START_CURRENT_ROW)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("frame start at CURRENT ROW is not implemented"),
|
||||
scanner_errposition(@2)));
|
||||
if ($4 & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WINDOWING_ERROR),
|
||||
errmsg("frame end cannot be UNBOUNDED PRECEDING"),
|
||||
scanner_errposition(@4)));
|
||||
/* shift converts START_ options to END_ options */
|
||||
$$ = FRAMEOPTION_BETWEEN | $2 | ($4 << 1);
|
||||
}
|
||||
;
|
||||
|
||||
/*
|
||||
* This is used for both frame start and frame end, with output set up on
|
||||
* the assumption it's frame start; the frame_extent productions must reject
|
||||
* invalid cases.
|
||||
*/
|
||||
frame_bound:
|
||||
UNBOUNDED PRECEDING
|
||||
{
|
||||
$$ = FRAMEOPTION_START_UNBOUNDED_PRECEDING;
|
||||
}
|
||||
| UNBOUNDED FOLLOWING
|
||||
{
|
||||
$$ = FRAMEOPTION_START_UNBOUNDED_FOLLOWING;
|
||||
}
|
||||
| CURRENT_P ROW
|
||||
{
|
||||
$$ = FRAMEOPTION_START_CURRENT_ROW;
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
/*
|
||||
* Supporting nonterminals for expressions.
|
||||
*/
|
||||
@@ -10012,6 +10093,7 @@ unreserved_keyword:
|
||||
| EXTERNAL
|
||||
| FAMILY
|
||||
| FIRST_P
|
||||
| FOLLOWING
|
||||
| FORCE
|
||||
| FORWARD
|
||||
| FUNCTION
|
||||
@@ -10086,6 +10168,7 @@ unreserved_keyword:
|
||||
| PARTITION
|
||||
| PASSWORD
|
||||
| PLANS
|
||||
| PRECEDING
|
||||
| PREPARE
|
||||
| PREPARED
|
||||
| PRESERVE
|
||||
@@ -10094,6 +10177,7 @@ unreserved_keyword:
|
||||
| PROCEDURAL
|
||||
| PROCEDURE
|
||||
| QUOTE
|
||||
| RANGE
|
||||
| READ
|
||||
| REASSIGN
|
||||
| RECHECK
|
||||
@@ -10151,6 +10235,7 @@ unreserved_keyword:
|
||||
| TRUNCATE
|
||||
| TRUSTED
|
||||
| TYPE_P
|
||||
| UNBOUNDED
|
||||
| UNCOMMITTED
|
||||
| UNENCRYPTED
|
||||
| UNKNOWN
|
||||
|
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.207 2008/12/28 18:53:58 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.208 2008/12/31 00:08:37 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -172,6 +172,7 @@ const ScanKeyword ScanKeywords[] = {
|
||||
{"fetch", FETCH, RESERVED_KEYWORD},
|
||||
{"first", FIRST_P, UNRESERVED_KEYWORD},
|
||||
{"float", FLOAT_P, COL_NAME_KEYWORD},
|
||||
{"following", FOLLOWING, UNRESERVED_KEYWORD},
|
||||
{"for", FOR, RESERVED_KEYWORD},
|
||||
{"force", FORCE, UNRESERVED_KEYWORD},
|
||||
{"foreign", FOREIGN, RESERVED_KEYWORD},
|
||||
@@ -299,6 +300,7 @@ const ScanKeyword ScanKeywords[] = {
|
||||
{"placing", PLACING, RESERVED_KEYWORD},
|
||||
{"plans", PLANS, UNRESERVED_KEYWORD},
|
||||
{"position", POSITION, COL_NAME_KEYWORD},
|
||||
{"preceding", PRECEDING, UNRESERVED_KEYWORD},
|
||||
{"precision", PRECISION, COL_NAME_KEYWORD},
|
||||
{"prepare", PREPARE, UNRESERVED_KEYWORD},
|
||||
{"prepared", PREPARED, UNRESERVED_KEYWORD},
|
||||
@@ -309,6 +311,7 @@ const ScanKeyword ScanKeywords[] = {
|
||||
{"procedural", PROCEDURAL, UNRESERVED_KEYWORD},
|
||||
{"procedure", PROCEDURE, UNRESERVED_KEYWORD},
|
||||
{"quote", QUOTE, UNRESERVED_KEYWORD},
|
||||
{"range", RANGE, UNRESERVED_KEYWORD},
|
||||
{"read", READ, UNRESERVED_KEYWORD},
|
||||
{"real", REAL, COL_NAME_KEYWORD},
|
||||
{"reassign", REASSIGN, UNRESERVED_KEYWORD},
|
||||
@@ -388,6 +391,7 @@ const ScanKeyword ScanKeywords[] = {
|
||||
{"truncate", TRUNCATE, UNRESERVED_KEYWORD},
|
||||
{"trusted", TRUSTED, UNRESERVED_KEYWORD},
|
||||
{"type", TYPE_P, UNRESERVED_KEYWORD},
|
||||
{"unbounded", UNBOUNDED, UNRESERVED_KEYWORD},
|
||||
{"uncommitted", UNCOMMITTED, UNRESERVED_KEYWORD},
|
||||
{"unencrypted", UNENCRYPTED, UNRESERVED_KEYWORD},
|
||||
{"union", UNION, RESERVED_KEYWORD},
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.85 2008/12/28 18:53:58 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.86 2008/12/31 00:08:37 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -123,25 +123,27 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
|
||||
locate_windowfunc((Node *) wfunc->args))));
|
||||
|
||||
/*
|
||||
* If the OVER clause just specifies a reference name, find that
|
||||
* If the OVER clause just specifies a window 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)
|
||||
if (windef->name)
|
||||
{
|
||||
Index winref = 0;
|
||||
ListCell *lc;
|
||||
|
||||
Assert(windef->refname == NULL &&
|
||||
windef->partitionClause == NIL &&
|
||||
windef->orderClause == NIL &&
|
||||
windef->frameOptions == FRAMEOPTION_DEFAULTS);
|
||||
|
||||
foreach(lc, pstate->p_windowdefs)
|
||||
{
|
||||
WindowDef *refwin = (WindowDef *) lfirst(lc);
|
||||
|
||||
winref++;
|
||||
if (refwin->name && strcmp(refwin->name, windef->refname) == 0)
|
||||
if (refwin->name && strcmp(refwin->name, windef->name) == 0)
|
||||
{
|
||||
wfunc->winref = winref;
|
||||
break;
|
||||
@@ -150,7 +152,7 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
|
||||
if (lc == NULL) /* didn't find it? */
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("window \"%s\" does not exist", windef->refname),
|
||||
errmsg("window \"%s\" does not exist", windef->name),
|
||||
parser_errposition(pstate, windef->location)));
|
||||
}
|
||||
else
|
||||
@@ -164,14 +166,15 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
|
||||
|
||||
winref++;
|
||||
if (refwin->refname && windef->refname &&
|
||||
strcmp(refwin->name, windef->refname) == 0)
|
||||
strcmp(refwin->refname, 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))
|
||||
equal(refwin->orderClause, windef->orderClause) &&
|
||||
refwin->frameOptions == windef->frameOptions)
|
||||
{
|
||||
/* found a duplicate window specification */
|
||||
wfunc->winref = winref;
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.182 2008/12/28 18:53:58 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.183 2008/12/31 00:08:37 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1554,7 +1554,7 @@ transformWindowDefinitions(ParseState *pstate,
|
||||
* Per spec, a windowdef that references a previous one copies the
|
||||
* previous partition clause (and mustn't specify its own). It can
|
||||
* specify its own ordering clause. but only if the previous one
|
||||
* had none.
|
||||
* had none. It always specifies its own framing clause.
|
||||
*/
|
||||
if (refwc)
|
||||
{
|
||||
@@ -1592,6 +1592,7 @@ transformWindowDefinitions(ParseState *pstate,
|
||||
wc->orderClause = orderClause;
|
||||
wc->copiedOrder = false;
|
||||
}
|
||||
wc->frameOptions = windef->frameOptions;
|
||||
wc->winref = winref;
|
||||
|
||||
result = lappend(result, wc);
|
||||
|
Reference in New Issue
Block a user