diff options
author | Dean Rasheed <dean.a.rasheed@gmail.com> | 2023-11-14 10:58:49 +0000 |
---|---|---|
committer | Dean Rasheed <dean.a.rasheed@gmail.com> | 2023-11-14 10:58:49 +0000 |
commit | 519fc1bd9e9d7b408903e44f55f83f6db30742b7 (patch) | |
tree | f31e231df8b28ddff5149504e7019b824191353a /src/backend/utils/adt/timestamp.c | |
parent | b41b1a7f490093643ce2080d1cf9323e24423eb8 (diff) |
Support +/- infinity in the interval data type.
This adds support for infinity to the interval data type, using the
same input/output representation as the other date/time data types
that support infinity. This allows various arithmetic operations on
infinite dates, timestamps and intervals.
The new values are represented by setting all fields of the interval
to INT32/64_MIN for -infinity, and INT32/64_MAX for +infinity. This
ensures that they compare as less/greater than all other interval
values, without the need for any special-case comparison code.
Note that, since those 2 values were formerly accepted as legal finite
intervals, pg_upgrade and dump/restore from an old database will turn
them from finite to infinite intervals. That seems OK, since those
exact values should be extremely rare in practice, and they are
outside the documented range supported by the interval type, which
gives us a certain amount of leeway.
Bump catalog version.
Joseph Koshakow, Jian He, and Ashutosh Bapat, reviewed by me.
Discussion: https://postgr.es/m/CAAvxfHea4%2BsPybKK7agDYOMo9N-Z3J6ZXf3BOM79pFsFNcRjwA%40mail.gmail.com
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); } |