Skip to content

Commit 66e6daa

Browse files
committed
Support default arguments and named-argument notation for window functions.
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.)
1 parent bc06fae commit 66e6daa

File tree

6 files changed

+102
-13
lines changed

6 files changed

+102
-13
lines changed

doc/src/sgml/syntax.sgml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2501,6 +2501,14 @@ SELECT concat_lower_or_upper('Hello', 'World', uppercase := true);
25012501
having numerous parameters that have default values, named or mixed
25022502
notation can save a great deal of writing and reduce chances for error.
25032503
</para>
2504+
2505+
<note>
2506+
<para>
2507+
Named and mixed call notations currently cannot be used when calling an
2508+
aggregate function (but they do work when an aggregate function is used
2509+
as a window function).
2510+
</para>
2511+
</note>
25042512
</sect2>
25052513
</sect1>
25062514

src/backend/optimizer/util/clauses.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2296,6 +2296,50 @@ eval_const_expressions_mutator(Node *node,
22962296
*/
22972297
return (Node *) copyObject(param);
22982298
}
2299+
case T_WindowFunc:
2300+
{
2301+
WindowFunc *expr = (WindowFunc *) node;
2302+
Oid funcid = expr->winfnoid;
2303+
List *args;
2304+
HeapTuple func_tuple;
2305+
WindowFunc *newexpr;
2306+
2307+
/*
2308+
* We can't really simplify a WindowFunc node, but we mustn't
2309+
* just fall through to the default processing, because we
2310+
* have to apply expand_function_arguments to its argument
2311+
* list. That takes care of inserting default arguments and
2312+
* expanding named-argument notation.
2313+
*/
2314+
func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
2315+
if (!HeapTupleIsValid(func_tuple))
2316+
elog(ERROR, "cache lookup failed for function %u", funcid);
2317+
2318+
args = expand_function_arguments(expr->args, expr->wintype,
2319+
func_tuple);
2320+
2321+
ReleaseSysCache(func_tuple);
2322+
2323+
/* Now, recursively simplify the args (which are a List) */
2324+
args = (List *)
2325+
expression_tree_mutator((Node *) args,
2326+
eval_const_expressions_mutator,
2327+
(void *) context);
2328+
2329+
/* And build the replacement WindowFunc node */
2330+
newexpr = makeNode(WindowFunc);
2331+
newexpr->winfnoid = expr->winfnoid;
2332+
newexpr->wintype = expr->wintype;
2333+
newexpr->wincollid = expr->wincollid;
2334+
newexpr->inputcollid = expr->inputcollid;
2335+
newexpr->args = args;
2336+
newexpr->winref = expr->winref;
2337+
newexpr->winstar = expr->winstar;
2338+
newexpr->winagg = expr->winagg;
2339+
newexpr->location = expr->location;
2340+
2341+
return (Node *) newexpr;
2342+
}
22992343
case T_FuncExpr:
23002344
{
23012345
FuncExpr *expr = (FuncExpr *) node;

src/backend/parser/parse_func.c

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -497,17 +497,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
497497
errmsg("window functions cannot return sets"),
498498
parser_errposition(pstate, location)));
499499

500-
/*
501-
* We might want to support this later, but for now reject it because
502-
* the planner and executor wouldn't cope with NamedArgExprs in a
503-
* WindowFunc node.
504-
*/
505-
if (argnames != NIL)
506-
ereport(ERROR,
507-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
508-
errmsg("window functions cannot use named arguments"),
509-
parser_errposition(pstate, location)));
510-
511500
/* parse_agg.c does additional window-func-specific processing */
512501
transformWindowFuncCall(pstate, wfunc, over);
513502

src/backend/utils/adt/ruleutils.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7455,25 +7455,28 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
74557455
StringInfo buf = context->buf;
74567456
Oid argtypes[FUNC_MAX_ARGS];
74577457
int nargs;
7458+
List *argnames;
74587459
ListCell *l;
74597460

74607461
if (list_length(wfunc->args) > FUNC_MAX_ARGS)
74617462
ereport(ERROR,
74627463
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
74637464
errmsg("too many arguments")));
74647465
nargs = 0;
7466+
argnames = NIL;
74657467
foreach(l, wfunc->args)
74667468
{
74677469
Node *arg = (Node *) lfirst(l);
74687470

7469-
Assert(!IsA(arg, NamedArgExpr));
7471+
if (IsA(arg, NamedArgExpr))
7472+
argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
74707473
argtypes[nargs] = exprType(arg);
74717474
nargs++;
74727475
}
74737476

74747477
appendStringInfo(buf, "%s(",
74757478
generate_function_name(wfunc->winfnoid, nargs,
7476-
NIL, argtypes,
7479+
argnames, argtypes,
74777480
false, NULL));
74787481
/* winstar can be set only in zero-argument aggregates */
74797482
if (wfunc->winstar)

src/test/regress/expected/window.out

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,3 +1022,38 @@ SELECT nth_value(four, 0) OVER (ORDER BY ten), ten, four FROM tenk1;
10221022
ERROR: argument of nth_value must be greater than zero
10231023
-- cleanup
10241024
DROP TABLE empsalary;
1025+
-- test user-defined window function with named args and default args
1026+
CREATE FUNCTION nth_value_def(val anyelement, n integer = 1) RETURNS anyelement
1027+
LANGUAGE internal WINDOW IMMUTABLE STRICT AS 'window_nth_value';
1028+
SELECT nth_value_def(n := 2, val := ten) OVER (PARTITION BY four), ten, four
1029+
FROM (SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten) s;
1030+
nth_value_def | ten | four
1031+
---------------+-----+------
1032+
0 | 0 | 0
1033+
0 | 0 | 0
1034+
0 | 4 | 0
1035+
1 | 1 | 1
1036+
1 | 1 | 1
1037+
1 | 7 | 1
1038+
1 | 9 | 1
1039+
| 0 | 2
1040+
3 | 1 | 3
1041+
3 | 3 | 3
1042+
(10 rows)
1043+
1044+
SELECT nth_value_def(ten) OVER (PARTITION BY four), ten, four
1045+
FROM (SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten) s;
1046+
nth_value_def | ten | four
1047+
---------------+-----+------
1048+
0 | 0 | 0
1049+
0 | 0 | 0
1050+
0 | 4 | 0
1051+
1 | 1 | 1
1052+
1 | 1 | 1
1053+
1 | 7 | 1
1054+
1 | 9 | 1
1055+
0 | 0 | 2
1056+
1 | 1 | 3
1057+
1 | 3 | 3
1058+
(10 rows)
1059+

src/test/regress/sql/window.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,13 @@ SELECT nth_value(four, 0) OVER (ORDER BY ten), ten, four FROM tenk1;
266266

267267
-- cleanup
268268
DROP TABLE empsalary;
269+
270+
-- test user-defined window function with named args and default args
271+
CREATE FUNCTION nth_value_def(val anyelement, n integer = 1) RETURNS anyelement
272+
LANGUAGE internal WINDOW IMMUTABLE STRICT AS 'window_nth_value';
273+
274+
SELECT nth_value_def(n := 2, val := ten) OVER (PARTITION BY four), ten, four
275+
FROM (SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten) s;
276+
277+
SELECT nth_value_def(ten) OVER (PARTITION BY four), ten, four
278+
FROM (SELECT * FROM tenk1 WHERE unique2 < 10 ORDER BY four, ten) s;

0 commit comments

Comments
 (0)