From 73dd55775b4ed2609b6b052046423db6241e6a54 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov 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