]> granicus.if.org Git - postgresql/commitdiff
Improve the error message given for modifying a window with frame clause.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 6 Nov 2013 02:58:08 +0000 (21:58 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 6 Nov 2013 02:58:08 +0000 (21:58 -0500)
For rather inscrutable reasons, SQL:2008 disallows copying-and-modifying a
window definition that has any explicit framing clause.  The error message
we gave for this only made sense if the referencing window definition
itself contains an explicit framing clause, which it might well not.
Moreover, in the context of an OVER clause it's not exactly obvious that
"OVER (windowname)" implies copy-and-modify while "OVER windowname" does
not.  This has led to multiple complaints, eg bug #5199 from Iliya
Krapchatov.  Change to a hopefully more intelligible error message, and
in the case where we have just "OVER (windowname)", add a HINT suggesting
that omitting the parentheses will fix it.  Also improve the related
documentation.  Back-patch to all supported branches.

doc/src/sgml/syntax.sgml
src/backend/parser/parse_clause.c

index e3dbc4b5ea596a40da01455bbb74717f5c983504..4fe872290a6dc5ec40e58efd4fd3c90622ec9483 100644 (file)
@@ -1730,10 +1730,10 @@ SELECT string_agg(a ORDER BY a, ',') FROM table;  -- incorrect
     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>) [ FILTER ( WHERE <replaceable>filter_clause</replaceable> ) ] OVER ( <replaceable class="parameter">window_definition</replaceable> )
 <replaceable>function_name</replaceable> (<optional><replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ... </optional></optional>) [ FILTER ( WHERE <replaceable>filter_clause</replaceable> ) ] OVER <replaceable>window_name</replaceable>
-<replaceable>function_name</replaceable> ( * ) [ FILTER ( WHERE <replaceable>filter_clause</replaceable> ) ] OVER ( <replaceable class="parameter">window_definition</replaceable> )
+<replaceable>function_name</replaceable> (<optional><replaceable>expression</replaceable> <optional>, <replaceable>expression</replaceable> ... </optional></optional>) [ FILTER ( WHERE <replaceable>filter_clause</replaceable> ) ] OVER ( <replaceable class="parameter">window_definition</replaceable> )
 <replaceable>function_name</replaceable> ( * ) [ FILTER ( WHERE <replaceable>filter_clause</replaceable> ) ] OVER <replaceable>window_name</replaceable>
+<replaceable>function_name</replaceable> ( * ) [ FILTER ( WHERE <replaceable>filter_clause</replaceable> ) ] OVER ( <replaceable class="parameter">window_definition</replaceable> )
 </synopsis>
     where <replaceable class="parameter">window_definition</replaceable>
     has the syntax
@@ -1768,15 +1768,14 @@ UNBOUNDED FOLLOWING
    <para>
     <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 supply an ordering clause and/or frame clause (the referenced
-    window must lack these clauses, if they are supplied here).
-    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"> reference
-    page for details.
+    Alternatively, a full <replaceable>window_definition</replaceable> can
+    be given within parentheses, using the same syntax as for defining a
+    named window in the <literal>WINDOW</literal> clause; see the
+    <xref linkend="sql-select"> reference page for details.  It's worth
+    pointing out that <literal>OVER wname</> is not exactly equivalent to
+    <literal>OVER (wname)</>; the latter implies copying and modifying the
+    window definition, and will be rejected if the referenced window
+    specification includes a frame clause.
    </para>
 
    <para>
@@ -1853,12 +1852,19 @@ UNBOUNDED FOLLOWING
     PRECEDING</literal> is not allowed.
    </para>
 
+   <para>
+    If <literal>FILTER</literal> is specified, then only the input
+    rows for which the <replaceable>filter_clause</replaceable>
+    evaluates to true are fed to the window function; other rows
+    are discarded.  Only aggregate window functions accept
+    a <literal>FILTER</literal> clause.
+   </para>
+
    <para>
     The built-in window functions are described in <xref
     linkend="functions-window-table">.  Other window functions can be added by
     the user.  Also, any built-in or user-defined aggregate function can be
-    used as a window function.  Only aggregate window functions accept
-    a <literal>FILTER</literal> clause.
+    used as a window function.
    </para>
 
    <para>
index ea90e58f7107867c5b8ad728b872bb1b52f7f7ab..1f6306a7d356890b88f69c72a8d3842349fde876 100644 (file)
@@ -1735,11 +1735,16 @@ transformWindowDefinitions(ParseState *pstate,
                /*
                 * Per spec, a windowdef that references a previous one copies the
                 * previous partition clause (and mustn't specify its own).  It can
-                * specify its own ordering clause. but only if the previous one had
+                * specify its own ordering clause, but only if the previous one had
                 * none.  It always specifies its own frame clause, and the previous
-                * one must not have a frame clause.  (Yeah, it's bizarre that each of
+                * one must not have a frame clause.  Yeah, it's bizarre that each of
                 * these cases works differently, but SQL:2008 says so; see 7.11
-                * <window clause> syntax rule 10 and general rule 1.)
+                * <window clause> syntax rule 10 and general rule 1.  The frame
+                * clause rule is especially bizarre because it makes "OVER foo"
+                * different from "OVER (foo)", and requires the latter to throw an
+                * error if foo has a nondefault frame clause.  Well, ours not to
+                * reason why, but we do go out of our way to throw a useful error
+                * message for such cases.
                 */
                if (refwc)
                {
@@ -1778,11 +1783,27 @@ transformWindowDefinitions(ParseState *pstate,
                        wc->copiedOrder = false;
                }
                if (refwc && refwc->frameOptions != FRAMEOPTION_DEFAULTS)
+               {
+                       /*
+                        * Use this message if this is a WINDOW clause, or if it's an OVER
+                        * clause that includes ORDER BY or framing clauses.  (We already
+                        * rejected PARTITION BY above, so no need to check that.)
+                        */
+                       if (windef->name ||
+                               orderClause || windef->frameOptions != FRAMEOPTION_DEFAULTS)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_WINDOWING_ERROR),
+                                                errmsg("cannot copy window \"%s\" because it has a frame clause",
+                                                               windef->refname),
+                                                parser_errposition(pstate, windef->location)));
+                       /* Else this clause is just OVER (foo), so say this: */
                        ereport(ERROR,
                                        (errcode(ERRCODE_WINDOWING_ERROR),
-                                        errmsg("cannot override frame clause of window \"%s\"",
-                                                       windef->refname),
+                       errmsg("cannot copy window \"%s\" because it has a frame clause",
+                                  windef->refname),
+                                        errhint("Omit the parentheses in this OVER clause."),
                                         parser_errposition(pstate, windef->location)));
+               }
                wc->frameOptions = windef->frameOptions;
                /* Process frame offset expressions */
                wc->startOffset = transformFrameOffset(pstate, wc->frameOptions,