*** a/doc/src/sgml/ref/select.sgml --- b/doc/src/sgml/ref/select.sgml *************** *** 595,604 **** GROUP BY expression [, ...] ! Aggregate functions, if any are used, are computed across all rows making up each group, producing a separate value for each group (whereas without GROUP BY, an aggregate produces a single value computed across all the selected rows). When GROUP BY is present, it is not valid for the SELECT list expressions to refer to ungrouped columns except within aggregate functions or if the --- 595,607 ---- ! In the absence of a FILTER 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 GROUP BY, an aggregate produces a single value computed across all the selected rows). + When a FILTER clause is present, only those + rows matching the FILTER clause are included. When GROUP BY is present, it is not valid for the SELECT 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: ! aggregate_name (expression [ , ... ] [ order_by_clause ] ) ! aggregate_name (ALL expression [ , ... ] [ order_by_clause ] ) ! aggregate_name (DISTINCT expression [ , ... ] [ order_by_clause ] ) ! aggregate_name ( * ) where aggregate_name is a previously defined aggregate (possibly qualified with a schema name), ! expression is ! any value expression that does not itself contain an aggregate ! expression or a window function call, and ! order_by_clause is a optional ! ORDER BY clause as described below. ! The first form of aggregate expression invokes the aggregate ! once for each input row. The second form is the same as the first, since ALL 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: ! aggregate_name (expression [ , ... ] [ order_by_clause ] ) [ FILTER ( WHERE filter_clause ) ] ! aggregate_name (ALL expression [ , ... ] [ order_by_clause ] ) [ FILTER ( WHERE filter_clause ) ] ! aggregate_name (DISTINCT expression [ , ... ] [ order_by_clause ] ) [ FILTER ( WHERE filter_clause ) ] ! aggregate_name ( * ) [ FILTER ( WHERE filter_clause ) ] where aggregate_name is a previously defined aggregate (possibly qualified with a schema name), ! expression is any value expression that ! does not itself contain an aggregate expression or a window ! function call, order_by_clause is a ! optional ORDER BY clause as described below. The ! aggregate_name can also be suffixed ! with FILTER as described below. ! 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 ALL is the default. The third form invokes the aggregate once for each distinct value *************** *** 1607,1612 **** sqrt(2) --- 1609,1629 ---- + Adding a FILTER clause to an aggregate specifies which values of + the expression being aggregated to evaluate. For example: + + 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) + + + + Ordinarily, the input rows are fed to the aggregate function in an unspecified order. In many cases this does not matter; for example, 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: ! function_name (expression , expression ... ) OVER ( window_definition ) ! function_name (expression , expression ... ) OVER window_name ! function_name ( * ) OVER ( window_definition ) ! function_name ( * ) OVER window_name where window_definition has the syntax --- 1726,1735 ---- The syntax of a window function call is one of the following: ! function_name (expression , expression ... ) [ FILTER ( WHERE filter_clause ) ] OVER ( window_definition ) ! function_name (expression , expression ... ) [ FILTER ( WHERE filter_clause ) ] OVER window_name ! function_name ( * ) [ FILTER ( WHERE filter_clause ) ] OVER ( window_definition ) ! function_name ( * ) [ FILTER ( WHERE filter_clause ) ] OVER window_name where window_definition has the syntax *************** *** 1836,1851 **** UNBOUNDED FOLLOWING The built-in window functions are described in . 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. ! The syntaxes using * are used for calling parameter-less ! aggregate functions as window functions, for example ! count(*) OVER (PARTITION BY x ORDER BY y). ! The asterisk (*) is customarily not used for non-aggregate window functions. ! Aggregate window functions, unlike normal aggregate functions, do not ! allow DISTINCT or ORDER BY to be used within the function argument list. --- 1853,1870 ---- The built-in window functions are described in . 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 FILTER clause is ! only valid for aggregate functions used in windowing. ! The syntaxes using * are used for calling ! parameter-less aggregate functions as window functions, for ! example count(*) OVER (PARTITION BY x ORDER BY y). ! The asterisk (*) is customarily not used for ! non-aggregate window functions. Aggregate window functions, ! unlike normal aggregate functions, do not allow ! DISTINCT or ORDER BY to be used within the function argument list. *** a/src/backend/executor/execQual.c --- b/src/backend/executor/execQual.c *************** *** 4395,4400 **** ExecInitExpr(Expr *node, PlanState *parent) --- 4395,4401 ---- astate->args = (List *) ExecInitExpr((Expr *) aggref->args, parent); + astate->agg_filter = ExecInitExpr(aggref->agg_filter, parent); /* * Complain if the aggregate's arguments contain any *************** *** 4433,4438 **** ExecInitExpr(Expr *node, PlanState *parent) --- 4434,4440 ---- 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 *************** *** 364,370 **** 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; --- 364,370 ---- 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 *************** *** 487,492 **** advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup) --- 487,504 ---- 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 *************** *** 226,234 **** advance_windowaggregate(WindowAggState *winstate, --- 226,247 ---- 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 *************** *** 1135,1140 **** _copyAggref(const Aggref *from) --- 1135,1141 ---- 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); *************** *** 1155,1160 **** _copyWindowFunc(const WindowFunc *from) --- 1156,1162 ---- 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); *************** *** 2153,2158 **** _copyFuncCall(const FuncCall *from) --- 2155,2161 ---- 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 *************** *** 195,200 **** _equalAggref(const Aggref *a, const Aggref *b) --- 195,201 ---- 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); *************** *** 210,215 **** _equalWindowFunc(const WindowFunc *a, const WindowFunc *b) --- 211,217 ---- 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); *************** *** 1997,2002 **** _equalFuncCall(const FuncCall *a, const FuncCall *b) --- 1999,2005 ---- 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; *************** *** 2948,2953 **** raw_expression_tree_walker(Node *node, --- 2956,2963 ---- 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 *************** *** 956,961 **** _outAggref(StringInfo str, const Aggref *node) --- 956,962 ---- 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); *************** *** 971,976 **** _outWindowFunc(StringInfo str, const WindowFunc *node) --- 972,978 ---- 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); *************** *** 2081,2086 **** _outFuncCall(StringInfo str, const FuncCall *node) --- 2083,2089 ---- 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 *************** *** 478,483 **** _readAggref(void) --- 478,484 ---- 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); *************** *** 498,503 **** _readWindowFunc(void) --- 499,505 ---- 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 *************** *** 491,496 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); --- 491,497 ---- opt_frame_clause frame_extent frame_bound %type opt_existing_window_name %type opt_if_not_exists + %type filter_clause /* * Non-keyword token types. These are hard-wired into the "flex" lexer. *************** *** 537,543 **** 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 --- 538,544 ---- 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 *************** *** 10952,10958 **** 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; --- 10953,10959 ---- * (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; *************** *** 10961,10984 **** 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; --- 10962,10987 ---- 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; *************** *** 10987,10997 **** 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; --- 10990,11001 ---- 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; *************** *** 11000,11010 **** 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; --- 11004,11015 ---- 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; *************** *** 11013,11023 **** 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; --- 11018,11029 ---- 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; *************** *** 11030,11040 **** 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; --- 11036,11047 ---- * 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; *************** *** 11043,11053 **** 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 --- 11050,11061 ---- 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 *************** *** 11066,11072 **** 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; } --- 11074,11081 ---- n->agg_star = TRUE; n->agg_distinct = FALSE; n->func_variadic = FALSE; ! n->agg_filter = $5; ! n->over = $6; n->location = @1; $$ = (Node *)n; } *************** *** 11600,11606 **** xmlexists_argument: window_clause: WINDOW window_definition_list { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } ! ; window_definition_list: window_definition { $$ = list_make1($1); } --- 11609,11615 ---- window_clause: WINDOW window_definition_list { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } ! ; window_definition_list: window_definition { $$ = list_make1($1); } *************** *** 11617,11622 **** window_definition: --- 11626,11636 ---- } ; + filter_clause: + FILTER '(' WHERE a_expr ')' { $$ = $4; } + | /*EMPTY*/ { $$ = NULL; } + ; + over_clause: OVER window_specification { $$ = $2; } | OVER ColId *************** *** 12834,12839 **** type_func_name_keyword: --- 12848,12854 ---- | 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_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 *************** *** 579,584 **** typedef struct AggrefExprState --- 579,585 ---- { ExprState xprstate; List *args; /* states of argument expressions */ + Expr *agg_filter; /* FILTER expression */ int aggno; /* ID number for agg within its plan node */ } AggrefExprState; *************** *** 590,595 **** typedef struct WindowFuncExprState --- 591,597 ---- { 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 *************** *** 241,246 **** typedef struct Aggref --- 241,247 ---- 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 */ *************** *** 257,262 **** typedef struct WindowFunc --- 258,264 ---- 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,1193 ---- (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 > 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) + + -- 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,463 ---- 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 > 10) from onek a + group by ten + having exists (select 1 from onek b where sum(distinct a.four) = b.four); + + -- 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;