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 4246a977ba.

Author: Amul Sul <sulamul@gmail.com>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/CAAJ_b95HEmFyzHZfsdPquSHeswcopk8MCG1Q_vn4tVkZ+xxofw@mail.gmail.com
pull/255/head
Michael Paquier 2 weeks ago
parent 5424f4da90
commit d03668ea05
  1. 25
      contrib/btree_gin/btree_gin.c
  2. 209
      src/backend/utils/adt/date.c
  3. 8
      src/include/utils/date.h

@ -7,6 +7,7 @@
#include "access/stratnum.h" #include "access/stratnum.h"
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
#include "nodes/miscnodes.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/date.h" #include "utils/date.h"
#include "utils/float.h" #include "utils/float.h"
@ -496,10 +497,10 @@ cvt_date_timestamp(Datum input)
{ {
DateADT val = DatumGetDateADT(input); DateADT val = DatumGetDateADT(input);
Timestamp result; Timestamp result;
int overflow; ErrorSaveContext escontext = {T_ErrorSaveContext};
result = date2timestamp_opt_overflow(val, &overflow); result = date2timestamp_safe(val, (Node *) &escontext);
/* We can ignore the overflow result, since result is useful as-is */ /* We can ignore errors, since result is useful as-is */
return TimestampGetDatum(result); return TimestampGetDatum(result);
} }
@ -530,11 +531,11 @@ static Datum
cvt_date_timestamptz(Datum input) cvt_date_timestamptz(Datum input)
{ {
DateADT val = DatumGetDateADT(input); DateADT val = DatumGetDateADT(input);
ErrorSaveContext escontext = {T_ErrorSaveContext};
TimestampTz result; TimestampTz result;
int overflow;
result = date2timestamptz_opt_overflow(val, &overflow); result = date2timestamptz_safe(val, (Node *) &escontext);
/* We can ignore the overflow result, since result is useful as-is */ /* We can ignore errors, since result is useful as-is */
return TimestampTzGetDatum(result); return TimestampTzGetDatum(result);
} }
@ -604,11 +605,11 @@ static Datum
cvt_timestamp_date(Datum input) cvt_timestamp_date(Datum input)
{ {
Timestamp val = DatumGetTimestamp(input); Timestamp val = DatumGetTimestamp(input);
ErrorSaveContext escontext = {T_ErrorSaveContext};
DateADT result; DateADT result;
int overflow;
result = timestamp2date_opt_overflow(val, &overflow); result = timestamp2date_safe(val, (Node *) &escontext);
/* We can ignore the overflow result, since result is useful as-is */ /* We can ignore errors, since result is useful as-is */
return DateADTGetDatum(result); return DateADTGetDatum(result);
} }
@ -616,11 +617,11 @@ static Datum
cvt_timestamptz_date(Datum input) cvt_timestamptz_date(Datum input)
{ {
TimestampTz val = DatumGetTimestampTz(input); TimestampTz val = DatumGetTimestampTz(input);
ErrorSaveContext escontext = {T_ErrorSaveContext};
DateADT result; DateADT result;
int overflow;
result = timestamptz2date_opt_overflow(val, &overflow); result = timestamptz2date_safe(val, (Node *) &escontext);
/* We can ignore the overflow result, since result is useful as-is */ /* We can ignore errors, since result is useful as-is */
return DateADTGetDatum(result); return DateADTGetDatum(result);
} }

@ -27,6 +27,7 @@
#include "common/int.h" #include "common/int.h"
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/miscnodes.h"
#include "nodes/supportnodes.h" #include "nodes/supportnodes.h"
#include "parser/scansup.h" #include "parser/scansup.h"
#include "utils/array.h" #include "utils/array.h"
@ -615,24 +616,21 @@ date_mii(PG_FUNCTION_ARGS)
/* /*
* Promote date to timestamp. * 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 escontext is NULL, we throw an out-of-range error (hard error).
* if overflow is NULL, we throw an out-of-range error. * If escontext is not NULL, we return NOBEGIN or NOEND for lower bound or
* if overflow is not NULL, we store +1 or -1 there to indicate the sign * upper bound overflow, respectively, and record a soft error.
* of the overflow, and return the appropriate timestamp infinity.
* *
* Note: *overflow = -1 is actually not possible currently, since both * Note: Lower bound overflow is currently not possible, as both date and
* datatypes have the same lower bound, Julian day zero. * timestamp datatypes share the same lower boundary: Julian day zero.
*/ */
Timestamp Timestamp
date2timestamp_opt_overflow(DateADT dateVal, int *overflow) date2timestamp_safe(DateADT dateVal, Node *escontext)
{ {
Timestamp result; Timestamp result;
if (overflow)
*overflow = 0;
if (DATE_IS_NOBEGIN(dateVal)) if (DATE_IS_NOBEGIN(dateVal))
TIMESTAMP_NOBEGIN(result); TIMESTAMP_NOBEGIN(result);
else if (DATE_IS_NOEND(dateVal)) 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 (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
{ {
if (overflow) TIMESTAMP_NOEND(result);
{ ereturn(escontext, result,
*overflow = 1; (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
TIMESTAMP_NOEND(result); errmsg("date out of range for timestamp")));
return result;
}
else
{
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range for timestamp")));
}
} }
/* date is days since 2000, timestamp is microseconds since same... */ /* date is days since 2000, timestamp is microseconds since same... */
@ -672,30 +662,27 @@ date2timestamp_opt_overflow(DateADT dateVal, int *overflow)
static TimestampTz static TimestampTz
date2timestamp(DateADT dateVal) date2timestamp(DateADT dateVal)
{ {
return date2timestamp_opt_overflow(dateVal, NULL); return date2timestamp_safe(dateVal, NULL);
} }
/* /*
* Promote date to timestamp with time zone. * 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 escontext is NULL, we throw an out-of-range error (hard error).
* if overflow is NULL, we throw an out-of-range error. * If escontext is not NULL, we return NOBEGIN or NOEND for lower bound or
* if overflow is not NULL, we store +1 or -1 there to indicate the sign * upper bound overflow, respectively, and record a soft error.
* of the overflow, and return the appropriate timestamptz infinity.
*/ */
TimestampTz TimestampTz
date2timestamptz_opt_overflow(DateADT dateVal, int *overflow) date2timestamptz_safe(DateADT dateVal, Node *escontext)
{ {
TimestampTz result; TimestampTz result;
struct pg_tm tt, struct pg_tm tt,
*tm = &tt; *tm = &tt;
int tz; int tz;
if (overflow)
*overflow = 0;
if (DATE_IS_NOBEGIN(dateVal)) if (DATE_IS_NOBEGIN(dateVal))
TIMESTAMP_NOBEGIN(result); TIMESTAMP_NOBEGIN(result);
else if (DATE_IS_NOEND(dateVal)) 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 (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
{ {
if (overflow) TIMESTAMP_NOEND(result);
{ ereturn(escontext, result,
*overflow = 1; (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
TIMESTAMP_NOEND(result); errmsg("date out of range for timestamp")));
return result;
}
else
{
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range for timestamp")));
}
} }
j2date(dateVal + POSTGRES_EPOCH_JDATE, j2date(dateVal + POSTGRES_EPOCH_JDATE,
@ -737,25 +716,14 @@ date2timestamptz_opt_overflow(DateADT dateVal, int *overflow)
*/ */
if (!IS_VALID_TIMESTAMP(result)) if (!IS_VALID_TIMESTAMP(result))
{ {
if (overflow) if (result < MIN_TIMESTAMP)
{ TIMESTAMP_NOBEGIN(result);
if (result < MIN_TIMESTAMP)
{
*overflow = -1;
TIMESTAMP_NOBEGIN(result);
}
else
{
*overflow = 1;
TIMESTAMP_NOEND(result);
}
}
else else
{ TIMESTAMP_NOEND(result);
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), ereturn(escontext, result,
errmsg("date out of range for timestamp"))); (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 static TimestampTz
date2timestamptz(DateADT dateVal) 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) date_cmp_timestamp_internal(DateADT dateVal, Timestamp dt2)
{ {
Timestamp dt1; Timestamp dt1;
int overflow; ErrorSaveContext escontext = {T_ErrorSaveContext};
dt1 = date2timestamp_opt_overflow(dateVal, &overflow); dt1 = date2timestamp_safe(dateVal, (Node *) &escontext);
if (overflow > 0) if (escontext.error_occurred)
{ {
Assert(TIMESTAMP_IS_NOEND(dt1)); /* NOBEGIN case cannot occur */
/* dt1 is larger than any finite timestamp, but less than infinity */ /* dt1 is larger than any finite timestamp, but less than infinity */
return TIMESTAMP_IS_NOEND(dt2) ? -1 : +1; return TIMESTAMP_IS_NOEND(dt2) ? -1 : +1;
} }
Assert(overflow == 0); /* -1 case cannot occur */
return timestamp_cmp_internal(dt1, dt2); return timestamp_cmp_internal(dt1, dt2);
} }
@ -888,18 +857,22 @@ int32
date_cmp_timestamptz_internal(DateADT dateVal, TimestampTz dt2) date_cmp_timestamptz_internal(DateADT dateVal, TimestampTz dt2)
{ {
TimestampTz dt1; TimestampTz dt1;
int overflow; ErrorSaveContext escontext = {T_ErrorSaveContext};
dt1 = date2timestamptz_opt_overflow(dateVal, &overflow); dt1 = date2timestamptz_safe(dateVal, (Node *) &escontext);
if (overflow > 0)
{ if (escontext.error_occurred)
/* dt1 is larger than any finite timestamp, but less than infinity */
return TIMESTAMP_IS_NOEND(dt2) ? -1 : +1;
}
if (overflow < 0)
{ {
/* dt1 is less than any finite timestamp, but more than -infinity */ if (TIMESTAMP_IS_NOEND(dt1))
return TIMESTAMP_IS_NOBEGIN(dt2) ? +1 : -1; {
/* 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); return timestamptz_cmp_internal(dt1, dt2);
@ -1364,34 +1337,31 @@ timestamp_date(PG_FUNCTION_ARGS)
Timestamp timestamp = PG_GETARG_TIMESTAMP(0); Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
DateADT result; DateADT result;
result = timestamp2date_opt_overflow(timestamp, NULL); result = timestamp2date_safe(timestamp, NULL);
PG_RETURN_DATEADT(result); PG_RETURN_DATEADT(result);
} }
/* /*
* Convert timestamp to date. * 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 escontext is NULL, we throw an out-of-range error (hard error).
* if overflow is NULL, we throw an out-of-range error. * If escontext is not NULL, we return NOBEGIN or NOEND for lower bound or
* if overflow is not NULL, we store +1 or -1 there to indicate the sign * upper bound overflow, respectively, and record a soft error.
* of the overflow, and return the appropriate date infinity.
* *
* Note: given the ranges of the types, overflow is only possible at * 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 DateADT
timestamp2date_opt_overflow(Timestamp timestamp, int *overflow) timestamp2date_safe(Timestamp timestamp, Node *escontext)
{ {
DateADT result; DateADT result;
struct pg_tm tt, struct pg_tm tt,
*tm = &tt; *tm = &tt;
fsec_t fsec; fsec_t fsec;
if (overflow)
*overflow = 0;
if (TIMESTAMP_IS_NOBEGIN(timestamp)) if (TIMESTAMP_IS_NOBEGIN(timestamp))
DATE_NOBEGIN(result); DATE_NOBEGIN(result);
else if (TIMESTAMP_IS_NOEND(timestamp)) 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 (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
{ {
if (overflow) if (timestamp < 0)
{ DATE_NOBEGIN(result);
if (timestamp < 0) else
{ DATE_NOEND(result); /* not actually reachable */
*overflow = -1;
DATE_NOBEGIN(result); ereturn(escontext, result,
}
else
{
*overflow = 1; /* not actually reachable */
DATE_NOEND(result);
}
return result;
}
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range"))); errmsg("timestamp out of range")));
} }
@ -1450,25 +1411,25 @@ timestamptz_date(PG_FUNCTION_ARGS)
TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
DateADT result; DateADT result;
result = timestamptz2date_opt_overflow(timestamp, NULL); result = timestamptz2date_safe(timestamp, NULL);
PG_RETURN_DATEADT(result); PG_RETURN_DATEADT(result);
} }
/* /*
* Convert timestamptz to date. * 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 escontext is NULL, we throw an out-of-range error (hard error).
* if overflow is NULL, we throw an out-of-range error. * If escontext is not NULL, we return NOBEGIN or NOEND for lower bound or
* if overflow is not NULL, we store +1 or -1 there to indicate the sign * upper bound overflow, respectively, and record a soft error.
* of the overflow, and return the appropriate date infinity.
* *
* Note: given the ranges of the types, overflow is only possible at * 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 DateADT
timestamptz2date_opt_overflow(TimestampTz timestamp, int *overflow) timestamptz2date_safe(TimestampTz timestamp, Node *escontext)
{ {
DateADT result; DateADT result;
struct pg_tm tt, struct pg_tm tt,
@ -1476,9 +1437,6 @@ timestamptz2date_opt_overflow(TimestampTz timestamp, int *overflow)
fsec_t fsec; fsec_t fsec;
int tz; int tz;
if (overflow)
*overflow = 0;
if (TIMESTAMP_IS_NOBEGIN(timestamp)) if (TIMESTAMP_IS_NOBEGIN(timestamp))
DATE_NOBEGIN(result); DATE_NOBEGIN(result);
else if (TIMESTAMP_IS_NOEND(timestamp)) 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 (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
{ {
if (overflow) if (timestamp < 0)
{ DATE_NOBEGIN(result);
if (timestamp < 0) else
{ DATE_NOEND(result); /* not actually reachable */
*overflow = -1;
DATE_NOBEGIN(result); ereturn(escontext, result,
}
else
{
*overflow = 1; /* not actually reachable */
DATE_NOEND(result);
}
return result;
}
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range"))); errmsg("timestamp out of range")));
} }

@ -98,10 +98,10 @@ TimeTzADTPGetDatum(const TimeTzADT *X)
/* date.c */ /* date.c */
extern int32 anytime_typmod_check(bool istz, int32 typmod); extern int32 anytime_typmod_check(bool istz, int32 typmod);
extern double date2timestamp_no_overflow(DateADT dateVal); extern double date2timestamp_no_overflow(DateADT dateVal);
extern Timestamp date2timestamp_opt_overflow(DateADT dateVal, int *overflow); extern Timestamp date2timestamp_safe(DateADT dateVal, Node *escontext);
extern TimestampTz date2timestamptz_opt_overflow(DateADT dateVal, int *overflow); extern TimestampTz date2timestamptz_safe(DateADT dateVal, Node *escontext);
extern DateADT timestamp2date_opt_overflow(Timestamp timestamp, int *overflow); extern DateADT timestamp2date_safe(Timestamp timestamp, Node *escontext);
extern DateADT timestamptz2date_opt_overflow(TimestampTz timestamp, int *overflow); extern DateADT timestamptz2date_safe(TimestampTz timestamp, Node *escontext);
extern int32 date_cmp_timestamp_internal(DateADT dateVal, Timestamp dt2); extern int32 date_cmp_timestamp_internal(DateADT dateVal, Timestamp dt2);
extern int32 date_cmp_timestamptz_internal(DateADT dateVal, TimestampTz dt2); extern int32 date_cmp_timestamptz_internal(DateADT dateVal, TimestampTz dt2);

Loading…
Cancel
Save