Hi, The attached patch proposes to use soft error reporting infrastructure for the date/timestamp conversion function, which currently depends on integer variables to control error throwing.
This continues the previous refactoring commit [1] where we adopted soft error reporting for some numeric functions. This patch applies the same pattern to the date/timestamp function. The change ensures consistency by utilizing the existing soft error reporting infrastructure. Note that in the patch, I renamed the function by replacing the "no_overflow" extension in the function name with "overflow_safe". Alternatively, we could just use "safe" alone. Suggestions are welcome. 1] https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=4246a977bad6e76c4276a0d52def8a3dced154bb -- Regards, Amul Sul EDB: http://www.enterprisedb.com
From 9d55abe251fe3085069c539a9888643b38ac6b86 Mon Sep 17 00:00:00 2001 From: Amul Sul <[email protected]> Date: Wed, 26 Nov 2025 14:54:03 +0530 Subject: [PATCH] Switch some date-related functions to use soft error reporting. Like 4246a977bad6e76, changing some functions related to the data type date/timestamp to use the soft error reporting rather than a custom boolean flag (called "overflow") that callers of these functions could rely on to bypass the generation of ERROR reports, letting the callers do their own error handling XXX: Replacing the "no_overflow" extension in the function name with "overflow_safe". We could, alternatively, just use "safe" alone. --- contrib/btree_gin/btree_gin.c | 17 +-- src/backend/utils/adt/date.c | 200 +++++++++++----------------------- src/include/utils/date.h | 12 +- 3 files changed, 78 insertions(+), 151 deletions(-) diff --git a/contrib/btree_gin/btree_gin.c b/contrib/btree_gin/btree_gin.c index 8c477d17e22..c07586bf213 100644 --- a/contrib/btree_gin/btree_gin.c +++ b/contrib/btree_gin/btree_gin.c @@ -7,6 +7,7 @@ #include "access/stratnum.h" #include "mb/pg_wchar.h" +#include "nodes/miscnodes.h" #include "utils/builtins.h" #include "utils/date.h" #include "utils/float.h" @@ -496,9 +497,9 @@ cvt_date_timestamp(Datum input) { DateADT val = DatumGetDateADT(input); Timestamp result; - int overflow; + ErrorSaveContext escontext = {T_ErrorSaveContext}; - result = date2timestamp_opt_overflow(val, &overflow); + result = date2timestamp_overflow_safe(val, (Node *) &escontext); /* We can ignore the overflow result, since result is useful as-is */ return TimestampGetDatum(result); } @@ -530,10 +531,10 @@ static Datum cvt_date_timestamptz(Datum input) { DateADT val = DatumGetDateADT(input); + ErrorSaveContext escontext = {T_ErrorSaveContext}; TimestampTz result; - int overflow; - result = date2timestamptz_opt_overflow(val, &overflow); + result = date2timestamptz_overflow_safe(val, (Node *) &escontext); /* We can ignore the overflow result, since result is useful as-is */ return TimestampTzGetDatum(result); } @@ -604,10 +605,10 @@ static Datum cvt_timestamp_date(Datum input) { Timestamp val = DatumGetTimestamp(input); + ErrorSaveContext escontext = {T_ErrorSaveContext}; DateADT result; - int overflow; - result = timestamp2date_opt_overflow(val, &overflow); + result = timestamp2date_overflow_safe(val, (Node *) &escontext); /* We can ignore the overflow result, since result is useful as-is */ return DateADTGetDatum(result); } @@ -616,10 +617,10 @@ static Datum cvt_timestamptz_date(Datum input) { TimestampTz val = DatumGetTimestampTz(input); + ErrorSaveContext escontext = {T_ErrorSaveContext}; DateADT result; - int overflow; - result = timestamptz2date_opt_overflow(val, &overflow); + result = timestamptz2date_overflow_safe(val, (Node *) &escontext); /* We can ignore the overflow result, since result is useful as-is */ return DateADTGetDatum(result); } diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 344f58b92f7..d7477a1fead 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -27,6 +27,7 @@ #include "common/int.h" #include "libpq/pqformat.h" #include "miscadmin.h" +#include "nodes/miscnodes.h" #include "nodes/supportnodes.h" #include "parser/scansup.h" #include "utils/array.h" @@ -613,26 +614,16 @@ date_mii(PG_FUNCTION_ARGS) /* - * Promote date to timestamp. + * Promotes date to timestamp, including soft error reporting capabilities. * - * 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 + * Note: Lower bound overflow is actually not possible currently, since both * datatypes have the same lower bound, Julian day zero. */ Timestamp -date2timestamp_opt_overflow(DateADT dateVal, int *overflow) +date2timestamp_overflow_safe(DateADT dateVal, Node *escontext) { Timestamp result; - if (overflow) - *overflow = 0; - if (DATE_IS_NOBEGIN(dateVal)) TIMESTAMP_NOBEGIN(result); else if (DATE_IS_NOEND(dateVal)) @@ -645,18 +636,10 @@ date2timestamp_opt_overflow(DateADT dateVal, int *overflow) */ if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE)) { - if (overflow) - { - *overflow = 1; - TIMESTAMP_NOEND(result); - return result; - } - else - { - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("date out of range for timestamp"))); - } + TIMESTAMP_NOEND(result); + ereturn(escontext, result, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date out of range for timestamp"))); } /* date is days since 2000, timestamp is microseconds since same... */ @@ -672,30 +655,21 @@ date2timestamp_opt_overflow(DateADT dateVal, int *overflow) static TimestampTz date2timestamp(DateADT dateVal) { - return date2timestamp_opt_overflow(dateVal, NULL); + return date2timestamp_overflow_safe(dateVal, NULL); } /* - * Promote date to timestamp with time zone. - * - * 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. + * Promotes date to timestamp with time zone, including soft error reporting + * capabilities. */ TimestampTz -date2timestamptz_opt_overflow(DateADT dateVal, int *overflow) +date2timestamptz_overflow_safe(DateADT dateVal, Node *escontext) { TimestampTz result; struct pg_tm tt, *tm = &tt; int tz; - if (overflow) - *overflow = 0; - if (DATE_IS_NOBEGIN(dateVal)) TIMESTAMP_NOBEGIN(result); else if (DATE_IS_NOEND(dateVal)) @@ -708,18 +682,10 @@ date2timestamptz_opt_overflow(DateADT dateVal, int *overflow) */ if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE)) { - if (overflow) - { - *overflow = 1; - TIMESTAMP_NOEND(result); - return result; - } - else - { - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("date out of range for timestamp"))); - } + TIMESTAMP_NOEND(result); + ereturn(escontext, result, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date out of range for timestamp"))); } j2date(dateVal + POSTGRES_EPOCH_JDATE, @@ -737,25 +703,14 @@ date2timestamptz_opt_overflow(DateADT dateVal, int *overflow) */ if (!IS_VALID_TIMESTAMP(result)) { - if (overflow) - { - if (result < MIN_TIMESTAMP) - { - *overflow = -1; - TIMESTAMP_NOBEGIN(result); - } - else - { - *overflow = 1; - TIMESTAMP_NOEND(result); - } - } + if (result < MIN_TIMESTAMP) + TIMESTAMP_NOBEGIN(result); else - { - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("date out of range for timestamp"))); - } + TIMESTAMP_NOEND(result); + + ereturn(escontext, result, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date out of range for timestamp"))); } } @@ -768,7 +723,7 @@ date2timestamptz_opt_overflow(DateADT dateVal, int *overflow) static TimestampTz date2timestamptz(DateADT dateVal) { - return date2timestamptz_opt_overflow(dateVal, NULL); + return date2timestamptz_overflow_safe(dateVal, NULL); } /* @@ -808,15 +763,16 @@ int32 date_cmp_timestamp_internal(DateADT dateVal, Timestamp dt2) { Timestamp dt1; - int overflow; + ErrorSaveContext escontext = {T_ErrorSaveContext}; - dt1 = date2timestamp_opt_overflow(dateVal, &overflow); - if (overflow > 0) + dt1 = date2timestamp_overflow_safe(dateVal, (Node *) &escontext); + if (escontext.error_occurred) { + Assert(TIMESTAMP_IS_NOEND(dt1)); /* NOBEGIN case cannot occur */ + /* 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); } @@ -888,18 +844,22 @@ int32 date_cmp_timestamptz_internal(DateADT dateVal, TimestampTz dt2) { TimestampTz dt1; - int overflow; + ErrorSaveContext escontext = {T_ErrorSaveContext}; - 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 = date2timestamptz_overflow_safe(dateVal, (Node *) &escontext); + + if (escontext.error_occurred) { - /* dt1 is less than any finite timestamp, but more than -infinity */ - return TIMESTAMP_IS_NOBEGIN(dt2) ? +1 : -1; + if (TIMESTAMP_IS_NOEND(dt1)) + { + /* dt1 is larger than any finite timestamp, but less than infinity */ + return TIMESTAMP_IS_NOEND(dt2) ? -1 : +1; + } + if (TIMESTAMP_IS_NOBEGIN(dt1)) + { + /* dt1 is less than any finite timestamp, but more than -infinity */ + return TIMESTAMP_IS_NOBEGIN(dt2) ? +1 : -1; + } } return timestamptz_cmp_internal(dt1, dt2); @@ -1364,34 +1324,24 @@ timestamp_date(PG_FUNCTION_ARGS) Timestamp timestamp = PG_GETARG_TIMESTAMP(0); DateADT result; - result = timestamp2date_opt_overflow(timestamp, NULL); + result = timestamp2date_overflow_safe(timestamp, NULL); PG_RETURN_DATEADT(result); } /* - * Convert timestamp to date. - * - * 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 date, 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 date infinity. + * Convert timestamp to date, including soft error reporting capabilities. * * Note: given the ranges of the types, overflow is only possible at * the minimum end of the range, but we don't assume that in this code. */ DateADT -timestamp2date_opt_overflow(Timestamp timestamp, int *overflow) +timestamp2date_overflow_safe(Timestamp timestamp, Node *escontext) { DateADT result; struct pg_tm tt, *tm = &tt; fsec_t fsec; - if (overflow) - *overflow = 0; - if (TIMESTAMP_IS_NOBEGIN(timestamp)) DATE_NOBEGIN(result); else if (TIMESTAMP_IS_NOEND(timestamp)) @@ -1400,21 +1350,12 @@ timestamp2date_opt_overflow(Timestamp timestamp, int *overflow) { if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) { - if (overflow) - { - if (timestamp < 0) - { - *overflow = -1; - DATE_NOBEGIN(result); - } - else - { - *overflow = 1; /* not actually reachable */ - DATE_NOEND(result); - } - return result; - } - ereport(ERROR, + if (timestamp < 0) + DATE_NOBEGIN(result); + else + DATE_NOEND(result); /* not actually reachable */ + + ereturn(escontext, result, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); } @@ -1450,25 +1391,18 @@ timestamptz_date(PG_FUNCTION_ARGS) TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); DateADT result; - result = timestamptz2date_opt_overflow(timestamp, NULL); + result = timestamptz2date_overflow_safe(timestamp, NULL); PG_RETURN_DATEADT(result); } /* - * Convert timestamptz to date. - * - * On successful conversion, *overflow is set to zero if it's not NULL. - * - * If the timestamptz is finite but out of the valid range for date, 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 date infinity. + * Convert timestamptz to date, including soft error reporting capabilities. * * Note: given the ranges of the types, overflow is only possible at * the minimum end of the range, but we don't assume that in this code. */ DateADT -timestamptz2date_opt_overflow(TimestampTz timestamp, int *overflow) +timestamptz2date_overflow_safe(TimestampTz timestamp, Node *escontext) { DateADT result; struct pg_tm tt, @@ -1476,9 +1410,6 @@ timestamptz2date_opt_overflow(TimestampTz timestamp, int *overflow) fsec_t fsec; int tz; - if (overflow) - *overflow = 0; - if (TIMESTAMP_IS_NOBEGIN(timestamp)) DATE_NOBEGIN(result); else if (TIMESTAMP_IS_NOEND(timestamp)) @@ -1487,21 +1418,12 @@ timestamptz2date_opt_overflow(TimestampTz timestamp, int *overflow) { if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) { - if (overflow) - { - if (timestamp < 0) - { - *overflow = -1; - DATE_NOBEGIN(result); - } - else - { - *overflow = 1; /* not actually reachable */ - DATE_NOEND(result); - } - return result; - } - ereport(ERROR, + if (timestamp < 0) + DATE_NOBEGIN(result); + else + DATE_NOEND(result); /* not actually reachable */ + + ereturn(escontext, result, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); } diff --git a/src/include/utils/date.h b/src/include/utils/date.h index abfda0b1ae9..a4064c3a551 100644 --- a/src/include/utils/date.h +++ b/src/include/utils/date.h @@ -98,10 +98,14 @@ TimeTzADTPGetDatum(const TimeTzADT *X) /* date.c */ 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 DateADT timestamp2date_opt_overflow(Timestamp timestamp, int *overflow); -extern DateADT timestamptz2date_opt_overflow(TimestampTz timestamp, int *overflow); +extern Timestamp date2timestamp_overflow_safe(DateADT dateVal, + Node *escontext); +extern TimestampTz date2timestamptz_overflow_safe(DateADT dateVal, + Node *escontext); +extern DateADT timestamp2date_overflow_safe(Timestamp timestamp, + Node *escontext); +extern DateADT timestamptz2date_overflow_safe(TimestampTz timestamp, + Node *escontext); extern int32 date_cmp_timestamp_internal(DateADT dateVal, Timestamp dt2); extern int32 date_cmp_timestamptz_internal(DateADT dateVal, TimestampTz dt2); -- 2.47.1
