Skip to content

Commit 30413d7

Browse files
tglsfdcpull[bot]
authored andcommitted
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 eec94ad commit 30413d7

File tree

17 files changed

+328
-80
lines changed

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)