diff options
Diffstat (limited to 'src/backend/utils/adt/timestamp.c')
| -rw-r--r-- | src/backend/utils/adt/timestamp.c | 1040 |
1 files changed, 848 insertions, 192 deletions
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 647b97aca69..45abb79e766 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -72,6 +72,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 an IntervalAggState allocated in the aggregate context. + */ +typedef struct IntervalAggState +{ + int64 N; /* count of finite intervals processed */ + Interval sumX; /* sum of finite intervals processed */ + /* These counts are *not* included in N! Use IA_TOTAL_COUNT() as needed */ + int64 pInfcount; /* count of +infinity intervals */ + int64 nInfcount; /* count of -infinity intervals */ +} 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); @@ -80,6 +95,8 @@ static bool AdjustIntervalForTypmod(Interval *interval, int32 typmod, static TimestampTz timestamp2timestamptz(Timestamp timestamp); static Timestamp timestamptz2timestamp(TimestampTz timestamp); +static void EncodeSpecialInterval(const Interval *interval, char *str); +static void interval_um_internal(const Interval *interval, Interval *result); /* common code for timestamptypmodin and timestamptztypmodin */ static int32 @@ -941,6 +958,14 @@ interval_in(PG_FUNCTION_ARGS) errmsg("interval out of range"))); break; + case DTK_LATE: + INTERVAL_NOEND(result); + break; + + case DTK_EARLY: + INTERVAL_NOBEGIN(result); + break; + default: elog(ERROR, "unexpected dtype %d while parsing interval \"%s\"", dtype, str); @@ -963,8 +988,13 @@ interval_out(PG_FUNCTION_ARGS) *itm = &tt; char buf[MAXDATELEN + 1]; - interval2itm(*span, itm); - EncodeInterval(itm, IntervalStyle, buf); + if (INTERVAL_NOT_FINITE(span)) + EncodeSpecialInterval(span, buf); + else + { + interval2itm(*span, itm); + EncodeInterval(itm, IntervalStyle, buf); + } result = pstrdup(buf); PG_RETURN_CSTRING(result); @@ -1350,6 +1380,10 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod, INT64CONST(0) }; + /* Typmod has no effect on infinite intervals */ + if (INTERVAL_NOT_FINITE(interval)) + return true; + /* * Unspecified range and precision? Then not necessary to adjust. Setting * typmod to -1 is the convention for all data types. @@ -1536,6 +1570,10 @@ make_interval(PG_FUNCTION_ARGS) pg_add_s64_overflow(result->time, (int64) secs, &result->time)) goto out_of_range; + /* make sure that the result is finite */ + if (INTERVAL_NOT_FINITE(result)) + goto out_of_range; + PG_RETURN_INTERVAL_P(result); out_of_range: @@ -1560,6 +1598,17 @@ EncodeSpecialTimestamp(Timestamp dt, char *str) elog(ERROR, "invalid argument for EncodeSpecialTimestamp"); } +static void +EncodeSpecialInterval(const Interval *interval, char *str) +{ + if (INTERVAL_IS_NOBEGIN(interval)) + strcpy(str, EARLY); + else if (INTERVAL_IS_NOEND(interval)) + strcpy(str, LATE); + else /* shouldn't happen */ + elog(ERROR, "invalid argument for EncodeSpecialInterval"); +} + Datum now(PG_FUNCTION_ARGS) { @@ -2015,6 +2064,9 @@ interval2itm(Interval span, struct pg_itm *itm) /* itm2interval() * Convert a pg_itm structure to an Interval. * Returns 0 if OK, -1 on overflow. + * + * This is for use in computations expected to produce finite results. Any + * inputs that lead to infinite results are treated as overflows. */ int itm2interval(struct pg_itm *itm, Interval *span) @@ -2038,12 +2090,21 @@ itm2interval(struct pg_itm *itm, Interval *span) if (pg_add_s64_overflow(span->time, itm->tm_usec, &span->time)) return -1; + if (INTERVAL_NOT_FINITE(span)) + return -1; return 0; } /* itmin2interval() * Convert a pg_itm_in structure to an Interval. * Returns 0 if OK, -1 on overflow. + * + * Note: if the result is infinite, it is not treated as an overflow. This + * avoids any dump/reload hazards from pre-17 databases that do not support + * infinite intervals, but do allow finite intervals with all fields set to + * INT_MIN/INT_MAX (outside the documented range). Such intervals will be + * silently converted to +/-infinity. This may not be ideal, but seems + * preferable to failure, and ought to be pretty unlikely in practice. */ int itmin2interval(struct pg_itm_in *itm_in, Interval *span) @@ -2088,7 +2149,9 @@ timestamp_finite(PG_FUNCTION_ARGS) Datum interval_finite(PG_FUNCTION_ARGS) { - PG_RETURN_BOOL(true); + Interval *interval = PG_GETARG_INTERVAL_P(0); + + PG_RETURN_BOOL(!INTERVAL_NOT_FINITE(interval)); } @@ -2442,6 +2505,15 @@ interval_cmp_internal(const Interval *interval1, const Interval *interval2) return int128_compare(span1, span2); } +static int +interval_sign(const Interval *interval) +{ + INT128 span = interval_cmp_value(interval); + INT128 zero = int64_to_int128(0); + + return int128_compare(span, zero); +} + Datum interval_eq(PG_FUNCTION_ARGS) { @@ -2714,10 +2786,39 @@ timestamp_mi(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); + /* + * Handle infinities. + * + * We treat anything that amounts to "infinity - infinity" as an error, + * since the interval type has nothing equivalent to NaN. + */ if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2)) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("cannot subtract infinite timestamps"))); + { + if (TIMESTAMP_IS_NOBEGIN(dt1)) + { + if (TIMESTAMP_IS_NOBEGIN(dt2)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + else + INTERVAL_NOBEGIN(result); + } + else if (TIMESTAMP_IS_NOEND(dt1)) + { + if (TIMESTAMP_IS_NOEND(dt2)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + else + INTERVAL_NOEND(result); + } + else if (TIMESTAMP_IS_NOBEGIN(dt2)) + INTERVAL_NOEND(result); + else /* TIMESTAMP_IS_NOEND(dt2) */ + INTERVAL_NOBEGIN(result); + + PG_RETURN_INTERVAL_P(result); + } if (unlikely(pg_sub_s64_overflow(dt1, dt2, &result->time))) ereport(ERROR, @@ -2783,6 +2884,10 @@ interval_justify_interval(PG_FUNCTION_ARGS) result->day = span->day; result->time = span->time; + /* do nothing for infinite intervals */ + if (INTERVAL_NOT_FINITE(result)) + PG_RETURN_INTERVAL_P(result); + /* pre-justify days if it might prevent overflow */ if ((result->day > 0 && result->time > 0) || (result->day < 0 && result->time < 0)) @@ -2858,6 +2963,10 @@ interval_justify_hours(PG_FUNCTION_ARGS) result->day = span->day; result->time = span->time; + /* do nothing for infinite intervals */ + if (INTERVAL_NOT_FINITE(result)) + PG_RETURN_INTERVAL_P(result); + TMODULO(result->time, wholeday, USECS_PER_DAY); if (pg_add_s32_overflow(result->day, wholeday, &result->day)) ereport(ERROR, @@ -2896,6 +3005,10 @@ interval_justify_days(PG_FUNCTION_ARGS) result->day = span->day; result->time = span->time; + /* do nothing for infinite intervals */ + if (INTERVAL_NOT_FINITE(result)) + PG_RETURN_INTERVAL_P(result); + wholemonth = result->day / DAYS_PER_MONTH; result->day -= wholemonth * DAYS_PER_MONTH; if (pg_add_s32_overflow(result->month, wholemonth, &result->month)) @@ -2934,7 +3047,31 @@ timestamp_pl_interval(PG_FUNCTION_ARGS) Interval *span = PG_GETARG_INTERVAL_P(1); Timestamp result; - if (TIMESTAMP_NOT_FINITE(timestamp)) + /* + * Handle infinities. + * + * We treat anything that amounts to "infinity - infinity" as an error, + * since the timestamp type has nothing equivalent to NaN. + */ + if (INTERVAL_IS_NOBEGIN(span)) + { + if (TIMESTAMP_IS_NOEND(timestamp)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + else + TIMESTAMP_NOBEGIN(result); + } + else if (INTERVAL_IS_NOEND(span)) + { + if (TIMESTAMP_IS_NOBEGIN(timestamp)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + else + TIMESTAMP_NOEND(result); + } + else if (TIMESTAMP_NOT_FINITE(timestamp)) result = timestamp; else { @@ -3013,9 +3150,7 @@ timestamp_mi_interval(PG_FUNCTION_ARGS) Interval *span = PG_GETARG_INTERVAL_P(1); Interval tspan; - tspan.month = -span->month; - tspan.day = -span->day; - tspan.time = -span->time; + interval_um_internal(span, &tspan); return DirectFunctionCall2(timestamp_pl_interval, TimestampGetDatum(timestamp), @@ -3042,7 +3177,31 @@ timestamptz_pl_interval_internal(TimestampTz timestamp, TimestampTz result; int tz; - if (TIMESTAMP_NOT_FINITE(timestamp)) + /* + * Handle infinities. + * + * We treat anything that amounts to "infinity - infinity" as an error, + * since the timestamptz type has nothing equivalent to NaN. + */ + if (INTERVAL_IS_NOBEGIN(span)) + { + if (TIMESTAMP_IS_NOEND(timestamp)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + else + TIMESTAMP_NOBEGIN(result); + } + else if (INTERVAL_IS_NOEND(span)) + { + if (TIMESTAMP_IS_NOBEGIN(timestamp)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + else + TIMESTAMP_NOEND(result); + } + else if (TIMESTAMP_NOT_FINITE(timestamp)) result = timestamp; else { @@ -3132,9 +3291,7 @@ timestamptz_mi_interval_internal(TimestampTz timestamp, { Interval tspan; - tspan.month = -span->month; - tspan.day = -span->day; - tspan.time = -span->time; + interval_um_internal(span, &tspan); return timestamptz_pl_interval_internal(timestamp, &tspan, attimezone); } @@ -3185,6 +3342,29 @@ timestamptz_mi_interval_at_zone(PG_FUNCTION_ARGS) PG_RETURN_TIMESTAMP(timestamptz_mi_interval_internal(timestamp, span, attimezone)); } +/* interval_um_internal() + * Negate an interval. + */ +static void +interval_um_internal(const Interval *interval, Interval *result) +{ + if (INTERVAL_IS_NOBEGIN(interval)) + INTERVAL_NOEND(result); + else if (INTERVAL_IS_NOEND(interval)) + INTERVAL_NOBEGIN(result); + else + { + /* Negate each field, guarding against overflow */ + if (pg_sub_s64_overflow(INT64CONST(0), interval->time, &result->time) || + pg_sub_s32_overflow(0, interval->day, &result->day) || + pg_sub_s32_overflow(0, interval->month, &result->month) || + INTERVAL_NOT_FINITE(result)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + } +} + Datum interval_um(PG_FUNCTION_ARGS) { @@ -3192,23 +3372,7 @@ interval_um(PG_FUNCTION_ARGS) Interval *result; result = (Interval *) palloc(sizeof(Interval)); - - result->time = -interval->time; - /* overflow check copied from int4um */ - if (interval->time != 0 && SAMESIGN(result->time, interval->time)) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("interval out of range"))); - result->day = -interval->day; - if (interval->day != 0 && SAMESIGN(result->day, interval->day)) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("interval out of range"))); - result->month = -interval->month; - if (interval->month != 0 && SAMESIGN(result->month, interval->month)) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("interval out of range"))); + interval_um_internal(interval, result); PG_RETURN_INTERVAL_P(result); } @@ -3243,6 +3407,21 @@ interval_larger(PG_FUNCTION_ARGS) PG_RETURN_INTERVAL_P(result); } +static void +finite_interval_pl(const Interval *span1, const Interval *span2, Interval *result) +{ + Assert(!INTERVAL_NOT_FINITE(span1)); + Assert(!INTERVAL_NOT_FINITE(span2)); + + if (pg_add_s32_overflow(span1->month, span2->month, &result->month) || + pg_add_s32_overflow(span1->day, span2->day, &result->day) || + pg_add_s64_overflow(span1->time, span2->time, &result->time) || + INTERVAL_NOT_FINITE(result)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); +} + Datum interval_pl(PG_FUNCTION_ARGS) { @@ -3252,29 +3431,51 @@ interval_pl(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); - 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"))); + /* + * Handle infinities. + * + * We treat anything that amounts to "infinity - infinity" as an error, + * since the interval type has nothing equivalent to NaN. + */ + if (INTERVAL_IS_NOBEGIN(span1)) + { + if (INTERVAL_IS_NOEND(span2)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + else + INTERVAL_NOBEGIN(result); + } + else if (INTERVAL_IS_NOEND(span1)) + { + if (INTERVAL_IS_NOBEGIN(span2)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + else + INTERVAL_NOEND(result); + } + else if (INTERVAL_NOT_FINITE(span2)) + memcpy(result, span2, sizeof(Interval)); + else + finite_interval_pl(span1, span2, result); - 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"))); + PG_RETURN_INTERVAL_P(result); +} + +static void +finite_interval_mi(const Interval *span1, const Interval *span2, Interval *result) +{ + Assert(!INTERVAL_NOT_FINITE(span1)); + Assert(!INTERVAL_NOT_FINITE(span2)); - result->time = span1->time + span2->time; - if (SAMESIGN(span1->time, span2->time) && - !SAMESIGN(result->time, span1->time)) + if (pg_sub_s32_overflow(span1->month, span2->month, &result->month) || + pg_sub_s32_overflow(span1->day, span2->day, &result->day) || + pg_sub_s64_overflow(span1->time, span2->time, &result->time) || + INTERVAL_NOT_FINITE(result)) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); - - PG_RETURN_INTERVAL_P(result); } Datum @@ -3286,27 +3487,36 @@ interval_mi(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); - result->month = span1->month - span2->month; - /* overflow check copied from int4mi */ - 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"))); + /* + * Handle infinities. + * + * We treat anything that amounts to "infinity - infinity" as an error, + * since the interval type has nothing equivalent to NaN. + */ + if (INTERVAL_IS_NOBEGIN(span1)) + { + if (INTERVAL_IS_NOBEGIN(span2)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + else + INTERVAL_NOBEGIN(result); + } + else if (INTERVAL_IS_NOEND(span1)) + { + if (INTERVAL_IS_NOEND(span2)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + else + INTERVAL_NOEND(result); + } + else if (INTERVAL_IS_NOBEGIN(span2)) + INTERVAL_NOEND(result); + else if (INTERVAL_IS_NOEND(span2)) + INTERVAL_NOBEGIN(result); + else + finite_interval_mi(span1, span2, result); PG_RETURN_INTERVAL_P(result); } @@ -3331,6 +3541,46 @@ interval_mul(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); + /* + * Handle NaN and infinities. + * + * We treat "0 * infinity" and "infinity * 0" as errors, since the + * interval type has nothing equivalent to NaN. + */ + if (isnan(factor)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + + if (INTERVAL_NOT_FINITE(span)) + { + if (factor == 0.0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + else if (factor < 0.0) + interval_um_internal(span, result); + else + memcpy(result, span, sizeof(Interval)); + + PG_RETURN_INTERVAL_P(result); + } + if (isinf(factor)) + { + int isign = interval_sign(span); + + if (isign == 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + else if (factor * isign < 0) + INTERVAL_NOBEGIN(result); + else + INTERVAL_NOEND(result); + + PG_RETURN_INTERVAL_P(result); + } + result_double = span->month * factor; if (isnan(result_double) || result_double > INT_MAX || result_double < INT_MIN) @@ -3391,6 +3641,11 @@ interval_mul(PG_FUNCTION_ARGS) errmsg("interval out of range"))); result->time = (int64) result_double; + if (INTERVAL_NOT_FINITE(result)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + PG_RETURN_INTERVAL_P(result); } @@ -3422,6 +3677,33 @@ interval_div(PG_FUNCTION_ARGS) (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + /* + * Handle NaN and infinities. + * + * We treat "infinity / infinity" as an error, since the interval type has + * nothing equivalent to NaN. Otherwise, dividing by infinity is handled + * by the regular division code, causing all fields to be set to zero. + */ + if (isnan(factor)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + + if (INTERVAL_NOT_FINITE(span)) + { + if (isinf(factor)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + + if (factor < 0.0) + interval_um_internal(span, result); + else + memcpy(result, span, sizeof(Interval)); + + PG_RETURN_INTERVAL_P(result); + } + result->month = (int32) (span->month / factor); result->day = (int32) (span->day / factor); @@ -3443,6 +3725,11 @@ interval_div(PG_FUNCTION_ARGS) result->day += (int32) month_remainder_days; result->time = rint(span->time / factor + sec_remainder * USECS_PER_SEC); + if (INTERVAL_NOT_FINITE(result)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + PG_RETURN_INTERVAL_P(result); } @@ -3466,11 +3753,21 @@ in_range_timestamptz_interval(PG_FUNCTION_ARGS) bool less = PG_GETARG_BOOL(4); TimestampTz sum; - if (int128_compare(interval_cmp_value(offset), int64_to_int128(0)) < 0) + if (interval_sign(offset) < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE), errmsg("invalid preceding or following size in window function"))); + /* + * Deal with cases where both base and offset are infinite, and computing + * base +/- offset would cause an error. As for float and numeric types, + * we assume that all values infinitely precede +infinity and infinitely + * follow -infinity. See in_range_float8_float8() for reasoning. + */ + if (INTERVAL_IS_NOEND(offset) && + (sub ? TIMESTAMP_IS_NOEND(base) : TIMESTAMP_IS_NOBEGIN(base))) + PG_RETURN_BOOL(true); + /* We don't currently bother to avoid overflow hazards here */ if (sub) sum = timestamptz_mi_interval_internal(base, offset, NULL); @@ -3493,11 +3790,21 @@ in_range_timestamp_interval(PG_FUNCTION_ARGS) bool less = PG_GETARG_BOOL(4); Timestamp sum; - if (int128_compare(interval_cmp_value(offset), int64_to_int128(0)) < 0) + if (interval_sign(offset) < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE), errmsg("invalid preceding or following size in window function"))); + /* + * Deal with cases where both base and offset are infinite, and computing + * base +/- offset would cause an error. As for float and numeric types, + * we assume that all values infinitely precede +infinity and infinitely + * follow -infinity. See in_range_float8_float8() for reasoning. + */ + if (INTERVAL_IS_NOEND(offset) && + (sub ? TIMESTAMP_IS_NOEND(base) : TIMESTAMP_IS_NOBEGIN(base))) + PG_RETURN_BOOL(true); + /* We don't currently bother to avoid overflow hazards here */ if (sub) sum = DatumGetTimestamp(DirectFunctionCall2(timestamp_mi_interval, @@ -3524,11 +3831,21 @@ in_range_interval_interval(PG_FUNCTION_ARGS) bool less = PG_GETARG_BOOL(4); Interval *sum; - if (int128_compare(interval_cmp_value(offset), int64_to_int128(0)) < 0) + if (interval_sign(offset) < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE), errmsg("invalid preceding or following size in window function"))); + /* + * Deal with cases where both base and offset are infinite, and computing + * base +/- offset would cause an error. As for float and numeric types, + * we assume that all values infinitely precede +infinity and infinitely + * follow -infinity. See in_range_float8_float8() for reasoning. + */ + if (INTERVAL_IS_NOEND(offset) && + (sub ? INTERVAL_IS_NOEND(base) : INTERVAL_IS_NOBEGIN(base))) + PG_RETURN_BOOL(true); + /* We don't currently bother to avoid overflow hazards here */ if (sub) sum = DatumGetIntervalP(DirectFunctionCall2(interval_mi, @@ -3547,161 +3864,327 @@ 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, in the aggregate's memory context. * - * 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. + * The function is used when the state data needs to be allocated in aggregate's + * context. When the state data needs to be allocated in the current memory + * context, we use palloc0 directly e.g. interval_avg_deserialize(). + */ +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; +} + +/* + * Accumulate a new input value for interval aggregate functions. + */ +static void +do_interval_accum(IntervalAggState *state, Interval *newval) +{ + /* Infinite inputs are counted separately, and do not affect "N" */ + if (INTERVAL_IS_NOBEGIN(newval)) + { + state->nInfcount++; + return; + } + + if (INTERVAL_IS_NOEND(newval)) + { + state->pInfcount++; + return; + } + + finite_interval_pl(&state->sumX, newval, &state->sumX); + state->N++; +} + +/* + * Remove the given interval value from the aggregated state. + */ +static void +do_interval_discard(IntervalAggState *state, Interval *newval) +{ + /* Infinite inputs are counted separately, and do not affect "N" */ + if (INTERVAL_IS_NOBEGIN(newval)) + { + state->nInfcount--; + return; + } + + if (INTERVAL_IS_NOEND(newval)) + { + state->pInfcount--; + return; + } + + /* Handle the to-be-discarded finite value. */ + state->N--; + if (state->N > 0) + finite_interval_mi(&state->sumX, newval, &state->sumX); + else + { + /* All values discarded, reset the state */ + Assert(state->N == 0); + memset(&state->sumX, 0, sizeof(state->sumX)); + } +} + +/* + * Transition function for sum() and avg() interval aggregates. */ +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 for sum() and avg() interval aggregates. + * + * Combine the given internal aggregate states and place the combination in + * the first argument. + */ Datum -interval_accum(PG_FUNCTION_ARGS) +interval_avg_combine(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 *state1; + IntervalAggState *state2; + + 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); + + if (state1 == NULL) + { + /* manually copy all fields from state2 to state1 */ + state1 = makeIntervalAggState(fcinfo); - deconstruct_array(transarray, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE, - &transdatums, NULL, &ndatums); - if (ndatums != 2) - elog(ERROR, "expected 2-element interval array"); + state1->N = state2->N; + state1->pInfcount = state2->pInfcount; + state1->nInfcount = state2->nInfcount; - sumX = *(DatumGetIntervalP(transdatums[0])); - N = *(DatumGetIntervalP(transdatums[1])); + state1->sumX.day = state2->sumX.day; + state1->sumX.month = state2->sumX.month; + state1->sumX.time = state2->sumX.time; - newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl, - IntervalPGetDatum(&sumX), - IntervalPGetDatum(newval))); - N.time += 1; + PG_RETURN_POINTER(state1); + } - transdatums[0] = IntervalPGetDatum(newsum); - transdatums[1] = IntervalPGetDatum(&N); + state1->N += state2->N; + state1->pInfcount += state2->pInfcount; + state1->nInfcount += state2->nInfcount; - result = construct_array(transdatums, 2, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE); + /* Accumulate finite interval values, if any. */ + if (state2->N > 0) + finite_interval_pl(&state1->sumX, &state2->sumX, &state1->sumX); - PG_RETURN_ARRAYTYPE_P(result); + PG_RETURN_POINTER(state1); } +/* + * interval_avg_serialize + * Serialize IntervalAggState for interval aggregates. + */ Datum -interval_combine(PG_FUNCTION_ARGS) +interval_avg_serialize(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 *state; + StringInfoData buf; + bytea *result; - Interval *newsum; - ArrayType *result; + /* Ensure we disallow calling when not in aggregate context */ + if (!AggCheckCallContext(fcinfo, NULL)) + elog(ERROR, "aggregate function called in non-aggregate context"); - deconstruct_array(transarray1, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE, - &transdatums1, NULL, &ndatums1); - if (ndatums1 != 2) - elog(ERROR, "expected 2-element interval array"); + state = (IntervalAggState *) PG_GETARG_POINTER(0); - sum1 = *(DatumGetIntervalP(transdatums1[0])); - N1 = *(DatumGetIntervalP(transdatums1[1])); + pq_begintypsend(&buf); - deconstruct_array(transarray2, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE, - &transdatums2, NULL, &ndatums2); - if (ndatums2 != 2) - elog(ERROR, "expected 2-element interval array"); + /* N */ + pq_sendint64(&buf, state->N); - sum2 = *(DatumGetIntervalP(transdatums2[0])); - N2 = *(DatumGetIntervalP(transdatums2[1])); + /* sumX */ + pq_sendint64(&buf, state->sumX.time); + pq_sendint32(&buf, state->sumX.day); + pq_sendint32(&buf, state->sumX.month); - newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl, - IntervalPGetDatum(&sum1), - IntervalPGetDatum(&sum2))); - N1.time += N2.time; + /* pInfcount */ + pq_sendint64(&buf, state->pInfcount); - transdatums1[0] = IntervalPGetDatum(newsum); - transdatums1[1] = IntervalPGetDatum(&N1); + /* nInfcount */ + pq_sendint64(&buf, state->nInfcount); - result = construct_array(transdatums1, 2, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE); + result = pq_endtypsend(&buf); - PG_RETURN_ARRAYTYPE_P(result); + PG_RETURN_BYTEA_P(result); } +/* + * interval_avg_deserialize + * Deserialize bytea into IntervalAggState for interval aggregates. + */ Datum -interval_accum_inv(PG_FUNCTION_ARGS) +interval_avg_deserialize(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; + bytea *sstate; + IntervalAggState *result; + StringInfoData buf; + + if (!AggCheckCallContext(fcinfo, NULL)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + sstate = PG_GETARG_BYTEA_PP(0); + + /* + * Initialize a StringInfo so that we can "receive" it using the standard + * recv-function infrastructure. + */ + initReadOnlyStringInfo(&buf, VARDATA_ANY(sstate), + VARSIZE_ANY_EXHDR(sstate)); + + result = (IntervalAggState *) palloc0(sizeof(IntervalAggState)); - deconstruct_array(transarray, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE, - &transdatums, NULL, &ndatums); - if (ndatums != 2) - elog(ERROR, "expected 2-element interval array"); + /* N */ + result->N = pq_getmsgint64(&buf); - sumX = *(DatumGetIntervalP(transdatums[0])); - N = *(DatumGetIntervalP(transdatums[1])); + /* sumX */ + result->sumX.time = pq_getmsgint64(&buf); + result->sumX.day = pq_getmsgint(&buf, 4); + result->sumX.month = pq_getmsgint(&buf, 4); - newsum = DatumGetIntervalP(DirectFunctionCall2(interval_mi, - IntervalPGetDatum(&sumX), - IntervalPGetDatum(newval))); - N.time -= 1; + /* pInfcount */ + result->pInfcount = pq_getmsgint64(&buf); - transdatums[0] = IntervalPGetDatum(newsum); - transdatums[1] = IntervalPGetDatum(&N); + /* nInfcount */ + result->nInfcount = pq_getmsgint64(&buf); - result = construct_array(transdatums, 2, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE); + pq_getmsgend(&buf); - PG_RETURN_ARRAYTYPE_P(result); + PG_RETURN_POINTER(result); } +/* + * Inverse transition function for sum() and avg() interval aggregates. + */ Datum -interval_avg(PG_FUNCTION_ARGS) +interval_avg_accum_inv(PG_FUNCTION_ARGS) { - ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); - Datum *transdatums; - int ndatums; - Interval sumX, - N; + IntervalAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0); - deconstruct_array(transarray, - INTERVALOID, sizeof(Interval), false, TYPALIGN_DOUBLE, - &transdatums, NULL, &ndatums); - if (ndatums != 2) - elog(ERROR, "expected 2-element interval array"); + /* Should not get here with no state */ + if (state == NULL) + elog(ERROR, "interval_avg_accum_inv called with NULL state"); - sumX = *(DatumGetIntervalP(transdatums[0])); - N = *(DatumGetIntervalP(transdatums[1])); + if (!PG_ARGISNULL(1)) + do_interval_discard(state, PG_GETARG_INTERVAL_P(1)); - /* SQL defines AVG of no values to be NULL */ - if (N.time == 0) + PG_RETURN_POINTER(state); +} + +/* avg(interval) aggregate final function */ +Datum +interval_avg(PG_FUNCTION_ARGS) +{ + IntervalAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0); + + /* If there were no non-null inputs, return NULL */ + if (state == NULL || IA_TOTAL_COUNT(state) == 0) PG_RETURN_NULL(); + /* + * Aggregating infinities that all have the same sign produces infinity + * with that sign. Aggregating infinities with different signs results in + * an error. + */ + if (state->pInfcount > 0 || state->nInfcount > 0) + { + Interval *result; + + if (state->pInfcount > 0 && state->nInfcount > 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range."))); + + result = (Interval *) palloc(sizeof(Interval)); + if (state->pInfcount > 0) + INTERVAL_NOEND(result); + else + INTERVAL_NOBEGIN(result); + + PG_RETURN_INTERVAL_P(result); + } + return DirectFunctionCall2(interval_div, - IntervalPGetDatum(&sumX), - Float8GetDatum((double) N.time)); + IntervalPGetDatum(&state->sumX), + Float8GetDatum((double) state->N)); } +/* sum(interval) aggregate final function */ +Datum +interval_sum(PG_FUNCTION_ARGS) +{ + IntervalAggState *state; + Interval *result; + + state = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0); + + /* If there were no non-null inputs, return NULL */ + if (state == NULL || IA_TOTAL_COUNT(state) == 0) + PG_RETURN_NULL(); + + /* + * Aggregating infinities that all have the same sign produces infinity + * with that sign. Aggregating infinities with different signs results in + * an error. + */ + if (state->pInfcount > 0 && state->nInfcount > 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range."))); + + result = (Interval *) palloc(sizeof(Interval)); + + if (state->pInfcount > 0) + INTERVAL_NOEND(result); + else if (state->nInfcount > 0) + INTERVAL_NOBEGIN(result); + else + memcpy(result, &state->sumX, sizeof(Interval)); + + PG_RETURN_INTERVAL_P(result); +} /* timestamp_age() * Calculate time difference while retaining year/month fields. @@ -3726,8 +4209,36 @@ timestamp_age(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); - if (timestamp2tm(dt1, NULL, tm1, &fsec1, NULL, NULL) == 0 && - timestamp2tm(dt2, NULL, tm2, &fsec2, NULL, NULL) == 0) + /* + * Handle infinities. + * + * We treat anything that amounts to "infinity - infinity" as an error, + * since the interval type has nothing equivalent to NaN. + */ + if (TIMESTAMP_IS_NOBEGIN(dt1)) + { + if (TIMESTAMP_IS_NOBEGIN(dt2)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + else + INTERVAL_NOBEGIN(result); + } + else if (TIMESTAMP_IS_NOEND(dt1)) + { + if (TIMESTAMP_IS_NOEND(dt2)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + else + INTERVAL_NOEND(result); + } + else if (TIMESTAMP_IS_NOBEGIN(dt2)) + INTERVAL_NOEND(result); + else if (TIMESTAMP_IS_NOEND(dt2)) + INTERVAL_NOBEGIN(result); + else if (timestamp2tm(dt1, NULL, tm1, &fsec1, NULL, NULL) == 0 && + timestamp2tm(dt2, NULL, tm2, &fsec2, NULL, NULL) == 0) { /* form the symbolic difference */ tm->tm_usec = fsec1 - fsec2; @@ -3846,8 +4357,36 @@ timestamptz_age(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); - if (timestamp2tm(dt1, &tz1, tm1, &fsec1, NULL, NULL) == 0 && - timestamp2tm(dt2, &tz2, tm2, &fsec2, NULL, NULL) == 0) + /* + * Handle infinities. + * + * We treat anything that amounts to "infinity - infinity" as an error, + * since the interval type has nothing equivalent to NaN. + */ + if (TIMESTAMP_IS_NOBEGIN(dt1)) + { + if (TIMESTAMP_IS_NOBEGIN(dt2)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + else + INTERVAL_NOBEGIN(result); + } + else if (TIMESTAMP_IS_NOEND(dt1)) + { + if (TIMESTAMP_IS_NOEND(dt2)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("interval out of range"))); + else + INTERVAL_NOEND(result); + } + else if (TIMESTAMP_IS_NOBEGIN(dt2)) + INTERVAL_NOEND(result); + else if (TIMESTAMP_IS_NOEND(dt2)) + INTERVAL_NOBEGIN(result); + else if (timestamp2tm(dt1, &tz1, tm1, &fsec1, NULL, NULL) == 0 && + timestamp2tm(dt2, &tz2, tm2, &fsec2, NULL, NULL) == 0) { /* form the symbolic difference */ tm->tm_usec = fsec1 - fsec2; @@ -3972,6 +4511,11 @@ timestamp_bin(PG_FUNCTION_ARGS) (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("origin out of range"))); + if (INTERVAL_NOT_FINITE(stride)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamps cannot be binned into infinite intervals"))); + if (stride->month != 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -4155,6 +4699,11 @@ timestamptz_bin(PG_FUNCTION_ARGS) (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("origin out of range"))); + if (INTERVAL_NOT_FINITE(stride)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamps cannot be binned into infinite intervals"))); + if (stride->month != 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -4393,6 +4942,12 @@ interval_trunc(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); + if (INTERVAL_NOT_FINITE(interval)) + { + memcpy(result, interval, sizeof(Interval)); + PG_RETURN_INTERVAL_P(result); + } + lowunits = downcase_truncate_identifier(VARDATA_ANY(units), VARSIZE_ANY_EXHDR(units), false); @@ -4737,7 +5292,7 @@ timestamp_part_common(PG_FUNCTION_ARGS, bool retnumeric) TIMESTAMP_IS_NOBEGIN(timestamp), false); - if (r) + if (r != 0.0) { if (retnumeric) { @@ -5011,7 +5566,7 @@ timestamptz_part_common(PG_FUNCTION_ARGS, bool retnumeric) TIMESTAMP_IS_NOBEGIN(timestamp), true); - if (r) + if (r != 0.0) { if (retnumeric) { @@ -5251,6 +5806,58 @@ extract_timestamptz(PG_FUNCTION_ARGS) return timestamptz_part_common(fcinfo, true); } +/* + * NonFiniteIntervalPart + * + * Used by interval_part when extracting from infinite interval. Returns + * +/-Infinity if that is the appropriate result, otherwise returns zero + * (which should be taken as meaning to return NULL). + * + * Errors thrown here for invalid units should exactly match those that + * would be thrown in the calling functions, else there will be unexpected + * discrepancies between finite- and infinite-input cases. + */ +static float8 +NonFiniteIntervalPart(int type, int unit, char *lowunits, bool isNegative) +{ + if ((type != UNITS) && (type != RESERV)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unit \"%s\" not recognized for type %s", + lowunits, format_type_be(INTERVALOID)))); + + switch (unit) + { + /* Oscillating units */ + case DTK_MICROSEC: + case DTK_MILLISEC: + case DTK_SECOND: + case DTK_MINUTE: + case DTK_MONTH: + case DTK_QUARTER: + return 0.0; + + /* Monotonically-increasing units */ + case DTK_HOUR: + case DTK_DAY: + case DTK_YEAR: + case DTK_DECADE: + case DTK_CENTURY: + case DTK_MILLENNIUM: + case DTK_EPOCH: + if (isNegative) + return -get_float8_infinity(); + else + return get_float8_infinity(); + + default: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("unit \"%s\" not supported for type %s", + lowunits, format_type_be(INTERVALOID)))); + return 0.0; /* keep compiler quiet */ + } +} /* interval_part() and extract_interval() * Extract specified field from interval. @@ -5275,6 +5882,33 @@ interval_part_common(PG_FUNCTION_ARGS, bool retnumeric) if (type == UNKNOWN_FIELD) type = DecodeSpecial(0, lowunits, &val); + if (INTERVAL_NOT_FINITE(interval)) + { + double r = NonFiniteIntervalPart(type, val, lowunits, + INTERVAL_IS_NOBEGIN(interval)); + + if (r != 0.0) + { + if (retnumeric) + { + if (r < 0) + return DirectFunctionCall3(numeric_in, + CStringGetDatum("-Infinity"), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1)); + else if (r > 0) + return DirectFunctionCall3(numeric_in, + CStringGetDatum("Infinity"), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1)); + } + else + PG_RETURN_FLOAT8(r); + } + else + PG_RETURN_NULL(); + } + if (type == UNITS) { interval2itm(*interval, tm); @@ -5517,6 +6151,13 @@ timestamp_izone(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) PG_RETURN_TIMESTAMPTZ(timestamp); + if (INTERVAL_NOT_FINITE(zone)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("interval time zone \"%s\" must be finite", + DatumGetCString(DirectFunctionCall1(interval_out, + PointerGetDatum(zone)))))); + if (zone->month != 0 || zone->day != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -5747,6 +6388,13 @@ timestamptz_izone(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) PG_RETURN_TIMESTAMP(timestamp); + if (INTERVAL_NOT_FINITE(zone)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("interval time zone \"%s\" must be finite", + DatumGetCString(DirectFunctionCall1(interval_out, + PointerGetDatum(zone)))))); + if (zone->month != 0 || zone->day != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -5783,7 +6431,6 @@ generate_series_timestamp(PG_FUNCTION_ARGS) Timestamp finish = PG_GETARG_TIMESTAMP(1); Interval *step = PG_GETARG_INTERVAL_P(2); MemoryContext oldcontext; - const Interval interval_zero = {0}; /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); @@ -5806,13 +6453,18 @@ generate_series_timestamp(PG_FUNCTION_ARGS) fctx->step = *step; /* Determine sign of the interval */ - fctx->step_sign = interval_cmp_internal(&fctx->step, &interval_zero); + fctx->step_sign = interval_sign(&fctx->step); if (fctx->step_sign == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("step size cannot equal zero"))); + if (INTERVAL_NOT_FINITE((&fctx->step))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("step size cannot be infinite"))); + funcctx->user_fctx = fctx; MemoryContextSwitchTo(oldcontext); } @@ -5864,7 +6516,6 @@ generate_series_timestamptz_internal(FunctionCallInfo fcinfo) Interval *step = PG_GETARG_INTERVAL_P(2); text *zone = (PG_NARGS() == 4) ? PG_GETARG_TEXT_PP(3) : NULL; MemoryContext oldcontext; - const Interval interval_zero = {0}; /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); @@ -5888,13 +6539,18 @@ generate_series_timestamptz_internal(FunctionCallInfo fcinfo) fctx->attimezone = zone ? lookup_timezone(zone) : session_timezone; /* Determine sign of the interval */ - fctx->step_sign = interval_cmp_internal(&fctx->step, &interval_zero); + fctx->step_sign = interval_sign(&fctx->step); if (fctx->step_sign == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("step size cannot equal zero"))); + if (INTERVAL_NOT_FINITE((&fctx->step))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("step size cannot be infinite"))); + funcctx->user_fctx = fctx; MemoryContextSwitchTo(oldcontext); } |
