Skip to content

Commit c60488b

Browse files
committed
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
1 parent 2661469 commit c60488b

17 files changed

+328
-80
lines changed

src/backend/utils/adt/date.c

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ Datum
111111
date_in(PG_FUNCTION_ARGS)
112112
{
113113
char *str = PG_GETARG_CSTRING(0);
114+
Node *escontext = fcinfo->context;
114115
DateADT date;
115116
fsec_t fsec;
116117
struct pg_tm tt,
@@ -130,7 +131,10 @@ date_in(PG_FUNCTION_ARGS)
130131
dterr = DecodeDateTime(field, ftype, nf,
131132
&dtype, tm, &fsec, &tzp, &extra);
132133
if (dterr != 0)
133-
DateTimeParseError(dterr, &extra, str, "date");
134+
{
135+
DateTimeParseError(dterr, &extra, str, "date", escontext);
136+
PG_RETURN_NULL();
137+
}
134138

135139
switch (dtype)
136140
{
@@ -150,21 +154,21 @@ date_in(PG_FUNCTION_ARGS)
150154
PG_RETURN_DATEADT(date);
151155

152156
default:
153-
DateTimeParseError(DTERR_BAD_FORMAT, &extra, str, "date");
154-
break;
157+
DateTimeParseError(DTERR_BAD_FORMAT, &extra, str, "date", escontext);
158+
PG_RETURN_NULL();
155159
}
156160

157161
/* Prevent overflow in Julian-day routines */
158162
if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
159-
ereport(ERROR,
163+
ereturn(escontext, (Datum) 0,
160164
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
161165
errmsg("date out of range: \"%s\"", str)));
162166

163167
date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
164168

165169
/* Now check for just-out-of-range dates */
166170
if (!IS_VALID_DATE(date))
167-
ereport(ERROR,
171+
ereturn(escontext, (Datum) 0,
168172
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
169173
errmsg("date out of range: \"%s\"", str)));
170174

@@ -1384,11 +1388,11 @@ Datum
13841388
time_in(PG_FUNCTION_ARGS)
13851389
{
13861390
char *str = PG_GETARG_CSTRING(0);
1387-
13881391
#ifdef NOT_USED
13891392
Oid typelem = PG_GETARG_OID(1);
13901393
#endif
13911394
int32 typmod = PG_GETARG_INT32(2);
1395+
Node *escontext = fcinfo->context;
13921396
TimeADT result;
13931397
fsec_t fsec;
13941398
struct pg_tm tt,
@@ -1408,7 +1412,10 @@ time_in(PG_FUNCTION_ARGS)
14081412
dterr = DecodeTimeOnly(field, ftype, nf,
14091413
&dtype, tm, &fsec, &tz, &extra);
14101414
if (dterr != 0)
1411-
DateTimeParseError(dterr, &extra, str, "time");
1415+
{
1416+
DateTimeParseError(dterr, &extra, str, "time", escontext);
1417+
PG_RETURN_NULL();
1418+
}
14121419

14131420
tm2time(tm, fsec, &result);
14141421
AdjustTimeForTypmod(&result, typmod);
@@ -2272,11 +2279,11 @@ Datum
22722279
timetz_in(PG_FUNCTION_ARGS)
22732280
{
22742281
char *str = PG_GETARG_CSTRING(0);
2275-
22762282
#ifdef NOT_USED
22772283
Oid typelem = PG_GETARG_OID(1);
22782284
#endif
22792285
int32 typmod = PG_GETARG_INT32(2);
2286+
Node *escontext = fcinfo->context;
22802287
TimeTzADT *result;
22812288
fsec_t fsec;
22822289
struct pg_tm tt,
@@ -2296,7 +2303,11 @@ timetz_in(PG_FUNCTION_ARGS)
22962303
dterr = DecodeTimeOnly(field, ftype, nf,
22972304
&dtype, tm, &fsec, &tz, &extra);
22982305
if (dterr != 0)
2299-
DateTimeParseError(dterr, &extra, str, "time with time zone");
2306+
{
2307+
DateTimeParseError(dterr, &extra, str, "time with time zone",
2308+
escontext);
2309+
PG_RETURN_NULL();
2310+
}
23002311

23012312
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
23022313
tm2timetz(tm, fsec, tz, result);
@@ -3071,7 +3082,7 @@ timetz_zone(PG_FUNCTION_ARGS)
30713082

30723083
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
30733084
if (dterr)
3074-
DateTimeParseError(dterr, &extra, NULL, NULL);
3085+
DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
30753086

30763087
if (type == TZ || type == DTZ)
30773088
{

src/backend/utils/adt/datetime.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4031,50 +4031,54 @@ DecodeUnits(int field, const char *lowtoken, int *val)
40314031
* we were trying to accept. (For some DTERR codes, these are not used and
40324032
* can be NULL.)
40334033
*
4034+
* If escontext points to an ErrorSaveContext node, that is filled instead
4035+
* of throwing an error.
4036+
*
40344037
* Note: it might seem useless to distinguish DTERR_INTERVAL_OVERFLOW and
40354038
* DTERR_TZDISP_OVERFLOW from DTERR_FIELD_OVERFLOW, but SQL99 mandates three
40364039
* separate SQLSTATE codes, so ...
40374040
*/
40384041
void
40394042
DateTimeParseError(int dterr, DateTimeErrorExtra *extra,
4040-
const char *str, const char *datatype)
4043+
const char *str, const char *datatype,
4044+
Node *escontext)
40414045
{
40424046
switch (dterr)
40434047
{
40444048
case DTERR_FIELD_OVERFLOW:
4045-
ereport(ERROR,
4049+
errsave(escontext,
40464050
(errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
40474051
errmsg("date/time field value out of range: \"%s\"",
40484052
str)));
40494053
break;
40504054
case DTERR_MD_FIELD_OVERFLOW:
40514055
/* <nanny>same as above, but add hint about DateStyle</nanny> */
4052-
ereport(ERROR,
4056+
errsave(escontext,
40534057
(errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
40544058
errmsg("date/time field value out of range: \"%s\"",
40554059
str),
40564060
errhint("Perhaps you need a different \"datestyle\" setting.")));
40574061
break;
40584062
case DTERR_INTERVAL_OVERFLOW:
4059-
ereport(ERROR,
4063+
errsave(escontext,
40604064
(errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW),
40614065
errmsg("interval field value out of range: \"%s\"",
40624066
str)));
40634067
break;
40644068
case DTERR_TZDISP_OVERFLOW:
4065-
ereport(ERROR,
4069+
errsave(escontext,
40664070
(errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
40674071
errmsg("time zone displacement out of range: \"%s\"",
40684072
str)));
40694073
break;
40704074
case DTERR_BAD_TIMEZONE:
4071-
ereport(ERROR,
4075+
errsave(escontext,
40724076
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
40734077
errmsg("time zone \"%s\" not recognized",
40744078
extra->dtee_timezone)));
40754079
break;
40764080
case DTERR_BAD_ZONE_ABBREV:
4077-
ereport(ERROR,
4081+
errsave(escontext,
40784082
(errcode(ERRCODE_CONFIG_FILE_ERROR),
40794083
errmsg("time zone \"%s\" not recognized",
40804084
extra->dtee_timezone),
@@ -4083,7 +4087,7 @@ DateTimeParseError(int dterr, DateTimeErrorExtra *extra,
40834087
break;
40844088
case DTERR_BAD_FORMAT:
40854089
default:
4086-
ereport(ERROR,
4090+
errsave(escontext,
40874091
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
40884092
errmsg("invalid input syntax for type %s: \"%s\"",
40894093
datatype, str)));
@@ -5026,7 +5030,7 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS)
50265030
tzp = FetchDynamicTimeZone(zoneabbrevtbl, tp, &extra);
50275031
if (tzp == NULL)
50285032
DateTimeParseError(DTERR_BAD_ZONE_ABBREV, &extra,
5029-
NULL, NULL);
5033+
NULL, NULL, NULL);
50305034
now = GetCurrentTransactionStartTimestamp();
50315035
gmtoffset = -DetermineTimeZoneAbbrevOffsetTS(now,
50325036
tp->token,

src/backend/utils/adt/formatting.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4251,7 +4251,7 @@ to_timestamp(PG_FUNCTION_ARGS)
42514251

42524252
if (dterr)
42534253
DateTimeParseError(dterr, &extra, text_to_cstring(date_txt),
4254-
"timestamptz");
4254+
"timestamptz", NULL);
42554255
}
42564256
else
42574257
tz = DetermineTimeZoneOffset(&tm, session_timezone);
@@ -4263,7 +4263,7 @@ to_timestamp(PG_FUNCTION_ARGS)
42634263

42644264
/* Use the specified fractional precision, if any. */
42654265
if (fprec)
4266-
AdjustTimestampForTypmod(&result, fprec);
4266+
AdjustTimestampForTypmod(&result, fprec, NULL);
42674267

42684268
PG_RETURN_TIMESTAMP(result);
42694269
}
@@ -4351,7 +4351,7 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
43514351
if (dterr)
43524352
DateTimeParseError(dterr, &extra,
43534353
text_to_cstring(date_txt),
4354-
"timestamptz");
4354+
"timestamptz", NULL);
43554355
}
43564356
else
43574357
{
@@ -4372,7 +4372,7 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
43724372
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
43734373
errmsg("timestamptz out of range"))));
43744374

4375-
AdjustTimestampForTypmod(&result, *typmod);
4375+
AdjustTimestampForTypmod(&result, *typmod, NULL); /* XXX */
43764376

43774377
*typid = TIMESTAMPTZOID;
43784378
return TimestampTzGetDatum(result);
@@ -4386,7 +4386,7 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
43864386
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
43874387
errmsg("timestamp out of range"))));
43884388

4389-
AdjustTimestampForTypmod(&result, *typmod);
4389+
AdjustTimestampForTypmod(&result, *typmod, NULL); /* XXX */
43904390

43914391
*typid = TIMESTAMPOID;
43924392
return TimestampGetDatum(result);
@@ -4440,7 +4440,7 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
44404440
if (dterr)
44414441
RETURN_ERROR(DateTimeParseError(dterr, &extra,
44424442
text_to_cstring(date_txt),
4443-
"timetz"));
4443+
"timetz", NULL));
44444444
}
44454445
else
44464446
{
@@ -4789,7 +4789,8 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
47894789
* said DTERR_MD_FIELD_OVERFLOW, because we don't want to print an
47904790
* irrelevant hint about datestyle.
47914791
*/
4792-
RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL, date_str, "timestamp"));
4792+
RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
4793+
date_str, "timestamp", NULL));
47934794
}
47944795
}
47954796

@@ -4799,7 +4800,8 @@ do_to_timestamp(text *date_txt, text *fmt, Oid collid, bool std,
47994800
tm->tm_sec < 0 || tm->tm_sec >= SECS_PER_MINUTE ||
48004801
*fsec < INT64CONST(0) || *fsec >= USECS_PER_SEC)
48014802
{
4802-
RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL, date_str, "timestamp"));
4803+
RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
4804+
date_str, "timestamp", NULL));
48034805
}
48044806

48054807
/* 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,
48104812
if (tmfc.tzh < 0 || tmfc.tzh > MAX_TZDISP_HOUR ||
48114813
tmfc.tzm < 0 || tmfc.tzm >= MINS_PER_HOUR)
48124814
{
4813-
RETURN_ERROR(DateTimeParseError(DTERR_TZDISP_OVERFLOW, NULL, date_str, "timestamp"));
4815+
RETURN_ERROR(DateTimeParseError(DTERR_TZDISP_OVERFLOW, NULL,
4816+
date_str, "timestamp", NULL));
48144817
}
48154818

48164819
tz = psprintf("%c%02d:%02d",

0 commit comments

Comments
 (0)