diff options
Diffstat (limited to 'src/backend/utils/adt/date.c')
-rw-r--r-- | src/backend/utils/adt/date.c | 118 |
1 files changed, 102 insertions, 16 deletions
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 332db7e9c00..420f383a804 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -160,6 +160,7 @@ date_in(PG_FUNCTION_ARGS) break; } + /* Prevent overflow in Julian-day routines */ if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday)) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), @@ -167,6 +168,12 @@ date_in(PG_FUNCTION_ARGS) date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE; + /* Now check for just-out-of-range dates */ + if (!IS_VALID_DATE(date)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date out of range: \"%s\"", str))); + PG_RETURN_DATEADT(date); } @@ -209,8 +216,7 @@ date_recv(PG_FUNCTION_ARGS) /* Limit to the same range that date_in() accepts. */ if (DATE_NOT_FINITE(result)) /* ok */ ; - else if (result < -POSTGRES_EPOCH_JDATE || - result >= JULIAN_MAX - POSTGRES_EPOCH_JDATE) + else if (!IS_VALID_DATE(result)) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("date out of range"))); @@ -258,6 +264,7 @@ make_date(PG_FUNCTION_ARGS) errmsg("date field value out of range: %d-%02d-%02d", tm.tm_year, tm.tm_mon, tm.tm_mday))); + /* Prevent overflow in Julian-day routines */ if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday)) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), @@ -266,6 +273,13 @@ make_date(PG_FUNCTION_ARGS) date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE; + /* Now check for just-out-of-range dates */ + if (!IS_VALID_DATE(date)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date out of range: %d-%02d-%02d", + tm.tm_year, tm.tm_mon, tm.tm_mday))); + PG_RETURN_DATEADT(date); } @@ -427,11 +441,21 @@ date_pli(PG_FUNCTION_ARGS) { DateADT dateVal = PG_GETARG_DATEADT(0); int32 days = PG_GETARG_INT32(1); + DateADT result; if (DATE_NOT_FINITE(dateVal)) - days = 0; /* can't change infinity */ + PG_RETURN_DATEADT(dateVal); /* can't change infinity */ + + result = dateVal + days; + + /* Check for integer overflow and out-of-allowed-range */ + if ((days >= 0 ? (result < dateVal) : (result > dateVal)) || + !IS_VALID_DATE(result)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date out of range"))); - PG_RETURN_DATEADT(dateVal + days); + PG_RETURN_DATEADT(result); } /* Subtract a number of days from a date, giving a new date. @@ -441,11 +465,21 @@ date_mii(PG_FUNCTION_ARGS) { DateADT dateVal = PG_GETARG_DATEADT(0); int32 days = PG_GETARG_INT32(1); + DateADT result; if (DATE_NOT_FINITE(dateVal)) - days = 0; /* can't change infinity */ + PG_RETURN_DATEADT(dateVal); /* can't change infinity */ + + result = dateVal - days; + + /* Check for integer overflow and out-of-allowed-range */ + if ((days >= 0 ? (result > dateVal) : (result < dateVal)) || + !IS_VALID_DATE(result)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date out of range"))); - PG_RETURN_DATEADT(dateVal - days); + PG_RETURN_DATEADT(result); } /* @@ -464,14 +498,18 @@ date2timestamp(DateADT dateVal) TIMESTAMP_NOEND(result); else { -#ifdef HAVE_INT64_TIMESTAMP - /* date is days since 2000, timestamp is microseconds since same... */ - result = dateVal * USECS_PER_DAY; - /* Date's range is wider than timestamp's, so check for overflow */ - if (result / USECS_PER_DAY != dateVal) + /* + * 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. + */ + if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE)) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("date out of range for timestamp"))); +#ifdef HAVE_INT64_TIMESTAMP + /* date is days since 2000, timestamp is microseconds since same... */ + result = dateVal * USECS_PER_DAY; #else /* date is days since 2000, timestamp is seconds since same... */ result = dateVal * (double) SECS_PER_DAY; @@ -495,6 +533,16 @@ date2timestamptz(DateADT dateVal) TIMESTAMP_NOEND(result); 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. + */ + if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date out of range for timestamp"))); + j2date(dateVal + POSTGRES_EPOCH_JDATE, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday)); tm->tm_hour = 0; @@ -504,14 +552,18 @@ date2timestamptz(DateADT dateVal) #ifdef HAVE_INT64_TIMESTAMP result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC; - /* Date's range is wider than timestamp's, so check for overflow */ - if ((result - tz * USECS_PER_SEC) / USECS_PER_DAY != dateVal) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("date out of range for timestamp"))); #else result = dateVal * (double) SECS_PER_DAY + tz; #endif + + /* + * Since it is possible to go beyond allowed timestamptz range because + * of time zone, check for allowed timestamp range after adding tz. + */ + if (!IS_VALID_TIMESTAMP(result)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date out of range for timestamp"))); } return result; @@ -1053,7 +1105,17 @@ abstime_date(PG_FUNCTION_ARGS) default: abstime2tm(abstime, &tz, tm, NULL); + /* Prevent overflow in Julian-day routines */ + if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("abstime out of range for date"))); result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE; + /* Now check for just-out-of-range dates */ + if (!IS_VALID_DATE(result)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("abstime out of range for date"))); break; } @@ -1678,7 +1740,13 @@ datetime_timestamp(PG_FUNCTION_ARGS) result = date2timestamp(date); if (!TIMESTAMP_NOT_FINITE(result)) + { result += time; + if (!IS_VALID_TIMESTAMP(result)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + } PG_RETURN_TIMESTAMP(result); } @@ -2550,11 +2618,29 @@ datetimetz_timestamptz(PG_FUNCTION_ARGS) TIMESTAMP_NOEND(result); 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. + */ + if (date >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date out of range for timestamp"))); #ifdef HAVE_INT64_TIMESTAMP result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC; #else result = date * (double) SECS_PER_DAY + time->time + time->zone; #endif + + /* + * Since it is possible to go beyond allowed timestamptz range because + * of time zone, check for allowed timestamp range after adding tz. + */ + if (!IS_VALID_TIMESTAMP(result)) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date out of range for timestamp"))); } PG_RETURN_TIMESTAMP(result); |