1
0
mirror of https://github.com/postgres/postgres.git synced 2025-12-22 17:42:17 +03:00

Convert datetime input functions to use "soft" error reporting.

This patch converts the input functions for date, time, timetz,
timestamp, timestamptz, and interval to the new soft-error style.
There's some related stuff in formatting.c that remains to be
cleaned up, but that seems like a separable project.

Discussion: https://postgr.es/m/3bbbb0df-7382-bf87-9737-340ba096e034@postgrespro.ru
This commit is contained in:
Tom Lane
2022-12-09 16:07:49 -05:00
parent 2661469d86
commit c60488b474
17 changed files with 328 additions and 80 deletions

View File

@@ -111,6 +111,7 @@ Datum
date_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
Node *escontext = fcinfo->context;
DateADT date;
fsec_t fsec;
struct pg_tm tt,
@@ -130,7 +131,10 @@ date_in(PG_FUNCTION_ARGS)
dterr = DecodeDateTime(field, ftype, nf,
&dtype, tm, &fsec, &tzp, &extra);
if (dterr != 0)
DateTimeParseError(dterr, &extra, str, "date");
{
DateTimeParseError(dterr, &extra, str, "date", escontext);
PG_RETURN_NULL();
}
switch (dtype)
{
@@ -150,13 +154,13 @@ date_in(PG_FUNCTION_ARGS)
PG_RETURN_DATEADT(date);
default:
DateTimeParseError(DTERR_BAD_FORMAT, &extra, str, "date");
break;
DateTimeParseError(DTERR_BAD_FORMAT, &extra, str, "date", escontext);
PG_RETURN_NULL();
}
/* Prevent overflow in Julian-day routines */
if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
ereport(ERROR,
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range: \"%s\"", str)));
@@ -164,7 +168,7 @@ date_in(PG_FUNCTION_ARGS)
/* Now check for just-out-of-range dates */
if (!IS_VALID_DATE(date))
ereport(ERROR,
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range: \"%s\"", str)));
@@ -1384,11 +1388,11 @@ Datum
time_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
int32 typmod = PG_GETARG_INT32(2);
Node *escontext = fcinfo->context;
TimeADT result;
fsec_t fsec;
struct pg_tm tt,
@@ -1408,7 +1412,10 @@ time_in(PG_FUNCTION_ARGS)
dterr = DecodeTimeOnly(field, ftype, nf,
&dtype, tm, &fsec, &tz, &extra);
if (dterr != 0)
DateTimeParseError(dterr, &extra, str, "time");
{
DateTimeParseError(dterr, &extra, str, "time", escontext);
PG_RETURN_NULL();
}
tm2time(tm, fsec, &result);
AdjustTimeForTypmod(&result, typmod);
@@ -2272,11 +2279,11 @@ Datum
timetz_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
int32 typmod = PG_GETARG_INT32(2);
Node *escontext = fcinfo->context;
TimeTzADT *result;
fsec_t fsec;
struct pg_tm tt,
@@ -2296,7 +2303,11 @@ timetz_in(PG_FUNCTION_ARGS)
dterr = DecodeTimeOnly(field, ftype, nf,
&dtype, tm, &fsec, &tz, &extra);
if (dterr != 0)
DateTimeParseError(dterr, &extra, str, "time with time zone");
{
DateTimeParseError(dterr, &extra, str, "time with time zone",
escontext);
PG_RETURN_NULL();
}
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
tm2timetz(tm, fsec, tz, result);
@@ -3071,7 +3082,7 @@ timetz_zone(PG_FUNCTION_ARGS)
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
if (dterr)
DateTimeParseError(dterr, &extra, NULL, NULL);
DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
if (type == TZ || type == DTZ)
{

View File

@@ -4031,50 +4031,54 @@ DecodeUnits(int field, const char *lowtoken, int *val)
* we were trying to accept. (For some DTERR codes, these are not used and
* can be NULL.)
*
* If escontext points to an ErrorSaveContext node, that is filled instead
* of throwing an error.
*
* Note: it might seem useless to distinguish DTERR_INTERVAL_OVERFLOW and
* DTERR_TZDISP_OVERFLOW from DTERR_FIELD_OVERFLOW, but SQL99 mandates three
* separate SQLSTATE codes, so ...
*/
void
DateTimeParseError(int dterr, DateTimeErrorExtra *extra,
const char *str, const char *datatype)
const char *str, const char *datatype,
Node *escontext)
{
switch (dterr)
{
case DTERR_FIELD_OVERFLOW:
ereport(ERROR,
errsave(escontext,
(errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
errmsg("date/time field value out of range: \"%s\"",
str)));
break;
case DTERR_MD_FIELD_OVERFLOW:
/* <nanny>same as above, but add hint about DateStyle</nanny> */
ereport(ERROR,
errsave(escontext,
(errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
errmsg("date/time field value out of range: \"%s\"",
str),
errhint("Perhaps you need a different \"datestyle\" setting.")));
break;
case DTERR_INTERVAL_OVERFLOW:
ereport(ERROR,
errsave(escontext,
(errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW),
errmsg("interval field value out of range: \"%s\"",
str)));
break;
case DTERR_TZDISP_OVERFLOW:
ereport(ERROR,
errsave(escontext,
(errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
errmsg("time zone displacement out of range: \"%s\"",
str)));
break;
case DTERR_BAD_TIMEZONE:
ereport(ERROR,
errsave(escontext,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("time zone \"%s\" not recognized",
extra->dtee_timezone)));
break;
case DTERR_BAD_ZONE_ABBREV:
ereport(ERROR,
errsave(escontext,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("time zone \"%s\" not recognized",
extra->dtee_timezone),
@@ -4083,7 +4087,7 @@ DateTimeParseError(int dterr, DateTimeErrorExtra *extra,
break;
case DTERR_BAD_FORMAT:
default:
ereport(ERROR,
errsave(escontext,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for type %s: \"%s\"",
datatype, str)));
@@ -5026,7 +5030,7 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS)
tzp = FetchDynamicTimeZone(zoneabbrevtbl, tp, &extra);
if (tzp == NULL)
DateTimeParseError(DTERR_BAD_ZONE_ABBREV, &extra,
NULL, NULL);
NULL, NULL, NULL);
now = GetCurrentTransactionStartTimestamp();
gmtoffset = -DetermineTimeZoneAbbrevOffsetTS(now,
tp->token,

View File

@@ -4251,7 +4251,7 @@ to_timestamp(PG_FUNCTION_ARGS)
if (dterr)
DateTimeParseError(dterr, &extra, text_to_cstring(date_txt),
"timestamptz");
"timestamptz", NULL);
}
else
tz = DetermineTimeZoneOffset(&tm, session_timezone);
@@ -4263,7 +4263,7 @@ to_timestamp(PG_FUNCTION_ARGS)
/* Use the specified fractional precision, if any. */
if (fprec)
AdjustTimestampForTypmod(&result, fprec);
AdjustTimestampForTypmod(&result, fprec, NULL);
PG_RETURN_TIMESTAMP(result);
}
@@ -4351,7 +4351,7 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
if (dterr)
DateTimeParseError(dterr, &extra,
text_to_cstring(date_txt),
"timestamptz");
"timestamptz", NULL);
}
else
{
@@ -4372,7 +4372,7 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamptz out of range"))));
AdjustTimestampForTypmod(&result, *typmod);
AdjustTimestampForTypmod(&result, *typmod, NULL); /* XXX */
*typid = TIMESTAMPTZOID;
return TimestampTzGetDatum(result);
@@ -4386,7 +4386,7 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range"))));
AdjustTimestampForTypmod(&result, *typmod);
AdjustTimestampForTypmod(&result, *typmod, NULL); /* XXX */
*typid = TIMESTAMPOID;
return TimestampGetDatum(result);
@@ -4440,7 +4440,7 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
if (dterr)
RETURN_ERROR(DateTimeParseError(dterr, &extra,
text_to_cstring(date_txt),
"timetz"));
"timetz", NULL));
}
else
{
@@ -4789,7 +4789,8 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
* said DTERR_MD_FIELD_OVERFLOW, because we don't want to print an
* irrelevant hint about datestyle.
*/
RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL, date_str, "timestamp"));
RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
date_str, "timestamp", NULL));
}
}
@@ -4799,7 +4800,8 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
tm->tm_sec < 0 || tm->tm_sec >= SECS_PER_MINUTE ||
*fsec < INT64CONST(0) || *fsec >= USECS_PER_SEC)
{
RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL, date_str, "timestamp"));
RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
date_str, "timestamp", NULL));
}
/* Save parsed time-zone into tm->tm_zone if it was specified */
@@ -4810,7 +4812,8 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
if (tmfc.tzh < 0 || tmfc.tzh > MAX_TZDISP_HOUR ||
tmfc.tzm < 0 || tmfc.tzm >= MINS_PER_HOUR)
{
RETURN_ERROR(DateTimeParseError(DTERR_TZDISP_OVERFLOW, NULL, date_str, "timestamp"));
RETURN_ERROR(DateTimeParseError(DTERR_TZDISP_OVERFLOW, NULL,
date_str, "timestamp", NULL));
}
tz = psprintf("%c%02d:%02d",

View File

@@ -74,7 +74,8 @@ 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 AdjustIntervalForTypmod(Interval *interval, int32 typmod);
static bool AdjustIntervalForTypmod(Interval *interval, int32 typmod,
Node *escontext);
static TimestampTz timestamp2timestamptz(Timestamp timestamp);
static Timestamp timestamptz2timestamp(TimestampTz timestamp);
@@ -145,11 +146,11 @@ Datum
timestamp_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
int32 typmod = PG_GETARG_INT32(2);
Node *escontext = fcinfo->context;
Timestamp result;
fsec_t fsec;
struct pg_tm tt,
@@ -169,13 +170,16 @@ timestamp_in(PG_FUNCTION_ARGS)
dterr = DecodeDateTime(field, ftype, nf,
&dtype, tm, &fsec, &tz, &extra);
if (dterr != 0)
DateTimeParseError(dterr, &extra, str, "timestamp");
{
DateTimeParseError(dterr, &extra, str, "timestamp", escontext);
PG_RETURN_NULL();
}
switch (dtype)
{
case DTK_DATE:
if (tm2timestamp(tm, fsec, NULL, &result) != 0)
ereport(ERROR,
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range: \"%s\"", str)));
break;
@@ -198,7 +202,7 @@ timestamp_in(PG_FUNCTION_ARGS)
TIMESTAMP_NOEND(result);
}
AdjustTimestampForTypmod(&result, typmod);
AdjustTimestampForTypmod(&result, typmod, escontext);
PG_RETURN_TIMESTAMP(result);
}
@@ -257,7 +261,7 @@ timestamp_recv(PG_FUNCTION_ARGS)
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
AdjustTimestampForTypmod(&timestamp, typmod);
AdjustTimestampForTypmod(&timestamp, typmod, NULL);
PG_RETURN_TIMESTAMP(timestamp);
}
@@ -328,17 +332,20 @@ timestamp_scale(PG_FUNCTION_ARGS)
result = timestamp;
AdjustTimestampForTypmod(&result, typmod);
AdjustTimestampForTypmod(&result, typmod, NULL);
PG_RETURN_TIMESTAMP(result);
}
/*
* AdjustTimestampForTypmodError --- round off a timestamp to suit given typmod
* AdjustTimestampForTypmod --- round off a timestamp to suit given typmod
* Works for either timestamp or timestamptz.
*
* Returns true on success, false on failure (if escontext points to an
* ErrorSaveContext; otherwise errors are thrown).
*/
bool
AdjustTimestampForTypmodError(Timestamp *time, int32 typmod, bool *error)
AdjustTimestampForTypmod(Timestamp *time, int32 typmod, Node *escontext)
{
static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {
INT64CONST(1000000),
@@ -364,18 +371,10 @@ AdjustTimestampForTypmodError(Timestamp *time, int32 typmod, bool *error)
&& (typmod != -1) && (typmod != MAX_TIMESTAMP_PRECISION))
{
if (typmod < 0 || typmod > MAX_TIMESTAMP_PRECISION)
{
if (error)
{
*error = true;
return false;
}
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)));
}
if (*time >= INT64CONST(0))
{
@@ -392,12 +391,6 @@ AdjustTimestampForTypmodError(Timestamp *time, int32 typmod, bool *error)
return true;
}
void
AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
{
(void) AdjustTimestampForTypmodError(time, typmod, NULL);
}
/* timestamptz_in()
* Convert a string to internal form.
*/
@@ -405,11 +398,11 @@ Datum
timestamptz_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
int32 typmod = PG_GETARG_INT32(2);
Node *escontext = fcinfo->context;
TimestampTz result;
fsec_t fsec;
struct pg_tm tt,
@@ -429,13 +422,17 @@ timestamptz_in(PG_FUNCTION_ARGS)
dterr = DecodeDateTime(field, ftype, nf,
&dtype, tm, &fsec, &tz, &extra);
if (dterr != 0)
DateTimeParseError(dterr, &extra, str, "timestamp with time zone");
{
DateTimeParseError(dterr, &extra, str, "timestamp with time zone",
escontext);
PG_RETURN_NULL();
}
switch (dtype)
{
case DTK_DATE:
if (tm2timestamp(tm, fsec, &tz, &result) != 0)
ereport(ERROR,
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range: \"%s\"", str)));
break;
@@ -458,7 +455,7 @@ timestamptz_in(PG_FUNCTION_ARGS)
TIMESTAMP_NOEND(result);
}
AdjustTimestampForTypmod(&result, typmod);
AdjustTimestampForTypmod(&result, typmod, escontext);
PG_RETURN_TIMESTAMPTZ(result);
}
@@ -525,7 +522,7 @@ parse_sane_timezone(struct pg_tm *tm, text *zone)
false);
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
if (dterr)
DateTimeParseError(dterr, &extra, NULL, NULL);
DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
if (type == TZ || type == DTZ)
{
@@ -824,7 +821,7 @@ timestamptz_recv(PG_FUNCTION_ARGS)
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
AdjustTimestampForTypmod(&timestamp, typmod);
AdjustTimestampForTypmod(&timestamp, typmod, NULL);
PG_RETURN_TIMESTAMPTZ(timestamp);
}
@@ -873,7 +870,7 @@ timestamptz_scale(PG_FUNCTION_ARGS)
result = timestamp;
AdjustTimestampForTypmod(&result, typmod);
AdjustTimestampForTypmod(&result, typmod, NULL);
PG_RETURN_TIMESTAMPTZ(result);
}
@@ -889,11 +886,11 @@ Datum
interval_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
int32 typmod = PG_GETARG_INT32(2);
Node *escontext = fcinfo->context;
Interval *result;
struct pg_itm_in tt,
*itm_in = &tt;
@@ -931,7 +928,8 @@ interval_in(PG_FUNCTION_ARGS)
{
if (dterr == DTERR_FIELD_OVERFLOW)
dterr = DTERR_INTERVAL_OVERFLOW;
DateTimeParseError(dterr, &extra, str, "interval");
DateTimeParseError(dterr, &extra, str, "interval", escontext);
PG_RETURN_NULL();
}
result = (Interval *) palloc(sizeof(Interval));
@@ -940,7 +938,7 @@ interval_in(PG_FUNCTION_ARGS)
{
case DTK_DELTA:
if (itmin2interval(itm_in, result) != 0)
ereport(ERROR,
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("interval out of range")));
break;
@@ -950,7 +948,7 @@ interval_in(PG_FUNCTION_ARGS)
dtype, str);
}
AdjustIntervalForTypmod(result, typmod);
AdjustIntervalForTypmod(result, typmod, escontext);
PG_RETURN_INTERVAL_P(result);
}
@@ -994,7 +992,7 @@ interval_recv(PG_FUNCTION_ARGS)
interval->day = pq_getmsgint(buf, sizeof(interval->day));
interval->month = pq_getmsgint(buf, sizeof(interval->month));
AdjustIntervalForTypmod(interval, typmod);
AdjustIntervalForTypmod(interval, typmod, NULL);
PG_RETURN_INTERVAL_P(interval);
}
@@ -1318,7 +1316,7 @@ interval_scale(PG_FUNCTION_ARGS)
result = palloc(sizeof(Interval));
*result = *interval;
AdjustIntervalForTypmod(result, typmod);
AdjustIntervalForTypmod(result, typmod, NULL);
PG_RETURN_INTERVAL_P(result);
}
@@ -1326,9 +1324,13 @@ interval_scale(PG_FUNCTION_ARGS)
/*
* Adjust interval for specified precision, in both YEAR to SECOND
* range and sub-second precision.
*
* Returns true on success, false on failure (if escontext points to an
* ErrorSaveContext; otherwise errors are thrown).
*/
static void
AdjustIntervalForTypmod(Interval *interval, int32 typmod)
static bool
AdjustIntervalForTypmod(Interval *interval, int32 typmod,
Node *escontext)
{
static const int64 IntervalScales[MAX_INTERVAL_PRECISION + 1] = {
INT64CONST(1000000),
@@ -1468,7 +1470,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)));
@@ -1489,6 +1491,8 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
}
}
}
return true;
}
/*
@@ -1609,7 +1613,7 @@ current_timestamp(PG_FUNCTION_ARGS)
ts = GetCurrentTransactionStartTimestamp();
if (typmod >= 0)
AdjustTimestampForTypmod(&ts, typmod);
AdjustTimestampForTypmod(&ts, typmod, NULL);
return TimestampTzGetDatum(ts);
}
@@ -1630,7 +1634,7 @@ sql_localtimestamp(PG_FUNCTION_ARGS)
ts = timestamptz2timestamp(GetCurrentTransactionStartTimestamp());
if (typmod >= 0)
AdjustTimestampForTypmod(&ts, typmod);
AdjustTimestampForTypmod(&ts, typmod, NULL);
return TimestampGetDatum(ts);
}
@@ -4324,7 +4328,7 @@ timestamptz_trunc_zone(PG_FUNCTION_ARGS)
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
if (dterr)
DateTimeParseError(dterr, &extra, NULL, NULL);
DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
if (type == TZ || type == DTZ)
{
@@ -5452,7 +5456,7 @@ timestamp_zone(PG_FUNCTION_ARGS)
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
if (dterr)
DateTimeParseError(dterr, &extra, NULL, NULL);
DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
if (type == TZ || type == DTZ)
{
@@ -5708,7 +5712,7 @@ timestamptz_zone(PG_FUNCTION_ARGS)
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
if (dterr)
DateTimeParseError(dterr, &extra, NULL, NULL);
DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
if (type == TZ || type == DTZ)
{