]> granicus.if.org Git - postgresql/commitdiff
Support default arguments and named-argument notation for window functions.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 6 Nov 2013 18:26:34 +0000 (13:26 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 6 Nov 2013 18:33:18 +0000 (13:33 -0500)
These things didn't work because the planner omitted to do the necessary
preprocessing of a WindowFunc's argument list.  Add the few dozen lines
of code needed to handle that.

Although this sounds like a feature addition, it's really a bug fix because
the default-argument case was likely to crash previously, due to lack of
checking of the number of supplied arguments in the built-in window
functions.  It's not a security issue because there's no way for a
non-superuser to create a window function definition with defaults that
refers to a built-in C function, but nonetheless people might be annoyed
that it crashes rather than producing a useful error message.  So
back-patch as far as the patch applies easily, which turns out to be 9.2.
I'll put a band-aid in earlier versions as a separate patch.

(Note that these features still don't work for aggregates, and fixing that
case will be harder since we represent aggregate arg lists as target lists
not bare expression lists.  There's no crash risk though because CREATE
AGGREGATE doesn't accept defaults, and we reject named-argument notation
when parsing an aggregate call.)

doc/src/sgml/syntax.sgml
src/backend/optimizer/util/clauses.c
src/backend/parser/parse_func.c
src/backend/utils/adt/ruleutils.c
src/test/regress/expected/window.out
src/test/regress/sql/window.sql

index 814ae4269956b90c783803af4899934427520ef8..d09c808a0487aea9b24619c8197092af4dc99d85 100644 (file)
@@ -2501,6 +2501,14 @@ SELECT concat_lower_or_upper('Hello', 'World', uppercase := true);
     having numerous parameters that have default values, named or mixed
     notation can save a great deal of writing and reduce chances for error.
    </para>
+
+   <note>
+    <para>
+     Named and mixed call notations currently cannot be used when calling an
+     aggregate function (but they do work when an aggregate function is used
+     as a window function).
+    </para>
+   </note>
   </sect2>
  </sect1>
 
index 6d5b20406e6bd5c666a90fb4cd28fbc3142c4976..7b11067090147e488d2b2b40e622241036660653 100644 (file)
@@ -2296,6 +2296,50 @@ eval_const_expressions_mutator(Node *node,
                                 */
                                return (Node *) copyObject(param);
                        }
+               case T_WindowFunc:
+                       {
+                               WindowFunc *expr = (WindowFunc *) node;
+                               Oid                     funcid = expr->winfnoid;
+                               List       *args;
+                               HeapTuple       func_tuple;
+                               WindowFunc *newexpr;
+
+                               /*
+                                * We can't really simplify a WindowFunc node, but we mustn't
+                                * just fall through to the default processing, because we
+                                * have to apply expand_function_arguments to its argument
+                                * list.  That takes care of inserting default arguments and
+                                * expanding named-argument notation.
+                                */
+                               func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+                               if (!HeapTupleIsValid(func_tuple))
+                                       elog(ERROR, "cache lookup failed for function %u", funcid);
+
+                               args = expand_function_arguments(expr->args, expr->wintype,
+                                                                                                func_tuple);
+
+                               ReleaseSysCache(func_tuple);
+
+                               /* Now, recursively simplify the args (which are a List) */
+                               args = (List *)
+                                       expression_tree_mutator((Node *) args,
+                                                                                       eval_const_expressions_mutator,
+                                                                                       (void *) context);
+
+                               /* And build the replacement WindowFunc node */
+                               newexpr = makeNode(WindowFunc);
+                               newexpr->winfnoid = expr->winfnoid;
+                               newexpr->wintype = expr->wintype;
+                               newexpr->wincollid = expr->wincollid;
+                               newexpr->inputcollid = expr->inputcollid;
+                               newexpr->args = args;
+                               newexpr->winref = expr->winref;
+                               newexpr->winstar = expr->winstar;
+                               newexpr->winagg = expr->winagg;
+                               newexpr->location = expr->location;
+
+                               return (Node *) newexpr;
+                       }
                case T_FuncExpr:
                        {
                                FuncExpr   *expr = (FuncExpr *) node;
index ae7d195a3ea45c1dca216d174216b07430157235..edb165fcc9ff9269fddf775132827617117d0806 100644 (file)
@@ -497,17 +497,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
                                         errmsg("window functions cannot return sets"),
                                         parser_errposition(pstate, location)));
 
-               /*
-                * We might want to support this later, but for now reject it because
-                * the planner and executor wouldn't cope with NamedArgExprs in a
-                * WindowFunc node.
-                */
-               if (argnames != NIL)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("window functions cannot use named arguments"),
-                                        parser_errposition(pstate, location)));
-
                /* parse_agg.c does additional window-func-specific processing */
                transformWindowFuncCall(pstate, wfunc, over);
 
index a7de92f3066942a1a22ffca4bf3c3e67c327577d..df3d8962648248ea47ff352a181546e2fed29f5f 100644 (file)
@@ -7455,6 +7455,7 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
        StringInfo      buf = context->buf;
        Oid                     argtypes[FUNC_MAX_ARGS];
        int                     nargs;
+       List       *argnames;
        ListCell   *l;
 
        if (list_length(wfunc->args) > FUNC_MAX_ARGS)
@@ -7462,18 +7463,20 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
                                (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
                                 errmsg("too many arguments")));
        nargs = 0;
+       argnames = NIL;
        foreach(l, wfunc->args)
        {
                Node       *arg = (Node *) lfirst(l);
 
-               Assert(!IsA(arg, NamedArgExpr));
+               if (IsA(arg, NamedArgExpr))
+                       argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
                argtypes[nargs] = exprType(arg);
                nargs++;
        }
 
        appendStringInfo(buf, "%s(",
                                         generate_function_name(wfunc->winfnoid, nargs,
-                                                                                       NIL, argtypes,
+                                                                                       argnames, argtypes,
                                                                                        false, NULL));
        /* winstar can be set only in zero-argument aggregates */
        if (wfunc->winstar)
index 752c7b42ff341bf895405e5f3f558dedb1986e63..200f1d2468e68df9a559ee3f8d879061d1b53578 100644 (file)
@@ -1022,3 +1022,38 @@ SELECT nth_value(four, 0) OVER (ORDER BY ten), ten, four FROM tenk1;
 ERROR:  argument of nth_value must be greater than zero
 -- cleanup
 DROP TABLE empsalary;
+-- test user-defined window function with named args and default args
+CREATE FUNCTION nth_value_def(val anyelement, n integer = 1) RETURNS anyelement
+  LANGUAGE internal WINDOW IMMUTABLE STRICT AS 'window_nth_value';
+SELECT nth_value_def(n := 2, val := ten) OVER (PARTITION BY four), ten, four
+  FROM (SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten) s;
+ nth_value_def | ten | four 
+---------------+-----+------
+             0 |   0 |    0
+             0 |   0 |    0
+             0 |   4 |    0
+             1 |   1 |    1
+             1 |   1 |    1
+             1 |   7 |    1
+             1 |   9 |    1
+               |   0 |    2
+             3 |   1 |    3
+             3 |   3 |    3
+(10 rows)
+
+SELECT nth_value_def(ten) OVER (PARTITION BY four), ten, four
+  FROM (SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten) s;
+ nth_value_def | ten | four 
+---------------+-----+------
+             0 |   0 |    0
+             0 |   0 |    0
+             0 |   4 |    0
+             1 |   1 |    1
+             1 |   1 |    1
+             1 |   7 |    1
+             1 |   9 |    1
+             0 |   0 |    2
+             1 |   1 |    3
+             1 |   3 |    3
+(10 rows)
+
index 769be0fdc61240db4bafd11f979f5064c9f47bf5..a97a04c4c28d5a7ee06fa190cc5e2b9bd43a30f6 100644 (file)
@@ -266,3 +266,13 @@ SELECT nth_value(four, 0) OVER (ORDER BY ten), ten, four FROM tenk1;
 
 -- cleanup
 DROP TABLE empsalary;
+
+-- test user-defined window function with named args and default args
+CREATE FUNCTION nth_value_def(val anyelement, n integer = 1) RETURNS anyelement
+  LANGUAGE internal WINDOW IMMUTABLE STRICT AS 'window_nth_value';
+
+SELECT nth_value_def(n := 2, val := ten) OVER (PARTITION BY four), ten, four
+  FROM (SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten) s;
+
+SELECT nth_value_def(ten) OVER (PARTITION BY four), ten, four
+  FROM (SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten) s;