From 2661469d862239ea8b9e3a1cf5352d833f6f0fec Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 9 Dec 2022 13:30:43 -0500 Subject: Allow DateTimeParseError to handle bad-timezone error messages. Pay down some ancient technical debt (dating to commit 022fd9966): fix a couple of places in datetime parsing that were throwing ereport's immediately instead of returning a DTERR code that could be interpreted by DateTimeParseError. The reason for that was that there was no mechanism for passing any auxiliary data (such as a zone name) to DateTimeParseError, and these errors seemed to really need it. Up to now it didn't matter that much just where the error got thrown, but now we'd like to have a hard policy that datetime parse errors get thrown from just the one place. Hence, invent a "DateTimeErrorExtra" struct that can be used to carry any extra values needed for specific DTERR codes. Perhaps in the future somebody will be motivated to use this to improve the specificity of other DateTimeParseError messages, but for now just deal with the timezone-error cases. This is on the way to making the datetime input functions report parse errors softly; but it's really an independent change, so commit separately. Discussion: https://postgr.es/m/3bbbb0df-7382-bf87-9737-340ba096e034@postgrespro.ru --- src/backend/utils/adt/timestamp.c | 54 +++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 17 deletions(-) (limited to 'src/backend/utils/adt/timestamp.c') diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index ef92323fd09..5a98ca1dec7 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -161,13 +161,15 @@ timestamp_in(PG_FUNCTION_ARGS) char *field[MAXDATEFIELDS]; int ftype[MAXDATEFIELDS]; char workbuf[MAXDATELEN + MAXDATEFIELDS]; + DateTimeErrorExtra extra; dterr = ParseDateTime(str, workbuf, sizeof(workbuf), field, ftype, MAXDATEFIELDS, &nf); if (dterr == 0) - dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz); + dterr = DecodeDateTime(field, ftype, nf, + &dtype, tm, &fsec, &tz, &extra); if (dterr != 0) - DateTimeParseError(dterr, str, "timestamp"); + DateTimeParseError(dterr, &extra, str, "timestamp"); switch (dtype) { @@ -419,13 +421,15 @@ timestamptz_in(PG_FUNCTION_ARGS) char *field[MAXDATEFIELDS]; int ftype[MAXDATEFIELDS]; char workbuf[MAXDATELEN + MAXDATEFIELDS]; + DateTimeErrorExtra extra; dterr = ParseDateTime(str, workbuf, sizeof(workbuf), field, ftype, MAXDATEFIELDS, &nf); if (dterr == 0) - dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz); + dterr = DecodeDateTime(field, ftype, nf, + &dtype, tm, &fsec, &tz, &extra); if (dterr != 0) - DateTimeParseError(dterr, str, "timestamp with time zone"); + DateTimeParseError(dterr, &extra, str, "timestamp with time zone"); switch (dtype) { @@ -470,7 +474,7 @@ static int parse_sane_timezone(struct pg_tm *tm, text *zone) { char tzname[TZ_STRLEN_MAX + 1]; - int rt; + int dterr; int tz; text_to_cstring_buffer(zone, tzname, sizeof(tzname)); @@ -497,19 +501,20 @@ parse_sane_timezone(struct pg_tm *tm, text *zone) "numeric time zone", tzname), errhint("Numeric time zones must have \"-\" or \"+\" as first character."))); - rt = DecodeTimezone(tzname, &tz); - if (rt != 0) + dterr = DecodeTimezone(tzname, &tz); + if (dterr != 0) { char *lowzone; int type, val; pg_tz *tzp; + DateTimeErrorExtra extra; - if (rt == DTERR_TZDISP_OVERFLOW) + if (dterr == DTERR_TZDISP_OVERFLOW) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("numeric time zone \"%s\" out of range", tzname))); - else if (rt != DTERR_BAD_FORMAT) + else if (dterr != DTERR_BAD_FORMAT) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("time zone \"%s\" not recognized", tzname))); @@ -518,7 +523,9 @@ parse_sane_timezone(struct pg_tm *tm, text *zone) lowzone = downcase_truncate_identifier(tzname, strlen(tzname), false); - type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp); + dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra); + if (dterr) + DateTimeParseError(dterr, &extra, NULL, NULL); if (type == TZ || type == DTZ) { @@ -897,6 +904,7 @@ interval_in(PG_FUNCTION_ARGS) char *field[MAXDATEFIELDS]; int ftype[MAXDATEFIELDS]; char workbuf[256]; + DateTimeErrorExtra extra; itm_in->tm_year = 0; itm_in->tm_mon = 0; @@ -923,7 +931,7 @@ interval_in(PG_FUNCTION_ARGS) { if (dterr == DTERR_FIELD_OVERFLOW) dterr = DTERR_INTERVAL_OVERFLOW; - DateTimeParseError(dterr, str, "interval"); + DateTimeParseError(dterr, &extra, str, "interval"); } result = (Interval *) palloc(sizeof(Interval)); @@ -4291,9 +4299,11 @@ timestamptz_trunc_zone(PG_FUNCTION_ARGS) TimestampTz result; char tzname[TZ_STRLEN_MAX + 1]; char *lowzone; - int type, + int dterr, + type, val; pg_tz *tzp; + DateTimeErrorExtra extra; /* * timestamptz_zone() doesn't look up the zone for infinite inputs, so we @@ -4312,7 +4322,9 @@ timestamptz_trunc_zone(PG_FUNCTION_ARGS) strlen(tzname), false); - type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp); + dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra); + if (dterr) + DateTimeParseError(dterr, &extra, NULL, NULL); if (type == TZ || type == DTZ) { @@ -5412,9 +5424,11 @@ timestamp_zone(PG_FUNCTION_ARGS) int tz; char tzname[TZ_STRLEN_MAX + 1]; char *lowzone; - int type, + int dterr, + type, val; pg_tz *tzp; + DateTimeErrorExtra extra; struct pg_tm tm; fsec_t fsec; @@ -5436,7 +5450,9 @@ timestamp_zone(PG_FUNCTION_ARGS) strlen(tzname), false); - type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp); + dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra); + if (dterr) + DateTimeParseError(dterr, &extra, NULL, NULL); if (type == TZ || type == DTZ) { @@ -5666,9 +5682,11 @@ timestamptz_zone(PG_FUNCTION_ARGS) int tz; char tzname[TZ_STRLEN_MAX + 1]; char *lowzone; - int type, + int dterr, + type, val; pg_tz *tzp; + DateTimeErrorExtra extra; if (TIMESTAMP_NOT_FINITE(timestamp)) PG_RETURN_TIMESTAMP(timestamp); @@ -5688,7 +5706,9 @@ timestamptz_zone(PG_FUNCTION_ARGS) strlen(tzname), false); - type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp); + dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra); + if (dterr) + DateTimeParseError(dterr, &extra, NULL, NULL); if (type == TZ || type == DTZ) { -- cgit v1.2.3