Skip to content

Commit f2ba1e9

Browse files
committed
Avoid unexpected conversion overflow in planner for distant date values.
The "date" type supports a wider range of dates than int64 timestamps do. However, there is pre-int64-timestamp code in the planner that assumes that all date values can be converted to timestamp with impunity. Fortunately, what we really need out of the conversion is always a double (float8) value; so even when the date is out of timestamp's range it's possible to produce a sane answer. All we need is a code path that doesn't try to force the result into int64. Per trouble report from David Rericha. Back-patch to all supported versions. Although this is surely a corner case, there's not much point in advertising a date range wider than timestamp's if we will choke on such values in unexpected places.
1 parent 31d2efa commit f2ba1e9

File tree

3 files changed

+36
-2
lines changed

3 files changed

+36
-2
lines changed

src/backend/utils/adt/date.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,39 @@ date2timestamptz(DateADT dateVal)
459459
return result;
460460
}
461461

462+
/*
463+
* date2timestamp_no_overflow
464+
*
465+
* This is chartered to produce a double value that is numerically
466+
* equivalent to the corresponding Timestamp value, if the date is in the
467+
* valid range of Timestamps, but in any case not throw an overflow error.
468+
* We can do this since the numerical range of double is greater than
469+
* that of non-erroneous timestamps. The results are currently only
470+
* used for statistical estimation purposes.
471+
*/
472+
double
473+
date2timestamp_no_overflow(DateADT dateVal)
474+
{
475+
double result;
476+
477+
if (DATE_IS_NOBEGIN(dateVal))
478+
result = -DBL_MAX;
479+
else if (DATE_IS_NOEND(dateVal))
480+
result = DBL_MAX;
481+
else
482+
{
483+
#ifdef HAVE_INT64_TIMESTAMP
484+
/* date is days since 2000, timestamp is microseconds since same... */
485+
result = dateVal * (double) USECS_PER_DAY;
486+
#else
487+
/* date is days since 2000, timestamp is seconds since same... */
488+
result = dateVal * (double) SECS_PER_DAY;
489+
#endif
490+
}
491+
492+
return result;
493+
}
494+
462495

463496
/*
464497
* Crosstype comparison functions for dates

src/backend/utils/adt/selfuncs.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3866,8 +3866,7 @@ convert_timevalue_to_scalar(Datum value, Oid typid)
38663866
return DatumGetTimestamp(DirectFunctionCall1(abstime_timestamp,
38673867
value));
38683868
case DATEOID:
3869-
return DatumGetTimestamp(DirectFunctionCall1(date_timestamp,
3870-
value));
3869+
return date2timestamp_no_overflow(DatumGetDateADT(value));
38713870
case INTERVALOID:
38723871
{
38733872
Interval *interval = DatumGetIntervalP(value);

src/include/utils/date.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ typedef struct
9191

9292

9393
/* date.c */
94+
extern double date2timestamp_no_overflow(DateADT dateVal);
95+
9496
extern Datum date_in(PG_FUNCTION_ARGS);
9597
extern Datum date_out(PG_FUNCTION_ARGS);
9698
extern Datum date_recv(PG_FUNCTION_ARGS);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy