From eeb03c4bddf07385372dda44558874a9f25dbb1b Mon Sep 17 00:00:00 2001 From: Ashutosh Bapat Date: Wed, 20 Sep 2023 15:41:00 +0530 Subject: [PATCH v27 4/8] Support infinite interval values in sum and avg window functions Results of arithmetic operations involing both infinity and -infinity intervals are undefined. Sliding window functions accumulate and discard values as the window slides through the data. Even if the data may have both infinities, none of windows may have both of them together and thus produce definite results. Hence we can not use normal operator functions to accumulate and discard values. Instead we maintain a count of both infinities seen respectively in an aggregate state and compute the aggregate for each window based on the accumulated finite value and the counts of infinities, all of which are updated as the window slides through the data. Initial patch by Jian He, significantly edited by Ashutosh Bapat --- src/backend/utils/adt/timestamp.c | 407 ++++++++++++++++++--------- src/include/catalog/pg_aggregate.dat | 23 +- src/include/catalog/pg_proc.dat | 20 +- src/test/regress/expected/window.out | 62 ++++ src/test/regress/sql/window.sql | 43 +++ src/tools/pgindent/typedefs.list | 1 + 6 files changed, 400 insertions(+), 156 deletions(-) diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index a70639db58..450b547036 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -73,6 +73,21 @@ typedef struct pg_tz *attimezone; } generate_series_timestamptz_fctx; +/* + * The transition datatype for interval aggregates is declared as Internal. It's + * a pointer to a IntervalAggState allocated in the aggregate context. + */ +typedef struct IntervalAggState +{ + int64 N; /* count of processed intervals */ + Interval sumX; /* sum of processed intervals */ + /* These counts are *not* included in N! Use IA_TOTAL_COUNT() as needed */ + int64 pInfcount; /* count of +Inf values */ + int64 nInfcount; /* count of -Inf values */ +} IntervalAggState; + +#define IA_TOTAL_COUNT(ia) \ + ((ia)->N + (ia)->pInfcount + (ia)->nInfcount) static TimeOffset time2t(const int hour, const int min, const int sec, const fsec_t fsec); static Timestamp dt2local(Timestamp dt, int timezone); @@ -82,6 +97,8 @@ static TimestampTz timestamp2timestamptz(Timestamp timestamp); static Timestamp timestamptz2timestamp(TimestampTz timestamp); static void interval_um_internal(Interval *interval, Interval *result); +static void finite_interval_pl(Interval *result, Interval *span1, + Interval *span2); /* common code for timestamptypmodin and timestamptztypmodin */ static int32 @@ -3443,34 +3460,7 @@ interval_pl(PG_FUNCTION_ARGS) else if (INTERVAL_NOT_FINITE(span2)) memcpy(result, span2, sizeof(Interval)); else - { - result->month = span1->month + span2->month; - /* overflow check copied from int4pl */ - if (SAMESIGN(span1->month, span2->month) && - !SAMESIGN(result->month, span1->month)) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("interval out of range"))); - - result->day = span1->day + span2->day; - if (SAMESIGN(span1->day, span2->day) && - !SAMESIGN(result->day, span1->day)) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("interval out of range"))); - - result->time = span1->time + span2->time; - if (SAMESIGN(span1->time, span2->time) && - !SAMESIGN(result->time, span1->time)) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("interval out of range"))); - - if (INTERVAL_NOT_FINITE(result)) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("interval out of range"))); - } + finite_interval_pl(result, span1, span2); PG_RETURN_INTERVAL_P(result); } @@ -3856,161 +3846,300 @@ in_range_interval_interval(PG_FUNCTION_ARGS) /* - * interval_accum, interval_accum_inv, and interval_avg implement the - * AVG(interval) aggregate. + * Prepare state data for an interval aggregate function that needs to compute + * sum and count. + */ +static IntervalAggState * +makeIntervalAggState(FunctionCallInfo fcinfo) +{ + IntervalAggState *state; + MemoryContext agg_context; + MemoryContext old_context; + + if (!AggCheckCallContext(fcinfo, &agg_context)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + old_context = MemoryContextSwitchTo(agg_context); + + state = (IntervalAggState *) palloc0(sizeof(IntervalAggState)); + + MemoryContextSwitchTo(old_context); + + return state; +} + +/* + * Function to add two finite intervals. * - * The transition datatype for this aggregate is a 2-element array of - * intervals, where the first is the running sum and the second contains - * the number of values so far in its 'time' field. This is a bit ugly - * but it beats inventing a specialized datatype for the purpose. + * We handle non-finite intervals in different ways when accumulating intervals + * and adding two intervals respectively. But the addition of finite interval + * has same implementation in both these cases. */ +static void +finite_interval_pl(Interval *result, Interval *span1, Interval *span2) +{ + Assert(!INTERVAL_NOT_FINITE(span1)); + Assert(!INTERVAL_NOT_FINITE(span2)); -Datum -interval_accum(PG_FUNCTION_ARGS) + result->month = span1->month + span2->month; + /* overflow check copied from int4pl */ + if (SAMESIGN(span1->month, span2->month) && + !SAMESIGN(result->month, span1->month)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + + result->day = span1->day + span2->day; + if (SAMESIGN(span1->day, span2->day) && + !SAMESIGN(result->day, span1->day)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + + result->time = span1->time + span2->time; + if (SAMESIGN(span1->time, span2->time) && + !SAMESIGN(result->time, span1->time)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + + if (INTERVAL_NOT_FINITE(result)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); +} + +/* + * Accumulate a new input value for interval aggregate functions. + */ +static void +do_interval_accum(IntervalAggState *state, Interval *newval) { - ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); - Interval *newval = PG_GETARG_INTERVAL_P(1); - Datum *transdatums; - int ndatums; - Interval sumX, - N; - Interval *newsum; - ArrayType *result; - deconstruct_array(transarray, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE, - &transdatums, NULL, &ndatums); - if (ndatums != 2) - elog(ERROR, "expected 2-element interval array"); + Interval temp; + + /* Count -infinity inputs separately from all else */ + if (INTERVAL_IS_NOBEGIN(newval)) + { + state->nInfcount++; + return; + } + + /* Count infinity inputs separately from all else */ + if (INTERVAL_IS_NOEND(newval)) + { + state->pInfcount++; + return; + } + + memcpy(&temp, &state->sumX, sizeof(Interval)); + finite_interval_pl(&state->sumX, &temp, newval); + state->N++; +} - sumX = *(DatumGetIntervalP(transdatums[0])); - N = *(DatumGetIntervalP(transdatums[1])); +/* + * Transition function for interval aggregates. + */ +Datum +interval_avg_accum(PG_FUNCTION_ARGS) +{ + IntervalAggState *state; - newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl, - IntervalPGetDatum(&sumX), - IntervalPGetDatum(newval))); - N.time += 1; + state = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0); - transdatums[0] = IntervalPGetDatum(newsum); - transdatums[1] = IntervalPGetDatum(&N); + /* Create the state data on the first call */ + if (state == NULL) + state = makeIntervalAggState(fcinfo); - result = construct_array(transdatums, 2, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE); + if (!PG_ARGISNULL(1)) + do_interval_accum(state, PG_GETARG_INTERVAL_P(1)); - PG_RETURN_ARRAYTYPE_P(result); + PG_RETURN_POINTER(state); } +/* + * Combine function for interval aggregates. + */ Datum -interval_combine(PG_FUNCTION_ARGS) +interval_avg_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; + IntervalAggState *state1; + IntervalAggState *state2; + + state1 = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0); + state2 = PG_ARGISNULL(1) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(1); - Interval *newsum; - ArrayType *result; + if (state2 == NULL) + PG_RETURN_POINTER(state1); + + /* manually copy all fields from state2 to state1 */ + if (state1 == NULL) + { + state1 = makeIntervalAggState(fcinfo); - deconstruct_array(transarray1, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE, - &transdatums1, NULL, &ndatums1); - if (ndatums1 != 2) - elog(ERROR, "expected 2-element interval array"); + state1->N = state2->N; + state1->pInfcount = state2->pInfcount; + state1->nInfcount = state2->nInfcount; - sum1 = *(DatumGetIntervalP(transdatums1[0])); - N1 = *(DatumGetIntervalP(transdatums1[1])); + state1->sumX.day = state2->sumX.day; + state1->sumX.month = state2->sumX.month; + state1->sumX.time = state2->sumX.time; - deconstruct_array(transarray2, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE, - &transdatums2, NULL, &ndatums2); - if (ndatums2 != 2) - elog(ERROR, "expected 2-element interval array"); + PG_RETURN_POINTER(state1); + } + + state1->N += state2->N; + state1->pInfcount += state2->pInfcount; + state1->nInfcount += state2->nInfcount; + + if (state2->N > 0) + { + Interval temp; + + /* Accumulate interval values */ + memcpy(&temp, &state1->sumX, sizeof(Interval)); + finite_interval_pl(&state1->sumX, &temp, &state2->sumX); + } + + PG_RETURN_POINTER(state1); +} - sum2 = *(DatumGetIntervalP(transdatums2[0])); - N2 = *(DatumGetIntervalP(transdatums2[1])); +/* + * Remove the given interval value from the aggregated state. + */ +static void +do_interval_discard(IntervalAggState *state, Interval *newval) +{ + /* Count -infinity inputs separately from all else */ + if (INTERVAL_IS_NOBEGIN(newval)) + { + state->nInfcount--; + return; + } - newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl, - IntervalPGetDatum(&sum1), - IntervalPGetDatum(&sum2))); - N1.time += N2.time; + /* Count infinity inputs separately from all else */ + if (INTERVAL_IS_NOEND(newval)) + { + state->pInfcount--; + return; + } - transdatums1[0] = IntervalPGetDatum(newsum); - transdatums1[1] = IntervalPGetDatum(&N1); + /* Handle to be discarded finite value. */ + if (state->N > 0) + { + Interval temp; + Interval neg_val; - result = construct_array(transdatums1, 2, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE); + neg_val.day = -newval->day; + neg_val.month = -newval->month; + neg_val.time = -newval->time; - PG_RETURN_ARRAYTYPE_P(result); + memcpy(&temp, &state->sumX, sizeof(Interval)); + finite_interval_pl(&state->sumX, &temp, &neg_val); + } + else + { + /* All values discarded, reset the state */ + memset(&state->sumX, 0, sizeof(state->sumX)); + } + state->N--; } +/* + * Generic inverse transition function for interval aggregates + */ Datum interval_accum_inv(PG_FUNCTION_ARGS) { - ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); - Interval *newval = PG_GETARG_INTERVAL_P(1); - Datum *transdatums; - int ndatums; - Interval sumX, - N; - Interval *newsum; - ArrayType *result; + IntervalAggState *state; - deconstruct_array(transarray, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE, - &transdatums, NULL, &ndatums); - if (ndatums != 2) - elog(ERROR, "expected 2-element interval array"); + state = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0); - sumX = *(DatumGetIntervalP(transdatums[0])); - N = *(DatumGetIntervalP(transdatums[1])); + /* Should not get here with no state */ + if (state == NULL) + elog(ERROR, "interval_accum_inv called with NULL state"); - newsum = DatumGetIntervalP(DirectFunctionCall2(interval_mi, - IntervalPGetDatum(&sumX), - IntervalPGetDatum(newval))); - N.time -= 1; + if (!PG_ARGISNULL(1)) + do_interval_discard(state, PG_GETARG_INTERVAL_P(1)); - transdatums[0] = IntervalPGetDatum(newsum); - transdatums[1] = IntervalPGetDatum(&N); + PG_RETURN_POINTER(state); +} - result = construct_array(transdatums, 2, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE); +/* avg(interval) aggregate final function */ +Datum +interval_avg(PG_FUNCTION_ARGS) +{ + IntervalAggState *state; + double N_datum; + Interval *sumX; + + sumX = (Interval *) palloc(sizeof(Interval)); + state = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0); + + if (state == NULL || IA_TOTAL_COUNT(state) == 0) + PG_RETURN_NULL(); - PG_RETURN_ARRAYTYPE_P(result); + /* adding plus and minus infinities gives error */ + if (state->pInfcount > 0 && state->nInfcount > 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range."))); + + if (state->pInfcount > 0) + { + INTERVAL_NOEND(sumX); + PG_RETURN_INTERVAL_P(sumX); + } + + if (state->nInfcount > 0) + { + INTERVAL_NOBEGIN(sumX); + PG_RETURN_INTERVAL_P(sumX); + } + + N_datum = (double) state->N; + sumX = &state->sumX; + + PG_RETURN_DATUM(DirectFunctionCall2(interval_div, IntervalPGetDatum(sumX), + Float8GetDatum(N_datum))); } +/* sum(interval) aggregate final function */ Datum -interval_avg(PG_FUNCTION_ARGS) +interval_sum(PG_FUNCTION_ARGS) { - ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); - Datum *transdatums; - int ndatums; - Interval sumX, - N; + IntervalAggState *state; + Interval *result; - deconstruct_array(transarray, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE, - &transdatums, NULL, &ndatums); - if (ndatums != 2) - elog(ERROR, "expected 2-element interval array"); + result = (Interval *) palloc(sizeof(Interval)); - sumX = *(DatumGetIntervalP(transdatums[0])); - N = *(DatumGetIntervalP(transdatums[1])); + state = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0); - /* SQL defines AVG of no values to be NULL */ - if (N.time == 0) + /* If there were no non-null inputs, return NULL */ + if (state == NULL || IA_TOTAL_COUNT(state) == 0) PG_RETURN_NULL(); - return DirectFunctionCall2(interval_div, - IntervalPGetDatum(&sumX), - Float8GetDatum((double) N.time)); -} + /* adding plus and minus interval infinities is not possible */ + if (state->pInfcount > 0 && state->nInfcount > 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range."))); + + if (state->pInfcount > 0) + { + INTERVAL_NOEND(result); + PG_RETURN_INTERVAL_P(result); + } + + if (state->nInfcount > 0) + { + INTERVAL_NOBEGIN(result); + PG_RETURN_INTERVAL_P(result); + } + memcpy(result, &state->sumX, sizeof(Interval)); + PG_RETURN_INTERVAL_P(result); +} /* timestamp_age() * Calculate time difference while retaining year/month fields. diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat index 1bc1d97d74..e2087d7be1 100644 --- a/src/include/catalog/pg_aggregate.dat +++ b/src/include/catalog/pg_aggregate.dat @@ -43,12 +43,13 @@ { aggfnoid => 'avg(float8)', aggtransfn => 'float8_accum', aggfinalfn => 'float8_avg', aggcombinefn => 'float8_combine', aggtranstype => '_float8', agginitval => '{0,0,0}' }, -{ 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}' }, +{ aggfnoid => 'avg(interval)', aggtransfn => 'interval_avg_accum', + aggfinalfn => 'interval_avg', aggcombinefn => 'interval_avg_combine', + aggmtransfn => 'interval_avg_accum', aggminvtransfn => 'interval_accum_inv', + aggmfinalfn => 'interval_avg', aggtranstype => 'internal', + aggmtranstype => 'internal', aggtransspace => '128', + aggmtransspace => '128' +}, # sum { aggfnoid => 'sum(int8)', aggtransfn => 'int8_avg_accum', @@ -72,10 +73,12 @@ { aggfnoid => 'sum(money)', aggtransfn => 'cash_pl', aggcombinefn => 'cash_pl', aggmtransfn => 'cash_pl', aggminvtransfn => 'cash_mi', aggtranstype => 'money', aggmtranstype => 'money' }, -{ aggfnoid => 'sum(interval)', aggtransfn => 'interval_pl', - aggcombinefn => 'interval_pl', aggmtransfn => 'interval_pl', - aggminvtransfn => 'interval_mi', aggtranstype => 'interval', - aggmtranstype => 'interval' }, +{ aggfnoid => 'sum(interval)', aggtransfn => 'interval_avg_accum', + aggfinalfn => 'interval_sum', aggcombinefn => 'interval_avg_combine', + aggmtransfn => 'interval_avg_accum', aggminvtransfn => 'interval_accum_inv', + aggmfinalfn => 'interval_sum', aggtranstype => 'internal', + aggmtranstype => 'internal', aggtransspace => '128', + aggmtransspace => '128'}, { aggfnoid => 'sum(numeric)', aggtransfn => 'numeric_avg_accum', aggfinalfn => 'numeric_sum', aggcombinefn => 'numeric_avg_combine', aggserialfn => 'numeric_avg_serialize', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index bc41e92677..791359a6e0 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -4917,17 +4917,23 @@ prosrc => 'numeric_poly_stddev_samp' }, { oid => '1843', descr => 'aggregate transition function', - proname => 'interval_accum', prorettype => '_interval', - proargtypes => '_interval interval', prosrc => 'interval_accum' }, + proname => 'interval_avg_accum', proisstrict => 'f', + prorettype => 'internal', proargtypes => 'internal interval', + prosrc => 'interval_avg_accum' }, { oid => '3325', descr => 'aggregate combine function', - proname => 'interval_combine', prorettype => '_interval', - proargtypes => '_interval _interval', prosrc => 'interval_combine' }, + proname => 'interval_avg_combine', proisstrict => 'f', + prorettype => 'internal', proargtypes => 'internal internal', + prosrc => 'interval_avg_combine' }, { oid => '3549', descr => 'aggregate transition function', - proname => 'interval_accum_inv', prorettype => '_interval', - proargtypes => '_interval interval', prosrc => 'interval_accum_inv' }, + proname => 'interval_accum_inv', proisstrict => 'f', + prorettype => 'internal', proargtypes => 'internal interval', + prosrc => 'interval_accum_inv' }, { oid => '1844', descr => 'aggregate final function', proname => 'interval_avg', prorettype => 'interval', - proargtypes => '_interval', prosrc => 'interval_avg' }, + proargtypes => 'internal', prosrc => 'interval_avg' }, +{ oid => '8069', descr => 'aggregate final function', + proname => 'interval_sum', proisstrict => 'f', prorettype => 'interval', + proargtypes => 'internal', prosrc => 'interval_sum' }, { oid => '1962', descr => 'aggregate transition function', proname => 'int2_avg_accum', prorettype => '_int8', proargtypes => '_int8 int2', prosrc => 'int2_avg_accum' }, diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out index 69a38df10b..45262924cd 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -4375,6 +4375,68 @@ SELECT i,AVG(v::interval) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDE 4 | (4 rows) +--order by. +SELECT x + ,avg(x) OVER(ORDER BY x ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING ) as curr_next_avg + ,sum(x) OVER(ORDER BY x ROWS BETWEEN 1 PRECEDING AND CURRENT ROW ) as prev1_curr_sum +FROM (VALUES (NULL::interval), + ('infinity'::interval), + ('infinity'::timestamptz - now()), + ('6 days'::interval), + (NULL::interval), + ('-infinity'::interval)) v(x); + x | curr_next_avg | prev1_curr_sum +-----------+---------------+---------------- + -infinity | -infinity | -infinity + @ 6 days | infinity | -infinity + infinity | infinity | infinity + infinity | infinity | infinity + | | infinity + | | +(6 rows) + +--no order by. +SELECT x + ,avg(x) OVER(ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING ) as curr_next_avg + ,sum(x) OVER(ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING ) as curr_next_sum + ,avg(x) OVER(ROWS BETWEEN 1 PRECEDING AND CURRENT ROW ) as prev1_curr_avg + ,sum(x) OVER(ROWS BETWEEN 1 PRECEDING AND CURRENT ROW ) as prev1_curr_sum + ,avg(x) OVER(ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING ) as prev1_next2_avg + ,sum(x) OVER(ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING ) as prev1_next2_sum +FROM (VALUES (NULL::interval), + ('infinity'::interval), + ('infinity'::timestamptz - now()), + ('6 days'::interval), + ('7 days'::interval), + (NULL::interval), + ('-infinity'::interval)) v(x); + x | curr_next_avg | curr_next_sum | prev1_curr_avg | prev1_curr_sum | prev1_next2_avg | prev1_next2_sum +-----------+-------------------+---------------+-------------------+----------------+-----------------+----------------- + | infinity | infinity | | | infinity | infinity + infinity | infinity | infinity | infinity | infinity | infinity | infinity + infinity | infinity | infinity | infinity | infinity | infinity | infinity + @ 6 days | @ 6 days 12 hours | @ 13 days | infinity | infinity | infinity | infinity + @ 7 days | @ 7 days | @ 7 days | @ 6 days 12 hours | @ 13 days | -infinity | -infinity + | -infinity | -infinity | @ 7 days | @ 7 days | -infinity | -infinity + -infinity | -infinity | -infinity | -infinity | -infinity | -infinity | -infinity +(7 rows) + +--should fail. +SELECT x, avg(x) OVER(ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) +FROM (VALUES (NULL::interval), + ('3 days'::interval), + ('infinity'::timestamptz - now()), + ('6 days'::interval), + ('-infinity'::interval)) v(x); +ERROR: interval out of range. +--should fail. +SELECT x, sum(x) OVER(ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) +FROM (VALUES (NULL::interval), + ('3 days'::interval), + ('infinity'::timestamptz - now()), + ('6 days'::interval), + ('-infinity'::interval)) v(x); +ERROR: interval out of range. SELECT i,SUM(v::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v); i | sum diff --git a/src/test/regress/sql/window.sql b/src/test/regress/sql/window.sql index 9113a92ae0..d00423946d 100644 --- a/src/test/regress/sql/window.sql +++ b/src/test/regress/sql/window.sql @@ -1591,6 +1591,49 @@ SELECT i,AVG(v::numeric) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED SELECT i,AVG(v::interval) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,'1 sec'),(2,'2 sec'),(3,NULL),(4,NULL)) t(i,v); +--order by. +SELECT x + ,avg(x) OVER(ORDER BY x ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING ) as curr_next_avg + ,sum(x) OVER(ORDER BY x ROWS BETWEEN 1 PRECEDING AND CURRENT ROW ) as prev1_curr_sum +FROM (VALUES (NULL::interval), + ('infinity'::interval), + ('infinity'::timestamptz - now()), + ('6 days'::interval), + (NULL::interval), + ('-infinity'::interval)) v(x); + +--no order by. +SELECT x + ,avg(x) OVER(ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING ) as curr_next_avg + ,sum(x) OVER(ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING ) as curr_next_sum + ,avg(x) OVER(ROWS BETWEEN 1 PRECEDING AND CURRENT ROW ) as prev1_curr_avg + ,sum(x) OVER(ROWS BETWEEN 1 PRECEDING AND CURRENT ROW ) as prev1_curr_sum + ,avg(x) OVER(ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING ) as prev1_next2_avg + ,sum(x) OVER(ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING ) as prev1_next2_sum +FROM (VALUES (NULL::interval), + ('infinity'::interval), + ('infinity'::timestamptz - now()), + ('6 days'::interval), + ('7 days'::interval), + (NULL::interval), + ('-infinity'::interval)) v(x); + +--should fail. +SELECT x, avg(x) OVER(ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) +FROM (VALUES (NULL::interval), + ('3 days'::interval), + ('infinity'::timestamptz - now()), + ('6 days'::interval), + ('-infinity'::interval)) v(x); + +--should fail. +SELECT x, sum(x) OVER(ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) +FROM (VALUES (NULL::interval), + ('3 days'::interval), + ('infinity'::timestamptz - now()), + ('6 days'::interval), + ('-infinity'::interval)) v(x); + SELECT i,SUM(v::smallint) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) FROM (VALUES(1,1),(2,2),(3,NULL),(4,NULL)) t(i,v); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 87c1aee379..236683e5ab 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1205,6 +1205,7 @@ IntegerSet InternalDefaultACL InternalGrant Interval +IntervalAggState IntoClause InvalMessageArray InvalidationMsgsGroup -- 2.35.3