mirror of
https://github.com/postgres/postgres.git
synced 2026-02-15 16:48:17 -05:00
The original specification for this called for the deserialization function to have signature "deserialize(serialtype) returns transtype", which is a security violation if transtype is INTERNAL (which it always would be in practice) and serialtype is not (which ditto). The patch blithely overrode the opr_sanity check for that, which was sloppy-enough work in itself, but the indisputable reason this cannot be allowed to stand is that CREATE FUNCTION will reject such a signature and thus it'd be impossible for extensions to create parallelizable aggregates. The minimum fix to make the signature type-safe is to add a second, dummy argument of type INTERNAL. But to lock it down a bit more and make misuse of INTERNAL-accepting functions less likely, let's get rid of the ability to specify a "serialtype" for an aggregate and just say that the only useful serialtype is BYTEA --- which, in practice, is the only interesting value anyway, due to the usefulness of the send/recv infrastructure for this purpose. That means we only have to allow "serialize(internal) returns bytea" and "deserialize(bytea, internal) returns internal" as the signatures for these support functions. In passing fix bogus signature of int4_avg_combine, which I found thanks to adding an opr_sanity check on combinefunc signatures. catversion bump due to removing pg_aggregate.aggserialtype and adjusting signatures of assorted built-in functions. David Rowley and Tom Lane Discussion: <27247.1466185504@sss.pgh.pa.us>
210 lines
5.2 KiB
PL/PgSQL
210 lines
5.2 KiB
PL/PgSQL
--
|
|
-- CREATE_AGGREGATE
|
|
--
|
|
|
|
-- all functions CREATEd
|
|
CREATE AGGREGATE newavg (
|
|
sfunc = int4_avg_accum, basetype = int4, stype = _int8,
|
|
finalfunc = int8_avg,
|
|
initcond1 = '{0,0}'
|
|
);
|
|
|
|
-- test comments
|
|
COMMENT ON AGGREGATE newavg_wrong (int4) IS 'an agg comment';
|
|
COMMENT ON AGGREGATE newavg (int4) IS 'an agg comment';
|
|
COMMENT ON AGGREGATE newavg (int4) IS NULL;
|
|
|
|
-- without finalfunc; test obsolete spellings 'sfunc1' etc
|
|
CREATE AGGREGATE newsum (
|
|
sfunc1 = int4pl, basetype = int4, stype1 = int4,
|
|
initcond1 = '0'
|
|
);
|
|
|
|
-- zero-argument aggregate
|
|
CREATE AGGREGATE newcnt (*) (
|
|
sfunc = int8inc, stype = int8,
|
|
initcond = '0', parallel = safe
|
|
);
|
|
|
|
-- old-style spelling of same (except without parallel-safe; that's too new)
|
|
CREATE AGGREGATE oldcnt (
|
|
sfunc = int8inc, basetype = 'ANY', stype = int8,
|
|
initcond = '0'
|
|
);
|
|
|
|
-- aggregate that only cares about null/nonnull input
|
|
CREATE AGGREGATE newcnt ("any") (
|
|
sfunc = int8inc_any, stype = int8,
|
|
initcond = '0'
|
|
);
|
|
|
|
COMMENT ON AGGREGATE nosuchagg (*) IS 'should fail';
|
|
COMMENT ON AGGREGATE newcnt (*) IS 'an agg(*) comment';
|
|
COMMENT ON AGGREGATE newcnt ("any") IS 'an agg(any) comment';
|
|
|
|
-- multi-argument aggregate
|
|
create function sum3(int8,int8,int8) returns int8 as
|
|
'select $1 + $2 + $3' language sql strict immutable;
|
|
|
|
create aggregate sum2(int8,int8) (
|
|
sfunc = sum3, stype = int8,
|
|
initcond = '0'
|
|
);
|
|
|
|
-- multi-argument aggregates sensitive to distinct/order, strict/nonstrict
|
|
create type aggtype as (a integer, b integer, c text);
|
|
|
|
create function aggf_trans(aggtype[],integer,integer,text) returns aggtype[]
|
|
as 'select array_append($1,ROW($2,$3,$4)::aggtype)'
|
|
language sql strict immutable;
|
|
|
|
create function aggfns_trans(aggtype[],integer,integer,text) returns aggtype[]
|
|
as 'select array_append($1,ROW($2,$3,$4)::aggtype)'
|
|
language sql immutable;
|
|
|
|
create aggregate aggfstr(integer,integer,text) (
|
|
sfunc = aggf_trans, stype = aggtype[],
|
|
initcond = '{}'
|
|
);
|
|
|
|
create aggregate aggfns(integer,integer,text) (
|
|
sfunc = aggfns_trans, stype = aggtype[], sspace = 10000,
|
|
initcond = '{}'
|
|
);
|
|
|
|
-- variadic aggregate
|
|
create function least_accum(anyelement, variadic anyarray)
|
|
returns anyelement language sql as
|
|
'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)';
|
|
|
|
create aggregate least_agg(variadic items anyarray) (
|
|
stype = anyelement, sfunc = least_accum
|
|
);
|
|
|
|
-- test ordered-set aggs using built-in support functions
|
|
create aggregate my_percentile_disc(float8 ORDER BY anyelement) (
|
|
stype = internal,
|
|
sfunc = ordered_set_transition,
|
|
finalfunc = percentile_disc_final,
|
|
finalfunc_extra = true
|
|
);
|
|
|
|
create aggregate my_rank(VARIADIC "any" ORDER BY VARIADIC "any") (
|
|
stype = internal,
|
|
sfunc = ordered_set_transition_multi,
|
|
finalfunc = rank_final,
|
|
finalfunc_extra = true,
|
|
hypothetical
|
|
);
|
|
|
|
alter aggregate my_percentile_disc(float8 ORDER BY anyelement)
|
|
rename to test_percentile_disc;
|
|
alter aggregate my_rank(VARIADIC "any" ORDER BY VARIADIC "any")
|
|
rename to test_rank;
|
|
|
|
\da test_*
|
|
|
|
-- moving-aggregate options
|
|
|
|
CREATE AGGREGATE sumdouble (float8)
|
|
(
|
|
stype = float8,
|
|
sfunc = float8pl,
|
|
mstype = float8,
|
|
msfunc = float8pl,
|
|
minvfunc = float8mi
|
|
);
|
|
|
|
-- aggregate combine and serialization functions
|
|
|
|
-- can't specify just one of serialfunc and deserialfunc
|
|
CREATE AGGREGATE myavg (numeric)
|
|
(
|
|
stype = internal,
|
|
sfunc = numeric_avg_accum,
|
|
serialfunc = numeric_avg_serialize
|
|
);
|
|
|
|
-- serialfunc must have correct parameters
|
|
CREATE AGGREGATE myavg (numeric)
|
|
(
|
|
stype = internal,
|
|
sfunc = numeric_avg_accum,
|
|
serialfunc = numeric_avg_deserialize,
|
|
deserialfunc = numeric_avg_deserialize
|
|
);
|
|
|
|
-- deserialfunc must have correct parameters
|
|
CREATE AGGREGATE myavg (numeric)
|
|
(
|
|
stype = internal,
|
|
sfunc = numeric_avg_accum,
|
|
serialfunc = numeric_avg_serialize,
|
|
deserialfunc = numeric_avg_serialize
|
|
);
|
|
|
|
-- ensure combine function parameters are checked
|
|
CREATE AGGREGATE myavg (numeric)
|
|
(
|
|
stype = internal,
|
|
sfunc = numeric_avg_accum,
|
|
serialfunc = numeric_avg_serialize,
|
|
deserialfunc = numeric_avg_deserialize,
|
|
combinefunc = int4larger
|
|
);
|
|
|
|
-- ensure create aggregate works.
|
|
CREATE AGGREGATE myavg (numeric)
|
|
(
|
|
stype = internal,
|
|
sfunc = numeric_avg_accum,
|
|
finalfunc = numeric_avg,
|
|
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,aggserialfn,aggdeserialfn
|
|
FROM pg_aggregate
|
|
WHERE aggfnoid = 'myavg'::REGPROC;
|
|
|
|
DROP AGGREGATE myavg (numeric);
|
|
|
|
-- invalid: bad parallel-safety marking
|
|
CREATE AGGREGATE mysum (int)
|
|
(
|
|
stype = int,
|
|
sfunc = int4pl,
|
|
parallel = pear
|
|
);
|
|
|
|
-- invalid: nonstrict inverse with strict forward function
|
|
|
|
CREATE FUNCTION float8mi_n(float8, float8) RETURNS float8 AS
|
|
$$ SELECT $1 - $2; $$
|
|
LANGUAGE SQL;
|
|
|
|
CREATE AGGREGATE invalidsumdouble (float8)
|
|
(
|
|
stype = float8,
|
|
sfunc = float8pl,
|
|
mstype = float8,
|
|
msfunc = float8pl,
|
|
minvfunc = float8mi_n
|
|
);
|
|
|
|
-- invalid: non-matching result types
|
|
|
|
CREATE FUNCTION float8mi_int(float8, float8) RETURNS int AS
|
|
$$ SELECT CAST($1 - $2 AS INT); $$
|
|
LANGUAGE SQL;
|
|
|
|
CREATE AGGREGATE wrongreturntype (float8)
|
|
(
|
|
stype = float8,
|
|
sfunc = float8pl,
|
|
mstype = float8,
|
|
msfunc = float8pl,
|
|
minvfunc = float8mi_int
|
|
);
|