diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 0f9fcaded9e..60583fed7a6 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -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); diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 3a06d63ed3d..ca3bb2a22ea 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -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): * " -- ::" * "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], - (fmask | DTK_DATE_M), - &tmask, tm, fsec, &is2digits)) < 0) - return -1; + dterr = DecodeNumberField(flen, field[i], + (fmask | DTK_DATE_M), + &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], - (fmask | DTK_DATE_M), - &tmask, tm, fsec, &is2digits)) < 0) - return -1; + dterr = DecodeNumberField(flen, field[i], + (fmask | DTK_DATE_M), + &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 | DTK_DATE_M), - &tmask, tm, fsec, &is2digits) != 0) - return -1; + else + { + dterr = DecodeNumber(flen, field[i], + (fmask | DTK_DATE_M), + &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,12 +2623,14 @@ 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. */ static int DecodeNumberField(int len, char *str, int fmask, - int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits) + int *tmask, struct tm * tm, fsec_t *fsec, int *is2digits) { char *cp; @@ -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: + /* same as above, but add hint about DateStyle */ + 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 diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c index f694349db7a..8b758b51195 100644 --- a/src/backend/utils/adt/nabstime.c +++ b/src/backend/utils/adt/nabstime.c @@ -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) { diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index ef998454ff9..961c70da81a 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -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)); diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h index d5facdd8e00..cda63ebe6de 100644 --- a/src/include/utils/datetime.h +++ b/src/include/utils/datetime.h @@ -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); diff --git a/src/test/regress/expected/abstime-solaris-1947.out b/src/test/regress/expected/abstime-solaris-1947.out index ecfdb9f0548..41a0861e3a1 100644 --- a/src/test/regress/expected/abstime-solaris-1947.out +++ b/src/test/regress/expected/abstime-solaris-1947.out @@ -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" diff --git a/src/test/regress/expected/abstime.out b/src/test/regress/expected/abstime.out index abece85133d..b7300469390 100644 --- a/src/test/regress/expected/abstime.out +++ b/src/test/regress/expected/abstime.out @@ -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" diff --git a/src/test/regress/expected/date.out b/src/test/regress/expected/date.out index ce1f76785d4..84bdcf232b9 100644 --- a/src/test/regress/expected/date.out +++ b/src/test/regress/expected/date.out @@ -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'); diff --git a/src/test/regress/expected/horology-no-DST-before-1970.out b/src/test/regress/expected/horology-no-DST-before-1970.out index 0aa1da407f4..379e17e0a0f 100644 --- a/src/test/regress/expected/horology-no-DST-before-1970.out +++ b/src/test/regress/expected/horology-no-DST-before-1970.out @@ -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 diff --git a/src/test/regress/expected/horology-solaris-1947.out b/src/test/regress/expected/horology-solaris-1947.out index cdb9c7f1d86..c5c70779a13 100644 --- a/src/test/regress/expected/horology-solaris-1947.out +++ b/src/test/regress/expected/horology-solaris-1947.out @@ -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 diff --git a/src/test/regress/expected/horology.out b/src/test/regress/expected/horology.out index 984e75e8b4c..940707e90fc 100644 --- a/src/test/regress/expected/horology.out +++ b/src/test/regress/expected/horology.out @@ -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 diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out index 0b45bb9c1a7..2ea2371c688 100644 --- a/src/test/regress/expected/timestamp.out +++ b/src/test/regress/expected/timestamp.out @@ -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; diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out index 49525518c7b..9214ae76e88 100644 --- a/src/test/regress/expected/timestamptz.out +++ b/src/test/regress/expected/timestamptz.out @@ -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;