Commit d1031cde authored by Tom Lane's avatar Tom Lane

Adjust date/time input parsing code to correctly distinguish the four

SQLSTATE error codes required by SQL99 (invalid format, datetime field
overflow, interval field overflow, invalid time zone displacement value).
Also emit a HINT about DateStyle in cases where it seems appropriate.
Per recent gripes.
parent 37222260
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.90 2003/08/08 00:10:31 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.91 2003/08/27 23:29:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -62,20 +62,19 @@ date_in(PG_FUNCTION_ARGS)
int tzp;
int dtype;
int nf;
int dterr;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char lowstr[MAXDATELEN + 1];
if (strlen(str) >= sizeof(lowstr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for date: \"%s\"", str)));
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp) != 0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for date: \"%s\"", str)));
dterr = DTERR_BAD_FORMAT;
else
dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp);
if (dterr != 0)
DateTimeParseError(dterr, str, "date");
switch (dtype)
{
......@@ -95,9 +94,8 @@ date_in(PG_FUNCTION_ARGS)
break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for date: \"%s\"", str)));
DateTimeParseError(DTERR_BAD_FORMAT, str, "date");
break;
}
date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
......@@ -559,21 +557,20 @@ time_in(PG_FUNCTION_ARGS)
*tm = &tt;
int tz;
int nf;
int dterr;
char lowstr[MAXDATELEN + 1];
char *field[MAXDATEFIELDS];
int dtype;
int ftype[MAXDATEFIELDS];
if (strlen(str) >= sizeof(lowstr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for time: \"%s\"", str)));
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for time: \"%s\"", str)));
dterr = DTERR_BAD_FORMAT;
else
dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
if (dterr != 0)
DateTimeParseError(dterr, str, "time");
tm2time(tm, fsec, &result);
AdjustTimeForTypmod(&result, typmod);
......@@ -1424,23 +1421,20 @@ timetz_in(PG_FUNCTION_ARGS)
*tm = &tt;
int tz;
int nf;
int dterr;
char lowstr[MAXDATELEN + 1];
char *field[MAXDATEFIELDS];
int dtype;
int ftype[MAXDATEFIELDS];
if (strlen(str) >= sizeof(lowstr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for time with time zone: \"%s\"",
str)));
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for time with time zone: \"%s\"",
str)));
dterr = DTERR_BAD_FORMAT;
else
dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
if (dterr != 0)
DateTimeParseError(dterr, str, "time with time zone");
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
tm2timetz(tm, fsec, tz, result);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.115 2003/08/25 23:30:27 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.116 2003/08/27 23:29:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -699,7 +699,7 @@ TrimTrailingZeros(char *str)
/* ParseDateTime()
* Break string into tokens based on a date/time context.
* Returns 0 if successful, -1 if bogus input detected.
* Returns 0 if successful, DTERR code if bogus input detected.
*
* timestr - the input string
* lowstr - workspace for field string storage (must be large enough for
......@@ -745,7 +745,7 @@ ParseDateTime(const char *timestr, char *lowstr,
/* Record start of current field */
if (nf >= maxfields)
return -1;
return DTERR_BAD_FORMAT;
field[nf] = lp;
/* leading digit? then date or time */
......@@ -866,7 +866,7 @@ ParseDateTime(const char *timestr, char *lowstr,
}
/* otherwise something wrong... */
else
return -1;
return DTERR_BAD_FORMAT;
}
/* ignore other punctuation but use as delimiter */
else if (ispunct((unsigned char) *cp))
......@@ -876,7 +876,7 @@ ParseDateTime(const char *timestr, char *lowstr,
}
/* otherwise, something is not right... */
else
return -1;
return DTERR_BAD_FORMAT;
/* force in a delimiter after each field */
*lp++ = '\0';
......@@ -891,7 +891,9 @@ ParseDateTime(const char *timestr, char *lowstr,
/* DecodeDateTime()
* Interpret previously parsed fields for general date and time.
* Return 0 if full date, 1 if only time, and -1 if problems.
* Return 0 if full date, 1 if only time, and negative DTERR code if problems.
* (Currently, all callers treat 1 as an error return too.)
*
* External format(s):
* "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
* "Fri Feb-7-1997 15:23:27"
......@@ -920,15 +922,16 @@ DecodeDateTime(char **field, int *ftype, int nf,
* format */
int i;
int val;
int dterr;
int mer = HR24;
int haveTextMonth = FALSE;
int is2digits = FALSE;
int bc = FALSE;
/***
/*
* We'll insist on at least all of the date fields, but initialize the
* remaining fields in case they are not set later...
***/
*/
*dtype = DTK_DATE;
tm->tm_hour = 0;
tm->tm_min = 0;
......@@ -955,14 +958,15 @@ DecodeDateTime(char **field, int *ftype, int nf,
int val;
if (tzp == NULL)
return -1;
return DTERR_BAD_FORMAT;
val = strtol(field[i], &cp, 10);
j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
/* Get the time zone from the end of the string */
if (DecodeTimezone(cp, tzp) != 0)
return -1;
dterr = DecodeTimezone(cp, tzp);
if (dterr)
return dterr;
tmask = DTK_DATE_M | DTK_TIME_M | DTK_M(TZ);
ptype = 0;
......@@ -979,7 +983,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
{
/* No time zone accepted? Then quit... */
if (tzp == NULL)
return -1;
return DTERR_BAD_FORMAT;
if (isdigit((unsigned char) *field[i]) || ptype != 0)
{
......@@ -989,7 +993,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
{
/* Sanity check; should not fail this test */
if (ptype != DTK_TIME)
return -1;
return DTERR_BAD_FORMAT;
ptype = 0;
}
......@@ -999,23 +1003,28 @@ DecodeDateTime(char **field, int *ftype, int nf,
* time already...
*/
if ((fmask & DTK_TIME_M) == DTK_TIME_M)
return -1;
return DTERR_BAD_FORMAT;
if ((cp = strchr(field[i], '-')) == NULL)
return -1;
return DTERR_BAD_FORMAT;
/* Get the time zone from the end of the string */
if (DecodeTimezone(cp, tzp) != 0)
return -1;
dterr = DecodeTimezone(cp, tzp);
if (dterr)
return dterr;
*cp = '\0';
/*
* Then read the rest of the field as a
* concatenated time
*/
if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], fmask,
&tmask, tm, fsec, &is2digits)) < 0)
return -1;
dterr = DecodeNumberField(strlen(field[i]), field[i],
fmask,
&tmask, tm,
fsec, &is2digits);
if (dterr < 0)
return dterr;
ftype[i] = dterr;
/*
* modify tmask after returning from
......@@ -1025,27 +1034,33 @@ DecodeDateTime(char **field, int *ftype, int nf,
}
else
{
if (DecodePosixTimezone(field[i], tzp) != 0)
return -1;
dterr = DecodePosixTimezone(field[i], tzp);
if (dterr)
return dterr;
ftype[i] = DTK_TZ;
tmask = DTK_M(TZ);
}
}
else if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
return -1;
else
{
dterr = DecodeDate(field[i], fmask, &tmask, tm);
if (dterr)
return dterr;
}
break;
case DTK_TIME:
if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
return -1;
dterr = DecodeTime(field[i], fmask, &tmask, tm, fsec);
if (dterr)
return dterr;
/*
* Check upper limit on hours; other limits checked in
* DecodeTime()
*/
if (tm->tm_hour > 23)
return -1;
return DTERR_FIELD_OVERFLOW;
break;
case DTK_TZ:
......@@ -1053,10 +1068,11 @@ DecodeDateTime(char **field, int *ftype, int nf,
int tz;
if (tzp == NULL)
return -1;
return DTERR_BAD_FORMAT;
if (DecodeTimezone(field[i], &tz) != 0)
return -1;
dterr = DecodeTimezone(field[i], &tz);
if (dterr)
return dterr;
/*
* Already have a time zone? Then maybe this is the
......@@ -1103,11 +1119,11 @@ DecodeDateTime(char **field, int *ftype, int nf,
case DTK_SECOND:
break;
default:
return 1;
return DTERR_BAD_FORMAT;
break;
}
else if (*cp != '\0')
return -1;
return DTERR_BAD_FORMAT;
switch (ptype)
{
......@@ -1159,7 +1175,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
frac = strtod(cp, &cp);
if (*cp != '\0')
return -1;
return DTERR_BAD_FORMAT;
#ifdef HAVE_INT64_TIMESTAMP
*fsec = rint(frac * 1000000);
#else
......@@ -1170,8 +1186,9 @@ DecodeDateTime(char **field, int *ftype, int nf,
case DTK_TZ:
tmask = DTK_M(TZ);
if (DecodeTimezone(field[i], tzp) != 0)
return -1;
dterr = DecodeTimezone(field[i], tzp);
if (dterr)
return dterr;
break;
case DTK_JULIAN:
......@@ -1187,7 +1204,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
time = strtod(cp, &cp);
if (*cp != '\0')
return -1;
return DTERR_BAD_FORMAT;
tmask |= DTK_TIME_M;
#ifdef HAVE_INT64_TIMESTAMP
......@@ -1202,16 +1219,20 @@ DecodeDateTime(char **field, int *ftype, int nf,
case DTK_TIME:
/* previous field was "t" for ISO time */
if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
&tmask, tm, fsec, &is2digits)) < 0)
return -1;
dterr = DecodeNumberField(strlen(field[i]), field[i],
(fmask | DTK_DATE_M),
&tmask, tm,
fsec, &is2digits);
if (dterr < 0)
return dterr;
ftype[i] = dterr;
if (tmask != DTK_TIME_M)
return -1;
return DTERR_BAD_FORMAT;
break;
default:
return -1;
return DTERR_BAD_FORMAT;
break;
}
......@@ -1229,8 +1250,9 @@ DecodeDateTime(char **field, int *ftype, int nf,
/* Embedded decimal and no date yet? */
if ((cp != NULL) && !(fmask & DTK_DATE_M))
{
if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
return -1;
dterr = DecodeDate(field[i], fmask, &tmask, tm);
if (dterr)
return dterr;
}
/* embedded decimal and several digits before? */
else if ((cp != NULL) && ((flen - strlen(cp)) > 2))
......@@ -1240,20 +1262,31 @@ DecodeDateTime(char **field, int *ftype, int nf,
* the type field to allow decoding other fields
* later. Example: 20011223 or 040506
*/
if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
&tmask, tm, fsec, &is2digits)) < 0)
return -1;
dterr = DecodeNumberField(flen, field[i], fmask,
&tmask, tm,
fsec, &is2digits);
if (dterr < 0)
return dterr;
ftype[i] = dterr;
}
else if (flen > 4)
{
if ((ftype[i] = DecodeNumberField(flen, field[i], fmask,
&tmask, tm, fsec, &is2digits)) < 0)
return -1;
dterr = DecodeNumberField(flen, field[i], fmask,
&tmask, tm,
fsec, &is2digits);
if (dterr < 0)
return dterr;
ftype[i] = dterr;
}
/* otherwise it is a single date/time field... */
else if (DecodeNumber(flen, field[i], fmask,
&tmask, tm, fsec, &is2digits) != 0)
return -1;
else
{
dterr = DecodeNumber(flen, field[i], fmask,
&tmask, tm,
fsec, &is2digits);
if (dterr)
return dterr;
}
}
break;
......@@ -1274,7 +1307,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("\"current\" is no longer supported")));
return -1;
return DTERR_BAD_FORMAT;
break;
case DTK_NOW:
......@@ -1356,7 +1389,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
tmask |= DTK_M(DTZ);
tm->tm_isdst = 1;
if (tzp == NULL)
return -1;
return DTERR_BAD_FORMAT;
*tzp += val * 60;
break;
......@@ -1369,7 +1402,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
tmask |= DTK_M(TZ);
tm->tm_isdst = 1;
if (tzp == NULL)
return -1;
return DTERR_BAD_FORMAT;
*tzp = val * 60;
ftype[i] = DTK_TZ;
break;
......@@ -1377,7 +1410,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
case TZ:
tm->tm_isdst = 0;
if (tzp == NULL)
return -1;
return DTERR_BAD_FORMAT;
*tzp = val * 60;
ftype[i] = DTK_TZ;
break;
......@@ -1411,9 +1444,9 @@ DecodeDateTime(char **field, int *ftype, int nf,
*/
tmask = 0;
/* No preceeding date? Then quit... */
/* No preceding date? Then quit... */
if ((fmask & DTK_DATE_M) != DTK_DATE_M)
return -1;
return DTERR_BAD_FORMAT;
/***
* We will need one of the following fields:
......@@ -1425,22 +1458,22 @@ DecodeDateTime(char **field, int *ftype, int nf,
|| ((ftype[i + 1] != DTK_NUMBER)
&& (ftype[i + 1] != DTK_TIME)
&& (ftype[i + 1] != DTK_DATE)))
return -1;
return DTERR_BAD_FORMAT;
ptype = val;
break;
default:
return -1;
return DTERR_BAD_FORMAT;
}
break;
default:
return -1;
return DTERR_BAD_FORMAT;
}
if (tmask & fmask)
return -1;
return DTERR_BAD_FORMAT;
fmask |= tmask;
}
......@@ -1477,18 +1510,18 @@ DecodeDateTime(char **field, int *ftype, int nf,
if (fmask & DTK_M(MONTH))
{
if (tm->tm_mon < 1 || tm->tm_mon > 12)
return -1;
return DTERR_MD_FIELD_OVERFLOW;
}
/* minimal check for valid day */
if (fmask & DTK_M(DAY))
{
if (tm->tm_mday < 1 || tm->tm_mday > 31)
return -1;
return DTERR_MD_FIELD_OVERFLOW;
}
if ((mer != HR24) && (tm->tm_hour > 12))
return -1;
return DTERR_FIELD_OVERFLOW;
if ((mer == AM) && (tm->tm_hour == 12))
tm->tm_hour = 0;
else if ((mer == PM) && (tm->tm_hour != 12))
......@@ -1498,14 +1531,19 @@ DecodeDateTime(char **field, int *ftype, int nf,
if (*dtype == DTK_DATE)
{
if ((fmask & DTK_DATE_M) != DTK_DATE_M)
return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
{
if ((fmask & DTK_TIME_M) == DTK_TIME_M)
return 1;
return DTERR_BAD_FORMAT;
}
/*
* check for valid day of month, now that we know for sure the
* month and year...
* Check for valid day of month, now that we know for sure the
* month and year. Note we don't use MD_FIELD_OVERFLOW here,
* since it seems unlikely that "Feb 29" is a YMD-order error.
*/
if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
return -1;
return DTERR_FIELD_OVERFLOW;
/* timezone not specified? then find local timezone if possible */
if ((tzp != NULL) && (!(fmask & DTK_M(TZ))))
......@@ -1515,14 +1553,14 @@ DecodeDateTime(char **field, int *ftype, int nf,
* then error
*/
if (fmask & DTK_M(DTZMOD))
return -1;
return DTERR_BAD_FORMAT;
*tzp = DetermineLocalTimeZone(tm);
}
}
return 0;
} /* DecodeDateTime() */
}
/* DetermineLocalTimeZone()
......@@ -1673,6 +1711,8 @@ DetermineLocalTimeZone(struct tm * tm)
/* DecodeTimeOnly()
* Interpret parsed string as time fields only.
* Returns 0 if successful, DTERR code if bogus input detected.
*
* Note that support for time zone is here for
* SQL92 TIME WITH TIME ZONE, but it reveals
* bogosity with SQL92 date/time standards, since
......@@ -1691,6 +1731,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
int ptype = 0; /* "prefix type" for ISO h04mm05s06 format */
int i;
int val;
int dterr;
int is2digits = FALSE;
int mer = HR24;
......@@ -1716,15 +1757,16 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
* time zones no matter what else!
*/
if (tzp == NULL)
return -1;
return DTERR_BAD_FORMAT;
/* Under limited circumstances, we will accept a date... */
if ((i == 0) && (nf >= 2)
&& ((ftype[nf - 1] == DTK_DATE)
|| (ftype[1] == DTK_TIME)))
{
if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
return -1;
dterr = DecodeDate(field[i], fmask, &tmask, tm);
if (dterr)
return dterr;
}
/* otherwise, this is a time and/or time zone */
else
......@@ -1739,34 +1781,40 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
* already...
*/
if ((fmask & DTK_TIME_M) == DTK_TIME_M)
return -1;
return DTERR_BAD_FORMAT;
/*
* Should not get here and fail. Sanity check
* only...
*/
if ((cp = strchr(field[i], '-')) == NULL)
return -1;
return DTERR_BAD_FORMAT;
/* Get the time zone from the end of the string */
if (DecodeTimezone(cp, tzp) != 0)
return -1;
dterr = DecodeTimezone(cp, tzp);
if (dterr)
return dterr;
*cp = '\0';
/*
* Then read the rest of the field as a
* concatenated time
*/
if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
&tmask, tm, fsec, &is2digits)) < 0)
return -1;
dterr = DecodeNumberField(strlen(field[i]), field[i],
(fmask | DTK_DATE_M),
&tmask, tm,
fsec, &is2digits);
if (dterr < 0)
return dterr;
ftype[i] = dterr;
tmask |= DTK_M(TZ);
}
else
{
if (DecodePosixTimezone(field[i], tzp) != 0)
return -1;
dterr = DecodePosixTimezone(field[i], tzp);
if (dterr)
return dterr;
ftype[i] = DTK_TZ;
tmask = DTK_M(TZ);
......@@ -1775,19 +1823,22 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
break;
case DTK_TIME:
if (DecodeTime(field[i], (fmask | DTK_DATE_M), &tmask, tm, fsec) != 0)
return -1;
dterr = DecodeTime(field[i], (fmask | DTK_DATE_M),
&tmask, tm, fsec);
if (dterr)
return dterr;
break;
case DTK_TZ:
if (tzp == NULL)
return -1;
{
int tz;
if (DecodeTimezone(field[i], &tz) != 0)
return -1;
if (tzp == NULL)
return DTERR_BAD_FORMAT;
dterr = DecodeTimezone(field[i], &tz);
if (dterr)
return dterr;
/*
* Already have a time zone? Then maybe this is the
......@@ -1827,7 +1878,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
case DTK_MONTH:
case DTK_DAY:
if (tzp == NULL)
return -1;
return DTERR_BAD_FORMAT;
default:
break;
}
......@@ -1846,11 +1897,11 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
case DTK_SECOND:
break;
default:
return 1;
return DTERR_BAD_FORMAT;
break;
}
else if (*cp != '\0')
return -1;
return DTERR_BAD_FORMAT;
switch (ptype)
{
......@@ -1902,7 +1953,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
frac = strtod(cp, &cp);
if (*cp != '\0')
return -1;
return DTERR_BAD_FORMAT;
#ifdef HAVE_INT64_TIMESTAMP
*fsec = rint(frac * 1000000);
#else
......@@ -1913,8 +1964,9 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
case DTK_TZ:
tmask = DTK_M(TZ);
if (DecodeTimezone(field[i], tzp) != 0)
return -1;
dterr = DecodeTimezone(field[i], tzp);
if (dterr)
return dterr;
break;
case DTK_JULIAN:
......@@ -1929,7 +1981,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
time = strtod(cp, &cp);
if (*cp != '\0')
return -1;
return DTERR_BAD_FORMAT;
tmask |= DTK_TIME_M;
#ifdef HAVE_INT64_TIMESTAMP
......@@ -1944,16 +1996,20 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
case DTK_TIME:
/* previous field was "t" for ISO time */
if ((ftype[i] = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M),
&tmask, tm, fsec, &is2digits)) < 0)
return -1;
dterr = DecodeNumberField(strlen(field[i]), field[i],
(fmask | DTK_DATE_M),
&tmask, tm,
fsec, &is2digits);
if (dterr < 0)
return dterr;
ftype[i] = dterr;
if (tmask != DTK_TIME_M)
return -1;
return DTERR_BAD_FORMAT;
break;
default:
return -1;
return DTERR_BAD_FORMAT;
break;
}
......@@ -1977,8 +2033,9 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
*/
if ((i == 0) && ((nf >= 2) && (ftype[nf - 1] == DTK_DATE)))
{
if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
return -1;
dterr = DecodeDate(field[i], fmask, &tmask, tm);
if (dterr)
return dterr;
}
/* embedded decimal and several digits before? */
else if ((flen - strlen(cp)) > 2)
......@@ -1988,26 +2045,37 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
* Set the type field to allow decoding other
* fields later. Example: 20011223 or 040506
*/
if ((ftype[i] = DecodeNumberField(flen, field[i],
dterr = DecodeNumberField(flen, field[i],
(fmask | DTK_DATE_M),
&tmask, tm, fsec, &is2digits)) < 0)
return -1;
&tmask, tm,
fsec, &is2digits);
if (dterr < 0)
return dterr;
ftype[i] = dterr;
}
else
return -1;
return DTERR_BAD_FORMAT;
}
else if (flen > 4)
{
if ((ftype[i] = DecodeNumberField(flen, field[i],
dterr = DecodeNumberField(flen, field[i],
(fmask | DTK_DATE_M),
&tmask, tm, fsec, &is2digits)) < 0)
return -1;
&tmask, tm,
fsec, &is2digits);
if (dterr < 0)
return dterr;
ftype[i] = dterr;
}
/* otherwise it is a single date/time field... */
else if (DecodeNumber(flen, field[i],
else
{
dterr = DecodeNumber(flen, field[i],
(fmask | DTK_DATE_M),
&tmask, tm, fsec, &is2digits) != 0)
return -1;
&tmask, tm,
fsec, &is2digits);
if (dterr)
return dterr;
}
}
break;
......@@ -2027,7 +2095,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("\"current\" is no longer supported")));
return -1;
return DTERR_BAD_FORMAT;
break;
case DTK_NOW:
......@@ -2046,7 +2114,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
break;
default:
return -1;
return DTERR_BAD_FORMAT;
}
break;
......@@ -2060,7 +2128,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
tmask |= DTK_M(DTZ);
tm->tm_isdst = 1;
if (tzp == NULL)
return -1;
return DTERR_BAD_FORMAT;
*tzp += val * 60;
break;
......@@ -2073,7 +2141,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
tmask |= DTK_M(TZ);
tm->tm_isdst = 1;
if (tzp == NULL)
return -1;
return DTERR_BAD_FORMAT;
*tzp = val * 60;
ftype[i] = DTK_TZ;
break;
......@@ -2081,7 +2149,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
case TZ:
tm->tm_isdst = 0;
if (tzp == NULL)
return -1;
return DTERR_BAD_FORMAT;
*tzp = val * 60;
ftype[i] = DTK_TZ;
break;
......@@ -2111,27 +2179,27 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
|| ((ftype[i + 1] != DTK_NUMBER)
&& (ftype[i + 1] != DTK_TIME)
&& (ftype[i + 1] != DTK_DATE)))
return -1;
return DTERR_BAD_FORMAT;
ptype = val;
break;
default:
return -1;
return DTERR_BAD_FORMAT;
}
break;
default:
return -1;
return DTERR_BAD_FORMAT;
}
if (tmask & fmask)
return -1;
return DTERR_BAD_FORMAT;
fmask |= tmask;
}
if ((mer != HR24) && (tm->tm_hour > 12))
return -1;
return DTERR_FIELD_OVERFLOW;
if ((mer == AM) && (tm->tm_hour == 12))
tm->tm_hour = 0;
else if ((mer == PM) && (tm->tm_hour != 12))
......@@ -2142,17 +2210,17 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
|| (tm->tm_min < 0) || (tm->tm_min > 59)
|| (tm->tm_sec < 0) || (tm->tm_sec > 60)
|| (*fsec < INT64CONST(0)) || (*fsec >= INT64CONST(1000000)))
return -1;
return DTERR_FIELD_OVERFLOW;
#else
if ((tm->tm_hour < 0) || (tm->tm_hour > 23)
|| (tm->tm_min < 0) || (tm->tm_min > 59)
|| (tm->tm_sec < 0) || (tm->tm_sec > 60)
|| (*fsec < 0) || (*fsec >= 1))
return -1;
return DTERR_FIELD_OVERFLOW;
#endif
if ((fmask & DTK_TIME_M) != DTK_TIME_M)
return -1;
return DTERR_BAD_FORMAT;
/* timezone not specified? then find local timezone if possible */
if ((tzp != NULL) && (!(fmask & DTK_M(TZ))))
......@@ -2165,7 +2233,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
* error
*/
if (fmask & DTK_M(DTZMOD))
return -1;
return DTERR_BAD_FORMAT;
if ((fmask & DTK_DATE_M) == 0)
GetCurrentDateTime(tmp);
......@@ -2183,20 +2251,22 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
}
return 0;
} /* DecodeTimeOnly() */
}
/* DecodeDate()
* Decode date string which includes delimiters.
* Return 0 if okay, a DTERR code if not.
*
* Insist on a complete set of fields.
*/
static int
DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
{
fsec_t fsec;
int nf = 0;
int i,
len;
int dterr;
int bc = FALSE;
int is2digits = FALSE;
int type,
......@@ -2232,7 +2302,7 @@ DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
#if 0
/* don't allow too many fields */
if (nf > 3)
return -1;
return DTERR_BAD_FORMAT;
#endif
*tmask = 0;
......@@ -2258,10 +2328,10 @@ DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
break;
default:
return -1;
return DTERR_BAD_FORMAT;
}
if (fmask & dmask)
return -1;
return DTERR_BAD_FORMAT;
fmask |= dmask;
*tmask |= dmask;
......@@ -2278,20 +2348,23 @@ DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
continue;
if ((len = strlen(field[i])) <= 0)
return -1;
return DTERR_BAD_FORMAT;
if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits) != 0)
return -1;
dterr = DecodeNumber(len, field[i], fmask,
&dmask, tm,
&fsec, &is2digits);
if (dterr)
return dterr;
if (fmask & dmask)
return -1;
return DTERR_BAD_FORMAT;
fmask |= dmask;
*tmask |= dmask;
}
if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
return -1;
return DTERR_BAD_FORMAT;
/* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
if (bc)
......@@ -2321,19 +2394,24 @@ DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
/* check for valid month */
if (tm->tm_mon < 1 || tm->tm_mon > 12)
return -1;
return DTERR_MD_FIELD_OVERFLOW;
/* check for valid day */
if (tm->tm_mday < 1 ||
tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
return -1;
if (tm->tm_mday < 1 || tm->tm_mday > 31)
return DTERR_MD_FIELD_OVERFLOW;
/* We don't want to hint about DateStyle for Feb 29 */
if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
return DTERR_FIELD_OVERFLOW;
return 0;
} /* DecodeDate() */
}
/* DecodeTime()
* Decode time string which includes delimiters.
* Return 0 if okay, a DTERR code if not.
*
* Only check the lower limit on hours, since this same code
* can be used to represent time spans.
*/
......@@ -2346,7 +2424,7 @@ DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, fsec_t *fsec)
tm->tm_hour = strtol(str, &cp, 10);
if (*cp != ':')
return -1;
return DTERR_BAD_FORMAT;
str = cp + 1;
tm->tm_min = strtol(str, &cp, 10);
if (*cp == '\0')
......@@ -2355,7 +2433,7 @@ DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, fsec_t *fsec)
*fsec = 0;
}
else if (*cp != ':')
return -1;
return DTERR_BAD_FORMAT;
else
{
str = cp + 1;
......@@ -2369,7 +2447,7 @@ DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, fsec_t *fsec)
str = cp;
frac = strtod(str, &cp);
if (*cp != '\0')
return -1;
return DTERR_BAD_FORMAT;
#ifdef HAVE_INT64_TIMESTAMP
*fsec = rint(frac * 1000000);
#else
......@@ -2377,7 +2455,7 @@ DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, fsec_t *fsec)
#endif
}
else
return -1;
return DTERR_BAD_FORMAT;
}
/* do a sanity check */
......@@ -2386,21 +2464,22 @@ DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, fsec_t *fsec)
|| (tm->tm_min < 0) || (tm->tm_min > 59)
|| (tm->tm_sec < 0) || (tm->tm_sec > 60)
|| (*fsec < INT64CONST(0)) || (*fsec >= INT64CONST(1000000)))
return -1;
return DTERR_FIELD_OVERFLOW;
#else
if ((tm->tm_hour < 0)
|| (tm->tm_min < 0) || (tm->tm_min > 59)
|| (tm->tm_sec < 0) || (tm->tm_sec > 60)
|| (*fsec < 0) || (*fsec >= 1))
return -1;
return DTERR_FIELD_OVERFLOW;
#endif
return 0;
} /* DecodeTime() */
}
/* DecodeNumber()
* Interpret plain numeric field as a date value in context.
* Return 0 if okay, a DTERR code if not.
*/
static int
DecodeNumber(int flen, char *str, int fmask,
......@@ -2408,12 +2487,13 @@ DecodeNumber(int flen, char *str, int fmask,
{
int val;
char *cp;
int dterr;
*tmask = 0;
val = strtol(str, &cp, 10);
if (cp == str)
return -1;
return DTERR_BAD_FORMAT;
if (*cp == '.')
{
......@@ -2425,15 +2505,18 @@ DecodeNumber(int flen, char *str, int fmask,
*/
if ((cp - str) > 2)
{
if (DecodeNumberField(flen, str, (fmask | DTK_DATE_M),
tmask, tm, fsec, is2digits) < 0)
return -1;
dterr = DecodeNumberField(flen, str,
(fmask | DTK_DATE_M),
tmask, tm,
fsec, is2digits);
if (dterr < 0)
return dterr;
return 0;
}
frac = strtod(cp, &cp);
if (*cp != '\0')
return -1;
return DTERR_BAD_FORMAT;
#ifdef HAVE_INT64_TIMESTAMP
*fsec = rint(frac * 1000000);
#else
......@@ -2441,7 +2524,7 @@ DecodeNumber(int flen, char *str, int fmask,
#endif
}
else if (*cp != '\0')
return -1;
return DTERR_BAD_FORMAT;
/* Special case for day of year */
if ((flen == 3) &&
......@@ -2515,14 +2598,16 @@ DecodeNumber(int flen, char *str, int fmask,
case (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY)):
/* we have all the date, so it must be a time field */
if (DecodeNumberField(flen, str, fmask,
tmask, tm, fsec, is2digits) < 0)
return -1;
dterr = DecodeNumberField(flen, str, fmask,
tmask, tm,
fsec, is2digits);
if (dterr < 0)
return dterr;
return 0;
default:
/* Anything else is bogus input */
return -1;
return DTERR_BAD_FORMAT;
}
/*
......@@ -2538,6 +2623,8 @@ DecodeNumber(int flen, char *str, int fmask,
/* DecodeNumberField()
* Interpret numeric string as a concatenated date or time field.
* Return a DTK token (>= 0) if successful, a DTERR code (< 0) if not.
*
* Use the context of previously decoded fields to help with
* the interpretation.
*/
......@@ -2623,14 +2710,14 @@ DecodeNumberField(int len, char *str, int fmask,
}
}
return -1;
} /* DecodeNumberField() */
return DTERR_BAD_FORMAT;
}
/* DecodeTimezone()
* Interpret string as a numeric timezone.
*
* Return 0 if okay (and set *tzp), nonzero if not okay.
* Return 0 if okay (and set *tzp), a DTERR code if not okay.
*
* NB: this must *not* ereport on failure; see commands/variable.c.
*
......@@ -2644,11 +2731,10 @@ DecodeTimezone(char *str, int *tzp)
int hr,
min;
char *cp;
int len;
/* leading character must be "+" or "-" */
if (*str != '+' && *str != '-')
return -1;
return DTERR_BAD_FORMAT;
hr = strtol((str + 1), &cp, 10);
......@@ -2656,28 +2742,30 @@ DecodeTimezone(char *str, int *tzp)
if (*cp == ':')
min = strtol((cp + 1), &cp, 10);
/* otherwise, might have run things together... */
else if ((*cp == '\0') && ((len = strlen(str)) > 3))
else if ((*cp == '\0') && (strlen(str) > 3))
{
min = strtol((str + len - 2), &cp, 10);
if ((min < 0) || (min >= 60))
return -1;
*(str + len - 2) = '\0';
hr = strtol((str + 1), &cp, 10);
if ((hr < 0) || (hr > 13))
return -1;
min = hr % 100;
hr = hr / 100;
}
else
min = 0;
tz = (hr * 60 + min) * 60;
if ((hr < 0) || (hr > 13))
return DTERR_TZDISP_OVERFLOW;
if ((min < 0) || (min >= 60))
return DTERR_TZDISP_OVERFLOW;
tz = (hr * 60 + min) * 60;
if (*str == '-')
tz = -tz;
*tzp = -tz;
return *cp != '\0';
} /* DecodeTimezone() */
if (*cp != '\0')
return DTERR_BAD_FORMAT;
return 0;
}
/* DecodePosixTimezone()
......@@ -2687,7 +2775,7 @@ DecodeTimezone(char *str, int *tzp)
* PST
* - thomas 2000-03-15
*
* Return 0 if okay (and set *tzp), nonzero if not okay.
* Return 0 if okay (and set *tzp), a DTERR code if not okay.
*
* NB: this must *not* ereport on failure; see commands/variable.c.
*/
......@@ -2697,6 +2785,7 @@ DecodePosixTimezone(char *str, int *tzp)
int val,
tz;
int type;
int dterr;
char *cp;
char delim;
......@@ -2708,8 +2797,9 @@ DecodePosixTimezone(char *str, int *tzp)
/* decode offset, if present */
if (*cp)
{
if (DecodeTimezone(cp, &tz) != 0)
return -1;
dterr = DecodeTimezone(cp, &tz);
if (dterr)
return dterr;
}
else
tz = 0;
......@@ -2728,11 +2818,11 @@ DecodePosixTimezone(char *str, int *tzp)
break;
default:
return -1;
return DTERR_BAD_FORMAT;
}
return 0;
} /* DecodePosixTimezone() */
}
/* DecodeSpecial()
......@@ -2786,12 +2876,12 @@ DecodeSpecial(int field, char *lowtoken, int *val)
}
return type;
} /* DecodeSpecial() */
}
/* DecodeInterval()
* Interpret previously parsed fields for general time interval.
* Return 0 if decoded and -1 if problems.
* Returns 0 if successful, DTERR code if bogus input detected.
*
* Allow "date" field DTK_DATE since this could be just
* an unsigned floating point number. - thomas 1997-11-16
......@@ -2803,12 +2893,12 @@ int
DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fsec_t *fsec)
{
int is_before = FALSE;
char *cp;
int fmask = 0,
tmask,
type;
int i;
int dterr;
int val;
double fval;
......@@ -2829,8 +2919,9 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
switch (ftype[i])
{
case DTK_TIME:
if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
return -1;
dterr = DecodeTime(field[i], fmask, &tmask, tm, fsec);
if (dterr)
return dterr;
type = DTK_DAY;
break;
......@@ -2850,8 +2941,8 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
cp = field[i] + 1;
while ((*cp != '\0') && (*cp != ':') && (*cp != '.'))
cp++;
if ((*cp == ':')
&& (DecodeTime((field[i] + 1), fmask, &tmask, tm, fsec) == 0))
if ((*cp == ':') &&
(DecodeTime(field[i] + 1, fmask, &tmask, tm, fsec) == 0))
{
if (*field[i] == '-')
{
......@@ -2903,7 +2994,7 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
{
fval = strtod(cp, &cp);
if (*cp != '\0')
return -1;
return DTERR_BAD_FORMAT;
if (val < 0)
fval = -(fval);
......@@ -2911,7 +3002,7 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
else if (*cp == '\0')
fval = 0;
else
return -1;
return DTERR_BAD_FORMAT;
tmask = 0; /* DTK_M(type); */
......@@ -3062,7 +3153,7 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
break;
default:
return -1;
return DTERR_BAD_FORMAT;
}
break;
......@@ -3090,16 +3181,16 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
break;
default:
return -1;
return DTERR_BAD_FORMAT;
}
break;
default:
return -1;
return DTERR_BAD_FORMAT;
}
if (tmask & fmask)
return -1;
return DTERR_BAD_FORMAT;
fmask |= tmask;
}
......@@ -3128,8 +3219,11 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
}
/* ensure that at least one time field has been found */
return (fmask != 0) ? 0 : -1;
} /* DecodeInterval() */
if (fmask == 0)
return DTERR_BAD_FORMAT;
return 0;
}
/* DecodeUnits()
......@@ -3165,6 +3259,57 @@ DecodeUnits(int field, char *lowtoken, int *val)
return type;
} /* DecodeUnits() */
/*
* Report an error detected by one of the datetime input processing routines.
*
* dterr is the error code, str is the original input string, datatype is
* the name of the datatype we were trying to accept.
*
* 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, const char *str, const char *datatype)
{
switch (dterr)
{
case DTERR_FIELD_OVERFLOW:
ereport(ERROR,
(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,
(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,
(errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW),
errmsg("interval field value out of range: \"%s\"",
str)));
break;
case DTERR_TZDISP_OVERFLOW:
ereport(ERROR,
(errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
errmsg("time zone displacement out of range: \"%s\"",
str)));
break;
case DTERR_BAD_FORMAT:
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
/* translator: first %s is datatype name */
errmsg("invalid input syntax for %s: \"%s\"",
datatype, str)));
break;
}
}
/* datebsearch()
* Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
......
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.114 2003/08/17 19:58:05 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.115 2003/08/27 23:29:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -364,6 +364,7 @@ abstimein(PG_FUNCTION_ARGS)
int tz = 0;
struct tm date,
*tm = &date;
int dterr;
char *field[MAXDATEFIELDS];
char lowstr[MAXDATELEN + 1];
int dtype;
......@@ -371,15 +372,13 @@ abstimein(PG_FUNCTION_ARGS)
ftype[MAXDATEFIELDS];
if (strlen(str) >= sizeof(lowstr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for abstime: \"%s\"", str)));
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for abstime: \"%s\"", str)));
dterr = DTERR_BAD_FORMAT;
else
dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
if (dterr != 0)
DateTimeParseError(dterr, str, "abstime");
switch (dtype)
{
......@@ -768,21 +767,24 @@ reltimein(PG_FUNCTION_ARGS)
*tm = &tt;
fsec_t fsec;
int dtype;
int dterr;
char *field[MAXDATEFIELDS];
int nf,
ftype[MAXDATEFIELDS];
char lowstr[MAXDATELEN + 1];
if (strlen(str) >= sizeof(lowstr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for reltime: \"%s\"", str)));
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for reltime: \"%s\"", str)));
dterr = DTERR_BAD_FORMAT;
else
dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeInterval(field, ftype, nf, &dtype, tm, &fsec);
if (dterr != 0)
{
if (dterr == DTERR_FIELD_OVERFLOW)
dterr = DTERR_INTERVAL_OVERFLOW;
DateTimeParseError(dterr, str, "reltime");
}
switch (dtype)
{
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.93 2003/08/26 21:31:11 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.94 2003/08/27 23:29:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -77,22 +77,19 @@ timestamp_in(PG_FUNCTION_ARGS)
int tz;
int dtype;
int nf;
int dterr;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char lowstr[MAXDATELEN + MAXDATEFIELDS];
if (strlen(str) >= sizeof(lowstr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for timestamp: \"%s\"",
str)));
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for timestamp: \"%s\"",
str)));
dterr = DTERR_BAD_FORMAT;
else
dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
if (dterr != 0)
DateTimeParseError(dterr, str, "timestamp");
switch (dtype)
{
......@@ -306,22 +303,19 @@ timestamptz_in(PG_FUNCTION_ARGS)
int tz;
int dtype;
int nf;
int dterr;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char lowstr[MAXDATELEN + MAXDATEFIELDS];
if (strlen(str) >= sizeof(lowstr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for timestamp with time zone: \"%s\"",
str)));
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for timestamp with time zone: \"%s\"",
str)));
dterr = DTERR_BAD_FORMAT;
else
dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
if (dterr != 0)
DateTimeParseError(dterr, str, "timestamp with time zone");
switch (dtype)
{
......@@ -468,6 +462,7 @@ interval_in(PG_FUNCTION_ARGS)
*tm = &tt;
int dtype;
int nf;
int dterr;
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char lowstr[MAXDATELEN + MAXDATEFIELDS];
......@@ -481,17 +476,17 @@ interval_in(PG_FUNCTION_ARGS)
fsec = 0;
if (strlen(str) >= sizeof(lowstr))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for interval: \"%s\"",
str)));
if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|| (DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0))
ereport(ERROR,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for interval: \"%s\"",
str)));
dterr = DTERR_BAD_FORMAT;
else
dterr = ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
dterr = DecodeInterval(field, ftype, nf, &dtype, tm, &fsec);
if (dterr != 0)
{
if (dterr == DTERR_FIELD_OVERFLOW)
dterr = DTERR_INTERVAL_OVERFLOW;
DateTimeParseError(dterr, str, "interval");
}
result = (Interval *) palloc(sizeof(Interval));
......
......@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: datetime.h,v 1.44 2003/08/05 18:30:21 tgl Exp $
* $Id: datetime.h,v 1.45 2003/08/27 23:29:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -204,7 +204,7 @@ typedef struct
*/
#define FMODULO(t,q,u) \
do { \
q = ((t < 0) ? ceil(t / u): floor(t / u)); \
q = ((t < 0) ? ceil(t / u) : floor(t / u)); \
if (q != 0) t -= rint(q * u); \
} while(0)
......@@ -222,7 +222,7 @@ do { \
#else
#define TMODULO(t,q,u) \
do { \
q = ((t < 0) ? ceil(t / u): floor(t / u)); \
q = ((t < 0) ? ceil(t / u) : floor(t / u)); \
if (q != 0) t -= rint(q * u); \
} while(0)
#endif
......@@ -253,6 +253,15 @@ extern int day_tab[2][13];
|| (((m) == JULIAN_MINMONTH) && ((d) >= JULIAN_MINDAY))))) \
&& ((y) < JULIAN_MAXYEAR))
/* Julian-date equivalents of Day 0 in Unix and Postgres reckoning */
#define UNIX_EPOCH_JDATE 2440588 /* == date2j(1970, 1, 1) */
#define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */
/*
* Info about limits of the Unix time_t data type. We assume that time_t
* is a signed int32 with origin 1970-01-01. Note this is only relevant
* when we use the C library's time routines for timezone processing.
*/
#define UTIME_MINYEAR (1901)
#define UTIME_MINMONTH (12)
#define UTIME_MINDAY (14)
......@@ -267,9 +276,17 @@ extern int day_tab[2][13];
|| (((y) == UTIME_MAXYEAR) && (((m) < UTIME_MAXMONTH) \
|| (((m) == UTIME_MAXMONTH) && ((d) <= UTIME_MAXDAY))))))
/* Julian-date equivalents of Day 0 in Unix and Postgres reckoning */
#define UNIX_EPOCH_JDATE 2440588 /* == date2j(1970, 1, 1) */
#define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */
/*
* Datetime input parsing routines (ParseDateTime, DecodeDateTime, etc)
* return zero or a positive value on success. On failure, they return
* one of these negative code values. DateTimeParseError may be used to
* produce a correct ereport.
*/
#define DTERR_BAD_FORMAT (-1)
#define DTERR_FIELD_OVERFLOW (-2)
#define DTERR_MD_FIELD_OVERFLOW (-3) /* triggers hint about DateStyle */
#define DTERR_INTERVAL_OVERFLOW (-4)
#define DTERR_TZDISP_OVERFLOW (-5)
extern void GetCurrentDateTime(struct tm * tm);
......@@ -283,14 +300,14 @@ extern int ParseDateTime(const char *timestr, char *lowstr,
extern int DecodeDateTime(char **field, int *ftype,
int nf, int *dtype,
struct tm * tm, fsec_t *fsec, int *tzp);
extern int DecodeTimeOnly(char **field, int *ftype,
int nf, int *dtype,
struct tm * tm, fsec_t *fsec, int *tzp);
extern int DecodeInterval(char **field, int *ftype,
int nf, int *dtype,
struct tm * tm, fsec_t *fsec);
extern void DateTimeParseError(int dterr, const char *str,
const char *datatype);
extern int DetermineLocalTimeZone(struct tm * tm);
......
......@@ -28,9 +28,10 @@ INSERT INTO ABSTIME_TBL (f1) VALUES (abstime '-infinity');
INSERT INTO ABSTIME_TBL (f1) VALUES (abstime 'May 10, 1947 23:59:12');
-- what happens if we specify slightly misformatted abstime?
INSERT INTO ABSTIME_TBL (f1) VALUES ('Feb 35, 1946 10:00:00');
ERROR: invalid input syntax for abstime: "Feb 35, 1946 10:00:00"
ERROR: date/time field value out of range: "Feb 35, 1946 10:00:00"
HINT: Perhaps you need a different DateStyle setting.
INSERT INTO ABSTIME_TBL (f1) VALUES ('Feb 28, 1984 25:08:10');
ERROR: invalid input syntax for abstime: "Feb 28, 1984 25:08:10"
ERROR: date/time field value out of range: "Feb 28, 1984 25:08:10"
-- badly formatted abstimes: these should result in invalid abstimes
INSERT INTO ABSTIME_TBL (f1) VALUES ('bad date format');
ERROR: invalid input syntax for abstime: "bad date format"
......
......@@ -28,9 +28,10 @@ INSERT INTO ABSTIME_TBL (f1) VALUES (abstime '-infinity');
INSERT INTO ABSTIME_TBL (f1) VALUES (abstime 'May 10, 1947 23:59:12');
-- what happens if we specify slightly misformatted abstime?
INSERT INTO ABSTIME_TBL (f1) VALUES ('Feb 35, 1946 10:00:00');
ERROR: invalid input syntax for abstime: "Feb 35, 1946 10:00:00"
ERROR: date/time field value out of range: "Feb 35, 1946 10:00:00"
HINT: Perhaps you need a different DateStyle setting.
INSERT INTO ABSTIME_TBL (f1) VALUES ('Feb 28, 1984 25:08:10');
ERROR: invalid input syntax for abstime: "Feb 28, 1984 25:08:10"
ERROR: date/time field value out of range: "Feb 28, 1984 25:08:10"
-- badly formatted abstimes: these should result in invalid abstimes
INSERT INTO ABSTIME_TBL (f1) VALUES ('bad date format');
ERROR: invalid input syntax for abstime: "bad date format"
......
......@@ -10,7 +10,7 @@ INSERT INTO DATE_TBL VALUES ('1996-03-01');
INSERT INTO DATE_TBL VALUES ('1996-03-02');
INSERT INTO DATE_TBL VALUES ('1997-02-28');
INSERT INTO DATE_TBL VALUES ('1997-02-29');
ERROR: invalid input syntax for date: "1997-02-29"
ERROR: date/time field value out of range: "1997-02-29"
INSERT INTO DATE_TBL VALUES ('1997-03-01');
INSERT INTO DATE_TBL VALUES ('1997-03-02');
INSERT INTO DATE_TBL VALUES ('2000-04-01');
......
......@@ -81,7 +81,8 @@ SELECT timestamp with time zone '12/27/2001 04:05:06.789-08';
-- should fail in mdy mode:
SELECT timestamp with time zone '27/12/2001 04:05:06.789-08';
ERROR: invalid input syntax for timestamp with time zone: "27/12/2001 04:05:06.789-08"
ERROR: date/time field value out of range: "27/12/2001 04:05:06.789-08"
HINT: Perhaps you need a different DateStyle setting.
set datestyle to dmy;
SELECT timestamp with time zone '27/12/2001 04:05:06.789-08';
timestamptz
......
......@@ -81,7 +81,8 @@ SELECT timestamp with time zone '12/27/2001 04:05:06.789-08';
-- should fail in mdy mode:
SELECT timestamp with time zone '27/12/2001 04:05:06.789-08';
ERROR: invalid input syntax for timestamp with time zone: "27/12/2001 04:05:06.789-08"
ERROR: date/time field value out of range: "27/12/2001 04:05:06.789-08"
HINT: Perhaps you need a different DateStyle setting.
set datestyle to dmy;
SELECT timestamp with time zone '27/12/2001 04:05:06.789-08';
timestamptz
......
......@@ -81,7 +81,8 @@ SELECT timestamp with time zone '12/27/2001 04:05:06.789-08';
-- should fail in mdy mode:
SELECT timestamp with time zone '27/12/2001 04:05:06.789-08';
ERROR: invalid input syntax for timestamp with time zone: "27/12/2001 04:05:06.789-08"
ERROR: date/time field value out of range: "27/12/2001 04:05:06.789-08"
HINT: Perhaps you need a different DateStyle setting.
set datestyle to dmy;
SELECT timestamp with time zone '27/12/2001 04:05:06.789-08';
timestamptz
......
......@@ -128,7 +128,7 @@ INSERT INTO TIMESTAMP_TBL VALUES ('Dec 31 17:32:01 1996');
INSERT INTO TIMESTAMP_TBL VALUES ('Jan 01 17:32:01 1997');
INSERT INTO TIMESTAMP_TBL VALUES ('Feb 28 17:32:01 1997');
INSERT INTO TIMESTAMP_TBL VALUES ('Feb 29 17:32:01 1997');
ERROR: invalid input syntax for timestamp: "Feb 29 17:32:01 1997"
ERROR: date/time field value out of range: "Feb 29 17:32:01 1997"
INSERT INTO TIMESTAMP_TBL VALUES ('Mar 01 17:32:01 1997');
INSERT INTO TIMESTAMP_TBL VALUES ('Dec 30 17:32:01 1997');
INSERT INTO TIMESTAMP_TBL VALUES ('Dec 31 17:32:01 1997');
......@@ -138,7 +138,7 @@ INSERT INTO TIMESTAMP_TBL VALUES ('Dec 31 17:32:01 2000');
INSERT INTO TIMESTAMP_TBL VALUES ('Jan 01 17:32:01 2001');
-- Currently unsupported syntax and ranges
INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 -0097');
ERROR: invalid input syntax for timestamp: "Feb 16 17:32:01 -0097"
ERROR: time zone displacement out of range: "Feb 16 17:32:01 -0097"
INSERT INTO TIMESTAMP_TBL VALUES ('Feb 16 17:32:01 5097 BC');
ERROR: timestamp out of range: "Feb 16 17:32:01 5097 BC"
SELECT '' AS "64", d1 FROM TIMESTAMP_TBL;
......
......@@ -123,7 +123,7 @@ INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 31 17:32:01 1996');
INSERT INTO TIMESTAMPTZ_TBL VALUES ('Jan 01 17:32:01 1997');
INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 28 17:32:01 1997');
INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 29 17:32:01 1997');
ERROR: invalid input syntax for timestamp with time zone: "Feb 29 17:32:01 1997"
ERROR: date/time field value out of range: "Feb 29 17:32:01 1997"
INSERT INTO TIMESTAMPTZ_TBL VALUES ('Mar 01 17:32:01 1997');
INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 30 17:32:01 1997');
INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 31 17:32:01 1997');
......@@ -133,7 +133,7 @@ INSERT INTO TIMESTAMPTZ_TBL VALUES ('Dec 31 17:32:01 2000');
INSERT INTO TIMESTAMPTZ_TBL VALUES ('Jan 01 17:32:01 2001');
-- Currently unsupported syntax and ranges
INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 -0097');
ERROR: invalid input syntax for timestamp with time zone: "Feb 16 17:32:01 -0097"
ERROR: time zone displacement out of range: "Feb 16 17:32:01 -0097"
INSERT INTO TIMESTAMPTZ_TBL VALUES ('Feb 16 17:32:01 5097 BC');
ERROR: timestamp out of range: "Feb 16 17:32:01 5097 BC"
SELECT '' AS "64", d1 FROM TIMESTAMPTZ_TBL;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment