On Sun, Apr 28, 2013 at 01:29:41PM -0700, David Fetter wrote:
> 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.
Rebased vs. master.
Cheers,
David.
--
David Fetter <[email protected]> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: [email protected]
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
***************
*** 594,603 **** 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
--- 594,606 ----
</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
***************
*** 4410,4415 **** ExecInitExpr(Expr *node, PlanState *parent)
--- 4410,4416 ----
astate->args = (List *)
ExecInitExpr((Expr *) aggref->args,
parent);
+ astate->agg_filter =
ExecInitExpr(aggref->agg_filter, parent);
/*
* Complain if the aggregate's
arguments contain any
***************
*** 4448,4453 **** ExecInitExpr(Expr *node, PlanState *parent)
--- 4449,4455 ----
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);
***************
*** 1995,2000 **** _equalFuncCall(const FuncCall *a, const FuncCall *b)
--- 1997,2003 ----
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
***************
*** 314,320 **** 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);
--- 314,320 ----
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
***************
*** 490,495 **** static Node *makeRecursiveViewSelect(char *relname, List
*aliases, Node *query);
--- 490,496 ----
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.
***************
*** 536,542 **** 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
--- 537,543 ----
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
***************
*** 12980,12985 **** type_func_name_keyword:
--- 12994,13000 ----
| 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, (Node *) 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, (Node *) 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
***************
*** 7424,7430 **** get_agg_expr(Aggref *aggref, deparse_context *context)
--- 7424,7438 ----
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, ')');
+
}
/*
***************
*** 7461,7466 **** get_windowfunc_expr(WindowFunc *wfunc, deparse_context
*context)
--- 7469,7481 ----
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 */
+ ExprState *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 */
+ ExprState *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 ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers