Prevent internal overflows in date-vs-timestamp and related comparisons.

The date-vs-timestamp, date-vs-timestamptz, and timestamp-vs-timestamptz
comparators all worked by promoting the first type to the second and
then doing a simple same-type comparison.  This works fine, except
when the conversion result is out of range, in which case we throw an
entirely avoidable error.  The sources of such failures are
(a) type date can represent dates much farther in the future than
the timestamp types can;
(b) timezone rotation might cause a just-in-range timestamp value to
become a just-out-of-range timestamptz value.

Up to now we just ignored these corner-case issues, but now we have
an actual user complaint (bug #16657 from Huss EL-Sheikh), so let's
do something about it.

It turns out that commit 52ad1e659 already built all the necessary
infrastructure to support error-free comparisons, but neglected to
actually use it in the main-line code paths.  Fix that, do a little
bit of code style review, and remove the now-duplicate logic in
jsonpath_exec.c.

Back-patch to v13 where 52ad1e659 came in.  We could take this back
further by back-patching said infrastructure, but given the small
number of complaints so far, I don't feel a great need to.

Discussion: https://postgr.es/m/16657-cde2f876d8cc7971@postgresql.org
This commit is contained in:
Tom Lane 2020-10-07 17:10:26 -04:00
parent 6c05e5b774
commit 3db322eaab
7 changed files with 260 additions and 252 deletions

View File

@ -554,15 +554,24 @@ date_mii(PG_FUNCTION_ARGS)
/*
* Promote date to timestamp.
*
* On overflow error is thrown if 'overflow' is NULL. Otherwise, '*overflow'
* is set to -1 (+1) when result value exceed lower (upper) boundary and zero
* returned.
* On successful conversion, *overflow is set to zero if it's not NULL.
*
* If the date is finite but out of the valid range for timestamp, then:
* if overflow is NULL, we throw an out-of-range error.
* if overflow is not NULL, we store +1 or -1 there to indicate the sign
* of the overflow, and return the appropriate timestamp infinity.
*
* Note: *overflow = -1 is actually not possible currently, since both
* datatypes have the same lower bound, Julian day zero.
*/
Timestamp
date2timestamp_opt_overflow(DateADT dateVal, int *overflow)
{
Timestamp result;
if (overflow)
*overflow = 0;
if (DATE_IS_NOBEGIN(dateVal))
TIMESTAMP_NOBEGIN(result);
else if (DATE_IS_NOEND(dateVal))
@ -570,7 +579,6 @@ date2timestamp_opt_overflow(DateADT dateVal, int *overflow)
else
{
/*
* Date's range is wider than timestamp's, so check for boundaries.
* Since dates have the same minimum values as timestamps, only upper
* boundary need be checked for overflow.
*/
@ -579,7 +587,8 @@ date2timestamp_opt_overflow(DateADT dateVal, int *overflow)
if (overflow)
{
*overflow = 1;
return (Timestamp) 0;
TIMESTAMP_NOEND(result);
return result;
}
else
{
@ -597,7 +606,7 @@ date2timestamp_opt_overflow(DateADT dateVal, int *overflow)
}
/*
* Single-argument version of date2timestamp_opt_overflow().
* Promote date to timestamp, throwing error for overflow.
*/
static TimestampTz
date2timestamp(DateADT dateVal)
@ -608,9 +617,12 @@ date2timestamp(DateADT dateVal)
/*
* Promote date to timestamp with time zone.
*
* On overflow error is thrown if 'overflow' is NULL. Otherwise, '*overflow'
* is set to -1 (+1) when result value exceed lower (upper) boundary and zero
* returned.
* On successful conversion, *overflow is set to zero if it's not NULL.
*
* If the date is finite but out of the valid range for timestamptz, then:
* if overflow is NULL, we throw an out-of-range error.
* if overflow is not NULL, we store +1 or -1 there to indicate the sign
* of the overflow, and return the appropriate timestamptz infinity.
*/
TimestampTz
date2timestamptz_opt_overflow(DateADT dateVal, int *overflow)
@ -620,6 +632,9 @@ date2timestamptz_opt_overflow(DateADT dateVal, int *overflow)
*tm = &tt;
int tz;
if (overflow)
*overflow = 0;
if (DATE_IS_NOBEGIN(dateVal))
TIMESTAMP_NOBEGIN(result);
else if (DATE_IS_NOEND(dateVal))
@ -627,7 +642,6 @@ date2timestamptz_opt_overflow(DateADT dateVal, int *overflow)
else
{
/*
* Date's range is wider than timestamp's, so check for boundaries.
* Since dates have the same minimum values as timestamps, only upper
* boundary need be checked for overflow.
*/
@ -636,7 +650,8 @@ date2timestamptz_opt_overflow(DateADT dateVal, int *overflow)
if (overflow)
{
*overflow = 1;
return (TimestampTz) 0;
TIMESTAMP_NOEND(result);
return result;
}
else
{
@ -664,13 +679,15 @@ date2timestamptz_opt_overflow(DateADT dateVal, int *overflow)
if (overflow)
{
if (result < MIN_TIMESTAMP)
{
*overflow = -1;
TIMESTAMP_NOBEGIN(result);
}
else
{
Assert(result >= END_TIMESTAMP);
*overflow = 1;
TIMESTAMP_NOEND(result);
}
return (TimestampTz) 0;
}
else
{
@ -685,7 +702,7 @@ date2timestamptz_opt_overflow(DateADT dateVal, int *overflow)
}
/*
* Single-argument version of date2timestamptz_opt_overflow().
* Promote date to timestamptz, throwing error for overflow.
*/
static TimestampTz
date2timestamptz(DateADT dateVal)
@ -726,16 +743,30 @@ date2timestamp_no_overflow(DateADT dateVal)
* Crosstype comparison functions for dates
*/
int32
date_cmp_timestamp_internal(DateADT dateVal, Timestamp dt2)
{
Timestamp dt1;
int overflow;
dt1 = date2timestamp_opt_overflow(dateVal, &overflow);
if (overflow > 0)
{
/* dt1 is larger than any finite timestamp, but less than infinity */
return TIMESTAMP_IS_NOEND(dt2) ? -1 : +1;
}
Assert(overflow == 0); /* -1 case cannot occur */
return timestamp_cmp_internal(dt1, dt2);
}
Datum
date_eq_timestamp(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
Timestamp dt1;
dt1 = date2timestamp(dateVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) == 0);
}
Datum
@ -743,11 +774,8 @@ date_ne_timestamp(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
Timestamp dt1;
dt1 = date2timestamp(dateVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) != 0);
}
Datum
@ -755,11 +783,8 @@ date_lt_timestamp(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
Timestamp dt1;
dt1 = date2timestamp(dateVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) < 0);
}
Datum
@ -767,11 +792,8 @@ date_gt_timestamp(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
Timestamp dt1;
dt1 = date2timestamp(dateVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) > 0);
}
Datum
@ -779,11 +801,8 @@ date_le_timestamp(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
Timestamp dt1;
dt1 = date2timestamp(dateVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) <= 0);
}
Datum
@ -791,11 +810,8 @@ date_ge_timestamp(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
Timestamp dt1;
dt1 = date2timestamp(dateVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) >= 0);
}
Datum
@ -803,11 +819,29 @@ date_cmp_timestamp(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
Timestamp dt1;
dt1 = date2timestamp(dateVal);
PG_RETURN_INT32(date_cmp_timestamp_internal(dateVal, dt2));
}
PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
int32
date_cmp_timestamptz_internal(DateADT dateVal, TimestampTz dt2)
{
TimestampTz dt1;
int overflow;
dt1 = date2timestamptz_opt_overflow(dateVal, &overflow);
if (overflow > 0)
{
/* dt1 is larger than any finite timestamp, but less than infinity */
return TIMESTAMP_IS_NOEND(dt2) ? -1 : +1;
}
if (overflow < 0)
{
/* dt1 is less than any finite timestamp, but more than -infinity */
return TIMESTAMP_IS_NOBEGIN(dt2) ? +1 : -1;
}
return timestamptz_cmp_internal(dt1, dt2);
}
Datum
@ -815,11 +849,8 @@ date_eq_timestamptz(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = date2timestamptz(dateVal);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) == 0);
}
Datum
@ -827,11 +858,8 @@ date_ne_timestamptz(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = date2timestamptz(dateVal);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) != 0);
}
Datum
@ -839,11 +867,8 @@ date_lt_timestamptz(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = date2timestamptz(dateVal);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) < 0);
}
Datum
@ -851,11 +876,8 @@ date_gt_timestamptz(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = date2timestamptz(dateVal);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) > 0);
}
Datum
@ -863,11 +885,8 @@ date_le_timestamptz(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = date2timestamptz(dateVal);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) <= 0);
}
Datum
@ -875,11 +894,8 @@ date_ge_timestamptz(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = date2timestamptz(dateVal);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) >= 0);
}
Datum
@ -887,11 +903,8 @@ date_cmp_timestamptz(PG_FUNCTION_ARGS)
{
DateADT dateVal = PG_GETARG_DATEADT(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = date2timestamptz(dateVal);
PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
PG_RETURN_INT32(date_cmp_timestamptz_internal(dateVal, dt2));
}
Datum
@ -899,11 +912,8 @@ timestamp_eq_date(PG_FUNCTION_ARGS)
{
Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
DateADT dateVal = PG_GETARG_DATEADT(1);
Timestamp dt2;
dt2 = date2timestamp(dateVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) == 0);
}
Datum
@ -911,11 +921,8 @@ timestamp_ne_date(PG_FUNCTION_ARGS)
{
Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
DateADT dateVal = PG_GETARG_DATEADT(1);
Timestamp dt2;
dt2 = date2timestamp(dateVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) != 0);
}
Datum
@ -923,11 +930,8 @@ timestamp_lt_date(PG_FUNCTION_ARGS)
{
Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
DateADT dateVal = PG_GETARG_DATEADT(1);
Timestamp dt2;
dt2 = date2timestamp(dateVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) > 0);
}
Datum
@ -935,11 +939,8 @@ timestamp_gt_date(PG_FUNCTION_ARGS)
{
Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
DateADT dateVal = PG_GETARG_DATEADT(1);
Timestamp dt2;
dt2 = date2timestamp(dateVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) < 0);
}
Datum
@ -947,11 +948,8 @@ timestamp_le_date(PG_FUNCTION_ARGS)
{
Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
DateADT dateVal = PG_GETARG_DATEADT(1);
Timestamp dt2;
dt2 = date2timestamp(dateVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) >= 0);
}
Datum
@ -959,11 +957,8 @@ timestamp_ge_date(PG_FUNCTION_ARGS)
{
Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
DateADT dateVal = PG_GETARG_DATEADT(1);
Timestamp dt2;
dt2 = date2timestamp(dateVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) <= 0);
}
Datum
@ -971,11 +966,8 @@ timestamp_cmp_date(PG_FUNCTION_ARGS)
{
Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
DateADT dateVal = PG_GETARG_DATEADT(1);
Timestamp dt2;
dt2 = date2timestamp(dateVal);
PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
PG_RETURN_INT32(-date_cmp_timestamp_internal(dateVal, dt1));
}
Datum
@ -983,11 +975,8 @@ timestamptz_eq_date(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
DateADT dateVal = PG_GETARG_DATEADT(1);
TimestampTz dt2;
dt2 = date2timestamptz(dateVal);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) == 0);
}
Datum
@ -995,11 +984,8 @@ timestamptz_ne_date(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
DateADT dateVal = PG_GETARG_DATEADT(1);
TimestampTz dt2;
dt2 = date2timestamptz(dateVal);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) != 0);
}
Datum
@ -1007,11 +993,8 @@ timestamptz_lt_date(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
DateADT dateVal = PG_GETARG_DATEADT(1);
TimestampTz dt2;
dt2 = date2timestamptz(dateVal);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) > 0);
}
Datum
@ -1019,11 +1002,8 @@ timestamptz_gt_date(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
DateADT dateVal = PG_GETARG_DATEADT(1);
TimestampTz dt2;
dt2 = date2timestamptz(dateVal);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) < 0);
}
Datum
@ -1031,11 +1011,8 @@ timestamptz_le_date(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
DateADT dateVal = PG_GETARG_DATEADT(1);
TimestampTz dt2;
dt2 = date2timestamptz(dateVal);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) >= 0);
}
Datum
@ -1043,11 +1020,8 @@ timestamptz_ge_date(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
DateADT dateVal = PG_GETARG_DATEADT(1);
TimestampTz dt2;
dt2 = date2timestamptz(dateVal);
PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) <= 0);
}
Datum
@ -1055,11 +1029,8 @@ timestamptz_cmp_date(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
DateADT dateVal = PG_GETARG_DATEADT(1);
TimestampTz dt2;
dt2 = date2timestamptz(dateVal);
PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
PG_RETURN_INT32(-date_cmp_timestamptz_internal(dateVal, dt1));
}
/*
@ -1079,6 +1050,7 @@ in_range_date_interval(PG_FUNCTION_ARGS)
Timestamp valStamp;
Timestamp baseStamp;
/* XXX we could support out-of-range cases here, perhaps */
valStamp = date2timestamp(val);
baseStamp = date2timestamp(base);

View File

@ -2602,93 +2602,36 @@ castTimeToTimeTz(Datum time, bool useTz)
return DirectFunctionCall1(time_timetz, time);
}
/*---
* Compares 'ts1' and 'ts2' timestamp, assuming that ts1 might be overflowed
* during cast from another datatype.
*
* 'overflow1' specifies overflow of 'ts1' value:
* 0 - no overflow,
* -1 - exceed lower boundary,
* 1 - exceed upper boundary.
*/
static int
cmpTimestampWithOverflow(Timestamp ts1, int overflow1, Timestamp ts2)
{
/*
* All the timestamps we deal with in jsonpath are produced by
* to_datetime() method. So, they should be valid.
*/
Assert(IS_VALID_TIMESTAMP(ts2));
/*
* Timestamp, which exceed lower (upper) bound, is always lower (higher)
* than any valid timestamp except minus (plus) infinity.
*/
if (overflow1)
{
if (overflow1 < 0)
{
if (TIMESTAMP_IS_NOBEGIN(ts2))
return 1;
else
return -1;
}
if (overflow1 > 0)
{
if (TIMESTAMP_IS_NOEND(ts2))
return -1;
else
return 1;
}
}
return timestamp_cmp_internal(ts1, ts2);
}
/*
* Compare date to timestamptz without throwing overflow error during cast.
* Compare date to timestamp.
* Note that this doesn't involve any timezone considerations.
*/
static int
cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
{
TimestampTz ts1;
int overflow = 0;
ts1 = date2timestamp_opt_overflow(date1, &overflow);
return cmpTimestampWithOverflow(ts1, overflow, ts2);
return date_cmp_timestamp_internal(date1, ts2);
}
/*
* Compare date to timestamptz without throwing overflow error during cast.
* Compare date to timestamptz.
*/
static int
cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
{
TimestampTz tstz1;
int overflow = 0;
checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
tstz1 = date2timestamptz_opt_overflow(date1, &overflow);
return cmpTimestampWithOverflow(tstz1, overflow, tstz2);
return date_cmp_timestamptz_internal(date1, tstz2);
}
/*
* Compare timestamp to timestamptz without throwing overflow error during cast.
* Compare timestamp to timestamptz.
*/
static int
cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
{
TimestampTz tstz1;
int overflow = 0;
checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
tstz1 = timestamp2timestamptz_opt_overflow(ts1, &overflow);
return cmpTimestampWithOverflow(tstz1, overflow, tstz2);
return timestamp_cmp_timestamptz_internal(ts1, tstz2);
}
/*

View File

@ -2156,16 +2156,34 @@ timestamp_hash_extended(PG_FUNCTION_ARGS)
* Cross-type comparison functions for timestamp vs timestamptz
*/
int32
timestamp_cmp_timestamptz_internal(Timestamp timestampVal, TimestampTz dt2)
{
TimestampTz dt1;
int overflow;
dt1 = timestamp2timestamptz_opt_overflow(timestampVal, &overflow);
if (overflow > 0)
{
/* dt1 is larger than any finite timestamp, but less than infinity */
return TIMESTAMP_IS_NOEND(dt2) ? -1 : +1;
}
if (overflow < 0)
{
/* dt1 is less than any finite timestamp, but more than -infinity */
return TIMESTAMP_IS_NOBEGIN(dt2) ? +1 : -1;
}
return timestamptz_cmp_internal(dt1, dt2);
}
Datum
timestamp_eq_timestamptz(PG_FUNCTION_ARGS)
{
Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = timestamp2timestamptz(timestampVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) == 0);
}
Datum
@ -2173,11 +2191,8 @@ timestamp_ne_timestamptz(PG_FUNCTION_ARGS)
{
Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = timestamp2timestamptz(timestampVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) != 0);
}
Datum
@ -2185,11 +2200,8 @@ timestamp_lt_timestamptz(PG_FUNCTION_ARGS)
{
Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = timestamp2timestamptz(timestampVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) < 0);
}
Datum
@ -2197,11 +2209,8 @@ timestamp_gt_timestamptz(PG_FUNCTION_ARGS)
{
Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = timestamp2timestamptz(timestampVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) > 0);
}
Datum
@ -2209,11 +2218,8 @@ timestamp_le_timestamptz(PG_FUNCTION_ARGS)
{
Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = timestamp2timestamptz(timestampVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) <= 0);
}
Datum
@ -2221,11 +2227,8 @@ timestamp_ge_timestamptz(PG_FUNCTION_ARGS)
{
Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = timestamp2timestamptz(timestampVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) >= 0);
}
Datum
@ -2233,11 +2236,8 @@ timestamp_cmp_timestamptz(PG_FUNCTION_ARGS)
{
Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
TimestampTz dt1;
dt1 = timestamp2timestamptz(timestampVal);
PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
PG_RETURN_INT32(timestamp_cmp_timestamptz_internal(timestampVal, dt2));
}
Datum
@ -2245,11 +2245,8 @@ timestamptz_eq_timestamp(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
TimestampTz dt2;
dt2 = timestamp2timestamptz(timestampVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) == 0);
}
Datum
@ -2257,11 +2254,8 @@ timestamptz_ne_timestamp(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
TimestampTz dt2;
dt2 = timestamp2timestamptz(timestampVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) != 0);
}
Datum
@ -2269,11 +2263,8 @@ timestamptz_lt_timestamp(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
TimestampTz dt2;
dt2 = timestamp2timestamptz(timestampVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) > 0);
}
Datum
@ -2281,11 +2272,8 @@ timestamptz_gt_timestamp(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
TimestampTz dt2;
dt2 = timestamp2timestamptz(timestampVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) < 0);
}
Datum
@ -2293,11 +2281,8 @@ timestamptz_le_timestamp(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
TimestampTz dt2;
dt2 = timestamp2timestamptz(timestampVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) >= 0);
}
Datum
@ -2305,11 +2290,8 @@ timestamptz_ge_timestamp(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
TimestampTz dt2;
dt2 = timestamp2timestamptz(timestampVal);
PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) <= 0);
}
Datum
@ -2317,11 +2299,8 @@ timestamptz_cmp_timestamp(PG_FUNCTION_ARGS)
{
TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
TimestampTz dt2;
dt2 = timestamp2timestamptz(timestampVal);
PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
PG_RETURN_INT32(-timestamp_cmp_timestamptz_internal(timestampVal, dt1));
}
@ -5178,9 +5157,12 @@ timestamp_timestamptz(PG_FUNCTION_ARGS)
/*
* Convert timestamp to timestamp with time zone.
*
* On overflow error is thrown if 'overflow' is NULL. Otherwise, '*overflow'
* is set to -1 (+1) when result value exceed lower (upper) boundary and zero
* returned.
* On successful conversion, *overflow is set to zero if it's not NULL.
*
* If the timestamp is finite but out of the valid range for timestamptz, then:
* if overflow is NULL, we throw an out-of-range error.
* if overflow is not NULL, we store +1 or -1 there to indicate the sign
* of the overflow, and return the appropriate timestamptz infinity.
*/
TimestampTz
timestamp2timestamptz_opt_overflow(Timestamp timestamp, int *overflow)
@ -5191,10 +5173,14 @@ timestamp2timestamptz_opt_overflow(Timestamp timestamp, int *overflow)
fsec_t fsec;
int tz;
if (overflow)
*overflow = 0;
if (TIMESTAMP_NOT_FINITE(timestamp))
return timestamp;
if (!timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL))
/* We don't expect this to fail, but check it pro forma */
if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0)
{
tz = DetermineTimeZoneOffset(tm, session_timezone);
@ -5207,13 +5193,16 @@ timestamp2timestamptz_opt_overflow(Timestamp timestamp, int *overflow)
else if (overflow)
{
if (result < MIN_TIMESTAMP)
{
*overflow = -1;
TIMESTAMP_NOBEGIN(result);
}
else
{
Assert(result >= END_TIMESTAMP);
*overflow = 1;
TIMESTAMP_NOEND(result);
}
return (TimestampTz) 0;
return result;
}
}
@ -5225,7 +5214,7 @@ timestamp2timestamptz_opt_overflow(Timestamp timestamp, int *overflow)
}
/*
* Single-argument version of timestamp2timestamptz_opt_overflow().
* Promote timestamp to timestamptz, throwing error for overflow.
*/
static TimestampTz
timestamp2timestamptz(Timestamp timestamp)

View File

@ -72,6 +72,9 @@ extern int32 anytime_typmod_check(bool istz, int32 typmod);
extern double date2timestamp_no_overflow(DateADT dateVal);
extern Timestamp date2timestamp_opt_overflow(DateADT dateVal, int *overflow);
extern TimestampTz date2timestamptz_opt_overflow(DateADT dateVal, int *overflow);
extern int32 date_cmp_timestamp_internal(DateADT dateVal, Timestamp dt2);
extern int32 date_cmp_timestamptz_internal(DateADT dateVal, TimestampTz dt2);
extern void EncodeSpecialDate(DateADT dt, char *str);
extern DateADT GetSQLCurrentDate(void);
extern TimeTzADT *GetSQLCurrentTime(int32 typmod);

View File

@ -99,6 +99,8 @@ extern int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2);
extern TimestampTz timestamp2timestamptz_opt_overflow(Timestamp timestamp,
int *overflow);
extern int32 timestamp_cmp_timestamptz_internal(Timestamp timestampVal,
TimestampTz dt2);
extern int isoweek2j(int year, int week);
extern void isoweek2date(int woy, int *year, int *mon, int *mday);

View File

@ -2,6 +2,12 @@
-- HOROLOGY
--
SET DateStyle = 'Postgres, MDY';
SHOW TimeZone; -- Many of these tests depend on the prevailing setting
TimeZone
----------
PST8PDT
(1 row)
--
-- Test various input formats
--
@ -2076,6 +2082,72 @@ SELECT '' AS "16", f1 AS "timestamp", date(f1) AS date
DROP TABLE TEMP_TIMESTAMP;
--
-- Comparisons between datetime types, especially overflow cases
---
SELECT '2202020-10-05'::date::timestamp; -- fail
ERROR: date out of range for timestamp
SELECT '2202020-10-05'::date > '2020-10-05'::timestamp as t;
t
---
t
(1 row)
SELECT '2020-10-05'::timestamp > '2202020-10-05'::date as f;
f
---
f
(1 row)
SELECT '2202020-10-05'::date::timestamptz; -- fail
ERROR: date out of range for timestamp
SELECT '2202020-10-05'::date > '2020-10-05'::timestamptz as t;
t
---
t
(1 row)
SELECT '2020-10-05'::timestamptz > '2202020-10-05'::date as f;
f
---
f
(1 row)
-- This conversion may work depending on timezone
SELECT '4714-11-24 BC'::date::timestamptz;
timestamptz
---------------------------------
Mon Nov 24 00:00:00 4714 PST BC
(1 row)
SET TimeZone = 'UTC-2';
SELECT '4714-11-24 BC'::date::timestamptz; -- fail
ERROR: date out of range for timestamp
SELECT '4714-11-24 BC'::date < '2020-10-05'::timestamptz as t;
t
---
t
(1 row)
SELECT '2020-10-05'::timestamptz >= '4714-11-24 BC'::date as t;
t
---
t
(1 row)
SELECT '4714-11-24 BC'::timestamp < '2020-10-05'::timestamptz as t;
t
---
t
(1 row)
SELECT '2020-10-05'::timestamptz >= '4714-11-24 BC'::timestamp as t;
t
---
t
(1 row)
RESET TimeZone;
--
-- Formats
--
SET DateStyle TO 'US,Postgres';

View File

@ -3,6 +3,8 @@
--
SET DateStyle = 'Postgres, MDY';
SHOW TimeZone; -- Many of these tests depend on the prevailing setting
--
-- Test various input formats
--
@ -279,6 +281,31 @@ SELECT '' AS "16", f1 AS "timestamp", date(f1) AS date
DROP TABLE TEMP_TIMESTAMP;
--
-- Comparisons between datetime types, especially overflow cases
---
SELECT '2202020-10-05'::date::timestamp; -- fail
SELECT '2202020-10-05'::date > '2020-10-05'::timestamp as t;
SELECT '2020-10-05'::timestamp > '2202020-10-05'::date as f;
SELECT '2202020-10-05'::date::timestamptz; -- fail
SELECT '2202020-10-05'::date > '2020-10-05'::timestamptz as t;
SELECT '2020-10-05'::timestamptz > '2202020-10-05'::date as f;
-- This conversion may work depending on timezone
SELECT '4714-11-24 BC'::date::timestamptz;
SET TimeZone = 'UTC-2';
SELECT '4714-11-24 BC'::date::timestamptz; -- fail
SELECT '4714-11-24 BC'::date < '2020-10-05'::timestamptz as t;
SELECT '2020-10-05'::timestamptz >= '4714-11-24 BC'::date as t;
SELECT '4714-11-24 BC'::timestamp < '2020-10-05'::timestamptz as t;
SELECT '2020-10-05'::timestamptz >= '4714-11-24 BC'::timestamp as t;
RESET TimeZone;
--
-- Formats
--