date_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
+ Node *escontext = fcinfo->context;
DateADT date;
fsec_t fsec;
struct pg_tm tt,
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)
{
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)));
/* 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)));
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,
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);
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,
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);
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)
{
* 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),
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)));
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,
if (dterr)
DateTimeParseError(dterr, &extra, text_to_cstring(date_txt),
- "timestamptz");
+ "timestamptz", NULL);
}
else
tz = DetermineTimeZoneOffset(&tm, session_timezone);
/* Use the specified fractional precision, if any. */
if (fprec)
- AdjustTimestampForTypmod(&result, fprec);
+ AdjustTimestampForTypmod(&result, fprec, NULL);
PG_RETURN_TIMESTAMP(result);
}
if (dterr)
DateTimeParseError(dterr, &extra,
text_to_cstring(date_txt),
- "timestamptz");
+ "timestamptz", NULL);
}
else
{
(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);
(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);
if (dterr)
RETURN_ERROR(DateTimeParseError(dterr, &extra,
text_to_cstring(date_txt),
- "timetz"));
+ "timetz", NULL));
}
else
{
* 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));
}
}
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 */
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",
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);
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,
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;
TIMESTAMP_NOEND(result);
}
- AdjustTimestampForTypmod(&result, typmod);
+ AdjustTimestampForTypmod(&result, typmod, escontext);
PG_RETURN_TIMESTAMP(result);
}
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
- AdjustTimestampForTypmod(×tamp, typmod);
+ AdjustTimestampForTypmod(×tamp, typmod, NULL);
PG_RETURN_TIMESTAMP(timestamp);
}
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),
&& (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))
{
return true;
}
-void
-AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
-{
- (void) AdjustTimestampForTypmodError(time, typmod, NULL);
-}
-
/* timestamptz_in()
* Convert a string to internal form.
*/
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,
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;
TIMESTAMP_NOEND(result);
}
- AdjustTimestampForTypmod(&result, typmod);
+ AdjustTimestampForTypmod(&result, typmod, escontext);
PG_RETURN_TIMESTAMPTZ(result);
}
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)
{
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
- AdjustTimestampForTypmod(×tamp, typmod);
+ AdjustTimestampForTypmod(×tamp, typmod, NULL);
PG_RETURN_TIMESTAMPTZ(timestamp);
}
result = timestamp;
- AdjustTimestampForTypmod(&result, typmod);
+ AdjustTimestampForTypmod(&result, typmod, NULL);
PG_RETURN_TIMESTAMPTZ(result);
}
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;
{
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));
{
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;
dtype, str);
}
- AdjustIntervalForTypmod(result, typmod);
+ AdjustIntervalForTypmod(result, typmod, escontext);
PG_RETURN_INTERVAL_P(result);
}
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);
}
result = palloc(sizeof(Interval));
*result = *interval;
- AdjustIntervalForTypmod(result, typmod);
+ AdjustIntervalForTypmod(result, typmod, NULL);
PG_RETURN_INTERVAL_P(result);
}
/*
* 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),
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)));
}
}
}
+
+ return true;
}
/*
ts = GetCurrentTransactionStartTimestamp();
if (typmod >= 0)
- AdjustTimestampForTypmod(&ts, typmod);
+ AdjustTimestampForTypmod(&ts, typmod, NULL);
return TimestampTzGetDatum(ts);
}
ts = timestamptz2timestamp(GetCurrentTransactionStartTimestamp());
if (typmod >= 0)
- AdjustTimestampForTypmod(&ts, typmod);
+ AdjustTimestampForTypmod(&ts, typmod, NULL);
return TimestampGetDatum(ts);
}
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)
{
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)
{
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)
{
#ifndef DATETIME_H
#define DATETIME_H
-#include "nodes/nodes.h"
#include "utils/timestamp.h"
/* this struct is declared in utils/tzparser.h: */
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);
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);
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 */
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
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
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
--
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');
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');
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
--
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;
--
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;
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
--
-- 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');
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');
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
--