*** 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/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 *************** *** 10944,10950 **** 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; --- 10945,10951 ---- * (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; *************** *** 10953,10976 **** 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; --- 10954,10979 ---- 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; *************** *** 10979,10989 **** 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; --- 10982,10993 ---- 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; *************** *** 10992,11002 **** 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; --- 10996,11007 ---- 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; *************** *** 11005,11015 **** 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; --- 11010,11021 ---- 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; *************** *** 11022,11032 **** 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; --- 11028,11039 ---- * 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; *************** *** 11035,11045 **** 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 --- 11042,11053 ---- 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 *************** *** 11058,11064 **** 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; } --- 11066,11073 ---- n->agg_star = TRUE; n->agg_distinct = FALSE; n->func_variadic = FALSE; ! n->agg_filter = $5; ! n->over = $6; n->location = @1; $$ = (Node *)n; } *************** *** 11592,11598 **** xmlexists_argument: window_clause: WINDOW window_definition_list { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } ! ; window_definition_list: window_definition { $$ = list_make1($1); } --- 11601,11607 ---- window_clause: WINDOW window_definition_list { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } ! ; window_definition_list: window_definition { $$ = list_make1($1); } *************** *** 11609,11614 **** window_definition: --- 11618,11628 ---- } ; + filter_clause: + FILTER '(' WHERE a_expr ')' { $$ = $4; } + | /*EMPTY*/ { $$ = NULL; } + ; + over_clause: OVER window_specification { $$ = $2; } | OVER ColId *************** *** 12885,12890 **** reserved_keyword: --- 12899,12905 ---- | EXCEPT | FALSE_P | FETCH + | FILTER | FOR | FOREIGN | FROM *** 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; /* *************** *** 482,487 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, --- 491,506 ---- NameListToString(funcname)), 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 */ *** 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, RESERVED_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 */