Commit 11c8669c authored by Robert Haas's avatar Robert Haas

Add parallel query support functions for assorted aggregates.

This lets us use parallel aggregate for a variety of useful cases
that didn't work before, like sum(int8), sum(numeric), several
versions of avg(), and various other functions.

Add some regression tests, as well, testing the general sanity of
these and future catalog entries.

David Rowley, reviewed by Tomas Vondra, with a few further changes
by me.
parent 71176854
This diff is collapsed.
......@@ -3524,6 +3524,55 @@ interval_accum(PG_FUNCTION_ARGS)
PG_RETURN_ARRAYTYPE_P(result);
}
Datum
interval_combine(PG_FUNCTION_ARGS)
{
ArrayType *transarray1 = PG_GETARG_ARRAYTYPE_P(0);
ArrayType *transarray2 = PG_GETARG_ARRAYTYPE_P(1);
Datum *transdatums1;
Datum *transdatums2;
int ndatums1;
int ndatums2;
Interval sum1,
N1;
Interval sum2,
N2;
Interval *newsum;
ArrayType *result;
deconstruct_array(transarray1,
INTERVALOID, sizeof(Interval), false, 'd',
&transdatums1, NULL, &ndatums1);
if (ndatums1 != 2)
elog(ERROR, "expected 2-element interval array");
sum1 = *(DatumGetIntervalP(transdatums1[0]));
N1 = *(DatumGetIntervalP(transdatums1[1]));
deconstruct_array(transarray2,
INTERVALOID, sizeof(Interval), false, 'd',
&transdatums2, NULL, &ndatums2);
if (ndatums2 != 2)
elog(ERROR, "expected 2-element interval array");
sum2 = *(DatumGetIntervalP(transdatums2[0]));
N2 = *(DatumGetIntervalP(transdatums2[1]));
newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl,
IntervalPGetDatum(&sum1),
IntervalPGetDatum(&sum2)));
N1.time += N2.time;
transdatums1[0] = IntervalPGetDatum(newsum);
transdatums1[1] = IntervalPGetDatum(&N1);
result = construct_array(transdatums1, 2,
INTERVALOID, sizeof(Interval), false, 'd');
PG_RETURN_ARRAYTYPE_P(result);
}
Datum
interval_accum_inv(PG_FUNCTION_ARGS)
{
......
......@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201604051
#define CATALOG_VERSION_NO 201604052
#endif
This diff is collapsed.
......@@ -2441,8 +2441,20 @@ DATA(insert OID = 1832 ( float8_stddev_samp PGNSP PGUID 12 1 0 0 0 f f f f t f
DESCR("aggregate final function");
DATA(insert OID = 1833 ( numeric_accum PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ _null_ numeric_accum _null_ _null_ _null_ ));
DESCR("aggregate transition function");
DATA(insert OID = 3341 ( numeric_combine PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ numeric_combine _null_ _null_ _null_ ));
DESCR("aggregate combine function");
DATA(insert OID = 2858 ( numeric_avg_accum PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ _null_ numeric_avg_accum _null_ _null_ _null_ ));
DESCR("aggregate transition function");
DATA(insert OID = 3337 ( numeric_avg_combine PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ numeric_avg_combine _null_ _null_ _null_ ));
DESCR("aggregate combine function");
DATA(insert OID = 2740 ( numeric_avg_serialize PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 17 "2281" _null_ _null_ _null_ _null_ _null_ numeric_avg_serialize _null_ _null_ _null_ ));
DESCR("aggregate serial function");
DATA(insert OID = 2741 ( numeric_avg_deserialize PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "17" _null_ _null_ _null_ _null_ _null_ numeric_avg_deserialize _null_ _null_ _null_ ));
DESCR("aggregate deserial function");
DATA(insert OID = 3335 ( numeric_serialize PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 17 "2281" _null_ _null_ _null_ _null_ _null_ numeric_serialize _null_ _null_ _null_ ));
DESCR("aggregate serial function");
DATA(insert OID = 3336 ( numeric_deserialize PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "17" _null_ _null_ _null_ _null_ _null_ numeric_deserialize _null_ _null_ _null_ ));
DESCR("aggregate deserial function");
DATA(insert OID = 3548 ( numeric_accum_inv PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ _null_ numeric_accum_inv _null_ _null_ _null_ ));
DESCR("aggregate transition function");
DATA(insert OID = 1834 ( int2_accum PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 21" _null_ _null_ _null_ _null_ _null_ int2_accum _null_ _null_ _null_ ));
......@@ -2451,6 +2463,12 @@ DATA(insert OID = 1835 ( int4_accum PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2
DESCR("aggregate transition function");
DATA(insert OID = 1836 ( int8_accum PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 20" _null_ _null_ _null_ _null_ _null_ int8_accum _null_ _null_ _null_ ));
DESCR("aggregate transition function");
DATA(insert OID = 3338 ( numeric_poly_combine PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ numeric_poly_combine _null_ _null_ _null_ ));
DESCR("aggregate combine function");
DATA(insert OID = 3339 ( numeric_poly_serialize PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 17 "2281" _null_ _null_ _null_ _null_ _null_ numeric_poly_serialize _null_ _null_ _null_ ));
DESCR("aggregate serial function");
DATA(insert OID = 3340 ( numeric_poly_deserialize PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "17" _null_ _null_ _null_ _null_ _null_ numeric_poly_deserialize _null_ _null_ _null_ ));
DESCR("aggregate deserial function");
DATA(insert OID = 2746 ( int8_avg_accum PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 20" _null_ _null_ _null_ _null_ _null_ int8_avg_accum _null_ _null_ _null_ ));
DESCR("aggregate transition function");
DATA(insert OID = 3567 ( int2_accum_inv PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 21" _null_ _null_ _null_ _null_ _null_ int2_accum_inv _null_ _null_ _null_ ));
......@@ -2461,6 +2479,14 @@ DATA(insert OID = 3569 ( int8_accum_inv PGNSP PGUID 12 1 0 0 0 f f f f f f i
DESCR("aggregate transition function");
DATA(insert OID = 3387 ( int8_avg_accum_inv PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 20" _null_ _null_ _null_ _null_ _null_ int8_avg_accum_inv _null_ _null_ _null_ ));
DESCR("aggregate transition function");
DATA(insert OID = 2785 ( int8_avg_combine PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ int8_avg_combine _null_ _null_ _null_ ));
DESCR("aggregate combine function");
DATA(insert OID = 2786 ( int8_avg_serialize PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 17 "2281" _null_ _null_ _null_ _null_ _null_ int8_avg_serialize _null_ _null_ _null_ ));
DESCR("aggregate serial function");
DATA(insert OID = 2787 ( int8_avg_deserialize PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "17" _null_ _null_ _null_ _null_ _null_ int8_avg_deserialize _null_ _null_ _null_ ));
DESCR("aggregate deserial function");
DATA(insert OID = 3324 ( int4_avg_combine PGNSP PGUID 12 1 0 0 0 f f f f f f i s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ int4_avg_combine _null_ _null_ _null_ ));
DESCR("aggregate combine function");
DATA(insert OID = 3178 ( numeric_sum PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 1700 "2281" _null_ _null_ _null_ _null_ _null_ numeric_sum _null_ _null_ _null_ ));
DESCR("aggregate final function");
DATA(insert OID = 1837 ( numeric_avg PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 1700 "2281" _null_ _null_ _null_ _null_ _null_ numeric_avg _null_ _null_ _null_ ));
......@@ -2494,6 +2520,8 @@ DESCR("aggregate final function");
DATA(insert OID = 1843 ( interval_accum PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 1187 "1187 1186" _null_ _null_ _null_ _null_ _null_ interval_accum _null_ _null_ _null_ ));
DESCR("aggregate transition function");
DATA(insert OID = 3325 ( interval_combine PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 1187 "1187 1187" _null_ _null_ _null_ _null_ _null_ interval_combine _null_ _null_ _null_ ));
DESCR("aggregate combine function");
DATA(insert OID = 3549 ( interval_accum_inv PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 1187 "1187 1186" _null_ _null_ _null_ _null_ _null_ interval_accum_inv _null_ _null_ _null_ ));
DESCR("aggregate transition function");
DATA(insert OID = 1844 ( interval_avg PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 1186 "1187" _null_ _null_ _null_ _null_ _null_ interval_avg _null_ _null_ _null_ ));
......
......@@ -1064,15 +1064,27 @@ extern Datum numeric_float8_no_overflow(PG_FUNCTION_ARGS);
extern Datum float4_numeric(PG_FUNCTION_ARGS);
extern Datum numeric_float4(PG_FUNCTION_ARGS);
extern Datum numeric_accum(PG_FUNCTION_ARGS);
extern Datum numeric_combine(PG_FUNCTION_ARGS);
extern Datum numeric_avg_accum(PG_FUNCTION_ARGS);
extern Datum numeric_avg_combine(PG_FUNCTION_ARGS);
extern Datum numeric_avg_serialize(PG_FUNCTION_ARGS);
extern Datum numeric_avg_deserialize(PG_FUNCTION_ARGS);
extern Datum numeric_serialize(PG_FUNCTION_ARGS);
extern Datum numeric_deserialize(PG_FUNCTION_ARGS);
extern Datum numeric_accum_inv(PG_FUNCTION_ARGS);
extern Datum int2_accum(PG_FUNCTION_ARGS);
extern Datum int4_accum(PG_FUNCTION_ARGS);
extern Datum int8_accum(PG_FUNCTION_ARGS);
extern Datum numeric_poly_combine(PG_FUNCTION_ARGS);
extern Datum numeric_poly_serialize(PG_FUNCTION_ARGS);
extern Datum numeric_poly_deserialize(PG_FUNCTION_ARGS);
extern Datum int2_accum_inv(PG_FUNCTION_ARGS);
extern Datum int4_accum_inv(PG_FUNCTION_ARGS);
extern Datum int8_accum_inv(PG_FUNCTION_ARGS);
extern Datum int8_avg_accum(PG_FUNCTION_ARGS);
extern Datum int8_avg_combine(PG_FUNCTION_ARGS);
extern Datum int8_avg_serialize(PG_FUNCTION_ARGS);
extern Datum int8_avg_deserialize(PG_FUNCTION_ARGS);
extern Datum numeric_avg(PG_FUNCTION_ARGS);
extern Datum numeric_sum(PG_FUNCTION_ARGS);
extern Datum numeric_var_pop(PG_FUNCTION_ARGS);
......@@ -1090,6 +1102,7 @@ extern Datum int4_sum(PG_FUNCTION_ARGS);
extern Datum int8_sum(PG_FUNCTION_ARGS);
extern Datum int2_avg_accum(PG_FUNCTION_ARGS);
extern Datum int4_avg_accum(PG_FUNCTION_ARGS);
extern Datum int4_avg_combine(PG_FUNCTION_ARGS);
extern Datum int2_avg_accum_inv(PG_FUNCTION_ARGS);
extern Datum int4_avg_accum_inv(PG_FUNCTION_ARGS);
extern Datum int8_avg_accum_inv(PG_FUNCTION_ARGS);
......
......@@ -187,6 +187,7 @@ extern Datum interval_mul(PG_FUNCTION_ARGS);
extern Datum mul_d_interval(PG_FUNCTION_ARGS);
extern Datum interval_div(PG_FUNCTION_ARGS);
extern Datum interval_accum(PG_FUNCTION_ARGS);
extern Datum interval_combine(PG_FUNCTION_ARGS);
extern Datum interval_accum_inv(PG_FUNCTION_ARGS);
extern Datum interval_avg(PG_FUNCTION_ARGS);
......
......@@ -101,24 +101,93 @@ CREATE AGGREGATE sumdouble (float8)
msfunc = float8pl,
minvfunc = float8mi
);
-- Test aggregate combine function
-- aggregate combine and serialization functions
-- Ensure stype and serialtype can't be the same
CREATE AGGREGATE myavg (numeric)
(
stype = internal,
sfunc = numeric_avg_accum,
serialtype = internal
);
ERROR: aggregate serialization type cannot be "internal"
-- if serialtype is specified we need a serialfunc and deserialfunc
CREATE AGGREGATE myavg (numeric)
(
stype = internal,
sfunc = numeric_avg_accum,
serialtype = bytea
);
ERROR: aggregate serialization function must be specified when serialization type is specified
CREATE AGGREGATE myavg (numeric)
(
stype = internal,
sfunc = numeric_avg_accum,
serialtype = bytea,
serialfunc = numeric_avg_serialize
);
ERROR: aggregate deserialization function must be specified when serialization type is specified
-- serialfunc must have correct parameters
CREATE AGGREGATE myavg (numeric)
(
stype = internal,
sfunc = numeric_avg_accum,
serialtype = bytea,
serialfunc = numeric_avg_deserialize,
deserialfunc = numeric_avg_deserialize
);
ERROR: function numeric_avg_deserialize(internal) does not exist
-- deserialfunc must have correct parameters
CREATE AGGREGATE myavg (numeric)
(
stype = internal,
sfunc = numeric_avg_accum,
serialtype = bytea,
serialfunc = numeric_avg_serialize,
deserialfunc = numeric_avg_serialize
);
ERROR: function numeric_avg_serialize(bytea) does not exist
-- ensure return type of serialfunc is checked
CREATE AGGREGATE myavg (numeric)
(
stype = internal,
sfunc = numeric_avg_accum,
serialtype = text,
serialfunc = numeric_avg_serialize,
deserialfunc = numeric_avg_deserialize
);
ERROR: return type of serialization function numeric_avg_serialize is not text
-- ensure combine function parameters are checked
CREATE AGGREGATE myavg (numeric)
(
stype = internal,
sfunc = numeric_avg_accum,
serialtype = bytea,
serialfunc = numeric_avg_serialize,
deserialfunc = numeric_avg_deserialize,
combinefunc = int4larger
);
ERROR: function int4larger(internal, internal) does not exist
-- ensure create aggregate works.
CREATE AGGREGATE mysum (int)
CREATE AGGREGATE myavg (numeric)
(
stype = int,
sfunc = int4pl,
combinefunc = int4pl
stype = internal,
sfunc = numeric_avg_accum,
finalfunc = numeric_avg,
serialtype = bytea,
serialfunc = numeric_avg_serialize,
deserialfunc = numeric_avg_deserialize,
combinefunc = numeric_avg_combine
);
-- Ensure all these functions made it into the catalog
SELECT aggfnoid,aggtransfn,aggcombinefn,aggtranstype
SELECT aggfnoid,aggtransfn,aggcombinefn,aggtranstype,aggserialfn,aggdeserialfn,aggserialtype
FROM pg_aggregate
WHERE aggfnoid = 'mysum'::REGPROC;
aggfnoid | aggtransfn | aggcombinefn | aggtranstype
----------+------------+--------------+--------------
mysum | int4pl | int4pl | 23
WHERE aggfnoid = 'myavg'::REGPROC;
aggfnoid | aggtransfn | aggcombinefn | aggtranstype | aggserialfn | aggdeserialfn | aggserialtype
----------+-------------------+---------------------+--------------+-----------------------+-------------------------+---------------
myavg | numeric_avg_accum | numeric_avg_combine | 2281 | numeric_avg_serialize | numeric_avg_deserialize | 17
(1 row)
DROP AGGREGATE mysum (int);
DROP AGGREGATE myavg (numeric);
-- invalid: nonstrict inverse with strict forward function
CREATE FUNCTION float8mi_n(float8, float8) RETURNS float8 AS
$$ SELECT $1 - $2; $$
......
......@@ -279,15 +279,21 @@ ORDER BY 1, 2;
-- Look for functions that return type "internal" and do not have any
-- "internal" argument. Such a function would be a security hole since
-- it might be used to call an internal function from an SQL command.
-- As of 7.3 this query should find only internal_in.
-- As of 7.3 this query should find internal_in, and as of 9.6 aggregate
-- deserialization will be found too. These should contain a runtime check to
-- ensure they can only be called in an aggregate context.
SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype = 'internal'::regtype AND NOT
'internal'::regtype = ANY (p1.proargtypes);
oid | proname
------+-------------
------+--------------------------
2741 | numeric_avg_deserialize
3336 | numeric_deserialize
3340 | numeric_poly_deserialize
2787 | int8_avg_deserialize
2304 | internal_in
(1 row)
(5 rows)
-- Look for functions that return a polymorphic type and do not have any
-- polymorphic argument. Calls of such functions would be unresolvable
......@@ -1528,6 +1534,89 @@ WHERE proisagg AND provariadic != 0 AND a.aggkind = 'n';
-----+---------
(0 rows)
-- Check that all serial functions have a return type the same as the serial
-- type.
SELECT a.aggserialfn,a.aggserialtype,p.prorettype
FROM pg_aggregate a
INNER JOIN pg_proc p ON a.aggserialfn = p.oid
WHERE a.aggserialtype <> p.prorettype;
aggserialfn | aggserialtype | prorettype
-------------+---------------+------------
(0 rows)
-- Check that all the deserial functions have the same input type as the
-- serialtype
SELECT a.aggserialfn,a.aggserialtype,p.proargtypes[0]
FROM pg_aggregate a
INNER JOIN pg_proc p ON a.aggdeserialfn = p.oid
WHERE p.proargtypes[0] <> a.aggserialtype;
aggserialfn | aggserialtype | proargtypes
-------------+---------------+-------------
(0 rows)
-- An aggregate should either have a complete set of serialtype, serial func
-- and deserial func, or none of them.
SELECT aggserialtype,aggserialfn,aggdeserialfn
FROM pg_aggregate
WHERE (aggserialtype <> 0 OR aggserialfn <> 0 OR aggdeserialfn <> 0)
AND (aggserialtype = 0 OR aggserialfn = 0 OR aggdeserialfn = 0);
aggserialtype | aggserialfn | aggdeserialfn
---------------+-------------+---------------
(0 rows)
-- Check that all aggregates with serialtypes have internal states.
-- (There's no point in serializing anything apart from internal)
SELECT aggfnoid,aggserialtype,aggtranstype
FROM pg_aggregate
WHERE aggserialtype <> 0 AND aggtranstype <> 'internal'::regtype;
aggfnoid | aggserialtype | aggtranstype
----------+---------------+--------------
(0 rows)
-- Check that all serial functions are strict. It's wasteful for these to be
-- called with NULL values.
SELECT aggfnoid,aggserialfn
FROM pg_aggregate a
INNER JOIN pg_proc p ON a.aggserialfn = p.oid
WHERE p.proisstrict = false;
aggfnoid | aggserialfn
----------+-------------
(0 rows)
-- Check that all deserial functions are strict. It's wasteful for these to be
-- called with NULL values.
SELECT aggfnoid,aggdeserialfn
FROM pg_aggregate a
INNER JOIN pg_proc p ON a.aggdeserialfn = p.oid
WHERE p.proisstrict = false;
aggfnoid | aggdeserialfn
----------+---------------
(0 rows)
-- Check that no combine functions with an INTERNAL return type are strict.
SELECT aggfnoid,aggcombinefn
FROM pg_aggregate a
INNER JOIN pg_proc p ON a.aggcombinefn = p.oid
INNER JOIN pg_type t ON a.aggtranstype = t.oid
WHERE t.typname = 'internal' AND p.proisstrict = true;
aggfnoid | aggcombinefn
----------+--------------
(0 rows)
-- Check that aggregates which have the same transition function also have
-- the same combine, serialization, and deserialization functions.
SELECT a.aggfnoid, a.aggcombinefn, a.aggserialfn, a.aggdeserialfn,
b.aggfnoid, b.aggcombinefn, b.aggserialfn, b.aggdeserialfn
FROM
pg_aggregate a, pg_aggregate b
WHERE
a.aggfnoid < b.aggfnoid AND a.aggtransfn = b.aggtransfn AND
(a.aggcombinefn != b.aggcombinefn OR a.aggserialfn != b.aggserialfn
OR a.aggdeserialfn != b.aggdeserialfn);
aggfnoid | aggcombinefn | aggserialfn | aggdeserialfn | aggfnoid | aggcombinefn | aggserialfn | aggdeserialfn
----------+--------------+-------------+---------------+----------+--------------+-------------+---------------
(0 rows)
-- **************** pg_opfamily ****************
-- Look for illegal values in pg_opfamily fields
SELECT p1.oid
......
......@@ -115,22 +115,91 @@ CREATE AGGREGATE sumdouble (float8)
minvfunc = float8mi
);
-- Test aggregate combine function
-- aggregate combine and serialization functions
-- Ensure stype and serialtype can't be the same
CREATE AGGREGATE myavg (numeric)
(
stype = internal,
sfunc = numeric_avg_accum,
serialtype = internal
);
-- if serialtype is specified we need a serialfunc and deserialfunc
CREATE AGGREGATE myavg (numeric)
(
stype = internal,
sfunc = numeric_avg_accum,
serialtype = bytea
);
CREATE AGGREGATE myavg (numeric)
(
stype = internal,
sfunc = numeric_avg_accum,
serialtype = bytea,
serialfunc = numeric_avg_serialize
);
-- serialfunc must have correct parameters
CREATE AGGREGATE myavg (numeric)
(
stype = internal,
sfunc = numeric_avg_accum,
serialtype = bytea,
serialfunc = numeric_avg_deserialize,
deserialfunc = numeric_avg_deserialize
);
-- deserialfunc must have correct parameters
CREATE AGGREGATE myavg (numeric)
(
stype = internal,
sfunc = numeric_avg_accum,
serialtype = bytea,
serialfunc = numeric_avg_serialize,
deserialfunc = numeric_avg_serialize
);
-- ensure return type of serialfunc is checked
CREATE AGGREGATE myavg (numeric)
(
stype = internal,
sfunc = numeric_avg_accum,
serialtype = text,
serialfunc = numeric_avg_serialize,
deserialfunc = numeric_avg_deserialize
);
-- ensure combine function parameters are checked
CREATE AGGREGATE myavg (numeric)
(
stype = internal,
sfunc = numeric_avg_accum,
serialtype = bytea,
serialfunc = numeric_avg_serialize,
deserialfunc = numeric_avg_deserialize,
combinefunc = int4larger
);
-- ensure create aggregate works.
CREATE AGGREGATE mysum (int)
CREATE AGGREGATE myavg (numeric)
(
stype = int,
sfunc = int4pl,
combinefunc = int4pl
stype = internal,
sfunc = numeric_avg_accum,
finalfunc = numeric_avg,
serialtype = bytea,
serialfunc = numeric_avg_serialize,
deserialfunc = numeric_avg_deserialize,
combinefunc = numeric_avg_combine
);
-- Ensure all these functions made it into the catalog
SELECT aggfnoid,aggtransfn,aggcombinefn,aggtranstype
SELECT aggfnoid,aggtransfn,aggcombinefn,aggtranstype,aggserialfn,aggdeserialfn,aggserialtype
FROM pg_aggregate
WHERE aggfnoid = 'mysum'::REGPROC;
WHERE aggfnoid = 'myavg'::REGPROC;
DROP AGGREGATE mysum (int);
DROP AGGREGATE myavg (numeric);
-- invalid: nonstrict inverse with strict forward function
......
......@@ -228,7 +228,9 @@ ORDER BY 1, 2;
-- Look for functions that return type "internal" and do not have any
-- "internal" argument. Such a function would be a security hole since
-- it might be used to call an internal function from an SQL command.
-- As of 7.3 this query should find only internal_in.
-- As of 7.3 this query should find internal_in, and as of 9.6 aggregate
-- deserialization will be found too. These should contain a runtime check to
-- ensure they can only be called in an aggregate context.
SELECT p1.oid, p1.proname
FROM pg_proc as p1
......@@ -1002,6 +1004,64 @@ SELECT p.oid, proname
FROM pg_proc AS p JOIN pg_aggregate AS a ON a.aggfnoid = p.oid
WHERE proisagg AND provariadic != 0 AND a.aggkind = 'n';
-- Check that all serial functions have a return type the same as the serial
-- type.
SELECT a.aggserialfn,a.aggserialtype,p.prorettype
FROM pg_aggregate a
INNER JOIN pg_proc p ON a.aggserialfn = p.oid
WHERE a.aggserialtype <> p.prorettype;
-- Check that all the deserial functions have the same input type as the
-- serialtype
SELECT a.aggserialfn,a.aggserialtype,p.proargtypes[0]
FROM pg_aggregate a
INNER JOIN pg_proc p ON a.aggdeserialfn = p.oid
WHERE p.proargtypes[0] <> a.aggserialtype;
-- An aggregate should either have a complete set of serialtype, serial func
-- and deserial func, or none of them.
SELECT aggserialtype,aggserialfn,aggdeserialfn
FROM pg_aggregate
WHERE (aggserialtype <> 0 OR aggserialfn <> 0 OR aggdeserialfn <> 0)
AND (aggserialtype = 0 OR aggserialfn = 0 OR aggdeserialfn = 0);
-- Check that all aggregates with serialtypes have internal states.
-- (There's no point in serializing anything apart from internal)
SELECT aggfnoid,aggserialtype,aggtranstype
FROM pg_aggregate
WHERE aggserialtype <> 0 AND aggtranstype <> 'internal'::regtype;
-- Check that all serial functions are strict. It's wasteful for these to be
-- called with NULL values.
SELECT aggfnoid,aggserialfn
FROM pg_aggregate a
INNER JOIN pg_proc p ON a.aggserialfn = p.oid
WHERE p.proisstrict = false;
-- Check that all deserial functions are strict. It's wasteful for these to be
-- called with NULL values.
SELECT aggfnoid,aggdeserialfn
FROM pg_aggregate a
INNER JOIN pg_proc p ON a.aggdeserialfn = p.oid
WHERE p.proisstrict = false;
-- Check that no combine functions with an INTERNAL return type are strict.
SELECT aggfnoid,aggcombinefn
FROM pg_aggregate a
INNER JOIN pg_proc p ON a.aggcombinefn = p.oid
INNER JOIN pg_type t ON a.aggtranstype = t.oid
WHERE t.typname = 'internal' AND p.proisstrict = true;
-- Check that aggregates which have the same transition function also have
-- the same combine, serialization, and deserialization functions.
SELECT a.aggfnoid, a.aggcombinefn, a.aggserialfn, a.aggdeserialfn,
b.aggfnoid, b.aggcombinefn, b.aggserialfn, b.aggdeserialfn
FROM
pg_aggregate a, pg_aggregate b
WHERE
a.aggfnoid < b.aggfnoid AND a.aggtransfn = b.aggtransfn AND
(a.aggcombinefn != b.aggcombinefn OR a.aggserialfn != b.aggserialfn
OR a.aggdeserialfn != b.aggdeserialfn);
-- **************** pg_opfamily ****************
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment