mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-15 18:13:18 +08:00
Add support for FINALFUNC_EXTRA
This PR adds support for finalizing aggregates with FINALFUNC_EXTRA. To do this, we need to pass NULLS correspond to all of the aggregate parameters to the ffunc as arguments following the partial state value. These arguments need to have the correct concrete types. For polymorphic aggregates, the types cannot be derived from the catalog but need to be somehow conveyed to the finalize_agg. Two designs were considered: 1) Encode the type names as part of the partial state (bytea) 2) Pass down the arguments as parameters to the finalize_agg In the end (2) was picked for the simple reason that (1) would have increased the size of each partial, sometimes considerably (esp. for small partial values). The types are passed down as names not OIDs because in the continuous agg case using OIDs is not safe for backup/restore and in the clustering case the datanodes may not have the same type OIDs either.
This commit is contained in:
parent
0f0b3e07e5
commit
d7b6ad239b
@ -51,7 +51,7 @@ CREATE AGGREGATE histogram (DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION
|
||||
FINALFUNC_EXTRA
|
||||
);
|
||||
|
||||
CREATE AGGREGATE _timescaledb_internal.finalize_agg( TEXT, NAME, NAME, BYTEA, anyelement) (
|
||||
CREATE AGGREGATE _timescaledb_internal.finalize_agg(agg_name TEXT, inner_agg_collation_schema NAME, inner_agg_collation_name NAME, inner_agg_input_types NAME[][], inner_agg_serialized_state BYTEA, return_type_dummy_val anyelement) (
|
||||
SFUNC = _timescaledb_internal.finalize_agg_sfunc,
|
||||
STYPE = internal,
|
||||
FINALFUNC = _timescaledb_internal.finalize_agg_ffunc,
|
||||
|
@ -3,18 +3,18 @@
|
||||
DROP FUNCTION IF EXISTS locf(ANYELEMENT,ANYELEMENT);
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.finalize_agg_sfunc(
|
||||
tstate internal, aggfn TEXT, inner_agg_collation_schema NAME, inner_agg_collation_name NAME, inner_agg_serialized_state BYTEA, return_type_dummy_val ANYELEMENT)
|
||||
tstate internal, aggfn TEXT, inner_agg_collation_schema NAME, inner_agg_collation_name NAME, inner_agg_input_types NAME[][], inner_agg_serialized_state BYTEA, return_type_dummy_val ANYELEMENT)
|
||||
RETURNS internal
|
||||
AS '@MODULE_PATHNAME@', 'ts_finalize_agg_sfunc'
|
||||
LANGUAGE C IMMUTABLE ;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.finalize_agg_ffunc(
|
||||
tstate internal, aggfn TEXT, inner_agg_collation_schema NAME, inner_agg_collation_name NAME, inner_agg_serialized_state BYTEA, return_type_dummy_val ANYELEMENT)
|
||||
tstate internal, aggfn TEXT, inner_agg_collation_schema NAME, inner_agg_collation_name NAME, inner_agg_input_types NAME[][], inner_agg_serialized_state BYTEA, return_type_dummy_val ANYELEMENT)
|
||||
RETURNS anyelement
|
||||
AS '@MODULE_PATHNAME@', 'ts_finalize_agg_ffunc'
|
||||
LANGUAGE C IMMUTABLE ;
|
||||
|
||||
CREATE AGGREGATE _timescaledb_internal.finalize_agg(TEXT, NAME, NAME, BYTEA, anyelement) (
|
||||
CREATE AGGREGATE _timescaledb_internal.finalize_agg(agg_name TEXT, inner_agg_collation_schema NAME, inner_agg_collation_name NAME, inner_agg_input_types NAME[][], inner_agg_serialized_state BYTEA, return_type_dummy_val anyelement) (
|
||||
SFUNC = _timescaledb_internal.finalize_agg_sfunc,
|
||||
STYPE = internal,
|
||||
FINALFUNC = _timescaledb_internal.finalize_agg_ffunc,
|
||||
|
@ -6,13 +6,13 @@ CREATE OR REPLACE FUNCTION _timescaledb_internal.partialize_agg(arg ANYELEMENT)
|
||||
RETURNS BYTEA AS '@MODULE_PATHNAME@', 'ts_partialize_agg' LANGUAGE C VOLATILE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.finalize_agg_sfunc(
|
||||
tstate internal, aggfn TEXT, inner_agg_collation_schema NAME, inner_agg_collation_name NAME, inner_agg_serialized_state BYTEA, return_type_dummy_val ANYELEMENT)
|
||||
RETURNS internal
|
||||
tstate internal, aggfn TEXT, inner_agg_collation_schema NAME, inner_agg_collation_name NAME, inner_agg_input_types NAME[][], inner_agg_serialized_state BYTEA, return_type_dummy_val ANYELEMENT)
|
||||
RETURNS internal
|
||||
AS '@MODULE_PATHNAME@', 'ts_finalize_agg_sfunc'
|
||||
LANGUAGE C IMMUTABLE ;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.finalize_agg_ffunc(
|
||||
tstate internal, aggfn TEXT, inner_agg_collation_schema NAME, inner_agg_collation_name NAME, inner_agg_serialized_state BYTEA, return_type_dummy_val ANYELEMENT)
|
||||
tstate internal, aggfn TEXT, inner_agg_collation_schema NAME, inner_agg_collation_name NAME, inner_agg_input_types NAME[][], inner_agg_serialized_state BYTEA, return_type_dummy_val ANYELEMENT)
|
||||
RETURNS anyelement
|
||||
AS '@MODULE_PATHNAME@', 'ts_finalize_agg_ffunc'
|
||||
LANGUAGE C IMMUTABLE ;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <util_aggfns.h>
|
||||
#include <catalog/namespace.h>
|
||||
#include <catalog/pg_collation.h>
|
||||
#include <parser/parse_agg.h>
|
||||
|
||||
TS_FUNCTION_INFO_V1(tsl_finalize_agg_sfunc);
|
||||
TS_FUNCTION_INFO_V1(tsl_finalize_agg_ffunc);
|
||||
@ -185,9 +186,70 @@ inner_agg_deserialize(FACombineFnMeta *combine_meta, bytea *serialized_partial,
|
||||
PG_RETURN_DATUM(deserialized);
|
||||
}
|
||||
|
||||
/* Convert a 2-dimensional array of schema, names to type OIDs */
|
||||
static Oid *
|
||||
get_input_types(ArrayType *input_types, size_t *number_types)
|
||||
{
|
||||
ArrayMetaState meta = { .element_type = NAMEOID };
|
||||
ArrayIterator iter;
|
||||
Datum slice_datum;
|
||||
bool slice_null;
|
||||
Oid *type_oids;
|
||||
int type_index = 0;
|
||||
|
||||
if (input_types == NULL)
|
||||
elog(ERROR, "cannot pass null input_type with FINALFUNC_EXTRA aggregates");
|
||||
|
||||
get_typlenbyvalalign(meta.element_type, &meta.typlen, &meta.typbyval, &meta.typalign);
|
||||
|
||||
if (ARR_NDIM(input_types) != 2)
|
||||
elog(ERROR, "invalid input type array: wrong number of dimensions");
|
||||
|
||||
*number_types = ARR_DIMS(input_types)[0];
|
||||
type_oids = palloc0(sizeof(*type_oids) * (*number_types));
|
||||
|
||||
iter = array_create_iterator(input_types, 1, &meta);
|
||||
|
||||
while (array_iterate(iter, &slice_datum, &slice_null))
|
||||
{
|
||||
Datum *slice_fields;
|
||||
int slice_elems;
|
||||
Name schema;
|
||||
Name type_name;
|
||||
Oid schema_oid;
|
||||
Oid type_oid;
|
||||
ArrayType *slice_array = DatumGetArrayTypeP(slice_datum);
|
||||
if (slice_null)
|
||||
elog(ERROR, "invalid input type array slice: cannot be null");
|
||||
deconstruct_array(slice_array,
|
||||
meta.element_type,
|
||||
meta.typlen,
|
||||
meta.typbyval,
|
||||
meta.typalign,
|
||||
&slice_fields,
|
||||
NULL,
|
||||
&slice_elems);
|
||||
if (slice_elems != 2)
|
||||
elog(ERROR, "invalid input type array: expecting slices of size 2");
|
||||
|
||||
schema = DatumGetName(slice_fields[0]);
|
||||
type_name = DatumGetName(slice_fields[1]);
|
||||
|
||||
schema_oid = get_namespace_oid(NameStr(*schema), false);
|
||||
type_oid = GetSysCacheOid2(TYPENAMENSP,
|
||||
PointerGetDatum(NameStr(*type_name)),
|
||||
ObjectIdGetDatum(schema_oid));
|
||||
if (!OidIsValid(type_oid))
|
||||
elog(ERROR, "invalid input type: %s.%s", NameStr(*schema), NameStr(*type_name));
|
||||
|
||||
type_oids[type_index++] = type_oid;
|
||||
}
|
||||
return type_oids;
|
||||
};
|
||||
|
||||
static FATransitionState *
|
||||
fa_transition_state_init(MemoryContext *fa_context, Oid inner_agg_fn_oid, Oid collation,
|
||||
AggState *fa_aggstate)
|
||||
AggState *fa_aggstate, ArrayType *input_types)
|
||||
{
|
||||
FATransitionState *tstate = NULL;
|
||||
HeapTuple inner_agg_tuple;
|
||||
@ -212,18 +274,6 @@ fa_transition_state_init(MemoryContext *fa_context, Oid inner_agg_fn_oid, Oid co
|
||||
elog(ERROR,
|
||||
"function calls with direct args are not supported by TimescaleDB finalize agg");
|
||||
|
||||
/*
|
||||
* For now we're going to disallow functions that have finalextra args. We
|
||||
* had hoped to support but, it turns out that it's a bit harder to support
|
||||
* than we'd realized, basically because the final extra arg currently
|
||||
* means that null values of the arg types are passed in to the finalize
|
||||
* function. We can't do this currently as we have effectively lost those
|
||||
* values. This can be one arg or more than one arg depending on the
|
||||
* aggregate type etc.
|
||||
*/
|
||||
if (inner_agg_form->aggfinalextra)
|
||||
elog(ERROR,
|
||||
"function calls with finalextra args are not supported by TimescaleDB finalize agg");
|
||||
tstate->final_meta->finalfnoid = inner_agg_form->aggfinalfn;
|
||||
tstate->combine_meta->combinefnoid = inner_agg_form->aggcombinefn;
|
||||
tstate->combine_meta->deserialfnoid = inner_agg_form->aggdeserialfn;
|
||||
@ -258,14 +308,43 @@ fa_transition_state_init(MemoryContext *fa_context, Oid inner_agg_fn_oid, Oid co
|
||||
/* initialize finalfn specific state */
|
||||
if (OidIsValid(tstate->final_meta->finalfnoid))
|
||||
{
|
||||
int num_args = 1;
|
||||
Oid *types = NULL;
|
||||
size_t number_types = 0;
|
||||
if (inner_agg_form->aggfinalextra)
|
||||
{
|
||||
types = get_input_types(input_types, &number_types);
|
||||
num_args += number_types;
|
||||
}
|
||||
if (num_args != get_func_nargs(tstate->final_meta->finalfnoid))
|
||||
elog(ERROR, "invalid number of input types");
|
||||
|
||||
fmgr_info(tstate->final_meta->finalfnoid, &tstate->final_meta->finalfn);
|
||||
/* pass the aggstate information from our current call context */
|
||||
InitFunctionCallInfoData(tstate->final_meta->finalfn_fcinfo,
|
||||
&tstate->final_meta->finalfn,
|
||||
1,
|
||||
num_args,
|
||||
collation,
|
||||
(void *) fa_aggstate,
|
||||
NULL);
|
||||
if (number_types > 0)
|
||||
{
|
||||
Expr *expr;
|
||||
int i;
|
||||
build_aggregate_finalfn_expr(types,
|
||||
num_args,
|
||||
inner_agg_form->aggtranstype,
|
||||
types[number_types - 1],
|
||||
collation,
|
||||
tstate->final_meta->finalfnoid,
|
||||
&expr);
|
||||
fmgr_info_set_expr((Node *) expr, &tstate->final_meta->finalfn);
|
||||
for (i = 1; i < num_args; i++)
|
||||
{
|
||||
tstate->final_meta->finalfn_fcinfo.arg[i] = (Datum) 0;
|
||||
tstate->final_meta->finalfn_fcinfo.argnull[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Need to init tstate->per_group_state->trans_value */
|
||||
@ -311,8 +390,8 @@ tsl_finalize_agg_sfunc(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid inner_agg_input_collid;
|
||||
FATransitionState *tstate = PG_ARGISNULL(0) ? NULL : (FATransitionState *) PG_GETARG_POINTER(0);
|
||||
bytea *inner_agg_serialized_state = PG_ARGISNULL(4) ? NULL : PG_GETARG_BYTEA_P(4);
|
||||
bool inner_agg_serialized_state_isnull = PG_ARGISNULL(4) ? true : false;
|
||||
bytea *inner_agg_serialized_state = PG_ARGISNULL(5) ? NULL : PG_GETARG_BYTEA_P(5);
|
||||
bool inner_agg_serialized_state_isnull = PG_ARGISNULL(5) ? true : false;
|
||||
Datum inner_agg_deserialized_state;
|
||||
AggState *fa_aggstate;
|
||||
MemoryContext fa_context, old_context;
|
||||
@ -332,6 +411,7 @@ tsl_finalize_agg_sfunc(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char *inner_agg_input_coll_schema = PG_ARGISNULL(2) ? NULL : NameStr(*PG_GETARG_NAME(2));
|
||||
char *inner_agg_input_coll_name = PG_ARGISNULL(3) ? NULL : NameStr(*PG_GETARG_NAME(3));
|
||||
ArrayType *input_types = PG_ARGISNULL(4) ? NULL : PG_GETARG_ARRAYTYPE_P(4);
|
||||
Oid inner_agg_fn_oid = aggfnoid_from_aggname(PG_GETARG_TEXT_PP(1));
|
||||
|
||||
inner_agg_input_collid =
|
||||
@ -341,7 +421,8 @@ tsl_finalize_agg_sfunc(PG_FUNCTION_ARGS)
|
||||
tstate = fa_transition_state_init(&fa_context,
|
||||
inner_agg_fn_oid,
|
||||
inner_agg_input_collid,
|
||||
fa_aggstate);
|
||||
fa_aggstate,
|
||||
input_types);
|
||||
/* intial trans_value = the partial state of the inner agg from first invocation */
|
||||
tstate->per_group_state->trans_value =
|
||||
inner_agg_deserialize(tstate->combine_meta,
|
||||
@ -407,7 +488,12 @@ tsl_finalize_agg_ffunc(PG_FUNCTION_ARGS)
|
||||
old_context = MemoryContextSwitchTo(fa_context);
|
||||
if (OidIsValid(tstate->final_meta->finalfnoid))
|
||||
{
|
||||
if (!(tstate->final_meta->finalfn.fn_strict && tstate->per_group_state->trans_value_isnull))
|
||||
/* don't execute if strict and the trans value is NULL or there are extra args (all extra
|
||||
* args are always NULL) */
|
||||
if (!(tstate->final_meta->finalfn.fn_strict &&
|
||||
tstate->per_group_state->trans_value_isnull) &&
|
||||
!(tstate->final_meta->finalfn.fn_strict &&
|
||||
tstate->final_meta->finalfn_fcinfo.nargs > 1))
|
||||
{
|
||||
tstate->final_meta->finalfn_fcinfo.arg[0] = tstate->per_group_state->trans_value;
|
||||
tstate->final_meta->finalfn_fcinfo.argnull[0] =
|
||||
|
@ -16,7 +16,7 @@ create or replace view v1(a , partial)
|
||||
as
|
||||
SELECT a, _timescaledb_internal.partialize_agg( count(b)) from foo group by a;
|
||||
create table t1 as select * from v1;
|
||||
select a, _timescaledb_internal.finalize_agg( 'count("any")', null, null, partial, cast('1' as int8) ) from t1
|
||||
select a, _timescaledb_internal.finalize_agg( 'count("any")', null, null, null, partial, cast('1' as int8) ) from t1
|
||||
group by a order by a ;
|
||||
a | finalize_agg
|
||||
---+--------------
|
||||
@ -26,7 +26,7 @@ group by a order by a ;
|
||||
(3 rows)
|
||||
|
||||
insert into t1 select * from t1;
|
||||
select a, _timescaledb_internal.finalize_agg( 'count("any")', null, null, partial, cast('1' as int8) ) from t1
|
||||
select a, _timescaledb_internal.finalize_agg( 'count("any")', null, null, null, partial, cast('1' as int8) ) from t1
|
||||
group by a order by a ;
|
||||
a | finalize_agg
|
||||
---+--------------
|
||||
@ -53,7 +53,7 @@ create or replace view v1(a , partialb, partialminc)
|
||||
as
|
||||
SELECT a, _timescaledb_internal.partialize_agg( sum(b)) , _timescaledb_internal.partialize_agg( min(c)) from foo group by a;
|
||||
create table t1 as select * from v1;
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(numeric)', null, null, partialb, cast('1' as numeric) ) sumb, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(numeric)', null, null, null, partialb, cast('1' as numeric) ) sumb, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
a | sumb | minc
|
||||
---+------+------
|
||||
1 | 150 | -10
|
||||
@ -67,7 +67,7 @@ insert into foo values( 5, 40, 0);
|
||||
--note that rows for 3 get added all over again + new row
|
||||
--sum aggfnoid 2114, min aggfnoid is 2136 oid numeric is 1700
|
||||
insert into t1 select * from v1 where ( a = 3 ) or a = 5;
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(numeric)', null, null, partialb, cast('1' as numeric) ) sumb, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(numeric)', null, null, null, partialb, cast('1' as numeric) ) sumb, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
a | sumb | minc
|
||||
---+------+------
|
||||
1 | 150 | -10
|
||||
@ -105,7 +105,7 @@ insert into foo values(12, 10, 20);
|
||||
insert into t1 select * from v1 where ( a = 3 and b = 0 ) or a = 5 or (a = 12 and b = 10) ;
|
||||
--results should match query: select a, sum(b+c), min(c) from foo group by a order by a;
|
||||
--sum aggfnoid 2111 for float8, min aggfnoid is 2136 oid numeric is 1700
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(double precision)', null, null, partialb, null::float8 ) sumcd, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(double precision)', null, null, null, partialb, null::float8 ) sumcd, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
a | sumcd | minc
|
||||
----+-------+------
|
||||
1 | 390 | -10
|
||||
@ -118,7 +118,7 @@ select a, _timescaledb_internal.finalize_agg( 'sum(double precision)', null, nul
|
||||
(7 rows)
|
||||
|
||||
insert into t1 select * from v1;
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(double precision)', null, null, partialb, null::float8 ) sumcd, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(double precision)', null, null, null, partialb, null::float8 ) sumcd, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
a | sumcd | minc
|
||||
----+-------+------
|
||||
1 | 780 | -10
|
||||
@ -160,9 +160,9 @@ insert into foo values(12, 10, 'hello', '2010-01-02 09:00:00-05');
|
||||
insert into t1 select * from v1 where (a = 12 and b = 10) ;
|
||||
--select a, sum(b), min(c) , max(d) from foo group by a order by a;
|
||||
--results should match above query
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(numeric)', null, null, partialb, null::numeric ) sumb
|
||||
, _timescaledb_internal.finalize_agg( 'min(text)', 'pg_catalog', 'default', partialc, null::text ) minc
|
||||
, _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, partiald, null::timestamptz ) maxd from t1 group by a order by a ;
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(numeric)', null, null, null, partialb, null::numeric ) sumb
|
||||
, _timescaledb_internal.finalize_agg( 'min(text)', 'pg_catalog', 'default', null, partialc, null::text ) minc
|
||||
, _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, null, partiald, null::timestamptz ) maxd from t1 group by a order by a ;
|
||||
a | sumb | minc | maxd
|
||||
----+------+-------+------------------------------
|
||||
1 | 150 | abc | Sun Jan 03 09:00:00 2010 PST
|
||||
@ -173,7 +173,7 @@ select a, _timescaledb_internal.finalize_agg( 'sum(numeric)', null, null, partia
|
||||
(5 rows)
|
||||
|
||||
--with having clause --
|
||||
select a, b , _timescaledb_internal.finalize_agg( 'min(text)', 'pg_catalog', 'default', partialc, null::text ) minc, _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, partiald, null::timestamptz ) maxd from t1 where b is not null group by a, b having _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, partiald, null::timestamptz ) is not null order by a, b;
|
||||
select a, b , _timescaledb_internal.finalize_agg( 'min(text)', 'pg_catalog', 'default', null, partialc, null::text ) minc, _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, null, partiald, null::timestamptz ) maxd from t1 where b is not null group by a, b having _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, null, partiald, null::timestamptz ) is not null order by a, b;
|
||||
a | b | minc | maxd
|
||||
----+----+-------+------------------------------
|
||||
1 | 10 | hello | Fri Jan 01 09:00:00 2010 PST
|
||||
@ -208,8 +208,8 @@ INSERT INTO foo values( 2, '2005-01-01 00:00:00+00', NULL);
|
||||
create or replace view v1(a, partialb, partialtv) as select a, _timescaledb_internal.partialize_agg( max(b) ), _timescaledb_internal.partialize_agg( min(toastval)) from foo group by a;
|
||||
create table t1 as select * from v1;
|
||||
insert into t1 select * from v1;
|
||||
select a, _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, partialb, null::timestamptz ) maxb,
|
||||
_timescaledb_internal.finalize_agg( 'min(text)', 'pg_catalog', 'default', partialtv, null::text ) = repeat('I am a tall big giraffe in the zoo. ', 1100) mintv_equal
|
||||
select a, _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, null, partialb, null::timestamptz ) maxb,
|
||||
_timescaledb_internal.finalize_agg( 'min(text)', 'pg_catalog', 'default', null, partialtv, null::text ) = repeat('I am a tall big giraffe in the zoo. ', 1100) mintv_equal
|
||||
from t1 group by a order by a;
|
||||
a | maxb | mintv_equal
|
||||
---+------------------------------+-------------
|
||||
@ -247,3 +247,78 @@ ERROR: The input to partialize must be an aggregate
|
||||
select length(_timescaledb_internal.partialize_agg(min(a)+min(a))) from foo;
|
||||
ERROR: The input to partialize must be an aggregate
|
||||
\set ON_ERROR_STOP 1
|
||||
--
|
||||
-- TEST FINALIZEFUNC_EXTRA
|
||||
--
|
||||
-- create special aggregate to test ffunc_extra
|
||||
-- Raise warning with the actual type being passed in
|
||||
CREATE OR REPLACE FUNCTION fake_ffunc(a int8, b int, x anyelement)
|
||||
RETURNS anyelement AS $$
|
||||
BEGIN
|
||||
RAISE WARNING 'type %', pg_typeof(x);
|
||||
RETURN x;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql;
|
||||
CREATE OR REPLACE FUNCTION fake_sfunc(a int8, b int, x anyelement)
|
||||
RETURNS int8 AS $$
|
||||
BEGIN
|
||||
RETURN b;
|
||||
END; $$
|
||||
LANGUAGE plpgsql;
|
||||
CREATE AGGREGATE aggregate_to_test_ffunc_extra(int, anyelement) (
|
||||
SFUNC = fake_sfunc,
|
||||
STYPE = int8,
|
||||
COMBINEFUNC = int8pl,
|
||||
FINALFUNC = fake_ffunc,
|
||||
PARALLEL = SAFE,
|
||||
FINALFUNC_EXTRA
|
||||
);
|
||||
select aggregate_to_test_ffunc_extra(8, 'name'::text);
|
||||
WARNING: type text
|
||||
aggregate_to_test_ffunc_extra
|
||||
-------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
--errors on wrong input type array
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, null, part, null::text) from cte;
|
||||
ERROR: cannot pass null input_type with FINALFUNC_EXTRA aggregates
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[array['a'::name, 'b'::name, 'c'::name]], part, null::text) from cte;
|
||||
ERROR: invalid input type array: expecting slices of size 2
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[array[]::name[]]::name[], part, null::text) from cte;
|
||||
ERROR: invalid input type array: wrong number of dimensions
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[]::name[], part, null::text) from cte;
|
||||
ERROR: invalid input type array: wrong number of dimensions
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[array['public'::name, 'int'::name], array['public', 'text']], part, null::text) from cte;
|
||||
ERROR: invalid input type: public.int
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[array['public'::name, 'int4'::name], array['public', 'text']], part, null::text) from cte;
|
||||
ERROR: invalid input type: public.int4
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[array['pg_catalog'::name, 'int4'::name], array['pg_catalog', 'text'], array['pg_catalog', 'text']], part, null::text) from cte;
|
||||
ERROR: invalid number of input types
|
||||
\set ON_ERROR_STOP 1
|
||||
--make sure right type in warning and is null returns true
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[array['pg_catalog'::name, 'int4'::name], array['pg_catalog', 'text']], part, null::text) is null from cte;
|
||||
WARNING: type text
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 1::bigint)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[array['pg_catalog'::name, 'int4'::name], array['pg_catalog', 'int8']], part, null::text) is null from cte;
|
||||
WARNING: type bigint
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
|
@ -21,11 +21,11 @@ as
|
||||
|
||||
create table t1 as select * from v1;
|
||||
|
||||
select a, _timescaledb_internal.finalize_agg( 'count("any")', null, null, partial, cast('1' as int8) ) from t1
|
||||
select a, _timescaledb_internal.finalize_agg( 'count("any")', null, null, null, partial, cast('1' as int8) ) from t1
|
||||
group by a order by a ;
|
||||
|
||||
insert into t1 select * from t1;
|
||||
select a, _timescaledb_internal.finalize_agg( 'count("any")', null, null, partial, cast('1' as int8) ) from t1
|
||||
select a, _timescaledb_internal.finalize_agg( 'count("any")', null, null, null, partial, cast('1' as int8) ) from t1
|
||||
group by a order by a ;
|
||||
|
||||
--TEST2 sum numeric and min on float--
|
||||
@ -51,7 +51,7 @@ as
|
||||
|
||||
create table t1 as select * from v1;
|
||||
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(numeric)', null, null, partialb, cast('1' as numeric) ) sumb, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(numeric)', null, null, null, partialb, cast('1' as numeric) ) sumb, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
|
||||
insert into foo values( 3, 0, -1);
|
||||
insert into foo values( 5, 40, 10);
|
||||
@ -59,7 +59,7 @@ insert into foo values( 5, 40, 0);
|
||||
--note that rows for 3 get added all over again + new row
|
||||
--sum aggfnoid 2114, min aggfnoid is 2136 oid numeric is 1700
|
||||
insert into t1 select * from v1 where ( a = 3 ) or a = 5;
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(numeric)', null, null, partialb, cast('1' as numeric) ) sumb, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(numeric)', null, null, null, partialb, cast('1' as numeric) ) sumb, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
|
||||
--TEST3 sum with expressions
|
||||
drop table t1;
|
||||
@ -95,10 +95,10 @@ insert into t1 select * from v1 where ( a = 3 and b = 0 ) or a = 5 or (a = 12 an
|
||||
|
||||
--results should match query: select a, sum(b+c), min(c) from foo group by a order by a;
|
||||
--sum aggfnoid 2111 for float8, min aggfnoid is 2136 oid numeric is 1700
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(double precision)', null, null, partialb, null::float8 ) sumcd, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(double precision)', null, null, null, partialb, null::float8 ) sumcd, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
|
||||
insert into t1 select * from v1;
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(double precision)', null, null, partialb, null::float8 ) sumcd, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(double precision)', null, null, null, partialb, null::float8 ) sumcd, _timescaledb_internal.finalize_agg( 'min(double precision)', null, null, null, partialminc, cast('1' as float8) ) minc from t1 group by a order by a ;
|
||||
|
||||
-- TEST4 with collation (text), NULLS and timestamp --
|
||||
drop table t1;
|
||||
@ -136,12 +136,12 @@ insert into t1 select * from v1 where (a = 12 and b = 10) ;
|
||||
|
||||
--select a, sum(b), min(c) , max(d) from foo group by a order by a;
|
||||
--results should match above query
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(numeric)', null, null, partialb, null::numeric ) sumb
|
||||
, _timescaledb_internal.finalize_agg( 'min(text)', 'pg_catalog', 'default', partialc, null::text ) minc
|
||||
, _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, partiald, null::timestamptz ) maxd from t1 group by a order by a ;
|
||||
select a, _timescaledb_internal.finalize_agg( 'sum(numeric)', null, null, null, partialb, null::numeric ) sumb
|
||||
, _timescaledb_internal.finalize_agg( 'min(text)', 'pg_catalog', 'default', null, partialc, null::text ) minc
|
||||
, _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, null, partiald, null::timestamptz ) maxd from t1 group by a order by a ;
|
||||
|
||||
--with having clause --
|
||||
select a, b , _timescaledb_internal.finalize_agg( 'min(text)', 'pg_catalog', 'default', partialc, null::text ) minc, _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, partiald, null::timestamptz ) maxd from t1 where b is not null group by a, b having _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, partiald, null::timestamptz ) is not null order by a, b;
|
||||
select a, b , _timescaledb_internal.finalize_agg( 'min(text)', 'pg_catalog', 'default', null, partialc, null::text ) minc, _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, null, partiald, null::timestamptz ) maxd from t1 where b is not null group by a, b having _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, null, partiald, null::timestamptz ) is not null order by a, b;
|
||||
|
||||
|
||||
--TEST5 test with TOAST data
|
||||
@ -165,8 +165,8 @@ create or replace view v1(a, partialb, partialtv) as select a, _timescaledb_int
|
||||
create table t1 as select * from v1;
|
||||
|
||||
insert into t1 select * from v1;
|
||||
select a, _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, partialb, null::timestamptz ) maxb,
|
||||
_timescaledb_internal.finalize_agg( 'min(text)', 'pg_catalog', 'default', partialtv, null::text ) = repeat('I am a tall big giraffe in the zoo. ', 1100) mintv_equal
|
||||
select a, _timescaledb_internal.finalize_agg( 'max(timestamp with time zone)', null, null, null, partialb, null::timestamptz ) maxb,
|
||||
_timescaledb_internal.finalize_agg( 'min(text)', 'pg_catalog', 'default', null, partialtv, null::text ) = repeat('I am a tall big giraffe in the zoo. ', 1100) mintv_equal
|
||||
from t1 group by a order by a;
|
||||
|
||||
--non top-level partials
|
||||
@ -183,3 +183,68 @@ select length(_timescaledb_internal.partialize_agg(min(a+1))) from foo;
|
||||
select length(_timescaledb_internal.partialize_agg(1+min(a))) from foo;
|
||||
select length(_timescaledb_internal.partialize_agg(min(a)+min(a))) from foo;
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
--
|
||||
-- TEST FINALIZEFUNC_EXTRA
|
||||
--
|
||||
|
||||
-- create special aggregate to test ffunc_extra
|
||||
-- Raise warning with the actual type being passed in
|
||||
CREATE OR REPLACE FUNCTION fake_ffunc(a int8, b int, x anyelement)
|
||||
RETURNS anyelement AS $$
|
||||
BEGIN
|
||||
RAISE WARNING 'type %', pg_typeof(x);
|
||||
RETURN x;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION fake_sfunc(a int8, b int, x anyelement)
|
||||
RETURNS int8 AS $$
|
||||
BEGIN
|
||||
RETURN b;
|
||||
END; $$
|
||||
LANGUAGE plpgsql;
|
||||
|
||||
|
||||
CREATE AGGREGATE aggregate_to_test_ffunc_extra(int, anyelement) (
|
||||
SFUNC = fake_sfunc,
|
||||
STYPE = int8,
|
||||
COMBINEFUNC = int8pl,
|
||||
FINALFUNC = fake_ffunc,
|
||||
PARALLEL = SAFE,
|
||||
FINALFUNC_EXTRA
|
||||
);
|
||||
|
||||
select aggregate_to_test_ffunc_extra(8, 'name'::text);
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
--errors on wrong input type array
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, null, part, null::text) from cte;
|
||||
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[array['a'::name, 'b'::name, 'c'::name]], part, null::text) from cte;
|
||||
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[array[]::name[]]::name[], part, null::text) from cte;
|
||||
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[]::name[], part, null::text) from cte;
|
||||
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[array['public'::name, 'int'::name], array['public', 'text']], part, null::text) from cte;
|
||||
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[array['public'::name, 'int4'::name], array['public', 'text']], part, null::text) from cte;
|
||||
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[array['pg_catalog'::name, 'int4'::name], array['pg_catalog', 'text'], array['pg_catalog', 'text']], part, null::text) from cte;
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
--make sure right type in warning and is null returns true
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 'name'::text)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[array['pg_catalog'::name, 'int4'::name], array['pg_catalog', 'text']], part, null::text) is null from cte;
|
||||
|
||||
with cte as (SELECT _timescaledb_internal.partialize_agg(aggregate_to_test_ffunc_extra(8, 1::bigint)) as part)
|
||||
select _timescaledb_internal.finalize_agg( 'aggregate_to_test_ffunc_extra(int, anyelement)', null, null, array[array['pg_catalog'::name, 'int4'::name], array['pg_catalog', 'int8']], part, null::text) is null from cte;
|
||||
|
Loading…
x
Reference in New Issue
Block a user