summaryrefslogtreecommitdiff
path: root/src/backend/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils')
-rw-r--r--src/backend/utils/adt/date.c57
-rw-r--r--src/backend/utils/adt/datetime.c26
-rw-r--r--src/backend/utils/adt/formatting.c2
-rw-r--r--src/backend/utils/adt/selfuncs.c4
-rw-r--r--src/backend/utils/adt/timestamp.c1040
5 files changed, 926 insertions, 203 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),
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 267dfd37b2e..fca9a2a6e93 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -3271,6 +3271,9 @@ ClearPgItmIn(struct pg_itm_in *itm_in)
*
* Allow ISO-style time span, with implicit units on number of days
* preceding an hh:mm:ss field. - thomas 1998-04-30
+ *
+ * itm_in remains undefined for infinite interval values for which dtype alone
+ * suffices.
*/
int
DecodeInterval(char **field, int *ftype, int nf, int range,
@@ -3574,6 +3577,8 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
if (parsing_unit_val)
return DTERR_BAD_FORMAT;
type = DecodeUnits(i, field[i], &uval);
+ if (type == UNKNOWN_FIELD)
+ type = DecodeSpecial(i, field[i], &uval);
if (type == IGNORE_DTF)
continue;
@@ -3597,6 +3602,27 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
type = uval;
break;
+ case RESERV:
+ tmask = (DTK_DATE_M | DTK_TIME_M);
+
+ /*
+ * Only reserved words corresponding to infinite
+ * intervals are accepted.
+ */
+ if (uval != DTK_LATE && uval != DTK_EARLY)
+ return DTERR_BAD_FORMAT;
+
+ /*
+ * Infinity cannot be followed by anything else. We
+ * could allow "ago" to reverse the sign of infinity
+ * but using signed infinity is more intuitive.
+ */
+ if (i != nf - 1)
+ return DTERR_BAD_FORMAT;
+
+ *dtype = uval;
+ break;
+
default:
return DTERR_BAD_FORMAT;
}
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 8131091f794..d176723d952 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -4127,7 +4127,7 @@ interval_to_char(PG_FUNCTION_ARGS)
struct pg_itm tt,
*itm = &tt;
- if (VARSIZE_ANY_EXHDR(fmt) <= 0)
+ if (VARSIZE_ANY_EXHDR(fmt) <= 0 || INTERVAL_NOT_FINITE(it))
PG_RETURN_NULL();
ZERO_tmtc(&tmtc);
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index c4fcd0076ea..4ea5415f204 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -4802,6 +4802,10 @@ convert_timevalue_to_scalar(Datum value, Oid typid, bool *failure)
* Convert the month part of Interval to days using assumed
* average month length of 365.25/12.0 days. Not too
* accurate, but plenty good enough for our purposes.
+ *
+ * This also works for infinite intervals, which just have all
+ * fields set to INT_MIN/INT_MAX, and so will produce a result
+ * smaller/larger than any finite interval.
*/
return interval->time + interval->day * (double) USECS_PER_DAY +
interval->month * ((DAYS_PER_YEAR / (double) MONTHS_PER_YEAR) * USECS_PER_DAY);
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);
}