Commit 95b07bc7 authored by Tom Lane's avatar Tom Lane

Support window functions a la SQL:2008.

Hitoshi Harada, with some kibitzing from Heikki and Tom.
parent 38e93482
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/contrib/tsearch2/tsearch2.c,v 1.6 2008/03/25 22:42:42 tgl Exp $ * $PostgreSQL: pgsql/contrib/tsearch2/tsearch2.c,v 1.7 2008/12/28 18:53:53 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -410,7 +410,15 @@ tsa_rewrite_accum(PG_FUNCTION_ARGS) ...@@ -410,7 +410,15 @@ tsa_rewrite_accum(PG_FUNCTION_ARGS)
MemoryContext aggcontext; MemoryContext aggcontext;
MemoryContext oldcontext; MemoryContext oldcontext;
if (fcinfo->context && IsA(fcinfo->context, AggState))
aggcontext = ((AggState *) fcinfo->context)->aggcontext; aggcontext = ((AggState *) fcinfo->context)->aggcontext;
else if (fcinfo->context && IsA(fcinfo->context, WindowAggState))
aggcontext = ((WindowAggState *) fcinfo->context)->wincontext;
else
{
elog(ERROR, "tsa_rewrite_accum called in non-aggregate context");
aggcontext = NULL; /* keep compiler quiet */
}
if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL) if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL)
{ {
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/advanced.sgml,v 1.54 2007/02/01 00:28:16 momjian Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/advanced.sgml,v 1.55 2008/12/28 18:53:53 tgl Exp $ -->
<chapter id="tutorial-advanced"> <chapter id="tutorial-advanced">
<title>Advanced Features</title> <title>Advanced Features</title>
...@@ -317,6 +317,242 @@ COMMIT; ...@@ -317,6 +317,242 @@ COMMIT;
</sect1> </sect1>
<sect1 id="tutorial-window">
<title id="tutorial-window-title">Window Functions</title>
<indexterm zone="tutorial-window">
<primary>window function</primary>
</indexterm>
<para>
A <firstterm>window function</> performs a calculation across a set of
table rows that are somehow related to the current row. This is comparable
to the type of calculation that can be done with an aggregate function.
But unlike regular aggregate functions, use of a window function does not
cause rows to become grouped into a single output row &mdash; the
rows retain their separate identities. Behind the scenes, the window
function is able to access more than just the current row of the query
result.
</para>
<para>
Here is an example that shows how to compare each employee's salary
with the average salary in his or her department:
<programlisting>
SELECT depname, empno, salary, avg(salary) OVER (PARTITION BY depname) FROM empsalary;
</programlisting>
<screen>
depname | empno | salary | avg
-----------+-------+--------+-----------------------
develop | 11 | 5200 | 5020.0000000000000000
develop | 7 | 4200 | 5020.0000000000000000
develop | 9 | 4500 | 5020.0000000000000000
develop | 8 | 6000 | 5020.0000000000000000
develop | 10 | 5200 | 5020.0000000000000000
personnel | 5 | 3500 | 3700.0000000000000000
personnel | 2 | 3900 | 3700.0000000000000000
sales | 3 | 4800 | 4866.6666666666666667
sales | 1 | 5000 | 4866.6666666666666667
sales | 4 | 4800 | 4866.6666666666666667
(10 rows)
</screen>
The first three output columns come directly from the table
<structname>empsalary</>, and there is one output row for each row in the
table. The fourth column represents an average taken across all the table
rows that have the same <structfield>depname</> value as the current row.
(This actually is the same function as the regular <function>avg</>
aggregate function, but the <literal>OVER</> clause causes it to be
treated as a window function and computed across an appropriate set of
rows.)
</para>
<para>
A window function call always contains an <literal>OVER</> clause
following the window function's name and argument(s). This is what
syntactically distinguishes it from a regular function or aggregate
function. The <literal>OVER</> clause determines exactly how the
rows of the query are split up for processing by the window function.
The <literal>PARTITION BY</> list within <literal>OVER</> specifies
dividing the rows into groups, or partitions, that share the same
values of the <literal>PARTITION BY</> expression(s). For each row,
the window function is computed across the rows that fall into the
same partition as the current row.
</para>
<para>
Although <function>avg</> will produce the same result no matter
what order it processes the partition's rows in, this is not true of all
window functions. When needed, you can control that order using
<literal>ORDER BY</> within <literal>OVER</>. Here is an example:
<programlisting>
SELECT depname, empno, salary, rank() OVER (PARTITION BY depname ORDER BY salary DESC) FROM empsalary;
</programlisting>
<screen>
depname | empno | salary | rank
-----------+-------+--------+------
develop | 8 | 6000 | 1
develop | 10 | 5200 | 2
develop | 11 | 5200 | 2
develop | 9 | 4500 | 4
develop | 7 | 4200 | 5
personnel | 2 | 3900 | 1
personnel | 5 | 3500 | 2
sales | 1 | 5000 | 1
sales | 4 | 4800 | 2
sales | 3 | 4800 | 2
(10 rows)
</screen>
As shown here, the <function>rank</> function produces a numerical rank
within the current row's partition for each distinct <literal>ORDER BY</>
value, in the order defined by the <literal>ORDER BY</> clause.
<function>rank</> needs no explicit parameter, because its behavior
is entirely determined by the <literal>OVER</> clause.
</para>
<para>
The rows considered by a window function are those of the <quote>virtual
table</> produced by the query's <literal>FROM</> clause as filtered by its
<literal>WHERE</>, <literal>GROUP BY</>, and <literal>HAVING</> clauses
if any. For example, a row removed because it does not meet the
<literal>WHERE</> condition is not seen by any window function.
A query can contain multiple window functions that slice up the data
in different ways by means of different <literal>OVER</> clauses, but
they all act on the same collection of rows defined by this virtual table.
</para>
<para>
We already saw that <literal>ORDER BY</> can be omitted if the ordering
of rows is not important. It is also possible to omit <literal>PARTITION
BY</>, in which case the window function is computed over all rows of the
virtual table; that is, there is one partition containing all the rows.
</para>
<para>
There is another important concept associated with window functions:
for each row, there is a set of rows within its partition called its
<firstterm>window frame</>. When <literal>ORDER BY</> is omitted the
frame is always the same as the partition. If <literal>ORDER BY</> is
supplied, the frame consists of all rows from the start of the partition
up to the current row, plus any following rows that are equal to the
current row according to the <literal>ORDER BY</> clause.
<footnote>
<para>
The SQL standard includes options to define the window frame in
other ways, but this definition is the only one currently supported
by <productname>PostgreSQL</productname>.
</para>
</footnote>
Many window functions act only on the rows of the window frame, rather
than of the whole partition. Here is an example using <function>sum</>:
</para>
<programlisting>
SELECT salary, sum(salary) OVER () FROM empsalary;
</programlisting>
<screen>
salary | sum
--------+-------
5200 | 47100
5000 | 47100
3500 | 47100
4800 | 47100
3900 | 47100
4200 | 47100
4500 | 47100
4800 | 47100
6000 | 47100
5200 | 47100
(10 rows)
</screen>
<para>
Above, since there is no <literal>ORDER BY</> in the <literal>OVER</>
clause, the window frame is the same as the partition, which for lack of
<literal>PARTITION BY</> is the whole table; in other words each sum is
taken over the whole table and so we get the same result for each output
row. But if we add an <literal>ORDER BY</> clause, we get very different
results:
</para>
<programlisting>
SELECT salary, sum(salary) OVER (ORDER BY salary) FROM empsalary;
</programlisting>
<screen>
salary | sum
--------+-------
3500 | 3500
3900 | 7400
4200 | 11600
4500 | 16100
4800 | 25700
4800 | 25700
5000 | 30700
5200 | 41100
5200 | 41100
6000 | 47100
(10 rows)
</screen>
<para>
Here the sum is taken from the first (lowest) salary up through the
current one, including any duplicates of the current one (notice the
results for the duplicated salaries).
</para>
<para>
Window functions are permitted only in the <literal>SELECT</literal> list
and the <literal>ORDER BY</> clause of the query. They are forbidden
elsewhere, such as in <literal>GROUP BY</>, <literal>HAVING</>
and <literal>WHERE</literal> clauses. This is because they logically
execute after the processing of those clauses. Also, window functions
execute after regular aggregate functions. This means it is valid to
include an aggregate function call in the arguments of a window function,
but not vice versa.
</para>
<para>
If there is a need to filter or group rows after the window calculations
are performed, you can use a sub-select. For example:
<programlisting>
SELECT depname, empno, salary, enroll_date
FROM
(SELECT depname, empno, salary, enroll_date,
rank() OVER (PARTITION BY depname ORDER BY salary DESC, empno) AS pos
FROM empsalary
) AS ss
WHERE pos < 3;
</programlisting>
The above query only shows the rows from the inner query having
<literal>rank</> less than <literal>3</>.
</para>
<para>
When a query involves multiple window functions, it is possible to write
out each one with a separate <literal>OVER</> clause, but this is
duplicative and error-prone if the same windowing behavior is wanted
for several functions. Instead, each windowing behavior can be named
in a <literal>WINDOW</> clause and then referenced in <literal>OVER</>.
For example:
<programlisting>
SELECT sum(salary) OVER w, avg(salary) OVER w
FROM empsalary
WINDOW w AS (PARTITION BY depname ORDER BY salary DESC);
</programlisting>
</para>
</sect1>
<sect1 id="tutorial-inheritance"> <sect1 id="tutorial-inheritance">
<title>Inheritance</title> <title>Inheritance</title>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/errcodes.sgml,v 1.25 2008/10/04 21:56:52 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/errcodes.sgml,v 1.26 2008/12/28 18:53:53 tgl Exp $ -->
<appendix id="errcodes-appendix"> <appendix id="errcodes-appendix">
<title><productname>PostgreSQL</productname> Error Codes</title> <title><productname>PostgreSQL</productname> Error Codes</title>
...@@ -378,6 +378,18 @@ ...@@ -378,6 +378,18 @@
<entry>invalid_argument_for_logarithm</entry> <entry>invalid_argument_for_logarithm</entry>
</row> </row>
<row>
<entry><literal>22014</literal></entry>
<entry>INVALID ARGUMENT FOR NTILE FUNCTION</entry>
<entry>invalid_argument_for_ntile_function</entry>
</row>
<row>
<entry><literal>22016</literal></entry>
<entry>INVALID ARGUMENT FOR NTH_VALUE FUNCTION</entry>
<entry>invalid_argument_for_nth_value_function</entry>
</row>
<row> <row>
<entry><literal>2201F</literal></entry> <entry><literal>2201F</literal></entry>
<entry>INVALID ARGUMENT FOR POWER FUNCTION</entry> <entry>INVALID ARGUMENT FOR POWER FUNCTION</entry>
...@@ -990,6 +1002,12 @@ ...@@ -990,6 +1002,12 @@
<entry>grouping_error</entry> <entry>grouping_error</entry>
</row> </row>
<row>
<entry><literal>42P20</literal></entry>
<entry>WINDOWING ERROR</entry>
<entry>windowing_error</entry>
</row>
<row> <row>
<entry><literal>42P19</literal></entry> <entry><literal>42P19</literal></entry>
<entry>INVALID RECURSION</entry> <entry>INVALID RECURSION</entry>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.463 2008/12/19 16:25:16 petere Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.464 2008/12/28 18:53:53 tgl Exp $ -->
<chapter id="functions"> <chapter id="functions">
<title>Functions and Operators</title> <title>Functions and Operators</title>
...@@ -10149,6 +10149,278 @@ SELECT xmlagg(x) FROM (SELECT x FROM test ORDER BY y DESC) AS tab; ...@@ -10149,6 +10149,278 @@ SELECT xmlagg(x) FROM (SELECT x FROM test ORDER BY y DESC) AS tab;
</sect1> </sect1>
<sect1 id="functions-window">
<title>Window Functions</title>
<indexterm zone="functions-window">
<primary>window function</primary>
<secondary>built-in</secondary>
</indexterm>
<para>
<firstterm>Window functions</firstterm> provide the ability to perform
calculations across sets of rows that are related to the current query
row. For information about this feature see
<xref linkend="tutorial-window"> and
<xref linkend="syntax-window-functions">.
</para>
<para>
The built-in window functions are listed in
<xref linkend="functions-window-table">. Note that these functions
<emphasis>must</> be invoked using window function syntax; that is an
<literal>OVER</> clause is required.
</para>
<para>
In addition to these functions, any built-in or user-defined aggregate
function can be used as a window function (see
<xref linkend="functions-aggregate"> for a list of the built-in aggregates).
Aggregate functions act as window functions only when an <literal>OVER</>
clause follows the call; otherwise they act as regular aggregates.
</para>
<table id="functions-window-table">
<title>General-Purpose Window Functions</title>
<tgroup cols="3">
<thead>
<row>
<entry>Function</entry>
<entry>Return Type</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<indexterm>
<primary>row_number</primary>
</indexterm>
<function>row_number()</function>
</entry>
<entry>
<type>bigint</type>
</entry>
<entry>number of the current row within its partition, counting from 1</entry>
</row>
<row>
<entry>
<indexterm>
<primary>rank</primary>
</indexterm>
<function>rank()</function>
</entry>
<entry>
<type>bigint</type>
</entry>
<entry>rank of the current row with gaps; same as <function>row_number</> of its first peer</entry>
</row>
<row>
<entry>
<indexterm>
<primary>dense_rank</primary>
</indexterm>
<function>dense_rank()</function>
</entry>
<entry>
<type>bigint</type>
</entry>
<entry>rank of the current row without gaps; this function counts peer groups</entry>
</row>
<row>
<entry>
<indexterm>
<primary>percent_rank</primary>
</indexterm>
<function>percent_rank()</function>
</entry>
<entry>
<type>double precision</type>
</entry>
<entry>relative rank of the current row: (<function>rank</> - 1) / (total rows - 1)</entry>
</row>
<row>
<entry>
<indexterm>
<primary>cume_dist</primary>
</indexterm>
<function>cume_dist()</function>
</entry>
<entry>
<type>double precision</type>
</entry>
<entry>relative rank of the current row: (number of rows preceding or peer with current row) / (total rows)</entry>
</row>
<row>
<entry>
<indexterm>
<primary>ntile</primary>
</indexterm>
<function>ntile(<replaceable class="parameter">num_buckets</replaceable> <type>integer</>)</function>
</entry>
<entry>
<type>integer</type>
</entry>
<entry>integer ranging from 1 to the argument value, dividing the
partition as equally as possible</entry>
</row>
<row>
<entry>
<indexterm>
<primary>lag</primary>
</indexterm>
<function>
lag(<replaceable class="parameter">value</replaceable> <type>any</>
[, <replaceable class="parameter">offset</replaceable> <type>integer</>
[, <replaceable class="parameter">default</replaceable> <type>any</> ]])
</function>
</entry>
<entry>
<type>same type as <replaceable class="parameter">value</replaceable></type>
</entry>
<entry>
returns <replaceable class="parameter">value</replaceable> evaluated at
the row that is <replaceable class="parameter">offset</replaceable>
rows before the current row within the partition; if there is no such
row, instead return <replaceable class="parameter">default</replaceable>.
Both <replaceable class="parameter">offset</replaceable> and
<replaceable class="parameter">default</replaceable> are evaluated
with respect to the current row. If omitted,
<replaceable class="parameter">offset</replaceable> defaults to 1 and
<replaceable class="parameter">default</replaceable> to null
</entry>
</row>
<row>
<entry>
<indexterm>
<primary>lead</primary>
</indexterm>
<function>
lead(<replaceable class="parameter">value</replaceable> <type>any</>
[, <replaceable class="parameter">offset</replaceable> <type>integer</>
[, <replaceable class="parameter">default</replaceable> <type>any</> ]])
</function>
</entry>
<entry>
<type>same type as <replaceable class="parameter">value</replaceable></type>
</entry>
<entry>
returns <replaceable class="parameter">value</replaceable> evaluated at
the row that is <replaceable class="parameter">offset</replaceable>
rows after the current row within the partition; if there is no such
row, instead return <replaceable class="parameter">default</replaceable>.
Both <replaceable class="parameter">offset</replaceable> and
<replaceable class="parameter">default</replaceable> are evaluated
with respect to the current row. If omitted,
<replaceable class="parameter">offset</replaceable> defaults to 1 and
<replaceable class="parameter">default</replaceable> to null
</entry>
</row>
<row>
<entry>
<indexterm>
<primary>first_value</primary>
</indexterm>
<function>first_value(<replaceable class="parameter">value</replaceable> <type>any</>)</function>
</entry>
<entry>
<type>same type as <replaceable class="parameter">value</replaceable></type>
</entry>
<entry>
returns <replaceable class="parameter">value</replaceable> evaluated
at the row that is the first row of the window frame
</entry>
</row>
<row>
<entry>
<indexterm>
<primary>last_value</primary>
</indexterm>
<function>last_value(<replaceable class="parameter">value</replaceable> <type>any</>)</function>
</entry>
<entry>
<type>same type as <replaceable class="parameter">value</replaceable></type>
</entry>
<entry>
returns <replaceable class="parameter">value</replaceable> evaluated
at the row that is the last row of the window frame
</entry>
</row>
<row>
<entry>
<indexterm>
<primary>nth_value</primary>
</indexterm>
<function>
nth_value(<replaceable class="parameter">value</replaceable> <type>any</>, <replaceable class="parameter">nth</replaceable> <type>integer</>)
</function>
</entry>
<entry>
<type>same type as <replaceable class="parameter">value</replaceable></type>
</entry>
<entry>
returns <replaceable class="parameter">value</replaceable> evaluated
at the row that is the <replaceable class="parameter">nth</replaceable>
row of the window frame (counting from 1); null if no such row
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
All of the functions listed in
<xref linkend="functions-window-table"> depend on the sort ordering
specified by the <literal>ORDER BY</> clause of the associated window
definition. Rows that are not distinct in the <literal>ORDER BY</>
ordering are said to be <firstterm>peers</>; the four ranking functions
are defined so that they give the same answer for any two peer rows.
</para>
<para>
Note that <function>first_value</>, <function>last_value</>, and
<function>nth_value</> consider only the rows within the <quote>window
frame</>, that is the rows from the start of the partition through the
last peer of the current row. This is particularly likely to give
unintuitive results for <function>last_value</>.
</para>
<para>
When an aggregate function is used as a window function, it aggregates
over the rows within the current row's window frame. To obtain
aggregation over the whole partition, be sure to omit <literal>ORDER BY</>
from the window definition. An aggregate used with <literal>ORDER BY</>
produces a <quote>running sum</> type of behavior, which may or may not
be what's wanted.
</para>
<note>
<para>
The SQL standard defines a <literal>RESPECT NULLS</> or
<literal>IGNORE NULLS</> option for <function>lead</>, <function>lag</>,
<function>first_value</>, <function>last_value</>, and
<function>nth_value</>. This is not implemented in
<productname>PostgreSQL</productname>: the behavior is always the
same as the standard's default, namely <literal>RESPECT NULLS</>.
Likewise, the standard's <literal>FROM FIRST</> or <literal>FROM LAST</>
option for <function>nth_value</> is not implemented: only the
default <literal>FROM FIRST</> behavior is supported.
</para>
</note>
</sect1>
<sect1 id="functions-subquery"> <sect1 id="functions-subquery">
<title>Subquery Expressions</title> <title>Subquery Expressions</title>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/queries.sgml,v 1.50 2008/10/14 00:41:34 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/queries.sgml,v 1.51 2008/12/28 18:53:54 tgl Exp $ -->
<chapter id="queries"> <chapter id="queries">
<title>Queries</title> <title>Queries</title>
...@@ -949,6 +949,57 @@ SELECT product_id, p.name, (sum(s.units) * (p.price - p.cost)) AS profit ...@@ -949,6 +949,57 @@ SELECT product_id, p.name, (sum(s.units) * (p.price - p.cost)) AS profit
5000. Note that the aggregate expressions do not necessarily need 5000. Note that the aggregate expressions do not necessarily need
to be the same in all parts of the query. to be the same in all parts of the query.
</para> </para>
<para>
If a query contains aggregate function calls, but no <literal>GROUP BY</>
clause, grouping still occurs: the result is a single group row (or
perhaps no rows at all, if the single row is then eliminated by
<literal>HAVING</>).
The same is true if it contains a <literal>HAVING</> clause, even
without any aggregate function calls or <literal>GROUP BY</> clause.
</para>
</sect2>
<sect2 id="queries-window">
<title>Window Function Processing</>
<indexterm zone="queries-window">
<primary>window function</primary>
<secondary>order of execution</>
</indexterm>
<para>
If the query contains any window functions (see
<xref linkend="tutorial-window"> and
<xref linkend="syntax-window-functions">), these functions are evaluated
after any grouping, aggregation, and <literal>HAVING</> filtering is
performed. That is, if the query uses any aggregates, <literal>GROUP
BY</>, or <literal>HAVING</>, then the rows seen by the window functions
are the group rows instead of the original table rows from
<literal>FROM</>/<literal>WHERE</>.
</para>
<para>
When multiple window functions are used, all the window functions having
syntactically equivalent <literal>PARTITION BY</> and <literal>ORDER BY</>
clauses in their window definitions are guaranteed to be evaluated in a
single pass over the data. Therefore they will see the same sort ordering,
even if the <literal>ORDER BY</> does not uniquely determine an ordering.
However, no guarantees are made about the evaluation of functions having
different <literal>PARTITION BY</> or <literal>ORDER BY</> specifications.
(In such cases a sort step is typically required between the passes of
window function evaluations, and the sort is not guaranteed to preserve
ordering of rows that its <literal>ORDER BY</> sees as equivalent.)
</para>
<para>
Currently, use of window functions always forces sorting, and so the
query output will be ordered according to one or another of the window
functions' <literal>PARTITION BY</>/<literal>ORDER BY</> clauses.
It is not recommendable to rely on this, however. Use an explicit
top-level <literal>ORDER BY</> clause if you want to be sure the
results are sorted in a particular way.
</para>
</sect2> </sect2>
</sect1> </sect1>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/query.sgml,v 1.50 2007/02/01 00:28:17 momjian Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/query.sgml,v 1.51 2008/12/28 18:53:54 tgl Exp $ -->
<chapter id="tutorial-sql"> <chapter id="tutorial-sql">
<title>The <acronym>SQL</acronym> Language</title> <title>The <acronym>SQL</acronym> Language</title>
...@@ -653,7 +653,7 @@ SELECT * ...@@ -653,7 +653,7 @@ SELECT *
Like most other relational database products, Like most other relational database products,
<productname>PostgreSQL</productname> supports <productname>PostgreSQL</productname> supports
aggregate functions. <firstterm>aggregate functions</>.
An aggregate function computes a single result from multiple input rows. An aggregate function computes a single result from multiple input rows.
For example, there are aggregates to compute the For example, there are aggregates to compute the
<function>count</function>, <function>sum</function>, <function>count</function>, <function>sum</function>,
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.112 2008/12/01 09:38:08 petere Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.113 2008/12/28 18:53:54 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -39,6 +39,7 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="parameter">expression</replac ...@@ -39,6 +39,7 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="parameter">expression</replac
[ WHERE <replaceable class="parameter">condition</replaceable> ] [ WHERE <replaceable class="parameter">condition</replaceable> ]
[ GROUP BY <replaceable class="parameter">expression</replaceable> [, ...] ] [ GROUP BY <replaceable class="parameter">expression</replaceable> [, ...] ]
[ HAVING <replaceable class="parameter">condition</replaceable> [, ...] ] [ HAVING <replaceable class="parameter">condition</replaceable> [, ...] ]
[ WINDOW <replaceable class="parameter">window_name</replaceable> AS ( <replaceable class="parameter">window_definition</replaceable> ) [, ...] ]
[ { UNION | INTERSECT | EXCEPT } [ ALL ] <replaceable class="parameter">select</replaceable> ] [ { UNION | INTERSECT | EXCEPT } [ ALL ] <replaceable class="parameter">select</replaceable> ]
[ ORDER BY <replaceable class="parameter">expression</replaceable> [ ASC | DESC | USING <replaceable class="parameter">operator</replaceable> ] [ NULLS { FIRST | LAST } ] [, ...] ] [ ORDER BY <replaceable class="parameter">expression</replaceable> [ ASC | DESC | USING <replaceable class="parameter">operator</replaceable> ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ LIMIT { <replaceable class="parameter">count</replaceable> | ALL } ] [ LIMIT { <replaceable class="parameter">count</replaceable> | ALL } ]
...@@ -566,6 +567,67 @@ HAVING <replaceable class="parameter">condition</replaceable> ...@@ -566,6 +567,67 @@ HAVING <replaceable class="parameter">condition</replaceable>
</para> </para>
</refsect2> </refsect2>
<refsect2 id="SQL-WINDOW">
<title id="sql-window-title"><literal>WINDOW</literal> Clause</title>
<para>
The optional <literal>WINDOW</literal> clause has the general form
<synopsis>
WINDOW <replaceable class="parameter">window_name</replaceable> AS ( <replaceable class="parameter">window_definition</replaceable> ) [, ...]
</synopsis>
where <replaceable class="parameter">window_name</replaceable> is
a name that can be referenced from subsequent window definitions or
<literal>OVER</> clauses, and
<replaceable class="parameter">window_definition</replaceable> is
<synopsis>
[ <replaceable class="parameter">existing_window_name</replaceable> ]
[ PARTITION BY <replaceable class="parameter">expression</replaceable> [, ...] ]
[ ORDER BY <replaceable class="parameter">expression</replaceable> [ ASC | DESC | USING <replaceable class="parameter">operator</replaceable> ] [ NULLS { FIRST | LAST } ] [, ...] ]
</synopsis>
The elements of the <literal>PARTITION BY</> list are interpreted in
the same fashion as elements of a
<xref linkend="sql-groupby" endterm="sql-groupby-title">, and
the elements of the <literal>ORDER BY</> list are interpreted in the
same fashion as elements of an
<xref linkend="sql-orderby" endterm="sql-orderby-title">.
The only difference is that these expressions can contain aggregate
function calls, which are not allowed in a regular <literal>GROUP BY</>
clause. They are allowed here because windowing occurs after grouping
and aggregation.
</para>
<para>
If an <replaceable class="parameter">existing_window_name</replaceable>
is specified it must refer to an earlier entry in the <literal>WINDOW</>
list; the new window copies its partitioning clause from that entry,
as well as its ordering clause if any. In this case the new window cannot
specify its own <literal>PARTITION BY</> clause, and it can specify
<literal>ORDER BY</> only if the copied window does not have one.
</para>
<para>
The purpose of a <literal>WINDOW</literal> clause is to specify the
behavior of <firstterm>window functions</> appearing in the query's
<xref linkend="sql-select-list" endterm="sql-select-list-title"> or
<xref linkend="sql-orderby" endterm="sql-orderby-title">. These functions
can reference the <literal>WINDOW</literal> clause entries by name
in their <literal>OVER</> clauses. A <literal>WINDOW</literal> clause
entry does not have to be referenced anywhere, however; if it is not
used in the query it is simply ignored. It is possible to use window
functions without any <literal>WINDOW</literal> clause at all, since
a window function call can specify its window definition directly in
its <literal>OVER</> clause. However, the <literal>WINDOW</literal>
clause saves typing when the same window definition is needed for more
than one window function.
</para>
<para>
Window functions are described in detail in
<xref linkend="tutorial-window"> and
<xref linkend="syntax-window-functions">.
</para>
</refsect2>
<refsect2 id="sql-select-list"> <refsect2 id="sql-select-list">
<title id="sql-select-list-title"><command>SELECT</command> List</title> <title id="sql-select-list-title"><command>SELECT</command> List</title>
...@@ -1387,6 +1449,19 @@ SELECT distributors.* WHERE distributors.name = 'Westward'; ...@@ -1387,6 +1449,19 @@ SELECT distributors.* WHERE distributors.name = 'Westward';
</para> </para>
</refsect2> </refsect2>
<refsect2>
<title><literal>WINDOW</literal> Clause Restrictions</title>
<para>
The SQL standard provides for an optional <quote>framing clause</>,
introduced by the key word <literal>RANGE</> or <literal>ROWS</>,
in window definitions. <productname>PostgreSQL</productname> does
not yet implement framing clauses, and always follows the
default framing behavior, which is equivalent to the framing clause
<literal>ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW</>.
</para>
</refsect2>
<refsect2> <refsect2>
<title><literal>LIMIT</literal> and <literal>OFFSET</literal></title> <title><literal>LIMIT</literal> and <literal>OFFSET</literal></title>
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/select_into.sgml,v 1.43 2008/11/14 10:22:47 petere Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/select_into.sgml,v 1.44 2008/12/28 18:53:54 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -29,6 +29,7 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="parameter">expression</replac ...@@ -29,6 +29,7 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="parameter">expression</replac
[ WHERE <replaceable class="parameter">condition</replaceable> ] [ WHERE <replaceable class="parameter">condition</replaceable> ]
[ GROUP BY <replaceable class="parameter">expression</replaceable> [, ...] ] [ GROUP BY <replaceable class="parameter">expression</replaceable> [, ...] ]
[ HAVING <replaceable class="parameter">condition</replaceable> [, ...] ] [ HAVING <replaceable class="parameter">condition</replaceable> [, ...] ]
[ WINDOW <replaceable class="parameter">window_name</replaceable> AS ( <replaceable class="parameter">window_definition</replaceable> ) [, ...] ]
[ { UNION | INTERSECT | EXCEPT } [ ALL ] <replaceable class="parameter">select</replaceable> ] [ { UNION | INTERSECT | EXCEPT } [ ALL ] <replaceable class="parameter">select</replaceable> ]
[ ORDER BY <replaceable class="parameter">expression</replaceable> [ ASC | DESC | USING <replaceable class="parameter">operator</replaceable> ] [ NULLS { FIRST | LAST } ] [, ...] ] [ ORDER BY <replaceable class="parameter">expression</replaceable> [ ASC | DESC | USING <replaceable class="parameter">operator</replaceable> ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ LIMIT { <replaceable class="parameter">count</replaceable> | ALL } ] [ LIMIT { <replaceable class="parameter">count</replaceable> | ALL } ]
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.126 2008/12/09 20:52:03 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.127 2008/12/28 18:53:54 tgl Exp $ -->
<chapter id="sql-syntax"> <chapter id="sql-syntax">
<title>SQL Syntax</title> <title>SQL Syntax</title>
...@@ -1201,6 +1201,12 @@ SELECT 3 OPERATOR(pg_catalog.+) 4; ...@@ -1201,6 +1201,12 @@ SELECT 3 OPERATOR(pg_catalog.+) 4;
</para> </para>
</listitem> </listitem>
<listitem>
<para>
A window function call.
</para>
</listitem>
<listitem> <listitem>
<para> <para>
A type cast. A type cast.
...@@ -1445,7 +1451,7 @@ $1.somecolumn ...@@ -1445,7 +1451,7 @@ $1.somecolumn
enclosed in parentheses: enclosed in parentheses:
<synopsis> <synopsis>
<replaceable>function</replaceable> (<optional><replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ... </optional></optional> ) <replaceable>function_name</replaceable> (<optional><replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ... </optional></optional> )
</synopsis> </synopsis>
</para> </para>
...@@ -1480,7 +1486,7 @@ sqrt(2) ...@@ -1480,7 +1486,7 @@ sqrt(2)
<synopsis> <synopsis>
<replaceable>aggregate_name</replaceable> (<replaceable>expression</replaceable> [ , ... ] ) <replaceable>aggregate_name</replaceable> (<replaceable>expression</replaceable> [ , ... ] )
<replaceable>aggregate_name</replaceable> (ALL <replaceable>expression</replaceable> [ , ... ] ) <replaceable>aggregate_name</replaceable> (ALL <replaceable>expression</replaceable> [ , ... ] )
<replaceable>aggregate_name</replaceable> (DISTINCT <replaceable>expression</replaceable> [ , ... ] ) <replaceable>aggregate_name</replaceable> (DISTINCT <replaceable>expression</replaceable>)
<replaceable>aggregate_name</replaceable> ( * ) <replaceable>aggregate_name</replaceable> ( * )
</synopsis> </synopsis>
...@@ -1488,7 +1494,7 @@ sqrt(2) ...@@ -1488,7 +1494,7 @@ sqrt(2)
defined aggregate (possibly qualified with a schema name), and defined aggregate (possibly qualified with a schema name), and
<replaceable>expression</replaceable> is <replaceable>expression</replaceable> is
any value expression that does not itself contain an aggregate any value expression that does not itself contain an aggregate
expression. expression or a window function call.
</para> </para>
<para> <para>
...@@ -1550,6 +1556,89 @@ sqrt(2) ...@@ -1550,6 +1556,89 @@ sqrt(2)
</note> </note>
</sect2> </sect2>
<sect2 id="syntax-window-functions">
<title>Window Function Calls</title>
<indexterm zone="syntax-window-functions">
<primary>window function</primary>
<secondary>invocation</secondary>
</indexterm>
<indexterm zone="syntax-window-functions">
<primary>OVER clause</primary>
</indexterm>
<para>
A <firstterm>window function call</firstterm> represents the application
of an aggregate-like function over some portion of the rows selected
by a query. Unlike regular aggregate function calls, this is not tied
to grouping of the selected rows into a single output row &mdash; each
row remains separate in the query output. However the window function
is able to scan all the rows that would be part of the current row's
group according to the grouping specification (<literal>PARTITION BY</>
list) of the window function call.
The syntax of a window function call is one of the following:
<synopsis>
<replaceable>function_name</replaceable> (<optional><replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ... </optional></optional>) OVER ( <replaceable class="parameter">window_definition</replaceable> )
<replaceable>function_name</replaceable> (<optional><replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ... </optional></optional>) OVER <replaceable>window_name</replaceable>
<replaceable>function_name</replaceable> ( * ) OVER ( <replaceable class="parameter">window_definition</replaceable> )
<replaceable>function_name</replaceable> ( * ) OVER <replaceable>window_name</replaceable>
</synopsis>
where <replaceable class="parameter">window_definition</replaceable>
has the syntax
<synopsis>
[ <replaceable class="parameter">window_name</replaceable> ]
[ PARTITION BY <replaceable class="parameter">expression</replaceable> [, ...] ]
[ ORDER BY <replaceable class="parameter">expression</replaceable> [ ASC | DESC | USING <replaceable class="parameter">operator</replaceable> ] [ NULLS { FIRST | LAST } ] [, ...] ]
</synopsis>
Here, <replaceable>expression</replaceable> represents any value
expression that does not itself contain window function calls.
The <literal>PARTITION BY</> and <literal>ORDER BY</> lists have
essentially the same syntax and semantics as <literal>GROUP BY</>
and <literal>ORDER BY</> clauses of the whole query.
<replaceable>window_name</replaceable> is a reference to a named window
specification defined in the query's <literal>WINDOW</literal> clause.
Named window specifications are usually referenced with just
<literal>OVER</> <replaceable>window_name</replaceable>, but it is
also possible to write a window name inside the parentheses and then
optionally override its ordering clause with <literal>ORDER BY</>.
This latter syntax follows the same rules as modifying an existing
window name within the <literal>WINDOW</literal> clause; see the
<xref linkend="sql-select" endterm="sql-select-title"> reference
page for details.
</para>
<para>
The built-in window functions are described in <xref
linkend="functions-window-table">. Also, any built-in or
user-defined aggregate function can be used as a window function.
Currently, there is no provision for user-defined window functions
other than aggregates.
</para>
<para>
The syntaxes using <literal>*</> are used for calling parameter-less
aggregate functions as window functions, for example
<literal>count(*) OVER (PARTITION BY x ORDER BY y)</>.
<literal>*</> is customarily not used for non-aggregate window functions.
Aggregate window functions, unlike normal aggregate functions, do not
allow <literal>DISTINCT</> to be used within the function argument list.
</para>
<para>
Window function calls are permitted only in the <literal>SELECT</literal>
list and the <literal>ORDER BY</> clause of the query.
</para>
<para>
More information about window functions can be found in
<xref linkend="tutorial-window"> and
<xref linkend="queries-window">.
</para>
</sect2>
<sect2 id="sql-syntax-type-casts"> <sect2 id="sql-syntax-type-casts">
<title>Type Casts</title> <title>Type Casts</title>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/xaggr.sgml,v 1.36 2008/11/20 21:10:44 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/xaggr.sgml,v 1.37 2008/12/28 18:53:54 tgl Exp $ -->
<sect1 id="xaggr"> <sect1 id="xaggr">
<title>User-Defined Aggregates</title> <title>User-Defined Aggregates</title>
...@@ -167,10 +167,13 @@ SELECT attrelid::regclass, array_accum(atttypid::regtype) ...@@ -167,10 +167,13 @@ SELECT attrelid::regclass, array_accum(atttypid::regtype)
<para> <para>
A function written in C can detect that it is being called as an A function written in C can detect that it is being called as an
aggregate transition or final function by seeing if it was passed aggregate transition or final function by seeing if it was passed
an <structname>AggState</> node as the function call <quote>context</>, an <structname>AggState</> or <structname>WindowAggState</> node
as the function call <quote>context</>,
for example by: for example by:
<programlisting> <programlisting>
if (fcinfo->context &amp;&amp; IsA(fcinfo->context, AggState)) if (fcinfo-&gt;context &amp;&amp;
(IsA(fcinfo-&gt;context, AggState) ||
IsA(fcinfo-&gt;context, WindowAggState)))
</programlisting> </programlisting>
One reason for checking this is that when it is true, the first input One reason for checking this is that when it is true, the first input
must be a temporary transition value and can therefore safely be modified must be a temporary transition value and can therefore safely be modified
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,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/catalog/dependency.c,v 1.83 2008/12/19 16:25:17 petere Exp $ * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.84 2008/12/28 18:53:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1480,6 +1480,14 @@ find_expr_references_walker(Node *node, ...@@ -1480,6 +1480,14 @@ find_expr_references_walker(Node *node,
context->addrs); context->addrs);
/* fall through to examine arguments */ /* fall through to examine arguments */
} }
else if (IsA(node, WindowFunc))
{
WindowFunc *wfunc = (WindowFunc *) node;
add_object_address(OCLASS_PROC, wfunc->winfnoid, 0,
context->addrs);
/* fall through to examine arguments */
}
else if (IsA(node, SubPlan)) else if (IsA(node, SubPlan))
{ {
/* Extra work needed here if we ever need this case */ /* Extra work needed here if we ever need this case */
...@@ -1602,6 +1610,7 @@ find_expr_references_walker(Node *node, ...@@ -1602,6 +1610,7 @@ find_expr_references_walker(Node *node,
/* query_tree_walker ignores ORDER BY etc, but we need those opers */ /* query_tree_walker ignores ORDER BY etc, but we need those opers */
find_expr_references_walker((Node *) query->sortClause, context); find_expr_references_walker((Node *) query->sortClause, context);
find_expr_references_walker((Node *) query->groupClause, context); find_expr_references_walker((Node *) query->groupClause, context);
find_expr_references_walker((Node *) query->windowClause, context);
find_expr_references_walker((Node *) query->distinctClause, context); find_expr_references_walker((Node *) query->distinctClause, context);
/* Examine substructure of query */ /* Examine substructure of query */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.347 2008/11/29 00:13:21 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.348 2008/12/28 18:53:54 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -2138,6 +2138,10 @@ cookDefault(ParseState *pstate, ...@@ -2138,6 +2138,10 @@ cookDefault(ParseState *pstate,
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR), (errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in default expression"))); errmsg("cannot use aggregate function in default expression")));
if (pstate->p_hasWindowFuncs)
ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR),
errmsg("cannot use window function in default expression")));
/* /*
* Coerce the expression to the correct type and typmod, if given. This * Coerce the expression to the correct type and typmod, if given. This
...@@ -2211,6 +2215,10 @@ cookConstraint(ParseState *pstate, ...@@ -2211,6 +2215,10 @@ cookConstraint(ParseState *pstate,
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR), (errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in check constraint"))); errmsg("cannot use aggregate function in check constraint")));
if (pstate->p_hasWindowFuncs)
ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR),
errmsg("cannot use window function in check constraint")));
return expr; return expr;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.157 2008/12/19 18:25:19 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.158 2008/12/28 18:53:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -80,6 +80,8 @@ ProcedureCreate(const char *procedureName, ...@@ -80,6 +80,8 @@ ProcedureCreate(const char *procedureName,
float4 prorows) float4 prorows)
{ {
Oid retval; Oid retval;
/* XXX we don't currently have a way to make new window functions */
bool isWindowFunc = false;
int parameterCount; int parameterCount;
int allParamCount; int allParamCount;
Oid *allParams; Oid *allParams;
...@@ -292,8 +294,7 @@ ProcedureCreate(const char *procedureName, ...@@ -292,8 +294,7 @@ ProcedureCreate(const char *procedureName,
values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows); values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows);
values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType); values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType);
values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg); values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg);
/* XXX we don't currently have a way to make new window functions */ values[Anum_pg_proc_proiswindow - 1] = BoolGetDatum(isWindowFunc);
values[Anum_pg_proc_proiswindow - 1] = BoolGetDatum(false);
values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer); values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);
values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict); values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);
values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet); values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet);
...@@ -440,18 +441,31 @@ ProcedureCreate(const char *procedureName, ...@@ -440,18 +441,31 @@ ProcedureCreate(const char *procedureName,
} }
} }
/* Can't change aggregate status, either */ /* Can't change aggregate or window-function status, either */
if (oldproc->proisagg != isAgg) if (oldproc->proisagg != isAgg)
{ {
if (oldproc->proisagg) if (oldproc->proisagg)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("function \"%s\" is an aggregate", errmsg("function \"%s\" is an aggregate function",
procedureName)));
else
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("function \"%s\" is not an aggregate function",
procedureName)));
}
if (oldproc->proiswindow != isWindowFunc)
{
if (oldproc->proiswindow)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("function \"%s\" is a window function",
procedureName))); procedureName)));
else else
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("function \"%s\" is not an aggregate", errmsg("function \"%s\" is not a window function",
procedureName))); procedureName)));
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California * Portions Copyright (c) 1994-5, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.181 2008/11/19 01:10:23 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.182 2008/12/28 18:53:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -590,6 +590,9 @@ explain_outNode(StringInfo str, ...@@ -590,6 +590,9 @@ explain_outNode(StringInfo str,
break; break;
} }
break; break;
case T_WindowAgg:
pname = "WindowAgg";
break;
case T_Unique: case T_Unique:
pname = "Unique"; pname = "Unique";
break; break;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.103 2008/12/18 18:20:33 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.104 2008/12/28 18:53:55 tgl Exp $
* *
* DESCRIPTION * DESCRIPTION
* These routines take the parse tree and pick out the * These routines take the parse tree and pick out the
...@@ -321,6 +321,10 @@ examine_parameter_list(List *parameters, Oid languageOid, ...@@ -321,6 +321,10 @@ examine_parameter_list(List *parameters, Oid languageOid,
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR), (errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in parameter default value"))); errmsg("cannot use aggregate function in parameter default value")));
if (pstate->p_hasWindowFuncs)
ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR),
errmsg("cannot use window function in parameter default value")));
*parameterDefaults = lappend(*parameterDefaults, def); *parameterDefaults = lappend(*parameterDefaults, def);
have_defaults = true; have_defaults = true;
...@@ -1538,6 +1542,10 @@ CreateCast(CreateCastStmt *stmt) ...@@ -1538,6 +1542,10 @@ CreateCast(CreateCastStmt *stmt)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cast function must not be an aggregate function"))); errmsg("cast function must not be an aggregate function")));
if (procstruct->proiswindow)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cast function must not be a window function")));
if (procstruct->proretset) if (procstruct->proretset)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Copyright (c) 2002-2008, PostgreSQL Global Development Group * Copyright (c) 2002-2008, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.93 2008/12/13 02:29:21 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.94 2008/12/28 18:53:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -347,6 +347,10 @@ EvaluateParams(PreparedStatement *pstmt, List *params, ...@@ -347,6 +347,10 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR), (errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in EXECUTE parameter"))); errmsg("cannot use aggregate function in EXECUTE parameter")));
if (pstate->p_hasWindowFuncs)
ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR),
errmsg("cannot use window function in EXECUTE parameter")));
given_type_id = exprType(expr); given_type_id = exprType(expr);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.274 2008/12/15 21:35:31 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.275 2008/12/28 18:53:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -5506,6 +5506,10 @@ ATPrepAlterColumnType(List **wqueue, ...@@ -5506,6 +5506,10 @@ ATPrepAlterColumnType(List **wqueue,
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR), (errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in transform expression"))); errmsg("cannot use aggregate function in transform expression")));
if (pstate->p_hasWindowFuncs)
ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR),
errmsg("cannot use window function in transform expression")));
} }
else else
{ {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.127 2008/11/30 19:01:29 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.128 2008/12/28 18:53:55 tgl Exp $
* *
* DESCRIPTION * DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the * The "DefineFoo" routines take the parse tree and pick out the
...@@ -2255,6 +2255,10 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid, ...@@ -2255,6 +2255,10 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR), (errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in check constraint"))); errmsg("cannot use aggregate function in check constraint")));
if (pstate->p_hasWindowFuncs)
ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR),
errmsg("cannot use window function in check constraint")));
/* /*
* Convert to string form for storage. * Convert to string form for storage.
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# Makefile for executor # Makefile for executor
# #
# IDENTIFICATION # IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/executor/Makefile,v 1.28 2008/10/04 21:56:52 tgl Exp $ # $PostgreSQL: pgsql/src/backend/executor/Makefile,v 1.29 2008/12/28 18:53:55 tgl Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
...@@ -22,6 +22,6 @@ OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \ ...@@ -22,6 +22,6 @@ OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \
nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \ nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
nodeValuesscan.o nodeCtescan.o nodeWorktablescan.o \ nodeValuesscan.o nodeCtescan.o nodeWorktablescan.o \
nodeLimit.o nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \ nodeLimit.o nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
tstoreReceiver.o spi.o nodeWindowAgg.o tstoreReceiver.o spi.o
include $(top_srcdir)/src/backend/common.mk include $(top_srcdir)/src/backend/common.mk
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, 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/backend/executor/execAmi.c,v 1.101 2008/10/28 17:13:51 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/execAmi.c,v 1.102 2008/12/28 18:53:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "executor/nodeBitmapHeapscan.h" #include "executor/nodeBitmapHeapscan.h"
#include "executor/nodeBitmapIndexscan.h" #include "executor/nodeBitmapIndexscan.h"
#include "executor/nodeBitmapOr.h" #include "executor/nodeBitmapOr.h"
#include "executor/nodeCtescan.h"
#include "executor/nodeFunctionscan.h" #include "executor/nodeFunctionscan.h"
#include "executor/nodeGroup.h" #include "executor/nodeGroup.h"
#include "executor/nodeGroup.h" #include "executor/nodeGroup.h"
...@@ -40,7 +41,7 @@ ...@@ -40,7 +41,7 @@
#include "executor/nodeTidscan.h" #include "executor/nodeTidscan.h"
#include "executor/nodeUnique.h" #include "executor/nodeUnique.h"
#include "executor/nodeValuesscan.h" #include "executor/nodeValuesscan.h"
#include "executor/nodeCtescan.h" #include "executor/nodeWindowAgg.h"
#include "executor/nodeWorktablescan.h" #include "executor/nodeWorktablescan.h"
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
#include "utils/syscache.h" #include "utils/syscache.h"
...@@ -210,6 +211,10 @@ ExecReScan(PlanState *node, ExprContext *exprCtxt) ...@@ -210,6 +211,10 @@ ExecReScan(PlanState *node, ExprContext *exprCtxt)
ExecReScanAgg((AggState *) node, exprCtxt); ExecReScanAgg((AggState *) node, exprCtxt);
break; break;
case T_WindowAggState:
ExecReScanWindowAgg((WindowAggState *) node, exprCtxt);
break;
case T_UniqueState: case T_UniqueState:
ExecReScanUnique((UniqueState *) node, exprCtxt); ExecReScanUnique((UniqueState *) node, exprCtxt);
break; break;
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execProcnode.c,v 1.63 2008/10/04 21:56:53 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/execProcnode.c,v 1.64 2008/12/28 18:53:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -85,6 +85,7 @@ ...@@ -85,6 +85,7 @@
#include "executor/nodeBitmapHeapscan.h" #include "executor/nodeBitmapHeapscan.h"
#include "executor/nodeBitmapIndexscan.h" #include "executor/nodeBitmapIndexscan.h"
#include "executor/nodeBitmapOr.h" #include "executor/nodeBitmapOr.h"
#include "executor/nodeCtescan.h"
#include "executor/nodeFunctionscan.h" #include "executor/nodeFunctionscan.h"
#include "executor/nodeGroup.h" #include "executor/nodeGroup.h"
#include "executor/nodeHash.h" #include "executor/nodeHash.h"
...@@ -104,7 +105,7 @@ ...@@ -104,7 +105,7 @@
#include "executor/nodeTidscan.h" #include "executor/nodeTidscan.h"
#include "executor/nodeUnique.h" #include "executor/nodeUnique.h"
#include "executor/nodeValuesscan.h" #include "executor/nodeValuesscan.h"
#include "executor/nodeCtescan.h" #include "executor/nodeWindowAgg.h"
#include "executor/nodeWorktablescan.h" #include "executor/nodeWorktablescan.h"
#include "miscadmin.h" #include "miscadmin.h"
...@@ -260,6 +261,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags) ...@@ -260,6 +261,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
estate, eflags); estate, eflags);
break; break;
case T_WindowAgg:
result = (PlanState *) ExecInitWindowAgg((WindowAgg *) node,
estate, eflags);
break;
case T_Unique: case T_Unique:
result = (PlanState *) ExecInitUnique((Unique *) node, result = (PlanState *) ExecInitUnique((Unique *) node,
estate, eflags); estate, eflags);
...@@ -425,6 +431,10 @@ ExecProcNode(PlanState *node) ...@@ -425,6 +431,10 @@ ExecProcNode(PlanState *node)
result = ExecAgg((AggState *) node); result = ExecAgg((AggState *) node);
break; break;
case T_WindowAggState:
result = ExecWindowAgg((WindowAggState *) node);
break;
case T_UniqueState: case T_UniqueState:
result = ExecUnique((UniqueState *) node); result = ExecUnique((UniqueState *) node);
break; break;
...@@ -601,6 +611,10 @@ ExecCountSlotsNode(Plan *node) ...@@ -601,6 +611,10 @@ ExecCountSlotsNode(Plan *node)
case T_Agg: case T_Agg:
return ExecCountSlotsAgg((Agg *) node); return ExecCountSlotsAgg((Agg *) node);
case T_WindowAgg:
return ExecCountSlotsWindowAgg((WindowAgg *) node);
break;
case T_Unique: case T_Unique:
return ExecCountSlotsUnique((Unique *) node); return ExecCountSlotsUnique((Unique *) node);
...@@ -749,6 +763,10 @@ ExecEndNode(PlanState *node) ...@@ -749,6 +763,10 @@ ExecEndNode(PlanState *node)
ExecEndAgg((AggState *) node); ExecEndAgg((AggState *) node);
break; break;
case T_WindowAggState:
ExecEndWindowAgg((WindowAggState *) node);
break;
case T_UniqueState: case T_UniqueState:
ExecEndUnique((UniqueState *) node); ExecEndUnique((UniqueState *) node);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.238 2008/12/18 19:38:22 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.239 2008/12/28 18:53:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -62,6 +62,9 @@ static Datum ExecEvalArrayRef(ArrayRefExprState *astate, ...@@ -62,6 +62,9 @@ static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
static Datum ExecEvalAggref(AggrefExprState *aggref, static Datum ExecEvalAggref(AggrefExprState *aggref,
ExprContext *econtext, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone); bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalWindowFunc(WindowFuncExprState *wfunc,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalVar(ExprState *exprstate, ExprContext *econtext, static Datum ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone); bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext, static Datum ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
...@@ -443,6 +446,27 @@ ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, ...@@ -443,6 +446,27 @@ ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext,
return econtext->ecxt_aggvalues[aggref->aggno]; return econtext->ecxt_aggvalues[aggref->aggno];
} }
/* ----------------------------------------------------------------
* ExecEvalWindowFunc
*
* Returns a Datum whose value is the value of the precomputed
* window function found in the given expression context.
* ----------------------------------------------------------------
*/
static Datum
ExecEvalWindowFunc(WindowFuncExprState *wfunc, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
if (isDone)
*isDone = ExprSingleResult;
if (econtext->ecxt_aggvalues == NULL) /* safety check */
elog(ERROR, "no window functions in this expression context");
*isNull = econtext->ecxt_aggnulls[wfunc->wfuncno];
return econtext->ecxt_aggvalues[wfunc->wfuncno];
}
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecEvalVar * ExecEvalVar
* *
...@@ -4062,12 +4086,12 @@ ExecEvalExprSwitchContext(ExprState *expression, ...@@ -4062,12 +4086,12 @@ ExecEvalExprSwitchContext(ExprState *expression,
* executions of the expression are needed. Typically the context will be * executions of the expression are needed. Typically the context will be
* the same as the per-query context of the associated ExprContext. * the same as the per-query context of the associated ExprContext.
* *
* Any Aggref and SubPlan nodes found in the tree are added to the lists * Any Aggref, WindowFunc, or SubPlan nodes found in the tree are added to the
* of such nodes held by the parent PlanState. Otherwise, we do very little * lists of such nodes held by the parent PlanState. Otherwise, we do very
* initialization here other than building the state-node tree. Any nontrivial * little initialization here other than building the state-node tree. Any
* work associated with initializing runtime info for a node should happen * nontrivial work associated with initializing runtime info for a node should
* during the first actual evaluation of that node. (This policy lets us * happen during the first actual evaluation of that node. (This policy lets
* avoid work if the node is never actually evaluated.) * us avoid work if the node is never actually evaluated.)
* *
* Note: there is no ExecEndExpr function; we assume that any resource * Note: there is no ExecEndExpr function; we assume that any resource
* cleanup needed will be handled by just releasing the memory context * cleanup needed will be handled by just releasing the memory context
...@@ -4145,11 +4169,49 @@ ExecInitExpr(Expr *node, PlanState *parent) ...@@ -4145,11 +4169,49 @@ ExecInitExpr(Expr *node, PlanState *parent)
else else
{ {
/* planner messed up */ /* planner messed up */
elog(ERROR, "aggref found in non-Agg plan node"); elog(ERROR, "Aggref found in non-Agg plan node");
} }
state = (ExprState *) astate; state = (ExprState *) astate;
} }
break; break;
case T_WindowFunc:
{
WindowFunc *wfunc = (WindowFunc *) node;
WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
wfstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWindowFunc;
if (parent && IsA(parent, WindowAggState))
{
WindowAggState *winstate = (WindowAggState *) parent;
int nfuncs;
winstate->funcs = lcons(wfstate, winstate->funcs);
nfuncs = ++winstate->numfuncs;
if (wfunc->winagg)
winstate->numaggs++;
wfstate->args = (List *) ExecInitExpr((Expr *) wfunc->args,
parent);
/*
* Complain if the windowfunc's arguments contain any
* windowfuncs; nested window functions are semantically
* nonsensical. (This should have been caught earlier,
* but we defend against it here anyway.)
*/
if (nfuncs != winstate->numfuncs)
ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR),
errmsg("window function calls cannot be nested")));
}
else
{
/* planner messed up */
elog(ERROR, "WindowFunc found in non-WindowAgg plan node");
}
state = (ExprState *) wfstate;
}
break;
case T_ArrayRef: case T_ArrayRef:
{ {
ArrayRef *aref = (ArrayRef *) node; ArrayRef *aref = (ArrayRef *) node;
......
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.416 2008/12/19 16:25:17 petere Exp $ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.417 2008/12/28 18:53:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -668,6 +668,32 @@ _copyAgg(Agg *from) ...@@ -668,6 +668,32 @@ _copyAgg(Agg *from)
return newnode; return newnode;
} }
/*
* _copyWindowAgg
*/
static WindowAgg *
_copyWindowAgg(WindowAgg *from)
{
WindowAgg *newnode = makeNode(WindowAgg);
CopyPlanFields((Plan *) from, (Plan *) newnode);
COPY_SCALAR_FIELD(partNumCols);
if (from->partNumCols > 0)
{
COPY_POINTER_FIELD(partColIdx, from->partNumCols * sizeof(AttrNumber));
COPY_POINTER_FIELD(partOperators, from->partNumCols * sizeof(Oid));
}
COPY_SCALAR_FIELD(ordNumCols);
if (from->ordNumCols > 0)
{
COPY_POINTER_FIELD(ordColIdx, from->ordNumCols * sizeof(AttrNumber));
COPY_POINTER_FIELD(ordOperators, from->ordNumCols * sizeof(Oid));
}
return newnode;
}
/* /*
* _copyUnique * _copyUnique
*/ */
...@@ -931,6 +957,25 @@ _copyAggref(Aggref *from) ...@@ -931,6 +957,25 @@ _copyAggref(Aggref *from)
return newnode; return newnode;
} }
/*
* _copyWindowFunc
*/
static WindowFunc *
_copyWindowFunc(WindowFunc *from)
{
WindowFunc *newnode = makeNode(WindowFunc);
COPY_SCALAR_FIELD(winfnoid);
COPY_SCALAR_FIELD(wintype);
COPY_NODE_FIELD(args);
COPY_SCALAR_FIELD(winref);
COPY_SCALAR_FIELD(winstar);
COPY_SCALAR_FIELD(winagg);
COPY_LOCATION_FIELD(location);
return newnode;
}
/* /*
* _copyArrayRef * _copyArrayRef
*/ */
...@@ -1729,6 +1774,21 @@ _copySortGroupClause(SortGroupClause *from) ...@@ -1729,6 +1774,21 @@ _copySortGroupClause(SortGroupClause *from)
return newnode; return newnode;
} }
static WindowClause *
_copyWindowClause(WindowClause *from)
{
WindowClause *newnode = makeNode(WindowClause);
COPY_STRING_FIELD(name);
COPY_STRING_FIELD(refname);
COPY_NODE_FIELD(partitionClause);
COPY_NODE_FIELD(orderClause);
COPY_SCALAR_FIELD(winref);
COPY_SCALAR_FIELD(copiedOrder);
return newnode;
}
static RowMarkClause * static RowMarkClause *
_copyRowMarkClause(RowMarkClause *from) _copyRowMarkClause(RowMarkClause *from)
{ {
...@@ -1850,6 +1910,7 @@ _copyFuncCall(FuncCall *from) ...@@ -1850,6 +1910,7 @@ _copyFuncCall(FuncCall *from)
COPY_SCALAR_FIELD(agg_star); COPY_SCALAR_FIELD(agg_star);
COPY_SCALAR_FIELD(agg_distinct); COPY_SCALAR_FIELD(agg_distinct);
COPY_SCALAR_FIELD(func_variadic); COPY_SCALAR_FIELD(func_variadic);
COPY_NODE_FIELD(over);
COPY_LOCATION_FIELD(location); COPY_LOCATION_FIELD(location);
return newnode; return newnode;
...@@ -1940,6 +2001,20 @@ _copySortBy(SortBy *from) ...@@ -1940,6 +2001,20 @@ _copySortBy(SortBy *from)
return newnode; return newnode;
} }
static WindowDef *
_copyWindowDef(WindowDef *from)
{
WindowDef *newnode = makeNode(WindowDef);
COPY_STRING_FIELD(name);
COPY_STRING_FIELD(refname);
COPY_NODE_FIELD(partitionClause);
COPY_NODE_FIELD(orderClause);
COPY_LOCATION_FIELD(location);
return newnode;
}
static RangeSubselect * static RangeSubselect *
_copyRangeSubselect(RangeSubselect *from) _copyRangeSubselect(RangeSubselect *from)
{ {
...@@ -2081,6 +2156,7 @@ _copyQuery(Query *from) ...@@ -2081,6 +2156,7 @@ _copyQuery(Query *from)
COPY_SCALAR_FIELD(resultRelation); COPY_SCALAR_FIELD(resultRelation);
COPY_NODE_FIELD(intoClause); COPY_NODE_FIELD(intoClause);
COPY_SCALAR_FIELD(hasAggs); COPY_SCALAR_FIELD(hasAggs);
COPY_SCALAR_FIELD(hasWindowFuncs);
COPY_SCALAR_FIELD(hasSubLinks); COPY_SCALAR_FIELD(hasSubLinks);
COPY_SCALAR_FIELD(hasDistinctOn); COPY_SCALAR_FIELD(hasDistinctOn);
COPY_SCALAR_FIELD(hasRecursive); COPY_SCALAR_FIELD(hasRecursive);
...@@ -2091,6 +2167,7 @@ _copyQuery(Query *from) ...@@ -2091,6 +2167,7 @@ _copyQuery(Query *from)
COPY_NODE_FIELD(returningList); COPY_NODE_FIELD(returningList);
COPY_NODE_FIELD(groupClause); COPY_NODE_FIELD(groupClause);
COPY_NODE_FIELD(havingQual); COPY_NODE_FIELD(havingQual);
COPY_NODE_FIELD(windowClause);
COPY_NODE_FIELD(distinctClause); COPY_NODE_FIELD(distinctClause);
COPY_NODE_FIELD(sortClause); COPY_NODE_FIELD(sortClause);
COPY_NODE_FIELD(limitOffset); COPY_NODE_FIELD(limitOffset);
...@@ -2153,6 +2230,7 @@ _copySelectStmt(SelectStmt *from) ...@@ -2153,6 +2230,7 @@ _copySelectStmt(SelectStmt *from)
COPY_NODE_FIELD(whereClause); COPY_NODE_FIELD(whereClause);
COPY_NODE_FIELD(groupClause); COPY_NODE_FIELD(groupClause);
COPY_NODE_FIELD(havingClause); COPY_NODE_FIELD(havingClause);
COPY_NODE_FIELD(windowClause);
COPY_NODE_FIELD(withClause); COPY_NODE_FIELD(withClause);
COPY_NODE_FIELD(valuesLists); COPY_NODE_FIELD(valuesLists);
COPY_NODE_FIELD(sortClause); COPY_NODE_FIELD(sortClause);
...@@ -3440,6 +3518,9 @@ copyObject(void *from) ...@@ -3440,6 +3518,9 @@ copyObject(void *from)
case T_Agg: case T_Agg:
retval = _copyAgg(from); retval = _copyAgg(from);
break; break;
case T_WindowAgg:
retval = _copyWindowAgg(from);
break;
case T_Unique: case T_Unique:
retval = _copyUnique(from); retval = _copyUnique(from);
break; break;
...@@ -3480,6 +3561,9 @@ copyObject(void *from) ...@@ -3480,6 +3561,9 @@ copyObject(void *from)
case T_Aggref: case T_Aggref:
retval = _copyAggref(from); retval = _copyAggref(from);
break; break;
case T_WindowFunc:
retval = _copyWindowFunc(from);
break;
case T_ArrayRef: case T_ArrayRef:
retval = _copyArrayRef(from); retval = _copyArrayRef(from);
break; break;
...@@ -3951,6 +4035,9 @@ copyObject(void *from) ...@@ -3951,6 +4035,9 @@ copyObject(void *from)
case T_SortBy: case T_SortBy:
retval = _copySortBy(from); retval = _copySortBy(from);
break; break;
case T_WindowDef:
retval = _copyWindowDef(from);
break;
case T_RangeSubselect: case T_RangeSubselect:
retval = _copyRangeSubselect(from); retval = _copyRangeSubselect(from);
break; break;
...@@ -3984,6 +4071,9 @@ copyObject(void *from) ...@@ -3984,6 +4071,9 @@ copyObject(void *from)
case T_SortGroupClause: case T_SortGroupClause:
retval = _copySortGroupClause(from); retval = _copySortGroupClause(from);
break; break;
case T_WindowClause:
retval = _copyWindowClause(from);
break;
case T_RowMarkClause: case T_RowMarkClause:
retval = _copyRowMarkClause(from); retval = _copyRowMarkClause(from);
break; break;
......
...@@ -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.341 2008/12/19 16:25:17 petere Exp $ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.342 2008/12/28 18:53:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -191,6 +191,20 @@ _equalAggref(Aggref *a, Aggref *b) ...@@ -191,6 +191,20 @@ _equalAggref(Aggref *a, Aggref *b)
return true; return true;
} }
static bool
_equalWindowFunc(WindowFunc *a, WindowFunc *b)
{
COMPARE_SCALAR_FIELD(winfnoid);
COMPARE_SCALAR_FIELD(wintype);
COMPARE_NODE_FIELD(args);
COMPARE_SCALAR_FIELD(winref);
COMPARE_SCALAR_FIELD(winstar);
COMPARE_SCALAR_FIELD(winagg);
COMPARE_LOCATION_FIELD(location);
return true;
}
static bool static bool
_equalArrayRef(ArrayRef *a, ArrayRef *b) _equalArrayRef(ArrayRef *a, ArrayRef *b)
{ {
...@@ -839,6 +853,7 @@ _equalQuery(Query *a, Query *b) ...@@ -839,6 +853,7 @@ _equalQuery(Query *a, Query *b)
COMPARE_SCALAR_FIELD(resultRelation); COMPARE_SCALAR_FIELD(resultRelation);
COMPARE_NODE_FIELD(intoClause); COMPARE_NODE_FIELD(intoClause);
COMPARE_SCALAR_FIELD(hasAggs); COMPARE_SCALAR_FIELD(hasAggs);
COMPARE_SCALAR_FIELD(hasWindowFuncs);
COMPARE_SCALAR_FIELD(hasSubLinks); COMPARE_SCALAR_FIELD(hasSubLinks);
COMPARE_SCALAR_FIELD(hasDistinctOn); COMPARE_SCALAR_FIELD(hasDistinctOn);
COMPARE_SCALAR_FIELD(hasRecursive); COMPARE_SCALAR_FIELD(hasRecursive);
...@@ -849,6 +864,7 @@ _equalQuery(Query *a, Query *b) ...@@ -849,6 +864,7 @@ _equalQuery(Query *a, Query *b)
COMPARE_NODE_FIELD(returningList); COMPARE_NODE_FIELD(returningList);
COMPARE_NODE_FIELD(groupClause); COMPARE_NODE_FIELD(groupClause);
COMPARE_NODE_FIELD(havingQual); COMPARE_NODE_FIELD(havingQual);
COMPARE_NODE_FIELD(windowClause);
COMPARE_NODE_FIELD(distinctClause); COMPARE_NODE_FIELD(distinctClause);
COMPARE_NODE_FIELD(sortClause); COMPARE_NODE_FIELD(sortClause);
COMPARE_NODE_FIELD(limitOffset); COMPARE_NODE_FIELD(limitOffset);
...@@ -903,6 +919,7 @@ _equalSelectStmt(SelectStmt *a, SelectStmt *b) ...@@ -903,6 +919,7 @@ _equalSelectStmt(SelectStmt *a, SelectStmt *b)
COMPARE_NODE_FIELD(whereClause); COMPARE_NODE_FIELD(whereClause);
COMPARE_NODE_FIELD(groupClause); COMPARE_NODE_FIELD(groupClause);
COMPARE_NODE_FIELD(havingClause); COMPARE_NODE_FIELD(havingClause);
COMPARE_NODE_FIELD(windowClause);
COMPARE_NODE_FIELD(withClause); COMPARE_NODE_FIELD(withClause);
COMPARE_NODE_FIELD(valuesLists); COMPARE_NODE_FIELD(valuesLists);
COMPARE_NODE_FIELD(sortClause); COMPARE_NODE_FIELD(sortClause);
...@@ -1894,6 +1911,7 @@ _equalFuncCall(FuncCall *a, FuncCall *b) ...@@ -1894,6 +1911,7 @@ _equalFuncCall(FuncCall *a, FuncCall *b)
COMPARE_SCALAR_FIELD(agg_star); COMPARE_SCALAR_FIELD(agg_star);
COMPARE_SCALAR_FIELD(agg_distinct); COMPARE_SCALAR_FIELD(agg_distinct);
COMPARE_SCALAR_FIELD(func_variadic); COMPARE_SCALAR_FIELD(func_variadic);
COMPARE_NODE_FIELD(over);
COMPARE_LOCATION_FIELD(location); COMPARE_LOCATION_FIELD(location);
return true; return true;
...@@ -1980,6 +1998,18 @@ _equalSortBy(SortBy *a, SortBy *b) ...@@ -1980,6 +1998,18 @@ _equalSortBy(SortBy *a, SortBy *b)
return true; return true;
} }
static bool
_equalWindowDef(WindowDef *a, WindowDef *b)
{
COMPARE_STRING_FIELD(name);
COMPARE_STRING_FIELD(refname);
COMPARE_NODE_FIELD(partitionClause);
COMPARE_NODE_FIELD(orderClause);
COMPARE_LOCATION_FIELD(location);
return true;
}
static bool static bool
_equalRangeSubselect(RangeSubselect *a, RangeSubselect *b) _equalRangeSubselect(RangeSubselect *a, RangeSubselect *b)
{ {
...@@ -2106,6 +2136,19 @@ _equalSortGroupClause(SortGroupClause *a, SortGroupClause *b) ...@@ -2106,6 +2136,19 @@ _equalSortGroupClause(SortGroupClause *a, SortGroupClause *b)
return true; return true;
} }
static bool
_equalWindowClause(WindowClause *a, WindowClause *b)
{
COMPARE_STRING_FIELD(name);
COMPARE_STRING_FIELD(refname);
COMPARE_NODE_FIELD(partitionClause);
COMPARE_NODE_FIELD(orderClause);
COMPARE_SCALAR_FIELD(winref);
COMPARE_SCALAR_FIELD(copiedOrder);
return true;
}
static bool static bool
_equalRowMarkClause(RowMarkClause *a, RowMarkClause *b) _equalRowMarkClause(RowMarkClause *a, RowMarkClause *b)
{ {
...@@ -2311,6 +2354,9 @@ equal(void *a, void *b) ...@@ -2311,6 +2354,9 @@ equal(void *a, void *b)
case T_Aggref: case T_Aggref:
retval = _equalAggref(a, b); retval = _equalAggref(a, b);
break; break;
case T_WindowFunc:
retval = _equalWindowFunc(a, b);
break;
case T_ArrayRef: case T_ArrayRef:
retval = _equalArrayRef(a, b); retval = _equalArrayRef(a, b);
break; break;
...@@ -2769,6 +2815,9 @@ equal(void *a, void *b) ...@@ -2769,6 +2815,9 @@ equal(void *a, void *b)
case T_SortBy: case T_SortBy:
retval = _equalSortBy(a, b); retval = _equalSortBy(a, b);
break; break;
case T_WindowDef:
retval = _equalWindowDef(a, b);
break;
case T_RangeSubselect: case T_RangeSubselect:
retval = _equalRangeSubselect(a, b); retval = _equalRangeSubselect(a, b);
break; break;
...@@ -2802,6 +2851,9 @@ equal(void *a, void *b) ...@@ -2802,6 +2851,9 @@ equal(void *a, void *b)
case T_SortGroupClause: case T_SortGroupClause:
retval = _equalSortGroupClause(a, b); retval = _equalSortGroupClause(a, b);
break; break;
case T_WindowClause:
retval = _equalWindowClause(a, b);
break;
case T_RowMarkClause: case T_RowMarkClause:
retval = _equalRowMarkClause(a, b); retval = _equalRowMarkClause(a, b);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.35 2008/10/21 20:42:52 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.36 2008/12/28 18:53:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -52,6 +52,9 @@ exprType(Node *expr) ...@@ -52,6 +52,9 @@ exprType(Node *expr)
case T_Aggref: case T_Aggref:
type = ((Aggref *) expr)->aggtype; type = ((Aggref *) expr)->aggtype;
break; break;
case T_WindowFunc:
type = ((WindowFunc *) expr)->wintype;
break;
case T_ArrayRef: case T_ArrayRef:
{ {
ArrayRef *arrayref = (ArrayRef *) expr; ArrayRef *arrayref = (ArrayRef *) expr;
...@@ -548,6 +551,8 @@ expression_returns_set_walker(Node *node, void *context) ...@@ -548,6 +551,8 @@ expression_returns_set_walker(Node *node, void *context)
/* Avoid recursion for some cases that can't return a set */ /* Avoid recursion for some cases that can't return a set */
if (IsA(node, Aggref)) if (IsA(node, Aggref))
return false; return false;
if (IsA(node, WindowFunc))
return false;
if (IsA(node, DistinctExpr)) if (IsA(node, DistinctExpr))
return false; return false;
if (IsA(node, ScalarArrayOpExpr)) if (IsA(node, ScalarArrayOpExpr))
...@@ -634,6 +639,10 @@ exprLocation(Node *expr) ...@@ -634,6 +639,10 @@ exprLocation(Node *expr)
/* function name should always be the first thing */ /* function name should always be the first thing */
loc = ((Aggref *) expr)->location; loc = ((Aggref *) expr)->location;
break; break;
case T_WindowFunc:
/* function name should always be the first thing */
loc = ((WindowFunc *) expr)->location;
break;
case T_ArrayRef: case T_ArrayRef:
/* just use array argument's location */ /* just use array argument's location */
loc = exprLocation((Node *) ((ArrayRef *) expr)->refexpr); loc = exprLocation((Node *) ((ArrayRef *) expr)->refexpr);
...@@ -868,6 +877,9 @@ exprLocation(Node *expr) ...@@ -868,6 +877,9 @@ exprLocation(Node *expr)
/* just use argument's location (ignore operator, if any) */ /* just use argument's location (ignore operator, if any) */
loc = exprLocation(((SortBy *) expr)->node); loc = exprLocation(((SortBy *) expr)->node);
break; break;
case T_WindowDef:
loc = ((WindowDef *) expr)->location;
break;
case T_TypeName: case T_TypeName:
loc = ((TypeName *) expr)->location; loc = ((TypeName *) expr)->location;
break; break;
...@@ -1045,6 +1057,16 @@ expression_tree_walker(Node *node, ...@@ -1045,6 +1057,16 @@ expression_tree_walker(Node *node,
return true; return true;
} }
break; break;
case T_WindowFunc:
{
WindowFunc *expr = (WindowFunc *) node;
/* recurse directly on List */
if (expression_tree_walker((Node *) expr->args,
walker, context))
return true;
}
break;
case T_ArrayRef: case T_ArrayRef:
{ {
ArrayRef *aref = (ArrayRef *) node; ArrayRef *aref = (ArrayRef *) node;
...@@ -1221,6 +1243,16 @@ expression_tree_walker(Node *node, ...@@ -1221,6 +1243,16 @@ expression_tree_walker(Node *node,
case T_Query: case T_Query:
/* Do nothing with a sub-Query, per discussion above */ /* Do nothing with a sub-Query, per discussion above */
break; break;
case T_WindowClause:
{
WindowClause *wc = (WindowClause *) node;
if (walker(wc->partitionClause, context))
return true;
if (walker(wc->orderClause, context))
return true;
}
break;
case T_CommonTableExpr: case T_CommonTableExpr:
{ {
CommonTableExpr *cte = (CommonTableExpr *) node; CommonTableExpr *cte = (CommonTableExpr *) node;
...@@ -1539,6 +1571,16 @@ expression_tree_mutator(Node *node, ...@@ -1539,6 +1571,16 @@ expression_tree_mutator(Node *node,
return (Node *) newnode; return (Node *) newnode;
} }
break; break;
case T_WindowFunc:
{
WindowFunc *wfunc = (WindowFunc *) node;
WindowFunc *newnode;
FLATCOPY(newnode, wfunc, WindowFunc);
MUTATE(newnode->args, wfunc->args, List *);
return (Node *) newnode;
}
break;
case T_ArrayRef: case T_ArrayRef:
{ {
ArrayRef *arrayref = (ArrayRef *) node; ArrayRef *arrayref = (ArrayRef *) node;
...@@ -1848,6 +1890,17 @@ expression_tree_mutator(Node *node, ...@@ -1848,6 +1890,17 @@ expression_tree_mutator(Node *node,
case T_Query: case T_Query:
/* Do nothing with a sub-Query, per discussion above */ /* Do nothing with a sub-Query, per discussion above */
return node; return node;
case T_WindowClause:
{
WindowClause *wc = (WindowClause *) node;
WindowClause *newnode;
FLATCOPY(newnode, wc, WindowClause);
MUTATE(newnode->partitionClause, wc->partitionClause, List *);
MUTATE(newnode->orderClause, wc->orderClause, List *);
return (Node *) newnode;
}
break;
case T_CommonTableExpr: case T_CommonTableExpr:
{ {
CommonTableExpr *cte = (CommonTableExpr *) node; CommonTableExpr *cte = (CommonTableExpr *) node;
...@@ -2280,6 +2333,8 @@ raw_expression_tree_walker(Node *node, bool (*walker) (), void *context) ...@@ -2280,6 +2333,8 @@ raw_expression_tree_walker(Node *node, bool (*walker) (), void *context)
return true; return true;
if (walker(stmt->havingClause, context)) if (walker(stmt->havingClause, context))
return true; return true;
if (walker(stmt->windowClause, context))
return true;
if (walker(stmt->withClause, context)) if (walker(stmt->withClause, context))
return true; return true;
if (walker(stmt->valuesLists, context)) if (walker(stmt->valuesLists, context))
...@@ -2318,6 +2373,8 @@ raw_expression_tree_walker(Node *node, bool (*walker) (), void *context) ...@@ -2318,6 +2373,8 @@ raw_expression_tree_walker(Node *node, bool (*walker) (), void *context)
if (walker(fcall->args, context)) if (walker(fcall->args, context))
return true; return true;
if (walker(fcall->over, context))
return true;
/* function name is deemed uninteresting */ /* function name is deemed uninteresting */
} }
break; break;
...@@ -2365,6 +2422,16 @@ raw_expression_tree_walker(Node *node, bool (*walker) (), void *context) ...@@ -2365,6 +2422,16 @@ raw_expression_tree_walker(Node *node, bool (*walker) (), void *context)
break; break;
case T_SortBy: case T_SortBy:
return walker(((SortBy *) node)->node, context); return walker(((SortBy *) node)->node, context);
case T_WindowDef:
{
WindowDef *wd = (WindowDef *) node;
if (walker(wd->partitionClause, context))
return true;
if (walker(wd->orderClause, context))
return true;
}
break;
case T_RangeSubselect: case T_RangeSubselect:
{ {
RangeSubselect *rs = (RangeSubselect *) node; RangeSubselect *rs = (RangeSubselect *) node;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.346 2008/12/01 21:06:12 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.347 2008/12/28 18:53:56 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*
...@@ -566,6 +566,36 @@ _outAgg(StringInfo str, Agg *node) ...@@ -566,6 +566,36 @@ _outAgg(StringInfo str, Agg *node)
WRITE_LONG_FIELD(numGroups); WRITE_LONG_FIELD(numGroups);
} }
static void
_outWindowAgg(StringInfo str, WindowAgg *node)
{
int i;
WRITE_NODE_TYPE("WINDOWAGG");
_outPlanInfo(str, (Plan *) node);
WRITE_INT_FIELD(partNumCols);
appendStringInfo(str, " :partColIdx");
for (i = 0; i < node->partNumCols; i++)
appendStringInfo(str, " %d", node->partColIdx[i]);
appendStringInfo(str, " :partOperations");
for (i = 0; i < node->partNumCols; i++)
appendStringInfo(str, " %u", node->partOperators[i]);
WRITE_INT_FIELD(ordNumCols);
appendStringInfo(str, " :ordColIdx");
for (i = 0; i< node->ordNumCols; i++)
appendStringInfo(str, " %d", node->ordColIdx[i]);
appendStringInfo(str, " :ordOperations");
for (i = 0; i < node->ordNumCols; i++)
appendStringInfo(str, " %u", node->ordOperators[i]);
}
static void static void
_outGroup(StringInfo str, Group *node) _outGroup(StringInfo str, Group *node)
{ {
...@@ -798,6 +828,20 @@ _outAggref(StringInfo str, Aggref *node) ...@@ -798,6 +828,20 @@ _outAggref(StringInfo str, Aggref *node)
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
static void
_outWindowFunc(StringInfo str, WindowFunc *node)
{
WRITE_NODE_TYPE("WINDOWFUNC");
WRITE_OID_FIELD(winfnoid);
WRITE_OID_FIELD(wintype);
WRITE_NODE_FIELD(args);
WRITE_UINT_FIELD(winref);
WRITE_BOOL_FIELD(winstar);
WRITE_BOOL_FIELD(winagg);
WRITE_LOCATION_FIELD(location);
}
static void static void
_outArrayRef(StringInfo str, ArrayRef *node) _outArrayRef(StringInfo str, ArrayRef *node)
{ {
...@@ -1440,6 +1484,7 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node) ...@@ -1440,6 +1484,7 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node)
WRITE_NODE_FIELD(placeholder_list); WRITE_NODE_FIELD(placeholder_list);
WRITE_NODE_FIELD(query_pathkeys); WRITE_NODE_FIELD(query_pathkeys);
WRITE_NODE_FIELD(group_pathkeys); WRITE_NODE_FIELD(group_pathkeys);
WRITE_NODE_FIELD(window_pathkeys);
WRITE_NODE_FIELD(distinct_pathkeys); WRITE_NODE_FIELD(distinct_pathkeys);
WRITE_NODE_FIELD(sort_pathkeys); WRITE_NODE_FIELD(sort_pathkeys);
WRITE_FLOAT_FIELD(total_table_pages, "%.0f"); WRITE_FLOAT_FIELD(total_table_pages, "%.0f");
...@@ -1722,6 +1767,7 @@ _outSelectStmt(StringInfo str, SelectStmt *node) ...@@ -1722,6 +1767,7 @@ _outSelectStmt(StringInfo str, SelectStmt *node)
WRITE_NODE_FIELD(whereClause); WRITE_NODE_FIELD(whereClause);
WRITE_NODE_FIELD(groupClause); WRITE_NODE_FIELD(groupClause);
WRITE_NODE_FIELD(havingClause); WRITE_NODE_FIELD(havingClause);
WRITE_NODE_FIELD(windowClause);
WRITE_NODE_FIELD(withClause); WRITE_NODE_FIELD(withClause);
WRITE_NODE_FIELD(valuesLists); WRITE_NODE_FIELD(valuesLists);
WRITE_NODE_FIELD(sortClause); WRITE_NODE_FIELD(sortClause);
...@@ -1744,6 +1790,7 @@ _outFuncCall(StringInfo str, FuncCall *node) ...@@ -1744,6 +1790,7 @@ _outFuncCall(StringInfo str, FuncCall *node)
WRITE_BOOL_FIELD(agg_star); WRITE_BOOL_FIELD(agg_star);
WRITE_BOOL_FIELD(agg_distinct); WRITE_BOOL_FIELD(agg_distinct);
WRITE_BOOL_FIELD(func_variadic); WRITE_BOOL_FIELD(func_variadic);
WRITE_NODE_FIELD(over);
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
...@@ -1866,6 +1913,7 @@ _outQuery(StringInfo str, Query *node) ...@@ -1866,6 +1913,7 @@ _outQuery(StringInfo str, Query *node)
WRITE_INT_FIELD(resultRelation); WRITE_INT_FIELD(resultRelation);
WRITE_NODE_FIELD(intoClause); WRITE_NODE_FIELD(intoClause);
WRITE_BOOL_FIELD(hasAggs); WRITE_BOOL_FIELD(hasAggs);
WRITE_BOOL_FIELD(hasWindowFuncs);
WRITE_BOOL_FIELD(hasSubLinks); WRITE_BOOL_FIELD(hasSubLinks);
WRITE_BOOL_FIELD(hasDistinctOn); WRITE_BOOL_FIELD(hasDistinctOn);
WRITE_BOOL_FIELD(hasRecursive); WRITE_BOOL_FIELD(hasRecursive);
...@@ -1876,6 +1924,7 @@ _outQuery(StringInfo str, Query *node) ...@@ -1876,6 +1924,7 @@ _outQuery(StringInfo str, Query *node)
WRITE_NODE_FIELD(returningList); WRITE_NODE_FIELD(returningList);
WRITE_NODE_FIELD(groupClause); WRITE_NODE_FIELD(groupClause);
WRITE_NODE_FIELD(havingQual); WRITE_NODE_FIELD(havingQual);
WRITE_NODE_FIELD(windowClause);
WRITE_NODE_FIELD(distinctClause); WRITE_NODE_FIELD(distinctClause);
WRITE_NODE_FIELD(sortClause); WRITE_NODE_FIELD(sortClause);
WRITE_NODE_FIELD(limitOffset); WRITE_NODE_FIELD(limitOffset);
...@@ -1895,6 +1944,19 @@ _outSortGroupClause(StringInfo str, SortGroupClause *node) ...@@ -1895,6 +1944,19 @@ _outSortGroupClause(StringInfo str, SortGroupClause *node)
WRITE_BOOL_FIELD(nulls_first); WRITE_BOOL_FIELD(nulls_first);
} }
static void
_outWindowClause(StringInfo str, WindowClause *node)
{
WRITE_NODE_TYPE("WINDOWCLAUSE");
WRITE_STRING_FIELD(name);
WRITE_STRING_FIELD(refname);
WRITE_NODE_FIELD(partitionClause);
WRITE_NODE_FIELD(orderClause);
WRITE_UINT_FIELD(winref);
WRITE_BOOL_FIELD(copiedOrder);
}
static void static void
_outRowMarkClause(StringInfo str, RowMarkClause *node) _outRowMarkClause(StringInfo str, RowMarkClause *node)
{ {
...@@ -2171,6 +2233,18 @@ _outSortBy(StringInfo str, SortBy *node) ...@@ -2171,6 +2233,18 @@ _outSortBy(StringInfo str, SortBy *node)
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
static void
_outWindowDef(StringInfo str, WindowDef *node)
{
WRITE_NODE_TYPE("WINDOWDEF");
WRITE_STRING_FIELD(name);
WRITE_STRING_FIELD(refname);
WRITE_NODE_FIELD(partitionClause);
WRITE_NODE_FIELD(orderClause);
WRITE_LOCATION_FIELD(location);
}
static void static void
_outRangeSubselect(StringInfo str, RangeSubselect *node) _outRangeSubselect(StringInfo str, RangeSubselect *node)
{ {
...@@ -2347,6 +2421,9 @@ _outNode(StringInfo str, void *obj) ...@@ -2347,6 +2421,9 @@ _outNode(StringInfo str, void *obj)
case T_Agg: case T_Agg:
_outAgg(str, obj); _outAgg(str, obj);
break; break;
case T_WindowAgg:
_outWindowAgg(str, obj);
break;
case T_Group: case T_Group:
_outGroup(str, obj); _outGroup(str, obj);
break; break;
...@@ -2392,6 +2469,9 @@ _outNode(StringInfo str, void *obj) ...@@ -2392,6 +2469,9 @@ _outNode(StringInfo str, void *obj)
case T_Aggref: case T_Aggref:
_outAggref(str, obj); _outAggref(str, obj);
break; break;
case T_WindowFunc:
_outWindowFunc(str, obj);
break;
case T_ArrayRef: case T_ArrayRef:
_outArrayRef(str, obj); _outArrayRef(str, obj);
break; break;
...@@ -2616,6 +2696,9 @@ _outNode(StringInfo str, void *obj) ...@@ -2616,6 +2696,9 @@ _outNode(StringInfo str, void *obj)
case T_SortGroupClause: case T_SortGroupClause:
_outSortGroupClause(str, obj); _outSortGroupClause(str, obj);
break; break;
case T_WindowClause:
_outWindowClause(str, obj);
break;
case T_RowMarkClause: case T_RowMarkClause:
_outRowMarkClause(str, obj); _outRowMarkClause(str, obj);
break; break;
...@@ -2661,6 +2744,9 @@ _outNode(StringInfo str, void *obj) ...@@ -2661,6 +2744,9 @@ _outNode(StringInfo str, void *obj)
case T_SortBy: case T_SortBy:
_outSortBy(str, obj); _outSortBy(str, obj);
break; break;
case T_WindowDef:
_outWindowDef(str, obj);
break;
case T_RangeSubselect: case T_RangeSubselect:
_outRangeSubselect(str, obj); _outRangeSubselect(str, obj);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.217 2008/11/15 19:43:46 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.218 2008/12/28 18:53:56 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
...@@ -153,6 +153,7 @@ _readQuery(void) ...@@ -153,6 +153,7 @@ _readQuery(void)
READ_INT_FIELD(resultRelation); READ_INT_FIELD(resultRelation);
READ_NODE_FIELD(intoClause); READ_NODE_FIELD(intoClause);
READ_BOOL_FIELD(hasAggs); READ_BOOL_FIELD(hasAggs);
READ_BOOL_FIELD(hasWindowFuncs);
READ_BOOL_FIELD(hasSubLinks); READ_BOOL_FIELD(hasSubLinks);
READ_BOOL_FIELD(hasDistinctOn); READ_BOOL_FIELD(hasDistinctOn);
READ_BOOL_FIELD(hasRecursive); READ_BOOL_FIELD(hasRecursive);
...@@ -163,6 +164,7 @@ _readQuery(void) ...@@ -163,6 +164,7 @@ _readQuery(void)
READ_NODE_FIELD(returningList); READ_NODE_FIELD(returningList);
READ_NODE_FIELD(groupClause); READ_NODE_FIELD(groupClause);
READ_NODE_FIELD(havingQual); READ_NODE_FIELD(havingQual);
READ_NODE_FIELD(windowClause);
READ_NODE_FIELD(distinctClause); READ_NODE_FIELD(distinctClause);
READ_NODE_FIELD(sortClause); READ_NODE_FIELD(sortClause);
READ_NODE_FIELD(limitOffset); READ_NODE_FIELD(limitOffset);
...@@ -217,6 +219,24 @@ _readSortGroupClause(void) ...@@ -217,6 +219,24 @@ _readSortGroupClause(void)
READ_DONE(); READ_DONE();
} }
/*
* _readWindowClause
*/
static WindowClause *
_readWindowClause(void)
{
READ_LOCALS(WindowClause);
READ_STRING_FIELD(name);
READ_STRING_FIELD(refname);
READ_NODE_FIELD(partitionClause);
READ_NODE_FIELD(orderClause);
READ_UINT_FIELD(winref);
READ_BOOL_FIELD(copiedOrder);
READ_DONE();
}
/* /*
* _readRowMarkClause * _readRowMarkClause
*/ */
...@@ -402,6 +422,25 @@ _readAggref(void) ...@@ -402,6 +422,25 @@ _readAggref(void)
READ_DONE(); READ_DONE();
} }
/*
* _readWindowFunc
*/
static WindowFunc *
_readWindowFunc(void)
{
READ_LOCALS(WindowFunc);
READ_OID_FIELD(winfnoid);
READ_OID_FIELD(wintype);
READ_NODE_FIELD(args);
READ_UINT_FIELD(winref);
READ_BOOL_FIELD(winstar);
READ_BOOL_FIELD(winagg);
READ_LOCATION_FIELD(location);
READ_DONE();
}
/* /*
* _readArrayRef * _readArrayRef
*/ */
...@@ -1091,6 +1130,8 @@ parseNodeString(void) ...@@ -1091,6 +1130,8 @@ parseNodeString(void)
return_value = _readQuery(); return_value = _readQuery();
else if (MATCH("SORTGROUPCLAUSE", 15)) else if (MATCH("SORTGROUPCLAUSE", 15))
return_value = _readSortGroupClause(); return_value = _readSortGroupClause();
else if (MATCH("WINDOWCLAUSE", 12))
return_value = _readWindowClause();
else if (MATCH("ROWMARKCLAUSE", 13)) else if (MATCH("ROWMARKCLAUSE", 13))
return_value = _readRowMarkClause(); return_value = _readRowMarkClause();
else if (MATCH("COMMONTABLEEXPR", 15)) else if (MATCH("COMMONTABLEEXPR", 15))
...@@ -1111,6 +1152,8 @@ parseNodeString(void) ...@@ -1111,6 +1152,8 @@ parseNodeString(void)
return_value = _readParam(); return_value = _readParam();
else if (MATCH("AGGREF", 6)) else if (MATCH("AGGREF", 6))
return_value = _readAggref(); return_value = _readAggref();
else if (MATCH("WINDOWFUNC", 10))
return_value = _readWindowFunc();
else if (MATCH("ARRAYREF", 8)) else if (MATCH("ARRAYREF", 8))
return_value = _readArrayRef(); return_value = _readArrayRef();
else if (MATCH("FUNCEXPR", 8)) else if (MATCH("FUNCEXPR", 8))
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.177 2008/11/15 19:43:46 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.178 2008/12/28 18:53:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -929,10 +929,13 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels) ...@@ -929,10 +929,13 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
* 1. If the subquery has a LIMIT clause, we must not push down any quals, * 1. If the subquery has a LIMIT clause, we must not push down any quals,
* since that could change the set of rows returned. * since that could change the set of rows returned.
* *
* 2. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push * 2. If the subquery contains any window functions, we can't push quals
* into it, because that would change the results.
*
* 3. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push
* quals into it, because that would change the results. * quals into it, because that would change the results.
* *
* 3. For subqueries using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can * 4. For subqueries using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can
* push quals into each component query, but the quals can only reference * push quals into each component query, but the quals can only reference
* subquery columns that suffer no type coercions in the set operation. * subquery columns that suffer no type coercions in the set operation.
* Otherwise there are possible semantic gotchas. So, we check the * Otherwise there are possible semantic gotchas. So, we check the
...@@ -950,6 +953,10 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery, ...@@ -950,6 +953,10 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
if (subquery->limitOffset != NULL || subquery->limitCount != NULL) if (subquery->limitOffset != NULL || subquery->limitCount != NULL)
return false; return false;
/* Check point 2 */
if (subquery->hasWindowFuncs)
return false;
/* Are we at top level, or looking at a setop component? */ /* Are we at top level, or looking at a setop component? */
if (subquery == topquery) if (subquery == topquery)
{ {
...@@ -1092,6 +1099,12 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual, ...@@ -1092,6 +1099,12 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
if (contain_subplans(qual)) if (contain_subplans(qual))
return false; return false;
/*
* It would be unsafe to push down window function calls, but at least
* for the moment we could never see any in a qual anyhow.
*/
Assert(!contain_window_function(qual));
/* /*
* Examine all Vars used in clause; since it's a restriction clause, all * Examine all Vars used in clause; since it's a restriction clause, all
* such Vars must refer to subselect output columns. * such Vars must refer to subselect output columns.
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,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/path/costsize.c,v 1.201 2008/11/22 22:47:05 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.202 2008/12/28 18:53:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1283,6 +1283,40 @@ cost_agg(Path *path, PlannerInfo *root, ...@@ -1283,6 +1283,40 @@ cost_agg(Path *path, PlannerInfo *root,
path->total_cost = total_cost; path->total_cost = total_cost;
} }
/*
* cost_windowagg
* Determines and returns the cost of performing a WindowAgg plan node,
* including the cost of its input.
*
* Input is assumed already properly sorted.
*/
void
cost_windowagg(Path *path, PlannerInfo *root,
int numWindowFuncs, int numPartCols, int numOrderCols,
Cost input_startup_cost, Cost input_total_cost,
double input_tuples)
{
Cost startup_cost;
Cost total_cost;
startup_cost = input_startup_cost;
total_cost = input_total_cost;
/*
* We charge one cpu_operator_cost per window function per tuple (often a
* drastic underestimate, but without a way to gauge how many tuples the
* window function will fetch, it's hard to do better). We also charge
* cpu_operator_cost per grouping column per tuple for grouping
* comparisons, plus cpu_tuple_cost per tuple for general overhead.
*/
total_cost += cpu_operator_cost * input_tuples * numWindowFuncs;
total_cost += cpu_operator_cost * input_tuples * (numPartCols + numOrderCols);
total_cost += cpu_tuple_cost * input_tuples;
path->startup_cost = startup_cost;
path->total_cost = total_cost;
}
/* /*
* cost_group * cost_group
* Determines and returns the cost of performing a Group plan node, * Determines and returns the cost of performing a Group plan node,
...@@ -2155,6 +2189,11 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) ...@@ -2155,6 +2189,11 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
* Vars and Consts are charged zero, and so are boolean operators (AND, * Vars and Consts are charged zero, and so are boolean operators (AND,
* OR, NOT). Simplistic, but a lot better than no model at all. * OR, NOT). Simplistic, but a lot better than no model at all.
* *
* Note that Aggref and WindowFunc nodes are (and should be) treated
* like Vars --- whatever execution cost they have is absorbed into
* plan-node-specific costing. As far as expression evaluation is
* concerned they're just like Vars.
*
* Should we try to account for the possibility of short-circuit * Should we try to account for the possibility of short-circuit
* evaluation of AND/OR? Probably *not*, because that would make the * evaluation of AND/OR? Probably *not*, because that would make the
* results depend on the clause ordering, and we are not in any position * results depend on the clause ordering, and we are not in any position
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,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/path/equivclass.c,v 1.14 2008/12/01 21:06:13 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/path/equivclass.c,v 1.15 2008/12/28 18:53:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -438,14 +438,16 @@ get_eclass_for_sort_expr(PlannerInfo *root, ...@@ -438,14 +438,16 @@ get_eclass_for_sort_expr(PlannerInfo *root,
/* /*
* add_eq_member doesn't check for volatile functions, set-returning * add_eq_member doesn't check for volatile functions, set-returning
* functions, or aggregates, but such could appear in sort expressions; so * functions, aggregates, or window functions, but such could appear
* we have to check whether its const-marking was correct. * in sort expressions; so we have to check whether its const-marking
* was correct.
*/ */
if (newec->ec_has_const) if (newec->ec_has_const)
{ {
if (newec->ec_has_volatile || if (newec->ec_has_volatile ||
expression_returns_set((Node *) expr) || expression_returns_set((Node *) expr) ||
contain_agg_clause((Node *) expr)) contain_agg_clause((Node *) expr) ||
contain_window_function((Node *) expr))
{ {
newec->ec_has_const = false; newec->ec_has_const = false;
newem->em_is_const = false; newem->em_is_const = false;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.252 2008/11/20 19:52:54 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.253 2008/12/28 18:53:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -3237,8 +3237,8 @@ make_agg(PlannerInfo *root, List *tlist, List *qual, ...@@ -3237,8 +3237,8 @@ make_agg(PlannerInfo *root, List *tlist, List *qual,
* anything for Aggref nodes; this is okay since they are really * anything for Aggref nodes; this is okay since they are really
* comparable to Vars. * comparable to Vars.
* *
* See notes in grouping_planner about why this routine and make_group are * See notes in grouping_planner about why only make_agg, make_windowagg
* the only ones in this file that worry about tlist eval cost. * and make_group worry about tlist eval cost.
*/ */
if (qual) if (qual)
{ {
...@@ -3260,6 +3260,53 @@ make_agg(PlannerInfo *root, List *tlist, List *qual, ...@@ -3260,6 +3260,53 @@ make_agg(PlannerInfo *root, List *tlist, List *qual,
return node; return node;
} }
WindowAgg *
make_windowagg(PlannerInfo *root, List *tlist, int numWindowFuncs,
int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
Plan *lefttree)
{
WindowAgg *node = makeNode(WindowAgg);
Plan *plan = &node->plan;
Path windowagg_path; /* dummy for result of cost_windowagg */
QualCost qual_cost;
node->partNumCols = partNumCols;
node->partColIdx = partColIdx;
node->partOperators = partOperators;
node->ordNumCols = ordNumCols;
node->ordColIdx = ordColIdx;
node->ordOperators = ordOperators;
copy_plan_costsize(plan, lefttree); /* only care about copying size */
cost_windowagg(&windowagg_path, root,
numWindowFuncs, partNumCols, ordNumCols,
lefttree->startup_cost,
lefttree->total_cost,
lefttree->plan_rows);
plan->startup_cost = windowagg_path.startup_cost;
plan->total_cost = windowagg_path.total_cost;
/*
* We also need to account for the cost of evaluation of the tlist.
*
* See notes in grouping_planner about why only make_agg, make_windowagg
* and make_group worry about tlist eval cost.
*/
cost_qual_eval(&qual_cost, tlist, root);
plan->startup_cost += qual_cost.startup;
plan->total_cost += qual_cost.startup;
plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
plan->targetlist = tlist;
plan->lefttree = lefttree;
plan->righttree = NULL;
/* WindowAgg nodes never have a qual clause */
plan->qual = NIL;
return node;
}
Group * Group *
make_group(PlannerInfo *root, make_group(PlannerInfo *root,
List *tlist, List *tlist,
...@@ -3300,8 +3347,8 @@ make_group(PlannerInfo *root, ...@@ -3300,8 +3347,8 @@ make_group(PlannerInfo *root,
* lower plan level and will only be copied by the Group node. Worth * lower plan level and will only be copied by the Group node. Worth
* fixing? * fixing?
* *
* See notes in grouping_planner about why this routine and make_agg are * See notes in grouping_planner about why only make_agg, make_windowagg
* the only ones in this file that worry about tlist eval cost. * and make_group worry about tlist eval cost.
*/ */
if (qual) if (qual)
{ {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.43 2008/08/25 22:42:33 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.44 2008/12/28 18:53:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -95,11 +95,11 @@ optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path) ...@@ -95,11 +95,11 @@ optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path)
/* /*
* Reject unoptimizable cases. * Reject unoptimizable cases.
* *
* We don't handle GROUP BY, because our current implementations of * We don't handle GROUP BY or windowing, because our current
* grouping require looking at all the rows anyway, and so there's not * implementations of grouping require looking at all the rows anyway,
* much point in optimizing MIN/MAX. * and so there's not much point in optimizing MIN/MAX.
*/ */
if (parse->groupClause) if (parse->groupClause || parse->hasWindowFuncs)
return NULL; return NULL;
/* /*
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.112 2008/10/22 20:17:51 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.113 2008/12/28 18:53:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -67,9 +67,9 @@ ...@@ -67,9 +67,9 @@
* PlannerInfo field and not a passed parameter is that the low-level routines * PlannerInfo field and not a passed parameter is that the low-level routines
* in indxpath.c need to see it.) * in indxpath.c need to see it.)
* *
* Note: the PlannerInfo node also includes group_pathkeys, distinct_pathkeys, * Note: the PlannerInfo node also includes group_pathkeys, window_pathkeys,
* and sort_pathkeys, which like query_pathkeys need to be canonicalized once * distinct_pathkeys, and sort_pathkeys, which like query_pathkeys need to be
* the info is available. * canonicalized once the info is available.
* *
* tuple_fraction is interpreted as follows: * tuple_fraction is interpreted as follows:
* 0: expect all tuples to be retrieved (normal case) * 0: expect all tuples to be retrieved (normal case)
...@@ -121,6 +121,8 @@ query_planner(PlannerInfo *root, List *tlist, ...@@ -121,6 +121,8 @@ query_planner(PlannerInfo *root, List *tlist,
root->query_pathkeys); root->query_pathkeys);
root->group_pathkeys = canonicalize_pathkeys(root, root->group_pathkeys = canonicalize_pathkeys(root,
root->group_pathkeys); root->group_pathkeys);
root->window_pathkeys = canonicalize_pathkeys(root,
root->window_pathkeys);
root->distinct_pathkeys = canonicalize_pathkeys(root, root->distinct_pathkeys = canonicalize_pathkeys(root,
root->distinct_pathkeys); root->distinct_pathkeys);
root->sort_pathkeys = canonicalize_pathkeys(root, root->sort_pathkeys = canonicalize_pathkeys(root,
...@@ -228,11 +230,12 @@ query_planner(PlannerInfo *root, List *tlist, ...@@ -228,11 +230,12 @@ query_planner(PlannerInfo *root, List *tlist,
/* /*
* We have completed merging equivalence sets, so it's now possible to * We have completed merging equivalence sets, so it's now possible to
* convert the requested query_pathkeys to canonical form. Also * convert the requested query_pathkeys to canonical form. Also
* canonicalize the groupClause, distinctClause and sortClause pathkeys * canonicalize the groupClause, windowClause, distinctClause and
* for use later. * sortClause pathkeys for use later.
*/ */
root->query_pathkeys = canonicalize_pathkeys(root, root->query_pathkeys); root->query_pathkeys = canonicalize_pathkeys(root, root->query_pathkeys);
root->group_pathkeys = canonicalize_pathkeys(root, root->group_pathkeys); root->group_pathkeys = canonicalize_pathkeys(root, root->group_pathkeys);
root->window_pathkeys = canonicalize_pathkeys(root, root->window_pathkeys);
root->distinct_pathkeys = canonicalize_pathkeys(root, root->distinct_pathkeys); root->distinct_pathkeys = canonicalize_pathkeys(root, root->distinct_pathkeys);
root->sort_pathkeys = canonicalize_pathkeys(root, root->sort_pathkeys); root->sort_pathkeys = canonicalize_pathkeys(root, root->sort_pathkeys);
...@@ -287,10 +290,12 @@ query_planner(PlannerInfo *root, List *tlist, ...@@ -287,10 +290,12 @@ query_planner(PlannerInfo *root, List *tlist,
* If both GROUP BY and ORDER BY are specified, we will need two * If both GROUP BY and ORDER BY are specified, we will need two
* levels of sort --- and, therefore, certainly need to read all the * levels of sort --- and, therefore, certainly need to read all the
* tuples --- unless ORDER BY is a subset of GROUP BY. Likewise if * tuples --- unless ORDER BY is a subset of GROUP BY. Likewise if
* we have both DISTINCT and GROUP BY. * we have both DISTINCT and GROUP BY, or if we have a window
* specification not compatible with the GROUP BY.
*/ */
if (!pathkeys_contained_in(root->sort_pathkeys, root->group_pathkeys) || if (!pathkeys_contained_in(root->sort_pathkeys, root->group_pathkeys) ||
!pathkeys_contained_in(root->distinct_pathkeys, root->group_pathkeys)) !pathkeys_contained_in(root->distinct_pathkeys, root->group_pathkeys) ||
!pathkeys_contained_in(root->window_pathkeys, root->group_pathkeys))
tuple_fraction = 0.0; tuple_fraction = 0.0;
} }
else if (parse->hasAggs || root->hasHavingQual) else if (parse->hasAggs || root->hasHavingQual)
......
This diff is collapsed.
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.146 2008/10/21 20:42:53 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.147 2008/12/28 18:53:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -415,6 +415,7 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset) ...@@ -415,6 +415,7 @@ 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;
...@@ -679,6 +680,11 @@ fix_expr_common(PlannerGlobal *glob, Node *node) ...@@ -679,6 +680,11 @@ fix_expr_common(PlannerGlobal *glob, Node *node)
record_plan_function_dependency(glob, record_plan_function_dependency(glob,
((Aggref *) node)->aggfnoid); ((Aggref *) node)->aggfnoid);
} }
else if (IsA(node, WindowFunc))
{
record_plan_function_dependency(glob,
((WindowFunc *) node)->winfnoid);
}
else if (IsA(node, FuncExpr)) else if (IsA(node, FuncExpr))
{ {
record_plan_function_dependency(glob, record_plan_function_dependency(glob,
......
...@@ -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.143 2008/12/08 00:16:09 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.144 2008/12/28 18:53:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1243,6 +1243,7 @@ simplify_EXISTS_query(Query *query) ...@@ -1243,6 +1243,7 @@ simplify_EXISTS_query(Query *query)
query->intoClause || query->intoClause ||
query->setOperations || query->setOperations ||
query->hasAggs || query->hasAggs ||
query->hasWindowFuncs ||
query->havingQual || query->havingQual ||
query->limitOffset || query->limitOffset ||
query->limitCount || query->limitCount ||
...@@ -1258,13 +1259,14 @@ simplify_EXISTS_query(Query *query) ...@@ -1258,13 +1259,14 @@ simplify_EXISTS_query(Query *query)
/* /*
* Otherwise, we can throw away the targetlist, as well as any GROUP, * Otherwise, we can throw away the targetlist, as well as any GROUP,
* DISTINCT, and ORDER BY clauses; none of those clauses will change * WINDOW, DISTINCT, and ORDER BY clauses; none of those clauses will
* a nonzero-rows result to zero rows or vice versa. (Furthermore, * change a nonzero-rows result to zero rows or vice versa. (Furthermore,
* since our parsetree representation of these clauses depends on the * since our parsetree representation of these clauses depends on the
* targetlist, we'd better throw them away if we drop the targetlist.) * targetlist, we'd better throw them away if we drop the targetlist.)
*/ */
query->targetList = NIL; query->targetList = NIL;
query->groupClause = NIL; query->groupClause = NIL;
query->windowClause = NIL;
query->distinctClause = NIL; query->distinctClause = NIL;
query->sortClause = NIL; query->sortClause = NIL;
query->hasDistinctOn = false; query->hasDistinctOn = false;
...@@ -1321,8 +1323,8 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect, ...@@ -1321,8 +1323,8 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
* The rest of the sub-select must not refer to any Vars of the parent * The rest of the sub-select must not refer to any Vars of the parent
* query. (Vars of higher levels should be okay, though.) * query. (Vars of higher levels should be okay, though.)
* *
* Note: we need not check for Aggs separately because we know the * Note: we need not check for Aggrefs separately because we know the
* sub-select is as yet unoptimized; any uplevel Agg must therefore * sub-select is as yet unoptimized; any uplevel Aggref must therefore
* contain an uplevel Var reference. This is not the case below ... * contain an uplevel Var reference. This is not the case below ...
*/ */
if (contain_vars_of_level((Node *) subselect, 1)) if (contain_vars_of_level((Node *) subselect, 1))
...@@ -1432,7 +1434,8 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect, ...@@ -1432,7 +1434,8 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
/* /*
* And there can't be any child Vars in the stuff we intend to pull up. * And there can't be any child Vars in the stuff we intend to pull up.
* (Note: we'd need to check for child Aggs too, except we know the * (Note: we'd need to check for child Aggs too, except we know the
* child has no aggs at all because of simplify_EXISTS_query's check.) * child has no aggs at all because of simplify_EXISTS_query's check.
* The same goes for window functions.)
*/ */
if (contain_vars_of_level((Node *) leftargs, 0)) if (contain_vars_of_level((Node *) leftargs, 0))
return NULL; return NULL;
...@@ -1955,6 +1958,7 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params) ...@@ -1955,6 +1958,7 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
case T_RecursiveUnion: case T_RecursiveUnion:
case T_Hash: case T_Hash:
case T_Agg: case T_Agg:
case T_WindowAgg:
case T_SeqScan: case T_SeqScan:
case T_Material: case T_Material:
case T_Sort: case T_Sort:
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.60 2008/11/11 19:05:21 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.61 2008/12/28 18:53:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -742,7 +742,10 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, ...@@ -742,7 +742,10 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
* Miscellaneous housekeeping. * Miscellaneous housekeeping.
*/ */
parse->hasSubLinks |= subquery->hasSubLinks; parse->hasSubLinks |= subquery->hasSubLinks;
/* subquery won't be pulled up if it hasAggs, so no work there */ /*
* subquery won't be pulled up if it hasAggs or hasWindowFuncs, so no
* work needed on those flags
*/
/* /*
* Return the adjusted subquery jointree to replace the RangeTblRef entry * Return the adjusted subquery jointree to replace the RangeTblRef entry
...@@ -931,6 +934,7 @@ is_simple_subquery(Query *subquery) ...@@ -931,6 +934,7 @@ is_simple_subquery(Query *subquery)
* limiting, or WITH. (XXX WITH could possibly be allowed later) * limiting, or WITH. (XXX WITH could possibly be allowed later)
*/ */
if (subquery->hasAggs || if (subquery->hasAggs ||
subquery->hasWindowFuncs ||
subquery->groupClause || subquery->groupClause ||
subquery->havingQual || subquery->havingQual ||
subquery->sortClause || subquery->sortClause ||
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.162 2008/11/15 19:43:46 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.163 2008/12/28 18:53:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -136,6 +136,7 @@ plan_set_operations(PlannerInfo *root, double tuple_fraction, ...@@ -136,6 +136,7 @@ plan_set_operations(PlannerInfo *root, double tuple_fraction,
Assert(parse->jointree->quals == NULL); Assert(parse->jointree->quals == NULL);
Assert(parse->groupClause == NIL); Assert(parse->groupClause == NIL);
Assert(parse->havingQual == NULL); Assert(parse->havingQual == NULL);
Assert(parse->windowClause == NIL);
Assert(parse->distinctClause == NIL); Assert(parse->distinctClause == NIL);
/* /*
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.83 2008/10/21 20:42:53 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.84 2008/12/28 18:53:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -101,28 +101,28 @@ flatten_tlist(List *tlist) ...@@ -101,28 +101,28 @@ flatten_tlist(List *tlist)
/* /*
* add_to_flat_tlist * add_to_flat_tlist
* Add more vars to a flattened tlist (if they're not already in it) * Add more items to a flattened tlist (if they're not already in it)
* *
* 'tlist' is the flattened tlist * 'tlist' is the flattened tlist
* 'vars' is a list of Var and/or PlaceHolderVar nodes * 'exprs' is a list of expressions (usually, but not necessarily, Vars)
* *
* Returns the extended tlist. * Returns the extended tlist.
*/ */
List * List *
add_to_flat_tlist(List *tlist, List *vars) add_to_flat_tlist(List *tlist, List *exprs)
{ {
int next_resno = list_length(tlist) + 1; int next_resno = list_length(tlist) + 1;
ListCell *v; ListCell *lc;
foreach(v, vars) foreach(lc, exprs)
{ {
Node *var = (Node *) lfirst(v); Node *expr = (Node *) lfirst(lc);
if (!tlist_member(var, tlist)) if (!tlist_member(expr, tlist))
{ {
TargetEntry *tle; TargetEntry *tle;
tle = makeTargetEntry(copyObject(var), /* copy needed?? */ tle = makeTargetEntry(copyObject(expr), /* copy needed?? */
next_resno++, next_resno++,
NULL, NULL,
false); false);
......
This diff is collapsed.
This diff is collapsed.
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.206 2008/12/19 16:25:17 petere Exp $ * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.207 2008/12/28 18:53:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -287,12 +287,14 @@ const ScanKeyword ScanKeywords[] = { ...@@ -287,12 +287,14 @@ const ScanKeyword ScanKeywords[] = {
{"order", ORDER, RESERVED_KEYWORD}, {"order", ORDER, RESERVED_KEYWORD},
{"out", OUT_P, COL_NAME_KEYWORD}, {"out", OUT_P, COL_NAME_KEYWORD},
{"outer", OUTER_P, TYPE_FUNC_NAME_KEYWORD}, {"outer", OUTER_P, TYPE_FUNC_NAME_KEYWORD},
{"over", OVER, TYPE_FUNC_NAME_KEYWORD},
{"overlaps", OVERLAPS, TYPE_FUNC_NAME_KEYWORD}, {"overlaps", OVERLAPS, TYPE_FUNC_NAME_KEYWORD},
{"overlay", OVERLAY, COL_NAME_KEYWORD}, {"overlay", OVERLAY, COL_NAME_KEYWORD},
{"owned", OWNED, UNRESERVED_KEYWORD}, {"owned", OWNED, UNRESERVED_KEYWORD},
{"owner", OWNER, UNRESERVED_KEYWORD}, {"owner", OWNER, UNRESERVED_KEYWORD},
{"parser", PARSER, UNRESERVED_KEYWORD}, {"parser", PARSER, UNRESERVED_KEYWORD},
{"partial", PARTIAL, UNRESERVED_KEYWORD}, {"partial", PARTIAL, UNRESERVED_KEYWORD},
{"partition", PARTITION, UNRESERVED_KEYWORD},
{"password", PASSWORD, UNRESERVED_KEYWORD}, {"password", PASSWORD, UNRESERVED_KEYWORD},
{"placing", PLACING, RESERVED_KEYWORD}, {"placing", PLACING, RESERVED_KEYWORD},
{"plans", PLANS, UNRESERVED_KEYWORD}, {"plans", PLANS, UNRESERVED_KEYWORD},
...@@ -411,6 +413,7 @@ const ScanKeyword ScanKeywords[] = { ...@@ -411,6 +413,7 @@ const ScanKeyword ScanKeywords[] = {
{"when", WHEN, RESERVED_KEYWORD}, {"when", WHEN, RESERVED_KEYWORD},
{"where", WHERE, RESERVED_KEYWORD}, {"where", WHERE, RESERVED_KEYWORD},
{"whitespace", WHITESPACE_P, UNRESERVED_KEYWORD}, {"whitespace", WHITESPACE_P, UNRESERVED_KEYWORD},
{"window", WINDOW, RESERVED_KEYWORD},
{"with", WITH, RESERVED_KEYWORD}, {"with", WITH, RESERVED_KEYWORD},
{"without", WITHOUT, UNRESERVED_KEYWORD}, {"without", WITHOUT, UNRESERVED_KEYWORD},
{"work", WORK, UNRESERVED_KEYWORD}, {"work", WORK, UNRESERVED_KEYWORD},
......
This diff is collapsed.
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.172 2008/12/14 19:45:52 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.173 2008/12/28 18:53:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -746,6 +746,7 @@ build_coercion_expression(Node *node, ...@@ -746,6 +746,7 @@ build_coercion_expression(Node *node,
/* Assert(targetTypeId == procstruct->prorettype); */ /* Assert(targetTypeId == procstruct->prorettype); */
Assert(!procstruct->proretset); Assert(!procstruct->proretset);
Assert(!procstruct->proisagg); Assert(!procstruct->proisagg);
Assert(!procstruct->proiswindow);
nargs = procstruct->pronargs; nargs = procstruct->pronargs;
Assert(nargs >= 1 && nargs <= 3); Assert(nargs >= 1 && nargs <= 3);
/* Assert(procstruct->proargtypes.values[0] == exprType(node)); */ /* Assert(procstruct->proargtypes.values[0] == exprType(node)); */
......
This diff is collapsed.
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.100 2008/10/04 21:56:54 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.101 2008/12/28 18:53:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -611,6 +611,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod_p) ...@@ -611,6 +611,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod_p)
stmt->whereClause != NULL || stmt->whereClause != NULL ||
stmt->groupClause != NIL || stmt->groupClause != NIL ||
stmt->havingClause != NULL || stmt->havingClause != NULL ||
stmt->windowClause != NIL ||
stmt->withClause != NULL || stmt->withClause != NULL ||
stmt->valuesLists != NIL || stmt->valuesLists != NIL ||
stmt->sortClause != NIL || stmt->sortClause != NIL ||
......
This diff is collapsed.
This diff is collapsed.
# #
# Makefile for utils/adt # Makefile for utils/adt
# #
# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.70 2008/11/03 20:17:20 adunstan Exp $ # $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.71 2008/12/28 18:53:59 tgl Exp $
# #
subdir = src/backend/utils/adt subdir = src/backend/utils/adt
...@@ -29,7 +29,7 @@ OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \ ...@@ -29,7 +29,7 @@ OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
tsginidx.o tsgistidx.o tsquery.o tsquery_cleanup.o tsquery_gist.o \ tsginidx.o tsgistidx.o tsquery.o tsquery_cleanup.o tsquery_gist.o \
tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \ tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
tsvector.o tsvector_op.o tsvector_parser.o \ tsvector.o tsvector_op.o tsvector_parser.o \
txid.o uuid.o xml.o txid.o uuid.o windowfuncs.o xml.o
like.o: like.c like_match.c like.o: like.c like_match.c
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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