From 0d1ca2f1ee2ef76bab1927b59a2f45b0f20d5c79 Mon Sep 17 00:00:00 2001 From: pgaddict Date: Fri, 15 Sep 2023 17:28:29 +0800 Subject: [PATCH v20 6/6] refactor avg(interval), sum(interval) aggregate. to make avg(interval), sum(interval) window function also work when intervals have special values: inf/-inf, We need a state to live through the whole aggregate function cycle. the state will accumulate or discard value. the aggregate transition and inverse transition function associated with interval type will update the state all the time. the aggregate final function will compute the final result. We already do the same logic in many data types like numeric. so invent a new struct: IntervalAggState. it will be initialized in aggregate memory context (I don't know when it will be destroyed). For avg(interval), sum(interval) every time accumulate a new interval value, change the state struct inner elements value. In IntervalAggStat we also track the number of special values. In the end, using the aggregate final function returns the result. To make window function work, we need to have an inverse transition function that can discard an interval from the state. tests include order by and non-order by window function case. also invent the sum(interval) aggregate final function. --- src/backend/utils/adt/timestamp.c | 455 +++++++++++++++++++-------- src/include/catalog/pg_aggregate.dat | 21 +- src/include/catalog/pg_proc.dat | 21 +- src/test/regress/expected/window.out | 64 ++++ src/test/regress/sql/window.sql | 45 +++ 5 files changed, 452 insertions(+), 154 deletions(-) diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 3100ad61..8fe1b60d 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -73,7 +73,21 @@ typedef struct pg_tz *attimezone; } generate_series_timestamptz_fctx; +typedef struct IntervalAggState +{ + MemoryContext agg_context; /* context we're calculating in */ + int64 N; /* count of processed numbers */ + Interval sumX; /* sum of processed intervals */ + /* These counts are *not* included in N! Use INF_TOTAL_COUNT() as needed */ + int64 pInfcount; /* count of +Inf values */ + int64 nInfcount; /* count of -Inf values */ +} IntervalAggState; +#define INF_TOTAL_COUNT(na) \ + ((na)->N + (na)->pInfcount + (na)->nInfcount) + +static void do_interval_accum(IntervalAggState *state, Interval *newval); +static IntervalAggState *makeIntervalAggState(FunctionCallInfo fcinfo); static TimeOffset time2t(const int hour, const int min, const int sec, const fsec_t fsec); static Timestamp dt2local(Timestamp dt, int timezone); static bool AdjustIntervalForTypmod(Interval *interval, int32 typmod, @@ -3860,161 +3874,328 @@ in_range_interval_interval(PG_FUNCTION_ARGS) /* - * interval_accum, interval_accum_inv, and interval_avg implement the - * AVG(interval) aggregate. + * Prepare state data for a interval aggregate function that needs to compute + * sum, count, avg. + */ +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)); + state->agg_context = agg_context; + + MemoryContextSwitchTo(old_context); + + return state; +} + +/* + * actual do the interval accumualation. + * if to be added interval value is not finite then record. + * state's infinity/-infinity count, else do interval addidition. + */ +static void +do_interval_accum(IntervalAggState *state, Interval *newval) +{ + + Interval *X; + Interval *p; +/* i think if temp as interval pointer, following interval addition will not work.*/ + Interval temp; + MemoryContext old_context; + + X = (Interval *) palloc(sizeof(Interval)); + + /* 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; + } + + X->day = newval->day; + X->month = newval->month; + X->time = newval->time; + + temp.day = state->sumX.day; + temp.month = state->sumX.month; + temp.time = state->sumX.time; + + /* The rest of this needs to work in the aggregate context */ + old_context = MemoryContextSwitchTo(state->agg_context); + + state->N++; + + /* Accumulate sums */ + p = &state->sumX; + { + p->month = temp.month + X->month; + /* overflow check copied from int4pl */ + if (SAMESIGN(temp.month, X->month) && + !SAMESIGN(p->month, temp.month)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + + p->day = temp.day + X->day; + if (SAMESIGN(temp.day, X->day) && + !SAMESIGN(p->day, temp.day)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + + p->time = temp.time + X->time; + if (SAMESIGN(temp.time, X->time) && + !SAMESIGN(p->time, temp.time)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + } + + MemoryContextSwitchTo(old_context); + return; +} + +/* + * transition function for AVG(interval) aggregate. + */ +Datum +interval_avg_accum(PG_FUNCTION_ARGS) +{ + IntervalAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0); + + /* Create the state data on the first call */ + if (state == NULL) + state = makeIntervalAggState(fcinfo); + + if (!PG_ARGISNULL(1)) + do_interval_accum(state, PG_GETARG_INTERVAL_P(1)); + + PG_RETURN_POINTER(state); +} + +/* + *combine function to conmbine 2 interval aggreagte state. + */ +Datum +interval_avg_combine(PG_FUNCTION_ARGS) +{ + IntervalAggState *state1; + IntervalAggState *state2; + MemoryContext agg_context; + MemoryContext old_context; + + if (!AggCheckCallContext(fcinfo, &agg_context)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + state1 = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0); + state2 = PG_ARGISNULL(1) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(1); + + if (state2 == NULL) + PG_RETURN_POINTER(state1); + + /* manually copy all fields from state2 to state1 */ + if (state1 == NULL) + { + old_context = MemoryContextSwitchTo(agg_context); + + state1 = makeIntervalAggState(fcinfo); + state1->agg_context = CurrentMemoryContext; + + state1->N = state2->N; + state1->pInfcount = state2->pInfcount; + state1->nInfcount = state2->nInfcount; + + state1->sumX.day = state2->sumX.day; + state1->sumX.month = state2->sumX.month; + state1->sumX.time = state2->sumX.time; + + MemoryContextSwitchTo(old_context); + + PG_RETURN_POINTER(state1); + } + + state1->N += state2->N; + state1->pInfcount += state2->pInfcount; + state1->nInfcount += state2->nInfcount; + + if (state2->N > 0) + { + /* The rest of this needs to work in the aggregate context */ + old_context = MemoryContextSwitchTo(agg_context); + + /* Accumulate interval values */ + do_interval_accum(state1, &state2->sumX); + + MemoryContextSwitchTo(old_context); + } + PG_RETURN_POINTER(state1); +} + +static bool +do_interval_discard(IntervalAggState *state, Interval *newval) +{ + Interval *X; + MemoryContext old_context; + + X = (Interval *) palloc(sizeof(Interval)); + + /* Count -infinity inputs separately from all else */ + if (INTERVAL_IS_NOBEGIN (newval)) + { + state->nInfcount--; + return true; + } + + /* Count infinity inputs separately from all else */ + if (INTERVAL_IS_NOEND(newval)) + { + state->pInfcount--; + return true; + } + + /* the value to be discarded is not special. assign to X */ + X->day = -newval->day; + X->month = -newval->month; + X->time = -newval->time; + + state->N--; + if (state->N > 0) + { + /* The rest of this needs to work in the aggregate context */ + old_context = MemoryContextSwitchTo(state->agg_context); + + do_interval_accum(state,X); + + /* do_interval_accum increase state->N by one, we need decrease one */ + state->N--; + MemoryContextSwitchTo(old_context); + } + else + { + /*now all values discarded, reset now */ + memset(&state->sumX, 0, sizeof(state->sumX)); + } + + return true; +} + +/* + * Generic inverse transition function for interval aggregates * - * 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. */ - -Datum -interval_accum(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; - - deconstruct_array(transarray, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE, - &transdatums, NULL, &ndatums); - if (ndatums != 2) - elog(ERROR, "expected 2-element interval array"); - - sumX = *(DatumGetIntervalP(transdatums[0])); - N = *(DatumGetIntervalP(transdatums[1])); - - newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl, - IntervalPGetDatum(&sumX), - IntervalPGetDatum(newval))); - N.time += 1; - - transdatums[0] = IntervalPGetDatum(newsum); - transdatums[1] = IntervalPGetDatum(&N); - - result = construct_array(transdatums, 2, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE); - - 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, TYPALIGN_DOUBLE, - &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, TYPALIGN_DOUBLE, - &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, TYPALIGN_DOUBLE); - - PG_RETURN_ARRAYTYPE_P(result); -} - 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)) + { + /* If we fail to perform the inverse transition, return NULL */ + if (!do_interval_discard(state, PG_GETARG_INTERVAL_P(1))) + PG_RETURN_NULL(); + } - transdatums[0] = IntervalPGetDatum(newsum); - transdatums[1] = IntervalPGetDatum(&N); - - result = construct_array(transdatums, 2, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE); - - PG_RETURN_ARRAYTYPE_P(result); + PG_RETURN_POINTER(state); } +/* avg(interval) aggregate final function */ Datum interval_avg(PG_FUNCTION_ARGS) { - ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); - Datum *transdatums; - int ndatums; - Interval sumX, - N; - - deconstruct_array(transarray, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE, - &transdatums, NULL, &ndatums); - if (ndatums != 2) - elog(ERROR, "expected 2-element interval array"); - - sumX = *(DatumGetIntervalP(transdatums[0])); - N = *(DatumGetIntervalP(transdatums[1])); - - /* SQL defines AVG of no values to be NULL */ - if (N.time == 0) + 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 || INF_TOTAL_COUNT(state) == 0) PG_RETURN_NULL(); - return DirectFunctionCall2(interval_div, - IntervalPGetDatum(&sumX), - Float8GetDatum((double) N.time)); + /* 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."))); /* maybe we should make it more verbose */ + + 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_sum(PG_FUNCTION_ARGS) +{ + IntervalAggState *state; + Interval *sumX; + + sumX = (Interval *) palloc(sizeof(Interval)); + + state = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0); + + /* If there were no non-null inputs, return NULL */ + if (state == NULL || INF_TOTAL_COUNT(state) == 0) + PG_RETURN_NULL(); + + /* adding plus and minus infinities should yield error */ + if (state->pInfcount > 0 && state->nInfcount > 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range."))); /* maybe we should make it more verbose */ + + if (state->pInfcount > 0) + { + INTERVAL_NOEND(sumX); + PG_RETURN_INTERVAL_P(sumX); + } + + if (state->nInfcount > 0) + { + INTERVAL_NOBEGIN(sumX); + PG_RETURN_INTERVAL_P(sumX); + } + + sumX = &state->sumX; + PG_RETURN_INTERVAL_P(sumX); +} /* 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 1bc1d97d..905359b3 100644 --- a/src/include/catalog/pg_aggregate.dat +++ b/src/include/catalog/pg_aggregate.dat @@ -43,12 +43,12 @@ { 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' +}, # sum { aggfnoid => 'sum(int8)', aggtransfn => 'int8_avg_accum', @@ -72,10 +72,11 @@ { 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'}, { 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 9805bc61..92857368 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -2356,6 +2356,7 @@ { oid => '1169', proname => 'interval_pl', prorettype => 'interval', proargtypes => 'interval interval', prosrc => 'interval_pl' }, + { oid => '1170', proname => 'interval_mi', prorettype => 'interval', proargtypes => 'interval interval', prosrc => 'interval_mi' }, @@ -4914,17 +4915,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 69a38df1..85b21d7a 100644 --- a/src/test/regress/expected/window.out +++ b/src/test/regress/expected/window.out @@ -4375,6 +4375,70 @@ 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 + ,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_avg +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 | curr_next_avg | prev1_curr_avg +-----------+---------------+----------------+---------------+---------------- + -infinity | -infinity | -infinity | -infinity | -infinity + @ 6 days | infinity | -infinity | infinity | -infinity + infinity | infinity | infinity | 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 9113a92a..d23370a1 100644 --- a/src/test/regress/sql/window.sql +++ b/src/test/regress/sql/window.sql @@ -1591,6 +1591,51 @@ 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 + ,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_avg +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); -- 2.34.1