Commit ec4be2ee authored by Tom Lane's avatar Tom Lane

Extend the set of frame options supported for window functions.

This patch allows the frame to start from CURRENT ROW (in either RANGE or
ROWS mode), and it also adds support for ROWS n PRECEDING and ROWS n FOLLOWING
start and end points.  (RANGE value PRECEDING/FOLLOWING isn't there yet ---
the grammar works, but that's all.)

Hitoshi Harada, reviewed by Pavel Stehule
parent a5348faf
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.501 2010/02/07 20:48:09 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.502 2010/02/12 17:33:19 tgl Exp $ -->
<chapter id="functions"> <chapter id="functions">
<title>Functions and Operators</title> <title>Functions and Operators</title>
...@@ -10559,21 +10559,23 @@ SELECT xmlagg(x) FROM (SELECT x FROM test ORDER BY y DESC) AS tab; ...@@ -10559,21 +10559,23 @@ SELECT xmlagg(x) FROM (SELECT x FROM test ORDER BY y DESC) AS tab;
<function>nth_value</> consider only the rows within the <quote>window <function>nth_value</> consider only the rows within the <quote>window
frame</>, which by default contains the rows from the start of the frame</>, which by default contains the rows from the start of the
partition through the last peer of the current row. This is partition through the last peer of the current row. This is
likely to give unhelpful results for <function>nth_value</> and likely to give unhelpful results for <function>last_value</> and
particularly <function>last_value</>. You can redefine the frame as sometimes also <function>nth_value</>. You can redefine the frame by
being the whole partition by adding <literal>ROWS BETWEEN UNBOUNDED adding a suitable frame specification (<literal>RANGE</> or
PRECEDING AND UNBOUNDED FOLLOWING</> to the <literal>OVER</> clause. <literal>ROWS</>) to the <literal>OVER</> clause.
See <xref linkend="syntax-window-functions"> for more information. See <xref linkend="syntax-window-functions"> for more information
about frame specifications.
</para> </para>
<para> <para>
When an aggregate function is used as a window function, it aggregates When an aggregate function is used as a window function, it aggregates
over the rows within the current row's window frame. To obtain over the rows within the current row's window frame.
aggregation over the whole partition, omit <literal>ORDER BY</> or use
<literal>ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING</>.
An aggregate used with <literal>ORDER BY</> and the default window frame An aggregate used with <literal>ORDER BY</> and the default window frame
definition produces a <quote>running sum</> type of behavior, which may or definition produces a <quote>running sum</> type of behavior, which may or
may not be what's wanted. may not be what's wanted. To obtain
aggregation over the whole partition, omit <literal>ORDER BY</> or use
<literal>ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING</>.
Other frame specifications can be used to obtain other effects.
</para> </para>
<note> <note>
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.128 2009/10/28 14:55:37 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.129 2010/02/12 17:33:19 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -616,27 +616,66 @@ WINDOW <replaceable class="parameter">window_name</replaceable> AS ( <replaceabl ...@@ -616,27 +616,66 @@ WINDOW <replaceable class="parameter">window_name</replaceable> AS ( <replaceabl
<para> <para>
The optional <replaceable class="parameter">frame_clause</> defines The optional <replaceable class="parameter">frame_clause</> defines
the <firstterm>window frame</> for window functions that depend on the the <firstterm>window frame</> for window functions that depend on the
frame (not all do). It can be one of frame (not all do). The window frame is a set of related rows for
each row of the query (called the <firstterm>current row</>).
The <replaceable class="parameter">frame_clause</> can be one of
<synopsis>
[ RANGE | ROWS ] <replaceable>frame_start</>
[ RANGE | ROWS ] BETWEEN <replaceable>frame_start</> AND <replaceable>frame_end</>
</synopsis>
where <replaceable>frame_start</> and <replaceable>frame_end</> can be
one of
<synopsis> <synopsis>
RANGE UNBOUNDED PRECEDING UNBOUNDED PRECEDING
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW <replaceable>value</replaceable> PRECEDING
RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING CURRENT ROW
ROWS UNBOUNDED PRECEDING <replaceable>value</replaceable> FOLLOWING
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW UNBOUNDED FOLLOWING
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
</synopsis> </synopsis>
The first two are equivalent and are also the default: they set the
frame to be all rows from the partition start up through the current row's If <replaceable>frame_end</> is omitted it defaults to <literal>CURRENT
last peer in the <literal>ORDER BY</> ordering (which means all rows if ROW</>. Restrictions are that
there is no <literal>ORDER BY</>). The options <replaceable>frame_start</> cannot be <literal>UNBOUNDED FOLLOWING</>,
<literal>RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING</> and <replaceable>frame_end</> cannot be <literal>UNBOUNDED PRECEDING</>,
<literal>ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING</> and the <replaceable>frame_end</> choice cannot appear earlier in the
are also equivalent: they always select all rows in the partition. above list than the <replaceable>frame_start</> choice &mdash; for example
Lastly, <literal>ROWS UNBOUNDED PRECEDING</> or its verbose equivalent <literal>RANGE BETWEEN CURRENT ROW AND <replaceable>value</>
<literal>ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW</> select PRECEDING</literal> is not allowed.
all rows up through the current row (regardless of duplicates). </para>
Beware that this option can produce implementation-dependent results
if the <literal>ORDER BY</> ordering does not order the rows uniquely. <para>
The default framing option is <literal>RANGE UNBOUNDED PRECEDING</>,
which is the same as <literal>RANGE BETWEEN UNBOUNDED PRECEDING AND
CURRENT ROW</>; it sets the frame to be all rows from the partition start
up through the current row's last peer in the <literal>ORDER BY</>
ordering (which means all rows if there is no <literal>ORDER BY</>).
In general, <literal>UNBOUNDED PRECEDING</> means that the frame
starts with the first row of the partition, and similarly
<literal>UNBOUNDED FOLLOWING</> means that the frame ends with the last
row of the partition (regardless of <literal>RANGE</> or <literal>ROWS</>
mode). In <literal>ROWS</> mode, <literal>CURRENT ROW</>
means that the frame starts or ends with the current row; but in
<literal>RANGE</> mode it means that the frame starts or ends with
the current row's first or last peer in the <literal>ORDER BY</> ordering.
The <replaceable>value</> <literal>PRECEDING</> and
<replaceable>value</> <literal>FOLLOWING</> cases are currently only
allowed in <literal>ROWS</> mode. They indicate that the frame starts
or ends with the row that many rows before or after the current row.
<replaceable>value</replaceable> must be an integer expression not
containing any variables, aggregate functions, or window functions.
The value must not be null or negative; but it can be zero, which
selects the current row itself.
</para>
<para>
Beware that the <literal>ROWS</> options can produce unpredictable
results if the <literal>ORDER BY</> ordering does not order the rows
uniquely. The <literal>RANGE</> options are designed to ensure that
rows that are peers in the <literal>ORDER BY</> ordering are treated
alike; any two peer rows will be both in or both not in the frame.
</para> </para>
<para> <para>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.141 2010/02/04 00:19:28 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.142 2010/02/12 17:33:19 tgl Exp $ -->
<chapter id="sql-syntax"> <chapter id="sql-syntax">
<title>SQL Syntax</title> <title>SQL Syntax</title>
...@@ -1667,14 +1667,21 @@ SELECT array_agg(a ORDER BY b DESC) FROM table; ...@@ -1667,14 +1667,21 @@ SELECT array_agg(a ORDER BY b DESC) FROM table;
and the optional <replaceable class="parameter">frame_clause</replaceable> and the optional <replaceable class="parameter">frame_clause</replaceable>
can be one of can be one of
<synopsis> <synopsis>
RANGE UNBOUNDED PRECEDING [ RANGE | ROWS ] <replaceable>frame_start</>
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW [ RANGE | ROWS ] BETWEEN <replaceable>frame_start</> AND <replaceable>frame_end</>
RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
ROWS UNBOUNDED PRECEDING
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
</synopsis> </synopsis>
where <replaceable>frame_start</> and <replaceable>frame_end</> can be
one of
<synopsis>
UNBOUNDED PRECEDING
<replaceable>value</replaceable> PRECEDING
CURRENT ROW
<replaceable>value</replaceable> FOLLOWING
UNBOUNDED FOLLOWING
</synopsis>
</para>
<para>
Here, <replaceable>expression</replaceable> represents any value Here, <replaceable>expression</replaceable> represents any value
expression that does not itself contain window function calls. expression that does not itself contain window function calls.
The <literal>PARTITION BY</> and <literal>ORDER BY</> lists have The <literal>PARTITION BY</> and <literal>ORDER BY</> lists have
...@@ -1699,19 +1706,35 @@ ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ...@@ -1699,19 +1706,35 @@ ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
The <replaceable class="parameter">frame_clause</replaceable> specifies The <replaceable class="parameter">frame_clause</replaceable> specifies
the set of rows constituting the <firstterm>window frame</>, for those the set of rows constituting the <firstterm>window frame</>, for those
window functions that act on the frame instead of the whole partition. window functions that act on the frame instead of the whole partition.
If <replaceable>frame_end</> is omitted it defaults to <literal>CURRENT
ROW</>. Restrictions are that
<replaceable>frame_start</> cannot be <literal>UNBOUNDED FOLLOWING</>,
<replaceable>frame_end</> cannot be <literal>UNBOUNDED PRECEDING</>,
and the <replaceable>frame_end</> choice cannot appear earlier in the
above list than the <replaceable>frame_start</> choice &mdash; for example
<literal>RANGE BETWEEN CURRENT ROW AND <replaceable>value</>
PRECEDING</literal> is not allowed.
The default framing option is <literal>RANGE UNBOUNDED PRECEDING</>, The default framing option is <literal>RANGE UNBOUNDED PRECEDING</>,
which is the same as <literal>RANGE BETWEEN UNBOUNDED PRECEDING AND which is the same as <literal>RANGE BETWEEN UNBOUNDED PRECEDING AND
CURRENT ROW</>; it selects rows up through the current row's last CURRENT ROW</>; it sets the frame to be all rows from the partition start
peer in the <literal>ORDER BY</> ordering (which means all rows if up through the current row's last peer in the <literal>ORDER BY</>
there is no <literal>ORDER BY</>). The options ordering (which means all rows if there is no <literal>ORDER BY</>).
<literal>RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING</> and In general, <literal>UNBOUNDED PRECEDING</> means that the frame
<literal>ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING</> starts with the first row of the partition, and similarly
are also equivalent: they always select all rows in the partition. <literal>UNBOUNDED FOLLOWING</> means that the frame ends with the last
Lastly, <literal>ROWS UNBOUNDED PRECEDING</> or its verbose equivalent row of the partition (regardless of <literal>RANGE</> or <literal>ROWS</>
<literal>ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW</> select mode). In <literal>ROWS</> mode, <literal>CURRENT ROW</>
all rows up through the current row (regardless of duplicates). means that the frame starts or ends with the current row; but in
Beware that this option can produce implementation-dependent results <literal>RANGE</> mode it means that the frame starts or ends with
if the <literal>ORDER BY</> ordering does not order the rows uniquely. the current row's first or last peer in the <literal>ORDER BY</> ordering.
The <replaceable>value</> <literal>PRECEDING</> and
<replaceable>value</> <literal>FOLLOWING</> cases are currently only
allowed in <literal>ROWS</> mode. They indicate that the frame starts
or ends with the row that many rows before or after the current row.
<replaceable>value</replaceable> must be an integer expression not
containing any variables, aggregate functions, or window functions.
The value must not be null or negative; but it can be zero, which
selects the current row itself.
</para> </para>
<para> <para>
......
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.172 2010/02/08 20:39:51 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.173 2010/02/12 17:33:19 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1999,7 +1999,7 @@ AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext) ...@@ -1999,7 +1999,7 @@ AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext)
if (fcinfo->context && IsA(fcinfo->context, WindowAggState)) if (fcinfo->context && IsA(fcinfo->context, WindowAggState))
{ {
if (aggcontext) if (aggcontext)
*aggcontext = ((WindowAggState *) fcinfo->context)->wincontext; *aggcontext = ((WindowAggState *) fcinfo->context)->aggcontext;
return AGG_CONTEXT_WINDOW; return AGG_CONTEXT_WINDOW;
} }
......
This diff is collapsed.
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.460 2010/01/28 23:21:11 petere Exp $ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.461 2010/02/12 17:33:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -718,6 +718,8 @@ _copyWindowAgg(WindowAgg *from) ...@@ -718,6 +718,8 @@ _copyWindowAgg(WindowAgg *from)
COPY_POINTER_FIELD(ordOperators, from->ordNumCols * sizeof(Oid)); COPY_POINTER_FIELD(ordOperators, from->ordNumCols * sizeof(Oid));
} }
COPY_SCALAR_FIELD(frameOptions); COPY_SCALAR_FIELD(frameOptions);
COPY_NODE_FIELD(startOffset);
COPY_NODE_FIELD(endOffset);
return newnode; return newnode;
} }
...@@ -1848,6 +1850,8 @@ _copyWindowClause(WindowClause *from) ...@@ -1848,6 +1850,8 @@ _copyWindowClause(WindowClause *from)
COPY_NODE_FIELD(partitionClause); COPY_NODE_FIELD(partitionClause);
COPY_NODE_FIELD(orderClause); COPY_NODE_FIELD(orderClause);
COPY_SCALAR_FIELD(frameOptions); COPY_SCALAR_FIELD(frameOptions);
COPY_NODE_FIELD(startOffset);
COPY_NODE_FIELD(endOffset);
COPY_SCALAR_FIELD(winref); COPY_SCALAR_FIELD(winref);
COPY_SCALAR_FIELD(copiedOrder); COPY_SCALAR_FIELD(copiedOrder);
...@@ -2076,6 +2080,8 @@ _copyWindowDef(WindowDef *from) ...@@ -2076,6 +2080,8 @@ _copyWindowDef(WindowDef *from)
COPY_NODE_FIELD(partitionClause); COPY_NODE_FIELD(partitionClause);
COPY_NODE_FIELD(orderClause); COPY_NODE_FIELD(orderClause);
COPY_SCALAR_FIELD(frameOptions); COPY_SCALAR_FIELD(frameOptions);
COPY_NODE_FIELD(startOffset);
COPY_NODE_FIELD(endOffset);
COPY_LOCATION_FIELD(location); COPY_LOCATION_FIELD(location);
return newnode; return newnode;
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.381 2010/01/28 23:21:11 petere Exp $ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.382 2010/02/12 17:33:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2056,6 +2056,8 @@ _equalWindowDef(WindowDef *a, WindowDef *b) ...@@ -2056,6 +2056,8 @@ _equalWindowDef(WindowDef *a, WindowDef *b)
COMPARE_NODE_FIELD(partitionClause); COMPARE_NODE_FIELD(partitionClause);
COMPARE_NODE_FIELD(orderClause); COMPARE_NODE_FIELD(orderClause);
COMPARE_SCALAR_FIELD(frameOptions); COMPARE_SCALAR_FIELD(frameOptions);
COMPARE_NODE_FIELD(startOffset);
COMPARE_NODE_FIELD(endOffset);
COMPARE_LOCATION_FIELD(location); COMPARE_LOCATION_FIELD(location);
return true; return true;
...@@ -2205,6 +2207,8 @@ _equalWindowClause(WindowClause *a, WindowClause *b) ...@@ -2205,6 +2207,8 @@ _equalWindowClause(WindowClause *a, WindowClause *b)
COMPARE_NODE_FIELD(partitionClause); COMPARE_NODE_FIELD(partitionClause);
COMPARE_NODE_FIELD(orderClause); COMPARE_NODE_FIELD(orderClause);
COMPARE_SCALAR_FIELD(frameOptions); COMPARE_SCALAR_FIELD(frameOptions);
COMPARE_NODE_FIELD(startOffset);
COMPARE_NODE_FIELD(endOffset);
COMPARE_SCALAR_FIELD(winref); COMPARE_SCALAR_FIELD(winref);
COMPARE_SCALAR_FIELD(copiedOrder); COMPARE_SCALAR_FIELD(copiedOrder);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.45 2010/01/02 16:57:46 momjian Exp $ * $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.46 2010/02/12 17:33:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1298,6 +1298,10 @@ expression_tree_walker(Node *node, ...@@ -1298,6 +1298,10 @@ expression_tree_walker(Node *node,
return true; return true;
if (walker(wc->orderClause, context)) if (walker(wc->orderClause, context))
return true; return true;
if (walker(wc->startOffset, context))
return true;
if (walker(wc->endOffset, context))
return true;
} }
break; break;
case T_CommonTableExpr: case T_CommonTableExpr:
...@@ -1950,6 +1954,8 @@ expression_tree_mutator(Node *node, ...@@ -1950,6 +1954,8 @@ expression_tree_mutator(Node *node,
FLATCOPY(newnode, wc, WindowClause); FLATCOPY(newnode, wc, WindowClause);
MUTATE(newnode->partitionClause, wc->partitionClause, List *); MUTATE(newnode->partitionClause, wc->partitionClause, List *);
MUTATE(newnode->orderClause, wc->orderClause, List *); MUTATE(newnode->orderClause, wc->orderClause, List *);
MUTATE(newnode->startOffset, wc->startOffset, Node *);
MUTATE(newnode->endOffset, wc->endOffset, Node *);
return (Node *) newnode; return (Node *) newnode;
} }
break; break;
...@@ -2475,6 +2481,10 @@ bool ...@@ -2475,6 +2481,10 @@ bool
return true; return true;
if (walker(wd->orderClause, context)) if (walker(wd->orderClause, context))
return true; return true;
if (walker(wd->startOffset, context))
return true;
if (walker(wd->endOffset, context))
return true;
} }
break; break;
case T_RangeSubselect: case T_RangeSubselect:
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.381 2010/01/28 23:21:12 petere Exp $ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.382 2010/02/12 17:33:20 tgl Exp $
* *
* NOTES * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -610,6 +610,8 @@ _outWindowAgg(StringInfo str, WindowAgg *node) ...@@ -610,6 +610,8 @@ _outWindowAgg(StringInfo str, WindowAgg *node)
appendStringInfo(str, " %u", node->ordOperators[i]); appendStringInfo(str, " %u", node->ordOperators[i]);
WRITE_INT_FIELD(frameOptions); WRITE_INT_FIELD(frameOptions);
WRITE_NODE_FIELD(startOffset);
WRITE_NODE_FIELD(endOffset);
} }
static void static void
...@@ -2035,6 +2037,8 @@ _outWindowClause(StringInfo str, WindowClause *node) ...@@ -2035,6 +2037,8 @@ _outWindowClause(StringInfo str, WindowClause *node)
WRITE_NODE_FIELD(partitionClause); WRITE_NODE_FIELD(partitionClause);
WRITE_NODE_FIELD(orderClause); WRITE_NODE_FIELD(orderClause);
WRITE_INT_FIELD(frameOptions); WRITE_INT_FIELD(frameOptions);
WRITE_NODE_FIELD(startOffset);
WRITE_NODE_FIELD(endOffset);
WRITE_UINT_FIELD(winref); WRITE_UINT_FIELD(winref);
WRITE_BOOL_FIELD(copiedOrder); WRITE_BOOL_FIELD(copiedOrder);
} }
...@@ -2326,6 +2330,8 @@ _outWindowDef(StringInfo str, WindowDef *node) ...@@ -2326,6 +2330,8 @@ _outWindowDef(StringInfo str, WindowDef *node)
WRITE_NODE_FIELD(partitionClause); WRITE_NODE_FIELD(partitionClause);
WRITE_NODE_FIELD(orderClause); WRITE_NODE_FIELD(orderClause);
WRITE_INT_FIELD(frameOptions); WRITE_INT_FIELD(frameOptions);
WRITE_NODE_FIELD(startOffset);
WRITE_NODE_FIELD(endOffset);
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.230 2010/01/02 16:57:46 momjian Exp $ * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.231 2010/02/12 17:33:20 tgl Exp $
* *
* NOTES * NOTES
* Path and Plan nodes do not have any readfuncs support, because we * Path and Plan nodes do not have any readfuncs support, because we
...@@ -279,6 +279,8 @@ _readWindowClause(void) ...@@ -279,6 +279,8 @@ _readWindowClause(void)
READ_NODE_FIELD(partitionClause); READ_NODE_FIELD(partitionClause);
READ_NODE_FIELD(orderClause); READ_NODE_FIELD(orderClause);
READ_INT_FIELD(frameOptions); READ_INT_FIELD(frameOptions);
READ_NODE_FIELD(startOffset);
READ_NODE_FIELD(endOffset);
READ_UINT_FIELD(winref); READ_UINT_FIELD(winref);
READ_BOOL_FIELD(copiedOrder); READ_BOOL_FIELD(copiedOrder);
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.270 2010/01/02 16:57:47 momjian Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.271 2010/02/12 17:33:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -3364,7 +3364,8 @@ make_windowagg(PlannerInfo *root, List *tlist, ...@@ -3364,7 +3364,8 @@ make_windowagg(PlannerInfo *root, List *tlist,
int numWindowFuncs, Index winref, int numWindowFuncs, Index winref,
int partNumCols, AttrNumber *partColIdx, Oid *partOperators, int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
int frameOptions, Plan *lefttree) int frameOptions, Node *startOffset, Node *endOffset,
Plan *lefttree)
{ {
WindowAgg *node = makeNode(WindowAgg); WindowAgg *node = makeNode(WindowAgg);
Plan *plan = &node->plan; Plan *plan = &node->plan;
...@@ -3379,6 +3380,8 @@ make_windowagg(PlannerInfo *root, List *tlist, ...@@ -3379,6 +3380,8 @@ make_windowagg(PlannerInfo *root, List *tlist,
node->ordColIdx = ordColIdx; node->ordColIdx = ordColIdx;
node->ordOperators = ordOperators; node->ordOperators = ordOperators;
node->frameOptions = frameOptions; node->frameOptions = frameOptions;
node->startOffset = startOffset;
node->endOffset = endOffset;
copy_plan_costsize(plan, lefttree); /* only care about copying size */ copy_plan_costsize(plan, lefttree); /* only care about copying size */
cost_windowagg(&windowagg_path, root, cost_windowagg(&windowagg_path, root,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.264 2010/02/10 03:38:35 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.265 2010/02/12 17:33:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -398,7 +398,10 @@ subquery_planner(PlannerGlobal *glob, Query *parse, ...@@ -398,7 +398,10 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
root->hasPseudoConstantQuals = false; root->hasPseudoConstantQuals = false;
/* /*
* Do expression preprocessing on targetlist and quals. * Do expression preprocessing on targetlist and quals, as well as other
* random expressions in the querytree. Note that we do not need to
* handle sort/group expressions explicitly, because they are actually
* part of the targetlist.
*/ */
parse->targetList = (List *) parse->targetList = (List *)
preprocess_expression(root, (Node *) parse->targetList, preprocess_expression(root, (Node *) parse->targetList,
...@@ -413,6 +416,17 @@ subquery_planner(PlannerGlobal *glob, Query *parse, ...@@ -413,6 +416,17 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
parse->havingQual = preprocess_expression(root, parse->havingQual, parse->havingQual = preprocess_expression(root, parse->havingQual,
EXPRKIND_QUAL); EXPRKIND_QUAL);
foreach(l, parse->windowClause)
{
WindowClause *wc = (WindowClause *) lfirst(l);
/* partitionClause/orderClause are sort/group expressions */
wc->startOffset = preprocess_expression(root, wc->startOffset,
EXPRKIND_LIMIT);
wc->endOffset = preprocess_expression(root, wc->endOffset,
EXPRKIND_LIMIT);
}
parse->limitOffset = preprocess_expression(root, parse->limitOffset, parse->limitOffset = preprocess_expression(root, parse->limitOffset,
EXPRKIND_LIMIT); EXPRKIND_LIMIT);
parse->limitCount = preprocess_expression(root, parse->limitCount, parse->limitCount = preprocess_expression(root, parse->limitCount,
...@@ -1513,6 +1527,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) ...@@ -1513,6 +1527,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
ordColIdx, ordColIdx,
ordOperators, ordOperators,
wc->frameOptions, wc->frameOptions,
wc->startOffset,
wc->endOffset,
result_plan); result_plan);
} }
} }
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.157 2010/01/15 22:36:32 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.158 2010/02/12 17:33:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -466,10 +466,26 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) ...@@ -466,10 +466,26 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
} }
break; break;
case T_Agg: case T_Agg:
case T_WindowAgg:
case T_Group: case T_Group:
set_upper_references(glob, plan, rtoffset); set_upper_references(glob, plan, rtoffset);
break; break;
case T_WindowAgg:
{
WindowAgg *wplan = (WindowAgg *) plan;
set_upper_references(glob, plan, rtoffset);
/*
* Like Limit node limit/offset expressions, WindowAgg has
* frame offset expressions, which cannot contain subplan
* variable refs, so fix_scan_expr works for them.
*/
wplan->startOffset =
fix_scan_expr(glob, wplan->startOffset, rtoffset);
wplan->endOffset =
fix_scan_expr(glob, wplan->endOffset, rtoffset);
}
break;
case T_Result: case T_Result:
{ {
Result *splan = (Result *) plan; Result *splan = (Result *) plan;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.158 2010/01/18 18:17:45 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.159 2010/02/12 17:33:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2098,9 +2098,15 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, ...@@ -2098,9 +2098,15 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
locally_added_param); locally_added_param);
break; break;
case T_WindowAgg:
finalize_primnode(((WindowAgg *) plan)->startOffset,
&context);
finalize_primnode(((WindowAgg *) plan)->endOffset,
&context);
break;
case T_Hash: case T_Hash:
case T_Agg: case T_Agg:
case T_WindowAgg:
case T_Material: case T_Material:
case T_Sort: case T_Sort:
case T_Unique: case T_Unique:
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.707 2010/02/08 04:33:54 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.708 2010/02/12 17:33:20 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -434,8 +434,8 @@ static TypeName *TableFuncTypeName(List *columns); ...@@ -434,8 +434,8 @@ static TypeName *TableFuncTypeName(List *columns);
%type <list> window_clause window_definition_list opt_partition_clause %type <list> window_clause window_definition_list opt_partition_clause
%type <windef> window_definition over_clause window_specification %type <windef> window_definition over_clause window_specification
opt_frame_clause frame_extent frame_bound
%type <str> opt_existing_window_name %type <str> opt_existing_window_name
%type <ival> opt_frame_clause frame_extent frame_bound
/* /*
...@@ -578,8 +578,18 @@ static TypeName *TableFuncTypeName(List *columns); ...@@ -578,8 +578,18 @@ static TypeName *TableFuncTypeName(List *columns);
* RANGE, ROWS to support opt_existing_window_name; and for RANGE, ROWS * RANGE, ROWS to support opt_existing_window_name; and for RANGE, ROWS
* so that they can follow a_expr without creating * so that they can follow a_expr without creating
* postfix-operator problems. * postfix-operator problems.
*/ *
%nonassoc IDENT PARTITION RANGE ROWS * The frame_bound productions UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING
* are even messier: since UNBOUNDED is an unreserved keyword (per spec!),
* there is no principled way to distinguish these from the productions
* a_expr PRECEDING/FOLLOWING. We hack this up by giving UNBOUNDED slightly
* lower precedence than PRECEDING and FOLLOWING. At present this doesn't
* appear to cause UNBOUNDED to be treated differently from other unreserved
* keywords anywhere else in the grammar, but it's definitely risky. We can
* blame any funny behavior of UNBOUNDED on the SQL standard, though.
*/
%nonassoc UNBOUNDED /* ideally should have same precedence as IDENT */
%nonassoc IDENT PARTITION RANGE ROWS PRECEDING FOLLOWING
%left Op OPERATOR /* multi-character ops and user-defined operators */ %left Op OPERATOR /* multi-character ops and user-defined operators */
%nonassoc NOTNULL %nonassoc NOTNULL
%nonassoc ISNULL %nonassoc ISNULL
...@@ -9907,6 +9917,8 @@ over_clause: OVER window_specification ...@@ -9907,6 +9917,8 @@ over_clause: OVER window_specification
n->partitionClause = NIL; n->partitionClause = NIL;
n->orderClause = NIL; n->orderClause = NIL;
n->frameOptions = FRAMEOPTION_DEFAULTS; n->frameOptions = FRAMEOPTION_DEFAULTS;
n->startOffset = NULL;
n->endOffset = NULL;
n->location = @2; n->location = @2;
$$ = n; $$ = n;
} }
...@@ -9922,7 +9934,10 @@ window_specification: '(' opt_existing_window_name opt_partition_clause ...@@ -9922,7 +9934,10 @@ window_specification: '(' opt_existing_window_name opt_partition_clause
n->refname = $2; n->refname = $2;
n->partitionClause = $3; n->partitionClause = $3;
n->orderClause = $4; n->orderClause = $4;
n->frameOptions = $5; /* copy relevant fields of opt_frame_clause */
n->frameOptions = $5->frameOptions;
n->startOffset = $5->startOffset;
n->endOffset = $5->endOffset;
n->location = @1; n->location = @1;
$$ = n; $$ = n;
} }
...@@ -9947,58 +9962,100 @@ opt_partition_clause: PARTITION BY expr_list { $$ = $3; } ...@@ -9947,58 +9962,100 @@ opt_partition_clause: PARTITION BY expr_list { $$ = $3; }
; ;
/* /*
* For frame clauses, we return a WindowDef, but only some fields are used:
* frameOptions, startOffset, and endOffset.
*
* This is only a subset of the full SQL:2008 frame_clause grammar. * This is only a subset of the full SQL:2008 frame_clause grammar.
* We don't support <expression> PRECEDING, <expression> FOLLOWING, * We don't support <window frame exclusion> yet.
* nor <window frame exclusion> yet.
*/ */
opt_frame_clause: opt_frame_clause:
RANGE frame_extent RANGE frame_extent
{ {
$$ = FRAMEOPTION_NONDEFAULT | FRAMEOPTION_RANGE | $2; WindowDef *n = $2;
n->frameOptions |= FRAMEOPTION_NONDEFAULT | FRAMEOPTION_RANGE;
if (n->frameOptions & (FRAMEOPTION_START_VALUE_PRECEDING |
FRAMEOPTION_END_VALUE_PRECEDING))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("RANGE PRECEDING is only supported with UNBOUNDED"),
parser_errposition(@1)));
if (n->frameOptions & (FRAMEOPTION_START_VALUE_FOLLOWING |
FRAMEOPTION_END_VALUE_FOLLOWING))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("RANGE FOLLOWING is only supported with UNBOUNDED"),
parser_errposition(@1)));
$$ = n;
} }
| ROWS frame_extent | ROWS frame_extent
{ {
$$ = FRAMEOPTION_NONDEFAULT | FRAMEOPTION_ROWS | $2; WindowDef *n = $2;
n->frameOptions |= FRAMEOPTION_NONDEFAULT | FRAMEOPTION_ROWS;
$$ = n;
} }
| /*EMPTY*/ | /*EMPTY*/
{ $$ = FRAMEOPTION_DEFAULTS; } {
WindowDef *n = makeNode(WindowDef);
n->frameOptions = FRAMEOPTION_DEFAULTS;
n->startOffset = NULL;
n->endOffset = NULL;
$$ = n;
}
; ;
frame_extent: frame_bound frame_extent: frame_bound
{ {
WindowDef *n = $1;
/* reject invalid cases */ /* reject invalid cases */
if ($1 & FRAMEOPTION_START_UNBOUNDED_FOLLOWING) if (n->frameOptions & FRAMEOPTION_START_UNBOUNDED_FOLLOWING)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR), (errcode(ERRCODE_WINDOWING_ERROR),
errmsg("frame start cannot be UNBOUNDED FOLLOWING"), errmsg("frame start cannot be UNBOUNDED FOLLOWING"),
parser_errposition(@1))); parser_errposition(@1)));
if ($1 & FRAMEOPTION_START_CURRENT_ROW) if (n->frameOptions & FRAMEOPTION_START_VALUE_FOLLOWING)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_WINDOWING_ERROR),
errmsg("frame start at CURRENT ROW is not implemented"), errmsg("frame starting from following row cannot end with current row"),
parser_errposition(@1))); parser_errposition(@1)));
$$ = $1 | FRAMEOPTION_END_CURRENT_ROW; n->frameOptions |= FRAMEOPTION_END_CURRENT_ROW;
$$ = n;
} }
| BETWEEN frame_bound AND frame_bound | BETWEEN frame_bound AND frame_bound
{ {
WindowDef *n1 = $2;
WindowDef *n2 = $4;
/* form merged options */
int frameOptions = n1->frameOptions;
/* shift converts START_ options to END_ options */
frameOptions |= n2->frameOptions << 1;
frameOptions |= FRAMEOPTION_BETWEEN;
/* reject invalid cases */ /* reject invalid cases */
if ($2 & FRAMEOPTION_START_UNBOUNDED_FOLLOWING) if (frameOptions & FRAMEOPTION_START_UNBOUNDED_FOLLOWING)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR), (errcode(ERRCODE_WINDOWING_ERROR),
errmsg("frame start cannot be UNBOUNDED FOLLOWING"), errmsg("frame start cannot be UNBOUNDED FOLLOWING"),
parser_errposition(@2))); parser_errposition(@2)));
if ($2 & FRAMEOPTION_START_CURRENT_ROW) if (frameOptions & FRAMEOPTION_END_UNBOUNDED_PRECEDING)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("frame start at CURRENT ROW is not implemented"),
parser_errposition(@2)));
if ($4 & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR), (errcode(ERRCODE_WINDOWING_ERROR),
errmsg("frame end cannot be UNBOUNDED PRECEDING"), errmsg("frame end cannot be UNBOUNDED PRECEDING"),
parser_errposition(@4))); parser_errposition(@4)));
/* shift converts START_ options to END_ options */ if ((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
$$ = FRAMEOPTION_BETWEEN | $2 | ($4 << 1); (frameOptions & FRAMEOPTION_END_VALUE_PRECEDING))
ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR),
errmsg("frame starting from current row cannot have preceding rows"),
parser_errposition(@4)));
if ((frameOptions & FRAMEOPTION_START_VALUE_FOLLOWING) &&
(frameOptions & (FRAMEOPTION_END_VALUE_PRECEDING |
FRAMEOPTION_END_CURRENT_ROW)))
ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR),
errmsg("frame starting from following row cannot have preceding rows"),
parser_errposition(@4)));
n1->frameOptions = frameOptions;
n1->endOffset = n2->startOffset;
$$ = n1;
} }
; ;
...@@ -10010,15 +10067,43 @@ frame_extent: frame_bound ...@@ -10010,15 +10067,43 @@ frame_extent: frame_bound
frame_bound: frame_bound:
UNBOUNDED PRECEDING UNBOUNDED PRECEDING
{ {
$$ = FRAMEOPTION_START_UNBOUNDED_PRECEDING; WindowDef *n = makeNode(WindowDef);
n->frameOptions = FRAMEOPTION_START_UNBOUNDED_PRECEDING;
n->startOffset = NULL;
n->endOffset = NULL;
$$ = n;
} }
| UNBOUNDED FOLLOWING | UNBOUNDED FOLLOWING
{ {
$$ = FRAMEOPTION_START_UNBOUNDED_FOLLOWING; WindowDef *n = makeNode(WindowDef);
n->frameOptions = FRAMEOPTION_START_UNBOUNDED_FOLLOWING;
n->startOffset = NULL;
n->endOffset = NULL;
$$ = n;
} }
| CURRENT_P ROW | CURRENT_P ROW
{ {
$$ = FRAMEOPTION_START_CURRENT_ROW; WindowDef *n = makeNode(WindowDef);
n->frameOptions = FRAMEOPTION_START_CURRENT_ROW;
n->startOffset = NULL;
n->endOffset = NULL;
$$ = n;
}
| a_expr PRECEDING
{
WindowDef *n = makeNode(WindowDef);
n->frameOptions = FRAMEOPTION_START_VALUE_PRECEDING;
n->startOffset = $1;
n->endOffset = NULL;
$$ = n;
}
| a_expr FOLLOWING
{
WindowDef *n = makeNode(WindowDef);
n->frameOptions = FRAMEOPTION_START_VALUE_FOLLOWING;
n->startOffset = $1;
n->endOffset = NULL;
$$ = n;
} }
; ;
...@@ -10981,7 +11066,8 @@ unreserved_keyword: ...@@ -10981,7 +11066,8 @@ unreserved_keyword:
* looks too much like a function call for an LR(1) parser. * looks too much like a function call for an LR(1) parser.
*/ */
col_name_keyword: col_name_keyword:
BIGINT BETWEEN
| BIGINT
| BIT | BIT
| BOOLEAN_P | BOOLEAN_P
| CHAR_P | CHAR_P
...@@ -11040,7 +11126,6 @@ col_name_keyword: ...@@ -11040,7 +11126,6 @@ col_name_keyword:
*/ */
type_func_name_keyword: type_func_name_keyword:
AUTHORIZATION AUTHORIZATION
| BETWEEN
| BINARY | BINARY
| CONCURRENTLY | CONCURRENTLY
| CROSS | CROSS
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.90 2010/01/02 16:57:49 momjian Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.91 2010/02/12 17:33:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -258,7 +258,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, ...@@ -258,7 +258,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
continue; continue;
if (equal(refwin->partitionClause, windef->partitionClause) && if (equal(refwin->partitionClause, windef->partitionClause) &&
equal(refwin->orderClause, windef->orderClause) && equal(refwin->orderClause, windef->orderClause) &&
refwin->frameOptions == windef->frameOptions) refwin->frameOptions == windef->frameOptions &&
equal(refwin->startOffset, windef->startOffset) &&
equal(refwin->endOffset, windef->endOffset))
{ {
/* found a duplicate window specification */ /* found a duplicate window specification */
wfunc->winref = winref; wfunc->winref = winref;
...@@ -505,6 +507,7 @@ parseCheckWindowFuncs(ParseState *pstate, Query *qry) ...@@ -505,6 +507,7 @@ parseCheckWindowFuncs(ParseState *pstate, Query *qry)
parser_errposition(pstate, parser_errposition(pstate,
locate_windowfunc(expr)))); locate_windowfunc(expr))));
} }
/* startOffset and limitOffset were checked in transformFrameOffset */
} }
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.196 2010/02/07 20:48:10 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.197 2010/02/12 17:33:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -72,6 +72,8 @@ static Node *transformFromClauseItem(ParseState *pstate, Node *n, ...@@ -72,6 +72,8 @@ static Node *transformFromClauseItem(ParseState *pstate, Node *n,
Relids *containedRels); Relids *containedRels);
static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype, static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype,
Var *l_colvar, Var *r_colvar); Var *l_colvar, Var *r_colvar);
static void checkExprIsVarFree(ParseState *pstate, Node *n,
const char *constructName);
static TargetEntry *findTargetlistEntrySQL92(ParseState *pstate, Node *node, static TargetEntry *findTargetlistEntrySQL92(ParseState *pstate, Node *node,
List **tlist, int clause); List **tlist, int clause);
static TargetEntry *findTargetlistEntrySQL99(ParseState *pstate, Node *node, static TargetEntry *findTargetlistEntrySQL99(ParseState *pstate, Node *node,
...@@ -85,6 +87,8 @@ static List *addTargetToGroupList(ParseState *pstate, TargetEntry *tle, ...@@ -85,6 +87,8 @@ static List *addTargetToGroupList(ParseState *pstate, TargetEntry *tle,
List *grouplist, List *targetlist, int location, List *grouplist, List *targetlist, int location,
bool resolveUnknown); bool resolveUnknown);
static WindowClause *findWindowClause(List *wclist, const char *name); static WindowClause *findWindowClause(List *wclist, const char *name);
static Node *transformFrameOffset(ParseState *pstate, int frameOptions,
Node *clause);
/* /*
...@@ -1177,10 +1181,28 @@ transformLimitClause(ParseState *pstate, Node *clause, ...@@ -1177,10 +1181,28 @@ transformLimitClause(ParseState *pstate, Node *clause,
qual = coerce_to_specific_type(pstate, qual, INT8OID, constructName); qual = coerce_to_specific_type(pstate, qual, INT8OID, constructName);
/* /* LIMIT can't refer to any vars or aggregates of the current query */
* LIMIT can't refer to any vars or aggregates of the current query checkExprIsVarFree(pstate, qual, constructName);
return qual;
}
/*
* checkExprIsVarFree
* Check that given expr has no Vars of the current query level
* (and no aggregates or window functions, either).
*
* This is used to check expressions that have to have a consistent value
* across all rows of the query, such as a LIMIT. Arguably it should reject
* volatile functions, too, but we don't do that --- whatever value the
* function gives on first execution is what you get.
*
* constructName does not affect the semantics, but is used in error messages
*/ */
if (contain_vars_of_level(qual, 0)) static void
checkExprIsVarFree(ParseState *pstate, Node *n, const char *constructName)
{
if (contain_vars_of_level(n, 0))
{ {
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE), (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
...@@ -1188,10 +1210,10 @@ transformLimitClause(ParseState *pstate, Node *clause, ...@@ -1188,10 +1210,10 @@ transformLimitClause(ParseState *pstate, Node *clause,
errmsg("argument of %s must not contain variables", errmsg("argument of %s must not contain variables",
constructName), constructName),
parser_errposition(pstate, parser_errposition(pstate,
locate_var_of_level(qual, 0)))); locate_var_of_level(n, 0))));
} }
if (pstate->p_hasAggs && if (pstate->p_hasAggs &&
checkExprHasAggs(qual)) checkExprHasAggs(n))
{ {
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR), (errcode(ERRCODE_GROUPING_ERROR),
...@@ -1199,10 +1221,10 @@ transformLimitClause(ParseState *pstate, Node *clause, ...@@ -1199,10 +1221,10 @@ transformLimitClause(ParseState *pstate, Node *clause,
errmsg("argument of %s must not contain aggregate functions", errmsg("argument of %s must not contain aggregate functions",
constructName), constructName),
parser_errposition(pstate, parser_errposition(pstate,
locate_agg_of_level(qual, 0)))); locate_agg_of_level(n, 0))));
} }
if (pstate->p_hasWindowFuncs && if (pstate->p_hasWindowFuncs &&
checkExprHasWindowFuncs(qual)) checkExprHasWindowFuncs(n))
{ {
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR), (errcode(ERRCODE_WINDOWING_ERROR),
...@@ -1210,10 +1232,8 @@ transformLimitClause(ParseState *pstate, Node *clause, ...@@ -1210,10 +1232,8 @@ transformLimitClause(ParseState *pstate, Node *clause,
errmsg("argument of %s must not contain window functions", errmsg("argument of %s must not contain window functions",
constructName), constructName),
parser_errposition(pstate, parser_errposition(pstate,
locate_windowfunc(qual)))); locate_windowfunc(n))));
} }
return qual;
} }
...@@ -1664,6 +1684,11 @@ transformWindowDefinitions(ParseState *pstate, ...@@ -1664,6 +1684,11 @@ transformWindowDefinitions(ParseState *pstate,
windef->refname), windef->refname),
parser_errposition(pstate, windef->location))); parser_errposition(pstate, windef->location)));
wc->frameOptions = windef->frameOptions; wc->frameOptions = windef->frameOptions;
/* Process frame offset expressions */
wc->startOffset = transformFrameOffset(pstate, wc->frameOptions,
windef->startOffset);
wc->endOffset = transformFrameOffset(pstate, wc->frameOptions,
windef->endOffset);
wc->winref = winref; wc->winref = winref;
result = lappend(result, wc); result = lappend(result, wc);
...@@ -2166,3 +2191,47 @@ findWindowClause(List *wclist, const char *name) ...@@ -2166,3 +2191,47 @@ findWindowClause(List *wclist, const char *name)
return NULL; return NULL;
} }
/*
* transformFrameOffset
* Process a window frame offset expression
*/
static Node *
transformFrameOffset(ParseState *pstate, int frameOptions, Node *clause)
{
const char *constructName = NULL;
Node *node;
/* Quick exit if no offset expression */
if (clause == NULL)
return NULL;
/* Transform the raw expression tree */
node = transformExpr(pstate, clause);
if (frameOptions & FRAMEOPTION_ROWS)
{
/*
* Like LIMIT clause, simply coerce to int8
*/
constructName = "ROWS";
node = coerce_to_specific_type(pstate, node, INT8OID, constructName);
}
else if (frameOptions & FRAMEOPTION_RANGE)
{
/*
* this needs a lot of thought to decide how to support in the
* context of Postgres' extensible datatype framework
*/
constructName = "RANGE";
/* error was already thrown by gram.y, this is just a backstop */
elog(ERROR, "window frame with value offset is not implemented");
}
else
Assert(false);
/* Disallow variables and aggregates in frame offsets */
checkExprIsVarFree(pstate, node, constructName);
return node;
}
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.320 2010/01/21 06:11:45 itagaki Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.321 2010/02/12 17:33:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -3160,6 +3160,16 @@ get_rule_windowspec(WindowClause *wc, List *targetList, ...@@ -3160,6 +3160,16 @@ get_rule_windowspec(WindowClause *wc, List *targetList,
appendStringInfoString(buf, "UNBOUNDED PRECEDING "); appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
else if (wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW) else if (wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW)
appendStringInfoString(buf, "CURRENT ROW "); appendStringInfoString(buf, "CURRENT ROW ");
else if (wc->frameOptions & FRAMEOPTION_START_VALUE)
{
get_rule_expr(wc->startOffset, context, false);
if (wc->frameOptions & FRAMEOPTION_START_VALUE_PRECEDING)
appendStringInfoString(buf, " PRECEDING ");
else if (wc->frameOptions & FRAMEOPTION_START_VALUE_FOLLOWING)
appendStringInfoString(buf, " FOLLOWING ");
else
Assert(false);
}
else else
Assert(false); Assert(false);
if (wc->frameOptions & FRAMEOPTION_BETWEEN) if (wc->frameOptions & FRAMEOPTION_BETWEEN)
...@@ -3169,6 +3179,16 @@ get_rule_windowspec(WindowClause *wc, List *targetList, ...@@ -3169,6 +3179,16 @@ get_rule_windowspec(WindowClause *wc, List *targetList,
appendStringInfoString(buf, "UNBOUNDED FOLLOWING "); appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
else if (wc->frameOptions & FRAMEOPTION_END_CURRENT_ROW) else if (wc->frameOptions & FRAMEOPTION_END_CURRENT_ROW)
appendStringInfoString(buf, "CURRENT ROW "); appendStringInfoString(buf, "CURRENT ROW ");
else if (wc->frameOptions & FRAMEOPTION_END_VALUE)
{
get_rule_expr(wc->endOffset, context, false);
if (wc->frameOptions & FRAMEOPTION_END_VALUE_PRECEDING)
appendStringInfoString(buf, " PRECEDING ");
else if (wc->frameOptions & FRAMEOPTION_END_VALUE_FOLLOWING)
appendStringInfoString(buf, " FOLLOWING ");
else
Assert(false);
}
else else
Assert(false); Assert(false);
} }
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.583 2010/02/07 20:48:11 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.584 2010/02/12 17:33:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 201002071 #define CATALOG_VERSION_NO 201002121
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.217 2010/01/05 23:25:36 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.218 2010/02/12 17:33:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1595,22 +1595,35 @@ typedef struct WindowAggState ...@@ -1595,22 +1595,35 @@ typedef struct WindowAggState
FmgrInfo *ordEqfunctions; /* equality funcs for ordering columns */ FmgrInfo *ordEqfunctions; /* equality funcs for ordering columns */
Tuplestorestate *buffer; /* stores rows of current partition */ Tuplestorestate *buffer; /* stores rows of current partition */
int current_ptr; /* read pointer # for current */ int current_ptr; /* read pointer # for current */
int agg_ptr; /* read pointer # for aggregates */
int64 spooled_rows; /* total # of rows in buffer */ int64 spooled_rows; /* total # of rows in buffer */
int64 currentpos; /* position of current row in partition */ int64 currentpos; /* position of current row in partition */
int64 frameheadpos; /* current frame head position */
int64 frametailpos; /* current frame tail position */ int64 frametailpos; /* current frame tail position */
/* use struct pointer to avoid including windowapi.h here */
struct WindowObjectData *agg_winobj; /* winobj for aggregate fetches */
int64 aggregatedbase; /* start row for current aggregates */
int64 aggregatedupto; /* rows before this one are aggregated */ int64 aggregatedupto; /* rows before this one are aggregated */
MemoryContext wincontext; /* context for partition-lifespan data */ int frameOptions; /* frame_clause options, see WindowDef */
ExprState *startOffset; /* expression for starting bound offset */
ExprState *endOffset; /* expression for ending bound offset */
Datum startOffsetValue; /* result of startOffset evaluation */
Datum endOffsetValue; /* result of endOffset evaluation */
MemoryContext partcontext; /* context for partition-lifespan data */
MemoryContext aggcontext; /* context for each aggregate data */
ExprContext *tmpcontext; /* short-term evaluation context */ ExprContext *tmpcontext; /* short-term evaluation context */
bool all_first; /* true if the scan is starting */
bool all_done; /* true if the scan is finished */ bool all_done; /* true if the scan is finished */
bool partition_spooled; /* true if all tuples in current bool partition_spooled; /* true if all tuples in current
* partition have been spooled into * partition have been spooled into
* tuplestore */ * tuplestore */
bool more_partitions;/* true if there's more partitions after this bool more_partitions; /* true if there's more partitions after
* one */ * this one */
bool frametail_valid;/* true if frametailpos is known up to date bool framehead_valid; /* true if frameheadpos is known up to date
* for current row */
bool frametail_valid; /* true if frametailpos is known up to date
* for current row */ * for current row */
TupleTableSlot *first_part_slot; /* first tuple of current or next TupleTableSlot *first_part_slot; /* first tuple of current or next
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.428 2010/02/08 04:33:54 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.429 2010/02/12 17:33:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -393,6 +393,8 @@ typedef struct WindowDef ...@@ -393,6 +393,8 @@ typedef struct WindowDef
List *partitionClause; /* PARTITION BY expression list */ List *partitionClause; /* PARTITION BY expression list */
List *orderClause; /* ORDER BY (list of SortBy) */ List *orderClause; /* ORDER BY (list of SortBy) */
int frameOptions; /* frame_clause options, see below */ int frameOptions; /* frame_clause options, see below */
Node *startOffset; /* expression for starting bound, if any */
Node *endOffset; /* expression for ending bound, if any */
int location; /* parse location, or -1 if none/unknown */ int location; /* parse location, or -1 if none/unknown */
} WindowDef; } WindowDef;
...@@ -414,6 +416,15 @@ typedef struct WindowDef ...@@ -414,6 +416,15 @@ typedef struct WindowDef
#define FRAMEOPTION_END_UNBOUNDED_FOLLOWING 0x00080 /* end is U. F. */ #define FRAMEOPTION_END_UNBOUNDED_FOLLOWING 0x00080 /* end is U. F. */
#define FRAMEOPTION_START_CURRENT_ROW 0x00100 /* start is C. R. */ #define FRAMEOPTION_START_CURRENT_ROW 0x00100 /* start is C. R. */
#define FRAMEOPTION_END_CURRENT_ROW 0x00200 /* end is C. R. */ #define FRAMEOPTION_END_CURRENT_ROW 0x00200 /* end is C. R. */
#define FRAMEOPTION_START_VALUE_PRECEDING 0x00400 /* start is V. P. */
#define FRAMEOPTION_END_VALUE_PRECEDING 0x00800 /* end is V. P. */
#define FRAMEOPTION_START_VALUE_FOLLOWING 0x01000 /* start is V. F. */
#define FRAMEOPTION_END_VALUE_FOLLOWING 0x02000 /* end is V. F. */
#define FRAMEOPTION_START_VALUE \
(FRAMEOPTION_START_VALUE_PRECEDING | FRAMEOPTION_START_VALUE_FOLLOWING)
#define FRAMEOPTION_END_VALUE \
(FRAMEOPTION_END_VALUE_PRECEDING | FRAMEOPTION_END_VALUE_FOLLOWING)
#define FRAMEOPTION_DEFAULTS \ #define FRAMEOPTION_DEFAULTS \
(FRAMEOPTION_RANGE | FRAMEOPTION_START_UNBOUNDED_PRECEDING | \ (FRAMEOPTION_RANGE | FRAMEOPTION_START_UNBOUNDED_PRECEDING | \
...@@ -799,6 +810,8 @@ typedef struct WindowClause ...@@ -799,6 +810,8 @@ typedef struct WindowClause
List *partitionClause; /* PARTITION BY list */ List *partitionClause; /* PARTITION BY list */
List *orderClause; /* ORDER BY list */ List *orderClause; /* ORDER BY list */
int frameOptions; /* frame_clause options, see WindowDef */ int frameOptions; /* frame_clause options, see WindowDef */
Node *startOffset; /* expression for starting bound, if any */
Node *endOffset; /* expression for ending bound, if any */
Index winref; /* ID referenced by window functions */ Index winref; /* ID referenced by window functions */
bool copiedOrder; /* did we copy orderClause from refname? */ bool copiedOrder; /* did we copy orderClause from refname? */
} WindowClause; } WindowClause;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.115 2010/01/02 16:58:04 momjian Exp $ * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.116 2010/02/12 17:33:21 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -552,6 +552,8 @@ typedef struct WindowAgg ...@@ -552,6 +552,8 @@ typedef struct WindowAgg
AttrNumber *ordColIdx; /* their indexes in the target list */ AttrNumber *ordColIdx; /* their indexes in the target list */
Oid *ordOperators; /* equality operators for ordering columns */ Oid *ordOperators; /* equality operators for ordering columns */
int frameOptions; /* frame_clause options, see WindowDef */ int frameOptions; /* frame_clause options, see WindowDef */
Node *startOffset; /* expression for starting bound, if any */
Node *endOffset; /* expression for ending bound, if any */
} WindowAgg; } WindowAgg;
/* ---------------- /* ----------------
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.124 2010/01/15 22:36:35 tgl Exp $ * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.125 2010/02/12 17:33:21 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -62,7 +62,8 @@ extern WindowAgg *make_windowagg(PlannerInfo *root, List *tlist, ...@@ -62,7 +62,8 @@ extern WindowAgg *make_windowagg(PlannerInfo *root, List *tlist,
int numWindowFuncs, Index winref, int numWindowFuncs, Index winref,
int partNumCols, AttrNumber *partColIdx, Oid *partOperators, int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
int frameOptions, Plan *lefttree); int frameOptions, Node *startOffset, Node *endOffset,
Plan *lefttree);
extern Group *make_group(PlannerInfo *root, List *tlist, List *qual, extern Group *make_group(PlannerInfo *root, List *tlist, List *qual,
int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators, int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
double numGroups, double numGroups,
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/include/parser/kwlist.h,v 1.11 2010/02/08 04:33:55 tgl Exp $ * $PostgreSQL: pgsql/src/include/parser/kwlist.h,v 1.12 2010/02/12 17:33:21 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,7 +53,7 @@ PG_KEYWORD("authorization", AUTHORIZATION, TYPE_FUNC_NAME_KEYWORD) ...@@ -53,7 +53,7 @@ PG_KEYWORD("authorization", AUTHORIZATION, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("backward", BACKWARD, UNRESERVED_KEYWORD) PG_KEYWORD("backward", BACKWARD, UNRESERVED_KEYWORD)
PG_KEYWORD("before", BEFORE, UNRESERVED_KEYWORD) PG_KEYWORD("before", BEFORE, UNRESERVED_KEYWORD)
PG_KEYWORD("begin", BEGIN_P, UNRESERVED_KEYWORD) PG_KEYWORD("begin", BEGIN_P, UNRESERVED_KEYWORD)
PG_KEYWORD("between", BETWEEN, TYPE_FUNC_NAME_KEYWORD) PG_KEYWORD("between", BETWEEN, COL_NAME_KEYWORD)
PG_KEYWORD("bigint", BIGINT, COL_NAME_KEYWORD) PG_KEYWORD("bigint", BIGINT, COL_NAME_KEYWORD)
PG_KEYWORD("binary", BINARY, TYPE_FUNC_NAME_KEYWORD) PG_KEYWORD("binary", BINARY, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("bit", BIT, COL_NAME_KEYWORD) PG_KEYWORD("bit", BIT, COL_NAME_KEYWORD)
......
...@@ -728,6 +728,193 @@ FROM (select distinct ten, four from tenk1) ss; ...@@ -728,6 +728,193 @@ FROM (select distinct ten, four from tenk1) ss;
3 | 2 | 4 | 2 3 | 2 | 4 | 2
(20 rows) (20 rows)
SELECT sum(unique1) over (order by four range between current row and unbounded following),
unique1, four
FROM tenk1 WHERE unique1 < 10;
sum | unique1 | four
-----+---------+------
45 | 0 | 0
45 | 8 | 0
45 | 4 | 0
33 | 5 | 1
33 | 9 | 1
33 | 1 | 1
18 | 6 | 2
18 | 2 | 2
10 | 3 | 3
10 | 7 | 3
(10 rows)
SELECT sum(unique1) over (rows between current row and unbounded following),
unique1, four
FROM tenk1 WHERE unique1 < 10;
sum | unique1 | four
-----+---------+------
45 | 4 | 0
41 | 2 | 2
39 | 1 | 1
38 | 6 | 2
32 | 9 | 1
23 | 8 | 0
15 | 5 | 1
10 | 3 | 3
7 | 7 | 3
0 | 0 | 0
(10 rows)
SELECT sum(unique1) over (rows between 2 preceding and 2 following),
unique1, four
FROM tenk1 WHERE unique1 < 10;
sum | unique1 | four
-----+---------+------
7 | 4 | 0
13 | 2 | 2
22 | 1 | 1
26 | 6 | 2
29 | 9 | 1
31 | 8 | 0
32 | 5 | 1
23 | 3 | 3
15 | 7 | 3
10 | 0 | 0
(10 rows)
SELECT sum(unique1) over (rows between 2 preceding and 1 preceding),
unique1, four
FROM tenk1 WHERE unique1 < 10;
sum | unique1 | four
-----+---------+------
| 4 | 0
4 | 2 | 2
6 | 1 | 1
3 | 6 | 2
7 | 9 | 1
15 | 8 | 0
17 | 5 | 1
13 | 3 | 3
8 | 7 | 3
10 | 0 | 0
(10 rows)
SELECT sum(unique1) over (rows between 1 following and 3 following),
unique1, four
FROM tenk1 WHERE unique1 < 10;
sum | unique1 | four
-----+---------+------
9 | 4 | 0
16 | 2 | 2
23 | 1 | 1
22 | 6 | 2
16 | 9 | 1
15 | 8 | 0
10 | 5 | 1
7 | 3 | 3
0 | 7 | 3
| 0 | 0
(10 rows)
SELECT sum(unique1) over (rows between unbounded preceding and 1 following),
unique1, four
FROM tenk1 WHERE unique1 < 10;
sum | unique1 | four
-----+---------+------
6 | 4 | 0
7 | 2 | 2
13 | 1 | 1
22 | 6 | 2
30 | 9 | 1
35 | 8 | 0
38 | 5 | 1
45 | 3 | 3
45 | 7 | 3
45 | 0 | 0
(10 rows)
SELECT sum(unique1) over (w range between current row and unbounded following),
unique1, four
FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four);
sum | unique1 | four
-----+---------+------
45 | 0 | 0
45 | 8 | 0
45 | 4 | 0
33 | 5 | 1
33 | 9 | 1
33 | 1 | 1
18 | 6 | 2
18 | 2 | 2
10 | 3 | 3
10 | 7 | 3
(10 rows)
-- fail: not implemented yet
SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding),
unique1, four
FROM tenk1 WHERE unique1 < 10;
ERROR: RANGE PRECEDING is only supported with UNBOUNDED
LINE 1: SELECT sum(unique1) over (order by four range between 2::int...
^
SELECT first_value(unique1) over w,
nth_value(unique1, 2) over w AS nth_2,
last_value(unique1) over w, unique1, four
FROM tenk1 WHERE unique1 < 10
WINDOW w AS (order by four range between current row and unbounded following);
first_value | nth_2 | last_value | unique1 | four
-------------+-------+------------+---------+------
0 | 8 | 7 | 0 | 0
0 | 8 | 7 | 8 | 0
0 | 8 | 7 | 4 | 0
5 | 9 | 7 | 5 | 1
5 | 9 | 7 | 9 | 1
5 | 9 | 7 | 1 | 1
6 | 2 | 7 | 6 | 2
6 | 2 | 7 | 2 | 2
3 | 7 | 7 | 3 | 3
3 | 7 | 7 | 7 | 3
(10 rows)
SELECT sum(unique1) over
(rows (SELECT unique1 FROM tenk1 ORDER BY unique1 LIMIT 1) + 1 PRECEDING),
unique1
FROM tenk1 WHERE unique1 < 10;
sum | unique1
-----+---------
4 | 4
6 | 2
3 | 1
7 | 6
15 | 9
17 | 8
13 | 5
8 | 3
10 | 7
7 | 0
(10 rows)
CREATE TEMP VIEW v_window AS
SELECT i, sum(i) over (order by i rows between 1 preceding and 1 following) as sum_rows
FROM generate_series(1, 10) i;
SELECT * FROM v_window;
i | sum_rows
----+----------
1 | 3
2 | 6
3 | 9
4 | 12
5 | 15
6 | 18
7 | 21
8 | 24
9 | 27
10 | 19
(10 rows)
SELECT pg_get_viewdef('v_window');
pg_get_viewdef
---------------------------------------------------------------------------------------------------------------------------------
SELECT i.i, sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_rows FROM generate_series(1, 10) i(i);
(1 row)
-- with UNION -- with UNION
SELECT count(*) OVER (PARTITION BY four) FROM (SELECT * FROM tenk1 UNION ALL SELECT * FROM tenk2)s LIMIT 0; SELECT count(*) OVER (PARTITION BY four) FROM (SELECT * FROM tenk1 UNION ALL SELECT * FROM tenk2)s LIMIT 0;
count count
......
# ---------- # ----------
# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.60 2010/02/07 22:40:33 tgl Exp $ # $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.61 2010/02/12 17:33:21 tgl Exp $
# #
# By convention, we put no more than twenty tests in any one parallel group; # By convention, we put no more than twenty tests in any one parallel group;
# this limits the number of connections needed to run the tests. # this limits the number of connections needed to run the tests.
...@@ -78,18 +78,19 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi ...@@ -78,18 +78,19 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi
test: privileges test: privileges
test: misc test: misc
# rules cannot run concurrently with any test that creates a view
test: rules
# ---------- # ----------
# Another group of parallel tests # Another group of parallel tests
# ---------- # ----------
test: select_views portals_p2 rules foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap
# ---------- # ----------
# Another group of parallel tests # Another group of parallel tests
# NB: temp.sql does a reconnect which transiently uses 2 connections, # NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests # so keep this parallel group to at most 19 tests
# ---------- # ----------
# "plpgsql" cannot run concurrently with "rules", nor can "plancache"
test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
# run stats by itself because its delay may be insufficient under heavy load # run stats by itself because its delay may be insufficient under heavy load
......
# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.55 2010/01/28 23:21:13 petere Exp $ # $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.56 2010/02/12 17:33:21 tgl Exp $
# This should probably be in an order similar to parallel_schedule. # This should probably be in an order similar to parallel_schedule.
test: tablespace test: tablespace
test: boolean test: boolean
...@@ -89,9 +89,9 @@ test: namespace ...@@ -89,9 +89,9 @@ test: namespace
test: prepared_xacts test: prepared_xacts
test: privileges test: privileges
test: misc test: misc
test: rules
test: select_views test: select_views
test: portals_p2 test: portals_p2
test: rules
test: foreign_key test: foreign_key
test: cluster test: cluster
test: dependency test: dependency
......
...@@ -161,6 +161,58 @@ SELECT four, ten/4 as two, ...@@ -161,6 +161,58 @@ SELECT four, ten/4 as two,
last_value(ten/4) over (partition by four order by ten/4 rows between unbounded preceding and current row) last_value(ten/4) over (partition by four order by ten/4 rows between unbounded preceding and current row)
FROM (select distinct ten, four from tenk1) ss; FROM (select distinct ten, four from tenk1) ss;
SELECT sum(unique1) over (order by four range between current row and unbounded following),
unique1, four
FROM tenk1 WHERE unique1 < 10;
SELECT sum(unique1) over (rows between current row and unbounded following),
unique1, four
FROM tenk1 WHERE unique1 < 10;
SELECT sum(unique1) over (rows between 2 preceding and 2 following),
unique1, four
FROM tenk1 WHERE unique1 < 10;
SELECT sum(unique1) over (rows between 2 preceding and 1 preceding),
unique1, four
FROM tenk1 WHERE unique1 < 10;
SELECT sum(unique1) over (rows between 1 following and 3 following),
unique1, four
FROM tenk1 WHERE unique1 < 10;
SELECT sum(unique1) over (rows between unbounded preceding and 1 following),
unique1, four
FROM tenk1 WHERE unique1 < 10;
SELECT sum(unique1) over (w range between current row and unbounded following),
unique1, four
FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four);
-- fail: not implemented yet
SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding),
unique1, four
FROM tenk1 WHERE unique1 < 10;
SELECT first_value(unique1) over w,
nth_value(unique1, 2) over w AS nth_2,
last_value(unique1) over w, unique1, four
FROM tenk1 WHERE unique1 < 10
WINDOW w AS (order by four range between current row and unbounded following);
SELECT sum(unique1) over
(rows (SELECT unique1 FROM tenk1 ORDER BY unique1 LIMIT 1) + 1 PRECEDING),
unique1
FROM tenk1 WHERE unique1 < 10;
CREATE TEMP VIEW v_window AS
SELECT i, sum(i) over (order by i rows between 1 preceding and 1 following) as sum_rows
FROM generate_series(1, 10) i;
SELECT * FROM v_window;
SELECT pg_get_viewdef('v_window');
-- with UNION -- with UNION
SELECT count(*) OVER (PARTITION BY four) FROM (SELECT * FROM tenk1 UNION ALL SELECT * FROM tenk2)s LIMIT 0; SELECT count(*) OVER (PARTITION BY four) FROM (SELECT * FROM tenk1 UNION ALL SELECT * FROM tenk2)s LIMIT 0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment