mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
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.
This commit is contained in:
@ -101,24 +101,93 @@ CREATE AGGREGATE sumdouble (float8)
|
||||
msfunc = float8pl,
|
||||
minvfunc = float8mi
|
||||
);
|
||||
-- Test aggregate combine function
|
||||
-- ensure create aggregate works.
|
||||
CREATE AGGREGATE mysum (int)
|
||||
-- aggregate combine and serialization functions
|
||||
-- Ensure stype and serialtype can't be the same
|
||||
CREATE AGGREGATE myavg (numeric)
|
||||
(
|
||||
stype = int,
|
||||
sfunc = int4pl,
|
||||
combinefunc = int4pl
|
||||
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 myavg (numeric)
|
||||
(
|
||||
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
|
||||
------+-------------
|
||||
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 ****************
|
||||
|
||||
|
Reference in New Issue
Block a user