From d03668ea0566b53522cf2628ab7aa630247640a4 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Mon, 1 Dec 2025 15:22:20 +0900 Subject: [PATCH] Switch some date/timestamp functions to use the soft error reporting This commit changes some functions related to the data types date and timestamp to use the soft error reporting rather than a custom boolean flag called "overflow", used to let the callers of these functions know if an overflow happens. This results in the removal of some boilerplate code, as it is possible to rely on an error context rather than a custom state, with the possibility to use the error generated inside the functions updated here, if necessary. These functions were suffixed with "_opt_overflow". They are now renamed to use "_safe" as suffix. This work is similar to 4246a977bad6. Author: Amul Sul Reviewed-by: Amit Langote Reviewed-by: Michael Paquier Discussion: https://postgr.es/m/CAAJ_b95HEmFyzHZfsdPquSHeswcopk8MCG1Q_vn4tVkZ+xxofw@mail.gmail.com --- contrib/btree_gin/btree_gin.c | 25 ++-- src/backend/utils/adt/date.c | 209 +++++++++++++--------------------- src/include/utils/date.h | 8 +- 3 files changed, 96 insertions(+), 146 deletions(-) diff --git a/contrib/btree_gin/btree_gin.c b/contrib/btree_gin/btree_gin.c index 8c477d17e22..1a2339a70c8 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,10 +497,10 @@ cvt_date_timestamp(Datum input) { DateADT val = DatumGetDateADT(input); Timestamp result; - int overflow; + ErrorSaveContext escontext = {T_ErrorSaveContext}; - result = date2timestamp_opt_overflow(val, &overflow); - /* We can ignore the overflow result, since result is useful as-is */ + result = date2timestamp_safe(val, (Node *) &escontext); + /* We can ignore errors, since result is useful as-is */ return TimestampGetDatum(result); } @@ -530,11 +531,11 @@ 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); - /* We can ignore the overflow result, since result is useful as-is */ + result = date2timestamptz_safe(val, (Node *) &escontext); + /* We can ignore errors, since result is useful as-is */ return TimestampTzGetDatum(result); } @@ -604,11 +605,11 @@ 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); - /* We can ignore the overflow result, since result is useful as-is */ + result = timestamp2date_safe(val, (Node *) &escontext); + /* We can ignore errors, since result is useful as-is */ return DateADTGetDatum(result); } @@ -616,11 +617,11 @@ 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); - /* We can ignore the overflow result, since result is useful as-is */ + result = timestamptz2date_safe(val, (Node *) &escontext); + /* We can ignore errors, 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..c4b8125dd66 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" @@ -615,24 +616,21 @@ date_mii(PG_FUNCTION_ARGS) /* * Promote date to timestamp. * - * On successful conversion, *overflow is set to zero if it's not NULL. + * If the date falls out of the valid range for the timestamp type, error + * handling proceeds based on escontext. * - * 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. + * If escontext is NULL, we throw an out-of-range error (hard error). + * If escontext is not NULL, we return NOBEGIN or NOEND for lower bound or + * upper bound overflow, respectively, and record a soft error. * - * Note: *overflow = -1 is actually not possible currently, since both - * datatypes have the same lower bound, Julian day zero. + * Note: Lower bound overflow is currently not possible, as both date and + * timestamp datatypes share the same lower boundary: Julian day zero. */ Timestamp -date2timestamp_opt_overflow(DateADT dateVal, int *overflow) +date2timestamp_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 +643,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 +662,27 @@ date2timestamp_opt_overflow(DateADT dateVal, int *overflow) static TimestampTz date2timestamp(DateADT dateVal) { - return date2timestamp_opt_overflow(dateVal, NULL); + return date2timestamp_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 falls out of the valid range for the timestamp type, error + * handling proceeds based on escontext. * - * 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. + * If escontext is NULL, we throw an out-of-range error (hard error). + * If escontext is not NULL, we return NOBEGIN or NOEND for lower bound or + * upper bound overflow, respectively, and record a soft error. */ TimestampTz -date2timestamptz_opt_overflow(DateADT dateVal, int *overflow) +date2timestamptz_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 +695,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 +716,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 +736,7 @@ date2timestamptz_opt_overflow(DateADT dateVal, int *overflow) static TimestampTz date2timestamptz(DateADT dateVal) { - return date2timestamptz_opt_overflow(dateVal, NULL); + return date2timestamptz_safe(dateVal, NULL); } /* @@ -808,15 +776,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_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 +857,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_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 +1337,31 @@ timestamp_date(PG_FUNCTION_ARGS) Timestamp timestamp = PG_GETARG_TIMESTAMP(0); DateADT result; - result = timestamp2date_opt_overflow(timestamp, NULL); + result = timestamp2date_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 falls out of the valid range for the date type, error + * handling proceeds based on escontext. * - * 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. + * If escontext is NULL, we throw an out-of-range error (hard error). + * If escontext is not NULL, we return NOBEGIN or NOEND for lower bound or + * upper bound overflow, respectively, and record a soft error. * * 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. + * the lower bound of the range, but we don't assume that in this code. */ DateADT -timestamp2date_opt_overflow(Timestamp timestamp, int *overflow) +timestamp2date_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 +1370,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 +1411,25 @@ timestamptz_date(PG_FUNCTION_ARGS) TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); DateADT result; - result = timestamptz2date_opt_overflow(timestamp, NULL); + result = timestamptz2date_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 timestamp falls out of the valid range for the date type, error + * handling proceeds based on escontext. * - * 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. + * If escontext is NULL, we throw an out-of-range error (hard error). + * If escontext is not NULL, we return NOBEGIN or NOEND for lower bound or + * upper bound overflow, respectively, and record a soft error. * * 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. + * the lower bound of the range, but we don't assume that in this code. */ DateADT -timestamptz2date_opt_overflow(TimestampTz timestamp, int *overflow) +timestamptz2date_safe(TimestampTz timestamp, Node *escontext) { DateADT result; struct pg_tm tt, @@ -1476,9 +1437,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 +1445,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..7316ac0ff17 100644 --- a/src/include/utils/date.h +++ b/src/include/utils/date.h @@ -98,10 +98,10 @@ 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_safe(DateADT dateVal, Node *escontext); +extern TimestampTz date2timestamptz_safe(DateADT dateVal, Node *escontext); +extern DateADT timestamp2date_safe(Timestamp timestamp, Node *escontext); +extern DateADT timestamptz2date_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.39.5