diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 4f374aac22..df890105cd 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -70,17 +70,28 @@ typedef struct static TimeOffset time2t(const int hour, const int min, const int sec, const fsec_t fsec); static Timestamp dt2local(Timestamp dt, int timezone); -static void AdjustTimestampForTypmod(Timestamp *time, int32 typmod); -static void AdjustIntervalForTypmod(Interval *interval, int32 typmod); +// static void AdjustTimestampForTypmod(Timestamp *time, int32 typmod); +static bool AdjustTimestampForTypmodSafe(Timestamp *time, int32 typmod, Node *escontext); +// static void AdjustIntervalForTypmod(Interval *interval, int32 typmod); +static bool AdjustIntervalForTypmodSafe(Interval *interval, int32 typmod, Node *escontext); static TimestampTz timestamp2timestamptz(Timestamp timestamp); -static inline Timestamp timestamp_offset_internal(Timestamp timestamp, - Interval *span); -static inline Timestamp timestamp_offset_multiple(Timestamp base, Interval *unit, - int64 mul); -static inline TimestampTz timestamptz_offset_internal(TimestampTz timestamp, - Interval *span); +static bool timestamp2timestamptz_safe(Timestamp timestamp, Timestamp *result, Node *escontext); +// static inline Timestamp timestamp_offset_internal(Timestamp timestamp, +// Interval *span); +static inline bool timestamp_offset_internal_safe(Timestamp timestamp, + Interval *span, Timestamp *result, Node* escontext); +// static inline Timestamp timestamp_offset_multiple(Timestamp base, Interval *unit, +// int64 mul); +static inline bool timestamp_offset_multiple_safe(Timestamp base, Interval *unit, + int64 mul, Timestamp *result, Node *escontext); +// static inline TimestampTz timestamptz_offset_internal(TimestampTz timestamp, +// Interval *span); +static inline bool timestamptz_offset_internal_safe(TimestampTz timestamp, + Interval *span, TimestampTz *result, Node *escontext); static inline TimestampTz timestamptz_offset_multiple(TimestampTz base, Interval *unit, int64 mul); +static inline bool timestamptz_offset_multiple_safe(TimestampTz base, + Interval *unit, int64 mul, TimestampTz *result, Node *escontext); /* Handy for comparisons. */ static const Interval IntervalZero = {0, 0, 0}; @@ -88,7 +99,17 @@ static const Interval IntervalZero = {0, 0, 0}; /* common code for timestamptypmodin and timestamptztypmodin */ static int32 -anytimestamp_typmodin(bool istz, ArrayType *ta) +anytimestamp_typmodin_safe(bool istz, ArrayType *ta, int32 *result, Node *escontext); + +// static int32 +// anytimestamp_typmodin(bool istz, ArrayType *ta) +// { + // int32 result; +// (void) anytimestamp_typmodin_safe(istz, ta, &result, NULL); + // return result; +// } +static int32 +anytimestamp_typmodin_safe(bool istz, ArrayType *ta, int32 *result, Node *escontext) { int32 typmod; int32 *tl; @@ -101,12 +122,12 @@ anytimestamp_typmodin(bool istz, ArrayType *ta) * shouldn't allow wrong number of modifiers for TIMESTAMP */ if (n != 1) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid type modifier"))); if (*tl < 0) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("TIMESTAMP(%d)%s precision must not be negative", *tl, (istz ? " WITH TIME ZONE" : "")))); @@ -122,7 +143,8 @@ anytimestamp_typmodin(bool istz, ArrayType *ta) else typmod = *tl; - return typmod; + *result = typmod; + return true; } /* common code for timestamptypmodout and timestamptztypmodout */ @@ -177,7 +199,7 @@ timestamp_in(PG_FUNCTION_ARGS) { case DTK_DATE: if (tm2timestamp(tm, fsec, NULL, &result) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range: \"%s\"", str))); break; @@ -195,7 +217,7 @@ timestamp_in(PG_FUNCTION_ARGS) break; case DTK_INVALID: - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("date/time value \"%s\" is no longer supported", str))); @@ -208,7 +230,8 @@ timestamp_in(PG_FUNCTION_ARGS) TIMESTAMP_NOEND(result); } - AdjustTimestampForTypmod(&result, typmod); + if (!AdjustTimestampForTypmodSafe(&result, typmod, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_TIMESTAMP(result); } @@ -231,7 +254,7 @@ timestamp_out(PG_FUNCTION_ARGS) else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf); else - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -265,7 +288,7 @@ timestamp_recv(PG_FUNCTION_ARGS) timestamp = (Timestamp) pq_getmsgfloat8(buf); if (isnan(timestamp)) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp cannot be NaN"))); #endif @@ -274,11 +297,12 @@ timestamp_recv(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) /* ok */ ; else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); - AdjustTimestampForTypmod(×tamp, typmod); + if (!AdjustTimestampForTypmodSafe(×tamp, typmod, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_TIMESTAMP(timestamp); } @@ -306,7 +330,11 @@ timestamptypmodin(PG_FUNCTION_ARGS) { ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); - PG_RETURN_INT32(anytimestamp_typmodin(false, ta)); + int32 typmod = 0; + if(!anytimestamp_typmodin_safe(false, ta, &typmod, fcinfo->context)) + PG_RETURN_NULL(); + + PG_RETURN_INT32(typmod); } Datum @@ -324,9 +352,19 @@ timestamptypmodout(PG_FUNCTION_ARGS) * interval_bound(timestamp, interval, int, timestamp) * returns timestamp */ -static Timestamp -timestamp_interval_bound_common(Timestamp val, Interval *width, - int32 shift, Timestamp reg) +static bool +timestamp_interval_bound_common_safe(Timestamp val, Interval *width, + int32 shift, Timestamp reg, Timestamp *result, Node *escontext); + +// static Timestamp +// timestamp_interval_bound_common(Timestamp val, Interval *width, +// int32 shift, Timestamp reg) +// { +// return timestamp_interval_bound_common_safe(val, width, shift, reg, NULL); +// } +static bool +timestamp_interval_bound_common_safe(Timestamp val, Interval *width, + int32 shift, Timestamp reg, Timestamp *result, Node *escontext) { int64 index = 0; float8 quo = 0.0; @@ -337,16 +375,18 @@ timestamp_interval_bound_common(Timestamp val, Interval *width, /* Insist on positive, interval width. */ if (interval_cmp_internal(width, &IntervalZero) <= 0) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_INVALID_INTERVAL_WIDTH), errmsg("width of time interval not positive"))); /* Just return non-finite timestamp. */ - if (TIMESTAMP_NOT_FINITE(val)) - return val; + if (TIMESTAMP_NOT_FINITE(val)) { + *result = val; + return true; + } if (TIMESTAMP_NOT_FINITE(reg)) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("bound for registration is not finite"))); @@ -366,8 +406,10 @@ timestamp_interval_bound_common(Timestamp val, Interval *width, if (safety <= 0) elog(ERROR, "interval_bound failed to converge"); - low = timestamp_offset_multiple(reg, width, index); - high = timestamp_offset_multiple(low, width, 1); + if (!timestamp_offset_multiple_safe(reg, width, index, &low, escontext)) + return false; + if (!timestamp_offset_multiple_safe(low, width, 1, &high, escontext)) + return false; Assert(high > low); @@ -405,9 +447,11 @@ timestamp_interval_bound_common(Timestamp val, Interval *width, /* If necessary, shift the interval. */ if (shift) - low = timestamp_offset_multiple(reg, width, index + shift); + if (!timestamp_offset_multiple_safe(reg, width, index + shift, &low, escontext)) + return false; - return low; + *result = low; + return true; } /* @@ -420,10 +464,13 @@ timestamp_interval_bound(PG_FUNCTION_ARGS) Interval *width = PG_GETARG_INTERVAL_P(1); int32 shift = 0; Timestamp reg = SetEpochTimestamp(); + Timestamp result; + + if(!timestamp_interval_bound_common_safe( + val, width, shift, reg, &result, fcinfo->context)) + PG_RETURN_NULL(); - PG_RETURN_TIMESTAMP( - timestamp_interval_bound_common( - val, width, shift, reg)); + PG_RETURN_TIMESTAMP(result); } /* @@ -436,6 +483,7 @@ timestamp_interval_bound_shift(PG_FUNCTION_ARGS) Interval *width; int32 shift = 0; Timestamp reg; + Timestamp result; /* NULL, if either of the first two arguments is NULL. */ if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) @@ -450,9 +498,9 @@ timestamp_interval_bound_shift(PG_FUNCTION_ARGS) reg = SetEpochTimestamp(); - PG_RETURN_TIMESTAMP( - timestamp_interval_bound_common( - val, width, shift, reg)); + if(!timestamp_interval_bound_common_safe( + val, width, shift, reg, &result, fcinfo->context)) + PG_RETURN_NULL(); } /* @@ -465,6 +513,7 @@ timestamp_interval_bound_shift_reg(PG_FUNCTION_ARGS) Interval *width; int32 shift = 0; Timestamp reg; + Timestamp result; /* NULL, if either of the first two arguments is NULL. */ if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) @@ -483,9 +532,9 @@ timestamp_interval_bound_shift_reg(PG_FUNCTION_ARGS) else reg = SetEpochTimestamp(); - PG_RETURN_TIMESTAMP( - timestamp_interval_bound_common( - val, width, shift, reg)); + if(!timestamp_interval_bound_common_safe( + val, width, shift, reg, &result, fcinfo->context)) + PG_RETURN_NULL(); } /* @@ -495,29 +544,42 @@ timestamp_interval_bound_shift_reg(PG_FUNCTION_ARGS) * interval_bound(timestamptz, interval, int, timestamptz) * returns timestamptz */ -static TimestampTz -timestamptz_interval_bound_common(TimestampTz val, Interval *width, - int32 shift, TimestampTz reg) +static bool +timestamptz_interval_bound_common_safe(TimestampTz val, Interval *width, + int32 shift, TimestampTz reg, TimestampTz *result, Node *escontext); + +// static TimestampTz +// timestamptz_interval_bound_common(TimestampTz val, Interval *width, +// int32 shift, TimestampTz reg) +// { +// return timestamptz_interval_bound_common_safe(val, width, shift, reg, NULL); +// } + +static bool +timestamptz_interval_bound_common_safe(TimestampTz val, Interval *width, + int32 shift, TimestampTz reg, TimestampTz *result, Node *escontext) { int64 index = 0; float8 quo = 0.0; Interval span; TimestampTz low; - TimestampTz high; + TimestampTz high = 0; int safety; /* Insist on positive, interval width. */ if (interval_cmp_internal(width, &IntervalZero) <= 0) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_INVALID_INTERVAL_WIDTH), errmsg("width of time interval not positive"))); /* Just return non-finite timestamp. */ - if (TIMESTAMP_NOT_FINITE(val)) - return val; + if (TIMESTAMP_NOT_FINITE(val)){ + *result = val; + return true; + } if (TIMESTAMP_NOT_FINITE(reg)) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("bound for registration is not finite"))); @@ -537,8 +599,10 @@ timestamptz_interval_bound_common(TimestampTz val, Interval *width, if (safety <= 0) elog(ERROR, "interval_bound failed to converge"); - low = timestamptz_offset_multiple(reg, width, index); - high = timestamptz_offset_multiple(low, width, 1); + if(!timestamptz_offset_multiple_safe(reg, width, index, &low, escontext)) + return false; + if(!timestamptz_offset_multiple_safe(low, width, 1, &high, escontext)) + return false; Assert(high > low); @@ -576,9 +640,11 @@ timestamptz_interval_bound_common(TimestampTz val, Interval *width, /* If necessary, shift the interval. */ if (shift) - low = timestamptz_offset_multiple(reg, width, index + shift); + if (!timestamptz_offset_multiple_safe(reg, width, index + shift, &low, escontext)) + return false; - return low; + *result = low; + return true; } /* @@ -591,10 +657,13 @@ timestamptz_interval_bound(PG_FUNCTION_ARGS) Interval *width = PG_GETARG_INTERVAL_P(1); int32 shift = 0; TimestampTz reg = SetEpochTimestamp(); + TimestampTz result; + + if (!timestamptz_interval_bound_common_safe( + val, width, shift, reg, &result, fcinfo->context)) + PG_RETURN_NULL(); - PG_RETURN_TIMESTAMPTZ( - timestamptz_interval_bound_common( - val, width, shift, reg)); + PG_RETURN_TIMESTAMPTZ(result); } @@ -608,6 +677,7 @@ timestamptz_interval_bound_shift(PG_FUNCTION_ARGS) Interval *width; int32 shift = 0; TimestampTz reg; + TimestampTz result; /* NULL, if either of the first two arguments is NULL. */ if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) @@ -622,9 +692,11 @@ timestamptz_interval_bound_shift(PG_FUNCTION_ARGS) reg = SetEpochTimestamp(); - PG_RETURN_TIMESTAMPTZ( - timestamptz_interval_bound_common( - val, width, shift, reg)); + if (!timestamptz_interval_bound_common_safe( + val, width, shift, reg, &result, fcinfo->context)) + PG_RETURN_NULL(); + + PG_RETURN_TIMESTAMPTZ(result); } /* @@ -637,6 +709,7 @@ timestamptz_interval_bound_shift_reg(PG_FUNCTION_ARGS) Interval *width; int32 shift = 0; TimestampTz reg; + TimestampTz result; /* NULL, if either of the first two arguments is NULL. */ if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) @@ -655,9 +728,11 @@ timestamptz_interval_bound_shift_reg(PG_FUNCTION_ARGS) else reg = SetEpochTimestamp(); - PG_RETURN_TIMESTAMPTZ( - timestamptz_interval_bound_common( - val, width, shift, reg)); + if (!timestamptz_interval_bound_common_safe( + val, width, shift, reg, &result, fcinfo->context)) + PG_RETURN_NULL(); + + PG_RETURN_TIMESTAMPTZ(result); } /* timestamp_transform() @@ -684,13 +759,20 @@ timestamp_scale(PG_FUNCTION_ARGS) result = timestamp; - AdjustTimestampForTypmod(&result, typmod); + if (!AdjustTimestampForTypmodSafe(&result, typmod, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_TIMESTAMP(result); } -static void -AdjustTimestampForTypmod(Timestamp *time, int32 typmod) +// static void +// AdjustTimestampForTypmod(Timestamp *time, int32 typmod) +// { +// (void) AdjustTimestampForTypmodSafe(time, typmod, NULL); +// } + +static bool +AdjustTimestampForTypmodSafe(Timestamp *time, int32 typmod, Node *escontext) { #ifdef HAVE_INT64_TIMESTAMP static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = { @@ -728,7 +810,7 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod) && (typmod != -1) && (typmod != MAX_TIMESTAMP_PRECISION)) { if (typmod < 0 || typmod > MAX_TIMESTAMP_PRECISION) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("timestamp(%d) precision must be between %d and %d", typmod, 0, MAX_TIMESTAMP_PRECISION))); @@ -755,6 +837,7 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod) *time = rint((double) *time * TimestampScales[typmod]) / TimestampScales[typmod]; #endif } + return true; } @@ -793,7 +876,7 @@ timestamptz_in(PG_FUNCTION_ARGS) { case DTK_DATE: if (tm2timestamp(tm, fsec, &tz, &result) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range: \"%s\"", str))); break; @@ -811,7 +894,7 @@ timestamptz_in(PG_FUNCTION_ARGS) break; case DTK_INVALID: - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("date/time value \"%s\" is no longer supported", str))); @@ -824,7 +907,8 @@ timestamptz_in(PG_FUNCTION_ARGS) TIMESTAMP_NOEND(result); } - AdjustTimestampForTypmod(&result, typmod); + if (!AdjustTimestampForTypmodSafe(&result, typmod, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_TIMESTAMPTZ(result); } @@ -836,8 +920,17 @@ timestamptz_in(PG_FUNCTION_ARGS) * Note: some code paths update tm->tm_isdst, and some don't; current callers * don't care, so we don't bother being consistent. */ -static int -parse_sane_timezone(struct pg_tm * tm, text *zone) +static bool +parse_sane_timezone_safe(struct pg_tm * tm, text *zone, int *result, Node *escontext); + +// static int +// parse_sane_timezone(struct pg_tm * tm, text *zone) +// { +// return parse_sane_timezone_safe(tm, zone, NULL); +// } + +static bool +parse_sane_timezone_safe(struct pg_tm * tm, text *zone, int *result, Node *escontext) { char tzname[TZ_STRLEN_MAX + 1]; int rt; @@ -861,7 +954,7 @@ parse_sane_timezone(struct pg_tm * tm, text *zone) * position of our input string. */ if (isdigit((unsigned char) *tzname)) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid input syntax for numeric time zone: \"%s\"", tzname), @@ -876,11 +969,11 @@ parse_sane_timezone(struct pg_tm * tm, text *zone) pg_tz *tzp; if (rt == DTERR_TZDISP_OVERFLOW) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("numeric time zone \"%s\" out of range", tzname))); else if (rt != DTERR_BAD_FORMAT) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("time zone \"%s\" not recognized", tzname))); @@ -907,13 +1000,14 @@ parse_sane_timezone(struct pg_tm * tm, text *zone) if (tzp) tz = DetermineTimeZoneOffset(tm, tzp); else - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("time zone \"%s\" not recognized", tzname))); } } - return tz; + *result = tz; + return true; } /* @@ -921,8 +1015,19 @@ parse_sane_timezone(struct pg_tm * tm, text *zone) * workhorse for make_timestamp and make_timestamptz */ static Timestamp -make_timestamp_internal(int year, int month, int day, - int hour, int min, double sec) +make_timestamp_internal_safe(int year, int month, int day, + int hour, int min, double sec, Timestamp *res, Node *escontext); + +// static Timestamp +// make_timestamp_internal(int year, int month, int day, +// int hour, int min, double sec) +// { +// return make_timestamp_internal_safe(year, month, day, hour, min, sec, NULL); +// } + +static Timestamp +make_timestamp_internal_safe(int year, int month, int day, + int hour, int min, double sec, Timestamp *res, Node *escontext) { struct pg_tm tm; TimeOffset date; @@ -941,13 +1046,13 @@ make_timestamp_internal(int year, int month, int day, dterr = ValidateDate(DTK_DATE_M, false, false, false, &tm); if (dterr != 0) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW), errmsg("date field value out of range: %d-%02d-%02d", year, month, day))); if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday)) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("date out of range: %d-%02d-%02d", year, month, day))); @@ -966,7 +1071,7 @@ make_timestamp_internal(int year, int month, int day, hour > HOURS_PER_DAY || /* test for > 24:00:00 */ (hour == HOURS_PER_DAY && (min > 0 || sec > 0))) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW), errmsg("time field value out of range: %d:%02d:%02g", hour, min, sec))); @@ -979,7 +1084,7 @@ make_timestamp_internal(int year, int month, int day, result = date * USECS_PER_DAY + time; /* check for major overflow */ if ((result - time) / USECS_PER_DAY != date) - ereport(ERROR, + ereturn(escontext, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g", year, month, day, @@ -989,7 +1094,7 @@ make_timestamp_internal(int year, int month, int day, /* caution: we want to allow 1999-12-31 24:00:00 */ if ((result < 0 && date > 0) || (result > 0 && date < -1)) - ereport(ERROR, + ereturn(escontext, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g", year, month, day, @@ -999,7 +1104,8 @@ make_timestamp_internal(int year, int month, int day, result = date * SECS_PER_DAY + time; #endif - return result; + *res = result; + return true; } /* @@ -1016,8 +1122,9 @@ make_timestamp(PG_FUNCTION_ARGS) float8 sec = PG_GETARG_FLOAT8(5); Timestamp result; - result = make_timestamp_internal(year, month, mday, - hour, min, sec); + if(!make_timestamp_internal_safe(year, month, mday, + hour, min, sec, &result, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_TIMESTAMP(result); } @@ -1035,11 +1142,16 @@ make_timestamptz(PG_FUNCTION_ARGS) int32 min = PG_GETARG_INT32(4); float8 sec = PG_GETARG_FLOAT8(5); Timestamp result; + TimestampTz result_tz; - result = make_timestamp_internal(year, month, mday, - hour, min, sec); + if (!make_timestamp_internal_safe(year, month, mday, + hour, min, sec, &result, fcinfo->context)) + PG_RETURN_NULL(); + + if (!timestamp2timestamptz_safe(result, &result_tz, fcinfo->context)) + PG_RETURN_NULL(); - PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(result)); + PG_RETURN_TIMESTAMPTZ(result_tz); } /* @@ -1061,15 +1173,17 @@ make_timestamptz_at_timezone(PG_FUNCTION_ARGS) int tz; fsec_t fsec; - timestamp = make_timestamp_internal(year, month, mday, - hour, min, sec); + if(!make_timestamp_internal_safe(year, month, mday, + hour, min, sec, ×tamp, fcinfo->context)) + PG_RETURN_NULL(); if (timestamp2tm(timestamp, NULL, &tt, &fsec, NULL, NULL) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); - tz = parse_sane_timezone(&tt, zone); + if (!parse_sane_timezone_safe(&tt, zone, &tz, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_TIMESTAMPTZ((TimestampTz) dt2local(timestamp, -tz)); } @@ -1094,7 +1208,7 @@ timestamptz_out(PG_FUNCTION_ARGS) else if (timestamp2tm(dt, &tz, tm, &fsec, &tzn, NULL) == 0) EncodeDateTime(tm, fsec, true, tz, tzn, DateStyle, buf); else - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -1133,11 +1247,12 @@ timestamptz_recv(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) /* ok */ ; else if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); - AdjustTimestampForTypmod(×tamp, typmod); + if (!AdjustTimestampForTypmodSafe(×tamp, typmod, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_TIMESTAMPTZ(timestamp); } @@ -1165,7 +1280,11 @@ timestamptztypmodin(PG_FUNCTION_ARGS) { ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); - PG_RETURN_INT32(anytimestamp_typmodin(true, ta)); + int32 typmod = 0; + if(!anytimestamp_typmodin_safe(true, ta, &typmod, fcinfo->context)) + PG_RETURN_NULL(); + + PG_RETURN_INT32(typmod); } Datum @@ -1190,7 +1309,8 @@ timestamptz_scale(PG_FUNCTION_ARGS) result = timestamp; - AdjustTimestampForTypmod(&result, typmod); + if (!AdjustTimestampForTypmodSafe(&result, typmod, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_TIMESTAMPTZ(result); } @@ -1260,13 +1380,13 @@ interval_in(PG_FUNCTION_ARGS) { case DTK_DELTA: if (tm2interval(tm, fsec, result) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); break; case DTK_INVALID: - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("date/time value \"%s\" is no longer supported", str))); break; @@ -1276,7 +1396,8 @@ interval_in(PG_FUNCTION_ARGS) dtype, str); } - AdjustIntervalForTypmod(result, typmod); + if (!AdjustIntervalForTypmodSafe(result, typmod, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_INTERVAL_P(result); } @@ -1291,10 +1412,10 @@ interval_out(PG_FUNCTION_ARGS) char *result; struct pg_tm tt, *tm = &tt; - fsec_t fsec; + fsec_t fsec = 0; char buf[MAXDATELEN + 1]; - if (interval2tm(*span, tm, &fsec) != 0) + if (interval2tm_safe(*span, tm, &fsec, fcinfo->context) != 0) elog(ERROR, "could not convert interval to tm"); EncodeInterval(tm, fsec, IntervalStyle, buf); @@ -1327,7 +1448,8 @@ interval_recv(PG_FUNCTION_ARGS) interval->day = pq_getmsgint(buf, sizeof(interval->day)); interval->month = pq_getmsgint(buf, sizeof(interval->month)); - AdjustIntervalForTypmod(interval, typmod); + if (!AdjustIntervalForTypmodSafe(interval, typmod, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_INTERVAL_P(interval); } @@ -1401,7 +1523,7 @@ intervaltypmodin(PG_FUNCTION_ARGS) /* all OK */ break; default: - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid INTERVAL type modifier"))); } @@ -1417,7 +1539,7 @@ intervaltypmodin(PG_FUNCTION_ARGS) else if (n == 2) { if (tl[1] < 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("INTERVAL(%d) precision must not be negative", tl[1]))); @@ -1434,7 +1556,7 @@ intervaltypmodin(PG_FUNCTION_ARGS) } else { - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid INTERVAL type modifier"))); typmod = 0; /* keep compiler quiet */ @@ -1646,7 +1768,8 @@ interval_scale(PG_FUNCTION_ARGS) result = palloc(sizeof(Interval)); *result = *interval; - AdjustIntervalForTypmod(result, typmod); + if (!AdjustIntervalForTypmodSafe(result, typmod, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_INTERVAL_P(result); } @@ -1655,8 +1778,14 @@ interval_scale(PG_FUNCTION_ARGS) * Adjust interval for specified precision, in both YEAR to SECOND * range and sub-second precision. */ -static void -AdjustIntervalForTypmod(Interval *interval, int32 typmod) +// static void +// AdjustIntervalForTypmod(Interval *interval, int32 typmod) +// { +// (void) AdjustIntervalForTypmodSafe(interval, typmod, NULL); +// } + +static bool +AdjustIntervalForTypmodSafe(Interval *interval, int32 typmod, Node* escontext) { #ifdef HAVE_INT64_TIMESTAMP static const int64 IntervalScales[MAX_INTERVAL_PRECISION + 1] = { @@ -1828,7 +1957,7 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod) if (precision != INTERVAL_FULL_PRECISION) { if (precision < 0 || precision > MAX_INTERVAL_PRECISION) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("interval(%d) precision must be between %d and %d", precision, 0, MAX_INTERVAL_PRECISION))); @@ -1862,6 +1991,7 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod) #endif } } + return true; } /* @@ -1884,7 +2014,7 @@ make_interval(PG_FUNCTION_ARGS) * inputs as well, but it's not entirely clear what limits to apply. */ if (isinf(secs) || isnan(secs)) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); @@ -2375,7 +2505,15 @@ tm2timestamp(struct pg_tm * tm, fsec_t fsec, int *tzp, Timestamp *result) * Convert a interval data type to a tm structure. */ int -interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec) +interval2tm_safe(Interval span, struct pg_tm * tm, fsec_t *fsec, Node* escontext); + +int +interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec) { + return interval2tm_safe(span, tm, fsec, NULL); +} + +int +interval2tm_safe(Interval span, struct pg_tm * tm, fsec_t *fsec, Node* escontext) { TimeOffset time; TimeOffset tfrac; @@ -2390,7 +2528,7 @@ interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec) time -= tfrac * USECS_PER_HOUR; tm->tm_hour = tfrac; if (!SAMESIGN(tm->tm_hour, tfrac)) - ereport(ERROR, + ereturn(escontext, -1, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); tfrac = time / USECS_PER_MINUTE; @@ -2674,7 +2812,8 @@ timestamp_eq_timestamptz(PG_FUNCTION_ARGS) TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt1; - dt1 = timestamp2timestamptz(timestampVal); + if (!timestamp2timestamptz_safe(timestampVal, &dt1, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0); } @@ -2686,7 +2825,8 @@ timestamp_ne_timestamptz(PG_FUNCTION_ARGS) TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt1; - dt1 = timestamp2timestamptz(timestampVal); + if (!timestamp2timestamptz_safe(timestampVal, &dt1, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0); } @@ -2698,7 +2838,8 @@ timestamp_lt_timestamptz(PG_FUNCTION_ARGS) TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt1; - dt1 = timestamp2timestamptz(timestampVal); + if (!timestamp2timestamptz_safe(timestampVal, &dt1, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0); } @@ -2710,7 +2851,8 @@ timestamp_gt_timestamptz(PG_FUNCTION_ARGS) TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt1; - dt1 = timestamp2timestamptz(timestampVal); + if (!timestamp2timestamptz_safe(timestampVal, &dt1, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0); } @@ -2722,7 +2864,8 @@ timestamp_le_timestamptz(PG_FUNCTION_ARGS) TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt1; - dt1 = timestamp2timestamptz(timestampVal); + if (!timestamp2timestamptz_safe(timestampVal, &dt1, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0); } @@ -2734,7 +2877,8 @@ timestamp_ge_timestamptz(PG_FUNCTION_ARGS) TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt1; - dt1 = timestamp2timestamptz(timestampVal); + if (!timestamp2timestamptz_safe(timestampVal, &dt1, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0); } @@ -2746,7 +2890,8 @@ timestamp_cmp_timestamptz(PG_FUNCTION_ARGS) TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); TimestampTz dt1; - dt1 = timestamp2timestamptz(timestampVal); + if (!timestamp2timestamptz_safe(timestampVal, &dt1, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2)); } @@ -2758,7 +2903,8 @@ timestamptz_eq_timestamp(PG_FUNCTION_ARGS) Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); TimestampTz dt2; - dt2 = timestamp2timestamptz(timestampVal); + if (!timestamp2timestamptz_safe(timestampVal, &dt2, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0); } @@ -2770,7 +2916,8 @@ timestamptz_ne_timestamp(PG_FUNCTION_ARGS) Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); TimestampTz dt2; - dt2 = timestamp2timestamptz(timestampVal); + if (!timestamp2timestamptz_safe(timestampVal, &dt2, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0); } @@ -2782,7 +2929,8 @@ timestamptz_lt_timestamp(PG_FUNCTION_ARGS) Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); TimestampTz dt2; - dt2 = timestamp2timestamptz(timestampVal); + if (!timestamp2timestamptz_safe(timestampVal, &dt2, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0); } @@ -2794,7 +2942,8 @@ timestamptz_gt_timestamp(PG_FUNCTION_ARGS) Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); TimestampTz dt2; - dt2 = timestamp2timestamptz(timestampVal); + if (!timestamp2timestamptz_safe(timestampVal, &dt2, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0); } @@ -2806,7 +2955,8 @@ timestamptz_le_timestamp(PG_FUNCTION_ARGS) Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); TimestampTz dt2; - dt2 = timestamp2timestamptz(timestampVal); + if (!timestamp2timestamptz_safe(timestampVal, &dt2, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0); } @@ -2818,7 +2968,8 @@ timestamptz_ge_timestamp(PG_FUNCTION_ARGS) Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); TimestampTz dt2; - dt2 = timestamp2timestamptz(timestampVal); + if (!timestamp2timestamptz_safe(timestampVal, &dt2, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0); } @@ -2830,7 +2981,8 @@ timestamptz_cmp_timestamp(PG_FUNCTION_ARGS) Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); TimestampTz dt2; - dt2 = timestamp2timestamptz(timestampVal); + if (!timestamp2timestamptz_safe(timestampVal, &dt2, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2)); } @@ -2952,11 +3104,22 @@ interval_div_internal(Interval *interval1, Interval *interval2, * NB This is nearly identical to to timestamptz_add_internal. * Chances are that any change made here should also be made there. */ -static inline Timestamp -timestamp_offset_internal(Timestamp timestamp, Interval *span) +static inline bool +timestamp_offset_internal_safe(Timestamp timestamp, Interval *span, Timestamp *result, Node *escontext); + +// static inline Timestamp +// timestamp_offset_internal(Timestamp timestamp, Interval *span) +// { +// return timestamp_offset_internal_safe(timestamp, span, NULL); +// } + +static inline bool +timestamp_offset_internal_safe(Timestamp timestamp, Interval *span, Timestamp *result, Node *escontext) { - if (TIMESTAMP_NOT_FINITE(timestamp)) - return timestamp; + if (TIMESTAMP_NOT_FINITE(timestamp)) { + *result = timestamp; + return true; + } if (span->month != 0) { @@ -2965,7 +3128,7 @@ timestamp_offset_internal(Timestamp timestamp, Interval *span) fsec_t fsec = 0; if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -2986,7 +3149,7 @@ timestamp_offset_internal(Timestamp timestamp, Interval *span) tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]); if (tm2timestamp(tm, fsec, NULL, ×tamp) != 0) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); } @@ -2999,7 +3162,7 @@ timestamp_offset_internal(Timestamp timestamp, Interval *span) int julian; if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -3008,20 +3171,29 @@ timestamp_offset_internal(Timestamp timestamp, Interval *span) j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); if (tm2timestamp(tm, fsec, NULL, ×tamp) != 0) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); } timestamp += span->time; - return timestamp; + *result = timestamp; + return true; } /* * Add a multiplied Interval to a Timestamp. */ -static inline Timestamp -timestamp_offset_multiple(Timestamp base, Interval *unit, int64 mul) +// static inline Timestamp +// timestamp_offset_multiple(Timestamp base, Interval *unit, int64 mul) +// { +// Timestamp result; +// (void) timestamp_offset_multiple_safe(base, unit, mul, &result, NULL); +// return result; +// } + +static inline bool +timestamp_offset_multiple_safe(Timestamp base, Interval *unit, int64 mul, Timestamp *result, Node *escontext) { Interval span; @@ -3029,7 +3201,10 @@ timestamp_offset_multiple(Timestamp base, Interval *unit, int64 mul) span.month = unit->month * mul; span.day = unit->day * mul; - return timestamp_offset_internal(base, &span); + if (!timestamp_offset_internal_safe(base, &span, result, escontext)) + return false; + + return true; } /* @@ -3046,8 +3221,17 @@ timestamp_offset_multiple(Timestamp base, Interval *unit, int64 mul) * NB This is nearly identical to to timestamp_offset_internal. * Chances are that any change made here should also be made there. */ -static inline TimestampTz -timestamptz_offset_internal(TimestampTz timestamp, Interval *span) +static inline bool +timestamptz_offset_internal_safe(TimestampTz timestamp, Interval *span, TimestampTz *result, Node *escontext); + +// static inline TimestampTz +// timestamptz_offset_internal(TimestampTz timestamp, Interval *span) +// { +// return timestamptz_offset_internal_safe(timestamp, span, NULL); +// } + +static inline bool +timestamptz_offset_internal_safe(TimestampTz timestamp, Interval *span, TimestampTz *result, Node *escontext) { int tz; @@ -3061,7 +3245,7 @@ timestamptz_offset_internal(TimestampTz timestamp, Interval *span) fsec_t fsec = 0; if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -3084,7 +3268,7 @@ timestamptz_offset_internal(TimestampTz timestamp, Interval *span) tz = DetermineTimeZoneOffset(tm, session_timezone); if (tm2timestamp(tm, fsec, &tz, ×tamp) != 0) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); } @@ -3097,7 +3281,7 @@ timestamptz_offset_internal(TimestampTz timestamp, Interval *span) int julian; if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -3108,21 +3292,33 @@ timestamptz_offset_internal(TimestampTz timestamp, Interval *span) tz = DetermineTimeZoneOffset(tm, session_timezone); if (tm2timestamp(tm, fsec, &tz, ×tamp) != 0) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); } timestamp += span->time; - return timestamp; + *result = timestamp; + return true; } /* * Add a multiplied Interval to a TimestampTz. */ -static inline TimestampTz -timestamptz_offset_multiple(TimestampTz base, Interval *unit, int64 mul) +static inline bool +timestamptz_offset_multiple_safe(TimestampTz base, Interval *unit, int64 mul, TimestampTz *result, Node *escontext); + +// static inline TimestampTz +// timestamptz_offset_multiple(TimestampTz base, Interval *unit, int64 mul) +// { +// TimestampTz result; +// (void) timestamptz_offset_multiple_safe(base, unit, &result, NULL); +// return result; +// } + +static inline bool +timestamptz_offset_multiple_safe(TimestampTz base, Interval *unit, int64 mul, TimestampTz *result, Node *escontext) { Interval span; @@ -3130,7 +3326,10 @@ timestamptz_offset_multiple(TimestampTz base, Interval *unit, int64 mul) span.month = unit->month * mul; span.day = unit->day * mul; - return timestamptz_offset_internal(base, &span); + if (!timestamptz_offset_internal_safe(base, &span, result, escontext)) + return false; + + return true; } @@ -3399,7 +3598,7 @@ timestamp_mi(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2)) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("cannot subtract infinite timestamps"))); @@ -3605,7 +3804,8 @@ timestamp_pl_interval(PG_FUNCTION_ARGS) Interval *span = PG_GETARG_INTERVAL_P(1); Timestamp result; - result = timestamp_offset_internal(timestamp, span); + if (!timestamp_offset_internal_safe(timestamp, span, &result, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_TIMESTAMP(result); } @@ -3622,7 +3822,8 @@ timestamp_mi_interval(PG_FUNCTION_ARGS) tspan.day = -span->day; tspan.time = -span->time; - result = timestamp_offset_internal(timestamp, &tspan); + if (!timestamp_offset_internal_safe(timestamp, &tspan, &result, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_TIMESTAMP(result); } @@ -3636,7 +3837,9 @@ timestamptz_pl_interval(PG_FUNCTION_ARGS) { TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0); Interval *span = PG_GETARG_INTERVAL_P(1); - TimestampTz result = timestamptz_offset_internal(timestamp, span); + TimestampTz result = 0; + if (!timestamptz_offset_internal_safe(timestamp, span, &result, fcinfo->context)) + PG_RETURN_NULL(); PG_RETURN_TIMESTAMP(result); } @@ -3647,13 +3850,15 @@ timestamptz_mi_interval(PG_FUNCTION_ARGS) TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0); Interval *span = PG_GETARG_INTERVAL_P(1); Interval tspan; - TimestampTz result; + TimestampTz result = 0; tspan.month = -span->month; tspan.day = -span->day; tspan.time = -span->time; - result = timestamptz_offset_internal(timestamp, &tspan); + if (!timestamptz_offset_internal_safe(timestamp, &tspan, &result, fcinfo->context)) + PG_RETURN_NULL(); + PG_RETURN_TIMESTAMP(result); } @@ -3669,17 +3874,17 @@ interval_um(PG_FUNCTION_ARGS) result->time = -interval->time; /* overflow check copied from int4um */ if (interval->time != 0 && SAMESIGN(result->time, interval->time)) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); result->day = -interval->day; if (interval->day != 0 && SAMESIGN(result->day, interval->day)) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); result->month = -interval->month; if (interval->month != 0 && SAMESIGN(result->month, interval->month)) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); @@ -3729,21 +3934,21 @@ interval_pl(PG_FUNCTION_ARGS) /* overflow check copied from int4pl */ if (SAMESIGN(span1->month, span2->month) && !SAMESIGN(result->month, span1->month)) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); result->day = span1->day + span2->day; if (SAMESIGN(span1->day, span2->day) && !SAMESIGN(result->day, span1->day)) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); result->time = span1->time + span2->time; if (SAMESIGN(span1->time, span2->time) && !SAMESIGN(result->time, span1->time)) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); @@ -3763,21 +3968,21 @@ interval_mi(PG_FUNCTION_ARGS) /* overflow check copied from int4mi */ if (!SAMESIGN(span1->month, span2->month) && !SAMESIGN(result->month, span1->month)) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); result->day = span1->day - span2->day; if (!SAMESIGN(span1->day, span2->day) && !SAMESIGN(result->day, span1->day)) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); result->time = span1->time - span2->time; if (!SAMESIGN(span1->time, span2->time) && !SAMESIGN(result->time, span1->time)) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); @@ -3806,14 +4011,14 @@ interval_mul(PG_FUNCTION_ARGS) result_double = span->month * factor; if (result_double > INT_MAX || result_double < INT_MIN) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); result->month = (int32) result_double; result_double = span->day * factor; if (result_double > INT_MAX || result_double < INT_MIN) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); result->day = (int32) result_double; @@ -3858,7 +4063,7 @@ interval_mul(PG_FUNCTION_ARGS) #ifdef HAVE_INT64_TIMESTAMP result_double = rint(span->time * factor + sec_remainder * USECS_PER_SEC); if (isnan(result_double) || !FLOAT8_FITS_IN_INT64(result_double)) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); result->time = (int64) result_double; @@ -3958,7 +4163,7 @@ interval_div(PG_FUNCTION_ARGS) result = (Interval *) palloc(sizeof(Interval)); if (factor == 0.0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); @@ -4000,7 +4205,7 @@ interval_interval_div(PG_FUNCTION_ARGS) float8 result = 0.0; if (interval_cmp_internal(divisor, &IntervalZero) == 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); @@ -4023,7 +4228,7 @@ interval_interval_mod(PG_FUNCTION_ARGS) Interval *result = (Interval *) palloc0(sizeof(Interval)); if (interval_cmp_internal(divisor, &IntervalZero) == 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); @@ -4305,12 +4510,12 @@ timestamp_age(PG_FUNCTION_ARGS) } if (tm2interval(tm, fsec, result) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); } else - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -4434,12 +4639,12 @@ timestamptz_age(PG_FUNCTION_ARGS) } if (tm2interval(tm, fsec, result) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); } else - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -4500,8 +4705,19 @@ timestamp_li_fraction(Timestamp x, Timestamp x0, Timestamp x1, * li_value(0.0, y0, y1) --> y0 * li_value(1.0, y0, y1) --> y1 */ +bool +timestamp_li_value_safe(float8 f, Timestamp y0, Timestamp y1, Timestamp *result, Node *escontext); + Timestamp timestamp_li_value(float8 f, Timestamp y0, Timestamp y1) +{ + Timestamp result = 0; + (void) timestamp_li_value_safe(f, y0, y1, &result, NULL); + return result; +} + +bool +timestamp_li_value_safe(float8 f, Timestamp y0, Timestamp y1, Timestamp *result, Node *escontext) { Timestamp y; Interval diffy; @@ -4513,10 +4729,14 @@ timestamp_li_value(float8 f, Timestamp y0, Timestamp y1) offset = DatumGetIntervalP(DirectFunctionCall2(interval_mul, IntervalPGetDatum(&diffy), Float8GetDatum(f))); - y = timestamp_offset_internal(y0, offset); + if (!timestamp_offset_internal_safe(y0, offset, result, escontext)) { + pfree(offset); + return false; + } + pfree(offset); - return y; + return true; } /* @@ -4542,11 +4762,22 @@ timestamptz_li_fraction(TimestampTz x, TimestampTz x0, TimestampTz x1, * li_value(0.0, y0, y1) --> y0 * li_value(1.0, y0, y1) --> y1 */ +bool +timestamptz_li_value_safe(float8 f, TimestampTz y0, TimestampTz y1, Timestamp *result, Node *escontext); + Timestamp timestamptz_li_value(float8 f, TimestampTz y0, TimestampTz y1) +{ + Timestamp result; + (void) timestamptz_li_value_safe(f, y0, y1, &result, NULL); + return result; +} + +bool +timestamptz_li_value_safe(float8 f, TimestampTz y0, TimestampTz y1, Timestamp *result, Node *escontext) { /* Internally identical to Timestamp */ - return timestamp_li_value(f, y0, y1); + return timestamptz_li_value_safe(f, y0, y1, result, escontext); } /*---------------------------------------------------------- @@ -4582,7 +4813,7 @@ timestamp_trunc(PG_FUNCTION_ARGS) if (type == UNITS) { if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -4671,7 +4902,7 @@ timestamp_trunc(PG_FUNCTION_ARGS) break; default: - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("timestamp units \"%s\" not supported", lowunits))); @@ -4679,13 +4910,13 @@ timestamp_trunc(PG_FUNCTION_ARGS) } if (tm2timestamp(tm, fsec, NULL, &result) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); } else { - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("timestamp units \"%s\" not recognized", lowunits))); @@ -4725,7 +4956,7 @@ timestamptz_trunc(PG_FUNCTION_ARGS) if (type == UNITS) { if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -4825,7 +5056,7 @@ timestamptz_trunc(PG_FUNCTION_ARGS) break; default: - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("timestamp with time zone units \"%s\" not " "supported", lowunits))); @@ -4836,13 +5067,13 @@ timestamptz_trunc(PG_FUNCTION_ARGS) tz = DetermineTimeZoneOffset(tm, session_timezone); if (tm2timestamp(tm, fsec, &tz, &result) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); } else { - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("timestamp with time zone units \"%s\" not recognized", lowunits))); @@ -4878,7 +5109,7 @@ interval_trunc(PG_FUNCTION_ARGS) if (type == UNITS) { - if (interval2tm(*interval, tm, &fsec) == 0) + if (interval2tm_safe(*interval, tm, &fsec, fcinfo->context) == 0) { switch (val) { @@ -4932,20 +5163,20 @@ interval_trunc(PG_FUNCTION_ARGS) default: if (val == DTK_WEEK) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("interval units \"%s\" not supported " "because months usually have fractional weeks", lowunits))); else - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("interval units \"%s\" not supported", lowunits))); } if (tm2interval(tm, fsec, result) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("interval out of range"))); } @@ -4954,7 +5185,7 @@ interval_trunc(PG_FUNCTION_ARGS) } else { - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("interval units \"%s\" not recognized", lowunits))); @@ -5170,7 +5401,7 @@ timestamp_part(PG_FUNCTION_ARGS) if (type == UNITS) { if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -5302,7 +5533,7 @@ timestamp_part(PG_FUNCTION_ARGS) case DTK_TZ_MINUTE: case DTK_TZ_HOUR: default: - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("timestamp units \"%s\" not supported", lowunits))); @@ -5322,7 +5553,7 @@ timestamp_part(PG_FUNCTION_ARGS) break; default: - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("timestamp units \"%s\" not supported", lowunits))); @@ -5332,7 +5563,7 @@ timestamp_part(PG_FUNCTION_ARGS) } else { - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("timestamp units \"%s\" not recognized", lowunits))); result = 0; @@ -5376,7 +5607,7 @@ timestamptz_part(PG_FUNCTION_ARGS) if (type == UNITS) { if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); @@ -5508,7 +5739,7 @@ timestamptz_part(PG_FUNCTION_ARGS) break; default: - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("timestamp with time zone units \"%s\" not supported", lowunits))); @@ -5529,7 +5760,7 @@ timestamptz_part(PG_FUNCTION_ARGS) break; default: - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("timestamp with time zone units \"%s\" not supported", lowunits))); @@ -5538,7 +5769,7 @@ timestamptz_part(PG_FUNCTION_ARGS) } else { - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("timestamp with time zone units \"%s\" not recognized", lowunits))); @@ -5576,7 +5807,7 @@ interval_part(PG_FUNCTION_ARGS) if (type == UNITS) { - if (interval2tm(*interval, tm, &fsec) == 0) + if (interval2tm_safe(*interval, tm, &fsec, fcinfo->context) == 0) { switch (val) { @@ -5644,7 +5875,7 @@ interval_part(PG_FUNCTION_ARGS) break; default: - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("interval units \"%s\" not supported", lowunits))); @@ -5671,7 +5902,7 @@ interval_part(PG_FUNCTION_ARGS) } else { - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("interval units \"%s\" not recognized", lowunits))); @@ -5735,7 +5966,7 @@ timestamp_zone(PG_FUNCTION_ARGS) { /* dynamic-offset abbreviation, resolve using specified time */ if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); tz = -DetermineTimeZoneAbbrevOffset(&tm, tzname, tzp); @@ -5749,19 +5980,19 @@ timestamp_zone(PG_FUNCTION_ARGS) { /* Apply the timezone change */ if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); tz = DetermineTimeZoneOffset(&tm, tzp); if (tm2timestamp(&tm, fsec, &tz, &result) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not convert to time zone \"%s\"", tzname))); } else { - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("time zone \"%s\" not recognized", tzname))); result = 0; /* keep compiler quiet */ @@ -5786,7 +6017,7 @@ timestamp_izone(PG_FUNCTION_ARGS) PG_RETURN_TIMESTAMPTZ(timestamp); if (zone->month != 0 || zone->day != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("interval time zone \"%s\" must not include months or days", DatumGetCString(DirectFunctionCall1(interval_out, @@ -5810,12 +6041,25 @@ Datum timestamp_timestamptz(PG_FUNCTION_ARGS) { Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + TimestampTz result; + + if (!timestamp2timestamptz_safe(timestamp, &result, fcinfo->context)) + PG_RETURN_NULL(); - PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp)); + PG_RETURN_TIMESTAMPTZ(result); } -static TimestampTz -timestamp2timestamptz(Timestamp timestamp) +static bool +timestamp2timestamptz_safe(Timestamp timestamp, TimestampTz *res, Node *escontext); + +// static TimestampTz +// timestamp2timestamptz(Timestamp timestamp) +// { +// return timestamp2timestamptz_safe(timestamp, NULL); +// } + +static bool +timestamp2timestamptz_safe(Timestamp timestamp, TimestampTz *res, Node *escontext) { TimestampTz result; struct pg_tm tt, @@ -5828,19 +6072,20 @@ timestamp2timestamptz(Timestamp timestamp) else { if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); tz = DetermineTimeZoneOffset(tm, session_timezone); if (tm2timestamp(tm, fsec, &tz, &result) != 0) - ereport(ERROR, + ereturn(escontext, false, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); } - return result; + *res = result; + return true; } /* timestamptz_timestamp() @@ -5861,11 +6106,11 @@ timestamptz_timestamp(PG_FUNCTION_ARGS) else { if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); if (tm2timestamp(tm, fsec, NULL, &result) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); } @@ -5934,18 +6179,18 @@ timestamptz_zone(PG_FUNCTION_ARGS) fsec_t fsec = 0; if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); if (tm2timestamp(&tm, fsec, NULL, &result) != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not convert to time zone \"%s\"", tzname))); } else { - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("time zone \"%s\" not recognized", tzname))); result = 0; /* keep compiler quiet */ @@ -5971,7 +6216,7 @@ timestamptz_izone(PG_FUNCTION_ARGS) PG_RETURN_TIMESTAMP(timestamp); if (zone->month != 0 || zone->day != 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("interval time zone \"%s\" must not include months or days", DatumGetCString(DirectFunctionCall1(interval_out, @@ -6032,7 +6277,7 @@ generate_series_timestamp(PG_FUNCTION_ARGS) fctx->step_sign = interval_cmp_internal(&fctx->step, &interval_zero); if (fctx->step_sign == 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("step size cannot equal zero"))); @@ -6113,7 +6358,7 @@ generate_series_timestamptz(PG_FUNCTION_ARGS) fctx->step_sign = interval_cmp_internal(&fctx->step, &interval_zero); if (fctx->step_sign == 0) - ereport(ERROR, + ereturn(fcinfo->context, 0, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("step size cannot equal zero"))); diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h index 7ee4db00be..aa587db19b 100644 --- a/src/include/utils/timestamp.h +++ b/src/include/utils/timestamp.h @@ -282,6 +282,7 @@ extern int timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm, 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 interval2tm_safe(Interval span, struct pg_tm * tm, fsec_t *fsec, Node *escontext); extern int tm2interval(struct pg_tm * tm, fsec_t fsec, Interval *span); extern Timestamp SetEpochTimestamp(void);