On Tue, Feb 26, 2013 at 01:09:30PM -0800, David Fetter wrote: > On Wed, Feb 13, 2013 at 06:45:31AM -0800, David Fetter wrote: > > On Sat, Feb 09, 2013 at 11:59:22PM -0800, David Fetter wrote: > > > Folks, > > > > > > Per suggestions and lots of help from Andrew Gierth, please find > > > attached a patch to clean up the call sites for FuncCall nodes, which > > > I'd like to expand centrally rather than in each of the 37 (or 38, but > > > I only redid 37) places where it's called. The remaining one is in > > > src/backend/nodes/copyfuncs.c, which has to be modified for any > > > changes in the that struct anyhow. > > > > > > The immediate purpose is two-fold: to reduce some redundancies, which > > > I believe is worth doing in and of itself, and to prepare for adding > > > FILTER on aggregates from the spec, and possibly other things in > > > the <aggregate function> part of the spec. > > > > > > Cheers, > > > David. > > > > Folks, > > > > Please find attached two versions of a patch which provides optional > > FILTER clause for aggregates (T612, "Advanced OLAP operations"). > > > > The first is intended to be applied on top of the previous patch, the > > second without it. The first is, I believe, clearer in what it's > > doing. Rather than simply mechanically visiting every place a > > function call might be constructed, it visits a central one to change > > the default, then goes only to the places where it's relevant. > > > > The patches are both early WIP as they contain no docs or regression > > tests yet. > > Docs and regression tests added, makeFuncArgs approached dropped for > now, will re-visit later.
Regression tests added to reflect bug fixes in COLLATE. Cheers, David. -- David Fetter <da...@fetter.org> http://fetter.org/ Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter Skype: davidfetter XMPP: david.fet...@gmail.com iCal: webcal://www.tripit.com/feed/ical/people/david74/tripit.ics Remember to vote! Consider donating to Postgres: http://www.postgresql.org/about/donate
*** a/doc/src/sgml/ref/select.sgml --- b/doc/src/sgml/ref/select.sgml *************** *** 595,604 **** GROUP BY <replaceable class="parameter">expression</replaceable> [, ...] </para> <para> ! Aggregate functions, if any are used, are computed across all rows making up each group, producing a separate value for each group (whereas without <literal>GROUP BY</literal>, an aggregate produces a single value computed across all the selected rows). When <literal>GROUP BY</literal> is present, it is not valid for the <command>SELECT</command> list expressions to refer to ungrouped columns except within aggregate functions or if the --- 595,607 ---- </para> <para> ! In the absence of a <literal>FILTER</literal> clause, ! aggregate functions, if any are used, are computed across all rows making up each group, producing a separate value for each group (whereas without <literal>GROUP BY</literal>, an aggregate produces a single value computed across all the selected rows). + When a <literal>FILTER</literal> clause is present, only those + rows matching the FILTER clause are included. When <literal>GROUP BY</literal> is present, it is not valid for the <command>SELECT</command> list expressions to refer to ungrouped columns except within aggregate functions or if the *** a/doc/src/sgml/syntax.sgml --- b/doc/src/sgml/syntax.sgml *************** *** 1562,1585 **** sqrt(2) syntax of an aggregate expression is one of the following: <synopsis> ! <replaceable>aggregate_name</replaceable> (<replaceable>expression</replaceable> [ , ... ] [ <replaceable>order_by_clause</replaceable> ] ) ! <replaceable>aggregate_name</replaceable> (ALL <replaceable>expression</replaceable> [ , ... ] [ <replaceable>order_by_clause</replaceable> ] ) ! <replaceable>aggregate_name</replaceable> (DISTINCT <replaceable>expression</replaceable> [ , ... ] [ <replaceable>order_by_clause</replaceable> ] ) ! <replaceable>aggregate_name</replaceable> ( * ) </synopsis> where <replaceable>aggregate_name</replaceable> is a previously defined aggregate (possibly qualified with a schema name), ! <replaceable>expression</replaceable> is ! any value expression that does not itself contain an aggregate ! expression or a window function call, and ! <replaceable>order_by_clause</replaceable> is a optional ! <literal>ORDER BY</> clause as described below. </para> <para> ! The first form of aggregate expression invokes the aggregate ! once for each input row. The second form is the same as the first, since <literal>ALL</literal> is the default. The third form invokes the aggregate once for each distinct value --- 1562,1587 ---- syntax of an aggregate expression is one of the following: <synopsis> ! <replaceable>aggregate_name</replaceable> (<replaceable>expression</replaceable> [ , ... ] [ <replaceable>order_by_clause</replaceable> ] ) [ FILTER ( WHERE <replaceable>filter_clause</replaceable> ) ] ! <replaceable>aggregate_name</replaceable> (ALL <replaceable>expression</replaceable> [ , ... ] [ <replaceable>order_by_clause</replaceable> ] ) [ FILTER ( WHERE <replaceable>filter_clause</replaceable> ) ] ! <replaceable>aggregate_name</replaceable> (DISTINCT <replaceable>expression</replaceable> [ , ... ] [ <replaceable>order_by_clause</replaceable> ] ) [ FILTER ( WHERE <replaceable>filter_clause</replaceable> ) ] ! <replaceable>aggregate_name</replaceable> ( * ) [ FILTER ( WHERE <replaceable>filter_clause</replaceable> ) ] </synopsis> where <replaceable>aggregate_name</replaceable> is a previously defined aggregate (possibly qualified with a schema name), ! <replaceable>expression</replaceable> is any value expression that ! does not itself contain an aggregate expression or a window ! function call, <replaceable>order_by_clause</replaceable> is a ! optional <literal>ORDER BY</> clause as described below. The ! <replaceable>aggregate_name</replaceable> can also be suffixed ! with <literal>FILTER</literal> as described below. </para> <para> ! The first form of aggregate expression invokes the aggregate once ! for each input row, or when a FILTER clause is present, each row ! matching same. The second form is the same as the first, since <literal>ALL</literal> is the default. The third form invokes the aggregate once for each distinct value *************** *** 1607,1612 **** sqrt(2) --- 1609,1629 ---- </para> <para> + Adding a FILTER clause to an aggregate specifies which values of + the expression being aggregated to evaluate. For example: + <programlisting> + SELECT + count(*) AS unfiltered, + count(*) FILTER (WHERE i < 5) AS filtered + FROM generate_series(1,10) AS s(i); + unfiltered | filtered + ------------+---------- + 10 | 4 + (1 row) + </programlisting> + </para> + + <para> Ordinarily, the input rows are fed to the aggregate function in an unspecified order. In many cases this does not matter; for example, <function>min</> produces the same result no matter what order it *************** *** 1709,1718 **** 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>) 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 --- 1726,1735 ---- 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> ( * ) [ FILTER ( WHERE <replaceable>filter_clause</replaceable> ) ] OVER <replaceable>window_name</replaceable> </synopsis> where <replaceable class="parameter">window_definition</replaceable> has the syntax *************** *** 1836,1851 **** UNBOUNDED FOLLOWING 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. </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)</>. ! The asterisk (<literal>*</>) is customarily not used for non-aggregate window functions. ! Aggregate window functions, unlike normal aggregate functions, do not ! allow <literal>DISTINCT</> or <literal>ORDER BY</> to be used within the function argument list. </para> --- 1853,1870 ---- 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. A <literal>FILTER</literal> clause is ! only valid for aggregate functions used in windowing. </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)</>. ! The asterisk (<literal>*</>) is customarily not used for ! non-aggregate window functions. Aggregate window functions, ! unlike normal aggregate functions, do not allow ! <literal>DISTINCT</> or <literal>ORDER BY</> to be used within the function argument list. </para> *** a/src/backend/executor/execQual.c --- b/src/backend/executor/execQual.c *************** *** 4402,4407 **** ExecInitExpr(Expr *node, PlanState *parent) --- 4402,4408 ---- astate->args = (List *) ExecInitExpr((Expr *) aggref->args, parent); + astate->agg_filter = ExecInitExpr(aggref->agg_filter, parent); /* * Complain if the aggregate's arguments contain any *************** *** 4440,4445 **** ExecInitExpr(Expr *node, PlanState *parent) --- 4441,4447 ---- wfstate->args = (List *) ExecInitExpr((Expr *) wfunc->args, parent); + wfstate->agg_filter = ExecInitExpr(wfunc->agg_filter, parent); /* * Complain if the windowfunc's arguments contain any *** a/src/backend/executor/functions.c --- b/src/backend/executor/functions.c *************** *** 381,387 **** sql_fn_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var) list_make1(subfield), list_make1(param), NIL, false, false, false, ! NULL, true, cref->location); } return param; --- 381,387 ---- list_make1(subfield), list_make1(param), NIL, false, false, false, ! NULL, NULL, true, cref->location); } return param; *** a/src/backend/executor/nodeAgg.c --- b/src/backend/executor/nodeAgg.c *************** *** 488,493 **** advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup) --- 488,505 ---- int i; TupleTableSlot *slot; + /* Skip anything FILTERed out */ + ExprState *filter = peraggstate->aggrefstate->agg_filter; + if (filter) + { + MemoryContext oldcontext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory); + bool isnull; + Datum res = ExecEvalExpr(filter, aggstate->tmpcontext, &isnull, NULL); + MemoryContextSwitchTo(oldcontext); + if (isnull || !DatumGetBool(res)) + continue; + } + /* Evaluate the current input expressions for this aggregate */ slot = ExecProject(peraggstate->evalproj, NULL); *** a/src/backend/executor/nodeWindowAgg.c --- b/src/backend/executor/nodeWindowAgg.c *************** *** 227,235 **** advance_windowaggregate(WindowAggState *winstate, --- 227,248 ---- int i; MemoryContext oldContext; ExprContext *econtext = winstate->tmpcontext; + ExprState *filter = wfuncstate->agg_filter; oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + /* Skip anything FILTERed out */ + if (filter) + { + bool isnull; + Datum res = ExecEvalExpr(filter, econtext, &isnull, NULL); + if (isnull || !DatumGetBool(res)) + { + MemoryContextSwitchTo(oldContext); + return; + } + } + /* We start from 1, since the 0th arg will be the transition value */ i = 1; foreach(arg, wfuncstate->args) *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** *** 1137,1142 **** _copyAggref(const Aggref *from) --- 1137,1143 ---- COPY_NODE_FIELD(args); COPY_NODE_FIELD(aggorder); COPY_NODE_FIELD(aggdistinct); + COPY_NODE_FIELD(agg_filter); COPY_SCALAR_FIELD(aggstar); COPY_SCALAR_FIELD(agglevelsup); COPY_LOCATION_FIELD(location); *************** *** 1157,1162 **** _copyWindowFunc(const WindowFunc *from) --- 1158,1164 ---- COPY_SCALAR_FIELD(wincollid); COPY_SCALAR_FIELD(inputcollid); COPY_NODE_FIELD(args); + COPY_NODE_FIELD(agg_filter); COPY_SCALAR_FIELD(winref); COPY_SCALAR_FIELD(winstar); COPY_SCALAR_FIELD(winagg); *************** *** 2155,2160 **** _copyFuncCall(const FuncCall *from) --- 2157,2163 ---- COPY_SCALAR_FIELD(agg_star); COPY_SCALAR_FIELD(agg_distinct); COPY_SCALAR_FIELD(func_variadic); + COPY_NODE_FIELD(agg_filter); COPY_NODE_FIELD(over); COPY_LOCATION_FIELD(location); *** a/src/backend/nodes/equalfuncs.c --- b/src/backend/nodes/equalfuncs.c *************** *** 196,201 **** _equalAggref(const Aggref *a, const Aggref *b) --- 196,202 ---- COMPARE_NODE_FIELD(args); COMPARE_NODE_FIELD(aggorder); COMPARE_NODE_FIELD(aggdistinct); + COMPARE_NODE_FIELD(agg_filter); COMPARE_SCALAR_FIELD(aggstar); COMPARE_SCALAR_FIELD(agglevelsup); COMPARE_LOCATION_FIELD(location); *************** *** 211,216 **** _equalWindowFunc(const WindowFunc *a, const WindowFunc *b) --- 212,218 ---- COMPARE_SCALAR_FIELD(wincollid); COMPARE_SCALAR_FIELD(inputcollid); COMPARE_NODE_FIELD(args); + COMPARE_NODE_FIELD(agg_filter); COMPARE_SCALAR_FIELD(winref); COMPARE_SCALAR_FIELD(winstar); COMPARE_SCALAR_FIELD(winagg); *************** *** 2009,2014 **** _equalFuncCall(const FuncCall *a, const FuncCall *b) --- 2011,2017 ---- COMPARE_SCALAR_FIELD(agg_star); COMPARE_SCALAR_FIELD(agg_distinct); COMPARE_SCALAR_FIELD(func_variadic); + COMPARE_NODE_FIELD(agg_filter); COMPARE_NODE_FIELD(over); COMPARE_LOCATION_FIELD(location); *** a/src/backend/nodes/nodeFuncs.c --- b/src/backend/nodes/nodeFuncs.c *************** *** 1570,1575 **** expression_tree_walker(Node *node, --- 1570,1578 ---- if (expression_tree_walker((Node *) expr->aggdistinct, walker, context)) return true; + if (expression_tree_walker((Node *) expr->agg_filter, + walker, context)) + return true; } break; case T_WindowFunc: *************** *** 1580,1585 **** expression_tree_walker(Node *node, --- 1583,1591 ---- if (expression_tree_walker((Node *) expr->args, walker, context)) return true; + if (expression_tree_walker((Node *) expr->agg_filter, + walker, context)) + return true; } break; case T_ArrayRef: *************** *** 2079,2084 **** expression_tree_mutator(Node *node, --- 2085,2091 ---- MUTATE(newnode->args, aggref->args, List *); MUTATE(newnode->aggorder, aggref->aggorder, List *); MUTATE(newnode->aggdistinct, aggref->aggdistinct, List *); + MUTATE(newnode->agg_filter, aggref->agg_filter, Expr *); return (Node *) newnode; } break; *************** *** 2089,2094 **** expression_tree_mutator(Node *node, --- 2096,2102 ---- FLATCOPY(newnode, wfunc, WindowFunc); MUTATE(newnode->args, wfunc->args, List *); + MUTATE(newnode->agg_filter, wfunc->agg_filter, Expr *); return (Node *) newnode; } break; *************** *** 2951,2956 **** raw_expression_tree_walker(Node *node, --- 2959,2966 ---- return true; if (walker(fcall->agg_order, context)) return true; + if (walker(fcall->agg_filter, context)) + return true; if (walker(fcall->over, context)) return true; /* function name is deemed uninteresting */ *** a/src/backend/nodes/outfuncs.c --- b/src/backend/nodes/outfuncs.c *************** *** 958,963 **** _outAggref(StringInfo str, const Aggref *node) --- 958,964 ---- WRITE_NODE_FIELD(args); WRITE_NODE_FIELD(aggorder); WRITE_NODE_FIELD(aggdistinct); + WRITE_NODE_FIELD(agg_filter); WRITE_BOOL_FIELD(aggstar); WRITE_UINT_FIELD(agglevelsup); WRITE_LOCATION_FIELD(location); *************** *** 973,978 **** _outWindowFunc(StringInfo str, const WindowFunc *node) --- 974,980 ---- WRITE_OID_FIELD(wincollid); WRITE_OID_FIELD(inputcollid); WRITE_NODE_FIELD(args); + WRITE_NODE_FIELD(agg_filter); WRITE_UINT_FIELD(winref); WRITE_BOOL_FIELD(winstar); WRITE_BOOL_FIELD(winagg); *************** *** 2083,2088 **** _outFuncCall(StringInfo str, const FuncCall *node) --- 2085,2091 ---- WRITE_BOOL_FIELD(agg_star); WRITE_BOOL_FIELD(agg_distinct); WRITE_BOOL_FIELD(func_variadic); + WRITE_NODE_FIELD(agg_filter); WRITE_NODE_FIELD(over); WRITE_LOCATION_FIELD(location); } *** a/src/backend/nodes/readfuncs.c --- b/src/backend/nodes/readfuncs.c *************** *** 479,484 **** _readAggref(void) --- 479,485 ---- READ_NODE_FIELD(args); READ_NODE_FIELD(aggorder); READ_NODE_FIELD(aggdistinct); + READ_NODE_FIELD(agg_filter); READ_BOOL_FIELD(aggstar); READ_UINT_FIELD(agglevelsup); READ_LOCATION_FIELD(location); *************** *** 499,504 **** _readWindowFunc(void) --- 500,506 ---- READ_OID_FIELD(wincollid); READ_OID_FIELD(inputcollid); READ_NODE_FIELD(args); + READ_NODE_FIELD(agg_filter); READ_UINT_FIELD(winref); READ_BOOL_FIELD(winstar); READ_BOOL_FIELD(winagg); *** a/src/backend/optimizer/plan/planagg.c --- b/src/backend/optimizer/plan/planagg.c *************** *** 313,319 **** find_minmax_aggs_walker(Node *node, List **context) ListCell *l; Assert(aggref->agglevelsup == 0); ! if (list_length(aggref->args) != 1 || aggref->aggorder != NIL) return true; /* it couldn't be MIN/MAX */ /* note: we do not care if DISTINCT is mentioned ... */ curTarget = (TargetEntry *) linitial(aggref->args); --- 313,319 ---- ListCell *l; Assert(aggref->agglevelsup == 0); ! if (list_length(aggref->args) != 1 || aggref->aggorder != NIL || aggref->agg_filter != NULL) return true; /* it couldn't be MIN/MAX */ /* note: we do not care if DISTINCT is mentioned ... */ curTarget = (TargetEntry *) linitial(aggref->args); *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** *** 489,494 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); --- 489,495 ---- opt_frame_clause frame_extent frame_bound %type <str> opt_existing_window_name %type <boolean> opt_if_not_exists + %type <node> filter_clause /* * Non-keyword token types. These are hard-wired into the "flex" lexer. *************** *** 535,541 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTENSION EXTERNAL EXTRACT ! FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS GLOBAL GRANT GRANTED GREATEST GROUP_P --- 536,542 ---- EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTENSION EXTERNAL EXTRACT ! FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS GLOBAL GRANT GRANTED GREATEST GROUP_P *************** *** 11087,11093 **** c_expr: columnref { $$ = $1; } * (Note that many of the special SQL functions wouldn't actually make any * sense as functional index entries, but we ignore that consideration here.) */ ! func_expr: func_name '(' ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 11088,11094 ---- * (Note that many of the special SQL functions wouldn't actually make any * sense as functional index entries, but we ignore that consideration here.) */ ! func_expr: func_name '(' ')' filter_clause over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 11096,11119 **** func_expr: func_name '(' ')' over_clause n->agg_star = FALSE; n->agg_distinct = FALSE; n->func_variadic = FALSE; ! n->over = $4; ! n->location = @1; ! $$ = (Node *)n; ! } ! | func_name '(' func_arg_list ')' over_clause ! { ! FuncCall *n = makeNode(FuncCall); ! n->funcname = $1; ! n->args = $3; ! n->agg_order = NIL; ! n->agg_star = FALSE; ! n->agg_distinct = FALSE; ! n->func_variadic = FALSE; n->over = $5; n->location = @1; $$ = (Node *)n; } ! | func_name '(' VARIADIC func_arg_expr ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 11097,11122 ---- n->agg_star = FALSE; n->agg_distinct = FALSE; n->func_variadic = FALSE; ! n->agg_filter = $4; n->over = $5; n->location = @1; $$ = (Node *)n; } ! | func_name '(' func_arg_list ')' filter_clause over_clause ! { ! FuncCall *n = makeNode(FuncCall); ! n->funcname = $1; ! n->args = $3; ! n->agg_order = NIL; ! n->agg_star = FALSE; ! n->agg_distinct = FALSE; ! n->func_variadic = FALSE; ! n->agg_filter = $5; ! n->over = $6; ! n->location = @1; ! $$ = (Node *)n; ! } ! | func_name '(' VARIADIC func_arg_expr ')' filter_clause over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 11122,11132 **** func_expr: func_name '(' ')' over_clause n->agg_star = FALSE; n->agg_distinct = FALSE; n->func_variadic = TRUE; ! n->over = $6; n->location = @1; $$ = (Node *)n; } ! | func_name '(' func_arg_list ',' VARIADIC func_arg_expr ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 11125,11136 ---- n->agg_star = FALSE; n->agg_distinct = FALSE; n->func_variadic = TRUE; ! n->agg_filter = $6; ! n->over = $7; n->location = @1; $$ = (Node *)n; } ! | func_name '(' func_arg_list ',' VARIADIC func_arg_expr ')' filter_clause over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 11135,11145 **** func_expr: func_name '(' ')' over_clause n->agg_star = FALSE; n->agg_distinct = FALSE; n->func_variadic = TRUE; ! n->over = $8; n->location = @1; $$ = (Node *)n; } ! | func_name '(' func_arg_list sort_clause ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 11139,11150 ---- n->agg_star = FALSE; n->agg_distinct = FALSE; n->func_variadic = TRUE; ! n->agg_filter = $8; ! n->over = $9; n->location = @1; $$ = (Node *)n; } ! | func_name '(' func_arg_list sort_clause ')' filter_clause over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 11148,11158 **** func_expr: func_name '(' ')' over_clause n->agg_star = FALSE; n->agg_distinct = FALSE; n->func_variadic = FALSE; ! n->over = $6; n->location = @1; $$ = (Node *)n; } ! | func_name '(' ALL func_arg_list opt_sort_clause ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 11153,11164 ---- n->agg_star = FALSE; n->agg_distinct = FALSE; n->func_variadic = FALSE; ! n->agg_filter = $6; ! n->over = $7; n->location = @1; $$ = (Node *)n; } ! | func_name '(' ALL func_arg_list opt_sort_clause ')' filter_clause over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 11165,11175 **** func_expr: func_name '(' ')' over_clause * for that in FuncCall at the moment. */ n->func_variadic = FALSE; ! n->over = $7; n->location = @1; $$ = (Node *)n; } ! | func_name '(' DISTINCT func_arg_list opt_sort_clause ')' over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; --- 11171,11182 ---- * for that in FuncCall at the moment. */ n->func_variadic = FALSE; ! n->agg_filter = $7; ! n->over = $8; n->location = @1; $$ = (Node *)n; } ! | func_name '(' DISTINCT func_arg_list opt_sort_clause ')' filter_clause over_clause { FuncCall *n = makeNode(FuncCall); n->funcname = $1; *************** *** 11178,11188 **** func_expr: func_name '(' ')' over_clause n->agg_star = FALSE; n->agg_distinct = TRUE; n->func_variadic = FALSE; ! n->over = $7; n->location = @1; $$ = (Node *)n; } ! | func_name '(' '*' ')' over_clause { /* * We consider AGGREGATE(*) to invoke a parameterless --- 11185,11196 ---- n->agg_star = FALSE; n->agg_distinct = TRUE; n->func_variadic = FALSE; ! n->agg_filter = $7; ! n->over = $8; n->location = @1; $$ = (Node *)n; } ! | func_name '(' '*' ')' filter_clause over_clause { /* * We consider AGGREGATE(*) to invoke a parameterless *************** *** 11201,11207 **** func_expr: func_name '(' ')' over_clause n->agg_star = TRUE; n->agg_distinct = FALSE; n->func_variadic = FALSE; ! n->over = $5; n->location = @1; $$ = (Node *)n; } --- 11209,11216 ---- n->agg_star = TRUE; n->agg_distinct = FALSE; n->func_variadic = FALSE; ! n->agg_filter = $5; ! n->over = $6; n->location = @1; $$ = (Node *)n; } *************** *** 11735,11741 **** xmlexists_argument: window_clause: WINDOW window_definition_list { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } ! ; window_definition_list: window_definition { $$ = list_make1($1); } --- 11744,11750 ---- window_clause: WINDOW window_definition_list { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } ! ; window_definition_list: window_definition { $$ = list_make1($1); } *************** *** 11752,11757 **** window_definition: --- 11761,11771 ---- } ; + filter_clause: + FILTER '(' WHERE a_expr ')' { $$ = $4; } + | /*EMPTY*/ { $$ = NULL; } + ; + over_clause: OVER window_specification { $$ = $2; } | OVER ColId *************** *** 12972,12977 **** type_func_name_keyword: --- 12986,12992 ---- | CONCURRENTLY | CROSS | CURRENT_SCHEMA + | FILTER | FREEZE | FULL | ILIKE *** a/src/backend/parser/parse_agg.c --- b/src/backend/parser/parse_agg.c *************** *** 44,50 **** typedef struct int sublevels_up; } check_ungrouped_columns_context; ! static int check_agg_arguments(ParseState *pstate, List *args); static bool check_agg_arguments_walker(Node *node, check_agg_arguments_context *context); static void check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry, --- 44,50 ---- int sublevels_up; } check_ungrouped_columns_context; ! static int check_agg_arguments(ParseState *pstate, List *args, Expr *filter); static bool check_agg_arguments_walker(Node *node, check_agg_arguments_context *context); static void check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry, *************** *** 160,166 **** transformAggregateCall(ParseState *pstate, Aggref *agg, * Check the arguments to compute the aggregate's level and detect * improper nesting. */ ! min_varlevel = check_agg_arguments(pstate, agg->args); agg->agglevelsup = min_varlevel; /* Mark the correct pstate level as having aggregates */ --- 160,166 ---- * Check the arguments to compute the aggregate's level and detect * improper nesting. */ ! min_varlevel = check_agg_arguments(pstate, agg->args, agg->agg_filter); agg->agglevelsup = min_varlevel; /* Mark the correct pstate level as having aggregates */ *************** *** 207,212 **** transformAggregateCall(ParseState *pstate, Aggref *agg, --- 207,215 ---- case EXPR_KIND_HAVING: /* okay */ break; + case EXPR_KIND_FILTER: + errkind = true; + break; case EXPR_KIND_WINDOW_PARTITION: /* okay */ break; *************** *** 309,315 **** transformAggregateCall(ParseState *pstate, Aggref *agg, * which we can't know until we finish scanning the arguments. */ static int ! check_agg_arguments(ParseState *pstate, List *args) { int agglevel; check_agg_arguments_context context; --- 312,318 ---- * which we can't know until we finish scanning the arguments. */ static int ! check_agg_arguments(ParseState *pstate, List *args, Expr *filter) { int agglevel; check_agg_arguments_context context; *************** *** 323,328 **** check_agg_arguments(ParseState *pstate, List *args) --- 326,335 ---- check_agg_arguments_walker, (void *) &context); + (void) expression_tree_walker((Node *) filter, + check_agg_arguments_walker, + (void *) &context); + /* * If we found no vars nor aggs at all, it's a level-zero aggregate; * otherwise, its level is the minimum of vars or aggs. *************** *** 481,486 **** transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, --- 488,496 ---- case EXPR_KIND_HAVING: errkind = true; break; + case EXPR_KIND_FILTER: + errkind = true; + break; case EXPR_KIND_WINDOW_PARTITION: case EXPR_KIND_WINDOW_ORDER: case EXPR_KIND_WINDOW_FRAME_RANGE: *** a/src/backend/parser/parse_collate.c --- b/src/backend/parser/parse_collate.c *************** *** 575,580 **** assign_collations_walker(Node *node, assign_collations_context *context) --- 575,584 ---- * the case above for T_TargetEntry will apply * appropriate checks to agg ORDER BY items. * + * Likewise, we assign collations for the (bool) + * expression in agg_filter, independently of + * any other args. + * * We need not recurse into the aggorder or * aggdistinct lists, because those contain only * SortGroupClause nodes which we need not *************** *** 595,600 **** assign_collations_walker(Node *node, assign_collations_context *context) --- 599,620 ---- (void) assign_collations_walker((Node *) tle, &loccontext); } + + assign_expr_collations(context->pstate, aggref->agg_filter); + } + break; + case T_WindowFunc: + { + /* + * WindowFunc requires special processing only for + * its agg_filter clause, as for aggregates. + */ + WindowFunc *wfunc = (WindowFunc *) node; + + (void) assign_collations_walker((Node *) wfunc->args, + &loccontext); + + assign_expr_collations(context->pstate, wfunc->agg_filter); } break; case T_CaseExpr: *** a/src/backend/parser/parse_expr.c --- b/src/backend/parser/parse_expr.c *************** *** 22,27 **** --- 22,28 ---- #include "nodes/nodeFuncs.h" #include "optimizer/var.h" #include "parser/analyze.h" + #include "parser/parse_clause.h" #include "parser/parse_coerce.h" #include "parser/parse_collate.h" #include "parser/parse_expr.h" *************** *** 463,469 **** transformIndirection(ParseState *pstate, Node *basenode, List *indirection) list_make1(n), list_make1(result), NIL, false, false, false, ! NULL, true, location); if (newresult == NULL) unknown_attribute(pstate, result, strVal(n), location); result = newresult; --- 464,470 ---- list_make1(n), list_make1(result), NIL, false, false, false, ! NULL, NULL, true, location); if (newresult == NULL) unknown_attribute(pstate, result, strVal(n), location); result = newresult; *************** *** 631,637 **** transformColumnRef(ParseState *pstate, ColumnRef *cref) list_make1(makeString(colname)), list_make1(node), NIL, false, false, false, ! NULL, true, cref->location); } break; } --- 632,638 ---- list_make1(makeString(colname)), list_make1(node), NIL, false, false, false, ! NULL, NULL, true, cref->location); } break; } *************** *** 676,682 **** transformColumnRef(ParseState *pstate, ColumnRef *cref) list_make1(makeString(colname)), list_make1(node), NIL, false, false, false, ! NULL, true, cref->location); } break; } --- 677,683 ---- list_make1(makeString(colname)), list_make1(node), NIL, false, false, false, ! NULL, NULL, true, cref->location); } break; } *************** *** 734,740 **** transformColumnRef(ParseState *pstate, ColumnRef *cref) list_make1(makeString(colname)), list_make1(node), NIL, false, false, false, ! NULL, true, cref->location); } break; } --- 735,741 ---- list_make1(makeString(colname)), list_make1(node), NIL, false, false, false, ! NULL, NULL, true, cref->location); } break; } *************** *** 1241,1246 **** transformFuncCall(ParseState *pstate, FuncCall *fn) --- 1242,1248 ---- { List *targs; ListCell *args; + Expr *tagg_filter; /* Transform the list of arguments ... */ targs = NIL; *************** *** 1250,1255 **** transformFuncCall(ParseState *pstate, FuncCall *fn) --- 1252,1263 ---- (Node *) lfirst(args))); } + /* Transform the aggregate filter using transformWhereClause, to + * which FILTER is virually identical... */ + tagg_filter = NULL; + if (fn->agg_filter != NULL) + tagg_filter = (Expr *)transformWhereClause(pstate, (Node *)fn->agg_filter, EXPR_KIND_FILTER, "FILTER"); + /* ... and hand off to ParseFuncOrColumn */ return ParseFuncOrColumn(pstate, fn->funcname, *************** *** 1258,1263 **** transformFuncCall(ParseState *pstate, FuncCall *fn) --- 1266,1272 ---- fn->agg_star, fn->agg_distinct, fn->func_variadic, + tagg_filter, fn->over, false, fn->location); *************** *** 1430,1435 **** transformSubLink(ParseState *pstate, SubLink *sublink) --- 1439,1445 ---- case EXPR_KIND_FROM_FUNCTION: case EXPR_KIND_WHERE: case EXPR_KIND_HAVING: + case EXPR_KIND_FILTER: case EXPR_KIND_WINDOW_PARTITION: case EXPR_KIND_WINDOW_ORDER: case EXPR_KIND_WINDOW_FRAME_RANGE: *************** *** 2579,2584 **** ParseExprKindName(ParseExprKind exprKind) --- 2589,2596 ---- return "WHERE"; case EXPR_KIND_HAVING: return "HAVING"; + case EXPR_KIND_FILTER: + return "FILTER"; case EXPR_KIND_WINDOW_PARTITION: return "window PARTITION BY"; case EXPR_KIND_WINDOW_ORDER: *** a/src/backend/parser/parse_func.c --- b/src/backend/parser/parse_func.c *************** *** 63,69 **** Node * ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, List *agg_order, bool agg_star, bool agg_distinct, bool func_variadic, ! WindowDef *over, bool is_column, int location) { Oid rettype; Oid funcid; --- 63,69 ---- ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, List *agg_order, bool agg_star, bool agg_distinct, bool func_variadic, ! Expr *agg_filter, WindowDef *over, bool is_column, int location) { Oid rettype; Oid funcid; *************** *** 175,181 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, * wasn't any aggregate or variadic decoration, nor an argument name. */ if (nargs == 1 && agg_order == NIL && !agg_star && !agg_distinct && ! over == NULL && !func_variadic && argnames == NIL && list_length(funcname) == 1) { Oid argtype = actual_arg_types[0]; --- 175,181 ---- * wasn't any aggregate or variadic decoration, nor an argument name. */ if (nargs == 1 && agg_order == NIL && !agg_star && !agg_distinct && ! agg_filter == NULL && over == NULL && !func_variadic && argnames == NIL && list_length(funcname) == 1) { Oid argtype = actual_arg_types[0]; *************** *** 251,256 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, --- 251,262 ---- errmsg("ORDER BY specified, but %s is not an aggregate function", NameListToString(funcname)), parser_errposition(pstate, location))); + if (agg_filter) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("FILTER specified, but %s is not an aggregate function", + NameListToString(funcname)), + parser_errposition(pstate, location))); if (over) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), *************** *** 402,407 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, --- 408,415 ---- /* aggcollid and inputcollid will be set by parse_collate.c */ /* args, aggorder, aggdistinct will be set by transformAggregateCall */ aggref->aggstar = agg_star; + /* filter */ + aggref->agg_filter = agg_filter; /* agglevelsup will be set by transformAggregateCall */ aggref->location = location; *************** *** 460,465 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, --- 468,474 ---- /* winref will be set by transformWindowFuncCall */ wfunc->winstar = agg_star; wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE); + wfunc->agg_filter = agg_filter; wfunc->location = location; /* *************** *** 483,488 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, --- 492,507 ---- parser_errposition(pstate, location))); /* + * Reject window functions which are not aggregates in the + * case of FILTER. + */ + if (!wfunc->winagg && agg_filter) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("FILTER is not implemented in non-aggregate window functions", + parser_errposition(pstate, location)))); + + /* * ordered aggs not allowed in windows yet */ if (agg_order != NIL) *** a/src/backend/utils/adt/ruleutils.c --- b/src/backend/utils/adt/ruleutils.c *************** *** 7419,7425 **** get_agg_expr(Aggref *aggref, deparse_context *context) --- 7419,7433 ---- appendStringInfoString(buf, " ORDER BY "); get_rule_orderby(aggref->aggorder, aggref->args, false, context); } + + if (aggref->agg_filter != NULL) + { + appendStringInfoString(buf, ") FILTER (WHERE "); + get_rule_expr((Node *)aggref->agg_filter, context, false); + } + appendStringInfoChar(buf, ')'); + } /* *************** *** 7456,7461 **** get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context) --- 7464,7476 ---- appendStringInfoChar(buf, '*'); else get_rule_expr((Node *) wfunc->args, context, true); + + if (wfunc->agg_filter != NULL) + { + appendStringInfoString(buf, ") FILTER (WHERE "); + get_rule_expr((Node *)wfunc->agg_filter, context, false); + } + appendStringInfoString(buf, ") OVER "); foreach(l, context->windowClause) *** a/src/include/nodes/execnodes.h --- b/src/include/nodes/execnodes.h *************** *** 584,589 **** typedef struct AggrefExprState --- 584,590 ---- { ExprState xprstate; List *args; /* states of argument expressions */ + Expr *agg_filter; /* FILTER expression */ int aggno; /* ID number for agg within its plan node */ } AggrefExprState; *************** *** 595,600 **** typedef struct WindowFuncExprState --- 596,602 ---- { ExprState xprstate; List *args; /* states of argument expressions */ + Expr *agg_filter; /* FILTER expression */ int wfuncno; /* ID number for wfunc within its plan node */ } WindowFuncExprState; *** a/src/include/nodes/parsenodes.h --- b/src/include/nodes/parsenodes.h *************** *** 295,300 **** typedef struct FuncCall --- 295,301 ---- bool agg_star; /* argument was really '*' */ bool agg_distinct; /* arguments were labeled DISTINCT */ bool func_variadic; /* last argument was labeled VARIADIC */ + Node *agg_filter; /* FILTER clause, if any */ struct WindowDef *over; /* OVER clause, if any */ int location; /* token location, or -1 if unknown */ } FuncCall; *** a/src/include/nodes/primnodes.h --- b/src/include/nodes/primnodes.h *************** *** 247,252 **** typedef struct Aggref --- 247,253 ---- List *args; /* arguments and sort expressions */ List *aggorder; /* ORDER BY (list of SortGroupClause) */ List *aggdistinct; /* DISTINCT (list of SortGroupClause) */ + Expr *agg_filter; /* FILTER expression */ bool aggstar; /* TRUE if argument list was really '*' */ Index agglevelsup; /* > 0 if agg belongs to outer query */ int location; /* token location, or -1 if unknown */ *************** *** 263,268 **** typedef struct WindowFunc --- 264,270 ---- Oid wincollid; /* OID of collation of result */ Oid inputcollid; /* OID of collation that function should use */ List *args; /* arguments to the window function */ + Expr *agg_filter; /* FILTER expression */ Index winref; /* index of associated WindowClause */ bool winstar; /* TRUE if argument list was really '*' */ bool winagg; /* is function a simple aggregate? */ *** a/src/include/parser/kwlist.h --- b/src/include/parser/kwlist.h *************** *** 155,160 **** PG_KEYWORD("extract", EXTRACT, COL_NAME_KEYWORD) --- 155,161 ---- PG_KEYWORD("false", FALSE_P, RESERVED_KEYWORD) PG_KEYWORD("family", FAMILY, UNRESERVED_KEYWORD) PG_KEYWORD("fetch", FETCH, RESERVED_KEYWORD) + PG_KEYWORD("filter", FILTER, TYPE_FUNC_NAME_KEYWORD) PG_KEYWORD("first", FIRST_P, UNRESERVED_KEYWORD) PG_KEYWORD("float", FLOAT_P, COL_NAME_KEYWORD) PG_KEYWORD("following", FOLLOWING, UNRESERVED_KEYWORD) *** a/src/include/parser/parse_func.h --- b/src/include/parser/parse_func.h *************** *** 46,52 **** extern Node *ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, List *agg_order, bool agg_star, bool agg_distinct, bool func_variadic, ! WindowDef *over, bool is_column, int location); extern FuncDetailCode func_get_detail(List *funcname, List *fargs, List *fargnames, --- 46,52 ---- List *funcname, List *fargs, List *agg_order, bool agg_star, bool agg_distinct, bool func_variadic, ! Expr *agg_filter, WindowDef *over, bool is_column, int location); extern FuncDetailCode func_get_detail(List *funcname, List *fargs, List *fargnames, *** a/src/include/parser/parse_node.h --- b/src/include/parser/parse_node.h *************** *** 39,44 **** typedef enum ParseExprKind --- 39,45 ---- EXPR_KIND_FROM_FUNCTION, /* function in FROM clause */ EXPR_KIND_WHERE, /* WHERE */ EXPR_KIND_HAVING, /* HAVING */ + EXPR_KIND_FILTER, /* FILTER */ EXPR_KIND_WINDOW_PARTITION, /* window definition PARTITION BY */ EXPR_KIND_WINDOW_ORDER, /* window definition ORDER BY */ EXPR_KIND_WINDOW_FRAME_RANGE, /* window frame clause with RANGE */ *** a/src/test/regress/expected/aggregates.out --- b/src/test/regress/expected/aggregates.out *************** *** 1154,1156 **** select string_agg(v, decode('ee', 'hex')) from bytea_test_table; --- 1154,1215 ---- (1 row) drop table bytea_test_table; + -- FILTER tests + select min(unique1) filter (where unique1 > 100) from tenk1; + min + ----- + 101 + (1 row) + + select ten, sum(distinct four) filter (where four::text ~ '123') from onek a + group by ten; + ten | sum + -----+----- + 0 | + 1 | + 2 | + 3 | + 4 | + 5 | + 6 | + 7 | + 8 | + 9 | + (10 rows) + + select ten, sum(distinct four) filter (where four > 10) from onek a + group by ten + having exists (select 1 from onek b where sum(distinct a.four) = b.four); + ten | sum + -----+----- + 0 | + 2 | + 4 | + 6 | + 8 | + (5 rows) + + select max(foo COLLATE "C") filter (where (bar collate "POSIX") > '0') from (values ('a', 'b')) AS v(foo,bar); + max + ----- + a + (1 row) + + -- outer-level aggregates + select + (select max((select i.unique2 from tenk1 i where i.unique1 = o.unique1)) filter (where o.unique1 < 10)) + from tenk1 o; + max + ------ + 9998 + (1 row) + + -- exercise lots of aggregate parts with FILTER + select aggfns(distinct a,b,c order by a,c using ~<~,b) filter (where a > 1) + from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), + generate_series(1,2) i; + aggfns + --------------------------- + {"(2,2,bar)","(3,1,baz)"} + (1 row) + *** a/src/test/regress/expected/window.out --- b/src/test/regress/expected/window.out *************** *** 1020,1024 **** SELECT ntile(0) OVER (ORDER BY ten), ten, four FROM tenk1; --- 1020,1037 ---- ERROR: argument of ntile must be greater than zero SELECT nth_value(four, 0) OVER (ORDER BY ten), ten, four FROM tenk1; ERROR: argument of nth_value must be greater than zero + -- filter + SELECT sum(salary), row_number() OVER (ORDER BY depname), sum( + sum(salary) FILTER (WHERE enroll_date > '2007-01-01') + ) FILTER (WHERE depname <> 'sales') OVER (ORDER BY depname DESC) AS "filtered_sum", + depname + FROM empsalary GROUP BY depname; + sum | row_number | filtered_sum | depname + -------+------------+--------------+----------- + 14600 | 3 | | sales + 7400 | 2 | 3500 | personnel + 25100 | 1 | 22600 | develop + (3 rows) + -- cleanup DROP TABLE empsalary; *** a/src/test/regress/sql/aggregates.sql --- b/src/test/regress/sql/aggregates.sql *************** *** 442,444 **** select string_agg(v, NULL) from bytea_test_table; --- 442,468 ---- select string_agg(v, decode('ee', 'hex')) from bytea_test_table; drop table bytea_test_table; + + -- FILTER tests + + select min(unique1) filter (where unique1 > 100) from tenk1; + + select ten, sum(distinct four) filter (where four::text ~ '123') from onek a + group by ten; + + select ten, sum(distinct four) filter (where four > 10) from onek a + group by ten + having exists (select 1 from onek b where sum(distinct a.four) = b.four); + + select max(foo COLLATE "C") filter (where (bar collate "POSIX") > '0') from (values ('a', 'b')) AS v(foo,bar); + + -- outer-level aggregates + select + (select max((select i.unique2 from tenk1 i where i.unique1 = o.unique1)) filter (where o.unique1 < 10)) + from tenk1 o; + + -- exercise lots of aggregate parts with FILTER + + select aggfns(distinct a,b,c order by a,c using ~<~,b) filter (where a > 1) + from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c), + generate_series(1,2) i; *** a/src/test/regress/sql/window.sql --- b/src/test/regress/sql/window.sql *************** *** 264,268 **** SELECT ntile(0) OVER (ORDER BY ten), ten, four FROM tenk1; --- 264,276 ---- SELECT nth_value(four, 0) OVER (ORDER BY ten), ten, four FROM tenk1; + -- filter + + SELECT sum(salary), row_number() OVER (ORDER BY depname), sum( + sum(salary) FILTER (WHERE enroll_date > '2007-01-01') + ) FILTER (WHERE depname <> 'sales') OVER (ORDER BY depname DESC) AS "filtered_sum", + depname + FROM empsalary GROUP BY depname; + -- cleanup DROP TABLE empsalary;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers