v9-0005-Add-safe-input-and-type-conversion-functions-for-.patch
text/x-patch
Filename: v9-0005-Add-safe-input-and-type-conversion-functions-for-.patch
Type: text/x-patch
Part: 4
Message:
Re: SQL/JSON features for v15
Patch
Format: format-patch
Series: patch v9-0005
Subject: Add safe input and type conversion functions for datetime types
| File | + | − |
|---|---|---|
| contrib/adminpack/adminpack.c | 1 | 1 |
| src/backend/utils/adt/date.c | 224 | 53 |
| src/backend/utils/adt/datetime.c | 13 | 3 |
| src/backend/utils/adt/timestamp.c | 101 | 25 |
| src/backend/utils/misc/guc.c | 1 | 1 |
| src/include/utils/date.h | 13 | 0 |
| src/include/utils/datetime.h | 4 | 2 |
| src/include/utils/timestamp.h | 6 | 0 |
From 73dd55775b4ed2609b6b052046423db6241e6a54 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Mon, 29 Aug 2022 23:54:45 +0300
Subject: [PATCH v9 5/9] Add safe input and type conversion functions for
datetime types
---
contrib/adminpack/adminpack.c | 2 +-
src/backend/utils/adt/date.c | 277 ++++++++++++++++++++++++------
src/backend/utils/adt/datetime.c | 16 +-
src/backend/utils/adt/timestamp.c | 126 +++++++++++---
src/backend/utils/misc/guc.c | 2 +-
src/include/utils/date.h | 13 ++
src/include/utils/datetime.h | 6 +-
src/include/utils/timestamp.h | 6 +
8 files changed, 363 insertions(+), 85 deletions(-)
diff --git a/contrib/adminpack/adminpack.c b/contrib/adminpack/adminpack.c
index 03addf1dc5f..4bf795d42a4 100644
--- a/contrib/adminpack/adminpack.c
+++ b/contrib/adminpack/adminpack.c
@@ -571,7 +571,7 @@ pg_logdir_ls_internal(FunctionCallInfo fcinfo)
if (ParseDateTime(timestampbuf, lowstr, MAXDATELEN, field, ftype, MAXDATEFIELDS, &nf))
continue;
- if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz))
+ if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz, true))
continue;
/* Seems the timestamp is OK; prepare and return tuple */
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 081dfa2450f..43a96fc5142 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -108,10 +108,9 @@ anytime_typmodout(bool istz, int32 typmod)
/* date_in()
* Given date text string, convert to internal date format.
*/
-Datum
-date_in(PG_FUNCTION_ARGS)
+DateADT
+date_in_opt_error(char *str, bool *error)
{
- char *str = PG_GETARG_CSTRING(0);
DateADT date;
fsec_t fsec;
struct pg_tm tt,
@@ -127,9 +126,18 @@ date_in(PG_FUNCTION_ARGS)
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,
+ !error);
if (dterr != 0)
+ {
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
DateTimeParseError(dterr, str, "date");
+ }
switch (dtype)
{
@@ -149,25 +157,56 @@ date_in(PG_FUNCTION_ARGS)
PG_RETURN_DATEADT(date);
default:
+ if (*error)
+ {
+ *error = true;
+ return 0;
+ }
+
DateTimeParseError(DTERR_BAD_FORMAT, str, "date");
break;
}
/* Prevent overflow in Julian-day routines */
if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
+ {
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range: \"%s\"", str)));
+ }
date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
/* Now check for just-out-of-range dates */
if (!IS_VALID_DATE(date))
+ {
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range: \"%s\"", str)));
+ }
- PG_RETURN_DATEADT(date);
+ return date;
+}
+
+/* date_in()
+ * Given date text string, convert to internal date format.
+ */
+Datum
+date_in(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_DATEADT(date_in_opt_error(PG_GETARG_CSTRING(0), NULL));
}
/* date_out()
@@ -1286,10 +1325,9 @@ date_timestamp(PG_FUNCTION_ARGS)
/* timestamp_date()
* Convert timestamp to date data type.
*/
-Datum
-timestamp_date(PG_FUNCTION_ARGS)
+DateADT
+timestamp_date_opt_error(Timestamp timestamp, bool *error)
{
- Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
DateADT result;
struct pg_tm tt,
*tm = &tt;
@@ -1302,16 +1340,32 @@ timestamp_date(PG_FUNCTION_ARGS)
else
{
if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
+ {
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
+ }
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
}
- PG_RETURN_DATEADT(result);
+ return result;
}
+/* timestamp_date()
+ * Convert timestamp to date data type.
+ */
+Datum
+timestamp_date(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_DATEADT(timestamp_date_opt_error(PG_GETARG_TIMESTAMP(0), NULL));
+}
/* date_timestamptz()
* Convert date to timestamp with time zone data type.
@@ -1327,14 +1381,9 @@ date_timestamptz(PG_FUNCTION_ARGS)
PG_RETURN_TIMESTAMP(result);
}
-
-/* timestamptz_date()
- * Convert timestamp with time zone to date data type.
- */
-Datum
-timestamptz_date(PG_FUNCTION_ARGS)
+DateADT
+timestamptz_date_opt_error(TimestampTz timestamp, bool *error)
{
- TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
DateADT result;
struct pg_tm tt,
*tm = &tt;
@@ -1348,14 +1397,31 @@ timestamptz_date(PG_FUNCTION_ARGS)
else
{
if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
+ {
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
+ }
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
}
- PG_RETURN_DATEADT(result);
+ return result;
+}
+
+/* timestamptz_date()
+ * Convert timestamp with time zone to date data type.
+ */
+Datum
+timestamptz_date(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_DATEADT(timestamptz_date_opt_error(PG_GETARG_TIMESTAMP(0), NULL));
}
@@ -1363,15 +1429,9 @@ timestamptz_date(PG_FUNCTION_ARGS)
* Time ADT
*****************************************************************************/
-Datum
-time_in(PG_FUNCTION_ARGS)
+TimeADT
+time_in_opt_error(char *str, int32 typmod, bool *error)
{
- char *str = PG_GETARG_CSTRING(0);
-
-#ifdef NOT_USED
- Oid typelem = PG_GETARG_OID(1);
-#endif
- int32 typmod = PG_GETARG_INT32(2);
TimeADT result;
fsec_t fsec;
struct pg_tm tt,
@@ -1387,14 +1447,35 @@ time_in(PG_FUNCTION_ARGS)
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,
+ !error);
if (dterr != 0)
+ {
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
DateTimeParseError(dterr, str, "time");
+ }
tm2time(tm, fsec, &result);
AdjustTimeForTypmod(&result, typmod);
- PG_RETURN_TIMEADT(result);
+ return result;
+}
+
+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);
+
+ PG_RETURN_TIMEADT(time_in_opt_error(str, typmod, NULL));
}
/* tm2time()
@@ -1889,22 +1970,32 @@ overlaps_time(PG_FUNCTION_ARGS)
/* timestamp_time()
* Convert timestamp to time data type.
*/
-Datum
-timestamp_time(PG_FUNCTION_ARGS)
+TimeADT
+timestamp_time_opt_error(Timestamp timestamp, bool *isnull, bool *error)
{
- Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
TimeADT result;
struct pg_tm tt,
*tm = &tt;
fsec_t fsec;
if (TIMESTAMP_NOT_FINITE(timestamp))
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
+ {
+ if (error)
+ {
+ *error = *isnull = true;
+ return (Datum) 0;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
+ }
/*
* Could also do this with time = (timestamp / USECS_PER_DAY *
@@ -1912,17 +2003,33 @@ timestamp_time(PG_FUNCTION_ARGS)
*/
result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
USECS_PER_SEC) + fsec;
+ *isnull = false;
- PG_RETURN_TIMEADT(result);
+ return result;
}
-/* timestamptz_time()
- * Convert timestamptz to time data type.
+/* timestamp_time()
+ * Convert timestamp to time data type.
*/
Datum
-timestamptz_time(PG_FUNCTION_ARGS)
+timestamp_time(PG_FUNCTION_ARGS)
+{
+ bool isnull;
+ TimeADT time =
+ timestamp_time_opt_error(PG_GETARG_TIMESTAMP(0), &isnull, NULL);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_TIMEADT(time);
+}
+
+/* timestamptz_time_opt_error()
+ * Convert timestamptz to time data type.
+ */
+TimeADT
+timestamptz_time_opt_error(TimestampTz timestamp, bool *isnull, bool *error)
{
- TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
TimeADT result;
struct pg_tm tt,
*tm = &tt;
@@ -1930,12 +2037,23 @@ timestamptz_time(PG_FUNCTION_ARGS)
fsec_t fsec;
if (TIMESTAMP_NOT_FINITE(timestamp))
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
+ {
+ if (error)
+ {
+ *error = *isnull = true;
+ return 0;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
+ }
/*
* Could also do this with time = (timestamp / USECS_PER_DAY *
@@ -1943,8 +2061,25 @@ timestamptz_time(PG_FUNCTION_ARGS)
*/
result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
USECS_PER_SEC) + fsec;
+ *isnull = false;
- PG_RETURN_TIMEADT(result);
+ return result;
+}
+
+/* timestamptz_time()
+ * Convert timestamptz to time data type.
+ */
+Datum
+timestamptz_time(PG_FUNCTION_ARGS)
+{
+ bool isnull;
+ TimeADT result =
+ timestamptz_time_opt_error(PG_GETARG_TIMESTAMP(0), &isnull, NULL);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_TIMEADT(result);
}
/* datetime_timestamp()
@@ -2249,15 +2384,9 @@ tm2timetz(struct pg_tm *tm, fsec_t fsec, int tz, TimeTzADT *result)
return 0;
}
-Datum
-timetz_in(PG_FUNCTION_ARGS)
+TimeTzADT *
+timetz_in_opt_error(char *str, int32 typmod, bool *error)
{
- char *str = PG_GETARG_CSTRING(0);
-
-#ifdef NOT_USED
- Oid typelem = PG_GETARG_OID(1);
-#endif
- int32 typmod = PG_GETARG_INT32(2);
TimeTzADT *result;
fsec_t fsec;
struct pg_tm tt,
@@ -2273,17 +2402,38 @@ timetz_in(PG_FUNCTION_ARGS)
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,
+ !error);
if (dterr != 0)
+ {
+ if (error)
+ {
+ *error = true;
+ return NULL;
+ }
+
DateTimeParseError(dterr, str, "time with time zone");
+ }
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
tm2timetz(tm, fsec, tz, result);
AdjustTimeForTypmod(&(result->time), typmod);
- PG_RETURN_TIMETZADT_P(result);
+ return result;
}
+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);
+
+ PG_RETURN_TIMETZADT_P(timetz_in_opt_error(str, typmod, NULL));
+}
Datum
timetz_out(PG_FUNCTION_ARGS)
{
@@ -2812,10 +2962,9 @@ time_timetz(PG_FUNCTION_ARGS)
/* timestamptz_timetz()
* Convert timestamp to timetz data type.
*/
-Datum
-timestamptz_timetz(PG_FUNCTION_ARGS)
+TimeTzADT *
+timestamptz_timetz_opt_error(TimestampTz timestamp, bool *error)
{
- TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
TimeTzADT *result;
struct pg_tm tt,
*tm = &tt;
@@ -2823,20 +2972,42 @@ timestamptz_timetz(PG_FUNCTION_ARGS)
fsec_t fsec;
if (TIMESTAMP_NOT_FINITE(timestamp))
- PG_RETURN_NULL();
+ return NULL;
if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
+ {
+ if (error)
+ {
+ *error = true;
+ return NULL;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
+ }
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
tm2timetz(tm, fsec, tz, result);
- PG_RETURN_TIMETZADT_P(result);
+ return result;
}
+/* timestamptz_timetz()
+ * Convert timestamp to timetz data type.
+ */
+Datum
+timestamptz_timetz(PG_FUNCTION_ARGS)
+{
+ TimeTzADT *result =
+ timestamptz_timetz_opt_error(PG_GETARG_TIMESTAMP(0), NULL);
+
+ if (result)
+ PG_RETURN_TIMETZADT_P(result);
+ else
+ PG_RETURN_NULL();
+}
/* datetimetz_timestamptz()
* Convert date and timetz to timestamp with time zone data type.
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 43fff50d490..3275e5fd948 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -971,7 +971,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,
+ bool throw_errors)
{
int fmask = 0,
tmask,
@@ -1114,8 +1115,13 @@ DecodeDateTime(char **field, int *ftype, int nf,
/*
* We should return an error code instead of
* ereport'ing directly, but then there is no way
- * to report the bad time zone name.
+ * to report the bad time zone name. But
+ * throwing errors is disallowed, simply
+ * return error code.
*/
+ if (!throw_errors)
+ return DTERR_BAD_FORMAT;
+
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("time zone \"%s\" not recognized",
@@ -1921,7 +1927,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,
+ bool throw_errors)
{
int fmask = 0,
tmask,
@@ -2022,6 +2029,9 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
* ereport'ing directly, but then there is no way
* to report the bad time zone name.
*/
+ if (!throw_errors)
+ return DTERR_BAD_FORMAT;
+
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("time zone \"%s\" not recognized",
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 49cdb290ac2..04a0e3eb063 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -139,18 +139,12 @@ anytimestamp_typmodout(bool istz, int32 typmod)
* USER I/O ROUTINES *
*****************************************************************************/
-/* timestamp_in()
- * Convert a string to internal form.
+/* timestamp_in_opt_error()
+ * Convert a string to internal form, returning error instead of throwing if needed.
*/
-Datum
-timestamp_in(PG_FUNCTION_ARGS)
+Timestamp
+timestamp_in_opt_error(char *str, int32 typmod, bool *error)
{
- char *str = PG_GETARG_CSTRING(0);
-
-#ifdef NOT_USED
- Oid typelem = PG_GETARG_OID(1);
-#endif
- int32 typmod = PG_GETARG_INT32(2);
Timestamp result;
fsec_t fsec;
struct pg_tm tt,
@@ -166,17 +160,34 @@ timestamp_in(PG_FUNCTION_ARGS)
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,
+ !error);
if (dterr != 0)
+ {
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
DateTimeParseError(dterr, str, "timestamp");
+ }
switch (dtype)
{
case DTK_DATE:
if (tm2timestamp(tm, fsec, NULL, &result) != 0)
+ {
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range: \"%s\"", str)));
+ }
break;
case DTK_EPOCH:
@@ -197,11 +208,27 @@ timestamp_in(PG_FUNCTION_ARGS)
TIMESTAMP_NOEND(result);
}
- AdjustTimestampForTypmod(&result, typmod);
+ AdjustTimestampForTypmodError(&result, typmod, error);
PG_RETURN_TIMESTAMP(result);
}
+/* timestamp_in()
+ * Convert a string to internal form.
+ */
+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);
+
+ PG_RETURN_TIMESTAMP(timestamp_in_opt_error(str, typmod, NULL));
+}
+
/* timestamp_out()
* Convert a timestamp to external form.
*/
@@ -400,15 +427,9 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
/* timestamptz_in()
* Convert a string to internal form.
*/
-Datum
-timestamptz_in(PG_FUNCTION_ARGS)
+TimestampTz
+timestamptz_in_opt_error(char *str, int32 typmod, bool *error)
{
- char *str = PG_GETARG_CSTRING(0);
-
-#ifdef NOT_USED
- Oid typelem = PG_GETARG_OID(1);
-#endif
- int32 typmod = PG_GETARG_INT32(2);
TimestampTz result;
fsec_t fsec;
struct pg_tm tt,
@@ -424,17 +445,34 @@ timestamptz_in(PG_FUNCTION_ARGS)
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,
+ !error);
if (dterr != 0)
+ {
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
DateTimeParseError(dterr, str, "timestamp with time zone");
+ }
switch (dtype)
{
case DTK_DATE:
if (tm2timestamp(tm, fsec, &tz, &result) != 0)
+ {
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range: \"%s\"", str)));
+ }
break;
case DTK_EPOCH:
@@ -455,9 +493,24 @@ timestamptz_in(PG_FUNCTION_ARGS)
TIMESTAMP_NOEND(result);
}
- AdjustTimestampForTypmod(&result, typmod);
+ AdjustTimestampForTypmodError(&result, typmod, error);
- PG_RETURN_TIMESTAMPTZ(result);
+ return result;
+}
+
+/* timestamptz_in()
+ * Convert a string to internal form.
+ */
+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);
+
+ PG_RETURN_TIMESTAMPTZ(timestamptz_in_opt_error(str, typmod, NULL));
}
/*
@@ -5614,8 +5667,8 @@ timestamptz_timestamp(PG_FUNCTION_ARGS)
PG_RETURN_TIMESTAMP(timestamptz2timestamp(timestamp));
}
-static Timestamp
-timestamptz2timestamp(TimestampTz timestamp)
+Timestamp
+timestamptz2timestamp_opt_error(TimestampTz timestamp, bool *error)
{
Timestamp result;
struct pg_tm tt,
@@ -5628,17 +5681,40 @@ timestamptz2timestamp(TimestampTz timestamp)
else
{
if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
+ {
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
+ }
+
if (tm2timestamp(tm, fsec, NULL, &result) != 0)
+ {
+ if (error)
+ {
+ *error = true;
+ return 0;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
+ }
}
return result;
}
+static Timestamp
+timestamptz2timestamp(TimestampTz timestamp)
+{
+ return timestamptz2timestamp_opt_error(timestamp, NULL);
+}
+
/* timestamptz_zone()
* Evaluate timestamp with time zone type at the specified time zone.
* Returns a timestamp without time zone.
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 9fbbfb1be54..7877bce6043 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -12866,7 +12866,7 @@ check_recovery_target_time(char **newval, void **extra, GucSource source)
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, true);
if (dterr != 0)
return false;
if (dtype != DTK_DATE)
diff --git a/src/include/utils/date.h b/src/include/utils/date.h
index 91ae24254df..f29168a025b 100644
--- a/src/include/utils/date.h
+++ b/src/include/utils/date.h
@@ -87,4 +87,17 @@ extern bool time_overflows(int hour, int min, int sec, fsec_t fsec);
extern bool float_time_overflows(int hour, int min, double sec);
extern void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
+extern DateADT timestamp_date_opt_error(Timestamp timestamp, bool *error);
+extern TimeADT timestamp_time_opt_error(Timestamp timestamp,
+ bool *isnull, bool *error);
+
+extern DateADT timestamptz_date_opt_error(Timestamp timestamp, bool *error);
+extern TimeADT timestamptz_time_opt_error(TimestampTz timestamp,
+ bool *isnull, bool *error);
+extern TimeTzADT *timestamptz_timetz_opt_error(TimestampTz timestamp,
+ bool *error);
+extern DateADT date_in_opt_error(char *str, bool *error);
+extern TimeADT time_in_opt_error(char *str, int32 typmod, bool *error);
+extern TimeTzADT *timetz_in_opt_error(char *str, int32 typmod, bool *error);
+
#endif /* DATE_H */
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index 4527e825177..68241d7c4cc 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -295,11 +295,13 @@ extern int ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
int maxfields, int *numfields);
extern int DecodeDateTime(char **field, int *ftype,
int nf, int *dtype,
- struct pg_tm *tm, fsec_t *fsec, int *tzp);
+ struct pg_tm *tm, fsec_t *fsec, int *tzp,
+ bool throw_errors);
extern int DecodeTimezone(char *str, int *tzp);
extern int DecodeTimeOnly(char **field, int *ftype,
int nf, int *dtype,
- struct pg_tm *tm, fsec_t *fsec, int *tzp);
+ struct pg_tm *tm, fsec_t *fsec, int *tzp,
+ bool throw_errors);
extern int DecodeInterval(char **field, int *ftype, int nf, int range,
int *dtype, struct pg_itm_in *itm_in);
extern int DecodeISO8601Interval(char *str,
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index edf3a973186..2685d14342d 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -102,8 +102,14 @@ extern int timestamp_cmp_internal(Timestamp dt1, Timestamp dt2);
extern TimestampTz timestamp2timestamptz_opt_overflow(Timestamp timestamp,
int *overflow);
+extern Timestamp timestamptz2timestamp_opt_error(TimestampTz timestamp,
+ bool *error);
extern int32 timestamp_cmp_timestamptz_internal(Timestamp timestampVal,
TimestampTz dt2);
+extern Timestamp timestamp_in_opt_error(char *str, int32 typmod,
+ bool *error);
+extern TimestampTz timestamptz_in_opt_error(char *str, int32 typmod,
+ bool *error);
extern int isoweek2j(int year, int week);
extern void isoweek2date(int woy, int *year, int *mon, int *mday);
--
2.17.1