Fix overflow hazards in interval input and output conversions.

DecodeInterval (interval input) was careless about integer-overflow
hazards, allowing bogus results to be obtained for sufficiently
large input values.  Also, since it initially converted the input
to a "struct tm", it was impossible to produce the full range of
representable interval values.

Meanwhile, EncodeInterval (interval output) and a few other
functions could suffer failures if asked to process sufficiently
large interval values, because they also relied on being able to
represent an interval in "struct tm" which is not designed to
handle that.

Fix all this stuff by introducing new struct types that are more
fit for purpose.

While this is clearly a bug fix, it's also an API break for any
code that's calling these functions directly.  So back-patching
doesn't seem wise, especially in view of the lack of field
complaints.

Joe Koshakow, editorialized a bit by me

Discussion: https://postgr.es/m/CAAvxfHff0JLYHwyBrtMx_=6wr=k2Xp+D+-X3vEhHjJYMj+mQcg@mail.gmail.com
This commit is contained in:
Tom Lane 2022-04-02 16:12:26 -04:00
parent f7e4d5c64f
commit e39f990467
9 changed files with 1479 additions and 378 deletions

File diff suppressed because it is too large Load Diff

View File

@ -491,11 +491,28 @@ typedef struct
/* ----------
* Datetime to char conversion
*
* To support intervals as well as timestamps, we use a custom "tm" struct
* that is almost like struct pg_tm, but has a 64-bit tm_hour field.
* We omit the tm_isdst and tm_zone fields, which are not used here.
* ----------
*/
struct fmt_tm
{
int tm_sec;
int tm_min;
int64 tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
long int tm_gmtoff;
};
typedef struct TmToChar
{
struct pg_tm tm; /* classic 'tm' struct */
struct fmt_tm tm; /* almost the classic 'tm' struct */
fsec_t fsec; /* fractional seconds */
const char *tzn; /* timezone */
} TmToChar;
@ -504,12 +521,25 @@ typedef struct TmToChar
#define tmtcTzn(_X) ((_X)->tzn)
#define tmtcFsec(_X) ((_X)->fsec)
/* Note: this is used to copy pg_tm to fmt_tm, so not quite a bitwise copy */
#define COPY_tm(_DST, _SRC) \
do { \
(_DST)->tm_sec = (_SRC)->tm_sec; \
(_DST)->tm_min = (_SRC)->tm_min; \
(_DST)->tm_hour = (_SRC)->tm_hour; \
(_DST)->tm_mday = (_SRC)->tm_mday; \
(_DST)->tm_mon = (_SRC)->tm_mon; \
(_DST)->tm_year = (_SRC)->tm_year; \
(_DST)->tm_wday = (_SRC)->tm_wday; \
(_DST)->tm_yday = (_SRC)->tm_yday; \
(_DST)->tm_gmtoff = (_SRC)->tm_gmtoff; \
} while(0)
/* Caution: this is used to zero both pg_tm and fmt_tm structs */
#define ZERO_tm(_X) \
do { \
(_X)->tm_sec = (_X)->tm_year = (_X)->tm_min = (_X)->tm_wday = \
(_X)->tm_hour = (_X)->tm_yday = (_X)->tm_isdst = 0; \
(_X)->tm_mday = (_X)->tm_mon = 1; \
(_X)->tm_zone = NULL; \
memset(_X, 0, sizeof(*(_X))); \
(_X)->tm_mday = (_X)->tm_mon = 1; \
} while(0)
#define ZERO_tmtc(_X) \
@ -2649,7 +2679,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
{
FormatNode *n;
char *s;
struct pg_tm *tm = &in->tm;
struct fmt_tm *tm = &in->tm;
int i;
/* cache localized days and months */
@ -2698,16 +2728,17 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
* display time as shown on a 12-hour clock, even for
* intervals
*/
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : (tm->tm_hour >= 0) ? 2 : 3,
tm->tm_hour % (HOURS_PER_DAY / 2) == 0 ? HOURS_PER_DAY / 2 :
tm->tm_hour % (HOURS_PER_DAY / 2));
sprintf(s, "%0*lld", S_FM(n->suffix) ? 0 : (tm->tm_hour >= 0) ? 2 : 3,
tm->tm_hour % (HOURS_PER_DAY / 2) == 0 ?
(long long) (HOURS_PER_DAY / 2) :
(long long) (tm->tm_hour % (HOURS_PER_DAY / 2)));
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
break;
case DCH_HH24:
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : (tm->tm_hour >= 0) ? 2 : 3,
tm->tm_hour);
sprintf(s, "%0*lld", S_FM(n->suffix) ? 0 : (tm->tm_hour >= 0) ? 2 : 3,
(long long) tm->tm_hour);
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
@ -2755,9 +2786,10 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
break;
#undef DCH_to_char_fsec
case DCH_SSSS:
sprintf(s, "%d", tm->tm_hour * SECS_PER_HOUR +
tm->tm_min * SECS_PER_MINUTE +
tm->tm_sec);
sprintf(s, "%lld",
(long long) (tm->tm_hour * SECS_PER_HOUR +
tm->tm_min * SECS_PER_MINUTE +
tm->tm_sec));
if (S_THth(n->suffix))
str_numth(s, s, S_TH_TYPE(n->suffix));
s += strlen(s);
@ -4088,7 +4120,8 @@ timestamp_to_char(PG_FUNCTION_ARGS)
text *fmt = PG_GETARG_TEXT_PP(1),
*res;
TmToChar tmtc;
struct pg_tm *tm;
struct pg_tm tt;
struct fmt_tm *tm;
int thisdate;
if (VARSIZE_ANY_EXHDR(fmt) <= 0 || TIMESTAMP_NOT_FINITE(dt))
@ -4097,10 +4130,11 @@ timestamp_to_char(PG_FUNCTION_ARGS)
ZERO_tmtc(&tmtc);
tm = tmtcTm(&tmtc);
if (timestamp2tm(dt, NULL, tm, &tmtcFsec(&tmtc), NULL, NULL) != 0)
if (timestamp2tm(dt, NULL, &tt, &tmtcFsec(&tmtc), NULL, NULL) != 0)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
COPY_tm(tm, &tt);
thisdate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
tm->tm_wday = (thisdate + 1) % 7;
@ -4120,7 +4154,8 @@ timestamptz_to_char(PG_FUNCTION_ARGS)
*res;
TmToChar tmtc;
int tz;
struct pg_tm *tm;
struct pg_tm tt;
struct fmt_tm *tm;
int thisdate;
if (VARSIZE_ANY_EXHDR(fmt) <= 0 || TIMESTAMP_NOT_FINITE(dt))
@ -4129,10 +4164,11 @@ timestamptz_to_char(PG_FUNCTION_ARGS)
ZERO_tmtc(&tmtc);
tm = tmtcTm(&tmtc);
if (timestamp2tm(dt, &tz, tm, &tmtcFsec(&tmtc), &tmtcTzn(&tmtc), NULL) != 0)
if (timestamp2tm(dt, &tz, &tt, &tmtcFsec(&tmtc), &tmtcTzn(&tmtc), NULL) != 0)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
COPY_tm(tm, &tt);
thisdate = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
tm->tm_wday = (thisdate + 1) % 7;
@ -4156,7 +4192,9 @@ interval_to_char(PG_FUNCTION_ARGS)
text *fmt = PG_GETARG_TEXT_PP(1),
*res;
TmToChar tmtc;
struct pg_tm *tm;
struct fmt_tm *tm;
struct pg_itm tt,
*itm = &tt;
if (VARSIZE_ANY_EXHDR(fmt) <= 0)
PG_RETURN_NULL();
@ -4164,8 +4202,14 @@ interval_to_char(PG_FUNCTION_ARGS)
ZERO_tmtc(&tmtc);
tm = tmtcTm(&tmtc);
if (interval2tm(*it, tm, &tmtcFsec(&tmtc)) != 0)
PG_RETURN_NULL();
interval2itm(*it, itm);
tmtc.fsec = itm->tm_usec;
tm->tm_sec = itm->tm_sec;
tm->tm_min = itm->tm_min;
tm->tm_hour = itm->tm_hour;
tm->tm_mday = itm->tm_mday;
tm->tm_mon = itm->tm_mon;
tm->tm_year = itm->tm_year;
/* wday is meaningless, yday approximates the total span in days */
tm->tm_yday = (tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon) * DAYS_PER_MONTH + tm->tm_mday;

View File

@ -889,9 +889,8 @@ interval_in(PG_FUNCTION_ARGS)
#endif
int32 typmod = PG_GETARG_INT32(2);
Interval *result;
fsec_t fsec;
struct pg_tm tt,
*tm = &tt;
struct pg_itm_in tt,
*itm_in = &tt;
int dtype;
int nf;
int range;
@ -900,13 +899,10 @@ interval_in(PG_FUNCTION_ARGS)
int ftype[MAXDATEFIELDS];
char workbuf[256];
tm->tm_year = 0;
tm->tm_mon = 0;
tm->tm_mday = 0;
tm->tm_hour = 0;
tm->tm_min = 0;
tm->tm_sec = 0;
fsec = 0;
itm_in->tm_year = 0;
itm_in->tm_mon = 0;
itm_in->tm_mday = 0;
itm_in->tm_usec = 0;
if (typmod >= 0)
range = INTERVAL_RANGE(typmod);
@ -917,12 +913,12 @@ interval_in(PG_FUNCTION_ARGS)
ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeInterval(field, ftype, nf, range,
&dtype, tm, &fsec);
&dtype, itm_in);
/* if those functions think it's a bad format, try ISO8601 style */
if (dterr == DTERR_BAD_FORMAT)
dterr = DecodeISO8601Interval(str,
&dtype, tm, &fsec);
&dtype, itm_in);
if (dterr != 0)
{
@ -936,7 +932,7 @@ interval_in(PG_FUNCTION_ARGS)
switch (dtype)
{
case DTK_DELTA:
if (tm2interval(tm, fsec, result) != 0)
if (itmin2interval(itm_in, result) != 0)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("interval out of range")));
@ -960,15 +956,12 @@ interval_out(PG_FUNCTION_ARGS)
{
Interval *span = PG_GETARG_INTERVAL_P(0);
char *result;
struct pg_tm tt,
*tm = &tt;
fsec_t fsec;
struct pg_itm tt,
*itm = &tt;
char buf[MAXDATELEN + 1];
if (interval2tm(*span, tm, &fsec) != 0)
elog(ERROR, "could not convert interval to tm");
EncodeInterval(tm, fsec, IntervalStyle, buf);
interval2itm(*span, itm);
EncodeInterval(itm, IntervalStyle, buf);
result = pstrdup(buf);
PG_RETURN_CSTRING(result);
@ -1960,50 +1953,77 @@ tm2timestamp(struct pg_tm *tm, fsec_t fsec, int *tzp, Timestamp *result)
}
/* interval2tm()
* Convert an interval data type to a tm structure.
/* interval2itm()
* Convert an Interval to a pg_itm structure.
* Note: overflow is not possible, because the pg_itm fields are
* wide enough for all possible conversion results.
*/
int
interval2tm(Interval span, struct pg_tm *tm, fsec_t *fsec)
void
interval2itm(Interval span, struct pg_itm *itm)
{
TimeOffset time;
TimeOffset tfrac;
tm->tm_year = span.month / MONTHS_PER_YEAR;
tm->tm_mon = span.month % MONTHS_PER_YEAR;
tm->tm_mday = span.day;
itm->tm_year = span.month / MONTHS_PER_YEAR;
itm->tm_mon = span.month % MONTHS_PER_YEAR;
itm->tm_mday = span.day;
time = span.time;
tfrac = time / USECS_PER_HOUR;
time -= tfrac * USECS_PER_HOUR;
tm->tm_hour = tfrac;
if (!SAMESIGN(tm->tm_hour, tfrac))
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("interval out of range")));
itm->tm_hour = tfrac;
tfrac = time / USECS_PER_MINUTE;
time -= tfrac * USECS_PER_MINUTE;
tm->tm_min = tfrac;
itm->tm_min = (int) tfrac;
tfrac = time / USECS_PER_SEC;
*fsec = time - (tfrac * USECS_PER_SEC);
tm->tm_sec = tfrac;
return 0;
time -= tfrac * USECS_PER_SEC;
itm->tm_sec = (int) tfrac;
itm->tm_usec = (int) time;
}
/* itm2interval()
* Convert a pg_itm structure to an Interval.
* Returns 0 if OK, -1 on overflow.
*/
int
tm2interval(struct pg_tm *tm, fsec_t fsec, Interval *span)
itm2interval(struct pg_itm *itm, Interval *span)
{
double total_months = (double) tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
int64 total_months = (int64) itm->tm_year * MONTHS_PER_YEAR + itm->tm_mon;
if (total_months > INT_MAX || total_months < INT_MIN)
return -1;
span->month = total_months;
span->day = tm->tm_mday;
span->time = (((((tm->tm_hour * INT64CONST(60)) +
tm->tm_min) * INT64CONST(60)) +
tm->tm_sec) * USECS_PER_SEC) + fsec;
span->month = (int32) total_months;
span->day = itm->tm_mday;
if (pg_mul_s64_overflow(itm->tm_hour, USECS_PER_HOUR,
&span->time))
return -1;
/* tm_min, tm_sec are 32 bits, so intermediate products can't overflow */
if (pg_add_s64_overflow(span->time, itm->tm_min * USECS_PER_MINUTE,
&span->time))
return -1;
if (pg_add_s64_overflow(span->time, itm->tm_sec * USECS_PER_SEC,
&span->time))
return -1;
if (pg_add_s64_overflow(span->time, itm->tm_usec,
&span->time))
return -1;
return 0;
}
/* itmin2interval()
* Convert a pg_itm_in structure to an Interval.
* Returns 0 if OK, -1 on overflow.
*/
int
itmin2interval(struct pg_itm_in *itm_in, Interval *span)
{
int64 total_months = (int64) itm_in->tm_year * MONTHS_PER_YEAR + itm_in->tm_mon;
if (total_months > INT_MAX || total_months < INT_MIN)
return -1;
span->month = (int32) total_months;
span->day = itm_in->tm_mday;
span->time = itm_in->tm_usec;
return 0;
}
@ -3612,10 +3632,9 @@ timestamp_age(PG_FUNCTION_ARGS)
Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
Interval *result;
fsec_t fsec,
fsec1,
fsec_t fsec1,
fsec2;
struct pg_tm tt,
struct pg_itm tt,
*tm = &tt;
struct pg_tm tt1,
*tm1 = &tt1;
@ -3628,7 +3647,7 @@ timestamp_age(PG_FUNCTION_ARGS)
timestamp2tm(dt2, NULL, tm2, &fsec2, NULL, NULL) == 0)
{
/* form the symbolic difference */
fsec = fsec1 - fsec2;
tm->tm_usec = fsec1 - fsec2;
tm->tm_sec = tm1->tm_sec - tm2->tm_sec;
tm->tm_min = tm1->tm_min - tm2->tm_min;
tm->tm_hour = tm1->tm_hour - tm2->tm_hour;
@ -3639,7 +3658,7 @@ timestamp_age(PG_FUNCTION_ARGS)
/* flip sign if necessary... */
if (dt1 < dt2)
{
fsec = -fsec;
tm->tm_usec = -tm->tm_usec;
tm->tm_sec = -tm->tm_sec;
tm->tm_min = -tm->tm_min;
tm->tm_hour = -tm->tm_hour;
@ -3649,9 +3668,9 @@ timestamp_age(PG_FUNCTION_ARGS)
}
/* propagate any negative fields into the next higher field */
while (fsec < 0)
while (tm->tm_usec < 0)
{
fsec += USECS_PER_SEC;
tm->tm_usec += USECS_PER_SEC;
tm->tm_sec--;
}
@ -3696,7 +3715,7 @@ timestamp_age(PG_FUNCTION_ARGS)
/* recover sign if necessary... */
if (dt1 < dt2)
{
fsec = -fsec;
tm->tm_usec = -tm->tm_usec;
tm->tm_sec = -tm->tm_sec;
tm->tm_min = -tm->tm_min;
tm->tm_hour = -tm->tm_hour;
@ -3705,7 +3724,7 @@ timestamp_age(PG_FUNCTION_ARGS)
tm->tm_year = -tm->tm_year;
}
if (tm2interval(tm, fsec, result) != 0)
if (itm2interval(tm, result) != 0)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("interval out of range")));
@ -3731,10 +3750,9 @@ timestamptz_age(PG_FUNCTION_ARGS)
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
Interval *result;
fsec_t fsec,
fsec1,
fsec_t fsec1,
fsec2;
struct pg_tm tt,
struct pg_itm tt,
*tm = &tt;
struct pg_tm tt1,
*tm1 = &tt1;
@ -3749,7 +3767,7 @@ timestamptz_age(PG_FUNCTION_ARGS)
timestamp2tm(dt2, &tz2, tm2, &fsec2, NULL, NULL) == 0)
{
/* form the symbolic difference */
fsec = fsec1 - fsec2;
tm->tm_usec = fsec1 - fsec2;
tm->tm_sec = tm1->tm_sec - tm2->tm_sec;
tm->tm_min = tm1->tm_min - tm2->tm_min;
tm->tm_hour = tm1->tm_hour - tm2->tm_hour;
@ -3760,7 +3778,7 @@ timestamptz_age(PG_FUNCTION_ARGS)
/* flip sign if necessary... */
if (dt1 < dt2)
{
fsec = -fsec;
tm->tm_usec = -tm->tm_usec;
tm->tm_sec = -tm->tm_sec;
tm->tm_min = -tm->tm_min;
tm->tm_hour = -tm->tm_hour;
@ -3770,9 +3788,9 @@ timestamptz_age(PG_FUNCTION_ARGS)
}
/* propagate any negative fields into the next higher field */
while (fsec < 0)
while (tm->tm_usec < 0)
{
fsec += USECS_PER_SEC;
tm->tm_usec += USECS_PER_SEC;
tm->tm_sec--;
}
@ -3821,7 +3839,7 @@ timestamptz_age(PG_FUNCTION_ARGS)
/* recover sign if necessary... */
if (dt1 < dt2)
{
fsec = -fsec;
tm->tm_usec = -tm->tm_usec;
tm->tm_sec = -tm->tm_sec;
tm->tm_min = -tm->tm_min;
tm->tm_hour = -tm->tm_hour;
@ -3830,7 +3848,7 @@ timestamptz_age(PG_FUNCTION_ARGS)
tm->tm_year = -tm->tm_year;
}
if (tm2interval(tm, fsec, result) != 0)
if (itm2interval(tm, result) != 0)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("interval out of range")));
@ -4317,8 +4335,7 @@ interval_trunc(PG_FUNCTION_ARGS)
int type,
val;
char *lowunits;
fsec_t fsec;
struct pg_tm tt,
struct pg_itm tt,
*tm = &tt;
result = (Interval *) palloc(sizeof(Interval));
@ -4331,8 +4348,7 @@ interval_trunc(PG_FUNCTION_ARGS)
if (type == UNITS)
{
if (interval2tm(*interval, tm, &fsec) == 0)
{
interval2itm(*interval, tm);
switch (val)
{
case DTK_MILLENNIUM:
@ -4366,10 +4382,10 @@ interval_trunc(PG_FUNCTION_ARGS)
tm->tm_sec = 0;
/* FALL THRU */
case DTK_SECOND:
fsec = 0;
tm->tm_usec = 0;
break;
case DTK_MILLISEC:
fsec = (fsec / 1000) * 1000;
tm->tm_usec = (tm->tm_usec / 1000) * 1000;
break;
case DTK_MICROSEC:
break;
@ -4382,13 +4398,10 @@ interval_trunc(PG_FUNCTION_ARGS)
(val == DTK_WEEK) ? errdetail("Months usually have fractional weeks.") : 0));
}
if (tm2interval(tm, fsec, result) != 0)
if (itm2interval(tm, result) != 0)
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("interval out of range")));
}
else
elog(ERROR, "could not convert interval to tm");
}
else
{
@ -5200,8 +5213,7 @@ interval_part_common(PG_FUNCTION_ARGS, bool retnumeric)
int type,
val;
char *lowunits;
fsec_t fsec;
struct pg_tm tt,
struct pg_itm tt,
*tm = &tt;
lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
@ -5214,12 +5226,11 @@ interval_part_common(PG_FUNCTION_ARGS, bool retnumeric)
if (type == UNITS)
{
if (interval2tm(*interval, tm, &fsec) == 0)
{
interval2itm(*interval, tm);
switch (val)
{
case DTK_MICROSEC:
intresult = tm->tm_sec * INT64CONST(1000000) + fsec;
intresult = tm->tm_sec * INT64CONST(1000000) + tm->tm_usec;
break;
case DTK_MILLISEC:
@ -5228,9 +5239,9 @@ interval_part_common(PG_FUNCTION_ARGS, bool retnumeric)
* tm->tm_sec * 1000 + fsec / 1000
* = (tm->tm_sec * 1'000'000 + fsec) / 1000
*/
PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + fsec, 3));
PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + tm->tm_usec, 3));
else
PG_RETURN_FLOAT8(tm->tm_sec * 1000.0 + fsec / 1000.0);
PG_RETURN_FLOAT8(tm->tm_sec * 1000.0 + tm->tm_usec / 1000.0);
break;
case DTK_SECOND:
@ -5239,9 +5250,9 @@ interval_part_common(PG_FUNCTION_ARGS, bool retnumeric)
* tm->tm_sec + fsec / 1'000'000
* = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000
*/
PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + fsec, 6));
PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + tm->tm_usec, 6));
else
PG_RETURN_FLOAT8(tm->tm_sec + fsec / 1000000.0);
PG_RETURN_FLOAT8(tm->tm_sec + tm->tm_usec / 1000000.0);
break;
case DTK_MINUTE:
@ -5290,12 +5301,6 @@ interval_part_common(PG_FUNCTION_ARGS, bool retnumeric)
lowunits, format_type_be(INTERVALOID))));
intresult = 0;
}
}
else
{
elog(ERROR, "could not convert interval to tm");
intresult = 0;
}
}
else if (type == RESERV && val == DTK_EPOCH)
{

View File

@ -40,6 +40,10 @@ typedef int64 TimestampTz;
typedef int64 TimeOffset;
typedef int32 fsec_t; /* fractional seconds (in microseconds) */
/*
* Storage format for type interval.
*/
typedef struct
{
TimeOffset time; /* all time units other than days, months and
@ -48,6 +52,41 @@ typedef struct
int32 month; /* months and years, after time for alignment */
} Interval;
/*
* Data structure representing a broken-down interval.
*
* For historical reasons, this is modeled on struct pg_tm for timestamps.
* Unlike the situation for timestamps, there's no magic interpretation
* needed for months or years: they're just zero or not. Note that fields
* can be negative; however, because of the divisions done while converting
* from struct Interval, only tm_mday could be INT_MIN. This is important
* because we may need to negate the values in some code paths.
*/
struct pg_itm
{
int tm_usec;
int tm_sec;
int tm_min;
int64 tm_hour; /* needs to be wide */
int tm_mday;
int tm_mon;
int tm_year;
};
/*
* Data structure for decoding intervals. We could just use struct pg_itm,
* but then the requirement for tm_usec to be 64 bits would propagate to
* places where it's not really needed. Also, omitting the fields that
* aren't used during decoding seems like a good error-prevention measure.
*/
struct pg_itm_in
{
int64 tm_usec; /* needs to be wide */
int tm_mday;
int tm_mon;
int tm_year;
};
/* Limits on the "precision" option (typmod) for these data types */
#define MAX_TIMESTAMP_PRECISION 6

View File

@ -23,6 +23,8 @@
typedef int64 pg_time_t;
/*
* Data structure representing a broken-down timestamp.
*
* CAUTION: the IANA timezone library (src/timezone/) follows the POSIX
* convention that tm_mon counts from 0 and tm_year is relative to 1900.
* However, Postgres' datetime functions generally treat tm_mon as counting
@ -44,6 +46,7 @@ struct pg_tm
const char *tm_zone;
};
/* These structs are opaque outside the timezone library */
typedef struct pg_tz pg_tz;
typedef struct pg_tzenum pg_tzenum;

View File

@ -300,9 +300,9 @@ extern int DecodeTimeOnly(char **field, int *ftype,
int nf, int *dtype,
struct pg_tm *tm, fsec_t *fsec, int *tzp);
extern int DecodeInterval(char **field, int *ftype, int nf, int range,
int *dtype, struct pg_tm *tm, fsec_t *fsec);
int *dtype, struct pg_itm_in *itm_in);
extern int DecodeISO8601Interval(char *str,
int *dtype, struct pg_tm *tm, fsec_t *fsec);
int *dtype, struct pg_itm_in *itm_in);
extern void DateTimeParseError(int dterr, const char *str,
const char *datatype) pg_attribute_noreturn();
@ -315,7 +315,7 @@ extern int DetermineTimeZoneAbbrevOffsetTS(TimestampTz ts, const char *abbr,
extern void EncodeDateOnly(struct pg_tm *tm, int style, char *str);
extern void EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, int style, char *str);
extern void EncodeDateTime(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str);
extern void EncodeInterval(struct pg_tm *tm, fsec_t fsec, int style, char *str);
extern void EncodeInterval(struct pg_itm *itm, int style, char *str);
extern void EncodeSpecialTimestamp(Timestamp dt, char *str);
extern int ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc,

View File

@ -88,8 +88,9 @@ extern int timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm,
fsec_t *fsec, const char **tzn, pg_tz *attimezone);
extern void dt2time(Timestamp dt, int *hour, int *min, int *sec, fsec_t *fsec);
extern int interval2tm(Interval span, struct pg_tm *tm, fsec_t *fsec);
extern int tm2interval(struct pg_tm *tm, fsec_t fsec, Interval *span);
extern void interval2itm(Interval span, struct pg_itm *itm);
extern int itm2interval(struct pg_itm *itm, Interval *span);
extern int itmin2interval(struct pg_itm_in *itm_in, Interval *span);
extern Timestamp SetEpochTimestamp(void);
extern void GetEpochTime(struct pg_tm *tm);

View File

@ -928,6 +928,617 @@ select interval '0:0:0.7', interval '@ 0.70 secs', interval '0.7 seconds';
@ 0.7 secs | @ 0.7 secs | @ 0.7 secs
(1 row)
-- test time fields using entire 64 bit microseconds range
select interval '2562047788.01521550194 hours';
interval
-----------------------------------
@ 2562047788 hours 54.775807 secs
(1 row)
select interval '-2562047788.01521550222 hours';
interval
---------------------------------------
@ 2562047788 hours 54.775808 secs ago
(1 row)
select interval '153722867280.912930117 minutes';
interval
-----------------------------------
@ 2562047788 hours 54.775807 secs
(1 row)
select interval '-153722867280.912930133 minutes';
interval
---------------------------------------
@ 2562047788 hours 54.775808 secs ago
(1 row)
select interval '9223372036854.775807 seconds';
interval
-----------------------------------
@ 2562047788 hours 54.775807 secs
(1 row)
select interval '-9223372036854.775808 seconds';
interval
---------------------------------------
@ 2562047788 hours 54.775808 secs ago
(1 row)
select interval '9223372036854775.807 milliseconds';
interval
-----------------------------------
@ 2562047788 hours 54.775807 secs
(1 row)
select interval '-9223372036854775.808 milliseconds';
interval
---------------------------------------
@ 2562047788 hours 54.775808 secs ago
(1 row)
select interval '9223372036854775807 microseconds';
interval
-----------------------------------
@ 2562047788 hours 54.775807 secs
(1 row)
select interval '-9223372036854775808 microseconds';
interval
---------------------------------------
@ 2562047788 hours 54.775808 secs ago
(1 row)
select interval 'PT2562047788H54.775807S';
interval
-----------------------------------
@ 2562047788 hours 54.775807 secs
(1 row)
select interval 'PT-2562047788H-54.775808S';
interval
---------------------------------------
@ 2562047788 hours 54.775808 secs ago
(1 row)
select interval 'PT2562047788:00:54.775807';
interval
-----------------------------------
@ 2562047788 hours 54.775807 secs
(1 row)
select interval 'PT2562047788.0152155019444';
interval
-----------------------------------
@ 2562047788 hours 54.775807 secs
(1 row)
select interval 'PT-2562047788.0152155022222';
interval
---------------------------------------
@ 2562047788 hours 54.775808 secs ago
(1 row)
-- overflow each date/time field
select interval '2147483648 years';
ERROR: interval field value out of range: "2147483648 years"
LINE 1: select interval '2147483648 years';
^
select interval '-2147483649 years';
ERROR: interval field value out of range: "-2147483649 years"
LINE 1: select interval '-2147483649 years';
^
select interval '2147483648 months';
ERROR: interval field value out of range: "2147483648 months"
LINE 1: select interval '2147483648 months';
^
select interval '-2147483649 months';
ERROR: interval field value out of range: "-2147483649 months"
LINE 1: select interval '-2147483649 months';
^
select interval '2147483648 days';
ERROR: interval field value out of range: "2147483648 days"
LINE 1: select interval '2147483648 days';
^
select interval '-2147483649 days';
ERROR: interval field value out of range: "-2147483649 days"
LINE 1: select interval '-2147483649 days';
^
select interval '2562047789 hours';
ERROR: interval field value out of range: "2562047789 hours"
LINE 1: select interval '2562047789 hours';
^
select interval '-2562047789 hours';
ERROR: interval field value out of range: "-2562047789 hours"
LINE 1: select interval '-2562047789 hours';
^
select interval '153722867281 minutes';
ERROR: interval field value out of range: "153722867281 minutes"
LINE 1: select interval '153722867281 minutes';
^
select interval '-153722867281 minutes';
ERROR: interval field value out of range: "-153722867281 minutes"
LINE 1: select interval '-153722867281 minutes';
^
select interval '9223372036855 seconds';
ERROR: interval field value out of range: "9223372036855 seconds"
LINE 1: select interval '9223372036855 seconds';
^
select interval '-9223372036855 seconds';
ERROR: interval field value out of range: "-9223372036855 seconds"
LINE 1: select interval '-9223372036855 seconds';
^
select interval '9223372036854777 millisecond';
ERROR: interval field value out of range: "9223372036854777 millisecond"
LINE 1: select interval '9223372036854777 millisecond';
^
select interval '-9223372036854777 millisecond';
ERROR: interval field value out of range: "-9223372036854777 millisecond"
LINE 1: select interval '-9223372036854777 millisecond';
^
select interval '9223372036854775808 microsecond';
ERROR: interval field value out of range: "9223372036854775808 microsecond"
LINE 1: select interval '9223372036854775808 microsecond';
^
select interval '-9223372036854775809 microsecond';
ERROR: interval field value out of range: "-9223372036854775809 microsecond"
LINE 1: select interval '-9223372036854775809 microsecond';
^
select interval 'P2147483648';
ERROR: interval field value out of range: "P2147483648"
LINE 1: select interval 'P2147483648';
^
select interval 'P-2147483649';
ERROR: interval field value out of range: "P-2147483649"
LINE 1: select interval 'P-2147483649';
^
select interval 'P1-2147483647-2147483647';
ERROR: interval out of range
LINE 1: select interval 'P1-2147483647-2147483647';
^
select interval 'PT2562047789';
ERROR: interval field value out of range: "PT2562047789"
LINE 1: select interval 'PT2562047789';
^
select interval 'PT-2562047789';
ERROR: interval field value out of range: "PT-2562047789"
LINE 1: select interval 'PT-2562047789';
^
-- overflow with date/time unit aliases
select interval '2147483647 weeks';
ERROR: interval field value out of range: "2147483647 weeks"
LINE 1: select interval '2147483647 weeks';
^
select interval '-2147483648 weeks';
ERROR: interval field value out of range: "-2147483648 weeks"
LINE 1: select interval '-2147483648 weeks';
^
select interval '2147483647 decades';
ERROR: interval field value out of range: "2147483647 decades"
LINE 1: select interval '2147483647 decades';
^
select interval '-2147483648 decades';
ERROR: interval field value out of range: "-2147483648 decades"
LINE 1: select interval '-2147483648 decades';
^
select interval '2147483647 centuries';
ERROR: interval field value out of range: "2147483647 centuries"
LINE 1: select interval '2147483647 centuries';
^
select interval '-2147483648 centuries';
ERROR: interval field value out of range: "-2147483648 centuries"
LINE 1: select interval '-2147483648 centuries';
^
select interval '2147483647 millennium';
ERROR: interval field value out of range: "2147483647 millennium"
LINE 1: select interval '2147483647 millennium';
^
select interval '-2147483648 millennium';
ERROR: interval field value out of range: "-2147483648 millennium"
LINE 1: select interval '-2147483648 millennium';
^
select interval '1 week 2147483647 days';
ERROR: interval field value out of range: "1 week 2147483647 days"
LINE 1: select interval '1 week 2147483647 days';
^
select interval '-1 week -2147483648 days';
ERROR: interval field value out of range: "-1 week -2147483648 days"
LINE 1: select interval '-1 week -2147483648 days';
^
select interval '2147483647 days 1 week';
ERROR: interval field value out of range: "2147483647 days 1 week"
LINE 1: select interval '2147483647 days 1 week';
^
select interval '-2147483648 days -1 week';
ERROR: interval field value out of range: "-2147483648 days -1 week"
LINE 1: select interval '-2147483648 days -1 week';
^
select interval 'P1W2147483647D';
ERROR: interval field value out of range: "P1W2147483647D"
LINE 1: select interval 'P1W2147483647D';
^
select interval 'P-1W-2147483648D';
ERROR: interval field value out of range: "P-1W-2147483648D"
LINE 1: select interval 'P-1W-2147483648D';
^
select interval 'P2147483647D1W';
ERROR: interval field value out of range: "P2147483647D1W"
LINE 1: select interval 'P2147483647D1W';
^
select interval 'P-2147483648D-1W';
ERROR: interval field value out of range: "P-2147483648D-1W"
LINE 1: select interval 'P-2147483648D-1W';
^
select interval '1 decade 2147483647 years';
ERROR: interval field value out of range: "1 decade 2147483647 years"
LINE 1: select interval '1 decade 2147483647 years';
^
select interval '1 century 2147483647 years';
ERROR: interval field value out of range: "1 century 2147483647 years"
LINE 1: select interval '1 century 2147483647 years';
^
select interval '1 millennium 2147483647 years';
ERROR: interval field value out of range: "1 millennium 2147483647 years"
LINE 1: select interval '1 millennium 2147483647 years';
^
select interval '-1 decade -2147483648 years';
ERROR: interval field value out of range: "-1 decade -2147483648 years"
LINE 1: select interval '-1 decade -2147483648 years';
^
select interval '-1 century -2147483648 years';
ERROR: interval field value out of range: "-1 century -2147483648 years"
LINE 1: select interval '-1 century -2147483648 years';
^
select interval '-1 millennium -2147483648 years';
ERROR: interval field value out of range: "-1 millennium -2147483648 years"
LINE 1: select interval '-1 millennium -2147483648 years';
^
select interval '2147483647 years 1 decade';
ERROR: interval field value out of range: "2147483647 years 1 decade"
LINE 1: select interval '2147483647 years 1 decade';
^
select interval '2147483647 years 1 century';
ERROR: interval field value out of range: "2147483647 years 1 century"
LINE 1: select interval '2147483647 years 1 century';
^
select interval '2147483647 years 1 millennium';
ERROR: interval field value out of range: "2147483647 years 1 millennium"
LINE 1: select interval '2147483647 years 1 millennium';
^
select interval '-2147483648 years -1 decade';
ERROR: interval field value out of range: "-2147483648 years -1 decade"
LINE 1: select interval '-2147483648 years -1 decade';
^
select interval '-2147483648 years -1 century';
ERROR: interval field value out of range: "-2147483648 years -1 century"
LINE 1: select interval '-2147483648 years -1 century';
^
select interval '-2147483648 years -1 millennium';
ERROR: interval field value out of range: "-2147483648 years -1 millennium"
LINE 1: select interval '-2147483648 years -1 millennium';
^
-- overflowing with fractional fields - postgres format
select interval '0.1 millennium 2147483647 months';
ERROR: interval field value out of range: "0.1 millennium 2147483647 months"
LINE 1: select interval '0.1 millennium 2147483647 months';
^
select interval '0.1 centuries 2147483647 months';
ERROR: interval field value out of range: "0.1 centuries 2147483647 months"
LINE 1: select interval '0.1 centuries 2147483647 months';
^
select interval '0.1 decades 2147483647 months';
ERROR: interval field value out of range: "0.1 decades 2147483647 months"
LINE 1: select interval '0.1 decades 2147483647 months';
^
select interval '0.1 yrs 2147483647 months';
ERROR: interval field value out of range: "0.1 yrs 2147483647 months"
LINE 1: select interval '0.1 yrs 2147483647 months';
^
select interval '-0.1 millennium -2147483648 months';
ERROR: interval field value out of range: "-0.1 millennium -2147483648 months"
LINE 1: select interval '-0.1 millennium -2147483648 months';
^
select interval '-0.1 centuries -2147483648 months';
ERROR: interval field value out of range: "-0.1 centuries -2147483648 months"
LINE 1: select interval '-0.1 centuries -2147483648 months';
^
select interval '-0.1 decades -2147483648 months';
ERROR: interval field value out of range: "-0.1 decades -2147483648 months"
LINE 1: select interval '-0.1 decades -2147483648 months';
^
select interval '-0.1 yrs -2147483648 months';
ERROR: interval field value out of range: "-0.1 yrs -2147483648 months"
LINE 1: select interval '-0.1 yrs -2147483648 months';
^
select interval '2147483647 months 0.1 millennium';
ERROR: interval field value out of range: "2147483647 months 0.1 millennium"
LINE 1: select interval '2147483647 months 0.1 millennium';
^
select interval '2147483647 months 0.1 centuries';
ERROR: interval field value out of range: "2147483647 months 0.1 centuries"
LINE 1: select interval '2147483647 months 0.1 centuries';
^
select interval '2147483647 months 0.1 decades';
ERROR: interval field value out of range: "2147483647 months 0.1 decades"
LINE 1: select interval '2147483647 months 0.1 decades';
^
select interval '2147483647 months 0.1 yrs';
ERROR: interval field value out of range: "2147483647 months 0.1 yrs"
LINE 1: select interval '2147483647 months 0.1 yrs';
^
select interval '-2147483648 months -0.1 millennium';
ERROR: interval field value out of range: "-2147483648 months -0.1 millennium"
LINE 1: select interval '-2147483648 months -0.1 millennium';
^
select interval '-2147483648 months -0.1 centuries';
ERROR: interval field value out of range: "-2147483648 months -0.1 centuries"
LINE 1: select interval '-2147483648 months -0.1 centuries';
^
select interval '-2147483648 months -0.1 decades';
ERROR: interval field value out of range: "-2147483648 months -0.1 decades"
LINE 1: select interval '-2147483648 months -0.1 decades';
^
select interval '-2147483648 months -0.1 yrs';
ERROR: interval field value out of range: "-2147483648 months -0.1 yrs"
LINE 1: select interval '-2147483648 months -0.1 yrs';
^
select interval '0.1 months 2147483647 days';
ERROR: interval field value out of range: "0.1 months 2147483647 days"
LINE 1: select interval '0.1 months 2147483647 days';
^
select interval '-0.1 months -2147483648 days';
ERROR: interval field value out of range: "-0.1 months -2147483648 days"
LINE 1: select interval '-0.1 months -2147483648 days';
^
select interval '2147483647 days 0.1 months';
ERROR: interval field value out of range: "2147483647 days 0.1 months"
LINE 1: select interval '2147483647 days 0.1 months';
^
select interval '-2147483648 days -0.1 months';
ERROR: interval field value out of range: "-2147483648 days -0.1 months"
LINE 1: select interval '-2147483648 days -0.1 months';
^
select interval '0.5 weeks 2147483647 days';
ERROR: interval field value out of range: "0.5 weeks 2147483647 days"
LINE 1: select interval '0.5 weeks 2147483647 days';
^
select interval '-0.5 weeks -2147483648 days';
ERROR: interval field value out of range: "-0.5 weeks -2147483648 days"
LINE 1: select interval '-0.5 weeks -2147483648 days';
^
select interval '2147483647 days 0.5 weeks';
ERROR: interval field value out of range: "2147483647 days 0.5 weeks"
LINE 1: select interval '2147483647 days 0.5 weeks';
^
select interval '-2147483648 days -0.5 weeks';
ERROR: interval field value out of range: "-2147483648 days -0.5 weeks"
LINE 1: select interval '-2147483648 days -0.5 weeks';
^
select interval '0.01 months 9223372036854775807 microseconds';
ERROR: interval field value out of range: "0.01 months 9223372036854775807 microseconds"
LINE 1: select interval '0.01 months 9223372036854775807 microsecond...
^
select interval '-0.01 months -9223372036854775808 microseconds';
ERROR: interval field value out of range: "-0.01 months -9223372036854775808 microseconds"
LINE 1: select interval '-0.01 months -9223372036854775808 microseco...
^
select interval '9223372036854775807 microseconds 0.01 months';
ERROR: interval field value out of range: "9223372036854775807 microseconds 0.01 months"
LINE 1: select interval '9223372036854775807 microseconds 0.01 month...
^
select interval '-9223372036854775808 microseconds -0.01 months';
ERROR: interval field value out of range: "-9223372036854775808 microseconds -0.01 months"
LINE 1: select interval '-9223372036854775808 microseconds -0.01 mon...
^
select interval '0.1 weeks 9223372036854775807 microseconds';
ERROR: interval field value out of range: "0.1 weeks 9223372036854775807 microseconds"
LINE 1: select interval '0.1 weeks 9223372036854775807 microseconds'...
^
select interval '-0.1 weeks -9223372036854775808 microseconds';
ERROR: interval field value out of range: "-0.1 weeks -9223372036854775808 microseconds"
LINE 1: select interval '-0.1 weeks -9223372036854775808 microsecond...
^
select interval '9223372036854775807 microseconds 0.1 weeks';
ERROR: interval field value out of range: "9223372036854775807 microseconds 0.1 weeks"
LINE 1: select interval '9223372036854775807 microseconds 0.1 weeks'...
^
select interval '-9223372036854775808 microseconds -0.1 weeks';
ERROR: interval field value out of range: "-9223372036854775808 microseconds -0.1 weeks"
LINE 1: select interval '-9223372036854775808 microseconds -0.1 week...
^
select interval '0.1 days 9223372036854775807 microseconds';
ERROR: interval field value out of range: "0.1 days 9223372036854775807 microseconds"
LINE 1: select interval '0.1 days 9223372036854775807 microseconds';
^
select interval '-0.1 days -9223372036854775808 microseconds';
ERROR: interval field value out of range: "-0.1 days -9223372036854775808 microseconds"
LINE 1: select interval '-0.1 days -9223372036854775808 microseconds...
^
select interval '9223372036854775807 microseconds 0.1 days';
ERROR: interval field value out of range: "9223372036854775807 microseconds 0.1 days"
LINE 1: select interval '9223372036854775807 microseconds 0.1 days';
^
select interval '-9223372036854775808 microseconds -0.1 days';
ERROR: interval field value out of range: "-9223372036854775808 microseconds -0.1 days"
LINE 1: select interval '-9223372036854775808 microseconds -0.1 days...
^
-- overflowing with fractional fields - ISO8601 format
select interval 'P0.1Y2147483647M';
ERROR: interval field value out of range: "P0.1Y2147483647M"
LINE 1: select interval 'P0.1Y2147483647M';
^
select interval 'P-0.1Y-2147483648M';
ERROR: interval field value out of range: "P-0.1Y-2147483648M"
LINE 1: select interval 'P-0.1Y-2147483648M';
^
select interval 'P2147483647M0.1Y';
ERROR: interval field value out of range: "P2147483647M0.1Y"
LINE 1: select interval 'P2147483647M0.1Y';
^
select interval 'P-2147483648M-0.1Y';
ERROR: interval field value out of range: "P-2147483648M-0.1Y"
LINE 1: select interval 'P-2147483648M-0.1Y';
^
select interval 'P0.1M2147483647D';
ERROR: interval field value out of range: "P0.1M2147483647D"
LINE 1: select interval 'P0.1M2147483647D';
^
select interval 'P-0.1M-2147483648D';
ERROR: interval field value out of range: "P-0.1M-2147483648D"
LINE 1: select interval 'P-0.1M-2147483648D';
^
select interval 'P2147483647D0.1M';
ERROR: interval field value out of range: "P2147483647D0.1M"
LINE 1: select interval 'P2147483647D0.1M';
^
select interval 'P-2147483648D-0.1M';
ERROR: interval field value out of range: "P-2147483648D-0.1M"
LINE 1: select interval 'P-2147483648D-0.1M';
^
select interval 'P0.5W2147483647D';
ERROR: interval field value out of range: "P0.5W2147483647D"
LINE 1: select interval 'P0.5W2147483647D';
^
select interval 'P-0.5W-2147483648D';
ERROR: interval field value out of range: "P-0.5W-2147483648D"
LINE 1: select interval 'P-0.5W-2147483648D';
^
select interval 'P2147483647D0.5W';
ERROR: interval field value out of range: "P2147483647D0.5W"
LINE 1: select interval 'P2147483647D0.5W';
^
select interval 'P-2147483648D-0.5W';
ERROR: interval field value out of range: "P-2147483648D-0.5W"
LINE 1: select interval 'P-2147483648D-0.5W';
^
select interval 'P0.01MT2562047788H54.775807S';
ERROR: interval field value out of range: "P0.01MT2562047788H54.775807S"
LINE 1: select interval 'P0.01MT2562047788H54.775807S';
^
select interval 'P-0.01MT-2562047788H-54.775808S';
ERROR: interval field value out of range: "P-0.01MT-2562047788H-54.775808S"
LINE 1: select interval 'P-0.01MT-2562047788H-54.775808S';
^
select interval 'P0.1DT2562047788H54.775807S';
ERROR: interval field value out of range: "P0.1DT2562047788H54.775807S"
LINE 1: select interval 'P0.1DT2562047788H54.775807S';
^
select interval 'P-0.1DT-2562047788H-54.775808S';
ERROR: interval field value out of range: "P-0.1DT-2562047788H-54.775808S"
LINE 1: select interval 'P-0.1DT-2562047788H-54.775808S';
^
select interval 'PT2562047788.1H54.775807S';
ERROR: interval field value out of range: "PT2562047788.1H54.775807S"
LINE 1: select interval 'PT2562047788.1H54.775807S';
^
select interval 'PT-2562047788.1H-54.775808S';
ERROR: interval field value out of range: "PT-2562047788.1H-54.775808S"
LINE 1: select interval 'PT-2562047788.1H-54.775808S';
^
select interval 'PT2562047788H0.1M54.775807S';
ERROR: interval field value out of range: "PT2562047788H0.1M54.775807S"
LINE 1: select interval 'PT2562047788H0.1M54.775807S';
^
select interval 'PT-2562047788H-0.1M-54.775808S';
ERROR: interval field value out of range: "PT-2562047788H-0.1M-54.775808S"
LINE 1: select interval 'PT-2562047788H-0.1M-54.775808S';
^
-- overflowing with fractional fields - ISO8601 alternative format
select interval 'P0.1-2147483647-00';
ERROR: interval field value out of range: "P0.1-2147483647-00"
LINE 1: select interval 'P0.1-2147483647-00';
^
select interval 'P00-0.1-2147483647';
ERROR: interval field value out of range: "P00-0.1-2147483647"
LINE 1: select interval 'P00-0.1-2147483647';
^
select interval 'P00-0.01-00T2562047788:00:54.775807';
ERROR: interval field value out of range: "P00-0.01-00T2562047788:00:54.775807"
LINE 1: select interval 'P00-0.01-00T2562047788:00:54.775807';
^
select interval 'P00-00-0.1T2562047788:00:54.775807';
ERROR: interval field value out of range: "P00-00-0.1T2562047788:00:54.775807"
LINE 1: select interval 'P00-00-0.1T2562047788:00:54.775807';
^
select interval 'PT2562047788.1:00:54.775807';
ERROR: interval field value out of range: "PT2562047788.1:00:54.775807"
LINE 1: select interval 'PT2562047788.1:00:54.775807';
^
select interval 'PT2562047788:01.:54.775807';
ERROR: interval field value out of range: "PT2562047788:01.:54.775807"
LINE 1: select interval 'PT2562047788:01.:54.775807';
^
-- overflowing with fractional fields - SQL standard format
select interval '0.1 2562047788:0:54.775807';
ERROR: interval field value out of range: "0.1 2562047788:0:54.775807"
LINE 1: select interval '0.1 2562047788:0:54.775807';
^
select interval '0.1 2562047788:0:54.775808 ago';
ERROR: interval field value out of range: "0.1 2562047788:0:54.775808 ago"
LINE 1: select interval '0.1 2562047788:0:54.775808 ago';
^
select interval '2562047788.1:0:54.775807';
ERROR: interval field value out of range: "2562047788.1:0:54.775807"
LINE 1: select interval '2562047788.1:0:54.775807';
^
select interval '2562047788.1:0:54.775808 ago';
ERROR: interval field value out of range: "2562047788.1:0:54.775808 ago"
LINE 1: select interval '2562047788.1:0:54.775808 ago';
^
select interval '2562047788:0.1:54.775807';
ERROR: invalid input syntax for type interval: "2562047788:0.1:54.775807"
LINE 1: select interval '2562047788:0.1:54.775807';
^
select interval '2562047788:0.1:54.775808 ago';
ERROR: invalid input syntax for type interval: "2562047788:0.1:54.775808 ago"
LINE 1: select interval '2562047788:0.1:54.775808 ago';
^
-- overflowing using AGO with INT_MIN
select interval '-2147483648 months ago';
ERROR: interval field value out of range: "-2147483648 months ago"
LINE 1: select interval '-2147483648 months ago';
^
select interval '-2147483648 days ago';
ERROR: interval field value out of range: "-2147483648 days ago"
LINE 1: select interval '-2147483648 days ago';
^
select interval '-9223372036854775808 microseconds ago';
ERROR: interval field value out of range: "-9223372036854775808 microseconds ago"
LINE 1: select interval '-9223372036854775808 microseconds ago';
^
select interval '-2147483648 months -2147483648 days -9223372036854775808 microseconds ago';
ERROR: interval field value out of range: "-2147483648 months -2147483648 days -9223372036854775808 microseconds ago"
LINE 1: select interval '-2147483648 months -2147483648 days -922337...
^
-- test that INT_MIN number is formatted properly
SET IntervalStyle to postgres;
select interval '-2147483648 months -2147483648 days -9223372036854775808 us';
interval
--------------------------------------------------------------------
-178956970 years -8 mons -2147483648 days -2562047788:00:54.775808
(1 row)
SET IntervalStyle to sql_standard;
select interval '-2147483648 months -2147483648 days -9223372036854775808 us';
interval
---------------------------------------------------
-178956970-8 -2147483648 -2562047788:00:54.775808
(1 row)
SET IntervalStyle to iso_8601;
select interval '-2147483648 months -2147483648 days -9223372036854775808 us';
interval
-----------------------------------------------------
P-178956970Y-8M-2147483648DT-2562047788H-54.775808S
(1 row)
SET IntervalStyle to postgres_verbose;
select interval '-2147483648 months -2147483648 days -9223372036854775808 us';
interval
------------------------------------------------------------------------------
@ 178956970 years 8 mons 2147483648 days 2562047788 hours 54.775808 secs ago
(1 row)
-- check that '30 days' equals '1 month' according to the hash function
select '30 days'::interval = '1 month'::interval as t;
t

View File

@ -318,6 +318,190 @@ select interval '-10 mons -3 days +03:55:06.70';
select interval '1 year 2 mons 3 days 04:05:06.699999';
select interval '0:0:0.7', interval '@ 0.70 secs', interval '0.7 seconds';
-- test time fields using entire 64 bit microseconds range
select interval '2562047788.01521550194 hours';
select interval '-2562047788.01521550222 hours';
select interval '153722867280.912930117 minutes';
select interval '-153722867280.912930133 minutes';
select interval '9223372036854.775807 seconds';
select interval '-9223372036854.775808 seconds';
select interval '9223372036854775.807 milliseconds';
select interval '-9223372036854775.808 milliseconds';
select interval '9223372036854775807 microseconds';
select interval '-9223372036854775808 microseconds';
select interval 'PT2562047788H54.775807S';
select interval 'PT-2562047788H-54.775808S';
select interval 'PT2562047788:00:54.775807';
select interval 'PT2562047788.0152155019444';
select interval 'PT-2562047788.0152155022222';
-- overflow each date/time field
select interval '2147483648 years';
select interval '-2147483649 years';
select interval '2147483648 months';
select interval '-2147483649 months';
select interval '2147483648 days';
select interval '-2147483649 days';
select interval '2562047789 hours';
select interval '-2562047789 hours';
select interval '153722867281 minutes';
select interval '-153722867281 minutes';
select interval '9223372036855 seconds';
select interval '-9223372036855 seconds';
select interval '9223372036854777 millisecond';
select interval '-9223372036854777 millisecond';
select interval '9223372036854775808 microsecond';
select interval '-9223372036854775809 microsecond';
select interval 'P2147483648';
select interval 'P-2147483649';
select interval 'P1-2147483647-2147483647';
select interval 'PT2562047789';
select interval 'PT-2562047789';
-- overflow with date/time unit aliases
select interval '2147483647 weeks';
select interval '-2147483648 weeks';
select interval '2147483647 decades';
select interval '-2147483648 decades';
select interval '2147483647 centuries';
select interval '-2147483648 centuries';
select interval '2147483647 millennium';
select interval '-2147483648 millennium';
select interval '1 week 2147483647 days';
select interval '-1 week -2147483648 days';
select interval '2147483647 days 1 week';
select interval '-2147483648 days -1 week';
select interval 'P1W2147483647D';
select interval 'P-1W-2147483648D';
select interval 'P2147483647D1W';
select interval 'P-2147483648D-1W';
select interval '1 decade 2147483647 years';
select interval '1 century 2147483647 years';
select interval '1 millennium 2147483647 years';
select interval '-1 decade -2147483648 years';
select interval '-1 century -2147483648 years';
select interval '-1 millennium -2147483648 years';
select interval '2147483647 years 1 decade';
select interval '2147483647 years 1 century';
select interval '2147483647 years 1 millennium';
select interval '-2147483648 years -1 decade';
select interval '-2147483648 years -1 century';
select interval '-2147483648 years -1 millennium';
-- overflowing with fractional fields - postgres format
select interval '0.1 millennium 2147483647 months';
select interval '0.1 centuries 2147483647 months';
select interval '0.1 decades 2147483647 months';
select interval '0.1 yrs 2147483647 months';
select interval '-0.1 millennium -2147483648 months';
select interval '-0.1 centuries -2147483648 months';
select interval '-0.1 decades -2147483648 months';
select interval '-0.1 yrs -2147483648 months';
select interval '2147483647 months 0.1 millennium';
select interval '2147483647 months 0.1 centuries';
select interval '2147483647 months 0.1 decades';
select interval '2147483647 months 0.1 yrs';
select interval '-2147483648 months -0.1 millennium';
select interval '-2147483648 months -0.1 centuries';
select interval '-2147483648 months -0.1 decades';
select interval '-2147483648 months -0.1 yrs';
select interval '0.1 months 2147483647 days';
select interval '-0.1 months -2147483648 days';
select interval '2147483647 days 0.1 months';
select interval '-2147483648 days -0.1 months';
select interval '0.5 weeks 2147483647 days';
select interval '-0.5 weeks -2147483648 days';
select interval '2147483647 days 0.5 weeks';
select interval '-2147483648 days -0.5 weeks';
select interval '0.01 months 9223372036854775807 microseconds';
select interval '-0.01 months -9223372036854775808 microseconds';
select interval '9223372036854775807 microseconds 0.01 months';
select interval '-9223372036854775808 microseconds -0.01 months';
select interval '0.1 weeks 9223372036854775807 microseconds';
select interval '-0.1 weeks -9223372036854775808 microseconds';
select interval '9223372036854775807 microseconds 0.1 weeks';
select interval '-9223372036854775808 microseconds -0.1 weeks';
select interval '0.1 days 9223372036854775807 microseconds';
select interval '-0.1 days -9223372036854775808 microseconds';
select interval '9223372036854775807 microseconds 0.1 days';
select interval '-9223372036854775808 microseconds -0.1 days';
-- overflowing with fractional fields - ISO8601 format
select interval 'P0.1Y2147483647M';
select interval 'P-0.1Y-2147483648M';
select interval 'P2147483647M0.1Y';
select interval 'P-2147483648M-0.1Y';
select interval 'P0.1M2147483647D';
select interval 'P-0.1M-2147483648D';
select interval 'P2147483647D0.1M';
select interval 'P-2147483648D-0.1M';
select interval 'P0.5W2147483647D';
select interval 'P-0.5W-2147483648D';
select interval 'P2147483647D0.5W';
select interval 'P-2147483648D-0.5W';
select interval 'P0.01MT2562047788H54.775807S';
select interval 'P-0.01MT-2562047788H-54.775808S';
select interval 'P0.1DT2562047788H54.775807S';
select interval 'P-0.1DT-2562047788H-54.775808S';
select interval 'PT2562047788.1H54.775807S';
select interval 'PT-2562047788.1H-54.775808S';
select interval 'PT2562047788H0.1M54.775807S';
select interval 'PT-2562047788H-0.1M-54.775808S';
-- overflowing with fractional fields - ISO8601 alternative format
select interval 'P0.1-2147483647-00';
select interval 'P00-0.1-2147483647';
select interval 'P00-0.01-00T2562047788:00:54.775807';
select interval 'P00-00-0.1T2562047788:00:54.775807';
select interval 'PT2562047788.1:00:54.775807';
select interval 'PT2562047788:01.:54.775807';
-- overflowing with fractional fields - SQL standard format
select interval '0.1 2562047788:0:54.775807';
select interval '0.1 2562047788:0:54.775808 ago';
select interval '2562047788.1:0:54.775807';
select interval '2562047788.1:0:54.775808 ago';
select interval '2562047788:0.1:54.775807';
select interval '2562047788:0.1:54.775808 ago';
-- overflowing using AGO with INT_MIN
select interval '-2147483648 months ago';
select interval '-2147483648 days ago';
select interval '-9223372036854775808 microseconds ago';
select interval '-2147483648 months -2147483648 days -9223372036854775808 microseconds ago';
-- test that INT_MIN number is formatted properly
SET IntervalStyle to postgres;
select interval '-2147483648 months -2147483648 days -9223372036854775808 us';
SET IntervalStyle to sql_standard;
select interval '-2147483648 months -2147483648 days -9223372036854775808 us';
SET IntervalStyle to iso_8601;
select interval '-2147483648 months -2147483648 days -9223372036854775808 us';
SET IntervalStyle to postgres_verbose;
select interval '-2147483648 months -2147483648 days -9223372036854775808 us';
-- check that '30 days' equals '1 month' according to the hash function
select '30 days'::interval = '1 month'::interval as t;
select interval_hash('30 days'::interval) = interval_hash('1 month'::interval) as t;