Convert datetime input functions to use "soft" error reporting.
authorTom Lane <[email protected]>
Fri, 9 Dec 2022 21:07:49 +0000 (16:07 -0500)
committerTom Lane <[email protected]>
Fri, 9 Dec 2022 21:07:49 +0000 (16:07 -0500)
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

17 files changed:
src/backend/utils/adt/date.c
src/backend/utils/adt/datetime.c
src/backend/utils/adt/formatting.c
src/backend/utils/adt/timestamp.c
src/include/utils/datetime.h
src/test/regress/expected/date.out
src/test/regress/expected/interval.out
src/test/regress/expected/time.out
src/test/regress/expected/timestamp.out
src/test/regress/expected/timestamptz.out
src/test/regress/expected/timetz.out
src/test/regress/sql/date.sql
src/test/regress/sql/interval.sql
src/test/regress/sql/time.sql
src/test/regress/sql/timestamp.sql
src/test/regress/sql/timestamptz.sql
src/test/regress/sql/timetz.sql

index 3c5a8a6985894e3bf97ec1d63415abdfd6485a6d..1cf7c7652d7d31054f015e0b764ddf0a48827ecf 100644 (file)
@@ -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)
        {
index 84bba97abc89b5493d96ab3727b7b34bc2a20dd6..b5b117a8ca796a5b8cac9da1b3e28e43e921ab17 100644 (file)
@@ -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,
index 0d065d8e418b1549d7ee4818bcaa043901553d41..eba981abd858a7a927195ad4b67c720a9519fd40 100644 (file)
@@ -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",
index 5a98ca1dec7f15f467cfb9100d96f30bd2b6969b..3f2508c0c4a2c92aab667147d4ad7554dbb57258 100644 (file)
@@ -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)
        {
index bb70b7544911e05f2fb4df8d81ae84cf2bc6b6b0..bdb7c06bec91a4ff5d423ee52a509030b156a69d 100644 (file)
@@ -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 */
index 75ff65937895eb50bcfbc3ed9801e15470ee2ed9..f8f83e40e9574234e90277bdd04ade102124ec94 100644 (file)
@@ -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
index 00885acd1df3ff3fa11a986dcf324d6a1c84b8be..579e92e7b392bf25239163c2a77d84138ffd9099 100644 (file)
@@ -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        
index f3a71503c25c90a771aea3b0bdd2dfc17c93ebb6..a44caededd785e1a6f3320b122b62ab6d252b3a5 100644 (file)
@@ -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
 --
index 79f81809558ecbbbc5e65b5a96a35a5a32da2897..be66274738e4c352ed9e8b7bccf9af399f8e9a70 100644 (file)
@@ -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');
index eba84191d367d02fdb686b511954af0668fd0e42..fb06acbccc48778f7ae39480f1d5ebb3e1d00a14 100644 (file)
@@ -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');
index 8942a9b95b617964b5bab045069dc2056679f657..984285663ba130391ec8850c239e1d28153f9cd9 100644 (file)
@@ -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
 --
index 8f7435b767c29889b7e72cf2db5f5b82020e8510..9fd15be5f9851f73f2b47f274ca2cb19c53ce991 100644 (file)
@@ -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;
 
 --
index 97d33a13236a662a42139cd2955fdcf35e9847ed..0517b5b82bd952430ef44eb390e15634c3d6c57f 100644 (file)
@@ -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;
index 3637f28798bff4ab0c896323a66dd2c3046743a0..b439cd6b41c27dcbfb63ccc45bc62cbbffa7ca0c 100644 (file)
@@ -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
 --
index ebc969f36cf6e5f88b2e160b7dc345c478adce58..e1175b12ce2edd10ac54e64c26db0801fe102cbe 100644 (file)
@@ -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');
 
index a107abc5a4c2012dfe591c6e909e7c66ccf05e1a..27263b3e0bb60f5824cc70b741532e496743845c 100644 (file)
@@ -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');
 
index 7b70f4656c9326e0d7142b99d04c68a8e31f3d58..b62aa3fe05763f5c0abad16f94b5f67e16498c0e 100644 (file)
@@ -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
 --