@@ -69,7 +69,8 @@ static int DetermineTimeZoneOffsetInternal(struct pg_tm *tm, pg_tz *tzp,
69
69
static bool DetermineTimeZoneAbbrevOffsetInternal (pg_time_t t ,
70
70
const char * abbr , pg_tz * tzp ,
71
71
int * offset , int * isdst );
72
- static pg_tz * FetchDynamicTimeZone (TimeZoneAbbrevTable * tbl , const datetkn * tp );
72
+ static pg_tz * FetchDynamicTimeZone (TimeZoneAbbrevTable * tbl , const datetkn * tp ,
73
+ DateTimeErrorExtra * extra );
73
74
74
75
75
76
const int day_tab [2 ][13 ] =
@@ -951,6 +952,9 @@ ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
951
952
* Return 0 if full date, 1 if only time, and negative DTERR code if problems.
952
953
* (Currently, all callers treat 1 as an error return too.)
953
954
*
955
+ * Inputs are field[] and ftype[] arrays, of length nf.
956
+ * Other arguments are outputs.
957
+ *
954
958
* External format(s):
955
959
* "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
956
960
* "Fri Feb-7-1997 15:23:27"
@@ -972,7 +976,8 @@ ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
972
976
*/
973
977
int
974
978
DecodeDateTime (char * * field , int * ftype , int nf ,
975
- int * dtype , struct pg_tm * tm , fsec_t * fsec , int * tzp )
979
+ int * dtype , struct pg_tm * tm , fsec_t * fsec , int * tzp ,
980
+ DateTimeErrorExtra * extra )
976
981
{
977
982
int fmask = 0 ,
978
983
tmask ,
@@ -1112,15 +1117,8 @@ DecodeDateTime(char **field, int *ftype, int nf,
1112
1117
namedTz = pg_tzset (field [i ]);
1113
1118
if (!namedTz )
1114
1119
{
1115
- /*
1116
- * We should return an error code instead of
1117
- * ereport'ing directly, but then there is no way
1118
- * to report the bad time zone name.
1119
- */
1120
- ereport (ERROR ,
1121
- (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
1122
- errmsg ("time zone \"%s\" not recognized" ,
1123
- field [i ])));
1120
+ extra -> dtee_timezone = field [i ];
1121
+ return DTERR_BAD_TIMEZONE ;
1124
1122
}
1125
1123
/* we'll apply the zone setting below */
1126
1124
tmask = DTK_M (TZ );
@@ -1376,7 +1374,10 @@ DecodeDateTime(char **field, int *ftype, int nf,
1376
1374
case DTK_STRING :
1377
1375
case DTK_SPECIAL :
1378
1376
/* timezone abbrevs take precedence over built-in tokens */
1379
- type = DecodeTimezoneAbbrev (i , field [i ], & val , & valtz );
1377
+ dterr = DecodeTimezoneAbbrev (i , field [i ],
1378
+ & type , & val , & valtz , extra );
1379
+ if (dterr )
1380
+ return dterr ;
1380
1381
if (type == UNKNOWN_FIELD )
1381
1382
type = DecodeSpecial (i , field [i ], & val );
1382
1383
if (type == IGNORE_DTF )
@@ -1912,6 +1913,9 @@ DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t, const char *abbr, pg_tz *tzp,
1912
1913
* Interpret parsed string as time fields only.
1913
1914
* Returns 0 if successful, DTERR code if bogus input detected.
1914
1915
*
1916
+ * Inputs are field[] and ftype[] arrays, of length nf.
1917
+ * Other arguments are outputs.
1918
+ *
1915
1919
* Note that support for time zone is here for
1916
1920
* SQL TIME WITH TIME ZONE, but it reveals
1917
1921
* bogosity with SQL date/time standards, since
@@ -1922,7 +1926,8 @@ DetermineTimeZoneAbbrevOffsetInternal(pg_time_t t, const char *abbr, pg_tz *tzp,
1922
1926
*/
1923
1927
int
1924
1928
DecodeTimeOnly (char * * field , int * ftype , int nf ,
1925
- int * dtype , struct pg_tm * tm , fsec_t * fsec , int * tzp )
1929
+ int * dtype , struct pg_tm * tm , fsec_t * fsec , int * tzp ,
1930
+ DateTimeErrorExtra * extra )
1926
1931
{
1927
1932
int fmask = 0 ,
1928
1933
tmask ,
@@ -2018,15 +2023,8 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
2018
2023
namedTz = pg_tzset (field [i ]);
2019
2024
if (!namedTz )
2020
2025
{
2021
- /*
2022
- * We should return an error code instead of
2023
- * ereport'ing directly, but then there is no way
2024
- * to report the bad time zone name.
2025
- */
2026
- ereport (ERROR ,
2027
- (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
2028
- errmsg ("time zone \"%s\" not recognized" ,
2029
- field [i ])));
2026
+ extra -> dtee_timezone = field [i ];
2027
+ return DTERR_BAD_TIMEZONE ;
2030
2028
}
2031
2029
/* we'll apply the zone setting below */
2032
2030
ftype [i ] = DTK_TZ ;
@@ -2278,7 +2276,10 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
2278
2276
case DTK_STRING :
2279
2277
case DTK_SPECIAL :
2280
2278
/* timezone abbrevs take precedence over built-in tokens */
2281
- type = DecodeTimezoneAbbrev (i , field [i ], & val , & valtz );
2279
+ dterr = DecodeTimezoneAbbrev (i , field [i ],
2280
+ & type , & val , & valtz , extra );
2281
+ if (dterr )
2282
+ return dterr ;
2282
2283
if (type == UNKNOWN_FIELD )
2283
2284
type = DecodeSpecial (i , field [i ], & val );
2284
2285
if (type == IGNORE_DTF )
@@ -3211,22 +3212,28 @@ DecodeTimezone(const char *str, int *tzp)
3211
3212
/* DecodeTimezoneAbbrev()
3212
3213
* Interpret string as a timezone abbreviation, if possible.
3213
3214
*
3214
- * Returns an abbreviation type (TZ, DTZ, or DYNTZ), or UNKNOWN_FIELD if
3215
+ * Sets *ftype to an abbreviation type (TZ, DTZ, or DYNTZ), or UNKNOWN_FIELD if
3215
3216
* string is not any known abbreviation. On success, set *offset and *tz to
3216
3217
* represent the UTC offset (for TZ or DTZ) or underlying zone (for DYNTZ).
3217
3218
* Note that full timezone names (such as America/New_York) are not handled
3218
3219
* here, mostly for historical reasons.
3219
3220
*
3221
+ * The function result is 0 or a DTERR code; in the latter case, *extra
3222
+ * is filled as needed. Note that unknown-abbreviation is not considered
3223
+ * an error case. Also note that many callers assume that the DTERR code
3224
+ * is one that DateTimeParseError does not require "str" or "datatype"
3225
+ * strings for.
3226
+ *
3220
3227
* Given string must be lowercased already.
3221
3228
*
3222
3229
* Implement a cache lookup since it is likely that dates
3223
3230
* will be related in format.
3224
3231
*/
3225
3232
int
3226
3233
DecodeTimezoneAbbrev (int field , const char * lowtoken ,
3227
- int * offset , pg_tz * * tz )
3234
+ int * ftype , int * offset , pg_tz * * tz ,
3235
+ DateTimeErrorExtra * extra )
3228
3236
{
3229
- int type ;
3230
3237
const datetkn * tp ;
3231
3238
3232
3239
tp = abbrevcache [field ];
@@ -3241,18 +3248,20 @@ DecodeTimezoneAbbrev(int field, const char *lowtoken,
3241
3248
}
3242
3249
if (tp == NULL )
3243
3250
{
3244
- type = UNKNOWN_FIELD ;
3251
+ * ftype = UNKNOWN_FIELD ;
3245
3252
* offset = 0 ;
3246
3253
* tz = NULL ;
3247
3254
}
3248
3255
else
3249
3256
{
3250
3257
abbrevcache [field ] = tp ;
3251
- type = tp -> type ;
3252
- if (type == DYNTZ )
3258
+ * ftype = tp -> type ;
3259
+ if (tp -> type == DYNTZ )
3253
3260
{
3254
3261
* offset = 0 ;
3255
- * tz = FetchDynamicTimeZone (zoneabbrevtbl , tp );
3262
+ * tz = FetchDynamicTimeZone (zoneabbrevtbl , tp , extra );
3263
+ if (* tz == NULL )
3264
+ return DTERR_BAD_ZONE_ABBREV ;
3256
3265
}
3257
3266
else
3258
3267
{
@@ -3261,7 +3270,7 @@ DecodeTimezoneAbbrev(int field, const char *lowtoken,
3261
3270
}
3262
3271
}
3263
3272
3264
- return type ;
3273
+ return 0 ;
3265
3274
}
3266
3275
3267
3276
@@ -4014,15 +4023,21 @@ DecodeUnits(int field, const char *lowtoken, int *val)
4014
4023
/*
4015
4024
* Report an error detected by one of the datetime input processing routines.
4016
4025
*
4017
- * dterr is the error code, str is the original input string, datatype is
4018
- * the name of the datatype we were trying to accept.
4026
+ * dterr is the error code, and *extra contains any auxiliary info we need
4027
+ * for the error report. extra can be NULL if not needed for the particular
4028
+ * dterr value.
4029
+ *
4030
+ * str is the original input string, and datatype is the name of the datatype
4031
+ * we were trying to accept. (For some DTERR codes, these are not used and
4032
+ * can be NULL.)
4019
4033
*
4020
4034
* Note: it might seem useless to distinguish DTERR_INTERVAL_OVERFLOW and
4021
4035
* DTERR_TZDISP_OVERFLOW from DTERR_FIELD_OVERFLOW, but SQL99 mandates three
4022
4036
* separate SQLSTATE codes, so ...
4023
4037
*/
4024
4038
void
4025
- DateTimeParseError (int dterr , const char * str , const char * datatype )
4039
+ DateTimeParseError (int dterr , DateTimeErrorExtra * extra ,
4040
+ const char * str , const char * datatype )
4026
4041
{
4027
4042
switch (dterr )
4028
4043
{
@@ -4052,6 +4067,20 @@ DateTimeParseError(int dterr, const char *str, const char *datatype)
4052
4067
errmsg ("time zone displacement out of range: \"%s\"" ,
4053
4068
str )));
4054
4069
break ;
4070
+ case DTERR_BAD_TIMEZONE :
4071
+ ereport (ERROR ,
4072
+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
4073
+ errmsg ("time zone \"%s\" not recognized" ,
4074
+ extra -> dtee_timezone )));
4075
+ break ;
4076
+ case DTERR_BAD_ZONE_ABBREV :
4077
+ ereport (ERROR ,
4078
+ (errcode (ERRCODE_CONFIG_FILE_ERROR ),
4079
+ errmsg ("time zone \"%s\" not recognized" ,
4080
+ extra -> dtee_timezone ),
4081
+ errdetail ("This time zone name appears in the configuration file for time zone abbreviation \"%s\"." ,
4082
+ extra -> dtee_abbrev )));
4083
+ break ;
4055
4084
case DTERR_BAD_FORMAT :
4056
4085
default :
4057
4086
ereport (ERROR ,
@@ -4880,9 +4909,12 @@ InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl)
4880
4909
4881
4910
/*
4882
4911
* Helper subroutine to locate pg_tz timezone for a dynamic abbreviation.
4912
+ *
4913
+ * On failure, returns NULL and fills *extra for a DTERR_BAD_ZONE_ABBREV error.
4883
4914
*/
4884
4915
static pg_tz *
4885
- FetchDynamicTimeZone (TimeZoneAbbrevTable * tbl , const datetkn * tp )
4916
+ FetchDynamicTimeZone (TimeZoneAbbrevTable * tbl , const datetkn * tp ,
4917
+ DateTimeErrorExtra * extra )
4886
4918
{
4887
4919
DynamicZoneAbbrev * dtza ;
4888
4920
@@ -4896,18 +4928,12 @@ FetchDynamicTimeZone(TimeZoneAbbrevTable *tbl, const datetkn *tp)
4896
4928
if (dtza -> tz == NULL )
4897
4929
{
4898
4930
dtza -> tz = pg_tzset (dtza -> zone );
4899
-
4900
- /*
4901
- * Ideally we'd let the caller ereport instead of doing it here, but
4902
- * then there is no way to report the bad time zone name.
4903
- */
4904
4931
if (dtza -> tz == NULL )
4905
- ereport (ERROR ,
4906
- (errcode (ERRCODE_CONFIG_FILE_ERROR ),
4907
- errmsg ("time zone \"%s\" not recognized" ,
4908
- dtza -> zone ),
4909
- errdetail ("This time zone name appears in the configuration file for time zone abbreviation \"%s\"." ,
4910
- tp -> token )));
4932
+ {
4933
+ /* Ooops, bogus zone name in config file entry */
4934
+ extra -> dtee_timezone = dtza -> zone ;
4935
+ extra -> dtee_abbrev = tp -> token ;
4936
+ }
4911
4937
}
4912
4938
return dtza -> tz ;
4913
4939
}
@@ -4993,10 +5019,14 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS)
4993
5019
{
4994
5020
/* Determine the current meaning of the abbrev */
4995
5021
pg_tz * tzp ;
5022
+ DateTimeErrorExtra extra ;
4996
5023
TimestampTz now ;
4997
5024
int isdst ;
4998
5025
4999
- tzp = FetchDynamicTimeZone (zoneabbrevtbl , tp );
5026
+ tzp = FetchDynamicTimeZone (zoneabbrevtbl , tp , & extra );
5027
+ if (tzp == NULL )
5028
+ DateTimeParseError (DTERR_BAD_ZONE_ABBREV , & extra ,
5029
+ NULL , NULL );
5000
5030
now = GetCurrentTransactionStartTimestamp ();
5001
5031
gmtoffset = - DetermineTimeZoneAbbrevOffsetTS (now ,
5002
5032
tp -> token ,
0 commit comments