From 44915ac8f5b56b0ef5cdba217e727e2c729aa64a Mon Sep 17 00:00:00 2001 From: Yuki Fujii Date: Fri, 27 Oct 2023 09:47:28 -0400 Subject: [PATCH] Partial aggregates push down v32 --- contrib/postgres_fdw/deparse.c | 113 ++- .../postgres_fdw/expected/postgres_fdw.out | 868 +++++++++++++++++- contrib/postgres_fdw/option.c | 4 +- contrib/postgres_fdw/postgres_fdw.c | 47 +- contrib/postgres_fdw/postgres_fdw.h | 10 + contrib/postgres_fdw/sql/postgres_fdw.sql | 311 ++++++- doc/src/sgml/catalogs.sgml | 12 + doc/src/sgml/postgres-fdw.sgml | 130 ++- doc/src/sgml/ref/create_aggregate.sgml | 34 + doc/src/sgml/xaggr.sgml | 11 +- src/backend/catalog/pg_aggregate.c | 101 ++ src/backend/commands/aggregatecmds.c | 4 + src/backend/optimizer/plan/planner.c | 133 ++- src/bin/pg_dump/pg_dump.c | 16 +- src/include/catalog/pg_aggregate.dat | 680 +++++++++++--- src/include/catalog/pg_aggregate.h | 4 + src/include/catalog/pg_proc.dat | 196 ++++ src/include/nodes/pathnodes.h | 5 + .../regress/expected/create_aggregate.out | 184 ++++ src/test/regress/expected/oidjoins.out | 1 + src/test/regress/sql/create_aggregate.sql | 183 ++++ 21 files changed, 2824 insertions(+), 223 deletions(-) diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index 09fd489a90..c443f8974a 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -202,7 +202,7 @@ static bool is_subquery_var(Var *node, RelOptInfo *foreignrel, int *relno, int *colno); static void get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel, int *relno, int *colno); - +static bool partial_agg_ok(Aggref *agg, PgFdwRelationInfo *fpinfo); /* * Examine each qual clause in input_conds, and classify them into two groups, @@ -907,8 +907,9 @@ foreign_expr_walker(Node *node, if (!IS_UPPER_REL(glob_cxt->foreignrel)) return false; - /* Only non-split aggregates are pushable. */ - if (agg->aggsplit != AGGSPLIT_SIMPLE) + if (agg->aggsplit != AGGSPLIT_SIMPLE && agg->aggsplit != AGGSPLIT_INITIAL_SERIAL) + return false; + if (agg->aggsplit == AGGSPLIT_INITIAL_SERIAL && !partial_agg_ok(agg, fpinfo)) return false; /* As usual, it must be shippable. */ @@ -3517,14 +3518,34 @@ deparseAggref(Aggref *node, deparse_expr_cxt *context) StringInfo buf = context->buf; bool use_variadic; - /* Only basic, non-split aggregation accepted. */ - Assert(node->aggsplit == AGGSPLIT_SIMPLE); + + Assert((node->aggsplit == AGGSPLIT_SIMPLE) || + (node->aggsplit == AGGSPLIT_INITIAL_SERIAL)); /* Check if need to print VARIADIC (cf. ruleutils.c) */ use_variadic = node->aggvariadic; - /* Find aggregate name from aggfnoid which is a pg_proc entry */ - appendFunctionName(node->aggfnoid, context); + if (node->aggsplit == AGGSPLIT_SIMPLE) + /* Find aggregate name from aggfnoid which is a pg_proc entry */ + appendFunctionName(node->aggfnoid, context); + else + { + HeapTuple aggtup; + Form_pg_aggregate aggform; + + /* Find aggregate name from aggfnoid and aggpartialfn */ + aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(node->aggfnoid)); + if (!HeapTupleIsValid(aggtup)) + elog(ERROR, "cache lookup failed for aggregate %u", node->aggfnoid); + aggform = (Form_pg_aggregate) GETSTRUCT(aggtup); + + if (aggform->aggpartialfn) + appendFunctionName(aggform->aggpartialfn, context); + else + elog(ERROR, "there is no aggpartialfn %u", node->aggfnoid); + ReleaseSysCache(aggtup); + } + appendStringInfoChar(buf, '('); /* Add DISTINCT */ @@ -3723,6 +3744,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context) Query *query = context->root->parse; ListCell *lc; bool first = true; + PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) context->foreignrel->fdw_private; /* Nothing to be done, if there's no GROUP BY clause in the query. */ if (!query->groupClause) @@ -3743,7 +3765,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context) * to empty, and in any case the redundancy situation on the remote might * be different than what we think here. */ - foreach(lc, query->groupClause) + foreach(lc, fpinfo->group_clause) { SortGroupClause *grp = (SortGroupClause *) lfirst(lc); @@ -4047,3 +4069,78 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel, /* Shouldn't get here */ elog(ERROR, "unexpected expression in subquery output"); } + +/* + * Can we assume a buit-in aggpartialfunc exists on the remote server? If + * check_partial_aggregate_support is false, we assume the partial aggregate + * function exists on the remote server. If true, we assume the partial + * aggregate function exists on the remote server if the aggregate is so + * simple that the local aggregate's OID and its partial aggregate function + * OID are the same. If not, we check the remote server version and assume + * the remote partial aggregate function exists if the remote server version + * is equal to or greater than the local server version. + */ +static bool +is_builtin_aggpartialfunc_shippable(Form_pg_aggregate aggform, + PgFdwRelationInfo *fpinfo) +{ + bool shippable = true; + + /* So simple, we assume it exists. */ + if (aggform->aggfnoid != aggform->aggpartialfn && + fpinfo->check_partial_aggregate_support) + { + if (fpinfo->remoteversion == 0) + { + PGconn *conn = GetConnection(fpinfo->user, false, NULL); + + fpinfo->remoteversion = PQserverVersion(conn); + } + + if (fpinfo->remoteversion < PG_VERSION_NUM) + shippable = false; + } + + return shippable; +} + +/* + * ---------- + * Check that partial aggregate "agg" is safe to push down. + * + * It is pushdown-safe when all of the following conditions are true: + * + * * agg is an AGGKIND_NORMAL aggregate which contains no DISTINCT or + * ORDER BY clauses + * * There is a local aggregate function for partial aggregation + * (aggpartialfunc) and it is assumed to exist on the remote server + * ---------- + */ +static bool +partial_agg_ok(Aggref *agg, PgFdwRelationInfo *fpinfo) +{ + HeapTuple aggtup; + Form_pg_aggregate aggform; + bool partial_agg_ok = true; + + Assert(agg->aggsplit == AGGSPLIT_INITIAL_SERIAL); + + /* We don't support complex partial aggregates */ + if (agg->aggdistinct || agg->aggkind != AGGKIND_NORMAL || agg->aggorder != NIL) + return false; + + aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(agg->aggfnoid)); + if (!HeapTupleIsValid(aggtup)) + elog(ERROR, "cache lookup failed for aggregate %u", agg->aggfnoid); + aggform = (Form_pg_aggregate) GETSTRUCT(aggtup); + + if (!aggform->aggpartialfn) + partial_agg_ok = false; + else if (is_builtin(aggform->aggpartialfn)) + partial_agg_ok = is_builtin_aggpartialfunc_shippable(aggform, fpinfo); + else if (!is_shippable(aggform->aggpartialfn, ProcedureRelationId, fpinfo)) + partial_agg_ok = false; + + ReleaseSysCache(aggtup); + return partial_agg_ok; +} diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 144c114d0f..d9febca7a0 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -9758,18 +9758,35 @@ RESET enable_partitionwise_join; -- =================================================================== -- test partitionwise aggregates -- =================================================================== -CREATE TABLE pagg_tab (a int, b int, c text) PARTITION BY RANGE(a); +ALTER SERVER loopback OPTIONS (ADD fdw_tuple_cost '0.1'); +CREATE TYPE mood AS enum ('sad', 'ok', 'happy'); +ALTER EXTENSION postgres_fdw ADD TYPE mood; +CREATE TABLE pagg_tab (a int, b int, c text, c_serial int4, + c_int4array _int4, c_interval interval, + c_money money, c_1c text, c_1b bytea, + c_bit bit(2), c_1or3int2 int2, + c_1or3int4 int4, c_1or3int8 int8, + c_bool bool, c_enum mood, c_pg_lsn pg_lsn, + c_tid tid, c_int4range int4range, + c_int4multirange int4multirange, + c_time time, c_timetz timetz, + c_timestamp timestamp, c_timestamptz timestamptz, + c_xid8 xid8) + PARTITION BY RANGE(a); CREATE TABLE pagg_tab_p1 (LIKE pagg_tab); CREATE TABLE pagg_tab_p2 (LIKE pagg_tab); CREATE TABLE pagg_tab_p3 (LIKE pagg_tab); -INSERT INTO pagg_tab_p1 SELECT i % 30, i % 50, to_char(i/30, 'FM0000') FROM generate_series(1, 3000) i WHERE (i % 30) < 10; -INSERT INTO pagg_tab_p2 SELECT i % 30, i % 50, to_char(i/30, 'FM0000') FROM generate_series(1, 3000) i WHERE (i % 30) < 20 and (i % 30) >= 10; -INSERT INTO pagg_tab_p3 SELECT i % 30, i % 50, to_char(i/30, 'FM0000') FROM generate_series(1, 3000) i WHERE (i % 30) < 30 and (i % 30) >= 20; +INSERT INTO pagg_tab_p1 SELECT i % 30, i % 50, to_char(i/30, 'FM0000'), i, array[(i % 2), 0], ((i % 2) || ' seconds')::interval, (i % 2)::money, ((i - 1) % 10)::text, (((i - 1) % 10)::text)::bytea, case when (i % 2) = 0 then B'01' else B'11' end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then true else false end, (case when (i % 2) = 0 then 'sad' else 'happy' end)::mood, ('0/' || i)::pg_lsn, ('(0,' || i || ')')::tid, int4range(0, i), int4multirange(int4range(0, i), int4range(100, 100 + i)), '00:00:00'::time + (i || ' seconds')::interval, '00:00:00'::timetz + (i || ' seconds')::interval, '2000-01-01'::timestamp + (i || ' seconds')::interval, '2000-01-01'::timestamptz + (i || ' seconds')::interval, ((i % 10)::text)::xid8 FROM generate_series(1, 3000) i WHERE (i % 30) < 10; +INSERT INTO pagg_tab_p2 SELECT i % 30, i % 50, to_char(i/30, 'FM0000'), i, array[(i % 2), 0], ((i % 2) || ' seconds')::interval, (i % 2)::money, ((i - 1) % 10)::text, (((i - 1) % 10)::text)::bytea, case when (i % 2) = 0 then B'01' else B'11' end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then true else false end, (case when (i % 2) = 0 then 'sad' else 'happy' end)::mood, ('0/' || i)::pg_lsn, ('(0,' || i || ')')::tid, int4range(0, i), int4multirange(int4range(0, i), int4range(100, 100 + i)), '00:00:00'::time + (i || ' seconds')::interval, '00:00:00'::timetz + (i || ' seconds')::interval, '2000-01-01'::timestamp + (i || ' seconds')::interval, '2000-01-01'::timestamptz + (i || ' seconds')::interval, ((i % 10)::text)::xid8 FROM generate_series(1, 3000) i WHERE (i % 30) < 20 and (i % 30) >= 10; +INSERT INTO pagg_tab_p3 SELECT i % 30, i % 50, to_char(i/30, 'FM0000'), i, array[(i % 2), 0], ((i % 2) || ' seconds')::interval, (i % 2)::money, ((i - 1) % 10)::text, (((i - 1) % 10)::text)::bytea, case when (i % 2) = 0 then B'01' else B'11' end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then true else false end, (case when (i % 2) = 0 then 'sad' else 'happy' end)::mood, ('0/' || i)::pg_lsn, ('(0,' || i || ')')::tid, int4range(0, i), int4multirange(int4range(0, i), int4range(100, 100 + i)), '00:00:00'::time + (i || ' seconds')::interval, '00:00:00'::timetz + (i || ' seconds')::interval, '2000-01-01'::timestamp + (i || ' seconds')::interval, '2000-01-01'::timestamptz + (i || ' seconds')::interval, ((i % 10)::text)::xid8 FROM generate_series(1, 3000) i WHERE (i % 30) < 30 and (i % 30) >= 20; -- Create foreign partitions CREATE FOREIGN TABLE fpagg_tab_p1 PARTITION OF pagg_tab FOR VALUES FROM (0) TO (10) SERVER loopback OPTIONS (table_name 'pagg_tab_p1'); CREATE FOREIGN TABLE fpagg_tab_p2 PARTITION OF pagg_tab FOR VALUES FROM (10) TO (20) SERVER loopback OPTIONS (table_name 'pagg_tab_p2'); CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3'); ANALYZE pagg_tab; +ANALYZE pagg_tab_p1; +ANALYZE pagg_tab_p2; +ANALYZE pagg_tab_p3; ANALYZE fpagg_tab_p1; ANALYZE fpagg_tab_p2; ANALYZE fpagg_tab_p3; @@ -9823,8 +9840,8 @@ SELECT a, sum(b), min(b), count(*) FROM pagg_tab GROUP BY a HAVING avg(b) < 22 O -- Should have all the columns in the target list for the given relation EXPLAIN (VERBOSE, COSTS OFF) SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1; - QUERY PLAN ------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Sort Output: t1.a, (count(((t1.*)::pagg_tab))) Sort Key: t1.a @@ -9835,21 +9852,21 @@ SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1; Filter: (avg(t1.b) < '22'::numeric) -> Foreign Scan on public.fpagg_tab_p1 t1 Output: t1.a, t1.*, t1.b - Remote SQL: SELECT a, b, c FROM public.pagg_tab_p1 + Remote SQL: SELECT a, b, c, c_serial, c_int4array, c_interval, c_money, c_1c, c_1b, c_bit, c_1or3int2, c_1or3int4, c_1or3int8, c_bool, c_enum, c_pg_lsn, c_tid, c_int4range, c_int4multirange, c_time, c_timetz, c_timestamp, c_timestamptz, c_xid8 FROM public.pagg_tab_p1 -> HashAggregate Output: t1_1.a, count(((t1_1.*)::pagg_tab)) Group Key: t1_1.a Filter: (avg(t1_1.b) < '22'::numeric) -> Foreign Scan on public.fpagg_tab_p2 t1_1 Output: t1_1.a, t1_1.*, t1_1.b - Remote SQL: SELECT a, b, c FROM public.pagg_tab_p2 + Remote SQL: SELECT a, b, c, c_serial, c_int4array, c_interval, c_money, c_1c, c_1b, c_bit, c_1or3int2, c_1or3int4, c_1or3int8, c_bool, c_enum, c_pg_lsn, c_tid, c_int4range, c_int4multirange, c_time, c_timetz, c_timestamp, c_timestamptz, c_xid8 FROM public.pagg_tab_p2 -> HashAggregate Output: t1_2.a, count(((t1_2.*)::pagg_tab)) Group Key: t1_2.a Filter: (avg(t1_2.b) < '22'::numeric) -> Foreign Scan on public.fpagg_tab_p3 t1_2 Output: t1_2.a, t1_2.*, t1_2.b - Remote SQL: SELECT a, b, c FROM public.pagg_tab_p3 + Remote SQL: SELECT a, b, c, c_serial, c_int4array, c_interval, c_money, c_1c, c_1b, c_bit, c_1or3int2, c_1or3int4, c_1or3int8, c_bool, c_enum, c_pg_lsn, c_tid, c_int4range, c_int4multirange, c_time, c_timetz, c_timestamp, c_timestamptz, c_xid8 FROM public.pagg_tab_p3 (25 rows) SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1; @@ -9863,28 +9880,837 @@ SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1; 21 | 100 (6 rows) --- When GROUP BY clause does not match with PARTITION KEY. -EXPLAIN (COSTS OFF) +-- Partial aggregates are unsafe to push down when there is a HAVING clause +EXPLAIN (VERBOSE, COSTS OFF) SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1; - QUERY PLAN ------------------------------------------------------------------ - Sort - Sort Key: pagg_tab.b - -> Finalize HashAggregate - Group Key: pagg_tab.b - Filter: (sum(pagg_tab.a) < 700) + QUERY PLAN +--------------------------------------------------------------------------------------------------------------------------------------------- + Finalize GroupAggregate + Output: pagg_tab.b, avg(pagg_tab.a), max(pagg_tab.a), count(*) + Group Key: pagg_tab.b + Filter: (sum(pagg_tab.a) < 700) + -> Sort + Output: pagg_tab.b, (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)), (PARTIAL sum(pagg_tab.a)) + Sort Key: pagg_tab.b -> Append -> Partial HashAggregate + Output: pagg_tab.b, PARTIAL avg(pagg_tab.a), PARTIAL max(pagg_tab.a), PARTIAL count(*), PARTIAL sum(pagg_tab.a) Group Key: pagg_tab.b - -> Foreign Scan on fpagg_tab_p1 pagg_tab + -> Foreign Scan on public.fpagg_tab_p1 pagg_tab + Output: pagg_tab.b, pagg_tab.a + Remote SQL: SELECT a, b FROM public.pagg_tab_p1 -> Partial HashAggregate + Output: pagg_tab_1.b, PARTIAL avg(pagg_tab_1.a), PARTIAL max(pagg_tab_1.a), PARTIAL count(*), PARTIAL sum(pagg_tab_1.a) Group Key: pagg_tab_1.b - -> Foreign Scan on fpagg_tab_p2 pagg_tab_1 + -> Foreign Scan on public.fpagg_tab_p2 pagg_tab_1 + Output: pagg_tab_1.b, pagg_tab_1.a + Remote SQL: SELECT a, b FROM public.pagg_tab_p2 -> Partial HashAggregate + Output: pagg_tab_2.b, PARTIAL avg(pagg_tab_2.a), PARTIAL max(pagg_tab_2.a), PARTIAL count(*), PARTIAL sum(pagg_tab_2.a) Group Key: pagg_tab_2.b - -> Foreign Scan on fpagg_tab_p3 pagg_tab_2 + -> Foreign Scan on public.fpagg_tab_p3 pagg_tab_2 + Output: pagg_tab_2.b, pagg_tab_2.a + Remote SQL: SELECT a, b FROM public.pagg_tab_p3 +(26 rows) + +SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1; + b | avg | max | count +----+---------------------+-----+------- + 0 | 10.0000000000000000 | 20 | 60 + 1 | 11.0000000000000000 | 21 | 60 + 10 | 10.0000000000000000 | 20 | 60 + 11 | 11.0000000000000000 | 21 | 60 + 20 | 10.0000000000000000 | 20 | 60 + 21 | 11.0000000000000000 | 21 | 60 + 30 | 10.0000000000000000 | 20 | 60 + 31 | 11.0000000000000000 | 21 | 60 + 40 | 10.0000000000000000 | 20 | 60 + 41 | 11.0000000000000000 | 21 | 60 +(10 rows) + +-- Partial aggregates are safe to push down without having clause +EXPLAIN (VERBOSE, COSTS OFF) +SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + Sort + Output: pagg_tab.b, (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)) + Sort Key: pagg_tab.b + -> Finalize HashAggregate + Output: pagg_tab.b, avg(pagg_tab.a), max(pagg_tab.a), count(*) + Group Key: pagg_tab.b + -> Append + -> Foreign Scan + Output: pagg_tab.b, (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)) + Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab) + Remote SQL: SELECT b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1 + -> Foreign Scan + Output: pagg_tab_1.b, (PARTIAL avg(pagg_tab_1.a)), (PARTIAL max(pagg_tab_1.a)), (PARTIAL count(*)) + Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1) + Remote SQL: SELECT b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1 + -> Foreign Scan + Output: pagg_tab_2.b, (PARTIAL avg(pagg_tab_2.a)), (PARTIAL max(pagg_tab_2.a)), (PARTIAL count(*)) + Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2) + Remote SQL: SELECT b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1 +(19 rows) + +SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1; + b | avg | max | count +----+---------------------+-----+------- + 0 | 10.0000000000000000 | 20 | 60 + 1 | 11.0000000000000000 | 21 | 60 + 2 | 12.0000000000000000 | 22 | 60 + 3 | 13.0000000000000000 | 23 | 60 + 4 | 14.0000000000000000 | 24 | 60 + 5 | 15.0000000000000000 | 25 | 60 + 6 | 16.0000000000000000 | 26 | 60 + 7 | 17.0000000000000000 | 27 | 60 + 8 | 18.0000000000000000 | 28 | 60 + 9 | 19.0000000000000000 | 29 | 60 + 10 | 10.0000000000000000 | 20 | 60 + 11 | 11.0000000000000000 | 21 | 60 + 12 | 12.0000000000000000 | 22 | 60 + 13 | 13.0000000000000000 | 23 | 60 + 14 | 14.0000000000000000 | 24 | 60 + 15 | 15.0000000000000000 | 25 | 60 + 16 | 16.0000000000000000 | 26 | 60 + 17 | 17.0000000000000000 | 27 | 60 + 18 | 18.0000000000000000 | 28 | 60 + 19 | 19.0000000000000000 | 29 | 60 + 20 | 10.0000000000000000 | 20 | 60 + 21 | 11.0000000000000000 | 21 | 60 + 22 | 12.0000000000000000 | 22 | 60 + 23 | 13.0000000000000000 | 23 | 60 + 24 | 14.0000000000000000 | 24 | 60 + 25 | 15.0000000000000000 | 25 | 60 + 26 | 16.0000000000000000 | 26 | 60 + 27 | 17.0000000000000000 | 27 | 60 + 28 | 18.0000000000000000 | 28 | 60 + 29 | 19.0000000000000000 | 29 | 60 + 30 | 10.0000000000000000 | 20 | 60 + 31 | 11.0000000000000000 | 21 | 60 + 32 | 12.0000000000000000 | 22 | 60 + 33 | 13.0000000000000000 | 23 | 60 + 34 | 14.0000000000000000 | 24 | 60 + 35 | 15.0000000000000000 | 25 | 60 + 36 | 16.0000000000000000 | 26 | 60 + 37 | 17.0000000000000000 | 27 | 60 + 38 | 18.0000000000000000 | 28 | 60 + 39 | 19.0000000000000000 | 29 | 60 + 40 | 10.0000000000000000 | 20 | 60 + 41 | 11.0000000000000000 | 21 | 60 + 42 | 12.0000000000000000 | 22 | 60 + 43 | 13.0000000000000000 | 23 | 60 + 44 | 14.0000000000000000 | 24 | 60 + 45 | 15.0000000000000000 | 25 | 60 + 46 | 16.0000000000000000 | 26 | 60 + 47 | 17.0000000000000000 | 27 | 60 + 48 | 18.0000000000000000 | 28 | 60 + 49 | 19.0000000000000000 | 29 | 60 +(50 rows) + +-- Partial aggregates are safe to push down even if we need both variable and variable-based expression +EXPLAIN (VERBOSE, COSTS OFF) +SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------- + Sort + Output: (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)), ((((pagg_tab.b / 2)))::numeric), ((pagg_tab.b / 2)) + Sort Key: ((((pagg_tab.b / 2)))::numeric) + -> Finalize HashAggregate + Output: avg(pagg_tab.a), max(pagg_tab.a), count(*), (((pagg_tab.b / 2)))::numeric, ((pagg_tab.b / 2)) + Group Key: ((pagg_tab.b / 2)) + -> Append + -> Foreign Scan + Output: ((pagg_tab.b / 2)), (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)), pagg_tab.b + Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab) + Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1, 2 + -> Foreign Scan + Output: ((pagg_tab_1.b / 2)), (PARTIAL avg(pagg_tab_1.a)), (PARTIAL max(pagg_tab_1.a)), (PARTIAL count(*)), pagg_tab_1.b + Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1) + Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1, 2 + -> Foreign Scan + Output: ((pagg_tab_2.b / 2)), (PARTIAL avg(pagg_tab_2.a)), (PARTIAL max(pagg_tab_2.a)), (PARTIAL count(*)), pagg_tab_2.b + Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2) + Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1, 2 +(19 rows) + +SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4; + avg | max | count | numeric +---------------------+-----+-------+--------- + 10.5000000000000000 | 21 | 120 | 0 + 12.5000000000000000 | 23 | 120 | 1 + 14.5000000000000000 | 25 | 120 | 2 + 16.5000000000000000 | 27 | 120 | 3 + 18.5000000000000000 | 29 | 120 | 4 + 10.5000000000000000 | 21 | 120 | 5 + 12.5000000000000000 | 23 | 120 | 6 + 14.5000000000000000 | 25 | 120 | 7 + 16.5000000000000000 | 27 | 120 | 8 + 18.5000000000000000 | 29 | 120 | 9 + 10.5000000000000000 | 21 | 120 | 10 + 12.5000000000000000 | 23 | 120 | 11 + 14.5000000000000000 | 25 | 120 | 12 + 16.5000000000000000 | 27 | 120 | 13 + 18.5000000000000000 | 29 | 120 | 14 + 10.5000000000000000 | 21 | 120 | 15 + 12.5000000000000000 | 23 | 120 | 16 + 14.5000000000000000 | 25 | 120 | 17 + 16.5000000000000000 | 27 | 120 | 18 + 18.5000000000000000 | 29 | 120 | 19 + 10.5000000000000000 | 21 | 120 | 20 + 12.5000000000000000 | 23 | 120 | 21 + 14.5000000000000000 | 25 | 120 | 22 + 16.5000000000000000 | 27 | 120 | 23 + 18.5000000000000000 | 29 | 120 | 24 +(25 rows) + +-- Partial aggregates are safe to push down for all built-in aggregates +EXPLAIN (VERBOSE, COSTS OFF) +SELECT /* aggregate function <> aggpartialfunc */ + array_agg(c_int4array), array_agg(b), + avg(b::int2), avg(b::int4), avg(b::int8), avg(c_interval), avg(b::float4), avg(b::float8), avg(b::numeric), + corr(b::float8, (b * b)::float8), + covar_pop(b::float8, (b * b)::float8), + covar_samp(b::float8, (b * b)::float8), + regr_avgx((2 * b)::float8, b::float8), + regr_avgy((2 * b)::float8, b::float8), + regr_intercept((2 * b + 3)::float8, b::float8), + regr_r2((b * b)::float8, b::float8), + regr_slope((2 * b + 3)::float8, b::float8), + regr_sxx((2 * b + 3)::float8, b::float8), + regr_sxy((b * b)::float8, b::float8), + regr_syy((2 * b + 3)::float8, b::float8), + stddev(b::float4), stddev(b::float8), stddev(b::int2), stddev(b::int4), stddev(b::int8), stddev(b::numeric), + stddev_pop(b::float4), stddev_pop(b::float8), stddev_pop(b::int2), stddev_pop(b::int4), stddev_pop(b::int8), stddev_pop(b::numeric), + stddev_samp(b::float4), stddev_samp(b::float8), stddev_samp(b::int2), stddev_samp(b::int4), stddev_samp(b::int8), stddev_samp(b::numeric), + string_agg(c_1c, ','), string_agg(c_1b, ','), + sum(b::int8), sum(b::numeric), + variance(b::float4), variance(b::float8), variance(b::int2), variance(b::int4), variance(b::int8), variance(b::numeric), + var_pop(b::float4), var_pop(b::float8), var_pop(b::int2), var_pop(b::int4), var_pop(b::int8), var_pop(b::numeric), + var_samp(b::float4), var_samp(b::float8), var_samp(b::int2), var_samp(b::int4), var_samp(b::int8), var_samp(b::numeric), + /* aggregate function = aggpartialfunc */ + any_value(b * 0), + bit_and(c_bit), bit_and(c_1or3int2), bit_and(c_1or3int4), bit_and(c_1or3int8), + bit_or(c_bit), bit_or(c_1or3int2), bit_or(c_1or3int4), bit_or(c_1or3int8), + bit_xor(c_bit), bit_xor(c_1or3int2), bit_xor(c_1or3int4), bit_xor(c_1or3int8), + bool_and(c_bool), + bool_or(c_bool), + count(b), count(*), + every(c_bool), + max(c_int4array), max(c_enum), max(c_1c::char(1)), max('2000-01-01'::date + b), max('0.0.0.0'::inet + b), max(b::float4), max(b::float8), max(b::int2), max(b::int4), max(b::int8), max(c_interval), max(c_money), max(b::numeric), max(b::oid), max(c_pg_lsn), max(c_tid), max(c_1c), max(c_time), max(c_timetz), max(c_timestamp), max(c_timestamptz), max(c_xid8), + min(c_int4array), min(c_enum), min(c_1c::char(1)), min('2000-01-01'::date + b), min('0.0.0.0'::inet + b), min(b::float4), min(b::float8), min(b::int2), min(b::int4), min(b::int8), min(c_interval), min(c_money), min(b::numeric), min(b::oid), min(c_pg_lsn), min(c_tid), min(c_1c), min(c_time), min(c_timetz), min(c_timestamp), min(c_timestamptz), min(c_xid8), + range_intersect_agg(c_int4range), range_intersect_agg(c_int4multirange), + regr_count((2 * b + 3)::float8, b::float8), + sum(b::float4), sum(b::float8), sum(b::int2), sum(b::int4), sum(c_interval), sum(c_money) + FROM pagg_tab WHERE c_serial between 1 and 30; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Finalize Aggregate + Output: array_agg(pagg_tab.c_int4array), array_agg(pagg_tab.b), avg((pagg_tab.b)::smallint), avg(pagg_tab.b), avg((pagg_tab.b)::bigint), avg(pagg_tab.c_interval), avg((pagg_tab.b)::real), avg((pagg_tab.b)::double precision), avg((pagg_tab.b)::numeric), corr((pagg_tab.b)::double precision, ((pagg_tab.b * pagg_tab.b))::double precision), covar_pop((pagg_tab.b)::double precision, ((pagg_tab.b * pagg_tab.b))::double precision), covar_samp((pagg_tab.b)::double precision, ((pagg_tab.b * pagg_tab.b))::double precision), regr_avgx(((2 * pagg_tab.b))::double precision, (pagg_tab.b)::double precision), regr_avgy(((2 * pagg_tab.b))::double precision, (pagg_tab.b)::double precision), regr_intercept((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision), regr_r2(((pagg_tab.b * pagg_tab.b))::double precision, (pagg_tab.b)::double precision), regr_slope((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision), regr_sxx((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision), regr_sxy(((pagg_tab.b * pagg_tab.b))::double precision, (pagg_tab.b)::double precision), regr_syy((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision), stddev((pagg_tab.b)::real), stddev((pagg_tab.b)::double precision), stddev((pagg_tab.b)::smallint), stddev(pagg_tab.b), stddev((pagg_tab.b)::bigint), stddev((pagg_tab.b)::numeric), stddev_pop((pagg_tab.b)::real), stddev_pop((pagg_tab.b)::double precision), stddev_pop((pagg_tab.b)::smallint), stddev_pop(pagg_tab.b), stddev_pop((pagg_tab.b)::bigint), stddev_pop((pagg_tab.b)::numeric), stddev_samp((pagg_tab.b)::real), stddev_samp((pagg_tab.b)::double precision), stddev_samp((pagg_tab.b)::smallint), stddev_samp(pagg_tab.b), stddev_samp((pagg_tab.b)::bigint), stddev_samp((pagg_tab.b)::numeric), string_agg(pagg_tab.c_1c, ','::text), string_agg(pagg_tab.c_1b, '\x2c'::bytea), sum((pagg_tab.b)::bigint), sum((pagg_tab.b)::numeric), variance((pagg_tab.b)::real), variance((pagg_tab.b)::double precision), variance((pagg_tab.b)::smallint), variance(pagg_tab.b), variance((pagg_tab.b)::bigint), variance((pagg_tab.b)::numeric), var_pop((pagg_tab.b)::real), var_pop((pagg_tab.b)::double precision), var_pop((pagg_tab.b)::smallint), var_pop(pagg_tab.b), var_pop((pagg_tab.b)::bigint), var_pop((pagg_tab.b)::numeric), var_samp((pagg_tab.b)::real), var_samp((pagg_tab.b)::double precision), var_samp((pagg_tab.b)::smallint), var_samp(pagg_tab.b), var_samp((pagg_tab.b)::bigint), var_samp((pagg_tab.b)::numeric), any_value((pagg_tab.b * 0)), bit_and(pagg_tab.c_bit), bit_and(pagg_tab.c_1or3int2), bit_and(pagg_tab.c_1or3int4), bit_and(pagg_tab.c_1or3int8), bit_or(pagg_tab.c_bit), bit_or(pagg_tab.c_1or3int2), bit_or(pagg_tab.c_1or3int4), bit_or(pagg_tab.c_1or3int8), bit_xor(pagg_tab.c_bit), bit_xor(pagg_tab.c_1or3int2), bit_xor(pagg_tab.c_1or3int4), bit_xor(pagg_tab.c_1or3int8), bool_and(pagg_tab.c_bool), bool_or(pagg_tab.c_bool), count(pagg_tab.b), count(*), every(pagg_tab.c_bool), max(pagg_tab.c_int4array), max(pagg_tab.c_enum), max((pagg_tab.c_1c)::character(1)), max(('01-01-2000'::date + pagg_tab.b)), max(('0.0.0.0'::inet + (pagg_tab.b)::bigint)), max((pagg_tab.b)::real), max((pagg_tab.b)::double precision), max((pagg_tab.b)::smallint), max(pagg_tab.b), max((pagg_tab.b)::bigint), max(pagg_tab.c_interval), max(pagg_tab.c_money), max((pagg_tab.b)::numeric), max((pagg_tab.b)::oid), max(pagg_tab.c_pg_lsn), max(pagg_tab.c_tid), max(pagg_tab.c_1c), max(pagg_tab.c_time), max(pagg_tab.c_timetz), max(pagg_tab.c_timestamp), max(pagg_tab.c_timestamptz), max(pagg_tab.c_xid8), min(pagg_tab.c_int4array), min(pagg_tab.c_enum), min((pagg_tab.c_1c)::character(1)), min(('01-01-2000'::date + pagg_tab.b)), min(('0.0.0.0'::inet + (pagg_tab.b)::bigint)), min((pagg_tab.b)::real), min((pagg_tab.b)::double precision), min((pagg_tab.b)::smallint), min(pagg_tab.b), min((pagg_tab.b)::bigint), min(pagg_tab.c_interval), min(pagg_tab.c_money), min((pagg_tab.b)::numeric), min((pagg_tab.b)::oid), min(pagg_tab.c_pg_lsn), min(pagg_tab.c_tid), min(pagg_tab.c_1c), min(pagg_tab.c_time), min(pagg_tab.c_timetz), min(pagg_tab.c_timestamp), min(pagg_tab.c_timestamptz), min(pagg_tab.c_xid8), range_intersect_agg(pagg_tab.c_int4range), range_intersect_agg(pagg_tab.c_int4multirange), regr_count((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision), sum((pagg_tab.b)::real), sum((pagg_tab.b)::double precision), sum((pagg_tab.b)::smallint), sum(pagg_tab.b), sum(pagg_tab.c_interval), sum(pagg_tab.c_money) + -> Append + -> Foreign Scan + Output: (PARTIAL array_agg(pagg_tab.c_int4array)), (PARTIAL array_agg(pagg_tab.b)), (PARTIAL avg((pagg_tab.b)::smallint)), (PARTIAL avg(pagg_tab.b)), (PARTIAL avg((pagg_tab.b)::bigint)), (PARTIAL avg(pagg_tab.c_interval)), (PARTIAL avg((pagg_tab.b)::real)), (PARTIAL avg((pagg_tab.b)::double precision)), (PARTIAL avg((pagg_tab.b)::numeric)), (PARTIAL corr((pagg_tab.b)::double precision, ((pagg_tab.b * pagg_tab.b))::double precision)), (PARTIAL covar_pop((pagg_tab.b)::double precision, ((pagg_tab.b * pagg_tab.b))::double precision)), (PARTIAL covar_samp((pagg_tab.b)::double precision, ((pagg_tab.b * pagg_tab.b))::double precision)), (PARTIAL regr_avgx(((2 * pagg_tab.b))::double precision, (pagg_tab.b)::double precision)), (PARTIAL regr_avgy(((2 * pagg_tab.b))::double precision, (pagg_tab.b)::double precision)), (PARTIAL regr_intercept((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision)), (PARTIAL regr_r2(((pagg_tab.b * pagg_tab.b))::double precision, (pagg_tab.b)::double precision)), (PARTIAL regr_slope((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision)), (PARTIAL regr_sxx((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision)), (PARTIAL regr_sxy(((pagg_tab.b * pagg_tab.b))::double precision, (pagg_tab.b)::double precision)), (PARTIAL regr_syy((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision)), (PARTIAL stddev((pagg_tab.b)::real)), (PARTIAL stddev((pagg_tab.b)::double precision)), (PARTIAL stddev((pagg_tab.b)::smallint)), (PARTIAL stddev(pagg_tab.b)), (PARTIAL stddev((pagg_tab.b)::bigint)), (PARTIAL stddev((pagg_tab.b)::numeric)), (PARTIAL stddev_pop((pagg_tab.b)::real)), (PARTIAL stddev_pop((pagg_tab.b)::double precision)), (PARTIAL stddev_pop((pagg_tab.b)::smallint)), (PARTIAL stddev_pop(pagg_tab.b)), (PARTIAL stddev_pop((pagg_tab.b)::bigint)), (PARTIAL stddev_pop((pagg_tab.b)::numeric)), (PARTIAL stddev_samp((pagg_tab.b)::real)), (PARTIAL stddev_samp((pagg_tab.b)::double precision)), (PARTIAL stddev_samp((pagg_tab.b)::smallint)), (PARTIAL stddev_samp(pagg_tab.b)), (PARTIAL stddev_samp((pagg_tab.b)::bigint)), (PARTIAL stddev_samp((pagg_tab.b)::numeric)), (PARTIAL string_agg(pagg_tab.c_1c, ','::text)), (PARTIAL string_agg(pagg_tab.c_1b, '\x2c'::bytea)), (PARTIAL sum((pagg_tab.b)::bigint)), (PARTIAL sum((pagg_tab.b)::numeric)), (PARTIAL variance((pagg_tab.b)::real)), (PARTIAL variance((pagg_tab.b)::double precision)), (PARTIAL variance((pagg_tab.b)::smallint)), (PARTIAL variance(pagg_tab.b)), (PARTIAL variance((pagg_tab.b)::bigint)), (PARTIAL variance((pagg_tab.b)::numeric)), (PARTIAL var_pop((pagg_tab.b)::real)), (PARTIAL var_pop((pagg_tab.b)::double precision)), (PARTIAL var_pop((pagg_tab.b)::smallint)), (PARTIAL var_pop(pagg_tab.b)), (PARTIAL var_pop((pagg_tab.b)::bigint)), (PARTIAL var_pop((pagg_tab.b)::numeric)), (PARTIAL var_samp((pagg_tab.b)::real)), (PARTIAL var_samp((pagg_tab.b)::double precision)), (PARTIAL var_samp((pagg_tab.b)::smallint)), (PARTIAL var_samp(pagg_tab.b)), (PARTIAL var_samp((pagg_tab.b)::bigint)), (PARTIAL var_samp((pagg_tab.b)::numeric)), (PARTIAL any_value((pagg_tab.b * 0))), (PARTIAL bit_and(pagg_tab.c_bit)), (PARTIAL bit_and(pagg_tab.c_1or3int2)), (PARTIAL bit_and(pagg_tab.c_1or3int4)), (PARTIAL bit_and(pagg_tab.c_1or3int8)), (PARTIAL bit_or(pagg_tab.c_bit)), (PARTIAL bit_or(pagg_tab.c_1or3int2)), (PARTIAL bit_or(pagg_tab.c_1or3int4)), (PARTIAL bit_or(pagg_tab.c_1or3int8)), (PARTIAL bit_xor(pagg_tab.c_bit)), (PARTIAL bit_xor(pagg_tab.c_1or3int2)), (PARTIAL bit_xor(pagg_tab.c_1or3int4)), (PARTIAL bit_xor(pagg_tab.c_1or3int8)), (PARTIAL bool_and(pagg_tab.c_bool)), (PARTIAL bool_or(pagg_tab.c_bool)), (PARTIAL count(pagg_tab.b)), (PARTIAL count(*)), (PARTIAL every(pagg_tab.c_bool)), (PARTIAL max(pagg_tab.c_int4array)), (PARTIAL max(pagg_tab.c_enum)), (PARTIAL max((pagg_tab.c_1c)::character(1))), (PARTIAL max(('01-01-2000'::date + pagg_tab.b))), (PARTIAL max(('0.0.0.0'::inet + (pagg_tab.b)::bigint))), (PARTIAL max((pagg_tab.b)::real)), (PARTIAL max((pagg_tab.b)::double precision)), (PARTIAL max((pagg_tab.b)::smallint)), (PARTIAL max(pagg_tab.b)), (PARTIAL max((pagg_tab.b)::bigint)), (PARTIAL max(pagg_tab.c_interval)), (PARTIAL max(pagg_tab.c_money)), (PARTIAL max((pagg_tab.b)::numeric)), (PARTIAL max((pagg_tab.b)::oid)), (PARTIAL max(pagg_tab.c_pg_lsn)), (PARTIAL max(pagg_tab.c_tid)), (PARTIAL max(pagg_tab.c_1c)), (PARTIAL max(pagg_tab.c_time)), (PARTIAL max(pagg_tab.c_timetz)), (PARTIAL max(pagg_tab.c_timestamp)), (PARTIAL max(pagg_tab.c_timestamptz)), (PARTIAL max(pagg_tab.c_xid8)), (PARTIAL min(pagg_tab.c_int4array)), (PARTIAL min(pagg_tab.c_enum)), (PARTIAL min((pagg_tab.c_1c)::character(1))), (PARTIAL min(('01-01-2000'::date + pagg_tab.b))), (PARTIAL min(('0.0.0.0'::inet + (pagg_tab.b)::bigint))), (PARTIAL min((pagg_tab.b)::real)), (PARTIAL min((pagg_tab.b)::double precision)), (PARTIAL min((pagg_tab.b)::smallint)), (PARTIAL min(pagg_tab.b)), (PARTIAL min((pagg_tab.b)::bigint)), (PARTIAL min(pagg_tab.c_interval)), (PARTIAL min(pagg_tab.c_money)), (PARTIAL min((pagg_tab.b)::numeric)), (PARTIAL min((pagg_tab.b)::oid)), (PARTIAL min(pagg_tab.c_pg_lsn)), (PARTIAL min(pagg_tab.c_tid)), (PARTIAL min(pagg_tab.c_1c)), (PARTIAL min(pagg_tab.c_time)), (PARTIAL min(pagg_tab.c_timetz)), (PARTIAL min(pagg_tab.c_timestamp)), (PARTIAL min(pagg_tab.c_timestamptz)), (PARTIAL min(pagg_tab.c_xid8)), (PARTIAL range_intersect_agg(pagg_tab.c_int4range)), (PARTIAL range_intersect_agg(pagg_tab.c_int4multirange)), (PARTIAL regr_count((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision)), (PARTIAL sum((pagg_tab.b)::real)), (PARTIAL sum((pagg_tab.b)::double precision)), (PARTIAL sum((pagg_tab.b)::smallint)), (PARTIAL sum(pagg_tab.b)), (PARTIAL sum(pagg_tab.c_interval)), (PARTIAL sum(pagg_tab.c_money)) + Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab) + Remote SQL: SELECT array_agg_p_anyarray(c_int4array), array_agg_p_anynonarray(b), avg_p_int2(b::smallint), avg_p_int4(b), avg_p_int8(b::bigint), avg_p_interval(c_interval), avg_p_float4(b::real), avg_p_float8(b::double precision), avg_p_numeric(b::numeric), corr_p(b::double precision, (b * b)::double precision), covar_pop_p(b::double precision, (b * b)::double precision), covar_samp_p(b::double precision, (b * b)::double precision), regr_avgx_p((2 * b)::double precision, b::double precision), regr_avgy_p((2 * b)::double precision, b::double precision), regr_intercept_p(((2 * b) + 3)::double precision, b::double precision), regr_r2_p((b * b)::double precision, b::double precision), regr_slope_p(((2 * b) + 3)::double precision, b::double precision), regr_sxx_p(((2 * b) + 3)::double precision, b::double precision), regr_sxy_p((b * b)::double precision, b::double precision), regr_syy_p(((2 * b) + 3)::double precision, b::double precision), stddev_p_float4(b::real), stddev_p_float8(b::double precision), stddev_p_int2(b::smallint), stddev_p_int4(b), stddev_p_int8(b::bigint), stddev_p_numeric(b::numeric), stddev_pop_p_float4(b::real), stddev_pop_p_float8(b::double precision), stddev_pop_p_int2(b::smallint), stddev_pop_p_int4(b), stddev_pop_p_int8(b::bigint), stddev_pop_p_numeric(b::numeric), stddev_samp_p_float4(b::real), stddev_samp_p_float8(b::double precision), stddev_samp_p_int2(b::smallint), stddev_samp_p_int4(b), stddev_samp_p_int8(b::bigint), stddev_samp_p_numeric(b::numeric), string_agg_p_text_text(c_1c, ','::text), string_agg_p_bytea_bytea(c_1b, E'\\x2c'::bytea), sum_p_int8(b::bigint), sum_p_numeric(b::numeric), variance_p_float4(b::real), variance_p_float8(b::double precision), variance_p_int2(b::smallint), variance_p_int4(b), variance_p_int8(b::bigint), variance_p_numeric(b::numeric), var_pop_p_float4(b::real), var_pop_p_float8(b::double precision), var_pop_p_int2(b::smallint), var_pop_p_int4(b), var_pop_p_int8(b::bigint), var_pop_p_numeric(b::numeric), var_samp_p_float4(b::real), var_samp_p_float8(b::double precision), var_samp_p_int2(b::smallint), var_samp_p_int4(b), var_samp_p_int8(b::bigint), var_samp_p_numeric(b::numeric), any_value((b * 0)), bit_and(c_bit), bit_and(c_1or3int2), bit_and(c_1or3int4), bit_and(c_1or3int8), bit_or(c_bit), bit_or(c_1or3int2), bit_or(c_1or3int4), bit_or(c_1or3int8), bit_xor(c_bit), bit_xor(c_1or3int2), bit_xor(c_1or3int4), bit_xor(c_1or3int8), bool_and(c_bool), bool_or(c_bool), count(b), count(*), every(c_bool), max(c_int4array), max(c_enum), max(c_1c::character(1)), max(('01-01-2000'::date + b)), max(('0.0.0.0'::inet + b)), max(b::real), max(b::double precision), max(b::smallint), max(b), max(b::bigint), max(c_interval), max(c_money), max(b::numeric), max(b::oid), max(c_pg_lsn), max(c_tid), max(c_1c), max(c_time), max(c_timetz), max(c_timestamp), max(c_timestamptz), max(c_xid8), min(c_int4array), min(c_enum), min(c_1c::character(1)), min(('01-01-2000'::date + b)), min(('0.0.0.0'::inet + b)), min(b::real), min(b::double precision), min(b::smallint), min(b), min(b::bigint), min(c_interval), min(c_money), min(b::numeric), min(b::oid), min(c_pg_lsn), min(c_tid), min(c_1c), min(c_time), min(c_timetz), min(c_timestamp), min(c_timestamptz), min(c_xid8), range_intersect_agg(c_int4range), range_intersect_agg(c_int4multirange), regr_count(((2 * b) + 3)::double precision, b::double precision), sum(b::real), sum(b::double precision), sum(b::smallint), sum(b), sum(c_interval), sum(c_money) FROM public.pagg_tab_p1 WHERE ((c_serial >= 1)) AND ((c_serial <= 30)) + -> Foreign Scan + Output: (PARTIAL array_agg(pagg_tab_1.c_int4array)), (PARTIAL array_agg(pagg_tab_1.b)), (PARTIAL avg((pagg_tab_1.b)::smallint)), (PARTIAL avg(pagg_tab_1.b)), (PARTIAL avg((pagg_tab_1.b)::bigint)), (PARTIAL avg(pagg_tab_1.c_interval)), (PARTIAL avg((pagg_tab_1.b)::real)), (PARTIAL avg((pagg_tab_1.b)::double precision)), (PARTIAL avg((pagg_tab_1.b)::numeric)), (PARTIAL corr((pagg_tab_1.b)::double precision, ((pagg_tab_1.b * pagg_tab_1.b))::double precision)), (PARTIAL covar_pop((pagg_tab_1.b)::double precision, ((pagg_tab_1.b * pagg_tab_1.b))::double precision)), (PARTIAL covar_samp((pagg_tab_1.b)::double precision, ((pagg_tab_1.b * pagg_tab_1.b))::double precision)), (PARTIAL regr_avgx(((2 * pagg_tab_1.b))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL regr_avgy(((2 * pagg_tab_1.b))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL regr_intercept((((2 * pagg_tab_1.b) + 3))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL regr_r2(((pagg_tab_1.b * pagg_tab_1.b))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL regr_slope((((2 * pagg_tab_1.b) + 3))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL regr_sxx((((2 * pagg_tab_1.b) + 3))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL regr_sxy(((pagg_tab_1.b * pagg_tab_1.b))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL regr_syy((((2 * pagg_tab_1.b) + 3))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL stddev((pagg_tab_1.b)::real)), (PARTIAL stddev((pagg_tab_1.b)::double precision)), (PARTIAL stddev((pagg_tab_1.b)::smallint)), (PARTIAL stddev(pagg_tab_1.b)), (PARTIAL stddev((pagg_tab_1.b)::bigint)), (PARTIAL stddev((pagg_tab_1.b)::numeric)), (PARTIAL stddev_pop((pagg_tab_1.b)::real)), (PARTIAL stddev_pop((pagg_tab_1.b)::double precision)), (PARTIAL stddev_pop((pagg_tab_1.b)::smallint)), (PARTIAL stddev_pop(pagg_tab_1.b)), (PARTIAL stddev_pop((pagg_tab_1.b)::bigint)), (PARTIAL stddev_pop((pagg_tab_1.b)::numeric)), (PARTIAL stddev_samp((pagg_tab_1.b)::real)), (PARTIAL stddev_samp((pagg_tab_1.b)::double precision)), (PARTIAL stddev_samp((pagg_tab_1.b)::smallint)), (PARTIAL stddev_samp(pagg_tab_1.b)), (PARTIAL stddev_samp((pagg_tab_1.b)::bigint)), (PARTIAL stddev_samp((pagg_tab_1.b)::numeric)), (PARTIAL string_agg(pagg_tab_1.c_1c, ','::text)), (PARTIAL string_agg(pagg_tab_1.c_1b, '\x2c'::bytea)), (PARTIAL sum((pagg_tab_1.b)::bigint)), (PARTIAL sum((pagg_tab_1.b)::numeric)), (PARTIAL variance((pagg_tab_1.b)::real)), (PARTIAL variance((pagg_tab_1.b)::double precision)), (PARTIAL variance((pagg_tab_1.b)::smallint)), (PARTIAL variance(pagg_tab_1.b)), (PARTIAL variance((pagg_tab_1.b)::bigint)), (PARTIAL variance((pagg_tab_1.b)::numeric)), (PARTIAL var_pop((pagg_tab_1.b)::real)), (PARTIAL var_pop((pagg_tab_1.b)::double precision)), (PARTIAL var_pop((pagg_tab_1.b)::smallint)), (PARTIAL var_pop(pagg_tab_1.b)), (PARTIAL var_pop((pagg_tab_1.b)::bigint)), (PARTIAL var_pop((pagg_tab_1.b)::numeric)), (PARTIAL var_samp((pagg_tab_1.b)::real)), (PARTIAL var_samp((pagg_tab_1.b)::double precision)), (PARTIAL var_samp((pagg_tab_1.b)::smallint)), (PARTIAL var_samp(pagg_tab_1.b)), (PARTIAL var_samp((pagg_tab_1.b)::bigint)), (PARTIAL var_samp((pagg_tab_1.b)::numeric)), (PARTIAL any_value((pagg_tab_1.b * 0))), (PARTIAL bit_and(pagg_tab_1.c_bit)), (PARTIAL bit_and(pagg_tab_1.c_1or3int2)), (PARTIAL bit_and(pagg_tab_1.c_1or3int4)), (PARTIAL bit_and(pagg_tab_1.c_1or3int8)), (PARTIAL bit_or(pagg_tab_1.c_bit)), (PARTIAL bit_or(pagg_tab_1.c_1or3int2)), (PARTIAL bit_or(pagg_tab_1.c_1or3int4)), (PARTIAL bit_or(pagg_tab_1.c_1or3int8)), (PARTIAL bit_xor(pagg_tab_1.c_bit)), (PARTIAL bit_xor(pagg_tab_1.c_1or3int2)), (PARTIAL bit_xor(pagg_tab_1.c_1or3int4)), (PARTIAL bit_xor(pagg_tab_1.c_1or3int8)), (PARTIAL bool_and(pagg_tab_1.c_bool)), (PARTIAL bool_or(pagg_tab_1.c_bool)), (PARTIAL count(pagg_tab_1.b)), (PARTIAL count(*)), (PARTIAL every(pagg_tab_1.c_bool)), (PARTIAL max(pagg_tab_1.c_int4array)), (PARTIAL max(pagg_tab_1.c_enum)), (PARTIAL max((pagg_tab_1.c_1c)::character(1))), (PARTIAL max(('01-01-2000'::date + pagg_tab_1.b))), (PARTIAL max(('0.0.0.0'::inet + (pagg_tab_1.b)::bigint))), (PARTIAL max((pagg_tab_1.b)::real)), (PARTIAL max((pagg_tab_1.b)::double precision)), (PARTIAL max((pagg_tab_1.b)::smallint)), (PARTIAL max(pagg_tab_1.b)), (PARTIAL max((pagg_tab_1.b)::bigint)), (PARTIAL max(pagg_tab_1.c_interval)), (PARTIAL max(pagg_tab_1.c_money)), (PARTIAL max((pagg_tab_1.b)::numeric)), (PARTIAL max((pagg_tab_1.b)::oid)), (PARTIAL max(pagg_tab_1.c_pg_lsn)), (PARTIAL max(pagg_tab_1.c_tid)), (PARTIAL max(pagg_tab_1.c_1c)), (PARTIAL max(pagg_tab_1.c_time)), (PARTIAL max(pagg_tab_1.c_timetz)), (PARTIAL max(pagg_tab_1.c_timestamp)), (PARTIAL max(pagg_tab_1.c_timestamptz)), (PARTIAL max(pagg_tab_1.c_xid8)), (PARTIAL min(pagg_tab_1.c_int4array)), (PARTIAL min(pagg_tab_1.c_enum)), (PARTIAL min((pagg_tab_1.c_1c)::character(1))), (PARTIAL min(('01-01-2000'::date + pagg_tab_1.b))), (PARTIAL min(('0.0.0.0'::inet + (pagg_tab_1.b)::bigint))), (PARTIAL min((pagg_tab_1.b)::real)), (PARTIAL min((pagg_tab_1.b)::double precision)), (PARTIAL min((pagg_tab_1.b)::smallint)), (PARTIAL min(pagg_tab_1.b)), (PARTIAL min((pagg_tab_1.b)::bigint)), (PARTIAL min(pagg_tab_1.c_interval)), (PARTIAL min(pagg_tab_1.c_money)), (PARTIAL min((pagg_tab_1.b)::numeric)), (PARTIAL min((pagg_tab_1.b)::oid)), (PARTIAL min(pagg_tab_1.c_pg_lsn)), (PARTIAL min(pagg_tab_1.c_tid)), (PARTIAL min(pagg_tab_1.c_1c)), (PARTIAL min(pagg_tab_1.c_time)), (PARTIAL min(pagg_tab_1.c_timetz)), (PARTIAL min(pagg_tab_1.c_timestamp)), (PARTIAL min(pagg_tab_1.c_timestamptz)), (PARTIAL min(pagg_tab_1.c_xid8)), (PARTIAL range_intersect_agg(pagg_tab_1.c_int4range)), (PARTIAL range_intersect_agg(pagg_tab_1.c_int4multirange)), (PARTIAL regr_count((((2 * pagg_tab_1.b) + 3))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL sum((pagg_tab_1.b)::real)), (PARTIAL sum((pagg_tab_1.b)::double precision)), (PARTIAL sum((pagg_tab_1.b)::smallint)), (PARTIAL sum(pagg_tab_1.b)), (PARTIAL sum(pagg_tab_1.c_interval)), (PARTIAL sum(pagg_tab_1.c_money)) + Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1) + Remote SQL: SELECT array_agg_p_anyarray(c_int4array), array_agg_p_anynonarray(b), avg_p_int2(b::smallint), avg_p_int4(b), avg_p_int8(b::bigint), avg_p_interval(c_interval), avg_p_float4(b::real), avg_p_float8(b::double precision), avg_p_numeric(b::numeric), corr_p(b::double precision, (b * b)::double precision), covar_pop_p(b::double precision, (b * b)::double precision), covar_samp_p(b::double precision, (b * b)::double precision), regr_avgx_p((2 * b)::double precision, b::double precision), regr_avgy_p((2 * b)::double precision, b::double precision), regr_intercept_p(((2 * b) + 3)::double precision, b::double precision), regr_r2_p((b * b)::double precision, b::double precision), regr_slope_p(((2 * b) + 3)::double precision, b::double precision), regr_sxx_p(((2 * b) + 3)::double precision, b::double precision), regr_sxy_p((b * b)::double precision, b::double precision), regr_syy_p(((2 * b) + 3)::double precision, b::double precision), stddev_p_float4(b::real), stddev_p_float8(b::double precision), stddev_p_int2(b::smallint), stddev_p_int4(b), stddev_p_int8(b::bigint), stddev_p_numeric(b::numeric), stddev_pop_p_float4(b::real), stddev_pop_p_float8(b::double precision), stddev_pop_p_int2(b::smallint), stddev_pop_p_int4(b), stddev_pop_p_int8(b::bigint), stddev_pop_p_numeric(b::numeric), stddev_samp_p_float4(b::real), stddev_samp_p_float8(b::double precision), stddev_samp_p_int2(b::smallint), stddev_samp_p_int4(b), stddev_samp_p_int8(b::bigint), stddev_samp_p_numeric(b::numeric), string_agg_p_text_text(c_1c, ','::text), string_agg_p_bytea_bytea(c_1b, E'\\x2c'::bytea), sum_p_int8(b::bigint), sum_p_numeric(b::numeric), variance_p_float4(b::real), variance_p_float8(b::double precision), variance_p_int2(b::smallint), variance_p_int4(b), variance_p_int8(b::bigint), variance_p_numeric(b::numeric), var_pop_p_float4(b::real), var_pop_p_float8(b::double precision), var_pop_p_int2(b::smallint), var_pop_p_int4(b), var_pop_p_int8(b::bigint), var_pop_p_numeric(b::numeric), var_samp_p_float4(b::real), var_samp_p_float8(b::double precision), var_samp_p_int2(b::smallint), var_samp_p_int4(b), var_samp_p_int8(b::bigint), var_samp_p_numeric(b::numeric), any_value((b * 0)), bit_and(c_bit), bit_and(c_1or3int2), bit_and(c_1or3int4), bit_and(c_1or3int8), bit_or(c_bit), bit_or(c_1or3int2), bit_or(c_1or3int4), bit_or(c_1or3int8), bit_xor(c_bit), bit_xor(c_1or3int2), bit_xor(c_1or3int4), bit_xor(c_1or3int8), bool_and(c_bool), bool_or(c_bool), count(b), count(*), every(c_bool), max(c_int4array), max(c_enum), max(c_1c::character(1)), max(('01-01-2000'::date + b)), max(('0.0.0.0'::inet + b)), max(b::real), max(b::double precision), max(b::smallint), max(b), max(b::bigint), max(c_interval), max(c_money), max(b::numeric), max(b::oid), max(c_pg_lsn), max(c_tid), max(c_1c), max(c_time), max(c_timetz), max(c_timestamp), max(c_timestamptz), max(c_xid8), min(c_int4array), min(c_enum), min(c_1c::character(1)), min(('01-01-2000'::date + b)), min(('0.0.0.0'::inet + b)), min(b::real), min(b::double precision), min(b::smallint), min(b), min(b::bigint), min(c_interval), min(c_money), min(b::numeric), min(b::oid), min(c_pg_lsn), min(c_tid), min(c_1c), min(c_time), min(c_timetz), min(c_timestamp), min(c_timestamptz), min(c_xid8), range_intersect_agg(c_int4range), range_intersect_agg(c_int4multirange), regr_count(((2 * b) + 3)::double precision, b::double precision), sum(b::real), sum(b::double precision), sum(b::smallint), sum(b), sum(c_interval), sum(c_money) FROM public.pagg_tab_p2 WHERE ((c_serial >= 1)) AND ((c_serial <= 30)) + -> Foreign Scan + Output: (PARTIAL array_agg(pagg_tab_2.c_int4array)), (PARTIAL array_agg(pagg_tab_2.b)), (PARTIAL avg((pagg_tab_2.b)::smallint)), (PARTIAL avg(pagg_tab_2.b)), (PARTIAL avg((pagg_tab_2.b)::bigint)), (PARTIAL avg(pagg_tab_2.c_interval)), (PARTIAL avg((pagg_tab_2.b)::real)), (PARTIAL avg((pagg_tab_2.b)::double precision)), (PARTIAL avg((pagg_tab_2.b)::numeric)), (PARTIAL corr((pagg_tab_2.b)::double precision, ((pagg_tab_2.b * pagg_tab_2.b))::double precision)), (PARTIAL covar_pop((pagg_tab_2.b)::double precision, ((pagg_tab_2.b * pagg_tab_2.b))::double precision)), (PARTIAL covar_samp((pagg_tab_2.b)::double precision, ((pagg_tab_2.b * pagg_tab_2.b))::double precision)), (PARTIAL regr_avgx(((2 * pagg_tab_2.b))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL regr_avgy(((2 * pagg_tab_2.b))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL regr_intercept((((2 * pagg_tab_2.b) + 3))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL regr_r2(((pagg_tab_2.b * pagg_tab_2.b))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL regr_slope((((2 * pagg_tab_2.b) + 3))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL regr_sxx((((2 * pagg_tab_2.b) + 3))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL regr_sxy(((pagg_tab_2.b * pagg_tab_2.b))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL regr_syy((((2 * pagg_tab_2.b) + 3))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL stddev((pagg_tab_2.b)::real)), (PARTIAL stddev((pagg_tab_2.b)::double precision)), (PARTIAL stddev((pagg_tab_2.b)::smallint)), (PARTIAL stddev(pagg_tab_2.b)), (PARTIAL stddev((pagg_tab_2.b)::bigint)), (PARTIAL stddev((pagg_tab_2.b)::numeric)), (PARTIAL stddev_pop((pagg_tab_2.b)::real)), (PARTIAL stddev_pop((pagg_tab_2.b)::double precision)), (PARTIAL stddev_pop((pagg_tab_2.b)::smallint)), (PARTIAL stddev_pop(pagg_tab_2.b)), (PARTIAL stddev_pop((pagg_tab_2.b)::bigint)), (PARTIAL stddev_pop((pagg_tab_2.b)::numeric)), (PARTIAL stddev_samp((pagg_tab_2.b)::real)), (PARTIAL stddev_samp((pagg_tab_2.b)::double precision)), (PARTIAL stddev_samp((pagg_tab_2.b)::smallint)), (PARTIAL stddev_samp(pagg_tab_2.b)), (PARTIAL stddev_samp((pagg_tab_2.b)::bigint)), (PARTIAL stddev_samp((pagg_tab_2.b)::numeric)), (PARTIAL string_agg(pagg_tab_2.c_1c, ','::text)), (PARTIAL string_agg(pagg_tab_2.c_1b, '\x2c'::bytea)), (PARTIAL sum((pagg_tab_2.b)::bigint)), (PARTIAL sum((pagg_tab_2.b)::numeric)), (PARTIAL variance((pagg_tab_2.b)::real)), (PARTIAL variance((pagg_tab_2.b)::double precision)), (PARTIAL variance((pagg_tab_2.b)::smallint)), (PARTIAL variance(pagg_tab_2.b)), (PARTIAL variance((pagg_tab_2.b)::bigint)), (PARTIAL variance((pagg_tab_2.b)::numeric)), (PARTIAL var_pop((pagg_tab_2.b)::real)), (PARTIAL var_pop((pagg_tab_2.b)::double precision)), (PARTIAL var_pop((pagg_tab_2.b)::smallint)), (PARTIAL var_pop(pagg_tab_2.b)), (PARTIAL var_pop((pagg_tab_2.b)::bigint)), (PARTIAL var_pop((pagg_tab_2.b)::numeric)), (PARTIAL var_samp((pagg_tab_2.b)::real)), (PARTIAL var_samp((pagg_tab_2.b)::double precision)), (PARTIAL var_samp((pagg_tab_2.b)::smallint)), (PARTIAL var_samp(pagg_tab_2.b)), (PARTIAL var_samp((pagg_tab_2.b)::bigint)), (PARTIAL var_samp((pagg_tab_2.b)::numeric)), (PARTIAL any_value((pagg_tab_2.b * 0))), (PARTIAL bit_and(pagg_tab_2.c_bit)), (PARTIAL bit_and(pagg_tab_2.c_1or3int2)), (PARTIAL bit_and(pagg_tab_2.c_1or3int4)), (PARTIAL bit_and(pagg_tab_2.c_1or3int8)), (PARTIAL bit_or(pagg_tab_2.c_bit)), (PARTIAL bit_or(pagg_tab_2.c_1or3int2)), (PARTIAL bit_or(pagg_tab_2.c_1or3int4)), (PARTIAL bit_or(pagg_tab_2.c_1or3int8)), (PARTIAL bit_xor(pagg_tab_2.c_bit)), (PARTIAL bit_xor(pagg_tab_2.c_1or3int2)), (PARTIAL bit_xor(pagg_tab_2.c_1or3int4)), (PARTIAL bit_xor(pagg_tab_2.c_1or3int8)), (PARTIAL bool_and(pagg_tab_2.c_bool)), (PARTIAL bool_or(pagg_tab_2.c_bool)), (PARTIAL count(pagg_tab_2.b)), (PARTIAL count(*)), (PARTIAL every(pagg_tab_2.c_bool)), (PARTIAL max(pagg_tab_2.c_int4array)), (PARTIAL max(pagg_tab_2.c_enum)), (PARTIAL max((pagg_tab_2.c_1c)::character(1))), (PARTIAL max(('01-01-2000'::date + pagg_tab_2.b))), (PARTIAL max(('0.0.0.0'::inet + (pagg_tab_2.b)::bigint))), (PARTIAL max((pagg_tab_2.b)::real)), (PARTIAL max((pagg_tab_2.b)::double precision)), (PARTIAL max((pagg_tab_2.b)::smallint)), (PARTIAL max(pagg_tab_2.b)), (PARTIAL max((pagg_tab_2.b)::bigint)), (PARTIAL max(pagg_tab_2.c_interval)), (PARTIAL max(pagg_tab_2.c_money)), (PARTIAL max((pagg_tab_2.b)::numeric)), (PARTIAL max((pagg_tab_2.b)::oid)), (PARTIAL max(pagg_tab_2.c_pg_lsn)), (PARTIAL max(pagg_tab_2.c_tid)), (PARTIAL max(pagg_tab_2.c_1c)), (PARTIAL max(pagg_tab_2.c_time)), (PARTIAL max(pagg_tab_2.c_timetz)), (PARTIAL max(pagg_tab_2.c_timestamp)), (PARTIAL max(pagg_tab_2.c_timestamptz)), (PARTIAL max(pagg_tab_2.c_xid8)), (PARTIAL min(pagg_tab_2.c_int4array)), (PARTIAL min(pagg_tab_2.c_enum)), (PARTIAL min((pagg_tab_2.c_1c)::character(1))), (PARTIAL min(('01-01-2000'::date + pagg_tab_2.b))), (PARTIAL min(('0.0.0.0'::inet + (pagg_tab_2.b)::bigint))), (PARTIAL min((pagg_tab_2.b)::real)), (PARTIAL min((pagg_tab_2.b)::double precision)), (PARTIAL min((pagg_tab_2.b)::smallint)), (PARTIAL min(pagg_tab_2.b)), (PARTIAL min((pagg_tab_2.b)::bigint)), (PARTIAL min(pagg_tab_2.c_interval)), (PARTIAL min(pagg_tab_2.c_money)), (PARTIAL min((pagg_tab_2.b)::numeric)), (PARTIAL min((pagg_tab_2.b)::oid)), (PARTIAL min(pagg_tab_2.c_pg_lsn)), (PARTIAL min(pagg_tab_2.c_tid)), (PARTIAL min(pagg_tab_2.c_1c)), (PARTIAL min(pagg_tab_2.c_time)), (PARTIAL min(pagg_tab_2.c_timetz)), (PARTIAL min(pagg_tab_2.c_timestamp)), (PARTIAL min(pagg_tab_2.c_timestamptz)), (PARTIAL min(pagg_tab_2.c_xid8)), (PARTIAL range_intersect_agg(pagg_tab_2.c_int4range)), (PARTIAL range_intersect_agg(pagg_tab_2.c_int4multirange)), (PARTIAL regr_count((((2 * pagg_tab_2.b) + 3))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL sum((pagg_tab_2.b)::real)), (PARTIAL sum((pagg_tab_2.b)::double precision)), (PARTIAL sum((pagg_tab_2.b)::smallint)), (PARTIAL sum(pagg_tab_2.b)), (PARTIAL sum(pagg_tab_2.c_interval)), (PARTIAL sum(pagg_tab_2.c_money)) + Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2) + Remote SQL: SELECT array_agg_p_anyarray(c_int4array), array_agg_p_anynonarray(b), avg_p_int2(b::smallint), avg_p_int4(b), avg_p_int8(b::bigint), avg_p_interval(c_interval), avg_p_float4(b::real), avg_p_float8(b::double precision), avg_p_numeric(b::numeric), corr_p(b::double precision, (b * b)::double precision), covar_pop_p(b::double precision, (b * b)::double precision), covar_samp_p(b::double precision, (b * b)::double precision), regr_avgx_p((2 * b)::double precision, b::double precision), regr_avgy_p((2 * b)::double precision, b::double precision), regr_intercept_p(((2 * b) + 3)::double precision, b::double precision), regr_r2_p((b * b)::double precision, b::double precision), regr_slope_p(((2 * b) + 3)::double precision, b::double precision), regr_sxx_p(((2 * b) + 3)::double precision, b::double precision), regr_sxy_p((b * b)::double precision, b::double precision), regr_syy_p(((2 * b) + 3)::double precision, b::double precision), stddev_p_float4(b::real), stddev_p_float8(b::double precision), stddev_p_int2(b::smallint), stddev_p_int4(b), stddev_p_int8(b::bigint), stddev_p_numeric(b::numeric), stddev_pop_p_float4(b::real), stddev_pop_p_float8(b::double precision), stddev_pop_p_int2(b::smallint), stddev_pop_p_int4(b), stddev_pop_p_int8(b::bigint), stddev_pop_p_numeric(b::numeric), stddev_samp_p_float4(b::real), stddev_samp_p_float8(b::double precision), stddev_samp_p_int2(b::smallint), stddev_samp_p_int4(b), stddev_samp_p_int8(b::bigint), stddev_samp_p_numeric(b::numeric), string_agg_p_text_text(c_1c, ','::text), string_agg_p_bytea_bytea(c_1b, E'\\x2c'::bytea), sum_p_int8(b::bigint), sum_p_numeric(b::numeric), variance_p_float4(b::real), variance_p_float8(b::double precision), variance_p_int2(b::smallint), variance_p_int4(b), variance_p_int8(b::bigint), variance_p_numeric(b::numeric), var_pop_p_float4(b::real), var_pop_p_float8(b::double precision), var_pop_p_int2(b::smallint), var_pop_p_int4(b), var_pop_p_int8(b::bigint), var_pop_p_numeric(b::numeric), var_samp_p_float4(b::real), var_samp_p_float8(b::double precision), var_samp_p_int2(b::smallint), var_samp_p_int4(b), var_samp_p_int8(b::bigint), var_samp_p_numeric(b::numeric), any_value((b * 0)), bit_and(c_bit), bit_and(c_1or3int2), bit_and(c_1or3int4), bit_and(c_1or3int8), bit_or(c_bit), bit_or(c_1or3int2), bit_or(c_1or3int4), bit_or(c_1or3int8), bit_xor(c_bit), bit_xor(c_1or3int2), bit_xor(c_1or3int4), bit_xor(c_1or3int8), bool_and(c_bool), bool_or(c_bool), count(b), count(*), every(c_bool), max(c_int4array), max(c_enum), max(c_1c::character(1)), max(('01-01-2000'::date + b)), max(('0.0.0.0'::inet + b)), max(b::real), max(b::double precision), max(b::smallint), max(b), max(b::bigint), max(c_interval), max(c_money), max(b::numeric), max(b::oid), max(c_pg_lsn), max(c_tid), max(c_1c), max(c_time), max(c_timetz), max(c_timestamp), max(c_timestamptz), max(c_xid8), min(c_int4array), min(c_enum), min(c_1c::character(1)), min(('01-01-2000'::date + b)), min(('0.0.0.0'::inet + b)), min(b::real), min(b::double precision), min(b::smallint), min(b), min(b::bigint), min(c_interval), min(c_money), min(b::numeric), min(b::oid), min(c_pg_lsn), min(c_tid), min(c_1c), min(c_time), min(c_timetz), min(c_timestamp), min(c_timestamptz), min(c_xid8), range_intersect_agg(c_int4range), range_intersect_agg(c_int4multirange), regr_count(((2 * b) + 3)::double precision, b::double precision), sum(b::real), sum(b::double precision), sum(b::smallint), sum(b), sum(c_interval), sum(c_money) FROM public.pagg_tab_p3 WHERE ((c_serial >= 1)) AND ((c_serial <= 30)) +(15 rows) + +SELECT /* aggregate function <> aggpartialfunc */ + array_agg(c_int4array), array_agg(b), + avg(b::int2), avg(b::int4), avg(b::int8), avg(c_interval), + avg(b::float4), avg(b::float8), + corr(b::float8, (b * b)::float8), + covar_pop(b::float8, (b * b)::float8), + covar_samp(b::float8, (b * b)::float8), + regr_avgx((2 * b)::float8, b::float8), + regr_avgy((2 * b)::float8, b::float8), + regr_intercept((2 * b + 3)::float8, b::float8), + regr_r2((b * b)::float8, b::float8), + regr_slope((2 * b + 3)::float8, b::float8), + regr_sxx((2 * b + 3)::float8, b::float8), + regr_sxy((b * b)::float8, b::float8), + regr_syy((2 * b + 3)::float8, b::float8), + stddev(b::float4), stddev(b::float8), stddev(b::int2), stddev(b::int4), stddev(b::int8), stddev(b::numeric), + stddev_pop(b::float4), stddev_pop(b::float8), stddev_pop(b::int2), stddev_pop(b::int4), stddev_pop(b::int8), stddev_pop(b::numeric), + stddev_samp(b::float4), stddev_samp(b::float8), stddev_samp(b::int2), stddev_samp(b::int4), stddev_samp(b::int8), stddev_samp(b::numeric), + string_agg(c_1c, ','), string_agg(c_1b, ','), + sum(b::int8), sum(b::numeric), + variance(b::float4), variance(b::float8), variance(b::int2), variance(b::int4), variance(b::int8), variance(b::numeric), + var_pop(b::float4), var_pop(b::float8), var_pop(b::int2), var_pop(b::int4), var_pop(b::int8), var_pop(b::numeric), + var_samp(b::float4), var_samp(b::float8), var_samp(b::int2), var_samp(b::int4), var_samp(b::int8), var_samp(b::numeric), + /* aggregate function = aggpartialfunc */ + any_value(b * 0), + bit_and(c_bit), bit_and(c_1or3int2), bit_and(c_1or3int4), bit_and(c_1or3int8), + bit_or(c_bit), bit_or(c_1or3int2), bit_or(c_1or3int4), bit_or(c_1or3int8), + bit_xor(c_bit), bit_xor(c_1or3int2), bit_xor(c_1or3int4), bit_xor(c_1or3int8), + bool_and(c_bool), + bool_or(c_bool), + count(b), count(*), + every(c_bool), + max(c_int4array), max(c_enum), max(c_1c::char(1)), max('2000-01-01'::date + b), max('0.0.0.0'::inet + b), max(b::float4), max(b::float8), max(b::int2), max(b::int4), max(b::int8), max(c_interval), max(c_money), max(b::numeric), max(b::oid), max(c_pg_lsn), max(c_tid), max(c_1c), max(c_time), max(c_timetz), max(c_timestamp), max(c_timestamptz), max(c_xid8), + min(c_int4array), min(c_enum), min(c_1c::char(1)), min('2000-01-01'::date + b), min('0.0.0.0'::inet + b), min(b::float4), min(b::float8), min(b::int2), min(b::int4), min(b::int8), min(c_interval), min(c_money), min(b::numeric), min(b::oid), min(c_pg_lsn), min(c_tid), min(c_1c), min(c_time), min(c_timetz), min(c_timestamp), min(c_timestamptz), min(c_xid8), + range_intersect_agg(c_int4range), range_intersect_agg(c_int4multirange), + regr_count((2 * b + 3)::float8, b::float8), + sum(b::float4), sum(b::float8), sum(b::int2), sum(b::int4), sum(c_interval), sum(c_money) + FROM pagg_tab WHERE c_serial between 1 and 30; + array_agg | array_agg | avg | avg | avg | avg | avg | avg | corr | covar_pop | covar_samp | regr_avgx | regr_avgy | regr_intercept | regr_r2 | regr_slope | regr_sxx | regr_sxy | regr_syy | stddev | stddev | stddev | stddev | stddev | stddev | stddev_pop | stddev_pop | stddev_pop | stddev_pop | stddev_pop | stddev_pop | stddev_samp | stddev_samp | stddev_samp | stddev_samp | stddev_samp | stddev_samp | string_agg | string_agg | sum | sum | variance | variance | variance | variance | variance | variance | var_pop | var_pop | var_pop | var_pop | var_pop | var_pop | var_samp | var_samp | var_samp | var_samp | var_samp | var_samp | any_value | bit_and | bit_and | bit_and | bit_and | bit_or | bit_or | bit_or | bit_or | bit_xor | bit_xor | bit_xor | bit_xor | bool_and | bool_or | count | count | every | max | max | max | max | max | max | max | max | max | max | max | max | max | max | max | max | max | max | max | max | max | max | min | min | min | min | min | min | min | min | min | min | min | min | min | min | min | min | min | min | min | min | min | min | range_intersect_agg | range_intersect_agg | regr_count | sum | sum | sum | sum | sum | sum +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------+---------------------+---------------------+---------------------+------------+------+------+--------------------+--------------------+------------+-----------+-----------+----------------+--------------------+------------+----------+----------+----------+-------------------+-------------------+--------------------+--------------------+--------------------+--------------------+------------------+------------------+--------------------+--------------------+--------------------+--------------------+-------------------+-------------------+--------------------+--------------------+--------------------+--------------------+-------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+-----+-----+----------+----------+---------------------+---------------------+---------------------+---------------------+-------------------+-------------------+---------------------+---------------------+---------------------+---------------------+----------+----------+---------------------+---------------------+---------------------+---------------------+-----------+---------+---------+---------+---------+--------+--------+--------+--------+---------+---------+---------+---------+----------+---------+-------+-------+-------+-------+-------+-----+------------+----------+-----+-----+-----+-----+-----+---------+-------+-----+-----+------+--------+-----+----------+-------------+--------------------------+------------------------------+-----+-------+-----+-----+------------+---------+-----+-----+-----+-----+-----+-----+-------+-----+-----+-----+-------+-----+----------+-------------+--------------------------+------------------------------+-----+---------------------+---------------------+------------+-----+-----+-----+-----+-----------+-------- + {{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0}} | {1,2,3,4,5,6,7,8,9,30,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29} | 15.5000000000000000 | 15.5000000000000000 | 15.5000000000000000 | @ 0.5 secs | 15.5 | 15.5 | 0.9702989135892578 | 2322.4166666666665 | 2402.5 | 15.5 | 31 | 3 | 0.9414799817124941 | 2 | 2247.5 | 69672.5 | 8990 | 8.803408430829505 | 8.803408430829505 | 8.8034084308295046 | 8.8034084308295046 | 8.8034084308295046 | 8.8034084308295046 | 8.65544144839919 | 8.65544144839919 | 8.6554414483991899 | 8.6554414483991899 | 8.6554414483991899 | 8.6554414483991899 | 8.803408430829505 | 8.803408430829505 | 8.8034084308295046 | 8.8034084308295046 | 8.8034084308295046 | 8.8034084308295046 | 0,1,2,3,4,5,6,7,8,9,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8 | \x302c312c322c332c342c352c362c372c382c392c392c302c312c322c332c342c352c362c372c382c392c302c312c322c332c342c352c362c372c38 | 465 | 465 | 77.5 | 77.5 | 77.5000000000000000 | 77.5000000000000000 | 77.5000000000000000 | 77.5000000000000000 | 74.91666666666667 | 74.91666666666667 | 74.9166666666666667 | 74.9166666666666667 | 74.9166666666666667 | 74.9166666666666667 | 77.5 | 77.5 | 77.5000000000000000 | 77.5000000000000000 | 77.5000000000000000 | 77.5000000000000000 | 0 | 01 | 1 | 1 | 1 | 11 | 3 | 3 | 3 | 10 | 2 | 2 | 2 | f | t | 30 | 30 | f | {1,0} | happy | 9 | 01-31-2000 | 0.0.0.30 | 30 | 30 | 30 | 30 | 30 | @ 1 sec | $1.00 | 30 | 30 | 0/30 | (0,30) | 9 | 00:00:30 | 00:00:30-07 | Sat Jan 01 00:00:30 2000 | Sat Jan 01 00:00:30 2000 PST | 9 | {0,0} | sad | 0 | 01-02-2000 | 0.0.0.1 | 1 | 1 | 1 | 1 | 1 | @ 0 | $0.00 | 1 | 1 | 0/1 | (0,1) | 0 | 00:00:01 | 00:00:01-07 | Sat Jan 01 00:00:01 2000 | Sat Jan 01 00:00:01 2000 PST | 0 | [0,1) | {[0,1),[100,101)} | 30 | 465 | 465 | 465 | 465 | @ 15 secs | $15.00 +(1 row) + +-- Tests for backward compatibility +ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false'); +EXPLAIN (VERBOSE, COSTS OFF) +SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + Sort + Output: pagg_tab.b, (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)) + Sort Key: pagg_tab.b + -> Finalize HashAggregate + Output: pagg_tab.b, avg(pagg_tab.a), max(pagg_tab.a), count(*) + Group Key: pagg_tab.b + -> Append + -> Foreign Scan + Output: pagg_tab.b, (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)) + Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab) + Remote SQL: SELECT b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1 + -> Foreign Scan + Output: pagg_tab_1.b, (PARTIAL avg(pagg_tab_1.a)), (PARTIAL max(pagg_tab_1.a)), (PARTIAL count(*)) + Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1) + Remote SQL: SELECT b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1 + -> Foreign Scan + Output: pagg_tab_2.b, (PARTIAL avg(pagg_tab_2.a)), (PARTIAL max(pagg_tab_2.a)), (PARTIAL count(*)) + Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2) + Remote SQL: SELECT b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1 +(19 rows) + +SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1; + b | avg | max | count +----+---------------------+-----+------- + 0 | 10.0000000000000000 | 20 | 60 + 1 | 11.0000000000000000 | 21 | 60 + 2 | 12.0000000000000000 | 22 | 60 + 3 | 13.0000000000000000 | 23 | 60 + 4 | 14.0000000000000000 | 24 | 60 + 5 | 15.0000000000000000 | 25 | 60 + 6 | 16.0000000000000000 | 26 | 60 + 7 | 17.0000000000000000 | 27 | 60 + 8 | 18.0000000000000000 | 28 | 60 + 9 | 19.0000000000000000 | 29 | 60 + 10 | 10.0000000000000000 | 20 | 60 + 11 | 11.0000000000000000 | 21 | 60 + 12 | 12.0000000000000000 | 22 | 60 + 13 | 13.0000000000000000 | 23 | 60 + 14 | 14.0000000000000000 | 24 | 60 + 15 | 15.0000000000000000 | 25 | 60 + 16 | 16.0000000000000000 | 26 | 60 + 17 | 17.0000000000000000 | 27 | 60 + 18 | 18.0000000000000000 | 28 | 60 + 19 | 19.0000000000000000 | 29 | 60 + 20 | 10.0000000000000000 | 20 | 60 + 21 | 11.0000000000000000 | 21 | 60 + 22 | 12.0000000000000000 | 22 | 60 + 23 | 13.0000000000000000 | 23 | 60 + 24 | 14.0000000000000000 | 24 | 60 + 25 | 15.0000000000000000 | 25 | 60 + 26 | 16.0000000000000000 | 26 | 60 + 27 | 17.0000000000000000 | 27 | 60 + 28 | 18.0000000000000000 | 28 | 60 + 29 | 19.0000000000000000 | 29 | 60 + 30 | 10.0000000000000000 | 20 | 60 + 31 | 11.0000000000000000 | 21 | 60 + 32 | 12.0000000000000000 | 22 | 60 + 33 | 13.0000000000000000 | 23 | 60 + 34 | 14.0000000000000000 | 24 | 60 + 35 | 15.0000000000000000 | 25 | 60 + 36 | 16.0000000000000000 | 26 | 60 + 37 | 17.0000000000000000 | 27 | 60 + 38 | 18.0000000000000000 | 28 | 60 + 39 | 19.0000000000000000 | 29 | 60 + 40 | 10.0000000000000000 | 20 | 60 + 41 | 11.0000000000000000 | 21 | 60 + 42 | 12.0000000000000000 | 22 | 60 + 43 | 13.0000000000000000 | 23 | 60 + 44 | 14.0000000000000000 | 24 | 60 + 45 | 15.0000000000000000 | 25 | 60 + 46 | 16.0000000000000000 | 26 | 60 + 47 | 17.0000000000000000 | 27 | 60 + 48 | 18.0000000000000000 | 28 | 60 + 49 | 19.0000000000000000 | 29 | 60 +(50 rows) + +ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true'); +EXPLAIN (VERBOSE, COSTS OFF) +SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + Sort + Output: pagg_tab.b, (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)) + Sort Key: pagg_tab.b + -> Finalize HashAggregate + Output: pagg_tab.b, avg(pagg_tab.a), max(pagg_tab.a), count(*) + Group Key: pagg_tab.b + -> Append + -> Foreign Scan + Output: pagg_tab.b, (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)) + Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab) + Remote SQL: SELECT b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1 + -> Foreign Scan + Output: pagg_tab_1.b, (PARTIAL avg(pagg_tab_1.a)), (PARTIAL max(pagg_tab_1.a)), (PARTIAL count(*)) + Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1) + Remote SQL: SELECT b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1 + -> Foreign Scan + Output: pagg_tab_2.b, (PARTIAL avg(pagg_tab_2.a)), (PARTIAL max(pagg_tab_2.a)), (PARTIAL count(*)) + Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2) + Remote SQL: SELECT b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1 +(19 rows) + +SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1; + b | avg | max | count +----+---------------------+-----+------- + 0 | 10.0000000000000000 | 20 | 60 + 1 | 11.0000000000000000 | 21 | 60 + 2 | 12.0000000000000000 | 22 | 60 + 3 | 13.0000000000000000 | 23 | 60 + 4 | 14.0000000000000000 | 24 | 60 + 5 | 15.0000000000000000 | 25 | 60 + 6 | 16.0000000000000000 | 26 | 60 + 7 | 17.0000000000000000 | 27 | 60 + 8 | 18.0000000000000000 | 28 | 60 + 9 | 19.0000000000000000 | 29 | 60 + 10 | 10.0000000000000000 | 20 | 60 + 11 | 11.0000000000000000 | 21 | 60 + 12 | 12.0000000000000000 | 22 | 60 + 13 | 13.0000000000000000 | 23 | 60 + 14 | 14.0000000000000000 | 24 | 60 + 15 | 15.0000000000000000 | 25 | 60 + 16 | 16.0000000000000000 | 26 | 60 + 17 | 17.0000000000000000 | 27 | 60 + 18 | 18.0000000000000000 | 28 | 60 + 19 | 19.0000000000000000 | 29 | 60 + 20 | 10.0000000000000000 | 20 | 60 + 21 | 11.0000000000000000 | 21 | 60 + 22 | 12.0000000000000000 | 22 | 60 + 23 | 13.0000000000000000 | 23 | 60 + 24 | 14.0000000000000000 | 24 | 60 + 25 | 15.0000000000000000 | 25 | 60 + 26 | 16.0000000000000000 | 26 | 60 + 27 | 17.0000000000000000 | 27 | 60 + 28 | 18.0000000000000000 | 28 | 60 + 29 | 19.0000000000000000 | 29 | 60 + 30 | 10.0000000000000000 | 20 | 60 + 31 | 11.0000000000000000 | 21 | 60 + 32 | 12.0000000000000000 | 22 | 60 + 33 | 13.0000000000000000 | 23 | 60 + 34 | 14.0000000000000000 | 24 | 60 + 35 | 15.0000000000000000 | 25 | 60 + 36 | 16.0000000000000000 | 26 | 60 + 37 | 17.0000000000000000 | 27 | 60 + 38 | 18.0000000000000000 | 28 | 60 + 39 | 19.0000000000000000 | 29 | 60 + 40 | 10.0000000000000000 | 20 | 60 + 41 | 11.0000000000000000 | 21 | 60 + 42 | 12.0000000000000000 | 22 | 60 + 43 | 13.0000000000000000 | 23 | 60 + 44 | 14.0000000000000000 | 24 | 60 + 45 | 15.0000000000000000 | 25 | 60 + 46 | 16.0000000000000000 | 26 | 60 + 47 | 17.0000000000000000 | 27 | 60 + 48 | 18.0000000000000000 | 28 | 60 + 49 | 19.0000000000000000 | 29 | 60 +(50 rows) + +ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0'); +SET enable_partitionwise_join=on; +EXPLAIN (VERBOSE, COSTS OFF) +SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + Finalize Aggregate + Output: avg(t1.b), avg((t1.b)::bigint) + -> Append + -> Foreign Scan + Output: (PARTIAL avg(t1.b)), (PARTIAL avg((t1.b)::bigint)) + Relations: Aggregate on ((public.fpagg_tab_p1 t1) INNER JOIN (public.fpagg_tab_p1 t2)) + Remote SQL: SELECT avg_p_int4(r4.b), avg_p_int8(r4.b::bigint) FROM (public.pagg_tab_p1 r4 INNER JOIN public.pagg_tab_p1 r7 ON (((r4.a = r7.a)))) + -> Foreign Scan + Output: (PARTIAL avg(t1_1.b)), (PARTIAL avg((t1_1.b)::bigint)) + Relations: Aggregate on ((public.fpagg_tab_p2 t1_1) INNER JOIN (public.fpagg_tab_p2 t2_1)) + Remote SQL: SELECT avg_p_int4(r5.b), avg_p_int8(r5.b::bigint) FROM (public.pagg_tab_p2 r5 INNER JOIN public.pagg_tab_p2 r8 ON (((r5.a = r8.a)))) + -> Foreign Scan + Output: (PARTIAL avg(t1_2.b)), (PARTIAL avg((t1_2.b)::bigint)) + Relations: Aggregate on ((public.fpagg_tab_p3 t1_2) INNER JOIN (public.fpagg_tab_p3 t2_2)) + Remote SQL: SELECT avg_p_int4(r6.b), avg_p_int8(r6.b::bigint) FROM (public.pagg_tab_p3 r6 INNER JOIN public.pagg_tab_p3 r9 ON (((r6.a = r9.a)))) (15 rows) +SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a); + avg | avg +---------------------+--------------------- + 24.5000000000000000 | 24.5000000000000000 +(1 row) + +RESET enable_partitionwise_join; +ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1'); +ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support); +-- List of aggregate functions such that the aggregate function supports partial aggregate +-- and aggpartialfn field of the aggregate function is invalid +SELECT aggfnoid::text + FROM pg_aggregate a JOIN pg_type b ON a.aggtranstype = b.oid + WHERE (aggcombinefn::text <> '-') AND (aggpartialfn::text = '-') + ORDER BY 1; + aggfnoid +---------- +(0 rows) + +-- List of aggregate functions which have incorrect aggpartialfunc +SELECT a.aggfnoid + FROM pg_aggregate a JOIN pg_type b ON a.aggtranstype = b.oid + JOIN pg_aggregate c on a.aggpartialfn = c.aggfnoid + WHERE (a.aggcombinefn::text <> '-') + AND (a.aggpartialfn::text <> '-') + AND (a.aggtransfn <> c.aggtransfn + OR (c.aggcombinefn::text <> '-' + AND a.aggcombinefn <> c.aggcombinefn) + OR (a.agginitval IS NOT NULL + AND c.agginitval IS NOT NULL + AND a.agginitval <> c.agginitval) + OR (b.typname = 'internal' + AND c.aggserialfn::text <> '-' + AND a.aggserialfn <> c.aggserialfn) + OR (b.typname = 'internal' + AND c.aggdeserialfn::text <> '-' + AND a.aggdeserialfn <> c.aggdeserialfn) + OR (b.typname = 'internal' + AND a.aggserialfn <> c.aggfinalfn) + OR (b.typname <> 'internal' + AND c.aggfinalfn::text <> '-')) + ORDER BY 1; + aggfnoid +---------- +(0 rows) + +-- It is unsafe to push down partial aggregates which contain DISTINCT clauses +EXPLAIN (VERBOSE, COSTS OFF) +SELECT max(a), count(distinct b) FROM pagg_tab; + QUERY PLAN +----------------------------------------------------------------------------------------- + Aggregate + Output: max(pagg_tab.a), count(DISTINCT pagg_tab.b) + -> Merge Append + Sort Key: pagg_tab.b + -> Foreign Scan on public.fpagg_tab_p1 pagg_tab_1 + Output: pagg_tab_1.a, pagg_tab_1.b + Remote SQL: SELECT a, b FROM public.pagg_tab_p1 ORDER BY b ASC NULLS LAST + -> Foreign Scan on public.fpagg_tab_p2 pagg_tab_2 + Output: pagg_tab_2.a, pagg_tab_2.b + Remote SQL: SELECT a, b FROM public.pagg_tab_p2 ORDER BY b ASC NULLS LAST + -> Foreign Scan on public.fpagg_tab_p3 pagg_tab_3 + Output: pagg_tab_3.a, pagg_tab_3.b + Remote SQL: SELECT a, b FROM public.pagg_tab_p3 ORDER BY b ASC NULLS LAST +(13 rows) + +SELECT max(a), count(distinct b) FROM pagg_tab; + max | count +-----+------- + 29 | 50 +(1 row) + +-- It is unsafe to push down partial aggregates which contain ORDER BY clauses +EXPLAIN (VERBOSE, COSTS OFF) SELECT array_agg(b order by b) FROM pagg_tab WHERE c_serial between 1 and 30; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------- + Aggregate + Output: array_agg(pagg_tab.b ORDER BY pagg_tab.b) + -> Sort + Output: pagg_tab.b + Sort Key: pagg_tab.b + -> Append + -> Foreign Scan on public.fpagg_tab_p1 pagg_tab_1 + Output: pagg_tab_1.b + Remote SQL: SELECT b FROM public.pagg_tab_p1 WHERE ((c_serial >= 1)) AND ((c_serial <= 30)) + -> Foreign Scan on public.fpagg_tab_p2 pagg_tab_2 + Output: pagg_tab_2.b + Remote SQL: SELECT b FROM public.pagg_tab_p2 WHERE ((c_serial >= 1)) AND ((c_serial <= 30)) + -> Foreign Scan on public.fpagg_tab_p3 pagg_tab_3 + Output: pagg_tab_3.b + Remote SQL: SELECT b FROM public.pagg_tab_p3 WHERE ((c_serial >= 1)) AND ((c_serial <= 30)) +(15 rows) + +SELECT array_agg(b order by b) FROM pagg_tab WHERE c_serial between 1 and 30; + array_agg +------------------------------------------------------------------------------------ + {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30} +(1 row) + +-- Create user-defined aggregates whose stype is internal +CREATE SCHEMA postgres_fdw_test; +CREATE AGGREGATE postgres_fdw_test.postgres_fdw_sum_p_int8(int8) ( + SFUNC = int8_avg_accum, + STYPE = internal, + COMBINEFUNC = int8_avg_combine, + FINALFUNC = int8_avg_serialize, + SERIALFUNC = int8_avg_serialize, + DESERIALFUNC = int8_avg_deserialize, + AGGPARTIALFUNC = postgres_fdw_test.postgres_fdw_sum_p_int8 +); +CREATE AGGREGATE postgres_fdw_sum(int8) ( + SFUNC = int8_avg_accum, + STYPE = internal, + COMBINEFUNC = int8_avg_combine, + FINALFUNC = numeric_poly_sum, + SERIALFUNC = int8_avg_serialize, + DESERIALFUNC = int8_avg_deserialize, + AGGPARTIALFUNC = postgres_fdw_test.postgres_fdw_sum_p_int8 +); +-- Create user-defined aggregates whose stype is not internal +CREATE AGGREGATE postgres_fdw_avg_p_int4(int4) ( + SFUNC = int4_avg_accum, + STYPE = _int8, + COMBINEFUNC = int4_avg_combine, + INITCOND = '{0,0}', + AGGPARTIALFUNC = postgres_fdw_avg_p_int4 +); +CREATE AGGREGATE postgres_fdw_avg(int4) ( + SFUNC = int4_avg_accum, + STYPE = _int8, + COMBINEFUNC = int4_avg_combine, + FINALFUNC = int8_avg, + INITCOND = '{0,0}', + AGGPARTIALFUNC = postgres_fdw_avg_p_int4 +); +CREATE AGGREGATE postgres_fdw_sum_noaggpartialfunc(int8) ( + SFUNC = int8_avg_accum, + STYPE = internal, + COMBINEFUNC = int8_avg_combine, + FINALFUNC = numeric_poly_sum, + SERIALFUNC = int8_avg_serialize, + DESERIALFUNC = int8_avg_deserialize +); +-- It's safe to push down partial aggregates for user-defined aggregate +-- when it belongs to an extension that is listed in the extension setting +-- and when its aggpartialfunc belongs to an extension that is listed +-- in the extension setting. +ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_sum(int8); +ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_test.postgres_fdw_sum_p_int8(int8); +ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_avg(int4); +ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_avg_p_int4(int4); +EXPLAIN (VERBOSE, COSTS OFF) +SELECT postgres_fdw_sum(b::int8) FROM pagg_tab; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- + Finalize Aggregate + Output: postgres_fdw_sum((pagg_tab.b)::bigint) + -> Append + -> Foreign Scan + Output: (PARTIAL postgres_fdw_sum((pagg_tab.b)::bigint)) + Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab) + Remote SQL: SELECT postgres_fdw_test.postgres_fdw_sum_p_int8(b::bigint) FROM public.pagg_tab_p1 + -> Foreign Scan + Output: (PARTIAL postgres_fdw_sum((pagg_tab_1.b)::bigint)) + Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1) + Remote SQL: SELECT postgres_fdw_test.postgres_fdw_sum_p_int8(b::bigint) FROM public.pagg_tab_p2 + -> Foreign Scan + Output: (PARTIAL postgres_fdw_sum((pagg_tab_2.b)::bigint)) + Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2) + Remote SQL: SELECT postgres_fdw_test.postgres_fdw_sum_p_int8(b::bigint) FROM public.pagg_tab_p3 +(15 rows) + +SELECT postgres_fdw_sum(b::int8) FROM pagg_tab; + postgres_fdw_sum +------------------ + 73500 +(1 row) + +-- It is unsafe to push down partial aggregates for user-defined aggregates +-- when they have no aggpartialfunc. +EXPLAIN (VERBOSE, COSTS OFF) +SELECT postgres_fdw_sum_noaggpartialfunc(b::int8) FROM pagg_tab; + QUERY PLAN +----------------------------------------------------------------------------------------- + Finalize Aggregate + Output: postgres_fdw_sum_noaggpartialfunc((pagg_tab.b)::bigint) + -> Append + -> Partial Aggregate + Output: PARTIAL postgres_fdw_sum_noaggpartialfunc((pagg_tab.b)::bigint) + -> Foreign Scan on public.fpagg_tab_p1 pagg_tab + Output: pagg_tab.b + Remote SQL: SELECT b FROM public.pagg_tab_p1 + -> Partial Aggregate + Output: PARTIAL postgres_fdw_sum_noaggpartialfunc((pagg_tab_1.b)::bigint) + -> Foreign Scan on public.fpagg_tab_p2 pagg_tab_1 + Output: pagg_tab_1.b + Remote SQL: SELECT b FROM public.pagg_tab_p2 + -> Partial Aggregate + Output: PARTIAL postgres_fdw_sum_noaggpartialfunc((pagg_tab_2.b)::bigint) + -> Foreign Scan on public.fpagg_tab_p3 pagg_tab_2 + Output: pagg_tab_2.b + Remote SQL: SELECT b FROM public.pagg_tab_p3 +(18 rows) + +SELECT postgres_fdw_sum_noaggpartialfunc(b::int8) FROM pagg_tab; + postgres_fdw_sum_noaggpartialfunc +----------------------------------- + 73500 +(1 row) + +ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_test.postgres_fdw_sum_p_int8(int8); +-- Reconnect for flushing the shippability cache +\c - +SET enable_partitionwise_aggregate TO true; +-- It is unsafe to push down partial aggregates for user-defined aggregates +-- when itself is aggpartialfunc and when it belongs to an extension +-- that's listed in the extension setting and when stype is internal. +EXPLAIN (VERBOSE, COSTS OFF) +SELECT postgres_fdw_test.postgres_fdw_sum_p_int8(b::int8) FROM pagg_tab; + QUERY PLAN +------------------------------------------------------------------------------------------------- + Finalize Aggregate + Output: postgres_fdw_test.postgres_fdw_sum_p_int8((pagg_tab.b)::bigint) + -> Append + -> Partial Aggregate + Output: PARTIAL postgres_fdw_test.postgres_fdw_sum_p_int8((pagg_tab.b)::bigint) + -> Foreign Scan on public.fpagg_tab_p1 pagg_tab + Output: pagg_tab.b + Remote SQL: SELECT b FROM public.pagg_tab_p1 + -> Partial Aggregate + Output: PARTIAL postgres_fdw_test.postgres_fdw_sum_p_int8((pagg_tab_1.b)::bigint) + -> Foreign Scan on public.fpagg_tab_p2 pagg_tab_1 + Output: pagg_tab_1.b + Remote SQL: SELECT b FROM public.pagg_tab_p2 + -> Partial Aggregate + Output: PARTIAL postgres_fdw_test.postgres_fdw_sum_p_int8((pagg_tab_2.b)::bigint) + -> Foreign Scan on public.fpagg_tab_p3 pagg_tab_2 + Output: pagg_tab_2.b + Remote SQL: SELECT b FROM public.pagg_tab_p3 +(18 rows) + +SELECT postgres_fdw_test.postgres_fdw_sum_p_int8(b::int8) FROM pagg_tab; + postgres_fdw_sum_p_int8 +------------------------------------------------------------ + \x0000000000000bb80000000200000001000000000000000000070dac +(1 row) + +ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_sum(int8); +ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_test.postgres_fdw_sum_p_int8(int8); +-- Reconnect for flushing the shippability cache +\c - +SET enable_partitionwise_aggregate TO true; +-- It is unsafe to push down partial aggregates for user-defined aggregates +-- when it doesn't belong to an extension that is listed in the extension setting +-- and when its aggpartialfunc belongs to an extension that is listed +-- in the extension setting. +EXPLAIN (VERBOSE, COSTS OFF) +SELECT postgres_fdw_sum(b::int8) FROM pagg_tab; + QUERY PLAN +------------------------------------------------------------------------ + Finalize Aggregate + Output: postgres_fdw_sum((pagg_tab.b)::bigint) + -> Append + -> Partial Aggregate + Output: PARTIAL postgres_fdw_sum((pagg_tab.b)::bigint) + -> Foreign Scan on public.fpagg_tab_p1 pagg_tab + Output: pagg_tab.b + Remote SQL: SELECT b FROM public.pagg_tab_p1 + -> Partial Aggregate + Output: PARTIAL postgres_fdw_sum((pagg_tab_1.b)::bigint) + -> Foreign Scan on public.fpagg_tab_p2 pagg_tab_1 + Output: pagg_tab_1.b + Remote SQL: SELECT b FROM public.pagg_tab_p2 + -> Partial Aggregate + Output: PARTIAL postgres_fdw_sum((pagg_tab_2.b)::bigint) + -> Foreign Scan on public.fpagg_tab_p3 pagg_tab_2 + Output: pagg_tab_2.b + Remote SQL: SELECT b FROM public.pagg_tab_p3 +(18 rows) + +SELECT postgres_fdw_sum(b::int8) FROM pagg_tab; + postgres_fdw_sum +------------------ + 73500 +(1 row) + +-- It's safe to push down partial aggregates for user-defined aggregate +-- whose stype is not internal +-- when it belongs to an extension that is listed in the extension setting +-- and when its aggpartialfunc belongs to an extension that is listed +-- in the extension setting. +EXPLAIN (VERBOSE, COSTS OFF) +SELECT postgres_fdw_avg(b) FROM pagg_tab; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Finalize Aggregate + Output: postgres_fdw_avg(pagg_tab.b) + -> Append + -> Foreign Scan + Output: (PARTIAL postgres_fdw_avg(pagg_tab.b)) + Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab) + Remote SQL: SELECT public.postgres_fdw_avg_p_int4(b) FROM public.pagg_tab_p1 + -> Foreign Scan + Output: (PARTIAL postgres_fdw_avg(pagg_tab_1.b)) + Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1) + Remote SQL: SELECT public.postgres_fdw_avg_p_int4(b) FROM public.pagg_tab_p2 + -> Foreign Scan + Output: (PARTIAL postgres_fdw_avg(pagg_tab_2.b)) + Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2) + Remote SQL: SELECT public.postgres_fdw_avg_p_int4(b) FROM public.pagg_tab_p3 +(15 rows) + +SELECT postgres_fdw_avg(b) FROM pagg_tab; + postgres_fdw_avg +--------------------- + 24.5000000000000000 +(1 row) + +-- It's safe to push down partial aggregates for user-defined aggregate +-- when itself is aggpartialfunc and when it belongs to an extension +-- that's listed in the extension setting and when stype is not internal. +EXPLAIN (VERBOSE, COSTS OFF) +SELECT postgres_fdw_avg_p_int4(b) FROM pagg_tab; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Finalize Aggregate + Output: postgres_fdw_avg_p_int4(pagg_tab.b) + -> Append + -> Foreign Scan + Output: (PARTIAL postgres_fdw_avg_p_int4(pagg_tab.b)) + Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab) + Remote SQL: SELECT public.postgres_fdw_avg_p_int4(b) FROM public.pagg_tab_p1 + -> Foreign Scan + Output: (PARTIAL postgres_fdw_avg_p_int4(pagg_tab_1.b)) + Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1) + Remote SQL: SELECT public.postgres_fdw_avg_p_int4(b) FROM public.pagg_tab_p2 + -> Foreign Scan + Output: (PARTIAL postgres_fdw_avg_p_int4(pagg_tab_2.b)) + Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2) + Remote SQL: SELECT public.postgres_fdw_avg_p_int4(b) FROM public.pagg_tab_p3 +(15 rows) + +SELECT postgres_fdw_avg_p_int4(b) FROM pagg_tab; + postgres_fdw_avg_p_int4 +------------------------- + {3000,73500} +(1 row) + +ALTER EXTENSION postgres_fdw DROP TYPE mood; +ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_test.postgres_fdw_sum_p_int8(int8); +ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_avg_p_int4(int4); +ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_avg(int4); +DROP AGGREGATE postgres_fdw_sum(int8); +DROP AGGREGATE postgres_fdw_test.postgres_fdw_sum_p_int8(int8); +DROP SCHEMA postgres_fdw_test; +DROP AGGREGATE postgres_fdw_avg(int4); +DROP AGGREGATE postgres_fdw_avg_p_int4(int4); +DROP AGGREGATE postgres_fdw_sum_noaggpartialfunc(int8); +ALTER SERVER loopback OPTIONS (DROP fdw_tuple_cost); -- =================================================================== -- access rights and superuser -- =================================================================== diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c index 8c822f4ef9..67e15c8bb9 100644 --- a/contrib/postgres_fdw/option.c +++ b/contrib/postgres_fdw/option.c @@ -126,7 +126,8 @@ postgres_fdw_validator(PG_FUNCTION_ARGS) strcmp(def->defname, "async_capable") == 0 || strcmp(def->defname, "parallel_commit") == 0 || strcmp(def->defname, "parallel_abort") == 0 || - strcmp(def->defname, "keep_connections") == 0) + strcmp(def->defname, "keep_connections") == 0 || + strcmp(def->defname, "check_partial_aggregate_support") == 0) { /* these accept only boolean values */ (void) defGetBoolean(def); @@ -268,6 +269,7 @@ InitPgFdwOptions(void) /* batch_size is available on both server and table */ {"batch_size", ForeignServerRelationId, false}, {"batch_size", ForeignTableRelationId, false}, + {"check_partial_aggregate_support", ForeignServerRelationId, false}, /* async_capable is available on both server and table */ {"async_capable", ForeignServerRelationId, false}, {"async_capable", ForeignTableRelationId, false}, diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 8b3206ceaa..20e2bfa2d7 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -36,6 +36,7 @@ #include "optimizer/pathnode.h" #include "optimizer/paths.h" #include "optimizer/planmain.h" +#include "optimizer/planner.h" #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" @@ -519,7 +520,7 @@ static bool foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinPathExtraData *extra); static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, - Node *havingQual); + GroupPathExtraData *extra); static List *get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel); static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel); @@ -650,6 +651,8 @@ postgresGetForeignRelSize(PlannerInfo *root, fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST; fpinfo->shippable_extensions = NIL; fpinfo->fetch_size = 100; + fpinfo->check_partial_aggregate_support = false; + fpinfo->remoteversion = 0; fpinfo->async_capable = false; apply_server_options(fpinfo); @@ -661,7 +664,7 @@ postgresGetForeignRelSize(PlannerInfo *root, * should match what ExecCheckPermissions() does. If we fail due to lack * of permissions, the query would have failed at runtime anyway. */ - if (fpinfo->use_remote_estimate) + if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support) { Oid userid; @@ -5948,9 +5951,10 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, fpinfo->pushdown_safe = true; /* Get user mapping */ - if (fpinfo->use_remote_estimate) + if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support) { - if (fpinfo_o->use_remote_estimate) + if (fpinfo_o->use_remote_estimate || + fpinfo_o->check_partial_aggregate_support) fpinfo->user = fpinfo_o->user; else fpinfo->user = fpinfo_i->user; @@ -6132,6 +6136,8 @@ apply_server_options(PgFdwRelationInfo *fpinfo) ExtractExtensionList(defGetString(def), false); else if (strcmp(def->defname, "fetch_size") == 0) (void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL); + else if (strcmp(def->defname, "check_partial_aggregate_support") == 0) + fpinfo->check_partial_aggregate_support = defGetBoolean(def); else if (strcmp(def->defname, "async_capable") == 0) fpinfo->async_capable = defGetBoolean(def); } @@ -6191,6 +6197,8 @@ merge_fdw_options(PgFdwRelationInfo *fpinfo, fpinfo->shippable_extensions = fpinfo_o->shippable_extensions; fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate; fpinfo->fetch_size = fpinfo_o->fetch_size; + fpinfo->check_partial_aggregate_support = fpinfo_o->check_partial_aggregate_support; + fpinfo->remoteversion = fpinfo_o->remoteversion; fpinfo->async_capable = fpinfo_o->async_capable; /* Merge the table level options from either side of the join. */ @@ -6373,7 +6381,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root, */ static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, - Node *havingQual) + GroupPathExtraData *extra) { Query *query = root->parse; PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private; @@ -6382,11 +6390,16 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, ListCell *lc; int i; List *tlist = NIL; + bool partial = extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL; /* We currently don't support pushing Grouping Sets. */ if (query->groupingSets) return false; + /* It is unsafe to push HAVING statements with partial aggregates */ + if ((extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL) && extra->havingQual) + return false; + /* Get the fpinfo of the underlying scan relation. */ ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private; @@ -6415,6 +6428,12 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, * a node, as long as it's not at top level; then no match is possible. */ i = 0; + fpinfo->group_clause = query->groupClause; + if (partial) + { + fpinfo->group_clause = extra->groupClausePartial; + grouping_target = extra->partial_target; + } foreach(lc, grouping_target->exprs) { Expr *expr = (Expr *) lfirst(lc); @@ -6426,7 +6445,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, * check the whole GROUP BY clause not just processed_groupClause, * because we will ship all of it, cf. appendGroupByClause. */ - if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause)) + if (sgref && get_sortgroupref_clause_noerr(sgref, fpinfo->group_clause)) { TargetEntry *tle; @@ -6512,9 +6531,9 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, * Classify the pushable and non-pushable HAVING clauses and save them in * remote_conds and local_conds of the grouped rel's fpinfo. */ - if (havingQual) + if (extra->havingQual) { - foreach(lc, (List *) havingQual) + foreach(lc, (List *) extra->havingQual) { Expr *expr = (Expr *) lfirst(lc); RestrictInfo *rinfo; @@ -6534,7 +6553,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, grouped_rel->relids, NULL, NULL); - if (is_foreign_expr(root, grouped_rel, expr)) + if (is_foreign_expr(root, grouped_rel, expr) && !partial) fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo); else fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo); @@ -6628,6 +6647,7 @@ postgresGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, /* Ignore stages we don't support; and skip any duplicate calls. */ if ((stage != UPPERREL_GROUP_AGG && + stage != UPPERREL_PARTIAL_GROUP_AGG && stage != UPPERREL_ORDERED && stage != UPPERREL_FINAL) || output_rel->fdw_private) @@ -6644,6 +6664,10 @@ postgresGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, add_foreign_grouping_paths(root, input_rel, output_rel, (GroupPathExtraData *) extra); break; + case UPPERREL_PARTIAL_GROUP_AGG: + add_foreign_grouping_paths(root, input_rel, output_rel, + (GroupPathExtraData *) extra); + break; case UPPERREL_ORDERED: add_foreign_ordered_paths(root, input_rel, output_rel); break; @@ -6684,7 +6708,8 @@ add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, return; Assert(extra->patype == PARTITIONWISE_AGGREGATE_NONE || - extra->patype == PARTITIONWISE_AGGREGATE_FULL); + extra->patype == PARTITIONWISE_AGGREGATE_FULL || + extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL); /* save the input_rel as outerrel in fpinfo */ fpinfo->outerrel = input_rel; @@ -6704,7 +6729,7 @@ add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, * Use HAVING qual from extra. In case of child partition, it will have * translated Vars. */ - if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual)) + if (!foreign_grouping_ok(root, grouped_rel, extra)) return; /* diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h index 47157ac887..a1a9688c9f 100644 --- a/contrib/postgres_fdw/postgres_fdw.h +++ b/contrib/postgres_fdw/postgres_fdw.h @@ -86,6 +86,15 @@ typedef struct PgFdwRelationInfo ForeignServer *server; UserMapping *user; /* only set in use_remote_estimate mode */ + /* for partial aggregate pushdown */ + bool check_partial_aggregate_support; + + /* + * If remoteversion is zero, it means the remote server version has not + * been acquired. + */ + int remoteversion; + int fetch_size; /* fetch size for this remote table */ /* @@ -110,6 +119,7 @@ typedef struct PgFdwRelationInfo /* Grouping information */ List *grouped_tlist; + List *group_clause; /* Subquery information */ bool make_outerrel_subquery; /* do we deparse outerrel as a diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql index a303bfb322..92326c07eb 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -2989,16 +2989,31 @@ RESET enable_partitionwise_join; -- =================================================================== -- test partitionwise aggregates -- =================================================================== - -CREATE TABLE pagg_tab (a int, b int, c text) PARTITION BY RANGE(a); +ALTER SERVER loopback OPTIONS (ADD fdw_tuple_cost '0.1'); + +CREATE TYPE mood AS enum ('sad', 'ok', 'happy'); +ALTER EXTENSION postgres_fdw ADD TYPE mood; + +CREATE TABLE pagg_tab (a int, b int, c text, c_serial int4, + c_int4array _int4, c_interval interval, + c_money money, c_1c text, c_1b bytea, + c_bit bit(2), c_1or3int2 int2, + c_1or3int4 int4, c_1or3int8 int8, + c_bool bool, c_enum mood, c_pg_lsn pg_lsn, + c_tid tid, c_int4range int4range, + c_int4multirange int4multirange, + c_time time, c_timetz timetz, + c_timestamp timestamp, c_timestamptz timestamptz, + c_xid8 xid8) + PARTITION BY RANGE(a); CREATE TABLE pagg_tab_p1 (LIKE pagg_tab); CREATE TABLE pagg_tab_p2 (LIKE pagg_tab); CREATE TABLE pagg_tab_p3 (LIKE pagg_tab); -INSERT INTO pagg_tab_p1 SELECT i % 30, i % 50, to_char(i/30, 'FM0000') FROM generate_series(1, 3000) i WHERE (i % 30) < 10; -INSERT INTO pagg_tab_p2 SELECT i % 30, i % 50, to_char(i/30, 'FM0000') FROM generate_series(1, 3000) i WHERE (i % 30) < 20 and (i % 30) >= 10; -INSERT INTO pagg_tab_p3 SELECT i % 30, i % 50, to_char(i/30, 'FM0000') FROM generate_series(1, 3000) i WHERE (i % 30) < 30 and (i % 30) >= 20; +INSERT INTO pagg_tab_p1 SELECT i % 30, i % 50, to_char(i/30, 'FM0000'), i, array[(i % 2), 0], ((i % 2) || ' seconds')::interval, (i % 2)::money, ((i - 1) % 10)::text, (((i - 1) % 10)::text)::bytea, case when (i % 2) = 0 then B'01' else B'11' end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then true else false end, (case when (i % 2) = 0 then 'sad' else 'happy' end)::mood, ('0/' || i)::pg_lsn, ('(0,' || i || ')')::tid, int4range(0, i), int4multirange(int4range(0, i), int4range(100, 100 + i)), '00:00:00'::time + (i || ' seconds')::interval, '00:00:00'::timetz + (i || ' seconds')::interval, '2000-01-01'::timestamp + (i || ' seconds')::interval, '2000-01-01'::timestamptz + (i || ' seconds')::interval, ((i % 10)::text)::xid8 FROM generate_series(1, 3000) i WHERE (i % 30) < 10; +INSERT INTO pagg_tab_p2 SELECT i % 30, i % 50, to_char(i/30, 'FM0000'), i, array[(i % 2), 0], ((i % 2) || ' seconds')::interval, (i % 2)::money, ((i - 1) % 10)::text, (((i - 1) % 10)::text)::bytea, case when (i % 2) = 0 then B'01' else B'11' end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then true else false end, (case when (i % 2) = 0 then 'sad' else 'happy' end)::mood, ('0/' || i)::pg_lsn, ('(0,' || i || ')')::tid, int4range(0, i), int4multirange(int4range(0, i), int4range(100, 100 + i)), '00:00:00'::time + (i || ' seconds')::interval, '00:00:00'::timetz + (i || ' seconds')::interval, '2000-01-01'::timestamp + (i || ' seconds')::interval, '2000-01-01'::timestamptz + (i || ' seconds')::interval, ((i % 10)::text)::xid8 FROM generate_series(1, 3000) i WHERE (i % 30) < 20 and (i % 30) >= 10; +INSERT INTO pagg_tab_p3 SELECT i % 30, i % 50, to_char(i/30, 'FM0000'), i, array[(i % 2), 0], ((i % 2) || ' seconds')::interval, (i % 2)::money, ((i - 1) % 10)::text, (((i - 1) % 10)::text)::bytea, case when (i % 2) = 0 then B'01' else B'11' end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then true else false end, (case when (i % 2) = 0 then 'sad' else 'happy' end)::mood, ('0/' || i)::pg_lsn, ('(0,' || i || ')')::tid, int4range(0, i), int4multirange(int4range(0, i), int4range(100, 100 + i)), '00:00:00'::time + (i || ' seconds')::interval, '00:00:00'::timetz + (i || ' seconds')::interval, '2000-01-01'::timestamp + (i || ' seconds')::interval, '2000-01-01'::timestamptz + (i || ' seconds')::interval, ((i % 10)::text)::xid8 FROM generate_series(1, 3000) i WHERE (i % 30) < 30 and (i % 30) >= 20; -- Create foreign partitions CREATE FOREIGN TABLE fpagg_tab_p1 PARTITION OF pagg_tab FOR VALUES FROM (0) TO (10) SERVER loopback OPTIONS (table_name 'pagg_tab_p1'); @@ -3006,6 +3021,9 @@ CREATE FOREIGN TABLE fpagg_tab_p2 PARTITION OF pagg_tab FOR VALUES FROM (10) TO CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3'); ANALYZE pagg_tab; +ANALYZE pagg_tab_p1; +ANALYZE pagg_tab_p2; +ANALYZE pagg_tab_p3; ANALYZE fpagg_tab_p1; ANALYZE fpagg_tab_p2; ANALYZE fpagg_tab_p3; @@ -3028,10 +3046,289 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1; SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1; --- When GROUP BY clause does not match with PARTITION KEY. -EXPLAIN (COSTS OFF) +-- Partial aggregates are unsafe to push down when there is a HAVING clause +EXPLAIN (VERBOSE, COSTS OFF) +SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1; SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1; +-- Partial aggregates are safe to push down without having clause +EXPLAIN (VERBOSE, COSTS OFF) +SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1; +SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1; + +-- Partial aggregates are safe to push down even if we need both variable and variable-based expression +EXPLAIN (VERBOSE, COSTS OFF) +SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4; +SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4; + +-- Partial aggregates are safe to push down for all built-in aggregates +EXPLAIN (VERBOSE, COSTS OFF) +SELECT /* aggregate function <> aggpartialfunc */ + array_agg(c_int4array), array_agg(b), + avg(b::int2), avg(b::int4), avg(b::int8), avg(c_interval), avg(b::float4), avg(b::float8), avg(b::numeric), + corr(b::float8, (b * b)::float8), + covar_pop(b::float8, (b * b)::float8), + covar_samp(b::float8, (b * b)::float8), + regr_avgx((2 * b)::float8, b::float8), + regr_avgy((2 * b)::float8, b::float8), + regr_intercept((2 * b + 3)::float8, b::float8), + regr_r2((b * b)::float8, b::float8), + regr_slope((2 * b + 3)::float8, b::float8), + regr_sxx((2 * b + 3)::float8, b::float8), + regr_sxy((b * b)::float8, b::float8), + regr_syy((2 * b + 3)::float8, b::float8), + stddev(b::float4), stddev(b::float8), stddev(b::int2), stddev(b::int4), stddev(b::int8), stddev(b::numeric), + stddev_pop(b::float4), stddev_pop(b::float8), stddev_pop(b::int2), stddev_pop(b::int4), stddev_pop(b::int8), stddev_pop(b::numeric), + stddev_samp(b::float4), stddev_samp(b::float8), stddev_samp(b::int2), stddev_samp(b::int4), stddev_samp(b::int8), stddev_samp(b::numeric), + string_agg(c_1c, ','), string_agg(c_1b, ','), + sum(b::int8), sum(b::numeric), + variance(b::float4), variance(b::float8), variance(b::int2), variance(b::int4), variance(b::int8), variance(b::numeric), + var_pop(b::float4), var_pop(b::float8), var_pop(b::int2), var_pop(b::int4), var_pop(b::int8), var_pop(b::numeric), + var_samp(b::float4), var_samp(b::float8), var_samp(b::int2), var_samp(b::int4), var_samp(b::int8), var_samp(b::numeric), + /* aggregate function = aggpartialfunc */ + any_value(b * 0), + bit_and(c_bit), bit_and(c_1or3int2), bit_and(c_1or3int4), bit_and(c_1or3int8), + bit_or(c_bit), bit_or(c_1or3int2), bit_or(c_1or3int4), bit_or(c_1or3int8), + bit_xor(c_bit), bit_xor(c_1or3int2), bit_xor(c_1or3int4), bit_xor(c_1or3int8), + bool_and(c_bool), + bool_or(c_bool), + count(b), count(*), + every(c_bool), + max(c_int4array), max(c_enum), max(c_1c::char(1)), max('2000-01-01'::date + b), max('0.0.0.0'::inet + b), max(b::float4), max(b::float8), max(b::int2), max(b::int4), max(b::int8), max(c_interval), max(c_money), max(b::numeric), max(b::oid), max(c_pg_lsn), max(c_tid), max(c_1c), max(c_time), max(c_timetz), max(c_timestamp), max(c_timestamptz), max(c_xid8), + min(c_int4array), min(c_enum), min(c_1c::char(1)), min('2000-01-01'::date + b), min('0.0.0.0'::inet + b), min(b::float4), min(b::float8), min(b::int2), min(b::int4), min(b::int8), min(c_interval), min(c_money), min(b::numeric), min(b::oid), min(c_pg_lsn), min(c_tid), min(c_1c), min(c_time), min(c_timetz), min(c_timestamp), min(c_timestamptz), min(c_xid8), + range_intersect_agg(c_int4range), range_intersect_agg(c_int4multirange), + regr_count((2 * b + 3)::float8, b::float8), + sum(b::float4), sum(b::float8), sum(b::int2), sum(b::int4), sum(c_interval), sum(c_money) + FROM pagg_tab WHERE c_serial between 1 and 30; + +SELECT /* aggregate function <> aggpartialfunc */ + array_agg(c_int4array), array_agg(b), + avg(b::int2), avg(b::int4), avg(b::int8), avg(c_interval), + avg(b::float4), avg(b::float8), + corr(b::float8, (b * b)::float8), + covar_pop(b::float8, (b * b)::float8), + covar_samp(b::float8, (b * b)::float8), + regr_avgx((2 * b)::float8, b::float8), + regr_avgy((2 * b)::float8, b::float8), + regr_intercept((2 * b + 3)::float8, b::float8), + regr_r2((b * b)::float8, b::float8), + regr_slope((2 * b + 3)::float8, b::float8), + regr_sxx((2 * b + 3)::float8, b::float8), + regr_sxy((b * b)::float8, b::float8), + regr_syy((2 * b + 3)::float8, b::float8), + stddev(b::float4), stddev(b::float8), stddev(b::int2), stddev(b::int4), stddev(b::int8), stddev(b::numeric), + stddev_pop(b::float4), stddev_pop(b::float8), stddev_pop(b::int2), stddev_pop(b::int4), stddev_pop(b::int8), stddev_pop(b::numeric), + stddev_samp(b::float4), stddev_samp(b::float8), stddev_samp(b::int2), stddev_samp(b::int4), stddev_samp(b::int8), stddev_samp(b::numeric), + string_agg(c_1c, ','), string_agg(c_1b, ','), + sum(b::int8), sum(b::numeric), + variance(b::float4), variance(b::float8), variance(b::int2), variance(b::int4), variance(b::int8), variance(b::numeric), + var_pop(b::float4), var_pop(b::float8), var_pop(b::int2), var_pop(b::int4), var_pop(b::int8), var_pop(b::numeric), + var_samp(b::float4), var_samp(b::float8), var_samp(b::int2), var_samp(b::int4), var_samp(b::int8), var_samp(b::numeric), + /* aggregate function = aggpartialfunc */ + any_value(b * 0), + bit_and(c_bit), bit_and(c_1or3int2), bit_and(c_1or3int4), bit_and(c_1or3int8), + bit_or(c_bit), bit_or(c_1or3int2), bit_or(c_1or3int4), bit_or(c_1or3int8), + bit_xor(c_bit), bit_xor(c_1or3int2), bit_xor(c_1or3int4), bit_xor(c_1or3int8), + bool_and(c_bool), + bool_or(c_bool), + count(b), count(*), + every(c_bool), + max(c_int4array), max(c_enum), max(c_1c::char(1)), max('2000-01-01'::date + b), max('0.0.0.0'::inet + b), max(b::float4), max(b::float8), max(b::int2), max(b::int4), max(b::int8), max(c_interval), max(c_money), max(b::numeric), max(b::oid), max(c_pg_lsn), max(c_tid), max(c_1c), max(c_time), max(c_timetz), max(c_timestamp), max(c_timestamptz), max(c_xid8), + min(c_int4array), min(c_enum), min(c_1c::char(1)), min('2000-01-01'::date + b), min('0.0.0.0'::inet + b), min(b::float4), min(b::float8), min(b::int2), min(b::int4), min(b::int8), min(c_interval), min(c_money), min(b::numeric), min(b::oid), min(c_pg_lsn), min(c_tid), min(c_1c), min(c_time), min(c_timetz), min(c_timestamp), min(c_timestamptz), min(c_xid8), + range_intersect_agg(c_int4range), range_intersect_agg(c_int4multirange), + regr_count((2 * b + 3)::float8, b::float8), + sum(b::float4), sum(b::float8), sum(b::int2), sum(b::int4), sum(c_interval), sum(c_money) + FROM pagg_tab WHERE c_serial between 1 and 30; + +-- Tests for backward compatibility +ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false'); +EXPLAIN (VERBOSE, COSTS OFF) +SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1; +SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1; + +ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true'); +EXPLAIN (VERBOSE, COSTS OFF) +SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1; +SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1; + +ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0'); +SET enable_partitionwise_join=on; +EXPLAIN (VERBOSE, COSTS OFF) +SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a); +SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a); +RESET enable_partitionwise_join; +ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1'); + +ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support); + +-- List of aggregate functions such that the aggregate function supports partial aggregate +-- and aggpartialfn field of the aggregate function is invalid +SELECT aggfnoid::text + FROM pg_aggregate a JOIN pg_type b ON a.aggtranstype = b.oid + WHERE (aggcombinefn::text <> '-') AND (aggpartialfn::text = '-') + ORDER BY 1; + +-- List of aggregate functions which have incorrect aggpartialfunc +SELECT a.aggfnoid + FROM pg_aggregate a JOIN pg_type b ON a.aggtranstype = b.oid + JOIN pg_aggregate c on a.aggpartialfn = c.aggfnoid + WHERE (a.aggcombinefn::text <> '-') + AND (a.aggpartialfn::text <> '-') + AND (a.aggtransfn <> c.aggtransfn + OR (c.aggcombinefn::text <> '-' + AND a.aggcombinefn <> c.aggcombinefn) + OR (a.agginitval IS NOT NULL + AND c.agginitval IS NOT NULL + AND a.agginitval <> c.agginitval) + OR (b.typname = 'internal' + AND c.aggserialfn::text <> '-' + AND a.aggserialfn <> c.aggserialfn) + OR (b.typname = 'internal' + AND c.aggdeserialfn::text <> '-' + AND a.aggdeserialfn <> c.aggdeserialfn) + OR (b.typname = 'internal' + AND a.aggserialfn <> c.aggfinalfn) + OR (b.typname <> 'internal' + AND c.aggfinalfn::text <> '-')) + ORDER BY 1; + +-- It is unsafe to push down partial aggregates which contain DISTINCT clauses +EXPLAIN (VERBOSE, COSTS OFF) +SELECT max(a), count(distinct b) FROM pagg_tab; +SELECT max(a), count(distinct b) FROM pagg_tab; + +-- It is unsafe to push down partial aggregates which contain ORDER BY clauses +EXPLAIN (VERBOSE, COSTS OFF) SELECT array_agg(b order by b) FROM pagg_tab WHERE c_serial between 1 and 30; +SELECT array_agg(b order by b) FROM pagg_tab WHERE c_serial between 1 and 30; + +-- Create user-defined aggregates whose stype is internal +CREATE SCHEMA postgres_fdw_test; +CREATE AGGREGATE postgres_fdw_test.postgres_fdw_sum_p_int8(int8) ( + SFUNC = int8_avg_accum, + STYPE = internal, + COMBINEFUNC = int8_avg_combine, + FINALFUNC = int8_avg_serialize, + SERIALFUNC = int8_avg_serialize, + DESERIALFUNC = int8_avg_deserialize, + AGGPARTIALFUNC = postgres_fdw_test.postgres_fdw_sum_p_int8 +); + +CREATE AGGREGATE postgres_fdw_sum(int8) ( + SFUNC = int8_avg_accum, + STYPE = internal, + COMBINEFUNC = int8_avg_combine, + FINALFUNC = numeric_poly_sum, + SERIALFUNC = int8_avg_serialize, + DESERIALFUNC = int8_avg_deserialize, + AGGPARTIALFUNC = postgres_fdw_test.postgres_fdw_sum_p_int8 +); + +-- Create user-defined aggregates whose stype is not internal +CREATE AGGREGATE postgres_fdw_avg_p_int4(int4) ( + SFUNC = int4_avg_accum, + STYPE = _int8, + COMBINEFUNC = int4_avg_combine, + INITCOND = '{0,0}', + AGGPARTIALFUNC = postgres_fdw_avg_p_int4 +); + +CREATE AGGREGATE postgres_fdw_avg(int4) ( + SFUNC = int4_avg_accum, + STYPE = _int8, + COMBINEFUNC = int4_avg_combine, + FINALFUNC = int8_avg, + INITCOND = '{0,0}', + AGGPARTIALFUNC = postgres_fdw_avg_p_int4 +); + +CREATE AGGREGATE postgres_fdw_sum_noaggpartialfunc(int8) ( + SFUNC = int8_avg_accum, + STYPE = internal, + COMBINEFUNC = int8_avg_combine, + FINALFUNC = numeric_poly_sum, + SERIALFUNC = int8_avg_serialize, + DESERIALFUNC = int8_avg_deserialize +); + +-- It's safe to push down partial aggregates for user-defined aggregate +-- when it belongs to an extension that is listed in the extension setting +-- and when its aggpartialfunc belongs to an extension that is listed +-- in the extension setting. +ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_sum(int8); +ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_test.postgres_fdw_sum_p_int8(int8); + +ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_avg(int4); +ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_avg_p_int4(int4); + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT postgres_fdw_sum(b::int8) FROM pagg_tab; +SELECT postgres_fdw_sum(b::int8) FROM pagg_tab; + +-- It is unsafe to push down partial aggregates for user-defined aggregates +-- when they have no aggpartialfunc. +EXPLAIN (VERBOSE, COSTS OFF) +SELECT postgres_fdw_sum_noaggpartialfunc(b::int8) FROM pagg_tab; +SELECT postgres_fdw_sum_noaggpartialfunc(b::int8) FROM pagg_tab; + +ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_test.postgres_fdw_sum_p_int8(int8); + +-- Reconnect for flushing the shippability cache +\c - +SET enable_partitionwise_aggregate TO true; + +-- It is unsafe to push down partial aggregates for user-defined aggregates +-- when itself is aggpartialfunc and when it belongs to an extension +-- that's listed in the extension setting and when stype is internal. +EXPLAIN (VERBOSE, COSTS OFF) +SELECT postgres_fdw_test.postgres_fdw_sum_p_int8(b::int8) FROM pagg_tab; +SELECT postgres_fdw_test.postgres_fdw_sum_p_int8(b::int8) FROM pagg_tab; + +ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_sum(int8); +ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_test.postgres_fdw_sum_p_int8(int8); + +-- Reconnect for flushing the shippability cache +\c - +SET enable_partitionwise_aggregate TO true; + +-- It is unsafe to push down partial aggregates for user-defined aggregates +-- when it doesn't belong to an extension that is listed in the extension setting +-- and when its aggpartialfunc belongs to an extension that is listed +-- in the extension setting. +EXPLAIN (VERBOSE, COSTS OFF) +SELECT postgres_fdw_sum(b::int8) FROM pagg_tab; +SELECT postgres_fdw_sum(b::int8) FROM pagg_tab; + +-- It's safe to push down partial aggregates for user-defined aggregate +-- whose stype is not internal +-- when it belongs to an extension that is listed in the extension setting +-- and when its aggpartialfunc belongs to an extension that is listed +-- in the extension setting. +EXPLAIN (VERBOSE, COSTS OFF) +SELECT postgres_fdw_avg(b) FROM pagg_tab; +SELECT postgres_fdw_avg(b) FROM pagg_tab; + +-- It's safe to push down partial aggregates for user-defined aggregate +-- when itself is aggpartialfunc and when it belongs to an extension +-- that's listed in the extension setting and when stype is not internal. +EXPLAIN (VERBOSE, COSTS OFF) +SELECT postgres_fdw_avg_p_int4(b) FROM pagg_tab; +SELECT postgres_fdw_avg_p_int4(b) FROM pagg_tab; + +ALTER EXTENSION postgres_fdw DROP TYPE mood; + +ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_test.postgres_fdw_sum_p_int8(int8); + +ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_avg_p_int4(int4); +ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_avg(int4); + +DROP AGGREGATE postgres_fdw_sum(int8); +DROP AGGREGATE postgres_fdw_test.postgres_fdw_sum_p_int8(int8); +DROP SCHEMA postgres_fdw_test; +DROP AGGREGATE postgres_fdw_avg(int4); +DROP AGGREGATE postgres_fdw_avg_p_int4(int4); +DROP AGGREGATE postgres_fdw_sum_noaggpartialfunc(int8); +ALTER SERVER loopback OPTIONS (DROP fdw_tuple_cost); + -- =================================================================== -- access rights and superuser -- =================================================================== diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 3ec7391ec5..a6dc334726 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -644,6 +644,18 @@ value starts out null. + + + + aggpartialfn regproc + (references pg_proc.oid) + + + Partial aggregate function (zero if none). + See for the definition + of partial aggregate function. + + diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml index 33cc6e07b7..1623e46054 100644 --- a/doc/src/sgml/postgres-fdw.sgml +++ b/doc/src/sgml/postgres-fdw.sgml @@ -451,6 +451,43 @@ OPTIONS (ADD password_required 'false'); + + Partial Aggregate Pushdown Options + + + By default, postgres_fdw assumes that each + built-in aggregate function has a partial aggregate function defined on + the remote server; see + for a definition of the partial aggregate functions. This may be + overridden using the following option: + + + + + + check_partial_aggregate_support (boolean) + + + If this option is false, postgres_fdw always + uses partial aggregate pushdown by assuming that each built-in + aggregate function has a partial aggregate function defined on + the remote server. If this option is true, local aggregates + whose partial computation function references itself are assumed + to exist on the remote server. If not, during query planning, + postgres_fdw will connect to the remote + server and retrieve the remote server version. If the remote + version is the same or newer, partial aggregate functions will be + assumed to exist. If older, postgres_fdw + checks that the remote server has a matching partial aggregate + function before performing partial aggregate pushdown. The default + is false. + + + + + + + Asynchronous Execution Options @@ -929,14 +966,15 @@ postgres=# SELECT postgres_fdw_disconnect_all(); postgres_fdw attempts to optimize remote queries to reduce the amount of data transferred from foreign servers. This is done by - sending query WHERE clauses to the remote server for - execution, and by not retrieving table columns that are not needed for - the current query. To reduce the risk of misexecution of queries, - WHERE clauses are not sent to the remote server unless they use - only data types, operators, and functions that are built-in or belong to an - extension that's listed in the foreign server's extensions - option. Operators and functions in such clauses must - be IMMUTABLE as well. + sending query WHERE clauses and aggregate expressions + to the remote server for execution, and by not retrieving table + columns that are not needed for the current query. To reduce the + risk of misexecution of queries, WHERE clauses + and aggregate expressions are not sent to the remote server unless + they only use data types, operators, and functions that are built-in + or belong to an extension that is listed in the foreign server's + extensions option. Operators and functions in such + clauses must be IMMUTABLE as well. For an UPDATE or DELETE query, postgres_fdw attempts to optimize the query execution by sending the whole query to the remote server if there are no query @@ -966,6 +1004,64 @@ postgres=# SELECT postgres_fdw_disconnect_all(); + + Partial aggregate pushdown + + Partial aggregate pushdown is an optimization for queries that contains + aggregate expressions for a partitioned table across one or more remote + servers. If multiple conditions are met, partial aggregate function + calls are sent to the remote servers. The conditions under which this + sending is active are as follows. + + + + + A partial aggregate function which corresponds to the aggregate + expression is defined on the local server and assumed to exist on + the remote server + + + + + The conditions in are true + + + + + The query doesn't contain a HAVING clause + + + + + The aggregate expressions in the query do not contain + DISTINCT or ORDER BY clauses + + + + + When partial aggregate pushdown is used for aggregate expressions, + remote queries replace aggregate function calls with partial + aggregate function calls. If the data type of the state value is not + internal, the partial aggregate function must not have + a final function. If internal, the partial aggregate + function must have the same final function as the aggregate function's + serialization function, and the partial aggregate function and the + aggregate function have the same transition function and data type + of the state value. If the partial aggregate function has any of the + following members: initial state, combine function, serialization + function, or deserialization function, the members must match the + corresponding members in the aggregate function. Built-in aggregate + functions that support partial aggregation must also have their + partial aggregate functions defined as built-in aggregates. See for details + on how the existance of remote-server partial aggregates are checked. + postgres_fdw assumes user-defined aggregate + functions have partial aggregate functions on the remote server only + if both belong to extensions listed in the foreign server's extensions + option. + + + Remote Query Execution Environment @@ -1031,15 +1127,25 @@ postgres=# SELECT postgres_fdw_disconnect_all(); back to 8.1. A limitation however is that postgres_fdw generally assumes that immutable built-in functions and operators are safe to send to the remote server for execution, if they appear in a - WHERE clause for a foreign table. Thus, a built-in - function that was added since the remote server's release might be sent - to it for execution, resulting in function does not exist or - a similar error. This type of failure can be worked around by + WHERE clause or aggregate expressions for a foreign table. + Thus, a built-in function that was added since the remote server's release + might be sent to it for execution, resulting in function does not + exist or a similar error. This type of failure can be worked around by rewriting the query, for example by embedding the foreign table reference in a sub-SELECT with OFFSET 0 as an optimization fence, and placing the problematic function or operator outside the sub-SELECT. + + + A similar problem can occur with partial aggregate pushdown. + Pushdown causes aggregate function calls to send partial aggregate + function calls to the remote server. If the partial aggregate + function doesn't exist on the remote server, it causes + function does not exist or a similar error; see + + on how to enable remote server checks. + diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml index 222e0aa5c9..12beb79dc7 100644 --- a/doc/src/sgml/ref/create_aggregate.sgml +++ b/doc/src/sgml/ref/create_aggregate.sgml @@ -42,6 +42,7 @@ CREATE [ OR REPLACE ] AGGREGATE nameminitial_condition ] [ , SORTOP = sort_operator ] [ , PARALLEL = { SAFE | RESTRICTED | UNSAFE } ] + [ , AGGPARTIALFUNC = aggpartialfunc ] ) CREATE [ OR REPLACE ] AGGREGATE name ( [ [ argmode ] [ argname ] arg_data_type [ , ... ] ] @@ -80,6 +81,7 @@ CREATE [ OR REPLACE ] AGGREGATE nameminitial_condition ] [ , SORTOP = sort_operator ] + [ , AGGPARTIALFUNC = aggpartialfunc ] ) @@ -246,6 +248,12 @@ CREATE [ OR REPLACE ] AGGREGATE namePARALLEL SAFE to enable parallel aggregation. + + Paraemter AGGPARTIALFUNC optionally defines a + partial aggregate function used for partial aggregate pushdown; see + for details. + + Aggregates that behave like MIN or MAX can sometimes be optimized by looking into an index instead of scanning every @@ -653,6 +661,32 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1; + + + aggpartialfunc + + + Parameter aggpartialfunc + reprsents the name of this aggregate function's + partial aggregate function. If specified, combinefunc must also be specified. + If state_data_type + is not internal, the partial aggregate function must + not have the ffunc. + If state_data_type + is internal, the partial aggregate function + must have the same final function as serialfunc. The partial + aggregate function and the aggregate function must have the same + sfunc and state_data_type. If the partial + aggregate function has any of the following members: initial + state, combine function, serialization function, or deserialization + function, the members must match with the corresponding members in + the aggregate function. + + + diff --git a/doc/src/sgml/xaggr.sgml b/doc/src/sgml/xaggr.sgml index bdad8d3dc2..2863731d00 100644 --- a/doc/src/sgml/xaggr.sgml +++ b/doc/src/sgml/xaggr.sgml @@ -536,9 +536,6 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households; parallel aggregation by having different worker processes scan different portions of a table. Each worker produces a partial state value, and at the end those state values are combined to produce a final state value. - (In the future this mode might also be used for purposes such as combining - aggregations over local and remote tables; but that is not implemented - yet.) @@ -612,6 +609,14 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households; parallel-safety markings on its support functions are not consulted. + + postgres_fdw can optimize queries containing + aggregate expressions on partitioned tables that span multiple + remote servers. This optimization sends partial + aggregate functions to remote servers; see for details. + + diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index ebc4454743..aaf1e62762 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -63,6 +63,7 @@ AggregateCreate(const char *aggName, List *aggmtransfnName, List *aggminvtransfnName, List *aggmfinalfnName, + List *aggpartialfnName, bool finalfnExtraArgs, bool mfinalfnExtraArgs, char finalfnModify, @@ -91,6 +92,8 @@ AggregateCreate(const char *aggName, Oid mtransfn = InvalidOid; /* can be omitted */ Oid minvtransfn = InvalidOid; /* can be omitted */ Oid mfinalfn = InvalidOid; /* can be omitted */ + bool isaggpartialfn = false; + Oid aggpartialfn = InvalidOid; /* can be omitted */ Oid sortop = InvalidOid; /* can be omitted */ Oid *aggArgTypes = parameterTypes->values; bool mtransIsStrict = false; @@ -569,6 +572,94 @@ AggregateCreate(const char *aggName, format_type_be(finaltype)))); } + /* + * Validate the aggpartialfunc, if present. + */ + if (aggpartialfnName) + { + char *aggpartialName; + Oid aggpartialNamespace; + + if (!aggcombinefnName) + elog(ERROR, "aggcombinefnName must be supplied if aggpartialfnName is supplied"); + + /* Convert list of names to a name and namespace */ + aggpartialNamespace = QualifiedNameGetCreationNamespace(aggpartialfnName, + &aggpartialName); + + if ((aggNamespace == aggpartialNamespace) + && (strcmp(aggName, aggpartialName) == 0)) + { + if (((aggTransType != INTERNALOID) && (finalfn != InvalidOid)) + || ((aggTransType == INTERNALOID) && (finalfn != serialfn))) + elog(ERROR, "%s is not its own aggpartialfunc", aggName); + isaggpartialfn = true; + } + else + { + HeapTuple aggtup; + Form_pg_aggregate aggpartialform; + Datum textInitVal; + char *strInitVal; + bool initValueIsNull; + + aggpartialfn = LookupFuncName(aggpartialfnName, numArgs, aggArgTypes, false); + + /* Check aggregate creator has permission to call the function */ + aclresult = object_aclcheck(ProcedureRelationId, aggpartialfn, + GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(aggpartialfn)); + + rettype = get_func_rettype(aggpartialfn); + + aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggpartialfn)); + if (!HeapTupleIsValid(aggtup)) + elog(ERROR, "cache lookup failed for aggpartialfunc %u", aggpartialfn); + + aggpartialform = (Form_pg_aggregate) GETSTRUCT(aggtup); + + if ((aggpartialform->aggcombinefn != InvalidOid) && + (aggpartialform->aggcombinefn != combinefn)) + elog(ERROR, "%s in aggregate and %s in its aggpartialfunc must match", + "combinefunc", "combinefunc"); + + if (aggpartialform->aggtransfn != transfn) + elog(ERROR, "%s in aggregate and its aggpartialfunc must match", "sfunc"); + + textInitVal = SysCacheGetAttr(AGGFNOID, aggtup, + Anum_pg_aggregate_agginitval, &initValueIsNull); + + if (!initValueIsNull && (agginitval != NULL)) + { + strInitVal = TextDatumGetCString(textInitVal); + if (strcmp(strInitVal, agginitval) != 0) + elog(ERROR, "%s in aggregate and %s in its aggpartialfunc must match", + "initcond", "initcond"); + } + + if (aggTransType == INTERNALOID) + { + if (aggpartialform->aggcombinefn != InvalidOid) + { + if (aggpartialform->aggserialfn != serialfn) + elog(ERROR, "%s in aggregate and %s in its aggpartialfunc must match", + "serialfunc", "serialfunc"); + + if (aggpartialform->aggdeserialfn != deserialfn) + elog(ERROR, "%s in aggregate and %s in its aggpartialfunc must match", + "deserialfunc", "deserialfunc"); + } + if (aggpartialform->aggfinalfn != serialfn) + elog(ERROR, "finalfunc of aggpartialfunc must match serialfunc of aggregate when stype is internal"); + } + else if (aggpartialform->aggfinalfn != InvalidOid) + elog(ERROR, "finalfunc of aggpartialfunc must not be supplied when stype isn't internal"); + + ReleaseSysCache(aggtup); + } + } + /* handle sortop, if supplied */ if (aggsortopName) { @@ -684,6 +775,9 @@ AggregateCreate(const char *aggName, values[Anum_pg_aggregate_aggminitval - 1] = CStringGetTextDatum(aggminitval); else nulls[Anum_pg_aggregate_aggminitval - 1] = true; + if (isaggpartialfn) + aggpartialfn = procOid; + values[Anum_pg_aggregate_aggpartialfn - 1] = ObjectIdGetDatum(aggpartialfn); if (replace) oldtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(procOid)); @@ -805,6 +899,13 @@ AggregateCreate(const char *aggName, add_exact_object_address(&referenced, addrs); } + /* Depends on aggpartialfunc, if any */ + if (OidIsValid(aggpartialfn) && (aggpartialfn != procOid)) + { + ObjectAddressSet(referenced, ProcedureRelationId, aggpartialfn); + add_exact_object_address(&referenced, addrs); + } + record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL); free_object_addresses(addrs); return myself; diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index fda9d1aa77..8c8f1789e9 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -70,6 +70,7 @@ DefineAggregate(ParseState *pstate, List *combinefuncName = NIL; List *serialfuncName = NIL; List *deserialfuncName = NIL; + List *aggpartialfuncName = NIL; List *mtransfuncName = NIL; List *minvtransfuncName = NIL; List *mfinalfuncName = NIL; @@ -143,6 +144,8 @@ DefineAggregate(ParseState *pstate, serialfuncName = defGetQualifiedName(defel); else if (strcmp(defel->defname, "deserialfunc") == 0) deserialfuncName = defGetQualifiedName(defel); + else if (strcmp(defel->defname, "aggpartialfunc") == 0) + aggpartialfuncName = defGetQualifiedName(defel); else if (strcmp(defel->defname, "msfunc") == 0) mtransfuncName = defGetQualifiedName(defel); else if (strcmp(defel->defname, "minvfunc") == 0) @@ -461,6 +464,7 @@ DefineAggregate(ParseState *pstate, mtransfuncName, /* fwd trans function name */ minvtransfuncName, /* inv trans function name */ mfinalfuncName, /* final function name */ + aggpartialfuncName, finalfuncExtraArgs, mfinalfuncExtraArgs, finalfuncModify, diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index a8cea5efe1..f195970967 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -206,7 +206,7 @@ static PathTarget *make_group_input_target(PlannerInfo *root, PathTarget *final_target); static PathTarget *make_partial_grouping_target(PlannerInfo *root, PathTarget *grouping_target, - Node *havingQual); + GroupPathExtraData *extra); static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist); static void optimize_window_clauses(PlannerInfo *root, WindowFuncLists *wflists); @@ -5415,6 +5415,99 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target) return set_pathtarget_cost_width(root, input_target); } +/* + * setGroupClausePartial + * Generate a groupClause and a pathtarget for partial aggregate + * pushdown by FDW and set them to GroupPathExtraData. + */ +static void +setGroupClausePartial(PathTarget *partial_target, List *non_group_exprs, + List *groupClause, GroupPathExtraData *extra) +{ + int exprno, + refno; + ListCell *lc; + Index maxRef = 0; + List *exprs_processed = NIL; + int exprs_num = 0; + + foreach(lc, groupClause) + { + SortGroupClause *sgc = (SortGroupClause *) lfirst(lc); + + if (sgc->tleSortGroupRef > maxRef) + maxRef = sgc->tleSortGroupRef; + } + maxRef++; + + extra->groupClausePartial = list_copy_deep(groupClause); + extra->partial_target = copy_pathtarget(partial_target); + + if (partial_target->exprs) + exprs_num = partial_target->exprs->length; + + foreach(lc, non_group_exprs) + { + Expr *expr = (Expr *) lfirst(lc); + + refno = -1; + if (list_member(exprs_processed, expr) || + (!IsA(expr, Var) && !IsA(expr, PlaceHolderVar))) + continue; + exprs_processed = lappend(exprs_processed, expr); + for (exprno = 0; exprno < exprs_num; exprno++) + { + Expr *target_expr = (Expr *) list_nth(partial_target->exprs, exprno); + + if (equal(target_expr, expr)) + { + refno = exprno; + break; + } + } + if (refno < 0) + { + SortGroupClause *grpcl = makeNode(SortGroupClause); + + grpcl->tleSortGroupRef = maxRef++; + extra->groupClausePartial = lappend(extra->groupClausePartial, grpcl); + add_column_to_pathtarget(extra->partial_target, expr, grpcl->tleSortGroupRef); + } + } +} + +/* + * adjustAggrefForPartial + * Adjust Aggrefs to put them in partial mode + */ +static void +adjustAggrefForPartial(List *exprs) +{ + ListCell *lc; + + foreach(lc, exprs) + { + Aggref *aggref = (Aggref *) lfirst(lc); + + if (IsA(aggref, Aggref)) + { + Aggref *newaggref; + + /* + * We shouldn't need to copy the substructure of the Aggref node, + * but flat-copy the node itself to avoid damaging other trees. + */ + newaggref = makeNode(Aggref); + memcpy(newaggref, aggref, sizeof(Aggref)); + + /* For now, assume serialization is required */ + mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL); + + lfirst(lc) = newaggref; + } + } +} + /* * make_partial_grouping_target * Generate appropriate PathTarget for output of partial aggregate @@ -5430,11 +5523,15 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target) * * grouping_target is the tlist to be emitted by the topmost aggregation step. * havingQual represents the HAVING clause. + * + * Modified PathTarget cannot be used by FDW as-is to deparse this statement. + * So, before modifying PathTarget, setGroupClausePartial generates + * another Pathtarget and another list of SortGroupClauses. */ static PathTarget * make_partial_grouping_target(PlannerInfo *root, PathTarget *grouping_target, - Node *havingQual) + GroupPathExtraData *extra) { PathTarget *partial_target; List *non_group_cols; @@ -5476,8 +5573,8 @@ make_partial_grouping_target(PlannerInfo *root, /* * If there's a HAVING clause, we'll need the Vars/Aggrefs it uses, too. */ - if (havingQual) - non_group_cols = lappend(non_group_cols, havingQual); + if (extra->havingQual) + non_group_cols = lappend(non_group_cols, extra->havingQual); /* * Pull out all the Vars, PlaceHolderVars, and Aggrefs mentioned in @@ -5490,35 +5587,17 @@ make_partial_grouping_target(PlannerInfo *root, PVC_INCLUDE_AGGREGATES | PVC_RECURSE_WINDOWFUNCS | PVC_INCLUDE_PLACEHOLDERS); - + setGroupClausePartial(partial_target, non_group_exprs, root->parse->groupClause, extra); add_new_columns_to_pathtarget(partial_target, non_group_exprs); + add_new_columns_to_pathtarget(extra->partial_target, non_group_exprs); /* * Adjust Aggrefs to put them in partial mode. At this point all Aggrefs * are at the top level of the target list, so we can just scan the list * rather than recursing through the expression trees. */ - foreach(lc, partial_target->exprs) - { - Aggref *aggref = (Aggref *) lfirst(lc); - - if (IsA(aggref, Aggref)) - { - Aggref *newaggref; - - /* - * We shouldn't need to copy the substructure of the Aggref node, - * but flat-copy the node itself to avoid damaging other trees. - */ - newaggref = makeNode(Aggref); - memcpy(newaggref, aggref, sizeof(Aggref)); - - /* For now, assume serialization is required */ - mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL); - - lfirst(lc) = newaggref; - } - } + adjustAggrefForPartial(partial_target->exprs); + adjustAggrefForPartial(extra->partial_target->exprs); /* clean up cruft */ list_free(non_group_exprs); @@ -7148,7 +7227,7 @@ create_partial_grouping_paths(PlannerInfo *root, */ partially_grouped_rel->reltarget = make_partial_grouping_target(root, grouped_rel->reltarget, - extra->havingQual); + extra); if (!extra->partial_costs_set) { diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 7afdbf4d9d..ec9ca8e7f4 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -13909,6 +13909,7 @@ dumpAgg(Archive *fout, const AggInfo *agginfo) const char *aggmtransfn; const char *aggminvtransfn; const char *aggmfinalfn; + const char *aggpartialfn; bool aggfinalextra; bool aggmfinalextra; char aggfinalmodify; @@ -13991,11 +13992,18 @@ dumpAgg(Archive *fout, const AggInfo *agginfo) if (fout->remoteVersion >= 110000) appendPQExpBufferStr(query, "aggfinalmodify,\n" - "aggmfinalmodify\n"); + "aggmfinalmodify,\n"); else appendPQExpBufferStr(query, "'0' AS aggfinalmodify,\n" - "'0' AS aggmfinalmodify\n"); + "'0' AS aggmfinalmodify,\n"); + + if (fout->remoteVersion >= 170000) + appendPQExpBufferStr(query, + "aggpartialfn\n"); + else + appendPQExpBufferStr(query, + "'-' AS aggpartialfn\n"); appendPQExpBufferStr(query, "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " @@ -14021,6 +14029,7 @@ dumpAgg(Archive *fout, const AggInfo *agginfo) aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn")); aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn")); aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn")); + aggpartialfn = PQgetvalue(res, 0, PQfnumber(res, "aggpartialfn")); aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn")); aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn")); aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn")); @@ -14110,6 +14119,9 @@ dumpAgg(Archive *fout, const AggInfo *agginfo) if (strcmp(aggdeserialfn, "-") != 0) appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn); + if (strcmp(aggpartialfn, "-") != 0) + appendPQExpBuffer(details, ",\n AGGPARTIALFUNC = %s", aggpartialfn); + if (strcmp(aggmtransfn, "-") != 0) { appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s", diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat index 1bc1d97d74..dd97b4dc34 100644 --- a/src/include/catalog/pg_aggregate.dat +++ b/src/include/catalog/pg_aggregate.dat @@ -13,22 +13,44 @@ [ # avg +{ aggfnoid => 'avg_p_int8', aggtransfn => 'int8_avg_accum', + aggfinalfn => 'int8_avg_serialize', aggcombinefn => 'int8_avg_combine', + aggserialfn => 'int8_avg_serialize', aggdeserialfn => 'int8_avg_deserialize', + aggtranstype => 'internal', aggtransspace => '48', + aggpartialfn => 'avg_p_int8' }, { aggfnoid => 'avg(int8)', aggtransfn => 'int8_avg_accum', aggfinalfn => 'numeric_poly_avg', aggcombinefn => 'int8_avg_combine', aggserialfn => 'int8_avg_serialize', aggdeserialfn => 'int8_avg_deserialize', aggmtransfn => 'int8_avg_accum', aggminvtransfn => 'int8_avg_accum_inv', aggmfinalfn => 'numeric_poly_avg', aggtranstype => 'internal', - aggtransspace => '48', aggmtranstype => 'internal', aggmtransspace => '48' }, + aggtransspace => '48', aggmtranstype => 'internal', aggmtransspace => '48', + aggpartialfn => 'avg_p_int8' }, +{ aggfnoid => 'avg_p_int4', aggtransfn => 'int4_avg_accum', + aggcombinefn => 'int4_avg_combine', aggtranstype => '_int8', + agginitval => '{0,0}', + aggpartialfn => 'avg_p_int4' }, { aggfnoid => 'avg(int4)', aggtransfn => 'int4_avg_accum', aggfinalfn => 'int8_avg', aggcombinefn => 'int4_avg_combine', aggmtransfn => 'int4_avg_accum', aggminvtransfn => 'int4_avg_accum_inv', aggmfinalfn => 'int8_avg', aggtranstype => '_int8', aggmtranstype => '_int8', - agginitval => '{0,0}', aggminitval => '{0,0}' }, + agginitval => '{0,0}', aggminitval => '{0,0}', + aggpartialfn => 'avg_p_int4' }, +{ aggfnoid => 'avg_p_int2', aggtransfn => 'int2_avg_accum', + aggcombinefn => 'int4_avg_combine', aggtranstype => '_int8', + agginitval => '{0,0}', + aggpartialfn => 'avg_p_int2' }, { aggfnoid => 'avg(int2)', aggtransfn => 'int2_avg_accum', aggfinalfn => 'int8_avg', aggcombinefn => 'int4_avg_combine', aggmtransfn => 'int2_avg_accum', aggminvtransfn => 'int2_avg_accum_inv', aggmfinalfn => 'int8_avg', aggtranstype => '_int8', aggmtranstype => '_int8', - agginitval => '{0,0}', aggminitval => '{0,0}' }, + agginitval => '{0,0}', aggminitval => '{0,0}', + aggpartialfn => 'avg_p_int2' }, +{ aggfnoid => 'avg_p_numeric', aggtransfn => 'numeric_avg_accum', + aggfinalfn => 'numeric_avg_serialize', aggcombinefn => 'numeric_avg_combine', + aggserialfn => 'numeric_avg_serialize', + aggdeserialfn => 'numeric_avg_deserialize', aggtranstype => 'internal', + aggtransspace => '128', + aggpartialfn => 'avg_p_numeric' }, { aggfnoid => 'avg(numeric)', aggtransfn => 'numeric_avg_accum', aggfinalfn => 'numeric_avg', aggcombinefn => 'numeric_avg_combine', aggserialfn => 'numeric_avg_serialize', @@ -36,46 +58,80 @@ aggmtransfn => 'numeric_avg_accum', aggminvtransfn => 'numeric_accum_inv', aggmfinalfn => 'numeric_avg', aggtranstype => 'internal', aggtransspace => '128', aggmtranstype => 'internal', - aggmtransspace => '128' }, + aggmtransspace => '128', + aggpartialfn => 'avg_p_numeric' }, +{ aggfnoid => 'avg_p_float4', aggtransfn => 'float4_accum', + aggcombinefn => 'float8_combine', aggtranstype => '_float8', + agginitval => '{0,0,0}', + aggpartialfn => 'avg_p_float4' }, { aggfnoid => 'avg(float4)', aggtransfn => 'float4_accum', aggfinalfn => 'float8_avg', aggcombinefn => 'float8_combine', - aggtranstype => '_float8', agginitval => '{0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0}', + aggpartialfn => 'avg_p_float4' }, +{ aggfnoid => 'avg_p_float8', aggtransfn => 'float8_accum', + aggcombinefn => 'float8_combine', aggtranstype => '_float8', + agginitval => '{0,0,0}', + aggpartialfn => 'avg_p_float8' }, { aggfnoid => 'avg(float8)', aggtransfn => 'float8_accum', aggfinalfn => 'float8_avg', aggcombinefn => 'float8_combine', - aggtranstype => '_float8', agginitval => '{0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0}', + aggpartialfn => 'avg_p_float8' }, +{ aggfnoid => 'avg_p_interval', aggtransfn => 'interval_accum', + aggcombinefn => 'interval_combine', aggtranstype => '_interval', + agginitval => '{0 second,0 second}', + aggpartialfn => 'avg_p_interval' }, { aggfnoid => 'avg(interval)', aggtransfn => 'interval_accum', aggfinalfn => 'interval_avg', aggcombinefn => 'interval_combine', aggmtransfn => 'interval_accum', aggminvtransfn => 'interval_accum_inv', aggmfinalfn => 'interval_avg', aggtranstype => '_interval', aggmtranstype => '_interval', agginitval => '{0 second,0 second}', - aggminitval => '{0 second,0 second}' }, + aggminitval => '{0 second,0 second}', + aggpartialfn => 'avg_p_interval' }, # sum +{ aggfnoid => 'sum_p_int8', aggtransfn => 'int8_avg_accum', + aggfinalfn => 'int8_avg_serialize', aggcombinefn => 'int8_avg_combine', + aggserialfn => 'int8_avg_serialize', aggdeserialfn => 'int8_avg_deserialize', + aggtranstype => 'internal', aggtransspace => '48', + aggpartialfn => 'sum_p_int8' }, { aggfnoid => 'sum(int8)', aggtransfn => 'int8_avg_accum', aggfinalfn => 'numeric_poly_sum', aggcombinefn => 'int8_avg_combine', aggserialfn => 'int8_avg_serialize', aggdeserialfn => 'int8_avg_deserialize', aggmtransfn => 'int8_avg_accum', aggminvtransfn => 'int8_avg_accum_inv', aggmfinalfn => 'numeric_poly_sum', aggtranstype => 'internal', - aggtransspace => '48', aggmtranstype => 'internal', aggmtransspace => '48' }, + aggtransspace => '48', aggmtranstype => 'internal', aggmtransspace => '48', + aggpartialfn => 'sum_p_int8' }, { aggfnoid => 'sum(int4)', aggtransfn => 'int4_sum', aggcombinefn => 'int8pl', aggmtransfn => 'int4_avg_accum', aggminvtransfn => 'int4_avg_accum_inv', aggmfinalfn => 'int2int4_sum', aggtranstype => 'int8', - aggmtranstype => '_int8', aggminitval => '{0,0}' }, + aggmtranstype => '_int8', aggminitval => '{0,0}', + aggpartialfn => 'sum(int4)' }, { aggfnoid => 'sum(int2)', aggtransfn => 'int2_sum', aggcombinefn => 'int8pl', aggmtransfn => 'int2_avg_accum', aggminvtransfn => 'int2_avg_accum_inv', aggmfinalfn => 'int2int4_sum', aggtranstype => 'int8', - aggmtranstype => '_int8', aggminitval => '{0,0}' }, + aggmtranstype => '_int8', aggminitval => '{0,0}', + aggpartialfn => 'sum(int2)' }, { aggfnoid => 'sum(float4)', aggtransfn => 'float4pl', - aggcombinefn => 'float4pl', aggtranstype => 'float4' }, + aggcombinefn => 'float4pl', aggtranstype => 'float4', + aggpartialfn => 'sum(float4)' }, { aggfnoid => 'sum(float8)', aggtransfn => 'float8pl', - aggcombinefn => 'float8pl', aggtranstype => 'float8' }, + aggcombinefn => 'float8pl', aggtranstype => 'float8', + aggpartialfn => 'sum(float8)' }, { aggfnoid => 'sum(money)', aggtransfn => 'cash_pl', aggcombinefn => 'cash_pl', aggmtransfn => 'cash_pl', aggminvtransfn => 'cash_mi', - aggtranstype => 'money', aggmtranstype => 'money' }, + aggtranstype => 'money', aggmtranstype => 'money', + aggpartialfn => 'sum(money)' }, { aggfnoid => 'sum(interval)', aggtransfn => 'interval_pl', aggcombinefn => 'interval_pl', aggmtransfn => 'interval_pl', aggminvtransfn => 'interval_mi', aggtranstype => 'interval', - aggmtranstype => 'interval' }, + aggmtranstype => 'interval', + aggpartialfn => 'sum(interval)' }, +{ aggfnoid => 'sum_p_numeric', aggtransfn => 'numeric_avg_accum', + aggfinalfn => 'numeric_avg_serialize', aggcombinefn => 'numeric_avg_combine', + aggserialfn => 'numeric_avg_serialize', + aggdeserialfn => 'numeric_avg_deserialize', aggtranstype => 'internal', + aggtransspace => '128', + aggpartialfn => 'sum_p_numeric' }, { aggfnoid => 'sum(numeric)', aggtransfn => 'numeric_avg_accum', aggfinalfn => 'numeric_sum', aggcombinefn => 'numeric_avg_combine', aggserialfn => 'numeric_avg_serialize', @@ -83,269 +139,436 @@ aggmtransfn => 'numeric_avg_accum', aggminvtransfn => 'numeric_accum_inv', aggmfinalfn => 'numeric_sum', aggtranstype => 'internal', aggtransspace => '128', aggmtranstype => 'internal', - aggmtransspace => '128' }, + aggmtransspace => '128', + aggpartialfn => 'sum_p_numeric' }, # max { aggfnoid => 'max(int8)', aggtransfn => 'int8larger', aggcombinefn => 'int8larger', aggsortop => '>(int8,int8)', - aggtranstype => 'int8' }, + aggtranstype => 'int8', + aggpartialfn => 'max(int8)' }, { aggfnoid => 'max(int4)', aggtransfn => 'int4larger', aggcombinefn => 'int4larger', aggsortop => '>(int4,int4)', - aggtranstype => 'int4' }, + aggtranstype => 'int4', + aggpartialfn => 'max(int4)' }, { aggfnoid => 'max(int2)', aggtransfn => 'int2larger', aggcombinefn => 'int2larger', aggsortop => '>(int2,int2)', - aggtranstype => 'int2' }, + aggtranstype => 'int2', + aggpartialfn => 'max(int2)' }, { aggfnoid => 'max(oid)', aggtransfn => 'oidlarger', aggcombinefn => 'oidlarger', aggsortop => '>(oid,oid)', - aggtranstype => 'oid' }, + aggtranstype => 'oid', + aggpartialfn => 'max(oid)' }, { aggfnoid => 'max(float4)', aggtransfn => 'float4larger', aggcombinefn => 'float4larger', aggsortop => '>(float4,float4)', - aggtranstype => 'float4' }, + aggtranstype => 'float4', + aggpartialfn => 'max(float4)' }, { aggfnoid => 'max(float8)', aggtransfn => 'float8larger', aggcombinefn => 'float8larger', aggsortop => '>(float8,float8)', - aggtranstype => 'float8' }, + aggtranstype => 'float8', + aggpartialfn => 'max(float8)' }, { aggfnoid => 'max(date)', aggtransfn => 'date_larger', aggcombinefn => 'date_larger', aggsortop => '>(date,date)', - aggtranstype => 'date' }, + aggtranstype => 'date', + aggpartialfn => 'max(date)' }, { aggfnoid => 'max(time)', aggtransfn => 'time_larger', aggcombinefn => 'time_larger', aggsortop => '>(time,time)', - aggtranstype => 'time' }, + aggtranstype => 'time', + aggpartialfn => 'max(time)' }, { aggfnoid => 'max(timetz)', aggtransfn => 'timetz_larger', aggcombinefn => 'timetz_larger', aggsortop => '>(timetz,timetz)', - aggtranstype => 'timetz' }, + aggtranstype => 'timetz', + aggpartialfn => 'max(timetz)' }, { aggfnoid => 'max(money)', aggtransfn => 'cashlarger', aggcombinefn => 'cashlarger', aggsortop => '>(money,money)', - aggtranstype => 'money' }, + aggtranstype => 'money', + aggpartialfn => 'max(money)' }, { aggfnoid => 'max(timestamp)', aggtransfn => 'timestamp_larger', aggcombinefn => 'timestamp_larger', aggsortop => '>(timestamp,timestamp)', - aggtranstype => 'timestamp' }, + aggtranstype => 'timestamp', + aggpartialfn => 'max(timestamp)' }, { aggfnoid => 'max(timestamptz)', aggtransfn => 'timestamptz_larger', aggcombinefn => 'timestamptz_larger', - aggsortop => '>(timestamptz,timestamptz)', aggtranstype => 'timestamptz' }, + aggsortop => '>(timestamptz,timestamptz)', aggtranstype => 'timestamptz', + aggpartialfn => 'max(timestamptz)' }, { aggfnoid => 'max(interval)', aggtransfn => 'interval_larger', aggcombinefn => 'interval_larger', aggsortop => '>(interval,interval)', - aggtranstype => 'interval' }, + aggtranstype => 'interval', + aggpartialfn => 'max(interval)' }, { aggfnoid => 'max(text)', aggtransfn => 'text_larger', aggcombinefn => 'text_larger', aggsortop => '>(text,text)', - aggtranstype => 'text' }, + aggtranstype => 'text', + aggpartialfn => 'max(text)' }, { aggfnoid => 'max(numeric)', aggtransfn => 'numeric_larger', aggcombinefn => 'numeric_larger', aggsortop => '>(numeric,numeric)', - aggtranstype => 'numeric' }, + aggtranstype => 'numeric', + aggpartialfn => 'max(numeric)' }, { aggfnoid => 'max(anyarray)', aggtransfn => 'array_larger', aggcombinefn => 'array_larger', aggsortop => '>(anyarray,anyarray)', - aggtranstype => 'anyarray' }, + aggtranstype => 'anyarray', + aggpartialfn => 'max(anyarray)' }, { aggfnoid => 'max(bpchar)', aggtransfn => 'bpchar_larger', aggcombinefn => 'bpchar_larger', aggsortop => '>(bpchar,bpchar)', - aggtranstype => 'bpchar' }, + aggtranstype => 'bpchar', + aggpartialfn => 'max(bpchar)' }, { aggfnoid => 'max(tid)', aggtransfn => 'tidlarger', aggcombinefn => 'tidlarger', aggsortop => '>(tid,tid)', - aggtranstype => 'tid' }, + aggtranstype => 'tid', + aggpartialfn => 'max(tid)' }, { aggfnoid => 'max(anyenum)', aggtransfn => 'enum_larger', aggcombinefn => 'enum_larger', aggsortop => '>(anyenum,anyenum)', - aggtranstype => 'anyenum' }, + aggtranstype => 'anyenum', + aggpartialfn => 'max(anyenum)' }, { aggfnoid => 'max(inet)', aggtransfn => 'network_larger', aggcombinefn => 'network_larger', aggsortop => '>(inet,inet)', - aggtranstype => 'inet' }, + aggtranstype => 'inet', + aggpartialfn => 'max(inet)' }, { aggfnoid => 'max(pg_lsn)', aggtransfn => 'pg_lsn_larger', aggcombinefn => 'pg_lsn_larger', aggsortop => '>(pg_lsn,pg_lsn)', - aggtranstype => 'pg_lsn' }, + aggtranstype => 'pg_lsn', + aggpartialfn => 'max(pg_lsn)' }, { aggfnoid => 'max(xid8)', aggtransfn => 'xid8_larger', aggcombinefn => 'xid8_larger', aggsortop => '>(xid8,xid8)', - aggtranstype => 'xid8' }, + aggtranstype => 'xid8', + aggpartialfn => 'max(xid8)' }, # min { aggfnoid => 'min(int8)', aggtransfn => 'int8smaller', aggcombinefn => 'int8smaller', aggsortop => '<(int8,int8)', - aggtranstype => 'int8' }, + aggtranstype => 'int8', + aggpartialfn => 'min(int8)' }, { aggfnoid => 'min(int4)', aggtransfn => 'int4smaller', aggcombinefn => 'int4smaller', aggsortop => '<(int4,int4)', - aggtranstype => 'int4' }, + aggtranstype => 'int4', + aggpartialfn => 'min(int4)' }, { aggfnoid => 'min(int2)', aggtransfn => 'int2smaller', aggcombinefn => 'int2smaller', aggsortop => '<(int2,int2)', - aggtranstype => 'int2' }, + aggtranstype => 'int2', + aggpartialfn => 'min(int2)' }, { aggfnoid => 'min(oid)', aggtransfn => 'oidsmaller', aggcombinefn => 'oidsmaller', aggsortop => '<(oid,oid)', - aggtranstype => 'oid' }, + aggtranstype => 'oid', + aggpartialfn => 'min(oid)' }, { aggfnoid => 'min(float4)', aggtransfn => 'float4smaller', aggcombinefn => 'float4smaller', aggsortop => '<(float4,float4)', - aggtranstype => 'float4' }, + aggtranstype => 'float4', + aggpartialfn => 'min(float4)' }, { aggfnoid => 'min(float8)', aggtransfn => 'float8smaller', aggcombinefn => 'float8smaller', aggsortop => '<(float8,float8)', - aggtranstype => 'float8' }, + aggtranstype => 'float8', + aggpartialfn => 'min(float8)' }, { aggfnoid => 'min(date)', aggtransfn => 'date_smaller', aggcombinefn => 'date_smaller', aggsortop => '<(date,date)', - aggtranstype => 'date' }, + aggtranstype => 'date', + aggpartialfn => 'min(date)' }, { aggfnoid => 'min(time)', aggtransfn => 'time_smaller', aggcombinefn => 'time_smaller', aggsortop => '<(time,time)', - aggtranstype => 'time' }, + aggtranstype => 'time', + aggpartialfn => 'min(time)' }, { aggfnoid => 'min(timetz)', aggtransfn => 'timetz_smaller', aggcombinefn => 'timetz_smaller', aggsortop => '<(timetz,timetz)', - aggtranstype => 'timetz' }, + aggtranstype => 'timetz', + aggpartialfn => 'min(timetz)' }, { aggfnoid => 'min(money)', aggtransfn => 'cashsmaller', aggcombinefn => 'cashsmaller', aggsortop => '<(money,money)', - aggtranstype => 'money' }, + aggtranstype => 'money', + aggpartialfn => 'min(money)' }, { aggfnoid => 'min(timestamp)', aggtransfn => 'timestamp_smaller', aggcombinefn => 'timestamp_smaller', aggsortop => '<(timestamp,timestamp)', - aggtranstype => 'timestamp' }, + aggtranstype => 'timestamp', + aggpartialfn => 'min(timestamp)' }, { aggfnoid => 'min(timestamptz)', aggtransfn => 'timestamptz_smaller', aggcombinefn => 'timestamptz_smaller', - aggsortop => '<(timestamptz,timestamptz)', aggtranstype => 'timestamptz' }, + aggsortop => '<(timestamptz,timestamptz)', aggtranstype => 'timestamptz', + aggpartialfn => 'min(timestamptz)' }, { aggfnoid => 'min(interval)', aggtransfn => 'interval_smaller', aggcombinefn => 'interval_smaller', aggsortop => '<(interval,interval)', - aggtranstype => 'interval' }, + aggtranstype => 'interval', + aggpartialfn => 'min(interval)' }, { aggfnoid => 'min(text)', aggtransfn => 'text_smaller', aggcombinefn => 'text_smaller', aggsortop => '<(text,text)', - aggtranstype => 'text' }, + aggtranstype => 'text', + aggpartialfn => 'min(text)' }, { aggfnoid => 'min(numeric)', aggtransfn => 'numeric_smaller', aggcombinefn => 'numeric_smaller', aggsortop => '<(numeric,numeric)', - aggtranstype => 'numeric' }, + aggtranstype => 'numeric', + aggpartialfn => 'min(numeric)' }, { aggfnoid => 'min(anyarray)', aggtransfn => 'array_smaller', aggcombinefn => 'array_smaller', aggsortop => '<(anyarray,anyarray)', - aggtranstype => 'anyarray' }, + aggtranstype => 'anyarray', + aggpartialfn => 'min(anyarray)' }, { aggfnoid => 'min(bpchar)', aggtransfn => 'bpchar_smaller', aggcombinefn => 'bpchar_smaller', aggsortop => '<(bpchar,bpchar)', - aggtranstype => 'bpchar' }, + aggtranstype => 'bpchar', + aggpartialfn => 'min(bpchar)' }, { aggfnoid => 'min(tid)', aggtransfn => 'tidsmaller', aggcombinefn => 'tidsmaller', aggsortop => '<(tid,tid)', - aggtranstype => 'tid' }, + aggtranstype => 'tid', + aggpartialfn => 'min(tid)' }, { aggfnoid => 'min(anyenum)', aggtransfn => 'enum_smaller', aggcombinefn => 'enum_smaller', aggsortop => '<(anyenum,anyenum)', - aggtranstype => 'anyenum' }, + aggtranstype => 'anyenum', + aggpartialfn => 'min(anyenum)' }, { aggfnoid => 'min(inet)', aggtransfn => 'network_smaller', aggcombinefn => 'network_smaller', aggsortop => '<(inet,inet)', - aggtranstype => 'inet' }, + aggtranstype => 'inet', + aggpartialfn => 'min(inet)' }, { aggfnoid => 'min(pg_lsn)', aggtransfn => 'pg_lsn_smaller', aggcombinefn => 'pg_lsn_smaller', aggsortop => '<(pg_lsn,pg_lsn)', - aggtranstype => 'pg_lsn' }, + aggtranstype => 'pg_lsn', + aggpartialfn => 'min(pg_lsn)' }, { aggfnoid => 'min(xid8)', aggtransfn => 'xid8_smaller', aggcombinefn => 'xid8_smaller', aggsortop => '<(xid8,xid8)', - aggtranstype => 'xid8' }, + aggtranstype => 'xid8', + aggpartialfn => 'min(xid8)' }, # count { aggfnoid => 'count(any)', aggtransfn => 'int8inc_any', aggcombinefn => 'int8pl', aggmtransfn => 'int8inc_any', aggminvtransfn => 'int8dec_any', aggtranstype => 'int8', - aggmtranstype => 'int8', agginitval => '0', aggminitval => '0' }, + aggmtranstype => 'int8', agginitval => '0', aggminitval => '0', + aggpartialfn => 'count(any)' }, { aggfnoid => 'count()', aggtransfn => 'int8inc', aggcombinefn => 'int8pl', aggmtransfn => 'int8inc', aggminvtransfn => 'int8dec', aggtranstype => 'int8', - aggmtranstype => 'int8', agginitval => '0', aggminitval => '0' }, + aggmtranstype => 'int8', agginitval => '0', aggminitval => '0', + aggpartialfn => 'count()' }, # var_pop +{ aggfnoid => 'var_pop_p_int8', aggtransfn => 'int8_accum', + aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine', + aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', + aggtranstype => 'internal', aggtransspace => '128', + aggpartialfn => 'var_pop_p_int8' }, { aggfnoid => 'var_pop(int8)', aggtransfn => 'int8_accum', aggfinalfn => 'numeric_var_pop', aggcombinefn => 'numeric_combine', aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', aggmtransfn => 'int8_accum', aggminvtransfn => 'int8_accum_inv', aggmfinalfn => 'numeric_var_pop', aggtranstype => 'internal', aggtransspace => '128', aggmtranstype => 'internal', - aggmtransspace => '128' }, + aggmtransspace => '128', + aggpartialfn => 'var_pop_p_int8' }, +{ aggfnoid => 'var_pop_p_int4', aggtransfn => 'int4_accum', + aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine', + aggserialfn => 'numeric_poly_serialize', + aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal', + aggtransspace => '48', + aggpartialfn => 'var_pop_p_int4' }, { aggfnoid => 'var_pop(int4)', aggtransfn => 'int4_accum', aggfinalfn => 'numeric_poly_var_pop', aggcombinefn => 'numeric_poly_combine', aggserialfn => 'numeric_poly_serialize', aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int4_accum', aggminvtransfn => 'int4_accum_inv', aggmfinalfn => 'numeric_poly_var_pop', aggtranstype => 'internal', aggtransspace => '48', - aggmtranstype => 'internal', aggmtransspace => '48' }, + aggmtranstype => 'internal', aggmtransspace => '48', + aggpartialfn => 'var_pop_p_int4' }, +{ aggfnoid => 'var_pop_p_int2', aggtransfn => 'int2_accum', + aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine', + aggserialfn => 'numeric_poly_serialize', + aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal', + aggtransspace => '48', + aggpartialfn => 'var_pop_p_int2' }, { aggfnoid => 'var_pop(int2)', aggtransfn => 'int2_accum', aggfinalfn => 'numeric_poly_var_pop', aggcombinefn => 'numeric_poly_combine', aggserialfn => 'numeric_poly_serialize', aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int2_accum', aggminvtransfn => 'int2_accum_inv', aggmfinalfn => 'numeric_poly_var_pop', aggtranstype => 'internal', aggtransspace => '48', - aggmtranstype => 'internal', aggmtransspace => '48' }, + aggmtranstype => 'internal', aggmtransspace => '48', + aggpartialfn => 'var_pop_p_int2' }, +{ aggfnoid => 'var_pop_p_float4', aggtransfn => 'float4_accum', + aggcombinefn => 'float8_combine', aggtranstype => '_float8', + agginitval => '{0,0,0}', + aggpartialfn => 'var_pop_p_float4' }, { aggfnoid => 'var_pop(float4)', aggtransfn => 'float4_accum', aggfinalfn => 'float8_var_pop', aggcombinefn => 'float8_combine', - aggtranstype => '_float8', agginitval => '{0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0}', + aggpartialfn => 'var_pop_p_float4' }, +{ aggfnoid => 'var_pop_p_float8', aggtransfn => 'float8_accum', + aggcombinefn => 'float8_combine', aggtranstype => '_float8', + agginitval => '{0,0,0}', + aggpartialfn => 'var_pop_p_float8' }, { aggfnoid => 'var_pop(float8)', aggtransfn => 'float8_accum', aggfinalfn => 'float8_var_pop', aggcombinefn => 'float8_combine', - aggtranstype => '_float8', agginitval => '{0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0}', + aggpartialfn => 'var_pop_p_float8' }, +{ aggfnoid => 'var_pop_p_numeric', aggtransfn => 'numeric_accum', + aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine', + aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', + aggtranstype => 'internal', aggtransspace => '128', + aggpartialfn => 'var_pop_p_numeric' }, { aggfnoid => 'var_pop(numeric)', aggtransfn => 'numeric_accum', aggfinalfn => 'numeric_var_pop', aggcombinefn => 'numeric_combine', aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', aggmtransfn => 'numeric_accum', aggminvtransfn => 'numeric_accum_inv', aggmfinalfn => 'numeric_var_pop', aggtranstype => 'internal', aggtransspace => '128', aggmtranstype => 'internal', - aggmtransspace => '128' }, + aggmtransspace => '128', + aggpartialfn => 'var_pop_p_numeric' }, # var_samp +{ aggfnoid => 'var_samp_p_int8', aggtransfn => 'int8_accum', + aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine', + aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', + aggtranstype => 'internal', aggtransspace => '128', + aggpartialfn => 'var_samp_p_int8' }, { aggfnoid => 'var_samp(int8)', aggtransfn => 'int8_accum', aggfinalfn => 'numeric_var_samp', aggcombinefn => 'numeric_combine', aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', aggmtransfn => 'int8_accum', aggminvtransfn => 'int8_accum_inv', aggmfinalfn => 'numeric_var_samp', aggtranstype => 'internal', aggtransspace => '128', aggmtranstype => 'internal', - aggmtransspace => '128' }, + aggmtransspace => '128', + aggpartialfn => 'var_samp_p_int8' }, +{ aggfnoid => 'var_samp_p_int4', aggtransfn => 'int4_accum', + aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine', + aggserialfn => 'numeric_poly_serialize', + aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal', + aggtransspace => '48', + aggpartialfn => 'var_samp_p_int4' }, { aggfnoid => 'var_samp(int4)', aggtransfn => 'int4_accum', aggfinalfn => 'numeric_poly_var_samp', aggcombinefn => 'numeric_poly_combine', aggserialfn => 'numeric_poly_serialize', aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int4_accum', aggminvtransfn => 'int4_accum_inv', aggmfinalfn => 'numeric_poly_var_samp', aggtranstype => 'internal', aggtransspace => '48', - aggmtranstype => 'internal', aggmtransspace => '48' }, + aggmtranstype => 'internal', aggmtransspace => '48', + aggpartialfn => 'var_samp_p_int4' }, +{ aggfnoid => 'var_samp_p_int2', aggtransfn => 'int2_accum', + aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine', + aggserialfn => 'numeric_poly_serialize', + aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal', + aggtransspace => '48', + aggpartialfn => 'var_samp_p_int2' }, { aggfnoid => 'var_samp(int2)', aggtransfn => 'int2_accum', aggfinalfn => 'numeric_poly_var_samp', aggcombinefn => 'numeric_poly_combine', aggserialfn => 'numeric_poly_serialize', aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int2_accum', aggminvtransfn => 'int2_accum_inv', aggmfinalfn => 'numeric_poly_var_samp', aggtranstype => 'internal', aggtransspace => '48', - aggmtranstype => 'internal', aggmtransspace => '48' }, + aggmtranstype => 'internal', aggmtransspace => '48', + aggpartialfn => 'var_samp_p_int2' }, +{ aggfnoid => 'var_samp_p_float4', aggtransfn => 'float4_accum', + aggcombinefn => 'float8_combine', aggtranstype => '_float8', + agginitval => '{0,0,0}', + aggpartialfn => 'var_samp_p_float4' }, { aggfnoid => 'var_samp(float4)', aggtransfn => 'float4_accum', aggfinalfn => 'float8_var_samp', aggcombinefn => 'float8_combine', - aggtranstype => '_float8', agginitval => '{0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0}', + aggpartialfn => 'var_samp_p_float4' }, +{ aggfnoid => 'var_samp_p_float8', aggtransfn => 'float8_accum', + aggcombinefn => 'float8_combine', aggtranstype => '_float8', + agginitval => '{0,0,0}', + aggpartialfn => 'var_samp_p_float8' }, { aggfnoid => 'var_samp(float8)', aggtransfn => 'float8_accum', aggfinalfn => 'float8_var_samp', aggcombinefn => 'float8_combine', - aggtranstype => '_float8', agginitval => '{0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0}', + aggpartialfn => 'var_samp_p_float8' }, +{ aggfnoid => 'var_samp_p_numeric', aggtransfn => 'numeric_accum', + aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine', + aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', + aggtranstype => 'internal', aggtransspace => '128', + aggpartialfn => 'var_samp_p_numeric' }, { aggfnoid => 'var_samp(numeric)', aggtransfn => 'numeric_accum', aggfinalfn => 'numeric_var_samp', aggcombinefn => 'numeric_combine', aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', aggmtransfn => 'numeric_accum', aggminvtransfn => 'numeric_accum_inv', aggmfinalfn => 'numeric_var_samp', aggtranstype => 'internal', aggtransspace => '128', aggmtranstype => 'internal', - aggmtransspace => '128' }, + aggmtransspace => '128', + aggpartialfn => 'var_samp_p_numeric' }, # variance: historical Postgres syntax for var_samp +{ aggfnoid => 'variance_p_int8', aggtransfn => 'int8_accum', + aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine', + aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', + aggtranstype => 'internal', aggtransspace => '128', + aggpartialfn => 'variance_p_int8' }, { aggfnoid => 'variance(int8)', aggtransfn => 'int8_accum', aggfinalfn => 'numeric_var_samp', aggcombinefn => 'numeric_combine', aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', aggmtransfn => 'int8_accum', aggminvtransfn => 'int8_accum_inv', aggmfinalfn => 'numeric_var_samp', aggtranstype => 'internal', aggtransspace => '128', aggmtranstype => 'internal', - aggmtransspace => '128' }, + aggmtransspace => '128', + aggpartialfn => 'variance_p_int8' }, +{ aggfnoid => 'variance_p_int4', aggtransfn => 'int4_accum', + aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine', + aggserialfn => 'numeric_poly_serialize', + aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal', + aggtransspace => '48', + aggpartialfn => 'variance_p_int4' }, { aggfnoid => 'variance(int4)', aggtransfn => 'int4_accum', aggfinalfn => 'numeric_poly_var_samp', aggcombinefn => 'numeric_poly_combine', aggserialfn => 'numeric_poly_serialize', aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int4_accum', aggminvtransfn => 'int4_accum_inv', aggmfinalfn => 'numeric_poly_var_samp', aggtranstype => 'internal', aggtransspace => '48', - aggmtranstype => 'internal', aggmtransspace => '48' }, + aggmtranstype => 'internal', aggmtransspace => '48', + aggpartialfn => 'variance_p_int4' }, +{ aggfnoid => 'variance_p_int2', aggtransfn => 'int2_accum', + aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine', + aggserialfn => 'numeric_poly_serialize', + aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal', + aggtransspace => '48', + aggpartialfn => 'variance_p_int2' }, { aggfnoid => 'variance(int2)', aggtransfn => 'int2_accum', aggfinalfn => 'numeric_poly_var_samp', aggcombinefn => 'numeric_poly_combine', aggserialfn => 'numeric_poly_serialize', aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int2_accum', aggminvtransfn => 'int2_accum_inv', aggmfinalfn => 'numeric_poly_var_samp', aggtranstype => 'internal', aggtransspace => '48', - aggmtranstype => 'internal', aggmtransspace => '48' }, + aggmtranstype => 'internal', aggmtransspace => '48', + aggpartialfn => 'variance_p_int2' }, +{ aggfnoid => 'variance_p_float4', aggtransfn => 'float4_accum', + aggcombinefn => 'float8_combine', aggtranstype => '_float8', + agginitval => '{0,0,0}', + aggpartialfn => 'variance_p_float4' }, { aggfnoid => 'variance(float4)', aggtransfn => 'float4_accum', aggfinalfn => 'float8_var_samp', aggcombinefn => 'float8_combine', - aggtranstype => '_float8', agginitval => '{0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0}', + aggpartialfn => 'variance_p_float4' }, +{ aggfnoid => 'variance_p_float8', aggtransfn => 'float8_accum', + aggcombinefn => 'float8_combine', aggtranstype => '_float8', + agginitval => '{0,0,0}', + aggpartialfn => 'variance_p_float8' }, { aggfnoid => 'variance(float8)', aggtransfn => 'float8_accum', aggfinalfn => 'float8_var_samp', aggcombinefn => 'float8_combine', - aggtranstype => '_float8', agginitval => '{0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0}', + aggpartialfn => 'variance_p_float8' }, +{ aggfnoid => 'variance_p_numeric', aggtransfn => 'numeric_accum', + aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine', + aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', + aggtranstype => 'internal', aggtransspace => '128', + aggpartialfn => 'variance_p_numeric' }, { aggfnoid => 'variance(numeric)', aggtransfn => 'numeric_accum', aggfinalfn => 'numeric_var_samp', aggcombinefn => 'numeric_combine', aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', aggmtransfn => 'numeric_accum', aggminvtransfn => 'numeric_accum_inv', aggmfinalfn => 'numeric_var_samp', aggtranstype => 'internal', aggtransspace => '128', aggmtranstype => 'internal', - aggmtransspace => '128' }, + aggmtransspace => '128', + aggpartialfn => 'variance_p_numeric' }, # stddev_pop +{ aggfnoid => 'stddev_pop_p_int8', aggtransfn => 'int8_accum', + aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine', + aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', + aggtranstype => 'internal', aggtransspace => '128', + aggpartialfn => 'stddev_pop_p_int8' }, { aggfnoid => 'stddev_pop(int8)', aggtransfn => 'int8_accum', aggfinalfn => 'numeric_stddev_pop', aggcombinefn => 'numeric_combine', aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', aggmtransfn => 'int8_accum', aggminvtransfn => 'int8_accum_inv', aggmfinalfn => 'numeric_stddev_pop', aggtranstype => 'internal', aggtransspace => '128', aggmtranstype => 'internal', - aggmtransspace => '128' }, + aggmtransspace => '128', + aggpartialfn => 'stddev_pop_p_int8' }, +{ aggfnoid => 'stddev_pop_p_int4', aggtransfn => 'int4_accum', + aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine', + aggserialfn => 'numeric_poly_serialize', + aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal', + aggtransspace => '48', + aggpartialfn => 'stddev_pop_p_int4' }, { aggfnoid => 'stddev_pop(int4)', aggtransfn => 'int4_accum', aggfinalfn => 'numeric_poly_stddev_pop', aggcombinefn => 'numeric_poly_combine', @@ -353,7 +576,14 @@ aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int4_accum', aggminvtransfn => 'int4_accum_inv', aggmfinalfn => 'numeric_poly_stddev_pop', aggtranstype => 'internal', aggtransspace => '48', - aggmtranstype => 'internal', aggmtransspace => '48' }, + aggmtranstype => 'internal', aggmtransspace => '48', + aggpartialfn => 'stddev_pop_p_int4' }, +{ aggfnoid => 'stddev_pop_p_int2', aggtransfn => 'int2_accum', + aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine', + aggserialfn => 'numeric_poly_serialize', + aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal', + aggtransspace => '48', + aggpartialfn => 'stddev_pop_p_int2' }, { aggfnoid => 'stddev_pop(int2)', aggtransfn => 'int2_accum', aggfinalfn => 'numeric_poly_stddev_pop', aggcombinefn => 'numeric_poly_combine', @@ -361,29 +591,58 @@ aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int2_accum', aggminvtransfn => 'int2_accum_inv', aggmfinalfn => 'numeric_poly_stddev_pop', aggtranstype => 'internal', aggtransspace => '48', - aggmtranstype => 'internal', aggmtransspace => '48' }, + aggmtranstype => 'internal', aggmtransspace => '48', + aggpartialfn => 'stddev_pop_p_int2' }, +{ aggfnoid => 'stddev_pop_p_float4', aggtransfn => 'float4_accum', + aggcombinefn => 'float8_combine', aggtranstype => '_float8', + agginitval => '{0,0,0}', + aggpartialfn => 'stddev_pop_p_float4' }, { aggfnoid => 'stddev_pop(float4)', aggtransfn => 'float4_accum', aggfinalfn => 'float8_stddev_pop', aggcombinefn => 'float8_combine', - aggtranstype => '_float8', agginitval => '{0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0}', + aggpartialfn => 'stddev_pop_p_float4' }, +{ aggfnoid => 'stddev_pop_p_float8', aggtransfn => 'float8_accum', + aggcombinefn => 'float8_combine', aggtranstype => '_float8', + agginitval => '{0,0,0}', + aggpartialfn => 'stddev_pop_p_float8' }, { aggfnoid => 'stddev_pop(float8)', aggtransfn => 'float8_accum', aggfinalfn => 'float8_stddev_pop', aggcombinefn => 'float8_combine', - aggtranstype => '_float8', agginitval => '{0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0}', + aggpartialfn => 'stddev_pop_p_float8' }, +{ aggfnoid => 'stddev_pop_p_numeric', aggtransfn => 'numeric_accum', + aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine', + aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', + aggtranstype => 'internal', aggtransspace => '128', + aggpartialfn => 'stddev_pop_p_numeric' }, { aggfnoid => 'stddev_pop(numeric)', aggtransfn => 'numeric_accum', aggfinalfn => 'numeric_stddev_pop', aggcombinefn => 'numeric_combine', aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', aggmtransfn => 'numeric_accum', aggminvtransfn => 'numeric_accum_inv', aggmfinalfn => 'numeric_stddev_pop', aggtranstype => 'internal', aggtransspace => '128', aggmtranstype => 'internal', - aggmtransspace => '128' }, + aggmtransspace => '128', + aggpartialfn => 'stddev_pop_p_numeric' }, # stddev_samp +{ aggfnoid => 'stddev_samp_p_int8', aggtransfn => 'int8_accum', + aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine', + aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', + aggtranstype => 'internal', aggtransspace => '128', + aggpartialfn => 'stddev_samp_p_int8' }, { aggfnoid => 'stddev_samp(int8)', aggtransfn => 'int8_accum', aggfinalfn => 'numeric_stddev_samp', aggcombinefn => 'numeric_combine', aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', aggmtransfn => 'int8_accum', aggminvtransfn => 'int8_accum_inv', aggmfinalfn => 'numeric_stddev_samp', aggtranstype => 'internal', aggtransspace => '128', aggmtranstype => 'internal', - aggmtransspace => '128' }, + aggmtransspace => '128', + aggpartialfn => 'stddev_samp_p_int8' }, +{ aggfnoid => 'stddev_samp_p_int4', aggtransfn => 'int4_accum', + aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine', + aggserialfn => 'numeric_poly_serialize', + aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal', + aggtransspace => '48', + aggpartialfn => 'stddev_samp_p_int4' }, { aggfnoid => 'stddev_samp(int4)', aggtransfn => 'int4_accum', aggfinalfn => 'numeric_poly_stddev_samp', aggcombinefn => 'numeric_poly_combine', @@ -391,7 +650,14 @@ aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int4_accum', aggminvtransfn => 'int4_accum_inv', aggmfinalfn => 'numeric_poly_stddev_samp', aggtranstype => 'internal', aggtransspace => '48', - aggmtranstype => 'internal', aggmtransspace => '48' }, + aggmtranstype => 'internal', aggmtransspace => '48', + aggpartialfn => 'stddev_samp_p_int4' }, +{ aggfnoid => 'stddev_samp_p_int2', aggtransfn => 'int2_accum', + aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine', + aggserialfn => 'numeric_poly_serialize', + aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal', + aggtransspace => '48', + aggpartialfn => 'stddev_samp_p_int2' }, { aggfnoid => 'stddev_samp(int2)', aggtransfn => 'int2_accum', aggfinalfn => 'numeric_poly_stddev_samp', aggcombinefn => 'numeric_poly_combine', @@ -399,29 +665,58 @@ aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int2_accum', aggminvtransfn => 'int2_accum_inv', aggmfinalfn => 'numeric_poly_stddev_samp', aggtranstype => 'internal', aggtransspace => '48', - aggmtranstype => 'internal', aggmtransspace => '48' }, + aggmtranstype => 'internal', aggmtransspace => '48', + aggpartialfn => 'stddev_samp_p_int2' }, +{ aggfnoid => 'stddev_samp_p_float4', aggtransfn => 'float4_accum', + aggcombinefn => 'float8_combine', aggtranstype => '_float8', + agginitval => '{0,0,0}', + aggpartialfn => 'stddev_samp_p_float4' }, { aggfnoid => 'stddev_samp(float4)', aggtransfn => 'float4_accum', aggfinalfn => 'float8_stddev_samp', aggcombinefn => 'float8_combine', - aggtranstype => '_float8', agginitval => '{0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0}', + aggpartialfn => 'stddev_samp_p_float4' }, +{ aggfnoid => 'stddev_samp_p_float8', aggtransfn => 'float8_accum', + aggcombinefn => 'float8_combine', aggtranstype => '_float8', + agginitval => '{0,0,0}', + aggpartialfn => 'stddev_samp_p_float8' }, { aggfnoid => 'stddev_samp(float8)', aggtransfn => 'float8_accum', aggfinalfn => 'float8_stddev_samp', aggcombinefn => 'float8_combine', - aggtranstype => '_float8', agginitval => '{0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0}', + aggpartialfn => 'stddev_samp_p_float8' }, +{ aggfnoid => 'stddev_samp_p_numeric', aggtransfn => 'numeric_accum', + aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine', + aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', + aggtranstype => 'internal', aggtransspace => '128', + aggpartialfn => 'stddev_samp_p_numeric' }, { aggfnoid => 'stddev_samp(numeric)', aggtransfn => 'numeric_accum', aggfinalfn => 'numeric_stddev_samp', aggcombinefn => 'numeric_combine', aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', aggmtransfn => 'numeric_accum', aggminvtransfn => 'numeric_accum_inv', aggmfinalfn => 'numeric_stddev_samp', aggtranstype => 'internal', aggtransspace => '128', aggmtranstype => 'internal', - aggmtransspace => '128' }, + aggmtransspace => '128', + aggpartialfn => 'stddev_samp_p_numeric' }, # stddev: historical Postgres syntax for stddev_samp +{ aggfnoid => 'stddev_p_int8', aggtransfn => 'int8_accum', + aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine', + aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', + aggtranstype => 'internal', aggtransspace => '128', + aggpartialfn => 'stddev_p_int8' }, { aggfnoid => 'stddev(int8)', aggtransfn => 'int8_accum', aggfinalfn => 'numeric_stddev_samp', aggcombinefn => 'numeric_combine', aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', aggmtransfn => 'int8_accum', aggminvtransfn => 'int8_accum_inv', aggmfinalfn => 'numeric_stddev_samp', aggtranstype => 'internal', aggtransspace => '128', aggmtranstype => 'internal', - aggmtransspace => '128' }, + aggmtransspace => '128', + aggpartialfn => 'stddev_p_int8' }, +{ aggfnoid => 'stddev_p_int4', aggtransfn => 'int4_accum', + aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine', + aggserialfn => 'numeric_poly_serialize', + aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal', + aggtransspace => '48', + aggpartialfn => 'stddev_p_int4' }, { aggfnoid => 'stddev(int4)', aggtransfn => 'int4_accum', aggfinalfn => 'numeric_poly_stddev_samp', aggcombinefn => 'numeric_poly_combine', @@ -429,7 +724,14 @@ aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int4_accum', aggminvtransfn => 'int4_accum_inv', aggmfinalfn => 'numeric_poly_stddev_samp', aggtranstype => 'internal', aggtransspace => '48', - aggmtranstype => 'internal', aggmtransspace => '48' }, + aggmtranstype => 'internal', aggmtransspace => '48', + aggpartialfn => 'stddev_p_int4' }, +{ aggfnoid => 'stddev_p_int2', aggtransfn => 'int2_accum', + aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine', + aggserialfn => 'numeric_poly_serialize', + aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal', + aggtransspace => '48', + aggpartialfn => 'stddev_p_int2' }, { aggfnoid => 'stddev(int2)', aggtransfn => 'int2_accum', aggfinalfn => 'numeric_poly_stddev_samp', aggcombinefn => 'numeric_poly_combine', @@ -437,138 +739,253 @@ aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int2_accum', aggminvtransfn => 'int2_accum_inv', aggmfinalfn => 'numeric_poly_stddev_samp', aggtranstype => 'internal', aggtransspace => '48', - aggmtranstype => 'internal', aggmtransspace => '48' }, + aggmtranstype => 'internal', aggmtransspace => '48', + aggpartialfn => 'stddev_p_int2' }, +{ aggfnoid => 'stddev_p_float4', aggtransfn => 'float4_accum', + aggcombinefn => 'float8_combine', aggtranstype => '_float8', + agginitval => '{0,0,0}', + aggpartialfn => 'stddev_p_float4' }, { aggfnoid => 'stddev(float4)', aggtransfn => 'float4_accum', aggfinalfn => 'float8_stddev_samp', aggcombinefn => 'float8_combine', - aggtranstype => '_float8', agginitval => '{0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0}', + aggpartialfn => 'stddev_p_float4' }, +{ aggfnoid => 'stddev_p_float8', aggtransfn => 'float8_accum', + aggcombinefn => 'float8_combine', aggtranstype => '_float8', + agginitval => '{0,0,0}', + aggpartialfn => 'stddev_p_float8' }, { aggfnoid => 'stddev(float8)', aggtransfn => 'float8_accum', aggfinalfn => 'float8_stddev_samp', aggcombinefn => 'float8_combine', - aggtranstype => '_float8', agginitval => '{0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0}', + aggpartialfn => 'stddev_p_float8' }, +{ aggfnoid => 'stddev_p_numeric', aggtransfn => 'numeric_accum', + aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine', + aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', + aggtranstype => 'internal', aggtransspace => '128', + aggpartialfn => 'stddev_p_numeric' }, { aggfnoid => 'stddev(numeric)', aggtransfn => 'numeric_accum', aggfinalfn => 'numeric_stddev_samp', aggcombinefn => 'numeric_combine', aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize', aggmtransfn => 'numeric_accum', aggminvtransfn => 'numeric_accum_inv', aggmfinalfn => 'numeric_stddev_samp', aggtranstype => 'internal', aggtransspace => '128', aggmtranstype => 'internal', - aggmtransspace => '128' }, + aggmtransspace => '128', + aggpartialfn => 'stddev_p_numeric' }, # SQL2003 binary regression aggregates { aggfnoid => 'regr_count', aggtransfn => 'int8inc_float8_float8', - aggcombinefn => 'int8pl', aggtranstype => 'int8', agginitval => '0' }, + aggcombinefn => 'int8pl', aggtranstype => 'int8', agginitval => '0', + aggpartialfn => 'regr_count' }, +{ aggfnoid => 'regr_sxx_p', aggtransfn => 'float8_regr_accum', + aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8', + agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_sxx_p' }, { aggfnoid => 'regr_sxx', aggtransfn => 'float8_regr_accum', aggfinalfn => 'float8_regr_sxx', aggcombinefn => 'float8_regr_combine', - aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_sxx_p' }, +{ aggfnoid => 'regr_syy_p', aggtransfn => 'float8_regr_accum', + aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8', + agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_syy_p' }, { aggfnoid => 'regr_syy', aggtransfn => 'float8_regr_accum', aggfinalfn => 'float8_regr_syy', aggcombinefn => 'float8_regr_combine', - aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_syy_p' }, +{ aggfnoid => 'regr_sxy_p', aggtransfn => 'float8_regr_accum', + aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8', + agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_sxy_p' }, { aggfnoid => 'regr_sxy', aggtransfn => 'float8_regr_accum', aggfinalfn => 'float8_regr_sxy', aggcombinefn => 'float8_regr_combine', - aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_sxy_p' }, +{ aggfnoid => 'regr_avgx_p', aggtransfn => 'float8_regr_accum', + aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8', + agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_avgx_p' }, { aggfnoid => 'regr_avgx', aggtransfn => 'float8_regr_accum', aggfinalfn => 'float8_regr_avgx', aggcombinefn => 'float8_regr_combine', - aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_avgx_p' }, +{ aggfnoid => 'regr_avgy_p', aggtransfn => 'float8_regr_accum', + aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8', + agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_avgy_p' }, { aggfnoid => 'regr_avgy', aggtransfn => 'float8_regr_accum', aggfinalfn => 'float8_regr_avgy', aggcombinefn => 'float8_regr_combine', - aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_avgy_p' }, +{ aggfnoid => 'regr_r2_p', aggtransfn => 'float8_regr_accum', + aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8', + agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_r2_p' }, { aggfnoid => 'regr_r2', aggtransfn => 'float8_regr_accum', aggfinalfn => 'float8_regr_r2', aggcombinefn => 'float8_regr_combine', - aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_r2_p' }, +{ aggfnoid => 'regr_slope_p', aggtransfn => 'float8_regr_accum', + aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8', + agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_slope_p' }, { aggfnoid => 'regr_slope', aggtransfn => 'float8_regr_accum', aggfinalfn => 'float8_regr_slope', aggcombinefn => 'float8_regr_combine', - aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_slope_p' }, +{ aggfnoid => 'regr_intercept_p', aggtransfn => 'float8_regr_accum', + aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8', + agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_intercept_p' }, { aggfnoid => 'regr_intercept', aggtransfn => 'float8_regr_accum', aggfinalfn => 'float8_regr_intercept', aggcombinefn => 'float8_regr_combine', - aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'regr_intercept_p' }, +{ aggfnoid => 'covar_pop_p', aggtransfn => 'float8_regr_accum', + aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8', + agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'covar_pop_p' }, { aggfnoid => 'covar_pop', aggtransfn => 'float8_regr_accum', aggfinalfn => 'float8_covar_pop', aggcombinefn => 'float8_regr_combine', - aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'covar_pop_p' }, +{ aggfnoid => 'covar_samp_p', aggtransfn => 'float8_regr_accum', + aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8', + agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'covar_samp_p' }, { aggfnoid => 'covar_samp', aggtransfn => 'float8_regr_accum', aggfinalfn => 'float8_covar_samp', aggcombinefn => 'float8_regr_combine', - aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'covar_samp_p' }, +{ aggfnoid => 'corr_p', aggtransfn => 'float8_regr_accum', + aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8', + agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'corr_p' }, { aggfnoid => 'corr', aggtransfn => 'float8_regr_accum', aggfinalfn => 'float8_corr', aggcombinefn => 'float8_regr_combine', - aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' }, + aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}', + aggpartialfn => 'corr_p' }, # boolean-and and boolean-or { aggfnoid => 'bool_and', aggtransfn => 'booland_statefunc', aggcombinefn => 'booland_statefunc', aggmtransfn => 'bool_accum', aggminvtransfn => 'bool_accum_inv', aggmfinalfn => 'bool_alltrue', aggsortop => '<(bool,bool)', aggtranstype => 'bool', - aggmtranstype => 'internal', aggmtransspace => '16' }, + aggmtranstype => 'internal', aggmtransspace => '16', + aggpartialfn => 'bool_and' }, { aggfnoid => 'bool_or', aggtransfn => 'boolor_statefunc', aggcombinefn => 'boolor_statefunc', aggmtransfn => 'bool_accum', aggminvtransfn => 'bool_accum_inv', aggmfinalfn => 'bool_anytrue', aggsortop => '>(bool,bool)', aggtranstype => 'bool', - aggmtranstype => 'internal', aggmtransspace => '16' }, + aggmtranstype => 'internal', aggmtransspace => '16', + aggpartialfn => 'bool_or' }, { aggfnoid => 'every', aggtransfn => 'booland_statefunc', aggcombinefn => 'booland_statefunc', aggmtransfn => 'bool_accum', aggminvtransfn => 'bool_accum_inv', aggmfinalfn => 'bool_alltrue', aggsortop => '<(bool,bool)', aggtranstype => 'bool', - aggmtranstype => 'internal', aggmtransspace => '16' }, + aggmtranstype => 'internal', aggmtransspace => '16', + aggpartialfn => 'every' }, # bitwise integer { aggfnoid => 'bit_and(int2)', aggtransfn => 'int2and', - aggcombinefn => 'int2and', aggtranstype => 'int2' }, + aggcombinefn => 'int2and', aggtranstype => 'int2', + aggpartialfn => 'bit_and(int2)' }, { aggfnoid => 'bit_or(int2)', aggtransfn => 'int2or', aggcombinefn => 'int2or', - aggtranstype => 'int2' }, + aggtranstype => 'int2', + aggpartialfn => 'bit_or(int2)' }, { aggfnoid => 'bit_xor(int2)', aggtransfn => 'int2xor', - aggcombinefn => 'int2xor', aggtranstype => 'int2' }, + aggcombinefn => 'int2xor', aggtranstype => 'int2', + aggpartialfn => 'bit_xor(int2)' }, { aggfnoid => 'bit_and(int4)', aggtransfn => 'int4and', - aggcombinefn => 'int4and', aggtranstype => 'int4' }, + aggcombinefn => 'int4and', aggtranstype => 'int4', + aggpartialfn => 'bit_and(int4)' }, { aggfnoid => 'bit_or(int4)', aggtransfn => 'int4or', aggcombinefn => 'int4or', - aggtranstype => 'int4' }, + aggtranstype => 'int4', + aggpartialfn => 'bit_or(int4)' }, { aggfnoid => 'bit_xor(int4)', aggtransfn => 'int4xor', - aggcombinefn => 'int4xor', aggtranstype => 'int4' }, + aggcombinefn => 'int4xor', aggtranstype => 'int4', + aggpartialfn => 'bit_xor(int4)' }, { aggfnoid => 'bit_and(int8)', aggtransfn => 'int8and', - aggcombinefn => 'int8and', aggtranstype => 'int8' }, + aggcombinefn => 'int8and', aggtranstype => 'int8', + aggpartialfn => 'bit_and(int8)' }, { aggfnoid => 'bit_or(int8)', aggtransfn => 'int8or', aggcombinefn => 'int8or', - aggtranstype => 'int8' }, + aggtranstype => 'int8', + aggpartialfn => 'bit_or(int8)' }, { aggfnoid => 'bit_xor(int8)', aggtransfn => 'int8xor', - aggcombinefn => 'int8xor', aggtranstype => 'int8' }, + aggcombinefn => 'int8xor', aggtranstype => 'int8', + aggpartialfn => 'bit_xor(int8)' }, { aggfnoid => 'bit_and(bit)', aggtransfn => 'bitand', aggcombinefn => 'bitand', - aggtranstype => 'bit' }, + aggtranstype => 'bit', + aggpartialfn => 'bit_and(bit)' }, { aggfnoid => 'bit_or(bit)', aggtransfn => 'bitor', aggcombinefn => 'bitor', - aggtranstype => 'bit' }, + aggtranstype => 'bit', + aggpartialfn => 'bit_or(bit)' }, { aggfnoid => 'bit_xor(bit)', aggtransfn => 'bitxor', aggcombinefn => 'bitxor', - aggtranstype => 'bit' }, + aggtranstype => 'bit', + aggpartialfn => 'bit_xor(bit)' }, # xml { aggfnoid => 'xmlagg', aggtransfn => 'xmlconcat2', aggtranstype => 'xml' }, # array +{ aggfnoid => 'array_agg_p_anynonarray', aggtransfn => 'array_agg_transfn', + aggfinalfn => 'array_agg_serialize', aggcombinefn => 'array_agg_combine', + aggserialfn => 'array_agg_serialize', aggdeserialfn => 'array_agg_deserialize', + aggtranstype => 'internal', + aggpartialfn => 'array_agg_p_anynonarray' }, { aggfnoid => 'array_agg(anynonarray)', aggtransfn => 'array_agg_transfn', aggfinalfn => 'array_agg_finalfn', aggcombinefn => 'array_agg_combine', aggserialfn => 'array_agg_serialize', aggdeserialfn => 'array_agg_deserialize', aggfinalextra => 't', - aggtranstype => 'internal' }, + aggtranstype => 'internal', + aggpartialfn => 'array_agg_p_anynonarray' }, +{ aggfnoid => 'array_agg_p_anyarray', aggtransfn => 'array_agg_array_transfn', + aggfinalfn => 'array_agg_array_serialize', + aggcombinefn => 'array_agg_array_combine', + aggserialfn => 'array_agg_array_serialize', + aggdeserialfn => 'array_agg_array_deserialize', aggtranstype => 'internal', + aggpartialfn => 'array_agg_p_anyarray' }, { aggfnoid => 'array_agg(anyarray)', aggtransfn => 'array_agg_array_transfn', aggfinalfn => 'array_agg_array_finalfn', aggcombinefn => 'array_agg_array_combine', aggserialfn => 'array_agg_array_serialize', aggdeserialfn => 'array_agg_array_deserialize', aggfinalextra => 't', - aggtranstype => 'internal' }, + aggtranstype => 'internal', + aggpartialfn => 'array_agg_p_anyarray' }, # text +{ aggfnoid => 'string_agg_p_text_text', aggtransfn => 'string_agg_transfn', + aggfinalfn => 'string_agg_serialize', aggcombinefn => 'string_agg_combine', + aggserialfn => 'string_agg_serialize', + aggdeserialfn => 'string_agg_deserialize', aggtranstype => 'internal', + aggpartialfn => 'string_agg_p_text_text' }, { aggfnoid => 'string_agg(text,text)', aggtransfn => 'string_agg_transfn', aggfinalfn => 'string_agg_finalfn', aggcombinefn => 'string_agg_combine', aggserialfn => 'string_agg_serialize', - aggdeserialfn => 'string_agg_deserialize', aggtranstype => 'internal' }, + aggdeserialfn => 'string_agg_deserialize', aggtranstype => 'internal', + aggpartialfn => 'string_agg_p_text_text' }, # bytea +{ aggfnoid => 'string_agg_p_bytea_bytea', + aggtransfn => 'bytea_string_agg_transfn', aggfinalfn => 'string_agg_serialize', + aggcombinefn => 'string_agg_combine', aggserialfn => 'string_agg_serialize', + aggdeserialfn => 'string_agg_deserialize', aggtranstype => 'internal', + aggpartialfn => 'string_agg_p_bytea_bytea' }, { aggfnoid => 'string_agg(bytea,bytea)', aggtransfn => 'bytea_string_agg_transfn', aggfinalfn => 'bytea_string_agg_finalfn', aggcombinefn => 'string_agg_combine', aggserialfn => 'string_agg_serialize', - aggdeserialfn => 'string_agg_deserialize', aggtranstype => 'internal' }, + aggdeserialfn => 'string_agg_deserialize', aggtranstype => 'internal', + aggpartialfn => 'string_agg_p_bytea_bytea' }, # range { aggfnoid => 'range_intersect_agg(anyrange)', aggtransfn => 'range_intersect_agg_transfn', - aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' }, + aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange', + aggpartialfn => 'range_intersect_agg(anyrange)' }, { aggfnoid => 'range_intersect_agg(anymultirange)', aggtransfn => 'multirange_intersect_agg_transfn', aggcombinefn => 'multirange_intersect_agg_transfn', - aggtranstype => 'anymultirange' }, + aggtranstype => 'anymultirange', + aggpartialfn => 'range_intersect_agg(anymultirange)' }, { aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn', aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't', aggtranstype => 'internal' }, @@ -658,6 +1075,7 @@ # any_value { aggfnoid => 'any_value(anyelement)', aggtransfn => 'any_value_transfn', - aggcombinefn => 'any_value_transfn', aggtranstype => 'anyelement' }, + aggcombinefn => 'any_value_transfn', aggtranstype => 'anyelement', + aggpartialfn => 'any_value(anyelement)' }, ] diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h index 085149e258..3e4f8331ee 100644 --- a/src/include/catalog/pg_aggregate.h +++ b/src/include/catalog/pg_aggregate.h @@ -55,6 +55,9 @@ CATALOG(pg_aggregate,2600,AggregateRelationId) /* function to convert bytea to transtype (0 if none) */ regproc aggdeserialfn BKI_DEFAULT(-) BKI_LOOKUP_OPT(pg_proc); + /* special aggregate function for partial aggregation (0 if none) */ + regproc aggpartialfn BKI_DEFAULT(-) BKI_LOOKUP_OPT(pg_proc); + /* forward function for moving-aggregate mode (0 if none) */ regproc aggmtransfn BKI_DEFAULT(-) BKI_LOOKUP_OPT(pg_proc); @@ -164,6 +167,7 @@ extern ObjectAddress AggregateCreate(const char *aggName, List *aggmtransfnName, List *aggminvtransfnName, List *aggmfinalfnName, + List *aggpartialfnName, bool finalfnExtraArgs, bool mfinalfnExtraArgs, char finalfnModify, diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index bc41e92677..7b27e8cebf 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -1658,6 +1658,10 @@ { oid => '2334', descr => 'aggregate final function', proname => 'array_agg_finalfn', proisstrict => 'f', prorettype => 'anyarray', proargtypes => 'internal anynonarray', prosrc => 'array_agg_finalfn' }, +{ oid => '6', descr => 'partial aggregation for array_agg(anynonarray)', + proname => 'array_agg_p_anynonarray', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'anynonarray', + prosrc => 'aggregate_dummy' }, { oid => '2335', descr => 'concatenate aggregate input into an array', proname => 'array_agg', prokind => 'a', proisstrict => 'f', prorettype => 'anyarray', proargtypes => 'anynonarray', @@ -1680,6 +1684,9 @@ proname => 'array_agg_array_finalfn', proisstrict => 'f', prorettype => 'anyarray', proargtypes => 'internal anyarray', prosrc => 'array_agg_array_finalfn' }, +{ oid => '7', descr => 'partial aggregation for array_agg(anyarray)', + proname => 'array_agg_p_anyarray', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'anyarray', prosrc => 'aggregate_dummy' }, { oid => '4053', descr => 'concatenate aggregate input into an array', proname => 'array_agg', prokind => 'a', proisstrict => 'f', prorettype => 'anyarray', proargtypes => 'anyarray', @@ -5004,6 +5011,10 @@ { oid => '3536', descr => 'aggregate final function', proname => 'string_agg_finalfn', proisstrict => 'f', prorettype => 'text', proargtypes => 'internal', prosrc => 'string_agg_finalfn' }, +{ oid => '8', descr => 'partial aggregation for string_agg(text, text)', + proname => 'string_agg_p_text_text', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'text text', + prosrc => 'aggregate_dummy' }, { oid => '3538', descr => 'concatenate aggregate input into a string', proname => 'string_agg', prokind => 'a', proisstrict => 'f', prorettype => 'text', proargtypes => 'text text', @@ -5016,6 +5027,10 @@ proname => 'bytea_string_agg_finalfn', proisstrict => 'f', prorettype => 'bytea', proargtypes => 'internal', prosrc => 'bytea_string_agg_finalfn' }, +{ oid => '9', descr => 'partial aggregation for string_agg(bytea, bytea)', + proname => 'string_agg_p_bytea_bytea', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'bytea bytea', + prosrc => 'aggregate_dummy' }, { oid => '3545', descr => 'concatenate aggregate input into a bytea', proname => 'string_agg', prokind => 'a', proisstrict => 'f', prorettype => 'bytea', proargtypes => 'bytea bytea', @@ -6597,36 +6612,61 @@ # Aggregates (moved here from pg_aggregate for 7.3) +{ oid => '111', descr => 'partial aggregation for avg(int8)', + proname => 'avg_p_int8', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, { oid => '2100', descr => 'the average (arithmetic mean) as numeric of all bigint values', proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, +{ oid => '226', descr => 'partial aggregation for avg(int4)', + proname => 'avg_p_int4', prokind => 'a', proisstrict => 'f', + prorettype => '_int8', proargtypes => 'int4', prosrc => 'aggregate_dummy' }, { oid => '2101', descr => 'the average (arithmetic mean) as numeric of all integer values', proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' }, +{ oid => '388', descr => 'partial aggregation for avg(int2)', + proname => 'avg_p_int2', prokind => 'a', proisstrict => 'f', + prorettype => '_int8', proargtypes => 'int2', prosrc => 'aggregate_dummy' }, { oid => '2102', descr => 'the average (arithmetic mean) as numeric of all smallint values', proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' }, +{ oid => '389', descr => 'partial aggregation for avg(numeric)', + proname => 'avg_p_numeric', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' }, { oid => '2103', descr => 'the average (arithmetic mean) as numeric of all numeric values', proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'numeric', prosrc => 'aggregate_dummy' }, +{ oid => '560', descr => 'partial aggregation for avg(float4)', + proname => 'avg_p_float4', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' }, { oid => '2104', descr => 'the average (arithmetic mean) as float8 of all float4 values', proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' }, +{ oid => '561', descr => 'partial aggregation for avg(float8)', + proname => 'avg_p_float8', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' }, { oid => '2105', descr => 'the average (arithmetic mean) as float8 of all float8 values', proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' }, +{ oid => '562', descr => 'partial aggregation for avg(interval)', + proname => 'avg_p_interval', prokind => 'a', proisstrict => 'f', + prorettype => '_interval', proargtypes => 'interval', + prosrc => 'aggregate_dummy' }, { oid => '2106', descr => 'the average (arithmetic mean) as interval of all interval values', proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'interval', proargtypes => 'interval', prosrc => 'aggregate_dummy' }, +{ oid => '563', descr => 'partial aggregation for sum(int8)', + proname => 'sum_p_int8', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, { oid => '2107', descr => 'sum as numeric across all bigint input values', proname => 'sum', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, @@ -6649,6 +6689,9 @@ proname => 'sum', prokind => 'a', proisstrict => 'f', prorettype => 'interval', proargtypes => 'interval', prosrc => 'aggregate_dummy' }, +{ oid => '564', descr => 'partial aggregation for sum(numeric)', + proname => 'sum_p_numeric', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' }, { oid => '2114', descr => 'sum as numeric across all numeric input values', proname => 'sum', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'numeric', prosrc => 'aggregate_dummy' }, @@ -6807,153 +6850,261 @@ proname => 'int8inc_support', prorettype => 'internal', proargtypes => 'internal', prosrc => 'int8inc_support' }, +{ oid => '565', descr => 'partial aggregation for var_pop(int8)', + proname => 'var_pop_p_int8', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, { oid => '2718', descr => 'population variance of bigint input values (square of the population standard deviation)', proname => 'var_pop', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, +{ oid => '566', descr => 'partial aggregation for var_pop(int4)', + proname => 'var_pop_p_int4', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int4', prosrc => 'aggregate_dummy' }, { oid => '2719', descr => 'population variance of integer input values (square of the population standard deviation)', proname => 'var_pop', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' }, +{ oid => '567', descr => 'partial aggregation for var_pop(int2)', + proname => 'var_pop_p_int2', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int2', prosrc => 'aggregate_dummy' }, { oid => '2720', descr => 'population variance of smallint input values (square of the population standard deviation)', proname => 'var_pop', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' }, +{ oid => '568', descr => 'partial aggregation for var_pop(float4)', + proname => 'var_pop_p_float4', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' }, { oid => '2721', descr => 'population variance of float4 input values (square of the population standard deviation)', proname => 'var_pop', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' }, +{ oid => '569', descr => 'partial aggregation for var_pop(float8)', + proname => 'var_pop_p_float8', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' }, { oid => '2722', descr => 'population variance of float8 input values (square of the population standard deviation)', proname => 'var_pop', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' }, +{ oid => '570', descr => 'partial aggregation for var_pop(numeric)', + proname => 'var_pop_p_numeric', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' }, { oid => '2723', descr => 'population variance of numeric input values (square of the population standard deviation)', proname => 'var_pop', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'numeric', prosrc => 'aggregate_dummy' }, +{ oid => '571', descr => 'partial aggregation for var_samp(int8)', + proname => 'var_samp_p_int8', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, { oid => '2641', descr => 'sample variance of bigint input values (square of the sample standard deviation)', proname => 'var_samp', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, +{ oid => '572', descr => 'partial aggregation for var_samp(int4)', + proname => 'var_samp_p_int4', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int4', prosrc => 'aggregate_dummy' }, { oid => '2642', descr => 'sample variance of integer input values (square of the sample standard deviation)', proname => 'var_samp', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' }, +{ oid => '573', descr => 'partial aggregation for var_samp(int2)', + proname => 'var_samp_p_int2', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int2', prosrc => 'aggregate_dummy' }, { oid => '2643', descr => 'sample variance of smallint input values (square of the sample standard deviation)', proname => 'var_samp', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' }, +{ oid => '574', descr => 'partial aggregation for var_samp(float4)', + proname => 'var_samp_p_float4', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' }, { oid => '2644', descr => 'sample variance of float4 input values (square of the sample standard deviation)', proname => 'var_samp', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' }, +{ oid => '575', descr => 'partial aggregation for var_samp(float8)', + proname => 'var_samp_p_float8', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' }, { oid => '2645', descr => 'sample variance of float8 input values (square of the sample standard deviation)', proname => 'var_samp', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' }, +{ oid => '576', descr => 'partial aggregation for var_samp(numeric)', + proname => 'var_samp_p_numeric', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' }, { oid => '2646', descr => 'sample variance of numeric input values (square of the sample standard deviation)', proname => 'var_samp', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'numeric', prosrc => 'aggregate_dummy' }, +{ oid => '577', descr => 'partial aggregation for variance(int8)', + proname => 'variance_p_int8', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, { oid => '2148', descr => 'historical alias for var_samp', proname => 'variance', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, +{ oid => '578', descr => 'partial aggregation for variance(int4)', + proname => 'variance_p_int4', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int4', prosrc => 'aggregate_dummy' }, { oid => '2149', descr => 'historical alias for var_samp', proname => 'variance', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' }, +{ oid => '579', descr => 'partial aggregation for variance(int2)', + proname => 'variance_p_int2', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int2', prosrc => 'aggregate_dummy' }, { oid => '2150', descr => 'historical alias for var_samp', proname => 'variance', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' }, +{ oid => '580', descr => 'partial aggregation for variance(float4)', + proname => 'variance_p_float4', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' }, { oid => '2151', descr => 'historical alias for var_samp', proname => 'variance', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' }, +{ oid => '581', descr => 'partial aggregation for variance(float8)', + proname => 'variance_p_float8', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' }, { oid => '2152', descr => 'historical alias for var_samp', proname => 'variance', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' }, +{ oid => '582', descr => 'partial aggregation for variance(numeric)', + proname => 'variance_p_numeric', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' }, { oid => '2153', descr => 'historical alias for var_samp', proname => 'variance', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'numeric', prosrc => 'aggregate_dummy' }, +{ oid => '583', descr => 'partial aggregation for stddev_pop(int8)', + proname => 'stddev_pop_p_int8', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, { oid => '2724', descr => 'population standard deviation of bigint input values', proname => 'stddev_pop', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, +{ oid => '703', descr => 'partial aggregation for stddev_pop(int4)', + proname => 'stddev_pop_p_int4', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int4', prosrc => 'aggregate_dummy' }, { oid => '2725', descr => 'population standard deviation of integer input values', proname => 'stddev_pop', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' }, +{ oid => '726', descr => 'partial aggregation for stddev_pop(int2)', + proname => 'stddev_pop_p_int2', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int2', prosrc => 'aggregate_dummy' }, { oid => '2726', descr => 'population standard deviation of smallint input values', proname => 'stddev_pop', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' }, +{ oid => '786', descr => 'partial aggregation for stddev_pop(float4)', + proname => 'stddev_pop_p_float4', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' }, { oid => '2727', descr => 'population standard deviation of float4 input values', proname => 'stddev_pop', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' }, +{ oid => '787', descr => 'partial aggregation for stddev_pop(float8)', + proname => 'stddev_pop_p_float8', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' }, { oid => '2728', descr => 'population standard deviation of float8 input values', proname => 'stddev_pop', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' }, +{ oid => '788', descr => 'partial aggregation for stddev_pop(numeric)', + proname => 'stddev_pop_p_numeric', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' }, { oid => '2729', descr => 'population standard deviation of numeric input values', proname => 'stddev_pop', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'numeric', prosrc => 'aggregate_dummy' }, +{ oid => '789', descr => 'partial aggregation for stddev_samp(int8)', + proname => 'stddev_samp_p_int8', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, { oid => '2712', descr => 'sample standard deviation of bigint input values', proname => 'stddev_samp', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, +{ oid => '811', descr => 'partial aggregation for stddev_samp(int4)', + proname => 'stddev_samp_p_int4', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int4', prosrc => 'aggregate_dummy' }, { oid => '2713', descr => 'sample standard deviation of integer input values', proname => 'stddev_samp', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' }, +{ oid => '812', descr => 'partial aggregation for stddev_samp(int2)', + proname => 'stddev_samp_p_int2', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int2', prosrc => 'aggregate_dummy' }, { oid => '2714', descr => 'sample standard deviation of smallint input values', proname => 'stddev_samp', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' }, +{ oid => '813', descr => 'partial aggregation for stddev_samp(float4)', + proname => 'stddev_samp_p_float4', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' }, { oid => '2715', descr => 'sample standard deviation of float4 input values', proname => 'stddev_samp', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' }, +{ oid => '814', descr => 'partial aggregation for stddev_samp(float8)', + proname => 'stddev_samp_p_float8', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' }, { oid => '2716', descr => 'sample standard deviation of float8 input values', proname => 'stddev_samp', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' }, +{ oid => '815', descr => 'partial aggregation for stddev_samp(numeric)', + proname => 'stddev_samp_p_numeric', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' }, { oid => '2717', descr => 'sample standard deviation of numeric input values', proname => 'stddev_samp', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'numeric', prosrc => 'aggregate_dummy' }, +{ oid => '816', descr => 'partial aggregation for stddev(int8)', + proname => 'stddev_p_int8', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, { oid => '2154', descr => 'historical alias for stddev_samp', proname => 'stddev', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' }, +{ oid => '970', descr => 'partial aggregation for stddev(int4)', + proname => 'stddev_p_int4', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int4', prosrc => 'aggregate_dummy' }, { oid => '2155', descr => 'historical alias for stddev_samp', proname => 'stddev', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' }, +{ oid => '1382', descr => 'partial aggregation for stddev(int2)', + proname => 'stddev_p_int2', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'int2', prosrc => 'aggregate_dummy' }, { oid => '2156', descr => 'historical alias for stddev_samp', proname => 'stddev', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' }, +{ oid => '1524', descr => 'partial aggregation for stddev(float4)', + proname => 'stddev_p_float4', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' }, { oid => '2157', descr => 'historical alias for stddev_samp', proname => 'stddev', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' }, +{ oid => '1533', descr => 'partial aggregation for stddev(float8)', + proname => 'stddev_p_float8', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' }, { oid => '2158', descr => 'historical alias for stddev_samp', proname => 'stddev', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' }, +{ oid => '1566', descr => 'partial aggregation for stddev(numeric)', + proname => 'stddev_p_numeric', prokind => 'a', proisstrict => 'f', + prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' }, { oid => '2159', descr => 'historical alias for stddev_samp', proname => 'stddev', prokind => 'a', proisstrict => 'f', prorettype => 'numeric', proargtypes => 'numeric', @@ -6964,52 +7115,97 @@ proname => 'regr_count', prokind => 'a', proisstrict => 'f', prorettype => 'int8', proargtypes => 'float8 float8', prosrc => 'aggregate_dummy' }, +{ oid => '1568', descr => 'partial aggregation for regr_sxx(float8, float8)', + proname => 'regr_sxx_p', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8 float8', + prosrc => 'aggregate_dummy' }, { oid => '2819', descr => 'sum of squares of the independent variable (sum(X^2) - sum(X)^2/N)', proname => 'regr_sxx', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8 float8', prosrc => 'aggregate_dummy' }, +{ oid => '2173', descr => 'partial aggregation for regr_syy(float8, float8)', + proname => 'regr_syy_p', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8 float8', + prosrc => 'aggregate_dummy' }, { oid => '2820', descr => 'sum of squares of the dependent variable (sum(Y^2) - sum(Y)^2/N)', proname => 'regr_syy', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8 float8', prosrc => 'aggregate_dummy' }, +{ oid => '3813', descr => 'partial aggregation for regr_sxy(float8, float8)', + proname => 'regr_sxy_p', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8 float8', + prosrc => 'aggregate_dummy' }, { oid => '2821', descr => 'sum of products of independent times dependent variable (sum(X*Y) - sum(X) * sum(Y)/N)', proname => 'regr_sxy', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8 float8', prosrc => 'aggregate_dummy' }, +{ oid => '3814', descr => 'partial aggregation for regr_avgx(float8, float8)', + proname => 'regr_avgx_p', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8 float8', + prosrc => 'aggregate_dummy' }, { oid => '2822', descr => 'average of the independent variable (sum(X)/N)', proname => 'regr_avgx', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8 float8', prosrc => 'aggregate_dummy' }, +{ oid => '4551', descr => 'partial aggregation for regr_avgy(float8, float8)', + proname => 'regr_avgy_p', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8 float8', + prosrc => 'aggregate_dummy' }, { oid => '2823', descr => 'average of the dependent variable (sum(Y)/N)', proname => 'regr_avgy', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8 float8', prosrc => 'aggregate_dummy' }, +{ oid => '4552', descr => 'partial aggregation for regr_r2(float8, float8)', + proname => 'regr_r2_p', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8 float8', + prosrc => 'aggregate_dummy' }, { oid => '2824', descr => 'square of the correlation coefficient', proname => 'regr_r2', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8 float8', prosrc => 'aggregate_dummy' }, +{ oid => '4553', descr => 'partial aggregation for regr_slope(float8, float8)', + proname => 'regr_slope_p', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8 float8', + prosrc => 'aggregate_dummy' }, { oid => '2825', descr => 'slope of the least-squares-fit linear equation determined by the (X, Y) pairs', proname => 'regr_slope', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8 float8', prosrc => 'aggregate_dummy' }, +{ oid => '4554', + descr => 'partial aggregation for regr_intercept(float8, float8)', + proname => 'regr_intercept_p', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8 float8', + prosrc => 'aggregate_dummy' }, { oid => '2826', descr => 'y-intercept of the least-squares-fit linear equation determined by the (X, Y) pairs', proname => 'regr_intercept', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8 float8', prosrc => 'aggregate_dummy' }, +{ oid => '4555', descr => 'partial aggregation for covar_pop(float8, float8)', + proname => 'covar_pop_p', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8 float8', + prosrc => 'aggregate_dummy' }, { oid => '2827', descr => 'population covariance', proname => 'covar_pop', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8 float8', prosrc => 'aggregate_dummy' }, +{ oid => '4556', descr => 'partial aggregation for covar_samp(float8, float8)', + proname => 'covar_samp_p', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8 float8', + prosrc => 'aggregate_dummy' }, { oid => '2828', descr => 'sample covariance', proname => 'covar_samp', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8 float8', prosrc => 'aggregate_dummy' }, +{ oid => '4557', descr => 'partial aggregation for corr(float8, float8)', + proname => 'corr_p', prokind => 'a', proisstrict => 'f', + prorettype => '_float8', proargtypes => 'float8 float8', + prosrc => 'aggregate_dummy' }, { oid => '2829', descr => 'correlation coefficient', proname => 'corr', prokind => 'a', proisstrict => 'f', prorettype => 'float8', proargtypes => 'float8 float8', prosrc => 'aggregate_dummy' }, diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index ed85dc7414..411a249482 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -3243,6 +3243,9 @@ typedef enum * havingQual gives list of quals to be applied after aggregation. * targetList gives list of columns to be projected. * patype is the type of partitionwise aggregation that is being performed. + * groupClausePartial is List of SortGroupClauses for partial aggregate + * pushdown by FDW + * partial_target is PathTarget for partial aggregate pushdown by FDW */ typedef struct { @@ -3257,6 +3260,8 @@ typedef struct Node *havingQual; List *targetList; PartitionwiseAggregateType patype; + List *groupClausePartial; + PathTarget *partial_target; } GroupPathExtraData; /* diff --git a/src/test/regress/expected/create_aggregate.out b/src/test/regress/expected/create_aggregate.out index dcf6909423..73e4cfde0f 100644 --- a/src/test/regress/expected/create_aggregate.out +++ b/src/test/regress/expected/create_aggregate.out @@ -322,3 +322,187 @@ WARNING: aggregate attribute "Finalfunc_extra" not recognized WARNING: aggregate attribute "Finalfunc_modify" not recognized WARNING: aggregate attribute "Parallel" not recognized ERROR: aggregate stype must be specified +-- invalid: finalfunc is specified and stype is not internal +-- even though aggpartialfunc is equal to the aggregate name +CREATE AGGREGATE udf_avg_int4_invalid(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + finalfunc = int8_avg, + combinefunc = int4_avg_combine, + initcond = '{0,0}', + aggpartialfunc = udf_avg_int4_invalid +); +ERROR: udf_avg_int4_invalid is not its own aggpartialfunc +-- invalid: finalfunc is not equal to serialfunc and stype is internal +-- even though aggpartialfunc is equal to the aggregate name +CREATE AGGREGATE udf_avg_int8_invalid(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_poly_avg, + combinefunc = int8_avg_combine, + serialfunc = int8_avg_serialize, + deserialfunc = int8_avg_deserialize, + aggpartialfunc = udf_avg_int8_invalid +); +ERROR: udf_avg_int8_invalid is not its own aggpartialfunc +-- invalid: aggpartialfunc which doesn't exist +CREATE AGGREGATE nonexistent_aggpartial_agg(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_poly_sum, + combinefunc = int8_avg_combine, + serialfunc = int8_avg_serialize, + deserialfunc = int8_avg_deserialize, + aggpartialfunc = aggpartial_foo +); +ERROR: function aggpartial_foo(bigint) does not exist +-- invalid: aggpartialfunc is specified and combinefunc isn't specified +CREATE AGGREGATE nocombinefunc_agg(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_poly_avg, + serialfunc = int8_avg_serialize, + deserialfunc = int8_avg_deserialize, + aggpartialfunc = avg_p_int8 +); +ERROR: aggcombinefnName must be supplied if aggpartialfnName is supplied +-- invalid: combinefunc in agg and combinefunc in aggpartialfunc don't match +CREATE AGGREGATE udf_avg_invalid_combinefunc(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_poly_avg, + combinefunc = numeric_avg_combine, + serialfunc = int8_avg_serialize, + deserialfunc = int8_avg_deserialize, + aggpartialfunc = avg_p_int8 +); +ERROR: combinefunc in aggregate and combinefunc in its aggpartialfunc must match +CREATE FUNCTION udf_float8_accum(_float8, float8) RETURNS _float8 AS +$$ SELECT float8_accum($1, $2) $$ +LANGUAGE SQL; +-- invalid: sfunc in agg and sfunc in aggpartialfunc don't match +CREATE AGGREGATE udf_avg_invalid_sfunc(float8) ( + sfunc = udf_float8_accum, + stype = _float8, + finalfunc = float8_avg, + combinefunc = float8_combine, + initcond = '{0,0,0}', + aggpartialfunc = avg_p_float8 +); +ERROR: sfunc in aggregate and its aggpartialfunc must match +DROP FUNCTION udf_float8_accum(_float8, float8); +-- invalid: initcond in agg and initcond in aggpartialfunc don't match +CREATE AGGREGATE udf_avg_invalid_initcond(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + initcond = '{1,0}', + finalfunc = int8_avg, + combinefunc = int4_avg_combine, + aggpartialfunc = avg_p_int4 +); +ERROR: initcond in aggregate and initcond in its aggpartialfunc must match +-- invalid: aggserialfn in agg and aggserialfn in aggpartialfunc don't match +CREATE AGGREGATE udf_avg_invalid_aggserialfn(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_poly_avg, + combinefunc = int8_avg_combine, + serialfunc = numeric_avg_serialize, + deserialfunc = int8_avg_deserialize, + aggpartialfunc = avg_p_int8 +); +ERROR: serialfunc in aggregate and serialfunc in its aggpartialfunc must match +-- invalid: aggdeserialfn in agg and aggdeserialfn in aggpartialfunc don't match +CREATE AGGREGATE udf_avg_invalid_aggdeserialfn(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_poly_avg, + combinefunc = int8_avg_combine, + serialfunc = int8_avg_serialize, + deserialfunc = numeric_avg_deserialize, + aggpartialfunc = avg_p_int8 +); +ERROR: deserialfunc in aggregate and deserialfunc in its aggpartialfunc must match +-- invalid: stype is internal and aggpartialfunc's finalfunc +-- isn't serialfunc of agg +CREATE AGGREGATE udf_avg_p_int8(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_avg_serialize, + combinefunc = int8_avg_combine, + serialfunc = int8_avg_serialize, + deserialfunc = int8_avg_deserialize +); +CREATE AGGREGATE udf_avg(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_poly_avg, + combinefunc = int8_avg_combine, + serialfunc = int8_avg_serialize, + deserialfunc = int8_avg_deserialize, + aggpartialfunc = udf_avg_p_int8 +); +ERROR: finalfunc of aggpartialfunc must match serialfunc of aggregate when stype is internal +-- invalid: stype is not internal and aggpartialfunc has finalfunc +CREATE AGGREGATE udf_avg_p_int4_hasfinal(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + finalfunc = int8_avg, + combinefunc = int4_avg_combine, + initcond = '{0,0}' +); +CREATE AGGREGATE udf_avg(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + finalfunc = int8_avg, + combinefunc = int4_avg_combine, + initcond = '{0,0}', + aggpartialfunc = udf_avg_p_int4_hasfinal +); +ERROR: finalfunc of aggpartialfunc must not be supplied when stype isn't internal +-- invalid: current user doesn't have execute privilege on aggpartialfunc +CREATE AGGREGATE udf_avg_p_int4(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + combinefunc = int4_avg_combine, + initcond = '{0,0}' +); +CREATE USER regress_priv_user1; +GRANT ALL ON SCHEMA public TO regress_priv_user1; +REVOKE EXECUTE ON FUNCTION udf_avg_p_int4 FROM public; +SET SESSION AUTHORIZATION regress_priv_user1; +CREATE AGGREGATE udf_avg_noprivilege(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + finalfunc = int8_avg, + combinefunc = int4_avg_combine, + initcond = '{0,0}', + aggpartialfunc = udf_avg_p_int4 +); +ERROR: permission denied for function udf_avg_p_int4 +SET SESSION AUTHORIZATION DEFAULT; +REVOKE ALL ON SCHEMA public FROM regress_priv_user1; +DROP USER regress_priv_user1; +-- An aggregate function that is aggpartialfunc +-- of another aggregate cannot be deleted. +CREATE AGGREGATE udf_avg_int4_p(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + combinefunc = int4_avg_combine, + initcond = '{0,0}', + aggpartialfunc = udf_avg_int4_p +); +CREATE AGGREGATE udf_avg_int4(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + finalfunc = int8_avg, + combinefunc = int4_avg_combine, + initcond = '{0,0}', + aggpartialfunc = udf_avg_int4_p +); +DROP AGGREGATE udf_avg_int4_p(int4); +ERROR: cannot drop function udf_avg_int4_p(integer) because other objects depend on it +DETAIL: function udf_avg_int4(integer) depends on function udf_avg_int4_p(integer) +HINT: Use DROP ... CASCADE to drop the dependent objects too. +DROP AGGREGATE udf_avg_int4_p(int4) CASCADE; +NOTICE: drop cascades to function udf_avg_int4(integer) diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out index 215eb899be..6c80efa7eb 100644 --- a/src/test/regress/expected/oidjoins.out +++ b/src/test/regress/expected/oidjoins.out @@ -145,6 +145,7 @@ NOTICE: checking pg_aggregate {aggfinalfn} => pg_proc {oid} NOTICE: checking pg_aggregate {aggcombinefn} => pg_proc {oid} NOTICE: checking pg_aggregate {aggserialfn} => pg_proc {oid} NOTICE: checking pg_aggregate {aggdeserialfn} => pg_proc {oid} +NOTICE: checking pg_aggregate {aggpartialfn} => pg_proc {oid} NOTICE: checking pg_aggregate {aggmtransfn} => pg_proc {oid} NOTICE: checking pg_aggregate {aggminvtransfn} => pg_proc {oid} NOTICE: checking pg_aggregate {aggmfinalfn} => pg_proc {oid} diff --git a/src/test/regress/sql/create_aggregate.sql b/src/test/regress/sql/create_aggregate.sql index d4b4036fd7..e90e0ad2d6 100644 --- a/src/test/regress/sql/create_aggregate.sql +++ b/src/test/regress/sql/create_aggregate.sql @@ -328,3 +328,186 @@ CREATE AGGREGATE case_agg(float8) "Finalfunc_modify" = read_write, "Parallel" = safe ); + +-- invalid: finalfunc is specified and stype is not internal +-- even though aggpartialfunc is equal to the aggregate name +CREATE AGGREGATE udf_avg_int4_invalid(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + finalfunc = int8_avg, + combinefunc = int4_avg_combine, + initcond = '{0,0}', + aggpartialfunc = udf_avg_int4_invalid +); + +-- invalid: finalfunc is not equal to serialfunc and stype is internal +-- even though aggpartialfunc is equal to the aggregate name +CREATE AGGREGATE udf_avg_int8_invalid(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_poly_avg, + combinefunc = int8_avg_combine, + serialfunc = int8_avg_serialize, + deserialfunc = int8_avg_deserialize, + aggpartialfunc = udf_avg_int8_invalid +); + +-- invalid: aggpartialfunc which doesn't exist +CREATE AGGREGATE nonexistent_aggpartial_agg(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_poly_sum, + combinefunc = int8_avg_combine, + serialfunc = int8_avg_serialize, + deserialfunc = int8_avg_deserialize, + aggpartialfunc = aggpartial_foo +); + +-- invalid: aggpartialfunc is specified and combinefunc isn't specified +CREATE AGGREGATE nocombinefunc_agg(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_poly_avg, + serialfunc = int8_avg_serialize, + deserialfunc = int8_avg_deserialize, + aggpartialfunc = avg_p_int8 +); + +-- invalid: combinefunc in agg and combinefunc in aggpartialfunc don't match +CREATE AGGREGATE udf_avg_invalid_combinefunc(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_poly_avg, + combinefunc = numeric_avg_combine, + serialfunc = int8_avg_serialize, + deserialfunc = int8_avg_deserialize, + aggpartialfunc = avg_p_int8 +); + +CREATE FUNCTION udf_float8_accum(_float8, float8) RETURNS _float8 AS +$$ SELECT float8_accum($1, $2) $$ +LANGUAGE SQL; + +-- invalid: sfunc in agg and sfunc in aggpartialfunc don't match +CREATE AGGREGATE udf_avg_invalid_sfunc(float8) ( + sfunc = udf_float8_accum, + stype = _float8, + finalfunc = float8_avg, + combinefunc = float8_combine, + initcond = '{0,0,0}', + aggpartialfunc = avg_p_float8 +); +DROP FUNCTION udf_float8_accum(_float8, float8); + +-- invalid: initcond in agg and initcond in aggpartialfunc don't match +CREATE AGGREGATE udf_avg_invalid_initcond(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + initcond = '{1,0}', + finalfunc = int8_avg, + combinefunc = int4_avg_combine, + aggpartialfunc = avg_p_int4 +); + +-- invalid: aggserialfn in agg and aggserialfn in aggpartialfunc don't match +CREATE AGGREGATE udf_avg_invalid_aggserialfn(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_poly_avg, + combinefunc = int8_avg_combine, + serialfunc = numeric_avg_serialize, + deserialfunc = int8_avg_deserialize, + aggpartialfunc = avg_p_int8 +); + +-- invalid: aggdeserialfn in agg and aggdeserialfn in aggpartialfunc don't match +CREATE AGGREGATE udf_avg_invalid_aggdeserialfn(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_poly_avg, + combinefunc = int8_avg_combine, + serialfunc = int8_avg_serialize, + deserialfunc = numeric_avg_deserialize, + aggpartialfunc = avg_p_int8 +); + +-- invalid: stype is internal and aggpartialfunc's finalfunc +-- isn't serialfunc of agg +CREATE AGGREGATE udf_avg_p_int8(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_avg_serialize, + combinefunc = int8_avg_combine, + serialfunc = int8_avg_serialize, + deserialfunc = int8_avg_deserialize +); +CREATE AGGREGATE udf_avg(int8) ( + sfunc = int8_avg_accum, + stype = internal, + finalfunc = numeric_poly_avg, + combinefunc = int8_avg_combine, + serialfunc = int8_avg_serialize, + deserialfunc = int8_avg_deserialize, + aggpartialfunc = udf_avg_p_int8 +); + +-- invalid: stype is not internal and aggpartialfunc has finalfunc +CREATE AGGREGATE udf_avg_p_int4_hasfinal(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + finalfunc = int8_avg, + combinefunc = int4_avg_combine, + initcond = '{0,0}' +); +CREATE AGGREGATE udf_avg(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + finalfunc = int8_avg, + combinefunc = int4_avg_combine, + initcond = '{0,0}', + aggpartialfunc = udf_avg_p_int4_hasfinal +); + +-- invalid: current user doesn't have execute privilege on aggpartialfunc +CREATE AGGREGATE udf_avg_p_int4(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + combinefunc = int4_avg_combine, + initcond = '{0,0}' +); +CREATE USER regress_priv_user1; +GRANT ALL ON SCHEMA public TO regress_priv_user1; +REVOKE EXECUTE ON FUNCTION udf_avg_p_int4 FROM public; +SET SESSION AUTHORIZATION regress_priv_user1; + +CREATE AGGREGATE udf_avg_noprivilege(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + finalfunc = int8_avg, + combinefunc = int4_avg_combine, + initcond = '{0,0}', + aggpartialfunc = udf_avg_p_int4 +); +SET SESSION AUTHORIZATION DEFAULT; +REVOKE ALL ON SCHEMA public FROM regress_priv_user1; +DROP USER regress_priv_user1; + +-- An aggregate function that is aggpartialfunc +-- of another aggregate cannot be deleted. +CREATE AGGREGATE udf_avg_int4_p(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + combinefunc = int4_avg_combine, + initcond = '{0,0}', + aggpartialfunc = udf_avg_int4_p +); +CREATE AGGREGATE udf_avg_int4(int4) ( + sfunc = int4_avg_accum, + stype = _int8, + finalfunc = int8_avg, + combinefunc = int4_avg_combine, + initcond = '{0,0}', + aggpartialfunc = udf_avg_int4_p +); +DROP AGGREGATE udf_avg_int4_p(int4); +DROP AGGREGATE udf_avg_int4_p(int4) CASCADE; -- 2.39.3