diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/access/transam/xlogrecovery.c | 4 | ||||
-rw-r--r-- | src/backend/utils/adt/date.c | 28 | ||||
-rw-r--r-- | src/backend/utils/adt/datetime.c | 124 | ||||
-rw-r--r-- | src/backend/utils/adt/formatting.c | 20 | ||||
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 54 | ||||
-rw-r--r-- | src/include/utils/datetime.h | 32 |
6 files changed, 173 insertions, 89 deletions
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c index 97b882564ff..d5a81f9d83e 100644 --- a/src/backend/access/transam/xlogrecovery.c +++ b/src/backend/access/transam/xlogrecovery.c @@ -4790,12 +4790,14 @@ check_recovery_target_time(char **newval, void **extra, GucSource source) char *field[MAXDATEFIELDS]; int ftype[MAXDATEFIELDS]; char workbuf[MAXDATELEN + MAXDATEFIELDS]; + DateTimeErrorExtra dtextra; TimestampTz timestamp; 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, &dtextra); if (dterr != 0) return false; if (dtype != DTK_DATE) diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 10c11e00db9..3c5a8a69858 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -122,13 +122,15 @@ date_in(PG_FUNCTION_ARGS) char *field[MAXDATEFIELDS]; int ftype[MAXDATEFIELDS]; char workbuf[MAXDATELEN + 1]; + DateTimeErrorExtra extra; dterr = ParseDateTime(str, workbuf, sizeof(workbuf), field, ftype, MAXDATEFIELDS, &nf); if (dterr == 0) - dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp); + dterr = DecodeDateTime(field, ftype, nf, + &dtype, tm, &fsec, &tzp, &extra); if (dterr != 0) - DateTimeParseError(dterr, str, "date"); + DateTimeParseError(dterr, &extra, str, "date"); switch (dtype) { @@ -148,7 +150,7 @@ date_in(PG_FUNCTION_ARGS) PG_RETURN_DATEADT(date); default: - DateTimeParseError(DTERR_BAD_FORMAT, str, "date"); + DateTimeParseError(DTERR_BAD_FORMAT, &extra, str, "date"); break; } @@ -1398,13 +1400,15 @@ time_in(PG_FUNCTION_ARGS) char *field[MAXDATEFIELDS]; int dtype; int ftype[MAXDATEFIELDS]; + DateTimeErrorExtra extra; dterr = ParseDateTime(str, workbuf, sizeof(workbuf), field, ftype, MAXDATEFIELDS, &nf); if (dterr == 0) - dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz); + dterr = DecodeTimeOnly(field, ftype, nf, + &dtype, tm, &fsec, &tz, &extra); if (dterr != 0) - DateTimeParseError(dterr, str, "time"); + DateTimeParseError(dterr, &extra, str, "time"); tm2time(tm, fsec, &result); AdjustTimeForTypmod(&result, typmod); @@ -2284,13 +2288,15 @@ timetz_in(PG_FUNCTION_ARGS) char *field[MAXDATEFIELDS]; int dtype; int ftype[MAXDATEFIELDS]; + DateTimeErrorExtra extra; dterr = ParseDateTime(str, workbuf, sizeof(workbuf), field, ftype, MAXDATEFIELDS, &nf); if (dterr == 0) - dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz); + dterr = DecodeTimeOnly(field, ftype, nf, + &dtype, tm, &fsec, &tz, &extra); if (dterr != 0) - DateTimeParseError(dterr, str, "time with time zone"); + DateTimeParseError(dterr, &extra, str, "time with time zone"); result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); tm2timetz(tm, fsec, tz, result); @@ -3042,9 +3048,11 @@ timetz_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; /* * Look up the requested timezone. First we look in the timezone @@ -3061,7 +3069,9 @@ timetz_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) { diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 6893c1ce09c..84bba97abc8 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -69,7 +69,8 @@ static int DetermineTimeZoneOffsetInternal(struct pg_tm *tm, pg_tz *tzp, static bool DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t, const char *abbr, pg_tz *tzp, int *offset, int *isdst); -static pg_tz *FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp); +static pg_tz *FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp, + DateTimeErrorExtra *extra); const int day_tab[2][13] = @@ -951,6 +952,9 @@ ParseDateTime(const char *timestr, char *workbuf, size_t buflen, * Return 0 if full date, 1 if only time, and negative DTERR code if problems. * (Currently, all callers treat 1 as an error return too.) * + * Inputs are field[] and ftype[] arrays, of length nf. + * Other arguments are outputs. + * * External format(s): * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>" * "Fri Feb-7-1997 15:23:27" @@ -972,7 +976,8 @@ ParseDateTime(const char *timestr, char *workbuf, size_t buflen, */ int DecodeDateTime(char **field, int *ftype, int nf, - int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp) + int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp, + DateTimeErrorExtra *extra) { int fmask = 0, tmask, @@ -1112,15 +1117,8 @@ DecodeDateTime(char **field, int *ftype, int nf, namedTz = pg_tzset(field[i]); if (!namedTz) { - /* - * We should return an error code instead of - * ereport'ing directly, but then there is no way - * to report the bad time zone name. - */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("time zone \"%s\" not recognized", - field[i]))); + extra->dtee_timezone = field[i]; + return DTERR_BAD_TIMEZONE; } /* we'll apply the zone setting below */ tmask = DTK_M(TZ); @@ -1376,7 +1374,10 @@ DecodeDateTime(char **field, int *ftype, int nf, case DTK_STRING: case DTK_SPECIAL: /* timezone abbrevs take precedence over built-in tokens */ - type = DecodeTimezoneAbbrev(i, field[i], &val, &valtz); + dterr = DecodeTimezoneAbbrev(i, field[i], + &type, &val, &valtz, extra); + if (dterr) + return dterr; if (type == UNKNOWN_FIELD) type = DecodeSpecial(i, field[i], &val); if (type == IGNORE_DTF) @@ -1912,6 +1913,9 @@ DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t, const char *abbr, pg_tz *tzp, * Interpret parsed string as time fields only. * Returns 0 if successful, DTERR code if bogus input detected. * + * Inputs are field[] and ftype[] arrays, of length nf. + * Other arguments are outputs. + * * Note that support for time zone is here for * SQL TIME WITH TIME ZONE, but it reveals * bogosity with SQL date/time standards, since @@ -1922,7 +1926,8 @@ DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t, const char *abbr, pg_tz *tzp, */ int DecodeTimeOnly(char **field, int *ftype, int nf, - int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp) + int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp, + DateTimeErrorExtra *extra) { int fmask = 0, tmask, @@ -2018,15 +2023,8 @@ DecodeTimeOnly(char **field, int *ftype, int nf, namedTz = pg_tzset(field[i]); if (!namedTz) { - /* - * We should return an error code instead of - * ereport'ing directly, but then there is no way - * to report the bad time zone name. - */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("time zone \"%s\" not recognized", - field[i]))); + extra->dtee_timezone = field[i]; + return DTERR_BAD_TIMEZONE; } /* we'll apply the zone setting below */ ftype[i] = DTK_TZ; @@ -2278,7 +2276,10 @@ DecodeTimeOnly(char **field, int *ftype, int nf, case DTK_STRING: case DTK_SPECIAL: /* timezone abbrevs take precedence over built-in tokens */ - type = DecodeTimezoneAbbrev(i, field[i], &val, &valtz); + dterr = DecodeTimezoneAbbrev(i, field[i], + &type, &val, &valtz, extra); + if (dterr) + return dterr; if (type == UNKNOWN_FIELD) type = DecodeSpecial(i, field[i], &val); if (type == IGNORE_DTF) @@ -3211,12 +3212,18 @@ DecodeTimezone(const char *str, int *tzp) /* DecodeTimezoneAbbrev() * Interpret string as a timezone abbreviation, if possible. * - * Returns an abbreviation type (TZ, DTZ, or DYNTZ), or UNKNOWN_FIELD if + * Sets *ftype to an abbreviation type (TZ, DTZ, or DYNTZ), or UNKNOWN_FIELD if * string is not any known abbreviation. On success, set *offset and *tz to * represent the UTC offset (for TZ or DTZ) or underlying zone (for DYNTZ). * Note that full timezone names (such as America/New_York) are not handled * here, mostly for historical reasons. * + * The function result is 0 or a DTERR code; in the latter case, *extra + * is filled as needed. Note that unknown-abbreviation is not considered + * an error case. Also note that many callers assume that the DTERR code + * is one that DateTimeParseError does not require "str" or "datatype" + * strings for. + * * Given string must be lowercased already. * * Implement a cache lookup since it is likely that dates @@ -3224,9 +3231,9 @@ DecodeTimezone(const char *str, int *tzp) */ int DecodeTimezoneAbbrev(int field, const char *lowtoken, - int *offset, pg_tz **tz) + int *ftype, int *offset, pg_tz **tz, + DateTimeErrorExtra *extra) { - int type; const datetkn *tp; tp = abbrevcache[field]; @@ -3241,18 +3248,20 @@ DecodeTimezoneAbbrev(int field, const char *lowtoken, } if (tp == NULL) { - type = UNKNOWN_FIELD; + *ftype = UNKNOWN_FIELD; *offset = 0; *tz = NULL; } else { abbrevcache[field] = tp; - type = tp->type; - if (type == DYNTZ) + *ftype = tp->type; + if (tp->type == DYNTZ) { *offset = 0; - *tz = FetchDynamicTimeZone(zoneabbrevtbl, tp); + *tz = FetchDynamicTimeZone(zoneabbrevtbl, tp, extra); + if (*tz == NULL) + return DTERR_BAD_ZONE_ABBREV; } else { @@ -3261,7 +3270,7 @@ DecodeTimezoneAbbrev(int field, const char *lowtoken, } } - return type; + return 0; } @@ -4014,15 +4023,21 @@ DecodeUnits(int field, const char *lowtoken, int *val) /* * Report an error detected by one of the datetime input processing routines. * - * dterr is the error code, str is the original input string, datatype is - * the name of the datatype we were trying to accept. + * dterr is the error code, and *extra contains any auxiliary info we need + * for the error report. extra can be NULL if not needed for the particular + * dterr value. + * + * str is the original input string, and datatype is the name of the datatype + * we were trying to accept. (For some DTERR codes, these are not used and + * can be NULL.) * * 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, const char *str, const char *datatype) +DateTimeParseError(int dterr, DateTimeErrorExtra *extra, + const char *str, const char *datatype) { switch (dterr) { @@ -4052,6 +4067,20 @@ DateTimeParseError(int dterr, const char *str, const char *datatype) errmsg("time zone displacement out of range: \"%s\"", str))); break; + case DTERR_BAD_TIMEZONE: + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("time zone \"%s\" not recognized", + extra->dtee_timezone))); + break; + case DTERR_BAD_ZONE_ABBREV: + ereport(ERROR, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("time zone \"%s\" not recognized", + extra->dtee_timezone), + errdetail("This time zone name appears in the configuration file for time zone abbreviation \"%s\".", + extra->dtee_abbrev))); + break; case DTERR_BAD_FORMAT: default: ereport(ERROR, @@ -4880,9 +4909,12 @@ InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl) /* * Helper subroutine to locate pg_tz timezone for a dynamic abbreviation. + * + * On failure, returns NULL and fills *extra for a DTERR_BAD_ZONE_ABBREV error. */ static pg_tz * -FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp) +FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp, + DateTimeErrorExtra *extra) { DynamicZoneAbbrev *dtza; @@ -4896,18 +4928,12 @@ FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp) if (dtza->tz == NULL) { dtza->tz = pg_tzset(dtza->zone); - - /* - * Ideally we'd let the caller ereport instead of doing it here, but - * then there is no way to report the bad time zone name. - */ if (dtza->tz == NULL) - ereport(ERROR, - (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("time zone \"%s\" not recognized", - dtza->zone), - errdetail("This time zone name appears in the configuration file for time zone abbreviation \"%s\".", - tp->token))); + { + /* Ooops, bogus zone name in config file entry */ + extra->dtee_timezone = dtza->zone; + extra->dtee_abbrev = tp->token; + } } return dtza->tz; } @@ -4993,10 +5019,14 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS) { /* Determine the current meaning of the abbrev */ pg_tz *tzp; + DateTimeErrorExtra extra; TimestampTz now; int isdst; - tzp = FetchDynamicTimeZone(zoneabbrevtbl, tp); + tzp = FetchDynamicTimeZone(zoneabbrevtbl, tp, &extra); + if (tzp == NULL) + DateTimeParseError(DTERR_BAD_ZONE_ABBREV, &extra, + NULL, NULL); now = GetCurrentTransactionStartTimestamp(); gmtoffset = -DetermineTimeZoneAbbrevOffsetTS(now, tp->token, diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index 311c9e748ba..0d065d8e418 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -4246,10 +4246,12 @@ to_timestamp(PG_FUNCTION_ARGS) /* Use the specified time zone, if any. */ if (tm.tm_zone) { + DateTimeErrorExtra extra; int dterr = DecodeTimezone(tm.tm_zone, &tz); if (dterr) - DateTimeParseError(dterr, text_to_cstring(date_txt), "timestamptz"); + DateTimeParseError(dterr, &extra, text_to_cstring(date_txt), + "timestamptz"); } else tz = DetermineTimeZoneOffset(&tm, session_timezone); @@ -4343,10 +4345,13 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict, if (tm.tm_zone) { + DateTimeErrorExtra extra; int dterr = DecodeTimezone(tm.tm_zone, tz); if (dterr) - DateTimeParseError(dterr, text_to_cstring(date_txt), "timestamptz"); + DateTimeParseError(dterr, &extra, + text_to_cstring(date_txt), + "timestamptz"); } else { @@ -4429,10 +4434,13 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict, if (tm.tm_zone) { + DateTimeErrorExtra extra; int dterr = DecodeTimezone(tm.tm_zone, tz); if (dterr) - RETURN_ERROR(DateTimeParseError(dterr, text_to_cstring(date_txt), "timetz")); + RETURN_ERROR(DateTimeParseError(dterr, &extra, + text_to_cstring(date_txt), + "timetz")); } else { @@ -4781,7 +4789,7 @@ 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, date_str, "timestamp")); + RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL, date_str, "timestamp")); } } @@ -4791,7 +4799,7 @@ 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, date_str, "timestamp")); + RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL, date_str, "timestamp")); } /* Save parsed time-zone into tm->tm_zone if it was specified */ @@ -4802,7 +4810,7 @@ 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, date_str, "timestamp")); + RETURN_ERROR(DateTimeParseError(DTERR_TZDISP_OVERFLOW, NULL, date_str, "timestamp")); } tz = psprintf("%c%02d:%02d", 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) { diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h index 934cb56a3a5..bb70b754491 100644 --- a/src/include/utils/datetime.h +++ b/src/include/utils/datetime.h @@ -276,13 +276,25 @@ extern PGDLLIMPORT const int day_tab[2][13]; * Datetime input parsing routines (ParseDateTime, DecodeDateTime, etc) * return zero or a positive value on success. On failure, they return * one of these negative code values. DateTimeParseError may be used to - * produce a correct ereport. + * produce a suitable error report. For some of these codes, + * DateTimeParseError requires additional information, which is carried + * in struct DateTimeErrorExtra. */ #define DTERR_BAD_FORMAT (-1) #define DTERR_FIELD_OVERFLOW (-2) #define DTERR_MD_FIELD_OVERFLOW (-3) /* triggers hint about DateStyle */ #define DTERR_INTERVAL_OVERFLOW (-4) #define DTERR_TZDISP_OVERFLOW (-5) +#define DTERR_BAD_TIMEZONE (-6) +#define DTERR_BAD_ZONE_ABBREV (-7) + +typedef struct DateTimeErrorExtra +{ + /* Needed for DTERR_BAD_TIMEZONE and DTERR_BAD_ZONE_ABBREV: */ + const char *dtee_timezone; /* incorrect time zone name */ + /* Needed for DTERR_BAD_ZONE_ABBREV: */ + const char *dtee_abbrev; /* relevant time zone abbreviation */ +} DateTimeErrorExtra; extern void GetCurrentDateTime(struct pg_tm *tm); @@ -293,19 +305,20 @@ extern int date2j(int year, int month, int day); extern int ParseDateTime(const char *timestr, char *workbuf, size_t buflen, char **field, int *ftype, int maxfields, int *numfields); -extern int DecodeDateTime(char **field, int *ftype, - int nf, int *dtype, - struct pg_tm *tm, fsec_t *fsec, int *tzp); +extern int DecodeDateTime(char **field, int *ftype, int nf, + int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp, + DateTimeErrorExtra *extra); extern int DecodeTimezone(const char *str, int *tzp); -extern int DecodeTimeOnly(char **field, int *ftype, - int nf, int *dtype, - struct pg_tm *tm, fsec_t *fsec, int *tzp); +extern int DecodeTimeOnly(char **field, int *ftype, int nf, + int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp, + DateTimeErrorExtra *extra); extern int DecodeInterval(char **field, int *ftype, int nf, int range, int *dtype, struct pg_itm_in *itm_in); extern int DecodeISO8601Interval(char *str, int *dtype, struct pg_itm_in *itm_in); -extern void DateTimeParseError(int dterr, const char *str, +extern void DateTimeParseError(int dterr, DateTimeErrorExtra *extra, + const char *str, const char *datatype) pg_attribute_noreturn(); extern int DetermineTimeZoneOffset(struct pg_tm *tm, pg_tz *tzp); @@ -323,7 +336,8 @@ extern int ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc, struct pg_tm *tm); extern int DecodeTimezoneAbbrev(int field, const char *lowtoken, - int *offset, pg_tz **tz); + int *ftype, int *offset, pg_tz **tz, + DateTimeErrorExtra *extra); extern int DecodeSpecial(int field, const char *lowtoken, int *val); extern int DecodeUnits(int field, const char *lowtoken, int *val); |