summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2022-12-09 21:07:49 +0000
committerTom Lane2022-12-09 21:07:49 +0000
commitc60488b4748b4316f1c92d62457671046e5c8994 (patch)
tree3868154eb7b042e1fa6bca2e0f581b5c8e28b5b6
parent2661469d862239ea8b9e3a1cf5352d833f6f0fec (diff)
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/[email protected]
-rw-r--r--src/backend/utils/adt/date.c31
-rw-r--r--src/backend/utils/adt/datetime.c22
-rw-r--r--src/backend/utils/adt/formatting.c21
-rw-r--r--src/backend/utils/adt/timestamp.c94
-rw-r--r--src/include/utils/datetime.h12
-rw-r--r--src/test/regress/expected/date.out31
-rw-r--r--src/test/regress/expected/interval.out31
-rw-r--r--src/test/regress/expected/time.out31
-rw-r--r--src/test/regress/expected/timestamp.out31
-rw-r--r--src/test/regress/expected/timestamptz.out31
-rw-r--r--src/test/regress/expected/timetz.out31
-rw-r--r--src/test/regress/sql/date.sql7
-rw-r--r--src/test/regress/sql/interval.sql7
-rw-r--r--src/test/regress/sql/time.sql7
-rw-r--r--src/test/regress/sql/timestamp.sql7
-rw-r--r--src/test/regress/sql/timestamptz.sql7
-rw-r--r--src/test/regress/sql/timetz.sql7
17 files changed, 328 insertions, 80 deletions
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 3c5a8a6985..1cf7c7652d 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -111,6 +111,7 @@ Datum
date_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
+ Node *escontext = fcinfo->context;
DateADT date;
fsec_t fsec;
struct pg_tm tt,
@@ -130,7 +131,10 @@ date_in(PG_FUNCTION_ARGS)
dterr = DecodeDateTime(field, ftype, nf,
&dtype, tm, &fsec, &tzp, &extra);
if (dterr != 0)
- DateTimeParseError(dterr, &extra, str, "date");
+ {
+ DateTimeParseError(dterr, &extra, str, "date", escontext);
+ PG_RETURN_NULL();
+ }
switch (dtype)
{
@@ -150,13 +154,13 @@ date_in(PG_FUNCTION_ARGS)
PG_RETURN_DATEADT(date);
default:
- DateTimeParseError(DTERR_BAD_FORMAT, &extra, str, "date");
- break;
+ DateTimeParseError(DTERR_BAD_FORMAT, &extra, str, "date", escontext);
+ PG_RETURN_NULL();
}
/* Prevent overflow in Julian-day routines */
if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range: \"%s\"", str)));
@@ -164,7 +168,7 @@ date_in(PG_FUNCTION_ARGS)
/* Now check for just-out-of-range dates */
if (!IS_VALID_DATE(date))
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range: \"%s\"", str)));
@@ -1384,11 +1388,11 @@ Datum
time_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
-
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
int32 typmod = PG_GETARG_INT32(2);
+ Node *escontext = fcinfo->context;
TimeADT result;
fsec_t fsec;
struct pg_tm tt,
@@ -1408,7 +1412,10 @@ time_in(PG_FUNCTION_ARGS)
dterr = DecodeTimeOnly(field, ftype, nf,
&dtype, tm, &fsec, &tz, &extra);
if (dterr != 0)
- DateTimeParseError(dterr, &extra, str, "time");
+ {
+ DateTimeParseError(dterr, &extra, str, "time", escontext);
+ PG_RETURN_NULL();
+ }
tm2time(tm, fsec, &result);
AdjustTimeForTypmod(&result, typmod);
@@ -2272,11 +2279,11 @@ Datum
timetz_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
-
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
int32 typmod = PG_GETARG_INT32(2);
+ Node *escontext = fcinfo->context;
TimeTzADT *result;
fsec_t fsec;
struct pg_tm tt,
@@ -2296,7 +2303,11 @@ timetz_in(PG_FUNCTION_ARGS)
dterr = DecodeTimeOnly(field, ftype, nf,
&dtype, tm, &fsec, &tz, &extra);
if (dterr != 0)
- DateTimeParseError(dterr, &extra, str, "time with time zone");
+ {
+ DateTimeParseError(dterr, &extra, str, "time with time zone",
+ escontext);
+ PG_RETURN_NULL();
+ }
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
tm2timetz(tm, fsec, tz, result);
@@ -3071,7 +3082,7 @@ timetz_zone(PG_FUNCTION_ARGS)
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
if (dterr)
- DateTimeParseError(dterr, &extra, NULL, NULL);
+ DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
if (type == TZ || type == DTZ)
{
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 84bba97abc..b5b117a8ca 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -4031,50 +4031,54 @@ DecodeUnits(int field, const char *lowtoken, int *val)
* we were trying to accept. (For some DTERR codes, these are not used and
* can be NULL.)
*
+ * If escontext points to an ErrorSaveContext node, that is filled instead
+ * of throwing an error.
+ *
* 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, DateTimeErrorExtra *extra,
- const char *str, const char *datatype)
+ const char *str, const char *datatype,
+ Node *escontext)
{
switch (dterr)
{
case DTERR_FIELD_OVERFLOW:
- ereport(ERROR,
+ errsave(escontext,
(errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
errmsg("date/time field value out of range: \"%s\"",
str)));
break;
case DTERR_MD_FIELD_OVERFLOW:
/* <nanny>same as above, but add hint about DateStyle</nanny> */
- ereport(ERROR,
+ errsave(escontext,
(errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
errmsg("date/time field value out of range: \"%s\"",
str),
errhint("Perhaps you need a different \"datestyle\" setting.")));
break;
case DTERR_INTERVAL_OVERFLOW:
- ereport(ERROR,
+ errsave(escontext,
(errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW),
errmsg("interval field value out of range: \"%s\"",
str)));
break;
case DTERR_TZDISP_OVERFLOW:
- ereport(ERROR,
+ errsave(escontext,
(errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
errmsg("time zone displacement out of range: \"%s\"",
str)));
break;
case DTERR_BAD_TIMEZONE:
- ereport(ERROR,
+ errsave(escontext,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("time zone \"%s\" not recognized",
extra->dtee_timezone)));
break;
case DTERR_BAD_ZONE_ABBREV:
- ereport(ERROR,
+ errsave(escontext,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("time zone \"%s\" not recognized",
extra->dtee_timezone),
@@ -4083,7 +4087,7 @@ DateTimeParseError(int dterr, DateTimeErrorExtra *extra,
break;
case DTERR_BAD_FORMAT:
default:
- ereport(ERROR,
+ errsave(escontext,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for type %s: \"%s\"",
datatype, str)));
@@ -5026,7 +5030,7 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS)
tzp = FetchDynamicTimeZone(zoneabbrevtbl, tp, &extra);
if (tzp == NULL)
DateTimeParseError(DTERR_BAD_ZONE_ABBREV, &extra,
- NULL, NULL);
+ NULL, 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 0d065d8e41..eba981abd8 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -4251,7 +4251,7 @@ to_timestamp(PG_FUNCTION_ARGS)
if (dterr)
DateTimeParseError(dterr, &extra, text_to_cstring(date_txt),
- "timestamptz");
+ "timestamptz", NULL);
}
else
tz = DetermineTimeZoneOffset(&tm, session_timezone);
@@ -4263,7 +4263,7 @@ to_timestamp(PG_FUNCTION_ARGS)
/* Use the specified fractional precision, if any. */
if (fprec)
- AdjustTimestampForTypmod(&result, fprec);
+ AdjustTimestampForTypmod(&result, fprec, NULL);
PG_RETURN_TIMESTAMP(result);
}
@@ -4351,7 +4351,7 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
if (dterr)
DateTimeParseError(dterr, &extra,
text_to_cstring(date_txt),
- "timestamptz");
+ "timestamptz", NULL);
}
else
{
@@ -4372,7 +4372,7 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamptz out of range"))));
- AdjustTimestampForTypmod(&result, *typmod);
+ AdjustTimestampForTypmod(&result, *typmod, NULL); /* XXX */
*typid = TIMESTAMPTZOID;
return TimestampTzGetDatum(result);
@@ -4386,7 +4386,7 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range"))));
- AdjustTimestampForTypmod(&result, *typmod);
+ AdjustTimestampForTypmod(&result, *typmod, NULL); /* XXX */
*typid = TIMESTAMPOID;
return TimestampGetDatum(result);
@@ -4440,7 +4440,7 @@ parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
if (dterr)
RETURN_ERROR(DateTimeParseError(dterr, &extra,
text_to_cstring(date_txt),
- "timetz"));
+ "timetz", NULL));
}
else
{
@@ -4789,7 +4789,8 @@ 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, NULL, date_str, "timestamp"));
+ RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
+ date_str, "timestamp", NULL));
}
}
@@ -4799,7 +4800,8 @@ 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, NULL, date_str, "timestamp"));
+ RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, NULL,
+ date_str, "timestamp", NULL));
}
/* 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,
if (tmfc.tzh < 0 || tmfc.tzh > MAX_TZDISP_HOUR ||
tmfc.tzm < 0 || tmfc.tzm >= MINS_PER_HOUR)
{
- RETURN_ERROR(DateTimeParseError(DTERR_TZDISP_OVERFLOW, NULL, date_str, "timestamp"));
+ RETURN_ERROR(DateTimeParseError(DTERR_TZDISP_OVERFLOW, NULL,
+ date_str, "timestamp", NULL));
}
tz = psprintf("%c%02d:%02d",
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 5a98ca1dec..3f2508c0c4 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -74,7 +74,8 @@ typedef struct
static TimeOffset time2t(const int hour, const int min, const int sec, const fsec_t fsec);
static Timestamp dt2local(Timestamp dt, int timezone);
-static void AdjustIntervalForTypmod(Interval *interval, int32 typmod);
+static bool AdjustIntervalForTypmod(Interval *interval, int32 typmod,
+ Node *escontext);
static TimestampTz timestamp2timestamptz(Timestamp timestamp);
static Timestamp timestamptz2timestamp(TimestampTz timestamp);
@@ -145,11 +146,11 @@ Datum
timestamp_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
-
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
int32 typmod = PG_GETARG_INT32(2);
+ Node *escontext = fcinfo->context;
Timestamp result;
fsec_t fsec;
struct pg_tm tt,
@@ -169,13 +170,16 @@ timestamp_in(PG_FUNCTION_ARGS)
dterr = DecodeDateTime(field, ftype, nf,
&dtype, tm, &fsec, &tz, &extra);
if (dterr != 0)
- DateTimeParseError(dterr, &extra, str, "timestamp");
+ {
+ DateTimeParseError(dterr, &extra, str, "timestamp", escontext);
+ PG_RETURN_NULL();
+ }
switch (dtype)
{
case DTK_DATE:
if (tm2timestamp(tm, fsec, NULL, &result) != 0)
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range: \"%s\"", str)));
break;
@@ -198,7 +202,7 @@ timestamp_in(PG_FUNCTION_ARGS)
TIMESTAMP_NOEND(result);
}
- AdjustTimestampForTypmod(&result, typmod);
+ AdjustTimestampForTypmod(&result, typmod, escontext);
PG_RETURN_TIMESTAMP(result);
}
@@ -257,7 +261,7 @@ timestamp_recv(PG_FUNCTION_ARGS)
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
- AdjustTimestampForTypmod(&timestamp, typmod);
+ AdjustTimestampForTypmod(&timestamp, typmod, NULL);
PG_RETURN_TIMESTAMP(timestamp);
}
@@ -328,17 +332,20 @@ timestamp_scale(PG_FUNCTION_ARGS)
result = timestamp;
- AdjustTimestampForTypmod(&result, typmod);
+ AdjustTimestampForTypmod(&result, typmod, NULL);
PG_RETURN_TIMESTAMP(result);
}
/*
- * AdjustTimestampForTypmodError --- round off a timestamp to suit given typmod
+ * AdjustTimestampForTypmod --- round off a timestamp to suit given typmod
* Works for either timestamp or timestamptz.
+ *
+ * Returns true on success, false on failure (if escontext points to an
+ * ErrorSaveContext; otherwise errors are thrown).
*/
bool
-AdjustTimestampForTypmodError(Timestamp *time, int32 typmod, bool *error)
+AdjustTimestampForTypmod(Timestamp *time, int32 typmod, Node *escontext)
{
static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {
INT64CONST(1000000),
@@ -364,18 +371,10 @@ AdjustTimestampForTypmodError(Timestamp *time, int32 typmod, bool *error)
&& (typmod != -1) && (typmod != MAX_TIMESTAMP_PRECISION))
{
if (typmod < 0 || typmod > MAX_TIMESTAMP_PRECISION)
- {
- if (error)
- {
- *error = true;
- return false;
- }
-
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("timestamp(%d) precision must be between %d and %d",
typmod, 0, MAX_TIMESTAMP_PRECISION)));
- }
if (*time >= INT64CONST(0))
{
@@ -392,12 +391,6 @@ AdjustTimestampForTypmodError(Timestamp *time, int32 typmod, bool *error)
return true;
}
-void
-AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
-{
- (void) AdjustTimestampForTypmodError(time, typmod, NULL);
-}
-
/* timestamptz_in()
* Convert a string to internal form.
*/
@@ -405,11 +398,11 @@ Datum
timestamptz_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
-
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
int32 typmod = PG_GETARG_INT32(2);
+ Node *escontext = fcinfo->context;
TimestampTz result;
fsec_t fsec;
struct pg_tm tt,
@@ -429,13 +422,17 @@ timestamptz_in(PG_FUNCTION_ARGS)
dterr = DecodeDateTime(field, ftype, nf,
&dtype, tm, &fsec, &tz, &extra);
if (dterr != 0)
- DateTimeParseError(dterr, &extra, str, "timestamp with time zone");
+ {
+ DateTimeParseError(dterr, &extra, str, "timestamp with time zone",
+ escontext);
+ PG_RETURN_NULL();
+ }
switch (dtype)
{
case DTK_DATE:
if (tm2timestamp(tm, fsec, &tz, &result) != 0)
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range: \"%s\"", str)));
break;
@@ -458,7 +455,7 @@ timestamptz_in(PG_FUNCTION_ARGS)
TIMESTAMP_NOEND(result);
}
- AdjustTimestampForTypmod(&result, typmod);
+ AdjustTimestampForTypmod(&result, typmod, escontext);
PG_RETURN_TIMESTAMPTZ(result);
}
@@ -525,7 +522,7 @@ parse_sane_timezone(struct pg_tm *tm, text *zone)
false);
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
if (dterr)
- DateTimeParseError(dterr, &extra, NULL, NULL);
+ DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
if (type == TZ || type == DTZ)
{
@@ -824,7 +821,7 @@ timestamptz_recv(PG_FUNCTION_ARGS)
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
- AdjustTimestampForTypmod(&timestamp, typmod);
+ AdjustTimestampForTypmod(&timestamp, typmod, NULL);
PG_RETURN_TIMESTAMPTZ(timestamp);
}
@@ -873,7 +870,7 @@ timestamptz_scale(PG_FUNCTION_ARGS)
result = timestamp;
- AdjustTimestampForTypmod(&result, typmod);
+ AdjustTimestampForTypmod(&result, typmod, NULL);
PG_RETURN_TIMESTAMPTZ(result);
}
@@ -889,11 +886,11 @@ Datum
interval_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
-
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
int32 typmod = PG_GETARG_INT32(2);
+ Node *escontext = fcinfo->context;
Interval *result;
struct pg_itm_in tt,
*itm_in = &tt;
@@ -931,7 +928,8 @@ interval_in(PG_FUNCTION_ARGS)
{
if (dterr == DTERR_FIELD_OVERFLOW)
dterr = DTERR_INTERVAL_OVERFLOW;
- DateTimeParseError(dterr, &extra, str, "interval");
+ DateTimeParseError(dterr, &extra, str, "interval", escontext);
+ PG_RETURN_NULL();
}
result = (Interval *) palloc(sizeof(Interval));
@@ -940,7 +938,7 @@ interval_in(PG_FUNCTION_ARGS)
{
case DTK_DELTA:
if (itmin2interval(itm_in, result) != 0)
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("interval out of range")));
break;
@@ -950,7 +948,7 @@ interval_in(PG_FUNCTION_ARGS)
dtype, str);
}
- AdjustIntervalForTypmod(result, typmod);
+ AdjustIntervalForTypmod(result, typmod, escontext);
PG_RETURN_INTERVAL_P(result);
}
@@ -994,7 +992,7 @@ interval_recv(PG_FUNCTION_ARGS)
interval->day = pq_getmsgint(buf, sizeof(interval->day));
interval->month = pq_getmsgint(buf, sizeof(interval->month));
- AdjustIntervalForTypmod(interval, typmod);
+ AdjustIntervalForTypmod(interval, typmod, NULL);
PG_RETURN_INTERVAL_P(interval);
}
@@ -1318,7 +1316,7 @@ interval_scale(PG_FUNCTION_ARGS)
result = palloc(sizeof(Interval));
*result = *interval;
- AdjustIntervalForTypmod(result, typmod);
+ AdjustIntervalForTypmod(result, typmod, NULL);
PG_RETURN_INTERVAL_P(result);
}
@@ -1326,9 +1324,13 @@ interval_scale(PG_FUNCTION_ARGS)
/*
* Adjust interval for specified precision, in both YEAR to SECOND
* range and sub-second precision.
+ *
+ * Returns true on success, false on failure (if escontext points to an
+ * ErrorSaveContext; otherwise errors are thrown).
*/
-static void
-AdjustIntervalForTypmod(Interval *interval, int32 typmod)
+static bool
+AdjustIntervalForTypmod(Interval *interval, int32 typmod,
+ Node *escontext)
{
static const int64 IntervalScales[MAX_INTERVAL_PRECISION + 1] = {
INT64CONST(1000000),
@@ -1468,7 +1470,7 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
if (precision != INTERVAL_FULL_PRECISION)
{
if (precision < 0 || precision > MAX_INTERVAL_PRECISION)
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("interval(%d) precision must be between %d and %d",
precision, 0, MAX_INTERVAL_PRECISION)));
@@ -1489,6 +1491,8 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
}
}
}
+
+ return true;
}
/*
@@ -1609,7 +1613,7 @@ current_timestamp(PG_FUNCTION_ARGS)
ts = GetCurrentTransactionStartTimestamp();
if (typmod >= 0)
- AdjustTimestampForTypmod(&ts, typmod);
+ AdjustTimestampForTypmod(&ts, typmod, NULL);
return TimestampTzGetDatum(ts);
}
@@ -1630,7 +1634,7 @@ sql_localtimestamp(PG_FUNCTION_ARGS)
ts = timestamptz2timestamp(GetCurrentTransactionStartTimestamp());
if (typmod >= 0)
- AdjustTimestampForTypmod(&ts, typmod);
+ AdjustTimestampForTypmod(&ts, typmod, NULL);
return TimestampGetDatum(ts);
}
@@ -4324,7 +4328,7 @@ timestamptz_trunc_zone(PG_FUNCTION_ARGS)
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
if (dterr)
- DateTimeParseError(dterr, &extra, NULL, NULL);
+ DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
if (type == TZ || type == DTZ)
{
@@ -5452,7 +5456,7 @@ timestamp_zone(PG_FUNCTION_ARGS)
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
if (dterr)
- DateTimeParseError(dterr, &extra, NULL, NULL);
+ DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
if (type == TZ || type == DTZ)
{
@@ -5708,7 +5712,7 @@ timestamptz_zone(PG_FUNCTION_ARGS)
dterr = DecodeTimezoneAbbrev(0, lowzone, &type, &val, &tzp, &extra);
if (dterr)
- DateTimeParseError(dterr, &extra, NULL, NULL);
+ DateTimeParseError(dterr, &extra, NULL, NULL, NULL);
if (type == TZ || type == DTZ)
{
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index bb70b75449..bdb7c06bec 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -16,7 +16,6 @@
#ifndef DATETIME_H
#define DATETIME_H
-#include "nodes/nodes.h"
#include "utils/timestamp.h"
/* this struct is declared in utils/tzparser.h: */
@@ -318,8 +317,8 @@ extern int DecodeISO8601Interval(char *str,
int *dtype, struct pg_itm_in *itm_in);
extern void DateTimeParseError(int dterr, DateTimeErrorExtra *extra,
- const char *str,
- const char *datatype) pg_attribute_noreturn();
+ const char *str, const char *datatype,
+ struct Node *escontext);
extern int DetermineTimeZoneOffset(struct pg_tm *tm, pg_tz *tzp);
extern int DetermineTimeZoneAbbrevOffset(struct pg_tm *tm, const char *abbr, pg_tz *tzp);
@@ -343,7 +342,7 @@ extern int DecodeUnits(int field, const char *lowtoken, int *val);
extern int j2day(int date);
-extern Node *TemporalSimplify(int32 max_precis, Node *node);
+extern struct Node *TemporalSimplify(int32 max_precis, struct Node *node);
extern bool CheckDateTokenTables(void);
@@ -351,8 +350,7 @@ extern TimeZoneAbbrevTable *ConvertTimeZoneAbbrevs(struct tzEntry *abbrevs,
int n);
extern void InstallTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl);
-extern void AdjustTimestampForTypmod(Timestamp *time, int32 typmod);
-extern bool AdjustTimestampForTypmodError(Timestamp *time, int32 typmod,
- bool *error);
+extern bool AdjustTimestampForTypmod(Timestamp *time, int32 typmod,
+ struct Node *escontext);
#endif /* DATETIME_H */
diff --git a/src/test/regress/expected/date.out b/src/test/regress/expected/date.out
index 75ff659378..f8f83e40e9 100644
--- a/src/test/regress/expected/date.out
+++ b/src/test/regress/expected/date.out
@@ -840,6 +840,37 @@ SELECT date '5874898-01-01'; -- out of range
ERROR: date out of range: "5874898-01-01"
LINE 1: SELECT date '5874898-01-01';
^
+-- Test non-error-throwing API
+SELECT pg_input_is_valid('now', 'date');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('garbage', 'date');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('6874898-01-01', 'date');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('garbage', 'date');
+ pg_input_error_message
+-----------------------------------------------
+ invalid input syntax for type date: "garbage"
+(1 row)
+
+SELECT pg_input_error_message('6874898-01-01', 'date');
+ pg_input_error_message
+------------------------------------
+ date out of range: "6874898-01-01"
+(1 row)
+
RESET datestyle;
--
-- Simple math
diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out
index 00885acd1d..579e92e7b3 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -72,6 +72,37 @@ INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 30 eons ago');
ERROR: invalid input syntax for type interval: "@ 30 eons ago"
LINE 1: INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 30 eons ago');
^
+-- Test non-error-throwing API
+SELECT pg_input_is_valid('1.5 weeks', 'interval');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('garbage', 'interval');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('@ 30 eons ago', 'interval');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('garbage', 'interval');
+ pg_input_error_message
+---------------------------------------------------
+ invalid input syntax for type interval: "garbage"
+(1 row)
+
+SELECT pg_input_error_message('@ 30 eons ago', 'interval');
+ pg_input_error_message
+---------------------------------------------------------
+ invalid input syntax for type interval: "@ 30 eons ago"
+(1 row)
+
-- test interval operators
SELECT * FROM INTERVAL_TBL;
f1
diff --git a/src/test/regress/expected/time.out b/src/test/regress/expected/time.out
index f3a71503c2..a44caededd 100644
--- a/src/test/regress/expected/time.out
+++ b/src/test/regress/expected/time.out
@@ -114,6 +114,37 @@ SELECT '25:00:00'::time; -- not allowed
ERROR: date/time field value out of range: "25:00:00"
LINE 1: SELECT '25:00:00'::time;
^
+-- Test non-error-throwing API
+SELECT pg_input_is_valid('12:00:00', 'time');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('25:00:00', 'time');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('15:36:39 America/New_York', 'time');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('25:00:00', 'time');
+ pg_input_error_message
+------------------------------------------------
+ date/time field value out of range: "25:00:00"
+(1 row)
+
+SELECT pg_input_error_message('15:36:39 America/New_York', 'time');
+ pg_input_error_message
+-----------------------------------------------------------------
+ invalid input syntax for type time: "15:36:39 America/New_York"
+(1 row)
+
--
-- TIME simple math
--
diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out
index 79f8180955..be66274738 100644
--- a/src/test/regress/expected/timestamp.out
+++ b/src/test/regress/expected/timestamp.out
@@ -119,6 +119,37 @@ INSERT INTO TIMESTAMP_TBL VALUES ('19970710 173201 America/Does_not_exist');
ERROR: time zone "america/does_not_exist" not recognized
LINE 1: INSERT INTO TIMESTAMP_TBL VALUES ('19970710 173201 America/D...
^
+-- Test non-error-throwing API
+SELECT pg_input_is_valid('now', 'timestamp');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('garbage', 'timestamp');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('2001-01-01 00:00 Nehwon/Lankhmar', 'timestamp');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('garbage', 'timestamp');
+ pg_input_error_message
+----------------------------------------------------
+ invalid input syntax for type timestamp: "garbage"
+(1 row)
+
+SELECT pg_input_error_message('2001-01-01 00:00 Nehwon/Lankhmar', 'timestamp');
+ pg_input_error_message
+--------------------------------------------
+ time zone "nehwon/lankhmar" not recognized
+(1 row)
+
-- Check date conversion and date arithmetic
INSERT INTO TIMESTAMP_TBL VALUES ('1997-06-10 18:32:01 PDT');
INSERT INTO TIMESTAMP_TBL VALUES ('Feb 10 17:32:01 1997');
diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out
index eba84191d3..fb06acbccc 100644
--- a/src/test/regress/expected/timestamptz.out
+++ b/src/test/regress/expected/timestamptz.out
@@ -170,6 +170,37 @@ SELECT '205000-01-10 17:32:01 Europe/Helsinki'::timestamptz; -- non-DST
Fri Jan 10 07:32:01 205000 PST
(1 row)
+-- Test non-error-throwing API
+SELECT pg_input_is_valid('now', 'timestamptz');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('garbage', 'timestamptz');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('2001-01-01 00:00 Nehwon/Lankhmar', 'timestamptz');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('garbage', 'timestamptz');
+ pg_input_error_message
+-------------------------------------------------------------------
+ invalid input syntax for type timestamp with time zone: "garbage"
+(1 row)
+
+SELECT pg_input_error_message('2001-01-01 00:00 Nehwon/Lankhmar', 'timestamptz');
+ pg_input_error_message
+--------------------------------------------
+ time zone "nehwon/lankhmar" not recognized
+(1 row)
+
-- Check date conversion and date arithmetic
INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-06-10 18:32:01 PDT');
INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 10 17:32:01 1997');
diff --git a/src/test/regress/expected/timetz.out b/src/test/regress/expected/timetz.out
index 8942a9b95b..984285663b 100644
--- a/src/test/regress/expected/timetz.out
+++ b/src/test/regress/expected/timetz.out
@@ -131,6 +131,37 @@ SELECT '25:00:00 PDT'::timetz; -- not allowed
ERROR: date/time field value out of range: "25:00:00 PDT"
LINE 1: SELECT '25:00:00 PDT'::timetz;
^
+-- Test non-error-throwing API
+SELECT pg_input_is_valid('12:00:00 PDT', 'timetz');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('25:00:00 PDT', 'timetz');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_is_valid('15:36:39 America/New_York', 'timetz');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('25:00:00 PDT', 'timetz');
+ pg_input_error_message
+----------------------------------------------------
+ date/time field value out of range: "25:00:00 PDT"
+(1 row)
+
+SELECT pg_input_error_message('15:36:39 America/New_York', 'timetz');
+ pg_input_error_message
+--------------------------------------------------------------------------------
+ invalid input syntax for type time with time zone: "15:36:39 America/New_York"
+(1 row)
+
--
-- TIME simple math
--
diff --git a/src/test/regress/sql/date.sql b/src/test/regress/sql/date.sql
index 8f7435b767..9fd15be5f9 100644
--- a/src/test/regress/sql/date.sql
+++ b/src/test/regress/sql/date.sql
@@ -193,6 +193,13 @@ SELECT date '4714-11-23 BC'; -- out of range
SELECT date '5874897-12-31';
SELECT date '5874898-01-01'; -- out of range
+-- Test non-error-throwing API
+SELECT pg_input_is_valid('now', 'date');
+SELECT pg_input_is_valid('garbage', 'date');
+SELECT pg_input_is_valid('6874898-01-01', 'date');
+SELECT pg_input_error_message('garbage', 'date');
+SELECT pg_input_error_message('6874898-01-01', 'date');
+
RESET datestyle;
--
diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql
index 97d33a1323..0517b5b82b 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -32,6 +32,13 @@ INSERT INTO INTERVAL_TBL (f1) VALUES ('5 months 12 hours');
INSERT INTO INTERVAL_TBL (f1) VALUES ('badly formatted interval');
INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 30 eons ago');
+-- Test non-error-throwing API
+SELECT pg_input_is_valid('1.5 weeks', 'interval');
+SELECT pg_input_is_valid('garbage', 'interval');
+SELECT pg_input_is_valid('@ 30 eons ago', 'interval');
+SELECT pg_input_error_message('garbage', 'interval');
+SELECT pg_input_error_message('@ 30 eons ago', 'interval');
+
-- test interval operators
SELECT * FROM INTERVAL_TBL;
diff --git a/src/test/regress/sql/time.sql b/src/test/regress/sql/time.sql
index 3637f28798..b439cd6b41 100644
--- a/src/test/regress/sql/time.sql
+++ b/src/test/regress/sql/time.sql
@@ -40,6 +40,13 @@ SELECT '23:59:60.01'::time; -- not allowed
SELECT '24:01:00'::time; -- not allowed
SELECT '25:00:00'::time; -- not allowed
+-- Test non-error-throwing API
+SELECT pg_input_is_valid('12:00:00', 'time');
+SELECT pg_input_is_valid('25:00:00', 'time');
+SELECT pg_input_is_valid('15:36:39 America/New_York', 'time');
+SELECT pg_input_error_message('25:00:00', 'time');
+SELECT pg_input_error_message('15:36:39 America/New_York', 'time');
+
--
-- TIME simple math
--
diff --git a/src/test/regress/sql/timestamp.sql b/src/test/regress/sql/timestamp.sql
index ebc969f36c..e1175b12ce 100644
--- a/src/test/regress/sql/timestamp.sql
+++ b/src/test/regress/sql/timestamp.sql
@@ -94,6 +94,13 @@ INSERT INTO TIMESTAMP_TBL VALUES ('19970210 173201 America/New_York');
-- this fails (even though TZ is a no-op, we still look it up)
INSERT INTO TIMESTAMP_TBL VALUES ('19970710 173201 America/Does_not_exist');
+-- Test non-error-throwing API
+SELECT pg_input_is_valid('now', 'timestamp');
+SELECT pg_input_is_valid('garbage', 'timestamp');
+SELECT pg_input_is_valid('2001-01-01 00:00 Nehwon/Lankhmar', 'timestamp');
+SELECT pg_input_error_message('garbage', 'timestamp');
+SELECT pg_input_error_message('2001-01-01 00:00 Nehwon/Lankhmar', 'timestamp');
+
-- Check date conversion and date arithmetic
INSERT INTO TIMESTAMP_TBL VALUES ('1997-06-10 18:32:01 PDT');
diff --git a/src/test/regress/sql/timestamptz.sql b/src/test/regress/sql/timestamptz.sql
index a107abc5a4..27263b3e0b 100644
--- a/src/test/regress/sql/timestamptz.sql
+++ b/src/test/regress/sql/timestamptz.sql
@@ -107,6 +107,13 @@ SELECT '20500110 173201 Europe/Helsinki'::timestamptz; -- non-DST
SELECT '205000-07-10 17:32:01 Europe/Helsinki'::timestamptz; -- DST
SELECT '205000-01-10 17:32:01 Europe/Helsinki'::timestamptz; -- non-DST
+-- Test non-error-throwing API
+SELECT pg_input_is_valid('now', 'timestamptz');
+SELECT pg_input_is_valid('garbage', 'timestamptz');
+SELECT pg_input_is_valid('2001-01-01 00:00 Nehwon/Lankhmar', 'timestamptz');
+SELECT pg_input_error_message('garbage', 'timestamptz');
+SELECT pg_input_error_message('2001-01-01 00:00 Nehwon/Lankhmar', 'timestamptz');
+
-- Check date conversion and date arithmetic
INSERT INTO TIMESTAMPTZ_TBL VALUES ('1997-06-10 18:32:01 PDT');
diff --git a/src/test/regress/sql/timetz.sql b/src/test/regress/sql/timetz.sql
index 7b70f4656c..b62aa3fe05 100644
--- a/src/test/regress/sql/timetz.sql
+++ b/src/test/regress/sql/timetz.sql
@@ -45,6 +45,13 @@ SELECT '23:59:60.01 PDT'::timetz; -- not allowed
SELECT '24:01:00 PDT'::timetz; -- not allowed
SELECT '25:00:00 PDT'::timetz; -- not allowed
+-- Test non-error-throwing API
+SELECT pg_input_is_valid('12:00:00 PDT', 'timetz');
+SELECT pg_input_is_valid('25:00:00 PDT', 'timetz');
+SELECT pg_input_is_valid('15:36:39 America/New_York', 'timetz');
+SELECT pg_input_error_message('25:00:00 PDT', 'timetz');
+SELECT pg_input_error_message('15:36:39 America/New_York', 'timetz');
+
--
-- TIME simple math
--