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/date.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/date.c')
-rw-r--r-- | src/backend/utils/adt/date.c | 57 |
1 files changed, 47 insertions, 10 deletions
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 544e1d32bfc..13745a0adc9 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -24,6 +24,7 @@ #include "access/xact.h" #include "catalog/pg_type.h" #include "common/hashfn.h" +#include "common/int.h" #include "libpq/pqformat.h" #include "miscadmin.h" #include "nodes/supportnodes.h" @@ -2013,6 +2014,11 @@ interval_time(PG_FUNCTION_ARGS) Interval *span = PG_GETARG_INTERVAL_P(0); TimeADT result; + if (INTERVAL_NOT_FINITE(span)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("cannot convert infinite interval to time"))); + result = span->time % USECS_PER_DAY; if (result < 0) result += USECS_PER_DAY; @@ -2049,6 +2055,11 @@ time_pl_interval(PG_FUNCTION_ARGS) Interval *span = PG_GETARG_INTERVAL_P(1); TimeADT result; + if (INTERVAL_NOT_FINITE(span)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("cannot add infinite interval to time"))); + result = time + span->time; result -= result / USECS_PER_DAY * USECS_PER_DAY; if (result < INT64CONST(0)) @@ -2067,6 +2078,11 @@ time_mi_interval(PG_FUNCTION_ARGS) Interval *span = PG_GETARG_INTERVAL_P(1); TimeADT result; + if (INTERVAL_NOT_FINITE(span)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("cannot subtract infinite interval from time"))); + result = time - span->time; result -= result / USECS_PER_DAY * USECS_PER_DAY; if (result < INT64CONST(0)) @@ -2090,7 +2106,8 @@ in_range_time_interval(PG_FUNCTION_ARGS) /* * Like time_pl_interval/time_mi_interval, we disregard the month and day - * fields of the offset. So our test for negative should too. + * fields of the offset. So our test for negative should too. This also + * catches -infinity, so we only need worry about +infinity below. */ if (offset->time < 0) ereport(ERROR, @@ -2100,13 +2117,14 @@ in_range_time_interval(PG_FUNCTION_ARGS) /* * We can't use time_pl_interval/time_mi_interval here, because their * wraparound behavior would give wrong (or at least undesirable) answers. - * Fortunately the equivalent non-wrapping behavior is trivial, especially - * since we don't worry about integer overflow. + * Fortunately the equivalent non-wrapping behavior is trivial, except + * that adding an infinite (or very large) interval might cause integer + * overflow. Subtraction cannot overflow here. */ if (sub) sum = base - offset->time; - else - sum = base + offset->time; + else if (pg_add_s64_overflow(base, offset->time, &sum)) + PG_RETURN_BOOL(less); if (less) PG_RETURN_BOOL(val <= sum); @@ -2581,6 +2599,11 @@ timetz_pl_interval(PG_FUNCTION_ARGS) Interval *span = PG_GETARG_INTERVAL_P(1); TimeTzADT *result; + if (INTERVAL_NOT_FINITE(span)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("cannot add infinite interval to time"))); + result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); result->time = time->time + span->time; @@ -2603,6 +2626,11 @@ timetz_mi_interval(PG_FUNCTION_ARGS) Interval *span = PG_GETARG_INTERVAL_P(1); TimeTzADT *result; + if (INTERVAL_NOT_FINITE(span)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("cannot subtract infinite interval from time"))); + result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); result->time = time->time - span->time; @@ -2630,7 +2658,8 @@ in_range_timetz_interval(PG_FUNCTION_ARGS) /* * Like timetz_pl_interval/timetz_mi_interval, we disregard the month and - * day fields of the offset. So our test for negative should too. + * day fields of the offset. So our test for negative should too. This + * also catches -infinity, so we only need worry about +infinity below. */ if (offset->time < 0) ereport(ERROR, @@ -2640,13 +2669,14 @@ in_range_timetz_interval(PG_FUNCTION_ARGS) /* * We can't use timetz_pl_interval/timetz_mi_interval here, because their * wraparound behavior would give wrong (or at least undesirable) answers. - * Fortunately the equivalent non-wrapping behavior is trivial, especially - * since we don't worry about integer overflow. + * Fortunately the equivalent non-wrapping behavior is trivial, except + * that adding an infinite (or very large) interval might cause integer + * overflow. Subtraction cannot overflow here. */ if (sub) sum.time = base->time - offset->time; - else - sum.time = base->time + offset->time; + else if (pg_add_s64_overflow(base->time, offset->time, &sum.time)) + PG_RETURN_BOOL(less); sum.zone = base->zone; if (less) @@ -3096,6 +3126,13 @@ timetz_izone(PG_FUNCTION_ARGS) TimeTzADT *result; int tz; + 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), |