aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/transam/xlogrecovery.c4
-rw-r--r--src/backend/utils/adt/date.c28
-rw-r--r--src/backend/utils/adt/datetime.c124
-rw-r--r--src/backend/utils/adt/formatting.c20
-rw-r--r--src/backend/utils/adt/timestamp.c54
-rw-r--r--src/include/utils/datetime.h32
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);